Managed uniform buffer support. 27/257327/17 devel/graphics
authorAdam Bialogonski <adam.b@samsung.com>
Mon, 17 May 2021 09:50:59 +0000 (10:50 +0100)
committerAdam Bialogonski <adam.b@samsung.com>
Tue, 25 May 2021 18:58:52 +0000 (19:58 +0100)
Single uniform buffer is created in order to store uniform data for all rendered items.

- UniformBuffer is made of one or many Graphics::Buffer objects
- Memory of UniformBuffer is continuous
- UniformBuffer may resize if needed
- UniformBufferView is now used to access uniform data for individual items
- UniformBUfferPoolView manages memory allocation using stack/pool allocation strategy
- UniformBufferPoolView lives only through a single frame and then rolls back all memory

Change-Id: I2f1d2a007a132a7745b6927912353f57a96be791

16 files changed:
automated-tests/src/dali/dali-test-suite-utils/test-graphics-buffer.cpp
automated-tests/src/dali/dali-test-suite-utils/test-graphics-buffer.h
automated-tests/src/dali/dali-test-suite-utils/test-graphics-controller.cpp
automated-tests/src/dali/utc-Dali-VertexBuffer.cpp
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
dali/internal/render/renderers/uniform-buffer-manager.h
dali/internal/render/renderers/uniform-buffer-view-pool.cpp [new file with mode: 0644]
dali/internal/render/renderers/uniform-buffer-view-pool.h [new file with mode: 0644]
dali/internal/render/renderers/uniform-buffer-view.cpp [new file with mode: 0644]
dali/internal/render/renderers/uniform-buffer-view.h [new file with mode: 0644]
dali/internal/render/renderers/uniform-buffer.cpp [new file with mode: 0644]
dali/internal/render/renderers/uniform-buffer.h [new file with mode: 0644]

index d8a1b1a..fb06c3d 100644 (file)
@@ -80,14 +80,15 @@ GLenum TestGraphicsBuffer::GetTarget()
   return target;
 }
 
