Create UBO for uniform buffer bindings 91/254291/12
authorRichard Huang <r.huang@samsung.com>
Thu, 25 Feb 2021 16:42:13 +0000 (16:42 +0000)
committerDavid Steele <david.steele@samsung.com>
Mon, 8 Mar 2021 16:30:53 +0000 (16:30 +0000)
Change-Id: Ie863070d46b92b4ce135e5eba87172641e13bd8b

dali/internal/file.list
dali/internal/render/common/render-manager.cpp
dali/internal/render/renderers/render-renderer.cpp
dali/internal/render/renderers/render-renderer.h
dali/internal/render/renderers/uniform-buffer-manager.cpp [new file with mode: 0644]
dali/internal/render/renderers/uniform-buffer-manager.h [new file with mode: 0644]
dali/internal/render/shaders/program.cpp
dali/internal/render/shaders/program.h
dali/internal/update/common/uniform-map.h

index eff7556..689d591 100644 (file)
@@ -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
index c519a5a..8c60e9b 100644 (file)
@@ -39,6 +39,7 @@
 #include <dali/internal/render/renderers/render-frame-buffer.h>
 #include <dali/internal/render/renderers/render-texture.h>
 #include <dali/internal/render/renderers/shader-cache.h>
+#include <dali/internal/render/renderers/uniform-buffer-manager.h>
 #include <dali/internal/render/shaders/program-controller.h>
 
 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<Dali::ThreadPool>(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<Render::UniformBufferManager> 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<Render::Renderer>& 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());
 }
index 4570797..3bba444 100644 (file)
@@ -32,6 +32,7 @@
 #include <dali/internal/render/renderers/shader-cache.h>
 #include <dali/internal/render/shaders/program.h>
 #include <dali/internal/render/shaders/scene-graph-shader.h>
+#include <dali/internal/update/common/uniform-map.h>
 
 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<Graphics::UniformBufferBinding>* 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<class T>
+bool Renderer::WriteDefaultUniform(const Graphics::UniformInfo* uniformInfo, Render::UniformBuffer& ubo, const std::vector<Graphics::UniformBufferBinding>& bindings, const T& data)
+{
+  if(uniformInfo)
+  {
+    WriteUniform(ubo, bindings, *uniformInfo, data);
+    return true;
+  }
+  return false;
+}
+
+template<class T>
+void Renderer::WriteUniform(Render::UniformBuffer& ubo, const std::vector<Graphics::UniformBufferBinding>& bindings, const Graphics::UniformInfo& uniformInfo, const T& data)
+{
+  WriteUniform(ubo, bindings, uniformInfo, &data, sizeof(T));
+}
+
+void Renderer::WriteUniform(Render::UniformBuffer& ubo, const std::vector<Graphics::UniformBufferBinding>& 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<Graphics::UniformBufferBinding>*& 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<uint32_t>(sizeof(Vector4)) * arrayIndex);
+            break;
+          }
+          case Property::Type::VECTOR2:
+          {
+            ubo.Write(&(*iter).propertyValue->GetVector2(updateBufferIndex),
+                      sizeof(Vector2),
+                      dst + static_cast<uint32_t>(sizeof(Vector4)) * arrayIndex);
+            break;
+          }
+          case Property::Type::VECTOR3:
+          {
+            ubo.Write(&(*iter).propertyValue->GetVector3(updateBufferIndex),
+                      sizeof(Vector3),
+                      dst + static_cast<uint32_t>(sizeof(Vector4)) * arrayIndex);
+            break;
+          }
+          case Property::Type::VECTOR4:
+          {
+            ubo.Write(&(*iter).propertyValue->GetVector4(updateBufferIndex),
+                      sizeof(Vector4),
+                      dst + static_cast<uint32_t>(sizeof(Vector4)) * arrayIndex);
+            break;
+          }
+          case Property::Type::MATRIX:
+          {
+            ubo.Write(&(*iter).propertyValue->GetMatrix(updateBufferIndex),
+                      sizeof(Matrix),
+                      dst + static_cast<uint32_t>(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<uint32_t>(sizeof(Vector4))));
+            }
+            break;
+          }
+          default:
+          {
+          }
+        }
+      }
+    }
+  }
+  // write output bindings
+  outBindings = &mUniformBufferBindings;
+
+  // Update offset
+  offset = dataOffset;
+}
+
 void Renderer::SetSortAttributes(BufferIndex                                             bufferIndex,
                                  SceneGraph::RenderInstructionProcessor::SortAttributes& sortAttributes) const
 {
index 51fa74f..26b7a9e 100644 (file)
 #include <dali/graphics-api/graphics-controller.h>
 #include <dali/integration-api/debug.h>
 #include <dali/internal/common/blending-options.h>
+#include <dali/internal/common/const-string.h>
 #include <dali/internal/common/message.h>
 #include <dali/internal/common/type-abstraction-enums.h>
 #include <dali/internal/event/common/property-input-impl.h>
 #include <dali/internal/render/data-providers/render-data-provider.h>
 #include <dali/internal/render/gl-resources/gl-resource-owner.h>
 #include <dali/internal/render/renderers/render-geometry.h>
+#include <dali/internal/render/renderers/uniform-buffer-manager.h>
+#include <dali/internal/render/shaders/program.h>
 #include <dali/internal/update/manager/render-instruction-processor.h>
 
 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<class T>
+  bool WriteDefaultUniform(const Graphics::UniformInfo* uniformInfo, Render::UniformBuffer& ubo, const std::vector<Graphics::UniformBufferBinding>& bindings, const T& data);
+
+  template<class T>
+  void WriteUniform(Render::UniformBuffer& ubo, const std::vector<Graphics::UniformBufferBinding>& bindings, const Graphics::UniformInfo& uniformInfo, const T& data);
+
+  void WriteUniform(Render::UniformBuffer& ubo, const std::vector<Graphics::UniformBufferBinding>& 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<Graphics::Pipeline>&& oldPipeline);
+    Graphics::UniquePtr<Graphics::Pipeline>&&            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<Graphics::UniformBufferBinding>*& 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<Graphics::UniformBufferBinding> mUniformBufferBindings{};
+
   Graphics::UniquePtr<Graphics::Pipeline> mGraphicsPipeline{}; ///< The graphics pipeline. (Cached implementation)
   std::vector<Graphics::ShaderState>      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<UniformIndexMap>;
@@ -505,6 +541,9 @@ private:
   };
 
   LegacyProgram mLegacyProgram; ///< The structure to pass the program ID into Graphics::PipelineCreateInfo
