From 848f9d81f6708b9c83d0490d135b989ad82239fa Mon Sep 17 00:00:00 2001 From: Richard Huang Date: Thu, 25 Feb 2021 16:42:13 +0000 Subject: [PATCH] Create UBO for uniform buffer bindings Change-Id: Ie863070d46b92b4ce135e5eba87172641e13bd8b --- dali/internal/file.list | 3 +- dali/internal/render/common/render-manager.cpp | 7 +- dali/internal/render/renderers/render-renderer.cpp | 260 ++++++++++++++++++++- dali/internal/render/renderers/render-renderer.h | 53 ++++- .../render/renderers/uniform-buffer-manager.cpp | 175 ++++++++++++++ .../render/renderers/uniform-buffer-manager.h | 160 +++++++++++++ dali/internal/render/shaders/program.cpp | 176 +++++++++++++- dali/internal/render/shaders/program.h | 67 ++++++ dali/internal/update/common/uniform-map.h | 25 +- 9 files changed, 902 insertions(+), 24 deletions(-) create mode 100644 dali/internal/render/renderers/uniform-buffer-manager.cpp create mode 100644 dali/internal/render/renderers/uniform-buffer-manager.h diff --git a/dali/internal/file.list b/dali/internal/file.list index eff7556..689d591 100644 --- a/dali/internal/file.list +++ b/dali/internal/file.list @@ -118,8 +118,9 @@ SET( internal_src_files ${internal_src_dir}/render/renderers/render-sampler.cpp ${internal_src_dir}/render/renderers/render-texture.cpp ${internal_src_dir}/render/renderers/render-vertex-buffer.cpp - ${internal_src_dir}/render/renderers/shader-cache.cpp + ${internal_src_dir}/render/renderers/uniform-buffer-manager.cpp + ${internal_src_dir}/render/shaders/program.cpp ${internal_src_dir}/render/shaders/program-controller.cpp ${internal_src_dir}/render/shaders/scene-graph-shader.cpp diff --git a/dali/internal/render/common/render-manager.cpp b/dali/internal/render/common/render-manager.cpp index c519a5a..8c60e9b 100644 --- a/dali/internal/render/common/render-manager.cpp +++ b/dali/internal/render/common/render-manager.cpp @@ -39,6 +39,7 @@ #include #include #include +#include #include namespace Dali @@ -84,6 +85,8 @@ struct RenderManager::Impl // Create thread pool with just one thread ( there may be a need to create more threads in the future ). threadPool = std::unique_ptr(new Dali::ThreadPool()); threadPool->Initialize(1u); + + uniformBufferManager.reset(new Render::UniformBufferManager(&graphicsController)); } ~Impl() @@ -166,6 +169,8 @@ struct RenderManager::Impl ProgramController programController; ///< Owner of the GL programs Render::ShaderCache shaderCache; ///< The cache for the graphics shaders + std::unique_ptr uniformBufferManager; ///< The uniform buffer manager + Integration::DepthBufferAvailable depthBufferAvailable; ///< Whether the depth buffer is available Integration::StencilBufferAvailable stencilBufferAvailable; ///< Whether the stencil buffer is available Integration::PartialUpdateAvailable partialUpdateAvailable; ///< Whether the partial update is available @@ -250,7 +255,7 @@ void RenderManager::SetShaderSaver(ShaderSaver& upstream) void RenderManager::AddRenderer(OwnerPointer& renderer) { // Initialize the renderer as we are now in render thread - renderer->Initialize(mImpl->context, mImpl->graphicsController, mImpl->programController, mImpl->shaderCache); + renderer->Initialize(mImpl->context, mImpl->graphicsController, mImpl->programController, mImpl->shaderCache, *(mImpl->uniformBufferManager.get())); mImpl->rendererContainer.PushBack(renderer.Release()); } diff --git a/dali/internal/render/renderers/render-renderer.cpp b/dali/internal/render/renderers/render-renderer.cpp index 4570797..3bba444 100644 --- a/dali/internal/render/renderers/render-renderer.cpp +++ b/dali/internal/render/renderers/render-renderer.cpp @@ -32,6 +32,7 @@ #include #include #include +#include namespace Dali { @@ -39,6 +40,12 @@ namespace Internal { namespace { +// Size of uniform buffer page used when resizing +constexpr uint32_t UBO_PAGE_SIZE = 8192u; + +// UBO allocation threshold below which the UBO will shrink +constexpr auto UBO_SHRINK_THRESHOLD = 0.75f; + /** * Helper to set view and projection matrices once per program * @param program to set the matrices to @@ -260,6 +267,16 @@ constexpr Graphics::BlendOp ConvertBlendEquation(DevelBlendEquation::Type blendE return Graphics::BlendOp{}; } +/** + * Helper function to calculate the correct alignment of data for uniform buffers + * @param dataSize size of uniform buffer + * @return aligned offset of data + */ +inline uint32_t GetUniformBufferDataAlignment(uint32_t dataSize) +{ + return ((dataSize / 256u) + ((dataSize % 256u) ? 1u : 0u)) * 256u; +} + } // namespace namespace Render @@ -316,12 +333,13 @@ Renderer::Renderer(SceneGraph::RenderDataProvider* dataProvider, mBlendingOptions.SetBlendColor(blendColor); } -void Renderer::Initialize(Context& context, Graphics::Controller& graphicsController, ProgramCache& programCache, Render::ShaderCache& shaderCache) +void Renderer::Initialize(Context& context, Graphics::Controller& graphicsController, ProgramCache& programCache, Render::ShaderCache& shaderCache, Render::UniformBufferManager& uniformBufferManager) { - mContext = &context; - mGraphicsController = &graphicsController; - mProgramCache = &programCache; - mShaderCache = &shaderCache; + mContext = &context; + mGraphicsController = &graphicsController; + mProgramCache = &programCache; + mShaderCache = &shaderCache; + mUniformBufferManager = &uniformBufferManager; } Renderer::~Renderer() = default; @@ -346,7 +364,7 @@ void Renderer::GlCleanup() { } -void Renderer::SetUniforms(BufferIndex bufferIndex, const SceneGraph::NodeDataProvider& node, const Vector3& size, Program& program) +void Renderer::BuildUniformIndexMap(BufferIndex bufferIndex, const SceneGraph::NodeDataProvider& node, const Vector3& size, Program& program) { // Check if the map has changed DALI_ASSERT_DEBUG(mRenderDataProvider && "No Uniform map data provider available"); @@ -371,8 +389,12 @@ void Renderer::SetUniforms(BufferIndex bufferIndex, const SceneGraph::NodeDataPr uint32_t mapIndex = 0; for(; mapIndex < uniformMap.Count(); ++mapIndex) { - mUniformIndexMap[mapIndex].propertyValue = uniformMap[mapIndex].propertyPtr; - mUniformIndexMap[mapIndex].uniformIndex = program.RegisterUniform(uniformMap[mapIndex].uniformName); + mUniformIndexMap[mapIndex].propertyValue = uniformMap[mapIndex].propertyPtr; + mUniformIndexMap[mapIndex].uniformIndex = program.RegisterUniform(uniformMap[mapIndex].uniformName); + mUniformIndexMap[mapIndex].uniformName = uniformMap[mapIndex].uniformName; + mUniformIndexMap[mapIndex].uniformNameHash = uniformMap[mapIndex].uniformNameHash; + mUniformIndexMap[mapIndex].uniformNameHashNoArray = uniformMap[mapIndex].uniformNameHashNoArray; + mUniformIndexMap[mapIndex].arrayIndex = uniformMap[mapIndex].arrayIndex; } for(uint32_t nodeMapIndex = 0; nodeMapIndex < uniformMapNode.Count(); ++nodeMapIndex) @@ -391,8 +413,12 @@ void Renderer::SetUniforms(BufferIndex bufferIndex, const SceneGraph::NodeDataPr if(!found) { - mUniformIndexMap[mapIndex].propertyValue = uniformMapNode[nodeMapIndex].propertyPtr; - mUniformIndexMap[mapIndex].uniformIndex = uniformIndex; + mUniformIndexMap[mapIndex].propertyValue = uniformMapNode[nodeMapIndex].propertyPtr; + mUniformIndexMap[mapIndex].uniformName = uniformMapNode[nodeMapIndex].uniformName; + mUniformIndexMap[mapIndex].uniformIndex = uniformIndex; + mUniformIndexMap[mapIndex].uniformNameHash = uniformMapNode[nodeMapIndex].uniformNameHash; + mUniformIndexMap[mapIndex].uniformNameHashNoArray = uniformMapNode[nodeMapIndex].uniformNameHashNoArray; + mUniformIndexMap[mapIndex].arrayIndex = uniformMapNode[nodeMapIndex].arrayIndex; ++mapIndex; } } @@ -400,6 +426,9 @@ void Renderer::SetUniforms(BufferIndex bufferIndex, const SceneGraph::NodeDataPr mUniformIndexMap.Resize(mapIndex); } + // The code below is disabled because the uniforms should now be set in the graphics backend. + + /* // Set uniforms in local map for(UniformIndexMappings::Iterator iter = mUniformIndexMap.Begin(), end = mUniformIndexMap.End(); @@ -414,6 +443,7 @@ void Renderer::SetUniforms(BufferIndex bufferIndex, const SceneGraph::NodeDataPr { program.SetSizeUniform3f(sizeLoc, size.x, size.y, size.z); } +*/ } void Renderer::SetUniformFromProperty(BufferIndex bufferIndex, Program& program, UniformIndexMap& map) @@ -784,6 +814,9 @@ void Renderer::Render(Context& conte { // Only set up and draw if we have textures and they are all valid + // The code below is disabled because the uniforms should now be set in the graphics backend. + + /* // set projection and view matrix if program has not yet received them yet this frame SetMatrices(*program, modelMatrix, viewMatrix, projectionMatrix, modelViewMatrix); @@ -802,8 +835,87 @@ void Renderer::Render(Context& conte program->SetUniform4f(loc, color.r, color.g, color.b, color.a * mRenderDataProvider->GetOpacity(bufferIndex)); } } +*/ + + BuildUniformIndexMap(bufferIndex, node, size, *program); + + // Create the UBO + uint32_t uniformBlockAllocationBytes{0u}; + uint32_t uniformBlockMaxSize{0u}; + uint32_t uboOffset{0u}; + + auto& reflection = mGraphicsController->GetProgramReflection(program->GetGraphicsProgram()); + for(auto i = 0u; i < reflection.GetUniformBlockCount(); ++i) + { + auto blockSize = GetUniformBufferDataAlignment(reflection.GetUniformBlockSize(i)); + if(uniformBlockMaxSize < blockSize) + { + uniformBlockMaxSize = blockSize; + } + uniformBlockAllocationBytes += blockSize; + } + + auto pagedAllocation = ((uniformBlockAllocationBytes / UBO_PAGE_SIZE + 1u)) * UBO_PAGE_SIZE; + + // Allocate twice memory as required by the uniform buffers + // todo: memory usage backlog to use optimal allocation + if(uniformBlockAllocationBytes && !mUniformBuffer[bufferIndex]) + { + mUniformBuffer[bufferIndex] = std::move(mUniformBufferManager->AllocateUniformBuffer(pagedAllocation)); + } + else if(uniformBlockAllocationBytes && (mUniformBuffer[bufferIndex]->GetSize() < pagedAllocation || + (pagedAllocation < uint32_t(float(mUniformBuffer[bufferIndex]->GetSize()) * UBO_SHRINK_THRESHOLD)))) + { + mUniformBuffer[bufferIndex]->Reserve(pagedAllocation); + } + + // Clear UBO + if(mUniformBuffer[bufferIndex]) + { + mUniformBuffer[bufferIndex]->Fill(0, 0u, 0u); + } + + // update the uniform buffer + // pass shared UBO and offset, return new offset for next item to be used + // don't process bindings if there are no uniform buffers allocated + auto ubo = mUniformBuffer[bufferIndex].get(); + if(ubo) + { + std::vector* bindings{nullptr}; + FillUniformBuffers(*program, instruction, *ubo, bindings, uboOffset, bufferIndex); + + Vector4 finalColor; + const Vector4& color = node.GetRenderColor(bufferIndex); + if(mPremultipledAlphaEnabled) + { + float alpha = color.a * mRenderDataProvider->GetOpacity(bufferIndex); + finalColor = Vector4(color.r * alpha, color.g * alpha, color.b * alpha, alpha); + } + else + { + finalColor = Vector4(color.r, color.g, color.b, color.a * mRenderDataProvider->GetOpacity(bufferIndex)); + } - SetUniforms(bufferIndex, node, size, *program); + // We know bindings for this renderer, so we can use 'offset' and write additional uniforms + Matrix modelViewProjectionMatrix(false); + Matrix::Multiply(modelViewProjectionMatrix, modelViewMatrix, projectionMatrix); + + WriteDefaultUniform(program->GetDefaultUniform(Program::DefaultUniformIndex::MODEL_MATRIX), *ubo, *bindings, modelMatrix); + WriteDefaultUniform(program->GetDefaultUniform(Program::DefaultUniformIndex::VIEW_MATRIX), *ubo, *bindings, viewMatrix); + WriteDefaultUniform(program->GetDefaultUniform(Program::DefaultUniformIndex::PROJECTION_MATRIX), *ubo, *bindings, projectionMatrix); + WriteDefaultUniform(program->GetDefaultUniform(Program::DefaultUniformIndex::MVP_MATRIX), *ubo, *bindings, modelViewProjectionMatrix); + WriteDefaultUniform(program->GetDefaultUniform(Program::DefaultUniformIndex::MODEL_VIEW_MATRIX), *ubo, *bindings, modelViewMatrix); + WriteDefaultUniform(program->GetDefaultUniform(Program::DefaultUniformIndex::SIZE), *ubo, *bindings, size); + WriteDefaultUniform(program->GetDefaultUniform(Program::DefaultUniformIndex::COLOR), *ubo, *bindings, finalColor); + + // Update normal matrix only when used in the shader + Matrix3 normalMatrix(modelViewMatrix); + normalMatrix.Invert(); + normalMatrix.Transpose(); + WriteDefaultUniform(program->GetDefaultUniform(Program::DefaultUniformIndex::NORMAL_MATRIX), *ubo, *bindings, normalMatrix); + + commandBuffer->BindUniformBuffers(*bindings); + } bool drawn = false; // Draw can fail if there are no vertex buffers or they haven't been uploaded yet // @todo We should detect this case much earlier to prevent unnecessary work @@ -838,6 +950,132 @@ void Renderer::Render(Context& conte } } +template +bool Renderer::WriteDefaultUniform(const Graphics::UniformInfo* uniformInfo, Render::UniformBuffer& ubo, const std::vector& bindings, const T& data) +{ + if(uniformInfo) + { + WriteUniform(ubo, bindings, *uniformInfo, data); + return true; + } + return false; +} + +template +void Renderer::WriteUniform(Render::UniformBuffer& ubo, const std::vector& bindings, const Graphics::UniformInfo& uniformInfo, const T& data) +{ + WriteUniform(ubo, bindings, uniformInfo, &data, sizeof(T)); +} + +void Renderer::WriteUniform(Render::UniformBuffer& ubo, const std::vector& bindings, const Graphics::UniformInfo& uniformInfo, const void* data, uint32_t size) +{ + ubo.Write(data, size, bindings[uniformInfo.bufferIndex].offset + uniformInfo.offset); +} + +void Renderer::FillUniformBuffers(Program& program, + const SceneGraph::RenderInstruction& instruction, + Render::UniformBuffer& ubo, + std::vector*& outBindings, + uint32_t& offset, + BufferIndex updateBufferIndex) +{ + auto& reflection = mGraphicsController->GetProgramReflection(program.GetGraphicsProgram()); + auto uboCount = reflection.GetUniformBlockCount(); + + mUniformBufferBindings.resize(uboCount); + + // Setup bindings + uint32_t dataOffset = offset; + for(auto i = 0u; i < uboCount; ++i) + { + mUniformBufferBindings[i].dataSize = reflection.GetUniformBlockSize(i); + mUniformBufferBindings[i].binding = reflection.GetUniformBlockBinding(i); + mUniformBufferBindings[i].offset = dataOffset; + + dataOffset += GetUniformBufferDataAlignment(mUniformBufferBindings[i].dataSize); + mUniformBufferBindings[i].buffer = ubo.GetBuffer(); + + for(UniformIndexMappings::Iterator iter = mUniformIndexMap.Begin(), + end = mUniformIndexMap.End(); + iter != end; + ++iter) + { + // @todo This means parsing the uniform string every frame. Instead, store the array index if present. + int arrayIndex = (*iter).arrayIndex; + + auto uniformInfo = Graphics::UniformInfo{}; + auto uniformFound = program.GetUniform((*iter).uniformName.GetCString(), + (*iter).uniformNameHashNoArray ? (*iter).uniformNameHashNoArray : (*iter).uniformNameHash, + uniformInfo); + + if(uniformFound) + { + auto dst = mUniformBufferBindings[uniformInfo.bufferIndex].offset + uniformInfo.offset; + + switch((*iter).propertyValue->GetType()) + { + case Property::Type::FLOAT: + case Property::Type::INTEGER: + case Property::Type::BOOLEAN: + { + ubo.Write(&(*iter).propertyValue->GetFloat(updateBufferIndex), + sizeof(float), + dst + static_cast(sizeof(Vector4)) * arrayIndex); + break; + } + case Property::Type::VECTOR2: + { + ubo.Write(&(*iter).propertyValue->GetVector2(updateBufferIndex), + sizeof(Vector2), + dst + static_cast(sizeof(Vector4)) * arrayIndex); + break; + } + case Property::Type::VECTOR3: + { + ubo.Write(&(*iter).propertyValue->GetVector3(updateBufferIndex), + sizeof(Vector3), + dst + static_cast(sizeof(Vector4)) * arrayIndex); + break; + } + case Property::Type::VECTOR4: + { + ubo.Write(&(*iter).propertyValue->GetVector4(updateBufferIndex), + sizeof(Vector4), + dst + static_cast(sizeof(Vector4)) * arrayIndex); + break; + } + case Property::Type::MATRIX: + { + ubo.Write(&(*iter).propertyValue->GetMatrix(updateBufferIndex), + sizeof(Matrix), + dst + static_cast(sizeof(Matrix)) * arrayIndex); + break; + } + case Property::Type::MATRIX3: + { + const auto& matrix = &(*iter).propertyValue->GetMatrix3(updateBufferIndex); + for(int i = 0; i < 3; ++i) + { + ubo.Write(&matrix->AsFloat()[i * 3], + sizeof(float) * 3, + dst + (i * static_cast(sizeof(Vector4)))); + } + break; + } + default: + { + } + } + } + } + } + // write output bindings + outBindings = &mUniformBufferBindings; + + // Update offset + offset = dataOffset; +} + void Renderer::SetSortAttributes(BufferIndex bufferIndex, SceneGraph::RenderInstructionProcessor::SortAttributes& sortAttributes) const { diff --git a/dali/internal/render/renderers/render-renderer.h b/dali/internal/render/renderers/render-renderer.h index 51fa74f..26b7a9e 100644 --- a/dali/internal/render/renderers/render-renderer.h +++ b/dali/internal/render/renderers/render-renderer.h @@ -26,12 +26,15 @@ #include #include #include +#include #include #include #include #include #include #include +#include +#include #include namespace Dali @@ -40,7 +43,6 @@ namespace Internal { class Context; class Texture; -class Program; class ProgramCache; namespace SceneGraph @@ -55,6 +57,7 @@ class RenderInstruction; //for relfection effect namespace Render { struct ShaderCache; +class UniformBufferManager; /** * Renderers are used to render meshes @@ -173,8 +176,9 @@ public: * @param[in] graphicsController The graphics controller to use * @param[in] programCache Cache of program objects * @param[in] shaderCache Cache of shaders + * @param[in] uniformBufferManager Uniform buffer manager */ - void Initialize(Context& context, Graphics::Controller& graphicsController, ProgramCache& programCache, Render::ShaderCache& shaderCache); + void Initialize(Context& context, Graphics::Controller& graphicsController, ProgramCache& programCache, Render::ShaderCache& shaderCache, Render::UniformBufferManager& uniformBufferManager); /** * Destructor @@ -405,6 +409,14 @@ public: */ bool Updated(BufferIndex bufferIndex, const SceneGraph::NodeDataProvider* node); + template + bool WriteDefaultUniform(const Graphics::UniformInfo* uniformInfo, Render::UniformBuffer& ubo, const std::vector& bindings, const T& data); + + template + void WriteUniform(Render::UniformBuffer& ubo, const std::vector& bindings, const Graphics::UniformInfo& uniformInfo, const T& data); + + void WriteUniform(Render::UniformBuffer& ubo, const std::vector& bindings, const Graphics::UniformInfo& uniformInfo, const void* data, uint32_t size); + private: struct UniformIndexMap; @@ -422,13 +434,13 @@ private: void SetBlending(Context& context, bool blend); /** - * Set the uniforms from properties according to the uniform map + * Builds a uniform map based on the index of the cached location in the Program. * @param[in] bufferIndex The index of the previous update buffer. * @param[in] node The node using the renderer * @param[in] size The size of the renderer * @param[in] program The shader program on which to set the uniforms. */ - void SetUniforms(BufferIndex bufferIndex, const SceneGraph::NodeDataProvider& node, const Vector3& size, Program& program); + void BuildUniformIndexMap(BufferIndex bufferIndex, const SceneGraph::NodeDataProvider& node, const Vector3& size, Program& program); /** * Set the program uniform in the map from the mapped property @@ -454,7 +466,23 @@ private: Program& program, const Dali::Internal::SceneGraph::RenderInstruction& instruction, bool blend, - Graphics::UniquePtr&& oldPipeline); + Graphics::UniquePtr&& oldPipeline); + + /** + * @brief Fill uniform buffer at index. Writes uniforms into given memory address + * + * @param[in] instruction The render instruction + * @param[in,out] ubo Target uniform buffer object + * @param[out] outBindings output bindings vector + * @param[out] offset output offset of the next uniform buffer memory address + * @param[in] updateBufferIndex update buffer index + */ + void FillUniformBuffers(Program& program, + const SceneGraph::RenderInstruction& instruction, + Render::UniformBuffer& ubo, + std::vector*& outBindings, + uint32_t& offset, + BufferIndex updateBufferIndex); private: Graphics::Controller* mGraphicsController; @@ -466,13 +494,21 @@ private: ProgramCache* mProgramCache{}; Render::ShaderCache* mShaderCache{}; + Render::UniformBufferManager* mUniformBufferManager{}; + std::vector mUniformBufferBindings{}; + Graphics::UniquePtr mGraphicsPipeline{}; ///< The graphics pipeline. (Cached implementation) std::vector mShaderStates{}; + using Hash = unsigned long; struct UniformIndexMap { - uint32_t uniformIndex; ///< The index of the cached location in the Program - const PropertyInputImpl* propertyValue; + uint32_t uniformIndex; ///< The index of the cached location in the Program + ConstString uniformName; ///< The uniform name + const PropertyInputImpl* propertyValue; ///< The property value + Hash uniformNameHash{0u}; + Hash uniformNameHashNoArray{0u}; + int32_t arrayIndex; ///< The array index }; using UniformIndexMappings = Dali::Vector; @@ -505,6 +541,9 @@ private: }; LegacyProgram mLegacyProgram; ///< The structure to pass the program ID into Graphics::PipelineCreateInfo + + using UniformBufferList = std::array, 2u>; + UniformBufferList mUniformBuffer{}; ///< The double-buffered uniform buffer }; } // namespace Render diff --git a/dali/internal/render/renderers/uniform-buffer-manager.cpp b/dali/internal/render/renderers/uniform-buffer-manager.cpp new file mode 100644 index 0000000..5b6a1ec --- /dev/null +++ b/dali/internal/render/renderers/uniform-buffer-manager.cpp @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "uniform-buffer-manager.h" +#include +#include + +#include +#include + +namespace Dali +{ +namespace Internal +{ +namespace Render +{ +UniformBuffer::UniformBuffer(Dali::Graphics::Controller* controller, + uint32_t sizeInBytes, + uint32_t alignment, + bool persistentMappedEnabled, + Graphics::BufferUsageFlags usageFlags) +: mController(controller), + mSize(0u), + mPersistentMappedEnabled(persistentMappedEnabled), + mUsageFlags(usageFlags) +{ + if(sizeInBytes) + { + Reserve(sizeInBytes); + } +} + +UniformBuffer::~UniformBuffer() +{ + if(mBuffer && mMemory) + { + mController->UnmapMemory(std::move(mMemory)); + } +} + +void UniformBuffer::Flush() +{ + if(mBuffer && mMemory) + { + mMemory->Flush(); + } +} + +void UniformBuffer::Reserve(uint32_t size) +{ + if(mBuffer && mMemory) + { + Unmap(); + mMemory = nullptr; + } + + mSize = size; + + Graphics::BufferCreateInfo createInfo; + createInfo.SetSize(mSize); + createInfo.SetUsage(mUsageFlags); + mBuffer = std::move(mController->CreateBuffer(createInfo, std::move(mBuffer))); + + mMapBufferInfo.buffer = mBuffer.get(); + mMapBufferInfo.usage = 0 | Graphics::MemoryUsageFlagBits::WRITE; + mMapBufferInfo.offset = 0; + mMapBufferInfo.size = size; + + if(mPersistentMappedEnabled) + { + Map(); + } +} + +void UniformBuffer::Fill(char data, uint32_t offset, uint32_t size) +{ + bool locallyMapped = (mMemory == nullptr); + if(locallyMapped) + { + Map(); + } + + if(mMemory) + { + void* ptr = mMemory->LockRegion(0, mSize); + + auto begin = (reinterpret_cast(ptr) + offset); + if(size == 0) + { + size = mSize - offset - 1; + } + auto end = begin + size; + std::fill(begin, end, data); + + mMemory->Unlock(true); + } + + if(locallyMapped) + { + Unmap(); + } +} + +void UniformBuffer::Write(const void* data, uint32_t size, uint32_t dstOffset) +{ + bool locallyMapped = (mMemory == nullptr); + if(locallyMapped) + { + Map(); + } + + if(mMemory) + { + void* ptr = mMemory->LockRegion(0, size); + if(dstOffset + size < mSize) + { + memcpy(reinterpret_cast(ptr) + dstOffset, data, size); + } + mMemory->Unlock(true); + } + + if(locallyMapped) + { + Unmap(); + } +} + +void UniformBuffer::Map() +{ + if(!mMemory) + { + mMemory = mController->MapBufferRange(mMapBufferInfo); + } +} + +void UniformBuffer::Unmap() +{ + if(mMemory) + { + mController->UnmapMemory(std::move(mMemory)); + } +} + +UniformBufferManager::UniformBufferManager(Dali::Graphics::Controller* controller) +: mController(controller) +{ +} + +UniformBufferManager::~UniformBufferManager() = default; + +Graphics::UniquePtr UniformBufferManager::AllocateUniformBuffer(uint32_t size) +{ + return Graphics::UniquePtr( + new UniformBuffer(mController, size, 256u, true, Dali::Graphics::BufferUsageFlags{0u} | Dali::Graphics::BufferUsage::TRANSFER_DST | Dali::Graphics::BufferUsage::UNIFORM_BUFFER)); +} + +} // namespace Render + +} // namespace Internal + +} // namespace Dali diff --git a/dali/internal/render/renderers/uniform-buffer-manager.h b/dali/internal/render/renderers/uniform-buffer-manager.h new file mode 100644 index 0000000..d7f3d60 --- /dev/null +++ b/dali/internal/render/renderers/uniform-buffer-manager.h @@ -0,0 +1,160 @@ +#ifndef DALI_INTERNAL_UNIFORM_BUFFER_MANAGER_H +#define DALI_INTERNAL_UNIFORM_BUFFER_MANAGER_H + +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +namespace Dali +{ +namespace Internal +{ +namespace Render +{ +class UniformBuffer +{ + friend class UniformBufferManager; + +private: + /** + * Constructor of UniformBuffer + * + * @param[in] mController Pointer of the graphics controller + * @param[in] sizeInBytes initial size of allocated buffer + * @param[in] alignment memory alignment in bytes + * @param[in] persistentMappingEnabled if true, buffer is mapped persistently + * @param[in] usageFlags type of usage ( Graphics::BufferUsage ) + */ + UniformBuffer(Dali::Graphics::Controller* mController, + uint32_t sizeInBytes, + uint32_t alignment, + bool persistentMappingEnabled, + Graphics::BufferUsageFlags usageFlags); + +public: + /** + * Destructor of UniformBuffer + */ + ~UniformBuffer(); + + /** + * Writes data into the buffer + * + * @param[in] data pointer to the source data + * @param[in] size size of source data + * @param[in] offset destination offset + */ + void Write(const void* data, uint32_t size, uint32_t offset); + + /** + * Flushes whole buffer range + */ + void Flush(); + + /** + * Returns allocated ( requested ) size + * @return size of buffer + */ + uint32_t GetSize() const + { + return mSize; + } + + /** + * Return Graphics API buffer + * @return pointer to the buffer object + */ + Dali::Graphics::Buffer* GetBuffer() const + { + return mBuffer.get(); + } + + /** + * Returns memory alignment + * @return memory alignment + */ + uint32_t GetAlignment() const + { + return mAlignment; + } + + /** + * Reserves buffer memory + * + * @param size requested size + */ + void Reserve(uint32_t size); + + /** + * Maps buffer memory + */ + void Map(); + + /** + * Unmaps buffer memory + */ + void Unmap(); + + /** + * Fills the buffer from the given offset with given data ( single 8bit value ) + * @param data char type data + * @param offset start offset + * @param size size to write, 0 if whole size + */ + void Fill(char data, uint32_t offset, uint32_t size); + +private: + Graphics::UniquePtr mBuffer; + Dali::Graphics::Controller* mController; + Graphics::UniquePtr mMemory{nullptr}; + + Graphics::MapBufferInfo mMapBufferInfo{}; + + uint32_t mCapacity{0}; ///< buffer capacity + uint32_t mSize; ///< buffer size + uint32_t mAlignment; + bool mPersistentMappedEnabled; + + Graphics::BufferUsageFlags mUsageFlags; +}; + +class UniformBufferManager +{ +public: + UniformBufferManager(Dali::Graphics::Controller* controller); + + ~UniformBufferManager(); + + /** + * Allocates uniform buffer with given size + * @param size + * @return + */ + Graphics::UniquePtr AllocateUniformBuffer(uint32_t size); + +private: + Dali::Graphics::Controller* mController; +}; + +} // namespace Render +} // namespace Internal +} // namespace Dali + +#endif // DALI_INTERNAL_UNIFORM_BUFFER_MANAGER_H diff --git a/dali/internal/render/shaders/program.cpp b/dali/internal/render/shaders/program.cpp index a6f6883..d62e421 100644 --- a/dali/internal/render/shaders/program.cpp +++ b/dali/internal/render/shaders/program.cpp @@ -21,10 +21,13 @@ // EXTERNAL INCLUDES #include #include +#include // INTERNAL INCLUDES +#include #include #include +#include #include #include #include @@ -35,6 +38,40 @@ #include #include +namespace +{ +void LogWithLineNumbers(const char* source) +{ + uint32_t lineNumber = 0u; + const char* prev = source; + const char* ptr = prev; + + while(true) + { + if(lineNumber > 200u) + { + break; + } + // seek the next end of line or end of text + while(*ptr != '\n' && *ptr != '\0') + { + ++ptr; + } + + std::string line(prev, ptr - prev); + Dali::Integration::Log::LogMessage(Dali::Integration::Log::DebugError, "%4d %s\n", lineNumber, line.c_str()); + + if(*ptr == '\0') + { + break; + } + prev = ++ptr; + ++lineNumber; + } +} + +} //namespace + namespace Dali { namespace Internal @@ -63,6 +100,20 @@ const char* const gStdUniforms[Program::UNIFORM_TYPE_LAST] = "uSize" // UNIFORM_SIZE }; +/** + * List of all default uniforms used for quicker lookup + */ +std::array DEFAULT_UNIFORM_HASHTABLE = + { + CalculateHash(std::string("uModelMatrix")), + CalculateHash(std::string("uMvpMatrix")), + CalculateHash(std::string("uViewMatrix")), + CalculateHash(std::string("uModelView")), + CalculateHash(std::string("uNormalMatrix")), + CalculateHash(std::string("uProjection")), + CalculateHash(std::string("uSize")), + CalculateHash(std::string("uColor"))}; + } // namespace // IMPLEMENTATION @@ -75,8 +126,9 @@ Program* Program::New(ProgramCache& cache, Internal::ShaderDataPtr shaderData, G // in order to maintain current functionality as long as needed gfxController.GetProgramParameter(*gfxProgram, 1, &programId); - size_t shaderHash = programId; - Program* program = cache.GetProgram(shaderHash); + size_t shaderHash = programId; + + Program* program = cache.GetProgram(shaderHash); if(nullptr == program) { @@ -85,6 +137,7 @@ Program* Program::New(ProgramCache& cache, Internal::ShaderDataPtr shaderData, G program->GetActiveSamplerUniforms(); cache.AddProgram(shaderHash, program); } + return program; } @@ -617,6 +670,8 @@ Program::Program(ProgramCache& cache, Internal::ShaderDataPtr shaderData, Graphi // Get program id and use it as hash for the cache // in order to maintain current functionality as long as needed mGfxController.GetProgramParameter(*mGfxProgram, 1, &mProgramId); + + BuildReflection(controller.GetProgramReflection(*mGfxProgram.get())); } Program::~Program() @@ -657,6 +712,123 @@ void Program::ResetAttribsUniformCache() } } +void Program::BuildReflection(const Graphics::Reflection& graphicsReflection) +{ + mReflectionDefaultUniforms.clear(); + mReflectionDefaultUniforms.resize(DEFAULT_UNIFORM_HASHTABLE.size()); + + auto uniformBlockCount = graphicsReflection.GetUniformBlockCount(); + + // add uniform block fields + for(auto i = 0u; i < uniformBlockCount; ++i) + { + Graphics::UniformBlockInfo uboInfo; + graphicsReflection.GetUniformBlock(i, uboInfo); + + // for each member store data + for(const auto& item : uboInfo.members) + { + auto hashValue = CalculateHash(item.name); + mReflection.emplace_back(ReflectionUniformInfo{hashValue, false, item}); + + // update buffer index + mReflection.back().uniformInfo.bufferIndex = i; + + // Update default uniforms + for(auto i = 0u; i < DEFAULT_UNIFORM_HASHTABLE.size(); ++i) + { + if(hashValue == DEFAULT_UNIFORM_HASHTABLE[i]) + { + mReflectionDefaultUniforms[i] = mReflection.back(); + break; + } + } + } + } + + // add samplers + auto samplers = graphicsReflection.GetSamplers(); + for(const auto& sampler : samplers) + { + mReflection.emplace_back(ReflectionUniformInfo{CalculateHash(sampler.name), false, sampler}); + } + + // check for potential collisions + std::map hashTest; + bool hasCollisions(false); + for(auto&& item : mReflection) + { + if(hashTest.find(item.hashValue) == hashTest.end()) + { + hashTest[item.hashValue] = false; + } + else + { + hashTest[item.hashValue] = true; + hasCollisions = true; + } + } + + // update collision flag for further use + if(hasCollisions) + { + for(auto&& item : mReflection) + { + item.hasCollision = hashTest[item.hashValue]; + } + } +} + +bool Program::GetUniform(const std::string& name, size_t hashedName, Graphics::UniformInfo& out) const +{ + if(mReflection.empty()) + { + return false; + } + + hashedName = !hashedName ? CalculateHash(name) : hashedName; + + for(const ReflectionUniformInfo& item : mReflection) + { + if(item.hashValue == hashedName) + { + if(!item.hasCollision || item.uniformInfo.name == name) + { + out = item.uniformInfo; + return true; + } + else + { + return false; + } + } + } + return false; +} + +bool Program::GetDefaultUniform(DefaultUniformIndex defaultUniformIndex, Graphics::UniformInfo& out) const +{ + if(mReflectionDefaultUniforms.empty()) + { + return false; + } + + auto& value = mReflectionDefaultUniforms[static_cast(defaultUniformIndex)]; + out = value.uniformInfo; + return true; +} + +const Graphics::UniformInfo* Program::GetDefaultUniform(DefaultUniformIndex defaultUniformIndex) const +{ + if(mReflectionDefaultUniforms.empty()) + { + return nullptr; + } + + const auto value = &mReflectionDefaultUniforms[static_cast(defaultUniformIndex)]; + return &value->uniformInfo; +} + } // namespace Internal } // namespace Dali diff --git a/dali/internal/render/shaders/program.h b/dali/internal/render/shaders/program.h index d26fcc5..cb50f17 100644 --- a/dali/internal/render/shaders/program.h +++ b/dali/internal/render/shaders/program.h @@ -35,7 +35,9 @@ namespace Graphics { class Controller; class Program; +class Reflection; } // namespace Graphics + class Matrix; namespace Integration @@ -102,6 +104,21 @@ public: }; /** + * Indices of default uniforms + */ + enum class DefaultUniformIndex + { + MODEL_MATRIX = 0, + MVP_MATRIX, + VIEW_MATRIX, + MODEL_VIEW_MATRIX, + NORMAL_MATRIX, + PROJECTION_MATRIX, + SIZE, + COLOR + }; + + /** * Creates a new program, or returns a copy of an existing program in the program cache * @param[in] cache where the programs are stored * @param[in] shaderData A pointer to a data structure containing the program source @@ -318,6 +335,34 @@ public: return *mGfxProgram; } + /** + * Retrieves uniform data. + * The lookup tries to minimise string comparisons. Ideally, when the hashedName is known + * and there are no hash collisions in the reflection it's the most optimal case. + * + * @param name Name of uniform + * @param hashedName Hash value from name or 0 if unknown + * @param out Reference to output structure + * + * @return False when uniform is not found or due to hash collision the result is ambiguous + */ + bool GetUniform(const std::string& name, size_t hashedName, Graphics::UniformInfo& out) const; + + /** + * Retrieves default uniform + * @param[in] defaultUniformIndex index of the uniform + * @param[out] outputUniformInfo the reference to UniformInfo object + * @return True is uniform found, false otherwise + */ + bool GetDefaultUniform(DefaultUniformIndex defaultUniformIndex, Graphics::UniformInfo& outputUniformInfo) const; + + /** + * Retrievs default uniform + * @param[in] defaultUniformIndex index of the uniform + * @return Valid pointer to the UniformInfo object or nullptr + */ + const Graphics::UniformInfo* GetDefaultUniform(DefaultUniformIndex defaultUniformIndex) const; + private: // Implementation /** * Constructor, private so no direct instantiation @@ -345,6 +390,23 @@ private: */ void ResetAttribsUniformCache(); + /** + * Struct ReflectionUniformInfo + * Contains details of a single uniform buffer field and/or sampler. + */ + struct ReflectionUniformInfo + { + size_t hashValue{0}; + bool hasCollision{false}; + Graphics::UniformInfo uniformInfo{}; + }; + + /** + * Build optimized shader reflection of uniforms + * @param graphicsReflection The graphics reflection + */ + void BuildReflection(const Graphics::Reflection& graphicsReflection); + private: // Data ProgramCache& mCache; ///< The program cache Integration::GlAbstraction& mGlAbstraction; ///< The OpenGL Abstraction layer @@ -370,6 +432,11 @@ private: // Data GLfloat mUniformCacheFloat4[MAX_UNIFORM_CACHE_SIZE][4]; ///< Value cache for uniforms of four floats Vector3 mSizeUniformCache; ///< Cache value for size uniform bool mModifiesGeometry; ///< True if the program changes geometry + + using UniformReflectionContainer = std::vector; + + UniformReflectionContainer mReflection{}; ///< Contains reflection build per program + UniformReflectionContainer mReflectionDefaultUniforms{}; ///< Contains default uniforms }; } // namespace Internal diff --git a/dali/internal/update/common/uniform-map.h b/dali/internal/update/common/uniform-map.h index 8080c82..d20782e 100644 --- a/dali/internal/update/common/uniform-map.h +++ b/dali/internal/update/common/uniform-map.h @@ -40,19 +40,40 @@ namespace SceneGraph class UniformPropertyMapping { public: + using Hash = unsigned long; + /** * Constructor */ UniformPropertyMapping(ConstString theUniformName, const PropertyInputImpl* thePropertyPtr) : propertyPtr(thePropertyPtr), - uniformName(theUniformName) + uniformName(theUniformName), + uniformNameHash(0u), + uniformNameHashNoArray(0u), + arrayIndex(0u) { + // Look for array index closing bracket + auto pos = theUniformName.GetStringView().rfind("]"); + + // If found, extract the array index and store it + if(pos != std::string::npos) + { + auto pos0 = theUniformName.GetStringView().rfind("[", pos); + arrayIndex = atoi(theUniformName.GetCString() + pos0 + 1); + // Calculate hash from name without array index + uniformNameHashNoArray = Dali::CalculateHash(theUniformName.GetStringView().substr(0, pos0).data()); + } + uniformName = theUniformName; + uniformNameHash = Dali::CalculateHash(theUniformName.GetCString()); } UniformPropertyMapping() = default; const PropertyInputImpl* propertyPtr{nullptr}; - ConstString uniformName; + ConstString uniformName{}; + Hash uniformNameHash{0u}; + Hash uniformNameHashNoArray{0u}; + int32_t arrayIndex{0u}; }; /** -- 2.7.4