-void TestGraphicsBuffer::BindAsUniformBuffer(const TestGraphicsProgram* program) const
+void TestGraphicsBuffer::BindAsUniformBuffer(const TestGraphicsProgram* program, const Dali::UniformBufferBindingDescriptor& uboBinding) const
 {
   auto* reflection = static_cast<const TestGraphicsReflection*>(&program->GetReflection());
 
   Graphics::UniformBlockInfo uboInfo{};
   reflection->GetUniformBlock(0, uboInfo);
 
-  auto* data = memory.data();
+  auto offset = uboBinding.offset;
+  auto* data = memory.data() + offset;
 
   for(const auto& member : uboInfo.members)
   {
index 87c9f87..1dc2715 100644 (file)
@@ -26,6 +26,7 @@
 namespace Dali
 {
 class TestGraphicsProgram;
+class UniformBufferBindingDescriptor;
 class TestGraphicsBuffer : public Graphics::Buffer
 {
 public:
@@ -40,7 +41,7 @@ public:
     return true;
   }
 
-  void BindAsUniformBuffer(const TestGraphicsProgram* program) const;
+  void BindAsUniformBuffer(const TestGraphicsProgram* program, const Dali::UniformBufferBindingDescriptor& uboBinding) const;
 
   TraceCallStack&            mCallStack;
   TestGlAbstraction&         mGl;
index 517463f..cbdd5dd 100644 (file)
@@ -604,7 +604,7 @@ void TestGraphicsController::ProcessCommandBuffer(TestGraphicsCommandBuffer& com
         auto  buffer   = bindings.standaloneUniformsBufferBinding;
 
         // based on reflection, issue gl calls
-        buffer.buffer->BindAsUniformBuffer(static_cast<const TestGraphicsProgram*>(currentPipeline->programState.program));
+        buffer.buffer->BindAsUniformBuffer(static_cast<const TestGraphicsProgram*>(currentPipeline->programState.program), bindings.standaloneUniformsBufferBinding);
         break;
       }
       case CommandType::BIND_SAMPLERS:
index 1f286aa..346c6b1 100644 (file)
@@ -262,8 +262,8 @@ int UtcDaliVertexBufferSetData02(void)
     const TestGlAbstraction::BufferDataCalls& bufferDataCalls =
       application.GetGlAbstraction().GetBufferDataCalls();
 
-    // test below includes updates of uniform buffers
-    DALI_TEST_EQUALS(bufferSubDataCalls.size(), 17u, TEST_LOCATION);
+    // Should be 15 (using single uniform buffer now)
+    DALI_TEST_EQUALS(bufferSubDataCalls.size(), 15u, TEST_LOCATION);
     DALI_TEST_EQUALS(bufferDataCalls.size(), 3u, TEST_LOCATION);
 
     if(bufferSubDataCalls.size())
index 7f4afe7..3ebaab1 100644 (file)
@@ -116,7 +116,10 @@ SET( internal_src_files
   ${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.cpp
   ${internal_src_dir}/render/renderers/uniform-buffer-manager.cpp
+  ${internal_src_dir}/render/renderers/uniform-buffer-view.cpp
+  ${internal_src_dir}/render/renderers/uniform-buffer-view-pool.cpp
 
   ${internal_src_dir}/render/shaders/program.cpp
   ${internal_src_dir}/render/shaders/program-controller.cpp
index 75dba9e..9263260 100644 (file)
 #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/renderers/uniform-buffer-view-pool.h>
 #include <dali/internal/render/shaders/program-controller.h>
 
+#include <dali/internal/render/renderers/uniform-buffer-manager.h>
+
 namespace Dali
 {
 namespace Internal
@@ -391,6 +394,9 @@ void RenderManager::PreRender(Integration::RenderStatus& status, bool forceClear
 {
   DALI_PRINT_RENDER_START(mImpl->renderBufferIndex);
 
+  // Rollback
+  mImpl->uniformBufferManager->GetUniformBufferViewPool(mImpl->renderBufferIndex)->Rollback();
+
   // Increment the frame count at the beginning of each frame
   ++mImpl->frameCount;
 
index 4a06a0b..711eef5 100644 (file)
@@ -19,7 +19,6 @@
 #include <dali/internal/render/renderers/render-renderer.h>
 
 // INTERNAL INCLUDES
-#include <dali/graphics-api/graphics-program.h>
 #include <dali/graphics-api/graphics-types.h>
 #include <dali/integration-api/debug.h>
 #include <dali/internal/common/image-sampler.h>
 #include <dali/internal/render/shaders/program.h>
 #include <dali/internal/render/shaders/render-shader.h>
 #include <dali/internal/update/common/uniform-map.h>
+#include <dali/internal/render/renderers/uniform-buffer-view.h>
+#include <dali/internal/render/renderers/uniform-buffer-view-pool.h>
 
-namespace Dali
-{
-namespace Internal
+namespace Dali::Internal
 {
 namespace
 {
-// Size of uniform buffer page used when resizing
-constexpr uint32_t UBO_PAGE_SIZE = 64;
-
-// UBO allocation threshold below which the UBO will shrink
-constexpr auto UBO_SHRINK_THRESHOLD = 0.75f;
-
 // Helper to get the vertex input format
 Dali::Graphics::VertexInputFormat GetPropertyVertexFormat(Property::Type propertyType)
 {
@@ -681,49 +674,39 @@ void Renderer::WriteUniformBuffer(
     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] = mUniformBufferManager->AllocateUniformBuffer(pagedAllocation);
-  }
-  else if(uniformBlockAllocationBytes && (mUniformBuffer[bufferIndex]->GetSize() < pagedAllocation ||
-                                          (pagedAllocation < uint32_t(float(mUniformBuffer[bufferIndex]->GetSize()) * UBO_SHRINK_THRESHOLD))))
+  // Create uniform buffer view from uniform buffer
+  Graphics::UniquePtr<Render::UniformBufferView> uboView {nullptr};
+  if(uniformBlockAllocationBytes)
   {
-    mUniformBuffer[bufferIndex]->Reserve(pagedAllocation);
-  }
+    auto uboPoolView = mUniformBufferManager->GetUniformBufferViewPool( bufferIndex );
 
-  // Clear UBO
-  if(mUniformBuffer[bufferIndex])
-  {
-    mUniformBuffer[bufferIndex]->Fill(0, 0u, 0u);
+    uboView = uboPoolView->CreateUniformBufferView( uniformBlockAllocationBytes );
   }
 
   // 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)
+  if(uboView)
   {
     auto uboCount = reflection.GetUniformBlockCount();
     mUniformBufferBindings.resize(uboCount);
 
     std::vector<Graphics::UniformBufferBinding>* bindings{&mUniformBufferBindings};
 
+    mUniformBufferBindings[0].buffer = uboView->GetBuffer( &mUniformBufferBindings[0].offset );
+
     // Write default uniforms
-    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::MODEL_VIEW_MATRIX), *ubo, *bindings, modelViewMatrix);
+    WriteDefaultUniform(program->GetDefaultUniform(Program::DefaultUniformIndex::MODEL_MATRIX), *uboView, *bindings, modelMatrix);
+    WriteDefaultUniform(program->GetDefaultUniform(Program::DefaultUniformIndex::VIEW_MATRIX), *uboView, *bindings, viewMatrix);
+    WriteDefaultUniform(program->GetDefaultUniform(Program::DefaultUniformIndex::PROJECTION_MATRIX), *uboView, *bindings, projectionMatrix);
+    WriteDefaultUniform(program->GetDefaultUniform(Program::DefaultUniformIndex::MODEL_VIEW_MATRIX), *uboView, *bindings, modelViewMatrix);
 
     auto mvpUniformInfo = program->GetDefaultUniform(Program::DefaultUniformIndex::MVP_MATRIX);
     if(mvpUniformInfo && !mvpUniformInfo->name.empty())
     {
       Matrix modelViewProjectionMatrix(false);
       Matrix::Multiply(modelViewProjectionMatrix, modelViewMatrix, projectionMatrix);
-      WriteDefaultUniform(mvpUniformInfo, *ubo, *bindings, modelViewProjectionMatrix);
+      WriteDefaultUniform(mvpUniformInfo, *uboView, *bindings, modelViewProjectionMatrix);
     }
 
     auto normalUniformInfo = program->GetDefaultUniform(Program::DefaultUniformIndex::NORMAL_MATRIX);
@@ -732,7 +715,7 @@ void Renderer::WriteUniformBuffer(
       Matrix3 normalMatrix(modelViewMatrix);
       normalMatrix.Invert();
       normalMatrix.Transpose();
-      WriteDefaultUniform(normalUniformInfo, *ubo, *bindings, normalMatrix);
+      WriteDefaultUniform(normalUniformInfo, *uboView, *bindings, normalMatrix);
     }
 
     Vector4        finalColor;
@@ -746,20 +729,20 @@ void Renderer::WriteUniformBuffer(
     {
       finalColor = Vector4(color.r, color.g, color.b, color.a * mRenderDataProvider->GetOpacity(bufferIndex));
     }
-    WriteDefaultUniform(program->GetDefaultUniform(Program::DefaultUniformIndex::COLOR), *ubo, *bindings, finalColor);
+    WriteDefaultUniform(program->GetDefaultUniform(Program::DefaultUniformIndex::COLOR), *uboView, *bindings, finalColor);
 
     // Write uniforms from the uniform map
-    FillUniformBuffer(*program, instruction, *ubo, bindings, uboOffset, bufferIndex);
+    FillUniformBuffer(*program, instruction, *uboView, bindings, uboOffset, bufferIndex);
 
     // Write uSize in the end, as it shouldn't be overridable by dynamic properties.
-    WriteDefaultUniform(program->GetDefaultUniform(Program::DefaultUniformIndex::SIZE), *ubo, *bindings, size);
+    WriteDefaultUniform(program->GetDefaultUniform(Program::DefaultUniformIndex::SIZE), *uboView, *bindings, size);
 
     commandBuffer.BindUniformBuffers(*bindings);
   }
 }
 
 template<class T>
-bool Renderer::WriteDefaultUniform(const Graphics::UniformInfo* uniformInfo, Render::UniformBuffer& ubo, const std::vector<Graphics::UniformBufferBinding>& bindings, const T& data)
+bool Renderer::WriteDefaultUniform(const Graphics::UniformInfo* uniformInfo, Render::UniformBufferView& ubo, const std::vector<Graphics::UniformBufferBinding>& bindings, const T& data)
 {
   if(uniformInfo && !uniformInfo->name.empty())
   {
@@ -770,19 +753,19 @@ bool Renderer::WriteDefaultUniform(const Graphics::UniformInfo* uniformInfo, Ren
 }
 
 template<class T>
-void Renderer::WriteUniform(Render::UniformBuffer& ubo, const std::vector<Graphics::UniformBufferBinding>& bindings, const Graphics::UniformInfo& uniformInfo, const T& data)
+void Renderer::WriteUniform(Render::UniformBufferView& 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)
+void Renderer::WriteUniform(Render::UniformBufferView& 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);
+  ubo.Write(data, size, ubo.GetOffset() + uniformInfo.offset);
 }
 
 void Renderer::FillUniformBuffer(Program&                                      program,
                                  const SceneGraph::RenderInstruction&          instruction,
-                                 Render::UniformBuffer&                        ubo,
+                                 Render::UniformBufferView&                    ubo,
                                  std::vector<Graphics::UniformBufferBinding>*& outBindings,
                                  uint32_t&                                     offset,
                                  BufferIndex                                   updateBufferIndex)
@@ -796,10 +779,9 @@ void Renderer::FillUniformBuffer(Program&                                      p
   {
     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();
+    mUniformBufferBindings[i].buffer = ubo.GetBuffer( &mUniformBufferBindings[i].offset );
 
     for(UniformIndexMappings::Iterator iter = mUniformIndexMap.Begin(),
                                        end  = mUniformIndexMap.End();
@@ -817,8 +799,7 @@ void Renderer::FillUniformBuffer(Program&                                      p
 
       if(uniformFound)
       {
-        auto dst = mUniformBufferBindings[uniformInfo.bufferIndex].offset + uniformInfo.offset;
-
+        auto dst = ubo.GetOffset() + uniformInfo.offset;
         switch((*iter).propertyValue->GetType())
         {
           case Property::Type::BOOLEAN:
@@ -1154,6 +1135,4 @@ Graphics::Pipeline& Renderer::PrepareGraphicsPipeline(
 
 } // namespace Render
 
-} // namespace Internal
-
 } // namespace Dali
index 62b82f7..205397b 100644 (file)
@@ -400,17 +400,17 @@ public:
 
   template<class T>
   bool WriteDefaultUniform(const Graphics::UniformInfo*                       uniformInfo,
-                           Render::UniformBuffer&                             ubo,
+                           Render::UniformBufferView&                             ubo,
                            const std::vector<Graphics::UniformBufferBinding>& bindings,
                            const T&                                           data);
 
   template<class T>
-  void WriteUniform(Render::UniformBuffer&                             ubo,
+  void WriteUniform(Render::UniformBufferView&                             ubo,
                     const std::vector<Graphics::UniformBufferBinding>& bindings,
                     const Graphics::UniformInfo&                       uniformInfo,
                     const T&                                           data);
 
-  void WriteUniform(Render::UniformBuffer&                             ubo,
+  void WriteUniform(Render::UniformBufferView&                             ubo,
                     const std::vector<Graphics::UniformBufferBinding>& bindings,
                     const Graphics::UniformInfo&                       uniformInfo,
                     const void*                                        data,
@@ -490,7 +490,7 @@ private:
    */
   void FillUniformBuffer(Program&                                      program,
                          const SceneGraph::RenderInstruction&          instruction,
-                         Render::UniformBuffer&                        ubo,
+                         Render::UniformBufferView&                        ubo,
                          std::vector<Graphics::UniformBufferBinding>*& outBindings,
                          uint32_t&                                     offset,
                          BufferIndex                                   updateBufferIndex);
@@ -550,8 +550,6 @@ private:
   bool                  mUpdated : 1;
 
   std::vector<Dali::DevelRenderer::DrawCommand> mDrawCommands; // Devel stuff
-
-  Graphics::UniquePtr<Render::UniformBuffer> mUniformBuffer[2]{nullptr, nullptr}; ///< The double-buffered uniform buffer
 };
 
 } // namespace Render
index 828cfae..8342a21 100644 (file)
  *
  */
 
-#include "uniform-buffer-manager.h"
+// CLASS HEADER
+#include <dali/internal/render/renderers/uniform-buffer-manager.h>
+
+// INTERNAL INCLUDES
+#include <dali/internal/render/renderers/uniform-buffer.h>
+#include <dali/internal/render/renderers/uniform-buffer-view.h>
+#include <dali/internal/render/renderers/uniform-buffer-view-pool.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()
+namespace Dali::Internal::Render
 {
-  Flush();
-  Unmap();
-}
 
-void UniformBuffer::Flush()
+UniformBufferManager::UniformBufferManager(Dali::Graphics::Controller* controller)
+: mController(controller)
 {
-  if(mBuffer && mMemory)
-  {
-    mMemory->Flush();
-  }
 }
 
-void UniformBuffer::Reserve(uint32_t size)
-{
-  if(mBuffer && mMemory)
-  {
-    Unmap();
-    mMemory = nullptr;
-  }
-
-  mSize = size;
-
-  auto createInfo = Graphics::BufferCreateInfo()
-                      .SetSize(mSize)
-                      .SetBufferPropertiesFlags(0 | Graphics::BufferPropertiesFlagBit::CPU_ALLOCATED)
-                      .SetUsage(mUsageFlags);
-
-  mBuffer = 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();
-  }
-}
+UniformBufferManager::~UniformBufferManager() = default;
 
-void UniformBuffer::Fill(char data, uint32_t offset, uint32_t size)
+Graphics::UniquePtr<UniformBuffer> UniformBufferManager::AllocateUniformBuffer(uint32_t size, uint32_t alignment)
 {
-  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();
-  }
+  return Graphics::UniquePtr<UniformBuffer>(
+    new UniformBuffer(mController, size, alignment, true, Dali::Graphics::BufferUsageFlags{0u} | Dali::Graphics::BufferUsage::TRANSFER_DST | Dali::Graphics::BufferUsage::UNIFORM_BUFFER));
 }
 
-void UniformBuffer::Write(const void* data, uint32_t size, uint32_t dstOffset)
+Graphics::UniquePtr<UniformBufferView> UniformBufferManager::CreateUniformBufferView( UniformBuffer* uniformBuffer, uint32_t offset, uint32_t size )
 {
-  bool locallyMapped = (mMemory == nullptr);
-  if(locallyMapped)
-  {
-    Map();
-  }
-
-  if(mMemory)
-  {
-    void* ptr = mMemory->LockRegion(dstOffset, size);
-    if(ptr && dstOffset + size < mSize)
-    {
-      memcpy(ptr, data, size);
-    }
-    mMemory->Unlock(true);
-  }
-
-  if(locallyMapped)
-  {
-    Unmap();
-  }
+  // Allocate offset of given UBO (allocation strategy may reuse memory)
+  return Graphics::UniquePtr<UniformBufferView>( new UniformBufferView(*uniformBuffer, offset, size) );
 }
 
-void UniformBuffer::Map()
+Graphics::UniquePtr<UniformBufferViewPool> UniformBufferManager::CreateUniformBufferViewPool()
 {
-  if(!mMemory)
-  {
-    mMemory = mController->MapBufferRange(mMapBufferInfo);
-  }
+  return Graphics::UniquePtr<UniformBufferViewPool>(
+    new UniformBufferViewPool( *this, 1 )
+    );
 }
 
-void UniformBuffer::Unmap()
+[[nodiscard]] UniformBufferViewPool* UniformBufferManager::GetUniformBufferViewPool( uint32_t bufferIndex )
 {
-  if(mMemory)
+  if(!mUniformBufferPoolStorage[bufferIndex])
   {
-    mController->UnmapMemory(std::move(mMemory));
+    // create new uniform buffer view pool with default (initial) capacity
+    mUniformBufferPoolStorage[bufferIndex] = CreateUniformBufferViewPool();
   }
+  return mUniformBufferPoolStorage[bufferIndex].get();
 }
 
-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
+} // namespace Dali::Internal::Render
index af9e9f3..cfcaac6 100644 (file)
  *
  */
 
+// INTERNAL INCLUDES
 #include <dali/graphics-api/graphics-controller.h>
 
-#include <memory>
-
-namespace Dali
-{
-namespace Internal
-{
-namespace Render
-{
-class UniformBuffer
+namespace Dali::Internal::Render
 {
-  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);
+class UniformBuffer;
+class UniformBufferView;
+class UniformBufferViewPool;
 
+/**
+ * Class UniformBufferManager
+ *
+ * Manages the uniform buffers.
+ *
+ */
+class UniformBufferManager
+{
 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;
-  }
+  explicit UniformBufferManager(Dali::Graphics::Controller* controller);
 
-  /**
-   * Return Graphics API buffer
-   * @return pointer to the buffer object
-   */
-  Dali::Graphics::Buffer* GetBuffer() const
-  {
-    return mBuffer.get();
-  }
+  ~UniformBufferManager();
 
   /**
-   * Returns memory alignment
-   * @return memory alignment
+   * Allocates uniform buffer with given size and alignment
+   * @param size Size of uniform buffer
+   * @param alignment Alignment
+   * @return new UniformBuffer
    */
-  uint32_t GetAlignment() const
-  {
-    return mAlignment;
-  }
+  Graphics::UniquePtr<UniformBuffer> AllocateUniformBuffer(uint32_t size, uint32_t alignment = 256);
 
   /**
-   * Reserves buffer memory
+   * Creates a view on UniformBuffer
    *
-   * @param size requested size
+   * @param uniformBuffer
+   * @param size
+   * @return Uniform buffer view
    */
-  void Reserve(uint32_t size);
+  Graphics::UniquePtr<UniformBufferView> CreateUniformBufferView( UniformBuffer* uniformBuffer, uint32_t offset, uint32_t size);
 
   /**
-   * Maps buffer memory
+   * Creates uniform buffer pool view
+   * @param size
+   * @return
    */
-  void Map();
+  Graphics::UniquePtr<UniformBufferViewPool> CreateUniformBufferViewPool();
 
   /**
-   * Unmaps buffer memory
+   * Returns Controller object
+   * @return controller object
    */
-  void Unmap();
+  [[nodiscard]] Graphics::Controller& GetController() const
+  {
+    return *mController;
+  }
 
   /**
-   * 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
+   * Returns embedded uniform buffer pool view for specified DAli buffer index
+   * @return Pointer to valid uniform buffer pool view
    */
-  void Fill(char data, uint32_t offset, uint32_t size);
+  [[nodiscard]] UniformBufferViewPool* GetUniformBufferViewPool( uint32_t bufferIndex );
 
 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{0};
-  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;
+
+  Graphics::UniquePtr<UniformBufferViewPool> mUniformBufferPoolStorage[2u]; ///< The pool view into UniformBuffer (double buffered)
 };
 
-} // namespace Render
-} // namespace Internal
 } // namespace Dali
 
 #endif // DALI_INTERNAL_UNIFORM_BUFFER_MANAGER_H
diff --git a/dali/internal/render/renderers/uniform-buffer-view-pool.cpp b/dali/internal/render/renderers/uniform-buffer-view-pool.cpp
new file mode 100644 (file)
index 0000000..ef57ea8
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * 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.
+ *
+ */
+
+// CLASS HEADER
+#include <dali/internal/render/renderers/uniform-buffer-view-pool.h>
+
+// INTERNAL INCLUDES
+#include <dali/internal/render/renderers/uniform-buffer-manager.h>
+#include <dali/internal/render/renderers/uniform-buffer.h>
+#include <dali/internal/render/renderers/uniform-buffer-view.h>
+#include <dali/graphics-api/graphics-buffer-create-info.h>
+#include <dali/graphics-api/graphics-buffer.h>
+
+namespace Dali::Internal::Render
+{
+namespace
+{
+// Default UBO page size set to 32kb
+const uint32_t DEFAULT_UBO_PAGE_SIZE = 32768;
+}
+
+UniformBufferViewPool::UniformBufferViewPool( UniformBufferManager& manager, uint32_t alignment ) :
+  mUboManager(manager)
+{
+  // Create initial UBO
+  mUniformBufferStorage = mUboManager.AllocateUniformBuffer( DEFAULT_UBO_PAGE_SIZE, alignment );
+  mAlignment = alignment;
+  mCurrentOffset = 0;
+}
+
+UniformBufferViewPool::~UniformBufferViewPool() = default;
+
+void UniformBufferViewPool::Rollback()
+{
+  // Rollback offset
+  mCurrentOffset = 0;
+
+  // turn buffer into single allocation by resizing it
+  // to current size with invalidation
+  auto currentSize = mUniformBufferStorage->GetSize();
+  mUniformBufferStorage->Resize( currentSize ? currentSize : DEFAULT_UBO_PAGE_SIZE, true );
+}
+
+Graphics::UniquePtr<UniformBufferView> UniformBufferViewPool::CreateUniformBufferView( size_t size )
+{
+  // find new offset
+  auto newOffset = ((mCurrentOffset + size) / mAlignment) * mAlignment;
+  if(mAlignment > 1 && newOffset < mCurrentOffset + size)
+  {
+    newOffset += mAlignment;
+  }
+
+  // resize Ubo if needed
+  if( newOffset >= mUniformBufferStorage->GetSize())
+  {
+    // move offset to the new buffer
+    mCurrentOffset = mUniformBufferStorage->GetSize();
+    newOffset = ((mCurrentOffset + size) / mAlignment) * mAlignment;
+
+    // Adjust current offset so the view doesn't intersect multiple buffers
+    auto prevSize = mUniformBufferStorage->GetSize();
+    if(mCurrentOffset + size >= prevSize )
+    {
+      mCurrentOffset = prevSize;
+      newOffset = ((mCurrentOffset + size) / mAlignment) * mAlignment;
+    }
+    mUniformBufferStorage->Resize( mUniformBufferStorage->GetSize()+DEFAULT_UBO_PAGE_SIZE, false );
+  }
+
+  // create buffer view from
+  Graphics::UniquePtr<UniformBufferView> uboView = mUboManager.CreateUniformBufferView(mUniformBufferStorage.get(), mCurrentOffset, size);
+
+  // adjust offset
+  mCurrentOffset = newOffset;
+
+  return uboView;
+}
+
+} // namespace Dali::Internal::Render
\ No newline at end of file
diff --git a/dali/internal/render/renderers/uniform-buffer-view-pool.h b/dali/internal/render/renderers/uniform-buffer-view-pool.h
new file mode 100644 (file)
index 0000000..9318f39
--- /dev/null
@@ -0,0 +1,83 @@
+#ifndef DALI_INTERNAL_UNIFORM_BUFFER_VIEW_POOL_H
+#define DALI_INTERNAL_UNIFORM_BUFFER_VIEW_POOL_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.
+ *
+ */
+
+// INTERNAL INCLUDES
+#include <dali/graphics-api/graphics-types.h>
+
+// EXTERNAL INCLUDES
+#include <stdint.h>
+#include <stdlib.h>
+
+namespace Dali::Internal::Render
+{
+
+class UniformBufferManager;
+class UniformBufferView;
+class UniformBuffer;
+
+/**
+ * Class UniformBufferPoolView
+ *
+ * The class is responsible for managing the UniformBuffer memory.
+ * It is stack-based pool allocator. It doesn't own the UniformBuffer but
+ * it's mere view into the memory.
+ *
+ * The view may however request UniformBuffer to resize if there is a need
+ * to allocate memory beyond its current size.
+ *
+ * The UniformBuffer may be trimmed on Rollback().
+ *
+ * Rollback() operation moves allocation pointer to the very beginning
+ * of the UniformBuffer. The data stored in the UniformBuffer after
+ * Rollback() is considered invalid and shouldn't be used by the client side.
+ */
+class UniformBufferViewPool
+{
+  friend class UniformBufferManager;
+
+private:
+
+  UniformBufferViewPool( UniformBufferManager& manager, uint32_t alignment );
+
+public:
+
+  ~UniformBufferViewPool();
+
+  /**
+   * Rolls back allocation to the beginning of pool
+   */
+  void Rollback();
+
+  /**
+   * Creates view for next free chunk of UBO memory of specified size.
+   */
+  Graphics::UniquePtr<UniformBufferView> CreateUniformBufferView( size_t size );
+
+private:
+
+  UniformBufferManager& mUboManager;
+  Graphics::UniquePtr<UniformBuffer> mUniformBufferStorage;
+
+  uint32_t mAlignment; // 1 for tightly packed emulated UBO
+  uint32_t mCurrentOffset;
+};
+
+} // namespace Dali::Internal::Render
+#endif //DALI_INTERNAL_UNIFORM_BUFFER_VIEW_POOL_H
\ No newline at end of file
diff --git a/dali/internal/render/renderers/uniform-buffer-view.cpp b/dali/internal/render/renderers/uniform-buffer-view.cpp
new file mode 100644 (file)
index 0000000..b8c00d0
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ *
+ */
+
+// CLASS HEADER
+#include <dali/internal/render/renderers/uniform-buffer-view.h>
+
+// INTERNAL INCLUDES
+#include <dali/internal/render/renderers/uniform-buffer.h>
+
+namespace Dali::Internal::Render
+{
+UniformBufferView::UniformBufferView( UniformBuffer& ubo, uint32_t offset, size_t size ) :
+  mUniformBuffer(&ubo),
+  mOffset(offset),
+  mSize (size)
+{
+}
+
+UniformBufferView::~UniformBufferView() = default;
+
+void UniformBufferView::Write(const void* data, uint32_t size, uint32_t offset)
+{
+  // Write into mapped buffer
+  mUniformBuffer->Write( data, size, offset );
+}
+
+Graphics::Buffer* UniformBufferView::GetBuffer( uint32_t* relativeOffset )
+{
+  auto buffer = mUniformBuffer->GetBufferByOffset( mOffset, relativeOffset, nullptr );
+  return buffer ? buffer->buffer.get() : nullptr;
+}
+} // Namespace Dali::Internal::Render
\ No newline at end of file
diff --git a/dali/internal/render/renderers/uniform-buffer-view.h b/dali/internal/render/renderers/uniform-buffer-view.h
new file mode 100644 (file)
index 0000000..2e6a608
--- /dev/null
@@ -0,0 +1,97 @@
+#ifndef DALI_INTERNAL_UNIFORM_BUFFER_VIEW_H
+#define DALI_INTERNAL_UNIFORM_BUFFER_VIEW_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.
+ *
+ */
+
+// EXTERNAL INCLUDES
+#include <stdint.h>
+#include <stdlib.h>
+
+namespace Dali
+{
+namespace Graphics
+{
+class Buffer;
+}
+namespace Internal::Render
+{
+
+class UniformBuffer;
+
+/**
+ * Class UniformBufferView
+ *
+ * The class is responsible for providing view into the UniformBuffer object
+ * giving access to part or all of its memory. UniformBufferView doesn't own
+ * memory.
+ *
+ * The memory accessed through the UniformBufferView should be addressed at
+ * the offset of 0 and up to specified size. The memory beyond the view may be
+ * invalid.
+ *
+ */
+class UniformBufferView
+{
+public:
+
+  UniformBufferView(UniformBuffer &ubo, uint32_t offset, size_t size);
+
+  ~UniformBufferView();
+
+  void Write(const void *data, uint32_t size, uint32_t offset);
+
+  /**
+   * Returns the size of the view
+   *
+   * @return size of view
+   */
+  [[nodiscard]] uint32_t GetSize() const
+  {
+    return mSize;
+  }
+
+  /**
+   * Returns the offset within the UBO
+   * @return Offset
+   */
+  [[nodiscard]] uint32_t GetOffset() const
+  {
+    return mOffset;
+  }
+
+  /**
+   * Returns Graphics buffer associated with this View
+   *
+   * If 'relativeOffset' isn't nullptr then the offset into the individual
+   * Graphics::Buffer is written.
+   *
+   * @param[out] relativeOffset the offset into individual Graphics::Buffer
+   *
+   * @return Pointer to a valid Graphics::Buffer object
+   */
+  [[nodiscard]] Graphics::Buffer *GetBuffer(uint32_t *relativeOffset = nullptr);
+
+private:
+
+  UniformBuffer *mUniformBuffer{nullptr}; ///< UniformBuffer that the view views
+  uint32_t mOffset{0u}; ///< Offset within the buffer
+  size_t   mSize{0u}; ///< Size of view
+};
+} // Namespace Internal::Render
+} // Namespace Dali
+#endif //DALI_INTERNAL_UNIFORM_BUFFER_VIEW_H
\ No newline at end of file
diff --git a/dali/internal/render/renderers/uniform-buffer.cpp b/dali/internal/render/renderers/uniform-buffer.cpp
new file mode 100644 (file)
index 0000000..ecae483
--- /dev/null
@@ -0,0 +1,219 @@
+/*
+ * 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.
+ *
+ */
+
+// CLASS HEADER
+#include <dali/internal/render/renderers/uniform-buffer.h>
+
+namespace Dali::Internal::Render
+{
+UniformBuffer::UniformBuffer(Dali::Graphics::Controller* controller,
+                             uint32_t                    sizeInBytes,
+                             uint32_t                    alignment,
+                             bool                        persistentMappedEnabled,
+                             Graphics::BufferUsageFlags  usageFlags)
+  : mController(controller),
+    mSize(0u),
+    mUsageFlags(usageFlags)
+{
+  mAlignment = alignment;
+  if(sizeInBytes)
+  {
+    Resize(sizeInBytes, true);
+  }
+}
+
+UniformBuffer::~UniformBuffer()
+{
+  // Unmap and flush all allocated buffers
+  for( auto i = 0u; i < mBuffers.size(); ++i)
+  {
+    Flush(i);
+    Unmap(i);
+  }
+}
+
+void UniformBuffer::Flush(uint32_t bufferIndex)
+{
+  const auto& buffer = mBuffers[bufferIndex];
+  if(buffer.buffer && buffer.memory)
+  {
+    buffer.memory->Flush();
+  }
+}
+
+void UniformBuffer::Resize( uint32_t newSize, bool invalidate )
+{
+  // The buffer is already optimal
+  if(newSize == mSize && !invalidate)
+  {
+    return;
+  }
+  if(invalidate && newSize == mSize && mBuffers.size() == 1)
+  {
+    return;
+  }
+
+  // Throw away content
+  if(invalidate)
+  {
+    mBuffers.clear();
+    mSize = 0;
+  }
+
+  // Adjust alignment, the alignment is needed for
+  // real UBOs (it should be given by the buffer requirements)
+  if(mAlignment)
+  {
+    newSize = ((newSize / mAlignment)+1)*mAlignment;
+  }
+
+  if(newSize > mSize)
+  {
+    auto createInfo = Graphics::BufferCreateInfo()
+      .SetSize(newSize - mSize)
+      .SetBufferPropertiesFlags(0 | Graphics::BufferPropertiesFlagBit::CPU_ALLOCATED)
+      .SetUsage(mUsageFlags);
+
+    auto buffer = mController->CreateBuffer(createInfo, nullptr);
+
+    mBuffers.emplace_back(GfxBuffer(std::move(buffer), createInfo));
+
+    mSize = newSize;
+  }
+}
+
+
+const UniformBuffer::GfxBuffer* UniformBuffer::GetBufferByOffset( uint32_t offset, uint32_t* newOffset, uint32_t* outBufferIndex ) const
+{
+  uint32_t bufferOffset = offset;
+  uint32_t bufferIndex = 0u;
+
+  // Find buffer if UBO is fragmented
+  if(mBuffers.size() > 1)
+  {
+    for(const auto& buffer : mBuffers)
+    {
+      if( bufferOffset >= buffer.createInfo.size)
+      {
+        bufferOffset -= buffer.createInfo.size;
+      }
+      else
+      {
+        break;
+      }
+      bufferIndex++;
+    }
+  }
+
+  auto& bufferDesc = mBuffers[bufferIndex];
+
+  if(outBufferIndex)
+  {
+    *outBufferIndex = bufferIndex;
+  }
+
+  if(newOffset)
+  {
+    *newOffset = bufferOffset;
+  }
+
+  return &bufferDesc;
+}
+
+void UniformBuffer::Write(const void* data, uint32_t size, uint32_t dstOffset)
+{
+  // find which buffer we want to write into
+  uint32_t bufferOffset = dstOffset;
+  uint32_t bufferIndex = 0u;
+
+  // Find buffer if UBO is fragmented
+  if(mBuffers.size() > 1)
+  {
+    for(const auto& buffer : mBuffers)
+    {
+      if( bufferOffset >= buffer.createInfo.size)
+      {
+        bufferOffset -= buffer.createInfo.size;
+      }
+      else
+      {
+        break;
+      }
+      bufferIndex++;
+    }
+  }
+
+  auto& bufferDesc = mBuffers[bufferIndex];
+
+  if(bufferDesc.needsUpdate)
+  {
+    mController->WaitIdle();
+    bufferDesc.needsUpdate = false;
+  }
+
+  DALI_ASSERT_ALWAYS( mBuffers.size() > bufferIndex );
+  DALI_ASSERT_ALWAYS( mBuffers[bufferIndex].buffer );
+  DALI_ASSERT_ALWAYS( mBuffers[bufferIndex].createInfo.size > bufferOffset + size );
+
+  bool locallyMapped = (bufferDesc.mappedPtr != nullptr);
+  if(!locallyMapped)
+  {
+    // Map once and keep it
+    Map( bufferIndex );
+  }
+
+  if(bufferDesc.memory)
+  {
+    void* ptr = bufferDesc.memory->LockRegion(bufferOffset, size);
+    if(ptr && bufferOffset + size < mSize)
+    {
+      memcpy(ptr, data, size);
+    }
+    bufferDesc.memory->Unlock(true);
+  }
+}
+
+void UniformBuffer::Map( uint32_t bufferIndex)
+{
+  auto& buffer = mBuffers[bufferIndex];
+
+  if(buffer.needsUpdate)
+  {
+    mController->WaitIdle();
+    buffer.needsUpdate = false;
+  }
+
+  if(!buffer.memory)
+  {
+    Graphics::MapBufferInfo info{};
+    info.buffer = buffer.buffer.get();
+    info.usage  = 0 | Graphics::MemoryUsageFlagBits::WRITE;
+    info.offset = 0;
+    info.size   = buffer.createInfo.size;
+    buffer.memory = mController->MapBufferRange(info);
+  }
+}
+
+void UniformBuffer::Unmap( uint32_t bufferIndex)
+{
+  auto& buffer = mBuffers[bufferIndex];
+  if(buffer.memory)
+  {
+    mController->UnmapMemory(std::move(buffer.memory));
+  }
+}
+}
\ No newline at end of file
diff --git a/dali/internal/render/renderers/uniform-buffer.h b/dali/internal/render/renderers/uniform-buffer.h
new file mode 100644 (file)
index 0000000..d665492
--- /dev/null
@@ -0,0 +1,185 @@
+#ifndef DALI_INTERNAL_UNIFORM_BUFFER_H
+#define DALI_INTERNAL_UNIFORM_BUFFER_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.
+ *
+ */
+
+// INTERNAL INCLUDES
+#include <dali/graphics-api/graphics-controller.h>
+
+// EXTERNAL INCLUDES
+#include <memory>
+
+namespace Dali::Internal::Render
+{
+/**
+ * Class UniformBuffer
+ *
+ * The class wraps one or more Graphics::Buffer objects and creates
+ * a continuous memory to store uniforms. The UniformBuffer may
+ * reallocate and merge individual Graphics::Buffer objects into one.
+ *
+ * From the client side, the UBO memory is continuous and individual
+ * Graphics::Buffer objects are not visible.
+ */
+class UniformBuffer
+{
+  friend class UniformBufferManager;
+  friend class UniformBufferView;
+  friend class UniformBufferViewPool;
+
+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
+   *
+   * @param[in] bufferIndex Index of Graphics::Buffer
+   */
+  void Flush( uint32_t bufferIndex = 0);
+
+  /**
+   * Returns allocated ( requested ) size
+   * @return size of buffer
+   */
+  [[nodiscard]] uint32_t GetSize() const
+  {
+    return mSize;
+  }
+
+  /**
+   * Return Graphics::Buffer object at specified array index
+   *
+   * @param[in] bufferIndex index of Graphics buffer
+   *
+   * @return pointer to the buffer object
+   */
+  [[nodiscard]] Dali::Graphics::Buffer* GetBuffer( uint32_t bufferIndex ) const
+  {
+    return mBuffers[bufferIndex].buffer.get();
+  }
+
+  /**
+   * Maps individual Graphics buffer memory
+   *
+   * @param[in] bufferIndex index of Graphics buffer
+   */
+  void Map( uint32_t bufferIndex = 0);
+
+  /**
+   * Unmaps individual Graphics buffer memory
+   *
+   * @param[in] bufferIndex index of Graphics buffer
+   */
+  void Unmap( uint32_t bufferIndex = 0);
+
+  /**
+   * Resizes the buffer
+   *
+   * The resize strategy depends on 'invalidate' parameter.
+   *
+   * If 'invalidate' is true, all the content if the buffer
+   * is discarded, the individual Graphics::Buffers are deleted
+   * and a single Graphics::Buffer is allocated.
+   *
+   * If 'invalidate' is false, additional Graphics::Buffer
+   * is created and all recorded content is kept unchanged.
+   *
+   * @param[in] newSize new size of UniformBuffer
+   * @param[in] invalidate specifies whether the content should be discarded
+   *
+   */
+  void Resize( uint32_t newSize, bool invalidate );
+
+private:
+
+  /**
+   * GfxBuffer wraps single GPU buffer and encapsulates individual
+   * buffer mapping and create info details.
+   *
+   * The array of GfxBuffers makes a single UniformBuffer.
+   */
+  struct GfxBuffer
+  {
+    GfxBuffer() = default;
+    ~GfxBuffer() = default;
+    GfxBuffer( GfxBuffer&& ) = default;
+    GfxBuffer(Graphics::UniquePtr<Graphics::Buffer>&& b, const Graphics::BufferCreateInfo& i) :
+      buffer(std::move(b)),
+      createInfo(i)
+    {
+    }
+
+    Graphics::UniquePtr<Graphics::Buffer> buffer{}; ///< Graphics buffer
+    Graphics::UniquePtr<Graphics::Memory> memory{}; ///< Mapped memory associated with buffer
+    Graphics::BufferCreateInfo createInfo{}; ///< create info describing the buffer
+    void* mappedPtr{}; ///< Mapped pointer (if mapped)
+    bool needsUpdate{true}; ///< Indicates whether the buffer needs flushing the queue
+  };
+
+  /**
+   * Returns GfxBuffer object by offset
+   *
+   * The memory of UniformBuffer is considered to be continuous, however,
+   * it may contain multiple graphics buffers.
+   *
+   */
+  const GfxBuffer* GetBufferByOffset( uint32_t offset, uint32_t* newOffset, uint32_t* bufferIndex ) const;
+
+  std::vector<GfxBuffer> mBuffers; ///< List of GfxBuffer objects
+
+  Dali::Graphics::Controller* mController; ///< Pointer to the controller
+
+  uint32_t mSize;        ///< Current size of buffer
+  uint32_t mAlignment{0}; ///< Buffer alignment
+
+  Graphics::BufferUsageFlags mUsageFlags;
+
+  bool mValid{false};
+};
+
+}
+
+#endif //DALI_INTERNAL_UNIFORM_BUFFER_H