+
+  using UniformBufferList = std::array<Graphics::UniquePtr<Render::UniformBuffer>, 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 (file)
index 0000000..5b6a1ec
--- /dev/null
@@ -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 <dali/graphics-api/graphics-buffer-create-info.h>
+#include <dali/graphics-api/graphics-buffer.h>
+
+#include <cstring>
+#include <memory>
+
+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<char*>(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<char*>(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<UniformBuffer> UniformBufferManager::AllocateUniformBuffer(uint32_t size)
+{
+  return Graphics::UniquePtr<UniformBuffer>(
+    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 (file)
index 0000000..d7f3d60
--- /dev/null
@@ -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 <dali/graphics-api/graphics-controller.h>
+
+#include <memory>
+
+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<Graphics::Buffer> mBuffer;
+  Dali::Graphics::Controller*           mController;
+  Graphics::UniquePtr<Graphics::Memory> 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<UniformBuffer> AllocateUniformBuffer(uint32_t size);
+
+private:
+  Dali::Graphics::Controller* mController;
+};
+
+} // namespace Render
+} // namespace Internal
+} // namespace Dali
+
+#endif // DALI_INTERNAL_UNIFORM_BUFFER_MANAGER_H
index a6f6883..d62e421 100644 (file)
 // EXTERNAL INCLUDES
 #include <cstring>
 #include <iomanip>
+#include <map>
 
 // INTERNAL INCLUDES
+#include <dali/devel-api/common/hash.h>
 #include <dali/graphics-api/graphics-controller.h>
 #include <dali/graphics-api/graphics-program.h>
+#include <dali/graphics-api/graphics-reflection.h>
 #include <dali/integration-api/debug.h>
 #include <dali/integration-api/gl-defines.h>
 #include <dali/internal/common/shader-data.h>
 #include <dali/public-api/common/dali-common.h>
 #include <dali/public-api/common/dali-vector.h>
 
+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<size_t, 8> 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<size_t, bool> 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<uint32_t>(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<uint32_t>(defaultUniformIndex)];
+  return &value->uniformInfo;
+}
+
 } // namespace Internal
 
 } // namespace Dali
index d26fcc5..cb50f17 100644 (file)
@@ -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<ReflectionUniformInfo>;
+
+  UniformReflectionContainer mReflection{};                ///< Contains reflection build per program
+  UniformReflectionContainer mReflectionDefaultUniforms{}; ///< Contains default uniforms
 };
 
 } // namespace Internal
index 8080c82..d20782e 100644 (file)
@@ -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};
 };
 
 /**