Refactored Uniform Buffer support 58/290958/18
authorDavid Steele <david.steele@samsung.com>
Wed, 21 Jun 2023 12:07:41 +0000 (13:07 +0100)
committerDavid Steele <david.steele@samsung.com>
Fri, 23 Jun 2023 15:12:45 +0000 (16:12 +0100)
Uniform blocks in GLES3 shaders are now supported by GPU buffer.
Fixed up test harness to handle CPU buffers properly
Fixed up test cases with wrong buffer counts

Added method to get uniform block alignment from the
backend - this is slightly more complicated than it
needs to be, because of the Vulkan-like API we have.

Enhanced test harness to enable testing of UBOs.
Adding reflection & test cases for uniform blocks

Ensured that multi-pass of draw commands doesn't
generate multiple blocks.

Ensured GPU uniform buffers are double-buffered

Ensured size of CPU/GPU for offscreen vs onscreen
buffers is correct.

Added test cases to ensure that large enough buffers
are allocated, and that properties are written
into the relevant uniform block areas within the buffers.

Change-Id: Id551d56ecea228320af3896afef49790d5ceec6d

31 files changed:
automated-tests/src/dali/dali-test-suite-utils/test-gl-abstraction.cpp
automated-tests/src/dali/dali-test-suite-utils/test-gl-abstraction.h
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/dali-test-suite-utils/test-graphics-controller.h
automated-tests/src/dali/dali-test-suite-utils/test-graphics-program.cpp
automated-tests/src/dali/dali-test-suite-utils/test-graphics-program.h
automated-tests/src/dali/dali-test-suite-utils/test-graphics-reflection.cpp
automated-tests/src/dali/dali-test-suite-utils/test-graphics-reflection.h
automated-tests/src/dali/dali-test-suite-utils/test-graphics-texture.h
automated-tests/src/dali/dali-test-suite-utils/test-trace-call-stack.cpp
automated-tests/src/dali/utc-Dali-Geometry.cpp
automated-tests/src/dali/utc-Dali-RenderTask.cpp
automated-tests/src/dali/utc-Dali-Renderer.cpp
automated-tests/src/dali/utc-Dali-VertexBuffer.cpp
dali/graphics-api/graphics-types.h
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 [deleted file]
dali/internal/render/renderers/uniform-buffer-view-pool.h [deleted file]
dali/internal/render/renderers/uniform-buffer-view.cpp
dali/internal/render/renderers/uniform-buffer-view.h
dali/internal/render/renderers/uniform-buffer.cpp
dali/internal/render/renderers/uniform-buffer.h
dali/internal/render/shaders/program.cpp
dali/internal/render/shaders/program.h

index 1d20a6a..38169a2 100644 (file)
 #include "test-gl-abstraction.h"
 #include "test-trace-call-stack.h"
 
-static const bool TRACE{
-  false};
+static const bool TRACE{false};
+
+uint32_t GetGLDataTypeSize(GLenum type)
+{
+  // There are many more types than what are covered here, but
+  // they are not supported in dali.
+  switch(type)
+  {
+    case GL_FLOAT: // "float", 1 float, 4 bytes
+      return 4;
+    case GL_FLOAT_VEC2: // "vec2", 2 floats, 8 bytes
+      return 8;
+    case GL_FLOAT_VEC3: // "vec3", 3 floats, 12 bytes
+      return 12;
+    case GL_FLOAT_VEC4: // "vec4", 4 floats, 16 bytes
+      return 16;
+    case GL_INT: // "int", 1 integer, 4 bytes
+      return 4;
+    case GL_FLOAT_MAT2: // "mat2", 4 floats, 16 bytes
+      return 16;
+    case GL_FLOAT_MAT3: // "mat3", 3 vec3, 36 bytes
+      return 36;
+    case GL_FLOAT_MAT4: // "mat4", 4 vec4, 64 bytes
+      return 64;
+    default:
+      return 0;
+  }
+}
 
 namespace Dali
 {
@@ -145,6 +171,12 @@ void TestGlAbstraction::Initialize()
     {"uLightCameraProjectionMatrix", GL_FLOAT_MAT4, 1},
     {"uLightCameraViewMatrix", GL_FLOAT_MAT4, 1}};
 
+  int offset = 0;
+  for(uint32_t i = 0; i < mActiveUniforms.size(); ++i)
+  {
+    mActiveUniforms[i].offset = offset;
+    offset += mActiveUniforms[i].size * GetGLDataTypeSize(mActiveUniforms[i].type);
+  }
   // WARNING: IF YOU CHANGE THIS LIST, ALSO CHANGE UNIFORMS IN test-graphics-reflection.cpp
 }
 
@@ -196,6 +228,17 @@ bool TestGlAbstraction::TextureRequiresConverting(const GLenum imageGlFormat, co
   return ((imageGlFormat == GL_RGB) && (textureGlFormat == GL_RGBA));
 }
 
+void TestGlAbstraction::SetActiveUniforms(const std::vector<ActiveUniform>& uniforms)
+{
+  mActiveUniforms = uniforms;
+  int offset      = 0;
+  for(uint32_t i = 0; i < uniforms.size(); ++i)
+  {
+    mActiveUniforms[i].offset = offset;
+    offset += mActiveUniforms[i].size * GetGLDataTypeSize(mActiveUniforms[i].type);
+  }
+}
+
 } // namespace Dali
 
 bool BlendEnabled(const Dali::TraceCallStack& callStack)
index cc27d9e..2ca98dc 100644 (file)
@@ -51,9 +51,10 @@ struct UniformData
 
 struct ActiveUniform
 {
-  std::string name;
-  GLenum      type;
-  GLint       size;
+  std::string name{};
+  GLenum      type{GL_FLOAT};
+  GLint       size{0};
+  GLint       offset{0};
 };
 
 class DALI_CORE_API TestGlAbstraction : public Dali::Integration::GlAbstraction
@@ -457,6 +458,10 @@ public:
 
   inline void DeleteBuffers(GLsizei n, const GLuint* buffers) override
   {
+    TraceCallStack::NamedParams namedParams;
+    namedParams["n"] << n;
+    namedParams["id"] << buffers[0];
+    mBufferTrace.PushCall("DeleteBuffers", namedParams.str(), namedParams);
   }
 
   inline void DeleteFramebuffers(GLsizei n, const GLuint* framebuffers) override
@@ -697,13 +702,21 @@ public:
   inline void GenBuffers(GLsizei n, GLuint* buffers) override
   {
     // avoids an assert in GpuBuffers
-    *buffers = 1u;
+    static GLuint id = 1;
 
-    std::ostringstream o;
-    o << n;
     TraceCallStack::NamedParams namedParams;
-    namedParams["n"] << o.str();
-    mBufferTrace.PushCall("GenBuffers", o.str(), namedParams);
+    namedParams["n"] << n;
+
+    // Allocate some buffer names
+    bool first = true;
+    while(n)
+    {
+      namedParams["buffers"] << (first ? "" : ", ") << id;
+      first      = false;
+      *buffers++ = id++;
+      --n;
+    }
+    mBufferTrace.PushCall("GenBuffers", namedParams.str(), namedParams);
   }
 
   inline void GenerateMipmap(GLenum target) override
@@ -800,10 +813,7 @@ public:
     *type = mAttribTypes[index];
   }
 
-  inline void SetActiveUniforms(const std::vector<ActiveUniform>& uniforms)
-  {
-    mActiveUniforms = uniforms;
-  }
+  void SetActiveUniforms(const std::vector<ActiveUniform>& uniforms);
 
   inline void GetActiveUniform(GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, char* name) override
   {
@@ -865,6 +875,9 @@ public:
       case GL_PROGRAM_BINARY_FORMATS_OES:
         *params = mBinaryFormats;
         break;
+      case GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT:
+        *params = mUniformBufferOffsetAlignment;
+        break;
     }
   }
 
@@ -1882,6 +1895,44 @@ public:
 
   inline void GetActiveUniformsiv(GLuint program, GLsizei uniformCount, const GLuint* uniformIndices, GLenum pname, GLint* params) override
   {
+    for(int i = 0; i < uniformCount; ++i)
+    {
+      if(i < int(mActiveUniforms.size()))
+      {
+        switch(pname)
+        {
+          case GL_UNIFORM_TYPE:
+          {
+            params[i] = mActiveUniforms[i].type;
+            break;
+          }
+          case GL_UNIFORM_SIZE:
+          {
+            params[i] = mActiveUniforms[i].size;
+            break;
+          }
+          case GL_UNIFORM_NAME_LENGTH:
+          {
+            params[i] = mActiveUniforms[i].name.length();
+            break;
+          }
+          case GL_UNIFORM_BLOCK_INDEX:
+          {
+            params[i] = -1;
+            break;
+          }
+          case GL_UNIFORM_OFFSET:
+          {
+            params[i] = mActiveUniforms[i].offset;
+            break;
+          }
+          case GL_UNIFORM_MATRIX_STRIDE:
+          {
+            break;
+          }
+        }
+      }
+    }
   }
 
   inline GLuint GetUniformBlockIndex(GLuint program, const GLchar* uniformBlockName) override
@@ -2169,7 +2220,10 @@ public: // TEST FUNCTIONS
   {
     mProgramBinaryLength = length;
   }
-
+  inline void SetUniformBufferOffsetAlignment(GLint align)
+  {
+    mUniformBufferOffsetAlignment = align;
+  }
   inline bool GetVertexAttribArrayState(GLuint index)
   {
     if(index >= MAX_ATTRIBUTE_CACHE_SIZE)
@@ -2582,6 +2636,7 @@ public:
   GLint                                 mNumBinaryFormats;
   GLint                                 mBinaryFormats;
   GLint                                 mProgramBinaryLength;
+  GLint                                 mUniformBufferOffsetAlignment{1};
   bool                                  mVertexAttribArrayState[MAX_ATTRIBUTE_CACHE_SIZE];
   bool                                  mVertexAttribArrayChanged; // whether the vertex attrib array has been changed
   bool                                  mGetProgramBinaryCalled;
index 42b53b1..456d978 100644 (file)
 
 namespace Dali
 {
-TestGraphicsBuffer::TestGraphicsBuffer(TraceCallStack& callStack, TestGlAbstraction& glAbstraction, uint32_t size, Graphics::BufferUsageFlags usage)
+TestGraphicsBuffer::TestGraphicsBuffer(const Graphics::BufferCreateInfo& createInfo, TestGraphicsController& controller, TestGlAbstraction& glAbstraction, TraceCallStack& callStack)
 : mCallStack(callStack),
+  mController(controller),
   mGl(glAbstraction),
-  mUsage(usage)
+  mCreateInfo(createInfo),
+  mUsage(createInfo.usage)
 {
-  memory.resize(size);
-  mGl.GetBufferTrace().EnableLogging(false);
+  if(createInfo.propertiesFlags & int(Graphics::BufferPropertiesFlagBit::CPU_ALLOCATED))
+  {
+    mCpuOnly = true;
+  }
+  else
+  {
+    mGl.GenBuffers(1, &mId);
+  }
+  memory.resize(createInfo.size);
+}
+
+TestGraphicsBuffer::~TestGraphicsBuffer()
+{
+  // Not strictly parameters, but useful for testing
+  TraceCallStack::NamedParams namedParams;
+  namedParams["usage"] << "0x" << std::hex << mCreateInfo.usage;
+  namedParams["propertiesFlags"] << mCreateInfo.propertiesFlags;
+
+  mCallStack.PushCall("Buffer::~Buffer", namedParams.str(), namedParams);
+  if(!mCpuOnly && mId)
+  {
+    mGl.DeleteBuffers(1, &mId);
+  }
+}
+
+void TestGraphicsBuffer::DiscardResource()
+{
+  mController.DiscardBuffer(this);
 }
 
 void TestGraphicsBuffer::Bind()
 {
   mCallStack.PushCall("Buffer::Bind", "");
-  if(!mId)
+  if(!mCpuOnly && mId > 0)
   {
-    mGl.GenBuffers(1, &mId);
+    mGl.BindBuffer(GetTarget(), mId);
   }
-  mGl.BindBuffer(GetTarget(), mId);
 }
 
 void TestGraphicsBuffer::Unbind()
 {
   mCallStack.PushCall("Buffer::Unbind", "");
-  if(mId)
+  if(!mCpuOnly)
   {
-    mGl.BindBuffer(GetTarget(), 0);
+    if(mId)
+    {
+      mGl.BindBuffer(GetTarget(), 0);
+    }
   }
 }
 
@@ -58,15 +88,19 @@ void TestGraphicsBuffer::Upload(uint32_t offset, uint32_t size)
   namedParams["offset"] << offset;
   namedParams["size"] << size;
   mCallStack.PushCall("Buffer::Upload", o.str(), namedParams);
-  if(size <= memory.size() && mCreated)
-  {
-    // Use subData to avoid re-allocation
-    mGl.BufferSubData(GetTarget(), static_cast<GLintptr>(static_cast<unsigned long>(offset)), static_cast<GLsizeiptr>(static_cast<unsigned long>(size)), &memory[offset]);
-  }
-  else
+
+  if(!mCpuOnly)
   {
-    mGl.BufferData(GetTarget(), static_cast<GLsizeiptr>(static_cast<unsigned long>(size)), &memory[0], GL_STATIC_DRAW); //@todo Query - do we need other usages?
-    mCreated = true;
+    if(size <= memory.size() && mCreated)
+    {
+      // Use subData to avoid re-allocation
+      mGl.BufferSubData(GetTarget(), static_cast<GLintptr>(static_cast<unsigned long>(offset)), static_cast<GLsizeiptr>(static_cast<unsigned long>(size)), &memory[offset]);
+    }
+    else
+    {
+      mGl.BufferData(GetTarget(), static_cast<GLsizeiptr>(static_cast<unsigned long>(size)), &memory[0], GL_STATIC_DRAW); //@todo Query - do we need other usages?
+      mCreated = true;
+    }
   }
 }
 
index 1dc2715..220ae06 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_TEST_GRAPHICS_BUFFER_H
 
 /*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 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.
@@ -17,6 +17,7 @@
  * limitations under the License.
  */
 
+#include <dali/graphics-api/graphics-buffer-create-info.h>
 #include <dali/graphics-api/graphics-buffer.h>
 #include <dali/graphics-api/graphics-types.h>
 
 namespace Dali
 {
 class TestGraphicsProgram;
+class TestGraphicsController;
 class UniformBufferBindingDescriptor;
+
 class TestGraphicsBuffer : public Graphics::Buffer
 {
 public:
-  TestGraphicsBuffer(TraceCallStack& callStack, TestGlAbstraction& glAbstraction, uint32_t size, Graphics::BufferUsageFlags usage);
+  TestGraphicsBuffer(const Graphics::BufferCreateInfo& createInfo, TestGraphicsController& controller, TestGlAbstraction& glAbstraction, TraceCallStack& callStack);
+  ~TestGraphicsBuffer();
+  void DiscardResource();
+
   void   Bind();
   void   Unbind();
   void   Upload(uint32_t offset, uint32_t size);
@@ -38,17 +44,21 @@ public:
 
   bool IsCPUAllocated() const
   {
-    return true;
+    return mCpuOnly;
   }
 
   void BindAsUniformBuffer(const TestGraphicsProgram* program, const Dali::UniformBufferBindingDescriptor& uboBinding) const;
 
-  TraceCallStack&            mCallStack;
-  TestGlAbstraction&         mGl;
-  std::vector<uint8_t>       memory;
+  TraceCallStack&         mCallStack;
+  TestGraphicsController& mController;
+  TestGlAbstraction&      mGl;
+  std::vector<uint8_t>    memory;
+
+  Graphics::BufferCreateInfo mCreateInfo;
   Graphics::BufferUsageFlags mUsage;
   GLuint                     mId{0};
   bool                       mCreated{false};
+  bool                       mCpuOnly{false};
 };
 
 } // namespace Dali
index 14c6d50..4bac7a6 100644 (file)
@@ -16,6 +16,7 @@
 
 #include "test-graphics-controller.h"
 
+#include <dali/graphics-api/graphics-types.h>
 #include "test-graphics-buffer.h"
 #include "test-graphics-command-buffer.h"
 #include "test-graphics-framebuffer.h"
 #include "test-graphics-texture.h"
 
 #include <dali/integration-api/gl-defines.h>
+#include <any>
 #include <cstdio>
 #include <iostream>
+#include <memory>
 #include <sstream>
 
-#include <any>
-
 namespace Dali
 {
+namespace
+{
+template<class T>
+struct TestGraphicsDeleter
+{
+  TestGraphicsDeleter() = default;
+  void operator()(T* object)
+  {
+    // Discard resource
+    object->DiscardResource();
+  }
+};
+
+} //namespace
+
 std::ostream& operator<<(std::ostream& o, const Graphics::BufferCreateInfo& bufferCreateInfo)
 {
   return o << "usage:" << std::hex << bufferCreateInfo.usage << ", size:" << std::dec << bufferCreateInfo.size;
@@ -1185,10 +1201,25 @@ bool TestGraphicsController::IsDrawOnResumeRequired()
 
 Graphics::UniquePtr<Graphics::Buffer> TestGraphicsController::CreateBuffer(const Graphics::BufferCreateInfo& createInfo, Graphics::UniquePtr<Graphics::Buffer>&& oldBuffer)
 {
-  std::ostringstream oss;
-  oss << "bufferCreateInfo:" << createInfo;
-  mCallStack.PushCall("CreateBuffer", oss.str());
-  return Graphics::MakeUnique<TestGraphicsBuffer>(mCallStack, mGl, createInfo.size, createInfo.usage);
+  TraceCallStack::NamedParams namedParams;
+  namedParams["usage"] << "0x" << std::hex << createInfo.usage;
+  namedParams["propertiesFlags"] << createInfo.propertiesFlags;
+  namedParams["size"] << createInfo.size;
+  mCallStack.PushCall("CreateBuffer", namedParams.str(), namedParams);
+
+  auto ptr = Graphics::MakeUnique<TestGraphicsBuffer, TestGraphicsDeleter<TestGraphicsBuffer>>(createInfo, *this, mGl, mCallStack);
+  mAllocatedBuffers.push_back(ptr.get());
+  return ptr;
+}
+
+void TestGraphicsController::DiscardBuffer(TestGraphicsBuffer* buffer)
+{
+  auto iter = std::find(mAllocatedBuffers.begin(), mAllocatedBuffers.end(), buffer);
+  if(iter != mAllocatedBuffers.end())
+  {
+    mAllocatedBuffers.erase(iter);
+  }
+  delete buffer;
 }
 
 Graphics::UniquePtr<Graphics::CommandBuffer> TestGraphicsController::CreateCommandBuffer(const Graphics::CommandBufferCreateInfo& commandBufferCreateInfo, Graphics::UniquePtr<Graphics::CommandBuffer>&& oldCommandBuffer)
@@ -1258,7 +1289,8 @@ Graphics::UniquePtr<Graphics::Program> TestGraphicsController::CreateProgram(con
   }
 
   mProgramCache.emplace_back();
-  mProgramCache.back().programImpl = new TestGraphicsProgramImpl(mGl, programCreateInfo, mVertexFormats, mCustomUniforms);
+  mProgramCache.back().programImpl = new TestGraphicsProgramImpl(mGl, programCreateInfo, mVertexFormats, mCustomUniforms, mCustomUniformBlocks);
+
   for(auto& shader : *(programCreateInfo.shaderState))
   {
     auto graphicsShader = Uncast<TestGraphicsShader>(shader.shader);
@@ -1329,8 +1361,24 @@ Graphics::MemoryRequirements TestGraphicsController::GetTextureMemoryRequirement
 
 Graphics::MemoryRequirements TestGraphicsController::GetBufferMemoryRequirements(Graphics::Buffer& buffer) const
 {
+  static GLint uniformAlign{0};
+
+  Graphics::MemoryRequirements reqs{};
   mCallStack.PushCall("GetBufferMemoryRequirements", "");
-  return Graphics::MemoryRequirements{};
+
+  auto gfxBuffer = Uncast<TestGraphicsBuffer>(&buffer);
+  if(gfxBuffer->mCreateInfo.usage & (0 | Graphics::BufferUsage::UNIFORM_BUFFER))
+  {
+    if(!uniformAlign)
+    {
+      // Throw off the shackles of constness
+      auto& gl = *const_cast<TestGlAbstraction*>(&mGl);
+      gl.GetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &uniformAlign);
+    }
+    reqs.size      = gfxBuffer->mCreateInfo.size;
+    reqs.alignment = uint32_t(uniformAlign);
+  }
+  return reqs;
 }
 
 Graphics::TextureProperties TestGraphicsController::GetTextureProperties(const Graphics::Texture& texture)
index 81b93d2..ade48d7 100644 (file)
@@ -2,7 +2,7 @@
 #define TEST_GRAPHICS_CONTROLLER_H
 
 /*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 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.
@@ -207,6 +207,8 @@ public:
    */
   Graphics::UniquePtr<Graphics::Buffer> CreateBuffer(const Graphics::BufferCreateInfo& bufferCreateInfo, Graphics::UniquePtr<Graphics::Buffer>&& oldBuffer) override;
 
+  void DiscardBuffer(TestGraphicsBuffer* buffer);
+
   /**
    * @brief Creates new CommandBuffer object
    *
@@ -384,6 +386,11 @@ public: // Test Functions
     mCustomUniforms = customUniforms;
   }
 
+  void AddCustomUniformBlock(const TestGraphicsReflection::TestUniformBlockInfo& blockInfo)
+  {
+    mCustomUniformBlocks.push_back(blockInfo);
+  }
+
   void ClearSubmitStack()
   {
     mSubmitStack.clear();
@@ -427,11 +434,14 @@ public:
   };
   std::vector<ProgramCache> mProgramCache;
 
+  std::vector<TestGraphicsBuffer*> mAllocatedBuffers;
+
   struct PipelineCache
   {
   };
 
-  std::vector<UniformData> mCustomUniforms;
+  std::vector<UniformData>                                  mCustomUniforms;
+  std::vector<TestGraphicsReflection::TestUniformBlockInfo> mCustomUniformBlocks;
 };
 
 } // namespace Dali
index 8f2e0c7..2706305 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 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.
 
 namespace Dali
 {
-TestGraphicsProgramImpl::TestGraphicsProgramImpl(TestGlAbstraction& gl, const Graphics::ProgramCreateInfo& createInfo, Property::Array& vertexFormats, std::vector<UniformData>& customUniforms)
+TestGraphicsProgramImpl::TestGraphicsProgramImpl(TestGlAbstraction& gl, const Graphics::ProgramCreateInfo& createInfo, Property::Array& vertexFormats, std::vector<UniformData>& customUniforms, std::vector<TestGraphicsReflection::TestUniformBlockInfo>& customUniformBlocks)
 : mGl(gl),
   mId(gl.CreateProgram()),
   mCreateInfo(createInfo),
-  mReflection(gl, mId, vertexFormats, createInfo, customUniforms)
+  mReflection(gl, mId, vertexFormats, createInfo, customUniforms, customUniformBlocks)
 {
   // Ensure active sampler uniforms are set
   mGl.SetCustomUniforms(customUniforms);
index 3899bec..8641800 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_TEST_GRAPHICS_PROGRAM_H
 
 /*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 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.
@@ -27,7 +27,7 @@ namespace Dali
 class TestGraphicsProgramImpl
 {
 public:
-  TestGraphicsProgramImpl(TestGlAbstraction& gl, const Graphics::ProgramCreateInfo& createInfo, Property::Array& vertexFormats, std::vector<UniformData>& customUniforms);
+  TestGraphicsProgramImpl(TestGlAbstraction& gl, const Graphics::ProgramCreateInfo& createInfo, Property::Array& vertexFormats, std::vector<UniformData>& customUniforms, std::vector<TestGraphicsReflection::TestUniformBlockInfo>& customUniformBlocks);
 
   // For API
   const TestGraphicsReflection& GetReflection() const
index 6cb90ba..2822c0a 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 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.
 #include <dali/public-api/object/property-map.h>
 #include <string>
 #include <vector>
+
+extern "C"
+{
+  void tet_infoline(const char* str);
+  void tet_printf(const char* format, ...);
+}
+
 namespace Dali
 {
 namespace
@@ -103,7 +110,7 @@ constexpr int GetSizeForType(Property::Type type)
 
 } // namespace
 
-TestGraphicsReflection::TestGraphicsReflection(TestGlAbstraction& gl, uint32_t programId, Property::Array& vfs, const Graphics::ProgramCreateInfo& createInfo, std::vector<UniformData>& customUniforms)
+TestGraphicsReflection::TestGraphicsReflection(TestGlAbstraction& gl, uint32_t programId, Property::Array& vfs, const Graphics::ProgramCreateInfo& createInfo, std::vector<UniformData>& customUniforms, std::vector<TestGraphicsReflection::TestUniformBlockInfo>& customUniformBlocks)
 : mGl(gl),
   mCustomUniforms(customUniforms)
 {
@@ -226,6 +233,10 @@ TestGraphicsReflection::TestGraphicsReflection(TestGlAbstraction& gl, uint32_t p
   mDefaultUniformBlock.size = offset;
 
   mUniformBlocks.push_back(mDefaultUniformBlock);
+  for(auto& element : customUniformBlocks)
+  {
+    mUniformBlocks.push_back(element);
+  }
 }
 
 uint32_t TestGraphicsReflection::GetVertexAttributeLocation(const std::string& name) const
@@ -247,11 +258,13 @@ uint32_t TestGraphicsReflection::GetVertexAttributeLocation(const std::string& n
 
 Dali::Graphics::VertexInputAttributeFormat TestGraphicsReflection::GetVertexAttributeFormat(uint32_t location) const
 {
+  tet_infoline("Warning, TestGraphicsReflection::GetVertexAttributeFormat is unimplemented\n");
   return Dali::Graphics::VertexInputAttributeFormat{};
 }
 
 std::string TestGraphicsReflection::GetVertexAttributeName(uint32_t location) const
 {
+  tet_infoline("Warning, TestGraphicsReflection::GetVertexAttributeName is unimplemented\n");
   return 0u;
 }
 
@@ -272,7 +285,11 @@ uint32_t TestGraphicsReflection::GetUniformBlockCount() const
 
 uint32_t TestGraphicsReflection::GetUniformBlockBinding(uint32_t index) const
 {
-  return 0u;
+  if(index >= mUniformBlocks.size())
+  {
+    return 0;
+  }
+  return mUniformBlocks[index].binding;
 }
 
 uint32_t TestGraphicsReflection::GetUniformBlockSize(uint32_t index) const
@@ -316,11 +333,13 @@ bool TestGraphicsReflection::GetUniformBlock(uint32_t index, Dali::Graphics::Uni
 
 std::vector<uint32_t> TestGraphicsReflection::GetUniformBlockLocations() const
 {
+  tet_infoline("Warning, TestGraphicsReflection::GetUniformBlockLocations is unimplemented\n");
   return std::vector<uint32_t>{};
 }
 
 std::string TestGraphicsReflection::GetUniformBlockName(uint32_t blockIndex) const
 {
+  tet_infoline("Warning, TestGraphicsReflection::GetUniformBlockName is unimplemented\n");
   return std::string{};
 }
 
@@ -362,11 +381,13 @@ uint32_t TestGraphicsReflection::GetUniformBlockMemberOffset(uint32_t blockIndex
 
 bool TestGraphicsReflection::GetNamedUniform(const std::string& name, Dali::Graphics::UniformInfo& out) const
 {
+  tet_infoline("Warning, TestGraphicsReflection::GetNamedUniform is unimplemented\n");
   return true;
 }
 
 const std::vector<Dali::Graphics::UniformInfo>& TestGraphicsReflection::GetSamplers() const
 {
+  tet_infoline("Warning, TestGraphicsReflection::GetSamplers is unimplemented\n");
   static std::vector<Dali::Graphics::UniformInfo> samplers{};
   return samplers;
 }
index e968bd5..e701e17 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_TEST_GRAPHICS_REFLECTION_H
 
 /*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 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.
@@ -26,7 +26,9 @@ namespace Dali
 class TestGraphicsReflection : public Graphics::Reflection
 {
 public:
-  TestGraphicsReflection(TestGlAbstraction& gl, uint32_t program_id, Property::Array& vertexFormats, const Graphics::ProgramCreateInfo& createInfo, std::vector<UniformData>& customUniforms);
+  class TestUniformBlockInfo;
+
+  TestGraphicsReflection(TestGlAbstraction& gl, uint32_t program_id, Property::Array& vertexFormats, const Graphics::ProgramCreateInfo& createInfo, std::vector<UniformData>& customUniforms, std::vector<TestUniformBlockInfo>& customUniformBlocks);
 
   uint32_t                                        GetVertexAttributeLocation(const std::string& name) const override;
   Dali::Graphics::VertexInputAttributeFormat      GetVertexAttributeFormat(uint32_t location) const override;
index 782904c..4d86364 100644 (file)
@@ -31,7 +31,7 @@ class TestGraphicsTexture : public Graphics::Texture
 public:
   TestGraphicsTexture(TestGlAbstraction& glAbstraction, const Graphics::TextureCreateInfo& createInfo);
 
-  ~TestGraphicsTexture();
+  ~TestGraphicsTexture() override;
 
   /**
    * Initialize the texture: allocate gl mem, apply default samplers
index 2316036..1284793 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 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.
@@ -248,11 +248,15 @@ int TraceCallStack::FindIndexFromMethodAndParams(std::string method, const Trace
 
       for(auto iter = params.mParams.begin(); iter != params.mParams.end(); ++iter)
       {
-        auto        paramIter = mCallStack[i].namedParams.find(iter->parameterName);
-        std::string value     = paramIter->value.str();
-        std::string iValue    = iter->value.str();
-
-        if(paramIter == mCallStack[i].namedParams.end() || value.compare(iValue))
+        auto paramIter = mCallStack[i].namedParams.find(iter->parameterName);
+        if(paramIter == mCallStack[i].namedParams.end())
+        {
+          match = false;
+          break;
+        }
+        std::string value  = paramIter->value.str();
+        std::string iValue = iter->value.str();
+        if(value.compare(iValue))
         {
           match = false;
           break;
index 2243872..bb1205d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 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.
@@ -201,8 +201,7 @@ int UtcDaliGeometryAddVertexBuffer(void)
   {
     const TestGlAbstraction::BufferDataCalls& bufferDataCalls =
       application.GetGlAbstraction().GetBufferDataCalls();
-
-    DALI_TEST_EQUALS(bufferDataCalls.size(), 3u, TEST_LOCATION);
+    DALI_TEST_EQUALS(bufferDataCalls.size(), 1u, TEST_LOCATION);
 
     DALI_TEST_EQUALS(bufferDataCalls[0], 4 * sizeof(TexturedQuadVertex), TEST_LOCATION);
   }
@@ -322,7 +321,7 @@ int UtcDaliGeometrySetIndexBuffer(void)
     const TestGlAbstraction::BufferDataCalls& bufferDataCalls =
       application.GetGlAbstraction().GetBufferDataCalls();
 
-    DALI_TEST_EQUALS(bufferDataCalls.size(), 3u, TEST_LOCATION);
+    DALI_TEST_EQUALS(bufferDataCalls.size(), 1u, TEST_LOCATION);
 
     DALI_TEST_EQUALS(bufferDataCalls[0], 4 * sizeof(TexturedQuadVertex), TEST_LOCATION);
   }
@@ -381,7 +380,7 @@ int UtcDaliGeometrySetIndexBuffer32Bits(void)
     const TestGlAbstraction::BufferDataCalls& bufferDataCalls =
       application.GetGlAbstraction().GetBufferDataCalls();
 
-    DALI_TEST_EQUALS(bufferDataCalls.size(), 3u, TEST_LOCATION);
+    DALI_TEST_EQUALS(bufferDataCalls.size(), 1u, TEST_LOCATION);
 
     DALI_TEST_EQUALS(bufferDataCalls[0], 4 * sizeof(TexturedQuadVertex), TEST_LOCATION);
   }
index 11219df..a74afa9 100644 (file)
@@ -78,7 +78,7 @@ void utc_dali_render_task_cleanup(void)
  * FinishedSignal                      1+ve
  */
 
-namespace                             // unnamed namespace
+namespace // unnamed namespace
 {
 const int RENDER_FRAME_INTERVAL = 16; ///< Duration of each frame in ms. (at approx 60FPS)
 
@@ -4103,12 +4103,12 @@ int UtcDaliRenderTaskRenderPassTag(void)
   Geometry geometry = Geometry::New();
 
   Property::Map map[2];
-  map[0]["vertex"]     = SHADER_COLOR_TEST_SHADER_VERT1.data();
-  map[0]["fragment"]   = SHADER_COLOR_TEST_SHADER_FRAG.data();
+  map[0]["vertex"]        = SHADER_COLOR_TEST_SHADER_VERT1.data();
+  map[0]["fragment"]      = SHADER_COLOR_TEST_SHADER_FRAG.data();
   map[0]["renderPassTag"] = 0;
 
-  map[1]["vertex"]     = SHADER_COLOR_TEST_SHADER_VERT2.data();
-  map[1]["fragment"]   = SHADER_COLOR_TEST_SHADER_FRAG.data();
+  map[1]["vertex"]        = SHADER_COLOR_TEST_SHADER_VERT2.data();
+  map[1]["fragment"]      = SHADER_COLOR_TEST_SHADER_FRAG.data();
   map[1]["renderPassTag"] = 1;
 
   Property::Array array;
@@ -4122,6 +4122,7 @@ int UtcDaliRenderTaskRenderPassTag(void)
   stage.Add(blue);
 
   auto& gfx = application.GetGraphicsController();
+  gfx.mCallStack.EnableLogging(true);
 
   RenderTaskList renderTaskList = stage.GetRenderTaskList();
   DALI_TEST_EQUALS(0u, renderTaskList.GetTask(0u).GetRenderPassTag(), TEST_LOCATION);
@@ -4197,4 +4198,4 @@ int UtcDaliRenderTaskWithWrongShaderData(void)
   DALI_TEST_EQUALS(0u, renderTaskList.GetTask(0u).GetRenderPassTag(), TEST_LOCATION);
 
   END_TEST;
-}
\ No newline at end of file
+}
index 9038a62..9a2d967 100644 (file)
@@ -20,6 +20,7 @@
 #include <dali/devel-api/common/capabilities.h>
 #include <dali/devel-api/common/stage.h>
 #include <dali/devel-api/rendering/renderer-devel.h>
+#include <dali/integration-api/debug.h>
 #include <dali/integration-api/render-task-list-integ.h>
 #include <dali/public-api/dali-core.h>
 #include <cstdio>
@@ -29,6 +30,7 @@
 #include <dali-test-suite-utils.h>
 #include <mesh-builder.h>
 #include <test-trace-call-stack.h>
+#include "test-actor-utils.h"
 #include "test-graphics-command-buffer.h"
 
 using namespace Dali;
@@ -4596,3 +4598,340 @@ int UtcDaliRendererVertexRange(void)
   DALI_TEST_EQUALS(drawTrace.CountMethod("DrawArrays"), 10, TEST_LOCATION);
   END_TEST;
 }
+
+TestGraphicsBuffer* FindUniformBuffer(int bufferIndex, TestGraphicsController& graphics)
+{
+  int counter = 0;
+  for(auto bufferPtr : graphics.mAllocatedBuffers)
+  {
+    if(((bufferPtr->mCreateInfo.usage & (0 | Graphics::BufferUsage::UNIFORM_BUFFER)) > 0) &&
+       !(bufferPtr->mCpuOnly))
+    {
+      if(counter == bufferIndex)
+      {
+        return bufferPtr;
+      }
+      ++counter;
+    }
+  }
+  return nullptr;
+}
+
+void CreateRendererProperties(Renderer renderer, const Matrix& m, const Matrix& n)
+{
+  for(int i = 0; i < 300; ++i)
+  {
+    std::ostringstream property;
+    property << "uBone[" << i << "]";
+    if(i < 299)
+      renderer.RegisterProperty(property.str(), m);
+    else
+      renderer.RegisterProperty(property.str(), n);
+  }
+  renderer.RegisterProperty("uNumberOfBlendShapes", 55.0f);
+  float weight = 0.5f;
+  for(int i = 0; i < 128; ++i)
+  {
+    std::ostringstream property;
+    property << "uBlendShapeWeight[" << i << "]";
+    renderer.RegisterProperty(property.str(), weight);
+  }
+  float w1                           = 0.01f;
+  float w2                           = 0.5f;
+  float w3                           = 0.79f;
+  renderer["uBlendShapeWeight[0]"]   = w1;
+  renderer["uBlendShapeWeight[55]"]  = w2;
+  renderer["uBlendShapeWeight[127]"] = w3;
+}
+
+int UtcDaliRendererUniformBlocks01(void)
+{
+  setenv("LOG_UNIFORM_BUFFER", "5f", 1); // Turns on buffer logging
+  TestApplication application;
+
+  tet_infoline("Test that uniforms in blocks are written to a gpu buffer");
+  auto& graphics = application.GetGraphicsController();
+  auto& gl       = application.GetGlAbstraction();
+  gl.mBufferTrace.EnableLogging(true);
+
+  gl.SetUniformBufferOffsetAlignment(1024); // Arbitrarily big to easily see it work in debug
+
+  const int MAX_BONE_COUNT{300};
+  const int skinningBlockSize = MAX_BONE_COUNT * sizeof(Matrix);
+
+  graphics.AddCustomUniformBlock(TestGraphicsReflection::TestUniformBlockInfo{"Skinning Block", 0, 0, skinningBlockSize, {{"uBone", Graphics::UniformClass::UNIFORM, 0, 0, {0}, {1}, MAX_BONE_COUNT, Property::Type::MATRIX}}});
+
+  const int MAX_MORPH_COUNT{128};
+  const int morphBlockSize = MAX_MORPH_COUNT * sizeof(float) + sizeof(float);
+  graphics.AddCustomUniformBlock(
+    TestGraphicsReflection::TestUniformBlockInfo{"MorphBlock", 0, 1, morphBlockSize, {{"uNumberOfBlendShapes", Graphics::UniformClass::UNIFORM, 0, 2, {0}, {2}, 0, Property::Type::FLOAT}, {"uBlendShapeWeight", Graphics::UniformClass::UNIFORM, 0, 2, {4}, {3}, MAX_MORPH_COUNT, Property::Type::FLOAT}}});
+
+  Actor    actor    = CreateActor(application.GetScene().GetRootLayer(), 0, TEST_LOCATION);
+  Shader   shader   = CreateShader(); // Don't care about src content
+  Geometry geometry = CreateQuadGeometry();
+  Renderer renderer = CreateRenderer(actor, geometry, shader, 0);
+  Matrix   m, n;
+  m.SetIdentity();
+  n.SetIdentity();
+  n.SetTransformComponents(Vector3(2.f, 2.f, 2.f), Quaternion(Radian(0.3f), Vector3::YAXIS), Vector3(200.0f, 1.0f, 20.0f));
+
+  CreateRendererProperties(renderer, m, n);
+
+  TraceCallStack& graphicsTrace = graphics.mCallStack;
+  TraceCallStack& cmdTrace      = graphics.mCommandBufferCallStack;
+  graphicsTrace.EnableLogging(true);
+  cmdTrace.EnableLogging(true);
+
+  application.SendNotification();
+  application.Render();
+
+  // We expect 1 vertex buffer, 1 index buffer and 1 uniform buffer (representing 2 blocks)
+  DALI_TEST_EQUALS(cmdTrace.CountMethod("BindUniformBuffers"), 1, TEST_LOCATION);
+
+  tet_infoline("Test that uBone[299] is written correctly");
+
+  bool found = false;
+  for(auto bufferPtr : graphics.mAllocatedBuffers)
+  {
+    if(((bufferPtr->mCreateInfo.usage & (0 | Graphics::BufferUsage::UNIFORM_BUFFER)) > 0) &&
+       !(bufferPtr->mCpuOnly))
+    {
+      // We have a GPU uniform buffer. Probably the right one.
+      // The custom uniform block above should point us to the right spot...
+      DALI_TEST_CHECK(bufferPtr->memory.size() >= skinningBlockSize);
+      found        = true;
+      Matrix* mPtr = reinterpret_cast<Dali::Matrix*>(&bufferPtr->memory[0] + sizeof(Dali::Matrix) * 299);
+      DALI_TEST_EQUALS(*mPtr, n, 0.0001, TEST_LOCATION);
+      break;
+    }
+  }
+  DALI_TEST_CHECK(found);
+
+  END_TEST;
+}
+
+int UtcDaliRendererUniformBlocks02(void)
+{
+  setenv("LOG_UNIFORM_BUFFER", "5f", 1); // Turns on buffer logging
+  TestApplication application;
+
+  tet_infoline("Test that repeated update/render cycles write into alternative buffers");
+  auto& graphics = application.GetGraphicsController();
+  auto& gl       = application.GetGlAbstraction();
+  gl.mBufferTrace.EnableLogging(true);
+
+  const uint32_t UNIFORM_BLOCK_ALIGNMENT(512);
+  gl.SetUniformBufferOffsetAlignment(UNIFORM_BLOCK_ALIGNMENT);
+
+  const int MAX_BONE_COUNT{300};
+  const int skinningBlockSize = MAX_BONE_COUNT * sizeof(Matrix);
+
+  graphics.AddCustomUniformBlock(TestGraphicsReflection::TestUniformBlockInfo{"Skinning Block", 0, 0, skinningBlockSize, {{"uBone", Graphics::UniformClass::UNIFORM, 0, 0, {0}, {1}, MAX_BONE_COUNT, Property::Type::MATRIX}}});
+
+  const int MAX_MORPH_COUNT{128};
+  const int morphBlockSize = MAX_MORPH_COUNT * sizeof(float) + sizeof(float);
+  graphics.AddCustomUniformBlock(
+    TestGraphicsReflection::TestUniformBlockInfo{"MorphBlock", 0, 1, morphBlockSize, {{"uNumberOfBlendShapes", Graphics::UniformClass::UNIFORM, 0, 2, {0}, {2}, 0, Property::Type::FLOAT}, {"uBlendShapeWeight", Graphics::UniformClass::UNIFORM, 0, 2, {4}, {3}, MAX_MORPH_COUNT, Property::Type::FLOAT}}});
+
+  Actor    actor    = CreateActor(application.GetScene().GetRootLayer(), 0, TEST_LOCATION);
+  Shader   shader   = CreateShader(); // Don't care about src content
+  Geometry geometry = CreateQuadGeometry();
+  Renderer renderer = CreateRenderer(actor, geometry, shader, 0);
+  Matrix   m, n;
+  m.SetIdentity();
+  n.SetIdentity();
+  n.SetTransformComponents(Vector3(2.f, 2.f, 2.f), Quaternion(Radian(0.3f), Vector3::YAXIS), Vector3(200.0f, 1.0f, 20.0f));
+
+  CreateRendererProperties(renderer, m, n);
+  float w1                           = 0.01f;
+  float w2                           = 0.5f;
+  float w3                           = 0.79f;
+  renderer["uBlendShapeWeight[0]"]   = w1;
+  renderer["uBlendShapeWeight[55]"]  = w2;
+  renderer["uBlendShapeWeight[127]"] = w3;
+
+  TraceCallStack& graphicsTrace = graphics.mCallStack;
+  TraceCallStack& cmdTrace      = graphics.mCommandBufferCallStack;
+  graphicsTrace.EnableLogging(true);
+  cmdTrace.EnableLogging(true);
+
+  application.SendNotification();
+  application.Render();
+
+  // We expect 1 vertex buffer, 1 index buffer and 1 uniform buffer (representing 2 blocks)
+  DALI_TEST_EQUALS(cmdTrace.CountMethod("BindUniformBuffers"), 1, TEST_LOCATION);
+
+  const uint32_t MORPH_BLOCK_OFFSET = (skinningBlockSize % UNIFORM_BLOCK_ALIGNMENT == 0) ? skinningBlockSize : ((skinningBlockSize / UNIFORM_BLOCK_ALIGNMENT) + 1) * UNIFORM_BLOCK_ALIGNMENT;
+
+  for(int i = 0; i < 50; ++i)
+  {
+    tet_infoline("\nTest that uBone[299] is written correctly");
+    TestGraphicsBuffer* bufferPtr = FindUniformBuffer(i % 2, graphics);
+    DALI_TEST_CHECK(graphics.mAllocatedBuffers.size() == (i == 0 ? 4 : 5));
+    DALI_TEST_CHECK(bufferPtr != nullptr);
+    Matrix* mPtr = reinterpret_cast<Dali::Matrix*>(&bufferPtr->memory[0] + sizeof(Dali::Matrix) * 299);
+    DALI_TEST_EQUALS(*mPtr, n, 0.0001, TEST_LOCATION);
+
+    float* wPtr1 = reinterpret_cast<float*>(&bufferPtr->memory[MORPH_BLOCK_OFFSET] + sizeof(float) * 1);
+    float* wPtr2 = reinterpret_cast<float*>(&bufferPtr->memory[MORPH_BLOCK_OFFSET] + sizeof(float) * 56);
+    float* wPtr3 = reinterpret_cast<float*>(&bufferPtr->memory[MORPH_BLOCK_OFFSET] + sizeof(float) * 128);
+
+    tet_printf("Test that uBlendShapeWeight[0] is written correctly as %4.2f\n", w1);
+    tet_printf("Test that uBlendShapeWeight[55] is written correctly as %4.2f\n", w2);
+    tet_printf("Test that uBlendShapeWeight[127] is written correctly as %4.2f\n", w3);
+
+    DALI_TEST_EQUALS(*wPtr1, w1, 0.0001f, TEST_LOCATION);
+    DALI_TEST_EQUALS(*wPtr2, w2, 0.0001f, TEST_LOCATION);
+    DALI_TEST_EQUALS(*wPtr3, w3, 0.0001f, TEST_LOCATION);
+
+    n.SetTransformComponents(Vector3(2.f, 2.f, 2.f), Quaternion(Radian(i * 0.3f), Vector3::YAXIS), Vector3(200.0f + i * 10.0f, -i, 20.0f));
+    renderer["uBone[299]"] = n;
+
+    w1 += 0.005f;
+    w2 += 0.005f;
+    w3 -= 0.01f;
+    renderer["uBlendShapeWeight[0]"]   = w1;
+    renderer["uBlendShapeWeight[55]"]  = w2;
+    renderer["uBlendShapeWeight[127]"] = w3;
+
+    application.SendNotification();
+    application.Render();
+  }
+
+  END_TEST;
+}
+
+int AlignSize(int size, int align)
+{
+  return (size % align == 0) ? size : ((size / align) + 1) * align;
+}
+
+int UtcDaliRendererUniformBlocks03(void)
+{
+  setenv("LOG_UNIFORM_BUFFER", "5f", 1); // Turns on buffer logging
+  TestApplication application;
+
+  tet_infoline("Test that adding actors grows the uniform buffer");
+  auto& graphics = application.GetGraphicsController();
+  auto& gl       = application.GetGlAbstraction();
+  gl.mBufferTrace.EnableLogging(true);
+
+  const uint32_t UNIFORM_BLOCK_ALIGNMENT(512);
+  gl.SetUniformBufferOffsetAlignment(UNIFORM_BLOCK_ALIGNMENT);
+
+  const int MAX_BONE_COUNT{300};
+  const int skinningBlockSize = MAX_BONE_COUNT * sizeof(Matrix);
+
+  graphics.AddCustomUniformBlock(TestGraphicsReflection::TestUniformBlockInfo{"Skinning Block", 0, 0, skinningBlockSize, {{"uBone", Graphics::UniformClass::UNIFORM, 0, 0, {0}, {1}, MAX_BONE_COUNT, Property::Type::MATRIX}}});
+
+  const int MAX_MORPH_COUNT{128};
+  const int morphBlockSize = MAX_MORPH_COUNT * sizeof(float) + sizeof(float);
+  graphics.AddCustomUniformBlock(
+    TestGraphicsReflection::TestUniformBlockInfo{"MorphBlock", 0, 1, morphBlockSize, {{"uNumberOfBlendShapes", Graphics::UniformClass::UNIFORM, 0, 2, {0}, {2}, 0, Property::Type::FLOAT}, {"uBlendShapeWeight", Graphics::UniformClass::UNIFORM, 0, 2, {4}, {3}, MAX_MORPH_COUNT, Property::Type::FLOAT}}});
+
+  Actor    actor    = CreateActor(application.GetScene().GetRootLayer(), 0, TEST_LOCATION);
+  Shader   shader   = CreateShader(); // Don't care about src content
+  Geometry geometry = CreateQuadGeometry();
+  Renderer renderer = CreateRenderer(actor, geometry, shader, 0);
+  Matrix   m, n;
+  m.SetIdentity();
+  n.SetIdentity();
+  n.SetTransformComponents(Vector3(2.f, 2.f, 2.f), Quaternion(Radian(0.3f), Vector3::YAXIS), Vector3(200.0f, 1.0f, 20.0f));
+
+  CreateRendererProperties(renderer, m, n);
+
+  TraceCallStack& graphicsTrace = graphics.mCallStack;
+  TraceCallStack& cmdTrace      = graphics.mCommandBufferCallStack;
+  graphicsTrace.EnableLogging(true);
+  cmdTrace.EnableLogging(true);
+
+  application.SendNotification();
+  application.Render();
+
+  // We expect 1 vertex buffer, 1 index buffer and 1 uniform buffer (representing 2 blocks)
+  DALI_TEST_EQUALS(cmdTrace.CountMethod("BindUniformBuffers"), 1, TEST_LOCATION);
+
+  unsigned int overallSize = 0;
+
+  for(int i = 0; i < 10; ++i)
+  {
+    overallSize += AlignSize(skinningBlockSize, UNIFORM_BLOCK_ALIGNMENT) + AlignSize(morphBlockSize, UNIFORM_BLOCK_ALIGNMENT);
+
+    DALI_TEST_CHECK(graphics.mAllocatedBuffers.size() == (i == 0 ? 4 : 5));
+
+    TestGraphicsBuffer* bufferPtr = graphics.mAllocatedBuffers.back();
+    tet_printf("\nTest that latest buffer is big enough(%d)>%d\n", bufferPtr->memory.size(), overallSize);
+
+    DALI_TEST_CHECK(bufferPtr->memory.size() >= overallSize);
+
+    Actor actor = CreateActor(application.GetScene().GetRootLayer(), 0, TEST_LOCATION);
+    actor.AddRenderer(renderer);
+    application.GetScene().Add(actor);
+
+    application.SendNotification();
+    application.Render();
+  }
+
+  END_TEST;
+}
+
+int UtcDaliRendererUniformBlocksUnregisterScene01(void)
+{
+  TestApplication application;
+
+  tet_infoline("Test that uniform buffers are unregistered after a scene is destroyed\n");
+
+  auto& graphics = application.GetGraphicsController();
+  auto& gl       = application.GetGlAbstraction();
+  graphics.mCallStack.EnableLogging(true);
+  graphics.mCommandBufferCallStack.EnableLogging(true);
+  gl.mBufferTrace.EnableLogging(true);
+  gl.mBufferTrace.Enable(true);
+
+  Actor dummyActor = CreateRenderableActor(CreateTexture(TextureType::TEXTURE_2D, Pixel::RGB888, 45, 45));
+  application.GetScene().Add(dummyActor);
+  application.SendNotification();
+  application.Render();
+
+  Dali::Integration::Scene scene = Dali::Integration::Scene::New(Size(480.0f, 800.0f));
+  DALI_TEST_CHECK(scene);
+  application.AddScene(scene);
+
+  Actor    actor    = CreateActor(scene.GetRootLayer(), 0, TEST_LOCATION);
+  Shader   shader   = CreateShader(); // Don't really care...
+  Geometry geometry = CreateQuadGeometry();
+  Renderer renderer = CreateRenderer(actor, geometry, shader, 0);
+
+  const int MAX_BONE_COUNT{300};
+  const int skinningBlockSize = MAX_BONE_COUNT * sizeof(Matrix);
+
+  graphics.AddCustomUniformBlock(TestGraphicsReflection::TestUniformBlockInfo{"Skinning Block", 0, 0, skinningBlockSize, {{"uBone", Graphics::UniformClass::UNIFORM, 0, 0, {0}, {1}, MAX_BONE_COUNT, Property::Type::MATRIX}}});
+  Matrix m;
+  m.SetIdentity();
+  for(int i = 0; i < MAX_BONE_COUNT; ++i)
+  {
+    std::ostringstream property;
+    property << "uBone[" << i << "]";
+    renderer.RegisterProperty(property.str(), m);
+  }
+  tet_infoline("--Expect new scene's buffers to be created here");
+  application.SendNotification();
+  application.Render();
+
+  scene.RemoveSceneObject(); // Scene's scene graph lifecycle is NOT managed by scene handle
+  scene.Discard();
+  scene.Reset();
+
+  gl.mBufferTrace.Reset();
+
+  tet_infoline("--Expect UnregisterScene to happen during this render cycle");
+  dummyActor[Actor::Property::SIZE] = Vector3(100, 100, 0);
+  application.SendNotification();
+  application.Render();
+
+  TraceCallStack::NamedParams namedParams;
+  namedParams["id"] << 6;
+  DALI_TEST_CHECK(gl.mBufferTrace.FindMethodAndParams("DeleteBuffers", namedParams));
+
+  END_TEST;
+}
index 605cbd5..d407b36 100644 (file)
@@ -276,7 +276,7 @@ int UtcDaliVertexBufferSetData01(void)
 
     DALI_TEST_CHECK(drawTrace.FindMethod("DrawArrays"));
 
-    DALI_TEST_EQUALS(bufferDataCalls.size(), 3u, TEST_LOCATION);
+    DALI_TEST_EQUALS(bufferDataCalls.size(), 1u, TEST_LOCATION);
 
     DALI_TEST_EQUALS(bufferDataCalls[0], sizeof(texturedQuadVertexData), TEST_LOCATION);
   }
@@ -325,19 +325,16 @@ int UtcDaliVertexBufferSetData02(void)
   application.SendNotification();
   application.Render();
 
-  {
-    const TestGlAbstraction::BufferSubDataCalls& bufferSubDataCalls =
-      application.GetGlAbstraction().GetBufferSubDataCalls();
+  auto&                                        gl                 = application.GetGlAbstraction();
+  const TestGlAbstraction::BufferSubDataCalls& bufferSubDataCalls = gl.GetBufferSubDataCalls();
+  const TestGlAbstraction::BufferDataCalls&    bufferDataCalls    = gl.GetBufferDataCalls();
 
-    const TestGlAbstraction::BufferDataCalls& bufferDataCalls =
-      application.GetGlAbstraction().GetBufferDataCalls();
+  DALI_TEST_EQUALS(bufferSubDataCalls.size(), 0u, TEST_LOCATION);
+  DALI_TEST_EQUALS(bufferDataCalls.size(), 1u, TEST_LOCATION);
+  DALI_TEST_EQUALS(bufferDataCalls[0], sizeof(texturedQuadVertexData), TEST_LOCATION);
 
-    // Should be 1 (Flush standalone uniform buffer per each RenderScene)
-    DALI_TEST_EQUALS(bufferSubDataCalls.size(), 1u, TEST_LOCATION);
-    DALI_TEST_EQUALS(bufferDataCalls.size(), 2u, TEST_LOCATION);
-
-    DALI_TEST_EQUALS(bufferDataCalls[0], sizeof(texturedQuadVertexData), TEST_LOCATION);
-  }
+  gl.ResetBufferDataCalls();
+  gl.ResetBufferSubDataCalls();
 
   // Re-upload the data on the vertexBuffer
   vertexBuffer.SetData(texturedQuadVertexData, 4);
@@ -345,6 +342,10 @@ int UtcDaliVertexBufferSetData02(void)
   application.SendNotification();
   application.Render(0);
 
+  DALI_TEST_EQUALS(bufferSubDataCalls.size(), 0u, TEST_LOCATION);
+  DALI_TEST_EQUALS(bufferDataCalls.size(), 1u, TEST_LOCATION);
+  DALI_TEST_EQUALS(bufferDataCalls[0], sizeof(texturedQuadVertexData), TEST_LOCATION);
+
   END_TEST;
 }
 
index 45b8e6f..e271bc2 100644 (file)
@@ -920,8 +920,8 @@ inline BufferPropertiesFlags operator|(BufferPropertiesFlags flags, BufferProper
  */
 struct MemoryRequirements
 {
-  size_t size;
-  size_t alignment;
+  size_t size{0u};
+  size_t alignment{1u};
 };
 
 using TextureUpdateFlags = uint32_t;
index efa4c8b..ece9186 100644 (file)
@@ -126,7 +126,6 @@ SET( internal_src_files
   ${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 8955f07..fdb9a3b 100644 (file)
@@ -44,7 +44,7 @@
 #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/renderers/uniform-buffer.h>
 #include <dali/internal/render/shaders/program-controller.h>
 
 #include <memory>
@@ -181,7 +181,7 @@ struct RenderManager::Impl
   Vector<Render::TextureKey>        updatedTextures{};     ///< The updated texture list
 
   uint32_t    frameCount{0u};                                                    ///< The current frame count
-  BufferIndex renderBufferIndex{SceneGraphBuffers::INITIAL_UPDATE_BUFFER_INDEX}; ///< The index of the buffer to read from; this is opposite of the "update" buffer
+  BufferIndex renderBufferIndex{SceneGraphBuffers::INITIAL_UPDATE_BUFFER_INDEX}; ///< The index of the buffer to read from;
 
   bool lastFrameWasRendered{false}; ///< Keeps track of the last frame being rendered due to having render instructions
   bool commandBufferSubmitted{false};
@@ -328,10 +328,12 @@ void RenderManager::InitializeScene(SceneGraph::Scene* scene)
 {
   scene->Initialize(mImpl->graphicsController, mImpl->depthBufferAvailable, mImpl->stencilBufferAvailable);
   mImpl->sceneContainer.push_back(scene);
+  mImpl->uniformBufferManager->RegisterScene(scene);
 }
 
 void RenderManager::UninitializeScene(SceneGraph::Scene* scene)
 {
+  mImpl->uniformBufferManager->UnregisterScene(scene);
   auto iter = std::find(mImpl->sceneContainer.begin(), mImpl->sceneContainer.end(), scene);
   if(iter != mImpl->sceneContainer.end())
   {
@@ -437,9 +439,7 @@ void RenderManager::RemoveRenderTracker(Render::RenderTracker* renderTracker)
 void RenderManager::PreRender(Integration::RenderStatus& status, bool forceClear)
 {
   DALI_PRINT_RENDER_START(mImpl->renderBufferIndex);
-
-  // Rollback
-  mImpl->uniformBufferManager->GetUniformBufferViewPool(mImpl->renderBufferIndex)->Rollback();
+  DALI_LOG_INFO(gLogFilter, Debug::Verbose, "\n\nNewFrame %d\n", mImpl->frameCount);
 
   // Increment the frame count at the beginning of each frame
   ++mImpl->frameCount;
@@ -447,13 +447,13 @@ void RenderManager::PreRender(Integration::RenderStatus& status, bool forceClear
   // Process messages queued during previous update
   mImpl->renderQueue.ProcessMessages(mImpl->renderBufferIndex);
 
-  uint32_t count = 0u;
+  uint32_t totalInstructionCount = 0u;
   for(auto& i : mImpl->sceneContainer)
   {
-    count += i->GetRenderInstructions().Count(mImpl->renderBufferIndex);
+    totalInstructionCount += i->GetRenderInstructions().Count(mImpl->renderBufferIndex);
   }
 
-  const bool haveInstructions = count > 0u;
+  const bool haveInstructions = totalInstructionCount > 0u;
 
   DALI_LOG_INFO(gLogFilter, Debug::General, "Render: haveInstructions(%s) || mImpl->lastFrameWasRendered(%s) || forceClear(%s)\n", haveInstructions ? "true" : "false", mImpl->lastFrameWasRendered ? "true" : "false", forceClear ? "true" : "false");
 
@@ -761,7 +761,7 @@ void RenderManager::RenderScene(Integration::RenderStatus& status, Integration::
     return;
   }
 
-  uint32_t count = sceneObject->GetRenderInstructions().Count(mImpl->renderBufferIndex);
+  uint32_t instructionCount = sceneObject->GetRenderInstructions().Count(mImpl->renderBufferIndex);
 
   std::vector<Graphics::RenderTarget*> targetstoPresent;
 
@@ -774,10 +774,77 @@ void RenderManager::RenderScene(Integration::RenderStatus& status, Integration::
     clippingRect = Rect<int>();
   }
 
-  // Prepare to lock and map standalone uniform buffer.
-  mImpl->uniformBufferManager->ReadyToLockUniformBuffer(mImpl->renderBufferIndex);
+  // Prefetch programs before we start rendering so reflection is
+  // ready, and we can pull exact size of UBO needed (no need to resize during drawing)
+  auto totalSizeCPU = 0u;
+  auto totalSizeGPU = 0u;
+
+  for(uint32_t i = 0; i < instructionCount; ++i)
+  {
+    RenderInstruction& instruction = sceneObject->GetRenderInstructions().At(mImpl->renderBufferIndex, i);
+
+    if((instruction.mFrameBuffer != nullptr && renderToFbo) ||
+       (instruction.mFrameBuffer == nullptr && !renderToFbo))
+    {
+      for(auto j = 0u; j < instruction.RenderListCount(); ++j)
+      {
+        const auto& renderList = instruction.GetRenderList(j);
+        for(auto k = 0u; k < renderList->Count(); ++k)
+        {
+          auto& item = renderList->GetItem(k);
+          if(item.mRenderer && item.mRenderer->NeedsProgram())
+          {
+            // Prepare and store used programs for further processing
+            auto program = item.mRenderer->PrepareProgram(instruction);
+            if(program)
+            {
+              auto memoryRequirements = program->GetUniformBlocksMemoryRequirements();
+
+              totalSizeCPU += memoryRequirements.totalCpuSizeRequired;
+              totalSizeGPU += memoryRequirements.totalGpuSizeRequired;
+            }
+          }
+        }
+      }
+    }
+  }
 
-  for(uint32_t i = 0; i < count; ++i)
+  DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Render scene (%s), CPU:%d GPU:%d\n", renderToFbo ? "Offscreen" : "Onscreen", totalSizeCPU, totalSizeGPU);
+
+  auto& uboManager = mImpl->uniformBufferManager;
+
+  uboManager->SetCurrentSceneRenderInfo(sceneObject, renderToFbo);
+  uboManager->Rollback(sceneObject, renderToFbo);
+
+  // Respec UBOs for this frame (orphan buffers or double buffer in the GPU)
+  if(instructionCount)
+  {
+    uboManager->GetUniformBufferForScene(sceneObject, renderToFbo, true)->ReSpecify(totalSizeCPU);
+    uboManager->GetUniformBufferForScene(sceneObject, renderToFbo, false)->ReSpecify(totalSizeGPU);
+  }
+
+#if defined(DEBUG_ENABLED)
+  auto uniformBuffer1 = uboManager->GetUniformBufferForScene(sceneObject, renderToFbo, true);
+  auto uniformBuffer2 = uboManager->GetUniformBufferForScene(sceneObject, renderToFbo, false);
+  if(uniformBuffer1)
+  {
+    DALI_LOG_INFO(gLogFilter, Debug::Verbose, "CPU buffer: Offset(%d), Cap(%d)\n", uniformBuffer1->GetCurrentOffset(), uniformBuffer1->GetCurrentCapacity());
+  }
+  else
+  {
+    DALI_LOG_INFO(gLogFilter, Debug::Verbose, "CPU buffer: nil\n");
+  }
+  if(uniformBuffer2)
+  {
+    DALI_LOG_INFO(gLogFilter, Debug::Verbose, "GPU buffer: Offset(%d), Cap(%d)\n", uniformBuffer2->GetCurrentOffset(), uniformBuffer2->GetCurrentCapacity());
+  }
+  else
+  {
+    DALI_LOG_INFO(gLogFilter, Debug::Verbose, "GPU buffer: nil\n");
+  }
+#endif
+
+  for(uint32_t i = 0; i < instructionCount; ++i)
   {
     RenderInstruction& instruction = sceneObject->GetRenderInstructions().At(mImpl->renderBufferIndex, i);
 
@@ -994,8 +1061,8 @@ void RenderManager::RenderScene(Integration::RenderStatus& status, Integration::
     mainCommandBuffer->EndRenderPass(syncObject);
   }
 
-  // Unlock standalone uniform buffer.
-  mImpl->uniformBufferManager->UnlockUniformBuffer(mImpl->renderBufferIndex);
+  // Flush UBOs
+  mImpl->uniformBufferManager->Flush(sceneObject, renderToFbo);
 
   mImpl->renderAlgorithms.SubmitCommandBuffer();
   mImpl->commandBufferSubmitted = true;
index 56dbcc6..3c8e4af 100644 (file)
@@ -33,8 +33,8 @@
 #include <dali/internal/render/renderers/render-texture.h>
 #include <dali/internal/render/renderers/render-vertex-buffer.h>
 #include <dali/internal/render/renderers/shader-cache.h>
-#include <dali/internal/render/renderers/uniform-buffer-view-pool.h>
 #include <dali/internal/render/renderers/uniform-buffer-view.h>
+#include <dali/internal/render/renderers/uniform-buffer.h>
 #include <dali/internal/render/shaders/program.h>
 #include <dali/internal/render/shaders/render-shader.h>
 #include <dali/internal/update/common/uniform-map.h>
@@ -42,6 +42,9 @@
 
 namespace Dali::Internal
 {
+Dali::Matrix* testMVP;
+uint32_t      mvpBufferIndex;
+
 namespace
 {
 // Helper to get the property value getter by type
@@ -464,6 +467,73 @@ void Renderer::Upload()
   mGeometry->Upload(*mGraphicsController);
 }
 
+bool Renderer::NeedsProgram() const
+{
+  // Our access to shader is currently through the RenderDataProvider, which
+  // returns a reference to the shader (as at the time, we couldn't have empty
+  // renderers). We prefer to keep this as a reference. So, we don't use it
+  // here. Instead, we use the mGeometry pointer to decide if this is an empty
+  // renderer.
+  return (!mRenderCallback && mGeometry != nullptr);
+}
+
+Program* Renderer::PrepareProgram(const SceneGraph::RenderInstruction& instruction)
+{
+  // Create Program
+  const SceneGraph::Shader& shader = mRenderDataProvider->GetShader();
+
+  ShaderDataPtr shaderData = shader.GetShaderData(instruction.mRenderPassTag);
+  if(!shaderData)
+  {
+    DALI_LOG_ERROR("Failed to get shader data.\n");
+    mCurrentProgram = nullptr;
+    return nullptr;
+  }
+
+  Program* program = Program::New(*mProgramCache,
+                                  shaderData,
+                                  *mGraphicsController);
+  if(!program)
+  {
+    DALI_LOG_ERROR("Failed to create program for shader at address %p.\n", reinterpret_cast<const void*>(&shader));
+    mCurrentProgram = nullptr;
+    return nullptr;
+  }
+
+  // If program doesn't have Gfx program object assigned yet, prepare it.
+  if(!program->GetGraphicsProgramPtr())
+  {
+    const std::vector<char>& vertShader   = shaderData->GetShaderForPipelineStage(Graphics::PipelineStage::VERTEX_SHADER);
+    const std::vector<char>& fragShader   = shaderData->GetShaderForPipelineStage(Graphics::PipelineStage::FRAGMENT_SHADER);
+    Dali::Graphics::Shader&  vertexShader = mShaderCache->GetShader(
+      vertShader,
+      Graphics::PipelineStage::VERTEX_SHADER,
+      shaderData->GetSourceMode());
+
+    Dali::Graphics::Shader& fragmentShader = mShaderCache->GetShader(
+      fragShader,
+      Graphics::PipelineStage::FRAGMENT_SHADER,
+      shaderData->GetSourceMode());
+
+    std::vector<Graphics::ShaderState> shaderStates{
+      Graphics::ShaderState()
+        .SetShader(vertexShader)
+        .SetPipelineStage(Graphics::PipelineStage::VERTEX_SHADER),
+      Graphics::ShaderState()
+        .SetShader(fragmentShader)
+        .SetPipelineStage(Graphics::PipelineStage::FRAGMENT_SHADER)};
+
+    auto createInfo = Graphics::ProgramCreateInfo();
+    createInfo.SetShaderState(shaderStates);
+    auto graphicsProgram = mGraphicsController->CreateProgram(createInfo, nullptr);
+    program->SetGraphicsProgram(std::move(graphicsProgram), *mUniformBufferManager); // generates reflection
+  }
+
+  // Set prefetched program to be used during rendering
+  mCurrentProgram = program;
+  return mCurrentProgram;
+}
+
 bool Renderer::Render(Graphics::CommandBuffer&                             commandBuffer,
                       BufferIndex                                          bufferIndex,
                       const SceneGraph::NodeDataProvider&                  node,
@@ -554,87 +624,46 @@ bool Renderer::Render(Graphics::CommandBuffer&                             comma
     blend = (commands[0]->queue != DevelRenderer::RENDER_QUEUE_OPAQUE) && blend;
   }
 
-  // Create Program
-  ShaderDataPtr shaderData = mRenderDataProvider->GetShader().GetShaderData(instruction.mRenderPassTag);
-  if(!shaderData)
-  {
-    DALI_LOG_ERROR("Failed to get shader data.\n");
-    return false;
-  }
+  bool drawn = false;
 
-  Program* program = Program::New(*mProgramCache,
-                                  shaderData,
-                                  *mGraphicsController);
-  if(!program)
+  // We should have a shader here (as only RenderCallback has no shader, and that's been early out)
+  Program* program = PrepareProgram(instruction);
+  if(program)
   {
-    DALI_LOG_ERROR("Failed to get program for shader at address %p.\n", reinterpret_cast<const void*>(&mRenderDataProvider->GetShader()));
-    return false;
-  }
+    // Prepare the graphics pipeline. This may either re-use an existing pipeline or create a new one.
+    auto& pipeline = PrepareGraphicsPipeline(*program, instruction, node, blend);
 
-  // If program doesn't have Gfx program object assigned yet, prepare it.
-  if(!program->GetGraphicsProgramPtr())
-  {
-    const std::vector<char>& vertShader   = shaderData->GetShaderForPipelineStage(Graphics::PipelineStage::VERTEX_SHADER);
-    const std::vector<char>& fragShader   = shaderData->GetShaderForPipelineStage(Graphics::PipelineStage::FRAGMENT_SHADER);
-    Dali::Graphics::Shader&  vertexShader = mShaderCache->GetShader(
-      vertShader,
-      Graphics::PipelineStage::VERTEX_SHADER,
-      shaderData->GetSourceMode());
+    commandBuffer.BindPipeline(pipeline);
+    BindTextures(commandBuffer, boundTextures);
 
-    Dali::Graphics::Shader& fragmentShader = mShaderCache->GetShader(
-      fragShader,
-      Graphics::PipelineStage::FRAGMENT_SHADER,
-      shaderData->GetSourceMode());
-
-    std::vector<Graphics::ShaderState> shaderStates{
-      Graphics::ShaderState()
-        .SetShader(vertexShader)
-        .SetPipelineStage(Graphics::PipelineStage::VERTEX_SHADER),
-      Graphics::ShaderState()
-        .SetShader(fragmentShader)
-        .SetPipelineStage(Graphics::PipelineStage::FRAGMENT_SHADER)};
-
-    auto createInfo = Graphics::ProgramCreateInfo();
-    createInfo.SetShaderState(shaderStates);
-    auto graphicsProgram = mGraphicsController->CreateProgram(createInfo, nullptr);
-    program->SetGraphicsProgram(std::move(graphicsProgram));
-  }
-
-  // Prepare the graphics pipeline. This may either re-use an existing pipeline or create a new one.
-  auto& pipeline = PrepareGraphicsPipeline(*program, instruction, node, blend);
-
-  commandBuffer.BindPipeline(pipeline);
-
-  BindTextures(commandBuffer, boundTextures);
-
-  std::size_t nodeIndex = BuildUniformIndexMap(bufferIndex, node, size, *program);
-
-  WriteUniformBuffer(bufferIndex, commandBuffer, program, instruction, node, modelMatrix, modelViewMatrix, viewMatrix, projectionMatrix, size, nodeIndex);
-
-  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
-
-  // Reuse latest bound vertex attributes location, or Bind buffers to attribute locations.
-  if(ReuseLatestBoundVertexAttributes(mGeometry) || mGeometry->BindVertexAttributes(commandBuffer))
-  {
-    uint32_t instanceCount = mRenderDataProvider->GetInstanceCount();
-
-    if(mDrawCommands.empty())
+    if(queueIndex == 0)
     {
-      drawn = mGeometry->Draw(*mGraphicsController, commandBuffer, mIndexedDrawFirstElement, mIndexedDrawElementsCount, instanceCount);
+      std::size_t nodeIndex = BuildUniformIndexMap(bufferIndex, node, size, *program);
+      WriteUniformBuffer(bufferIndex, commandBuffer, program, instruction, node, modelMatrix, modelViewMatrix, viewMatrix, projectionMatrix, size, nodeIndex);
     }
-    else
+    // @todo We should detect this case much earlier to prevent unnecessary work
+    // Reuse latest bound vertex attributes location, or Bind buffers to attribute locations.
+    if(ReuseLatestBoundVertexAttributes(mGeometry) || mGeometry->BindVertexAttributes(commandBuffer))
     {
-      for(auto& cmd : commands)
+      uint32_t instanceCount = mRenderDataProvider->GetInstanceCount();
+
+      if(mDrawCommands.empty())
       {
-        drawn |= mGeometry->Draw(*mGraphicsController, commandBuffer, cmd->firstIndex, cmd->elementCount, instanceCount);
+        drawn = mGeometry->Draw(*mGraphicsController, commandBuffer, mIndexedDrawFirstElement, mIndexedDrawElementsCount, instanceCount);
+      }
+      else
+      {
+        for(auto& cmd : commands)
+        {
+          drawn |= mGeometry->Draw(*mGraphicsController, commandBuffer, cmd->firstIndex, cmd->elementCount, instanceCount);
+        }
       }
     }
-  }
-  else
-  {
-    // BindVertexAttributes failed. Reset cached geometry.
-    ReuseLatestBoundVertexAttributes(nullptr);
+    else
+    {
+      // BindVertexAttributes failed. Reset cached geometry.
+      ReuseLatestBoundVertexAttributes(nullptr);
+    }
   }
 
   return drawn;
@@ -756,45 +785,51 @@ void Renderer::WriteUniformBuffer(
   const Vector3&                       size,
   std::size_t                          nodeIndex)
 {
-  // Create the UBO
-  uint32_t uboOffset{0u};
-
   auto& reflection = mGraphicsController->GetProgramReflection(program->GetGraphicsProgram());
 
-  uint32_t uniformBlockAllocationBytes = program->GetUniformBlocksMemoryRequirements().totalSizeRequired;
+  const auto& programRequirements = program->GetUniformBlocksMemoryRequirements();
+
+  // Allocate UBO view per each block (include standalone block)
+  auto blockCount = programRequirements.blockCount;
 
-  // Create uniform buffer view from uniform buffer
-  Graphics::UniquePtr<Render::UniformBufferView> uboView{nullptr};
-  if(uniformBlockAllocationBytes)
+  std::vector<std::unique_ptr<UniformBufferView>> uboViews;
+  uboViews.resize(blockCount);
+
+  // Prepare bindings
+  auto uboCount = reflection.GetUniformBlockCount();
+  mUniformBufferBindings.resize(uboCount);
+
+  for(auto i = 0u; i < blockCount; ++i)
   {
-    auto uboPoolView = mUniformBufferManager->GetUniformBufferViewPool(bufferIndex);
-    uboView          = uboPoolView->CreateUniformBufferView(uniformBlockAllocationBytes);
+    bool standaloneUniforms = (i == 0);
+    if(programRequirements.blockSize[i])
+    {
+      auto uniformBufferView             = mUniformBufferManager->CreateUniformBufferView(programRequirements.blockSize[i], standaloneUniforms);
+      mUniformBufferBindings[i].buffer   = uniformBufferView->GetBuffer();
+      mUniformBufferBindings[i].offset   = uniformBufferView->GetOffset();
+      mUniformBufferBindings[i].binding  = standaloneUniforms ? 0 : i - 1;
+      mUniformBufferBindings[i].dataSize = reflection.GetUniformBlockSize(i);
+      uboViews[i].reset(uniformBufferView.release());
+    }
   }
 
   // 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
-  if(uboView)
+  if(!uboViews.empty())
   {
-    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), *uboView, modelMatrix);
-    WriteDefaultUniform(program->GetDefaultUniform(Program::DefaultUniformIndex::VIEW_MATRIX), *uboView, viewMatrix);
-    WriteDefaultUniform(program->GetDefaultUniform(Program::DefaultUniformIndex::PROJECTION_MATRIX), *uboView, projectionMatrix);
-    WriteDefaultUniform(program->GetDefaultUniform(Program::DefaultUniformIndex::MODEL_VIEW_MATRIX), *uboView, modelViewMatrix);
+    WriteDefaultUniformV2(program->GetDefaultUniform(Program::DefaultUniformIndex::MODEL_MATRIX), uboViews, modelMatrix);
+    WriteDefaultUniformV2(program->GetDefaultUniform(Program::DefaultUniformIndex::VIEW_MATRIX), uboViews, viewMatrix);
+    WriteDefaultUniformV2(program->GetDefaultUniform(Program::DefaultUniformIndex::PROJECTION_MATRIX), uboViews, projectionMatrix);
+    WriteDefaultUniformV2(program->GetDefaultUniform(Program::DefaultUniformIndex::MODEL_VIEW_MATRIX), uboViews, modelViewMatrix);
 
     auto mvpUniformInfo = program->GetDefaultUniform(Program::DefaultUniformIndex::MVP_MATRIX);
     if(mvpUniformInfo && !mvpUniformInfo->name.empty())
     {
       Matrix modelViewProjectionMatrix(false);
       MatrixUtils::MultiplyProjectionMatrix(modelViewProjectionMatrix, modelViewMatrix, projectionMatrix);
-      WriteDefaultUniform(mvpUniformInfo, *uboView, modelViewProjectionMatrix);
+      WriteDefaultUniformV2(mvpUniformInfo, uboViews, modelViewProjectionMatrix);
     }
 
     auto normalUniformInfo = program->GetDefaultUniform(Program::DefaultUniformIndex::NORMAL_MATRIX);
@@ -803,7 +838,7 @@ void Renderer::WriteUniformBuffer(
       Matrix3 normalMatrix(modelViewMatrix);
       normalMatrix.Invert();
       normalMatrix.Transpose();
-      WriteDefaultUniform(normalUniformInfo, *uboView, normalMatrix);
+      WriteDefaultUniformV2(normalUniformInfo, uboViews, normalMatrix);
     }
 
     Vector4        finalColor;                               ///< Applied renderer's opacity color
@@ -817,16 +852,17 @@ void Renderer::WriteUniformBuffer(
     {
       finalColor = Vector4(color.r, color.g, color.b, color.a * mRenderDataProvider->GetOpacity(bufferIndex));
     }
-    WriteDefaultUniform(program->GetDefaultUniform(Program::DefaultUniformIndex::COLOR), *uboView, finalColor);
-    WriteDefaultUniform(program->GetDefaultUniform(Program::DefaultUniformIndex::ACTOR_COLOR), *uboView, color);
+
+    WriteDefaultUniformV2(program->GetDefaultUniform(Program::DefaultUniformIndex::COLOR), uboViews, finalColor);
+    WriteDefaultUniformV2(program->GetDefaultUniform(Program::DefaultUniformIndex::ACTOR_COLOR), uboViews, color);
 
     // Write uniforms from the uniform map
-    FillUniformBuffer(*program, instruction, *uboView, bindings, uboOffset, bufferIndex, nodeIndex);
+    FillUniformBuffer(*program, instruction, uboViews, bufferIndex, nodeIndex);
 
     // Write uSize in the end, as it shouldn't be overridable by dynamic properties.
-    WriteDefaultUniform(program->GetDefaultUniform(Program::DefaultUniformIndex::SIZE), *uboView, size);
+    WriteDefaultUniformV2(program->GetDefaultUniform(Program::DefaultUniformIndex::SIZE), uboViews, size);
 
-    commandBuffer.BindUniformBuffers(*bindings);
+    commandBuffer.BindUniformBuffers(mUniformBufferBindings);
   }
 }
 
@@ -842,6 +878,17 @@ bool Renderer::WriteDefaultUniform(const Graphics::UniformInfo* uniformInfo, Ren
 }
 
 template<class T>
+bool Renderer::WriteDefaultUniformV2(const Graphics::UniformInfo* uniformInfo, const std::vector<std::unique_ptr<Render::UniformBufferView>>& uboViews, const T& data)
+{
+  if(uniformInfo && !uniformInfo->name.empty())
+  {
+    WriteUniform(*uboViews[uniformInfo->bufferIndex], *uniformInfo, data);
+    return true;
+  }
+  return false;
+}
+
+template<class T>
 void Renderer::WriteUniform(Render::UniformBufferView& ubo, const Graphics::UniformInfo& uniformInfo, const T& data)
 {
   WriteUniform(ubo, uniformInfo, &data, sizeof(T));
@@ -852,84 +899,68 @@ void Renderer::WriteUniform(Render::UniformBufferView& ubo, const Graphics::Unif
   ubo.Write(data, size, ubo.GetOffset() + uniformInfo.offset);
 }
 
-void Renderer::FillUniformBuffer(Program&                                      program,
-                                 const SceneGraph::RenderInstruction&          instruction,
-                                 Render::UniformBufferView&                    ubo,
-                                 std::vector<Graphics::UniformBufferBinding>*& outBindings,
-                                 uint32_t&                                     offset,
-                                 BufferIndex                                   updateBufferIndex,
-                                 std::size_t                                   nodeIndex)
+void Renderer::FillUniformBuffer(Program&                                                       program,
+                                 const SceneGraph::RenderInstruction&                           instruction,
+                                 const std::vector<std::unique_ptr<Render::UniformBufferView>>& uboViews,
+                                 BufferIndex                                                    updateBufferIndex,
+                                 std::size_t                                                    nodeIndex)
 {
-  auto& reflection = mGraphicsController->GetProgramReflection(program.GetGraphicsProgram());
-  auto  uboCount   = reflection.GetUniformBlockCount();
-
-  // Setup bindings
-  uint32_t dataOffset = offset;
-  for(auto i = 0u; i < uboCount; ++i)
+  for(auto& iter : mUniformIndexMaps[nodeIndex])
   {
-    mUniformBufferBindings[i].dataSize = reflection.GetUniformBlockSize(i);
-    mUniformBufferBindings[i].binding  = reflection.GetUniformBlockBinding(i);
-
-    dataOffset += GetUniformBufferDataAlignment(mUniformBufferBindings[i].dataSize);
-    mUniformBufferBindings[i].buffer = ubo.GetBuffer(&mUniformBufferBindings[i].offset);
-
-    for(auto iter = mUniformIndexMaps[nodeIndex].begin(),
-             end  = mUniformIndexMaps[nodeIndex].end();
-        iter != end;
-        ++iter)
+    auto& uniform    = iter;
+    int   arrayIndex = uniform.arrayIndex;
+    if(!uniform.uniformFunc)
     {
-      auto& uniform    = *iter;
-      int   arrayIndex = uniform.arrayIndex;
-
-      if(!uniform.uniformFunc)
+      auto uniformInfo  = Graphics::UniformInfo{};
+      auto uniformFound = program.GetUniform(uniform.uniformName.GetStringView(),
+                                             uniform.uniformNameHash,
+                                             uniform.uniformNameHashNoArray,
+                                             uniformInfo);
+
+      UniformBufferView* ubo = nullptr;
+      if(uniformFound)
       {
-        auto uniformInfo  = Graphics::UniformInfo{};
-        auto uniformFound = program.GetUniform(uniform.uniformName.GetStringView(),
-                                               uniform.uniformNameHash,
-                                               uniform.uniformNameHashNoArray,
-                                               uniformInfo);
-
-        uniform.uniformOffset   = uniformInfo.offset;
-        uniform.uniformLocation = uniformInfo.location;
-
-        if(uniformFound)
-        {
-          auto       dst      = ubo.GetOffset() + uniformInfo.offset;
-          const auto typeSize = GetPropertyValueSizeForUniform((*iter).propertyValue->GetType());
-          const auto dest     = dst + static_cast<uint32_t>(typeSize) * arrayIndex;
-          const auto func     = GetPropertyValueGetter((*iter).propertyValue->GetType());
-
-          ubo.Write(&((*iter).propertyValue->*func)(updateBufferIndex),
-                    typeSize,
-                    dest);
-
-          uniform.uniformSize = typeSize;
-          uniform.uniformFunc = func;
-        }
+        ubo = uboViews[uniformInfo.bufferIndex].get();
       }
       else
       {
-        auto       dst      = ubo.GetOffset() + uniform.uniformOffset;
-        const auto typeSize = uniform.uniformSize;
-        const auto dest     = dst + static_cast<uint32_t>(typeSize) * arrayIndex;
-        const auto func     = uniform.uniformFunc;
-
-        ubo.Write(&((*iter).propertyValue->*func)(updateBufferIndex),
-                  typeSize,
-                  dest);
+        continue;
       }
+
+      uniform.uniformOffset     = uniformInfo.offset;
+      uniform.uniformLocation   = uniformInfo.location;
+      uniform.uniformBlockIndex = uniformInfo.bufferIndex;
+
+      auto       dst      = ubo->GetOffset() + uniformInfo.offset;
+      const auto typeSize = GetPropertyValueSizeForUniform(iter.propertyValue->GetType());
+      const auto dest     = dst + static_cast<uint32_t>(typeSize) * arrayIndex;
+      const auto func     = GetPropertyValueGetter(iter.propertyValue->GetType());
+      uniform.uniformSize = typeSize;
+      uniform.uniformFunc = func;
+
+      ubo->Write(&(iter.propertyValue->*func)(updateBufferIndex),
+                 typeSize,
+                 dest);
     }
-  }
-  // write output bindings
-  outBindings = &mUniformBufferBindings;
+    else
+    {
+      UniformBufferView* ubo = uboViews[uniform.uniformBlockIndex].get();
+
+      auto       dst      = ubo->GetOffset() + uniform.uniformOffset;
+      const auto typeSize = uniform.uniformSize;
+      const auto dest     = dst + static_cast<uint32_t>(typeSize) * arrayIndex;
+      const auto func     = uniform.uniformFunc;
 
-  // Update offset
-  offset = dataOffset;
+      ubo->Write(&(iter.propertyValue->*func)(updateBufferIndex),
+                 typeSize,
+                 dest);
+    }
+  }
 }
 
 void Renderer::SetSortAttributes(SceneGraph::RenderInstructionProcessor::SortAttributes& sortAttributes) const
 {
-  sortAttributes.shader   = &(mRenderDataProvider->GetShader());
+  sortAttributes.shader   = &mRenderDataProvider->GetShader();
   sortAttributes.geometry = mGeometry;
 }
 
index 1f4aa6e..5af3bb7 100644 (file)
@@ -60,6 +60,7 @@ struct ShaderCache;
 class PipelineCache;
 class PipelineCacheL2;
 class UniformBufferManager;
+class UniformBufferV2;
 class Renderer;
 
 using PipelineCacheL2Container = std::list<PipelineCacheL2>;
@@ -423,6 +424,20 @@ public:
               uint32_t                                             queueIndex);
 
   /**
+   * Returns true if this will create a draw command with it's own geometry
+   * and shader. Some renderers don't have a shader/geometry, e.g. drawable-actor
+   * creates an empty renderer.
+   */
+  bool NeedsProgram() const;
+
+  /**
+   * If we need a program, prepare it and return it.
+   * @param[in] instruction The render instruction
+   * @return the prepared program, or nullptr.
+   */
+  Program* PrepareProgram(const SceneGraph::RenderInstruction& instruction);
+
+  /**
    * Sets RenderCallback object
    *
    * @param[in] callback Valid pointer to RenderCallback object
@@ -477,14 +492,19 @@ public:
                            const T&                     data);
 
   template<class T>
+  bool WriteDefaultUniformV2(const Graphics::UniformInfo*                                   uniformInfo,
+                             const std::vector<std::unique_ptr<Render::UniformBufferView>>& uboViews,
+                             const T&                                                       data);
+
+  template<class T>
   void WriteUniform(Render::UniformBufferView&   ubo,
                     const Graphics::UniformInfo& uniformInfo,
                     const T&                     data);
 
-  void WriteUniform(Render::UniformBufferView&   ubo,
-                    const Graphics::UniformInfo& uniformInfo,
-                    const void*                  data,
-                    uint32_t                     size);
+  static void WriteUniform(Render::UniformBufferView&   ubo,
+                           const Graphics::UniformInfo& uniformInfo,
+                           const void*                  data,
+                           uint32_t                     size);
 
   [[nodiscard]] FaceCullingMode::Type GetFaceCullMode() const
   {
@@ -585,18 +605,15 @@ private:
    * @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] uboViews Target uniform buffer object
    * @param[in] updateBufferIndex update buffer index
+   * @param[in] nodeIndex Index of node/renderer pair in mUniformIndexMaps
    */
-  void FillUniformBuffer(Program&                                      program,
-                         const SceneGraph::RenderInstruction&          instruction,
-                         Render::UniformBufferView&                    ubo,
-                         std::vector<Graphics::UniformBufferBinding>*& outBindings,
-                         uint32_t&                                     offset,
-                         BufferIndex                                   updateBufferIndex,
-                         std::size_t                                   nodeIndex);
+  void FillUniformBuffer(Program&                                                       program,
+                         const SceneGraph::RenderInstruction&                           instruction,
+                         const std::vector<std::unique_ptr<Render::UniformBufferView>>& uboViews,
+                         BufferIndex                                                    updateBufferIndex,
+                         std::size_t                                                    nodeIndex);
 
 private:
   Graphics::Controller*           mGraphicsController;
@@ -628,6 +645,7 @@ private:
     int16_t    uniformLocation{0u};
     uint16_t   uniformOffset{0u};
     uint16_t   uniformSize{0u};
+    uint16_t   uniformBlockIndex{0u};
     FuncGetter uniformFunc{0};
   };
 
@@ -662,6 +680,8 @@ private:
   RenderCallback*                               mRenderCallback{nullptr};
   std::unique_ptr<RenderCallbackInput>          mRenderCallbackInput{nullptr};
   std::vector<Graphics::Texture*>               mRenderCallbackTextureBindings{};
+
+  Program* mCurrentProgram{nullptr}; ///< Prefetched program
 };
 
 } // namespace Render
index 5e04f82..df8ee47 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 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.
 #include <dali/internal/render/renderers/uniform-buffer-manager.h>
 
 // INTERNAL INCLUDES
-#include <dali/internal/render/renderers/uniform-buffer-view-pool.h>
 #include <dali/internal/render/renderers/uniform-buffer-view.h>
 #include <dali/internal/render/renderers/uniform-buffer.h>
 
+#include <dali/internal/update/common/scene-graph-scene.h>
+
 #include <dali/graphics-api/graphics-buffer-create-info.h>
 #include <dali/graphics-api/graphics-buffer.h>
 
 #include <cstring>
 #include <memory>
 
+namespace
+{
+uint32_t CPU_MEMORY_ALIGNMENT{256};
+}
+
 namespace Dali::Internal::Render
 {
 UniformBufferManager::UniformBufferManager(Dali::Graphics::Controller* controller)
@@ -38,47 +44,188 @@ UniformBufferManager::UniformBufferManager(Dali::Graphics::Controller* controlle
 
 UniformBufferManager::~UniformBufferManager() = default;
 
-Graphics::UniquePtr<UniformBuffer> UniformBufferManager::AllocateUniformBuffer(uint32_t size, uint32_t alignment)
+void UniformBufferManager::SetCurrentSceneRenderInfo(SceneGraph::Scene* scene, bool offscreen)
 {
-  // TODO : Current code only assume CPU_ALLOCATED uniform buffer now
-  return Graphics::UniquePtr<UniformBuffer>(
-    new UniformBuffer(mController,
-                      size,
-                      alignment,
-                      Dali::Graphics::BufferUsageFlags{0u} | Dali::Graphics::BufferUsage::TRANSFER_DST | Dali::Graphics::BufferUsage::UNIFORM_BUFFER,
-                      Dali::Graphics::BufferPropertiesFlags{0u} | Dali::Graphics::BufferPropertiesFlagBit::CPU_ALLOCATED));
+  mCurrentSceneOffscreen = offscreen;
+  mCurrentScene          = scene;
+
+  // Only works if RegisterScene has been called...
+  mCurrentUBOSet = FindSetForScene(scene);
 }
 
-Graphics::UniquePtr<UniformBufferView> UniformBufferManager::CreateUniformBufferView(UniformBuffer* uniformBuffer, uint32_t offset, uint32_t size)
+Graphics::UniquePtr<UniformBufferView> UniformBufferManager::CreateUniformBufferView(uint32_t size, bool emulated)
 {
   // Allocate offset of given UBO (allocation strategy may reuse memory)
-  return Graphics::UniquePtr<UniformBufferView>(new UniformBufferView(*uniformBuffer, offset, size));
+
+  DALI_ASSERT_DEBUG(mCurrentUBOSet && "UBO set should exist when creating view");
+  if(!mCurrentUBOSet)
+  {
+    return Graphics::UniquePtr<UniformBufferView>(nullptr);
+  }
+
+  UBOSet::BufferType                    bufferType = UBOSet::GetBufferType(mCurrentSceneOffscreen, emulated);
+  Graphics::UniquePtr<UniformBufferV2>& ubo        = mCurrentUBOSet->GetBuffer(bufferType);
+
+  // Use current offset and increment it after
+  auto offset = ubo->GetCurrentOffset();
+  auto retval = Graphics::UniquePtr<UniformBufferView>(new UniformBufferView(*ubo.get(), offset, size));
+
+  // make sure new offset will meet alignment requirements
+  uint32_t alignedSize = ubo->AlignSize(size);
+  ubo->IncrementOffsetBy(alignedSize);
+  return retval;
+}
+
+void UniformBufferManager::RegisterScene(SceneGraph::Scene* scene)
+{
+  auto iter = mUBOMap.find(scene);
+  if(iter == mUBOMap.end())
+  {
+    // Create new UBO set
+    UBOSet uboSet;
+
+    uint32_t cpuAlignment = GetUniformBlockAlignment(true);
+    uint32_t gpuAlignment = GetUniformBlockAlignment(false);
+
+    // Create all buffers per scene
+    uboSet.cpuBufferOnScreen  = Render::UniformBufferV2::New(mController, true, cpuAlignment);
+    uboSet.gpuBufferOnScreen  = Render::UniformBufferV2::New(mController, false, gpuAlignment);
+    uboSet.cpuBufferOffScreen = Render::UniformBufferV2::New(mController, true, cpuAlignment);
+    uboSet.gpuBufferOffScreen = Render::UniformBufferV2::New(mController, false, gpuAlignment);
+
+    mUBOMap.emplace(std::pair(scene, std::move(uboSet)));
+  }
+}
+
+void UniformBufferManager::UnregisterScene(SceneGraph::Scene* scene)
+{
+  auto iter = mUBOMap.find(scene);
+  if(iter != mUBOMap.end())
+  {
+    mUBOMap.erase(iter);
+  }
+}
+
+UniformBufferV2* UniformBufferManager::GetUniformBufferForScene(SceneGraph::Scene* scene, bool offscreen, bool emulated)
+{
+  auto* uboSet = GetUBOSetForScene(scene);
+  if(!uboSet)
+  {
+    return nullptr;
+  }
+  DALI_ASSERT_DEBUG(mCurrentScene == scene && "Scene should match current cache");
+  return uboSet->GetBuffer(UBOSet::GetBufferType(offscreen, emulated)).get();
+}
+
+void UniformBufferManager::Rollback(SceneGraph::Scene* scene, bool offscreen)
+{
+  auto* uboSet = GetUBOSetForScene(scene);
+  if(uboSet)
+  {
+    if(offscreen)
+    {
+      uboSet->cpuBufferOffScreen->Rollback();
+      uboSet->gpuBufferOffScreen->Rollback();
+    }
+    else
+    {
+      uboSet->cpuBufferOnScreen->Rollback();
+      uboSet->gpuBufferOnScreen->Rollback();
+    }
+  }
+}
+
+void UniformBufferManager::Flush(SceneGraph::Scene* scene, bool offscreen)
+{
+  auto* uboSet = GetUBOSetForScene(scene);
+  if(uboSet)
+  {
+    if(offscreen)
+    {
+      uboSet->cpuBufferOffScreen->Flush();
+      uboSet->gpuBufferOffScreen->Flush();
+    }
+    else
+    {
+      uboSet->cpuBufferOnScreen->Flush();
+      uboSet->gpuBufferOnScreen->Flush();
+    }
+  }
 }
 
-Graphics::UniquePtr<UniformBufferViewPool> UniformBufferManager::CreateUniformBufferViewPool()
+uint32_t UniformBufferManager::GetUniformBlockAlignment(bool emulated)
 {
-  return Graphics::UniquePtr<UniformBufferViewPool>(
-    new UniformBufferViewPool(*this, 1));
+  if(emulated)
+  {
+    return CPU_MEMORY_ALIGNMENT;
+  }
+
+  if(mCachedUniformBlockAlignment == 0)
+  {
+    /* Getting the block alignment. This is a little complicated for the following reasons:
+     *
+     * On GL, this is as simple as calling glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT).
+     *
+     * On Vulkan, this needs a test buffer to be allocated with the right usage flags,
+     * and GetBufferMemoryRequirements will return alignment that would then be common
+     * to all similar buffers.
+     *
+     * We have generally copied the Vulkan api into our GraphicsAPI, with some changes.
+     * So, we have to use Graphics::GetBufferMemoryRequirements() which we call on a test buffer.
+     *
+     * Note, this doesn't change during the graphics context lifetime, so can be cached.
+     */
+
+    Graphics::BufferPropertiesFlags flags = 0u;
+
+    const uint32_t TEST_BUFFER_ALLOCATION{256};
+    auto           createInfo = Graphics::BufferCreateInfo()
+                        .SetSize(TEST_BUFFER_ALLOCATION)
+                        .SetBufferPropertiesFlags(flags)
+                        .SetUsage(0u | Graphics::BufferUsage::UNIFORM_BUFFER);
+
+    Graphics::UniquePtr<Graphics::Buffer> graphicsBuffer = mController->CreateBuffer(createInfo, nullptr);
+    Graphics::MemoryRequirements          requirements   = mController->GetBufferMemoryRequirements(*graphicsBuffer.get());
+    mCachedUniformBlockAlignment                         = requirements.alignment;
+  }
+
+  return mCachedUniformBlockAlignment;
 }
 
-[[nodiscard]] UniformBufferViewPool* UniformBufferManager::GetUniformBufferViewPool(uint32_t bufferIndex)
+UniformBufferManager::UBOSet* UniformBufferManager::GetUBOSetForScene(SceneGraph::Scene* scene)
 {
-  if(!mUniformBufferPoolStorage[bufferIndex])
+  if(mCurrentScene == scene && mCurrentScene == scene)
   {
-    // create new uniform buffer view pool with default (initial) capacity
-    mUniformBufferPoolStorage[bufferIndex] = CreateUniformBufferViewPool();
+    return mCurrentUBOSet;
   }
-  return mUniformBufferPoolStorage[bufferIndex].get();
+  mCurrentUBOSet = FindSetForScene(scene);
+  return mCurrentUBOSet;
 }
 
-void UniformBufferManager::ReadyToLockUniformBuffer(uint32_t bufferIndex)
+// Find efficiently without caching
+UniformBufferManager::UBOSet* UniformBufferManager::FindSetForScene(SceneGraph::Scene* scene)
 {
-  GetUniformBufferViewPool(bufferIndex)->ReadyToLockUniformBuffer();
+  if(mUBOMap.size() == 1 && mUBOMap.begin()->first == scene)
+  {
+    return &mUBOMap.begin()->second;
+  }
+  else
+  {
+    auto iter = mUBOMap.find(scene);
+    if(iter != mUBOMap.end())
+    {
+      return &iter->second;
+    }
+  }
+  return nullptr;
 }
 
-void UniformBufferManager::UnlockUniformBuffer(uint32_t bufferIndex)
+UniformBufferManager::UBOSet::UBOSet(UniformBufferManager::UBOSet&& rhs)
 {
-  GetUniformBufferViewPool(bufferIndex)->UnlockUniformBuffer();
+  cpuBufferOnScreen.swap(rhs.cpuBufferOnScreen);
+  gpuBufferOnScreen.swap(rhs.gpuBufferOnScreen);
+  cpuBufferOffScreen.swap(rhs.cpuBufferOffScreen);
+  gpuBufferOffScreen.swap(rhs.gpuBufferOffScreen);
 }
 
 } // namespace Dali::Internal::Render
index 4c651d6..fce57f6 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_INTERNAL_UNIFORM_BUFFER_MANAGER_H
 
 /*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 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.
  */
 
 // INTERNAL INCLUDES
+#include <dali/devel-api/common/map-wrapper.h>
 #include <dali/graphics-api/graphics-controller.h>
 
+namespace Dali::Internal::SceneGraph
+{
+class Scene;
+}
+
 namespace Dali::Internal::Render
 {
-class UniformBuffer;
+class UniformBufferV2;
 class UniformBufferView;
-class UniformBufferViewPool;
 
 /**
  * Class UniformBufferManager
@@ -40,66 +45,136 @@ public:
 
   ~UniformBufferManager();
 
+  Graphics::UniquePtr<UniformBufferView> CreateUniformBufferView(uint32_t size, bool emulated = true);
+
   /**
-   * @brief Allocates uniform buffer with given size and alignment
-   * @param size Size of uniform buffer
-   * @param alignment Alignment
-   * @return new UniformBuffer
+   * @brief Registers scene with the manager
+   * The manager creates a set of UBOs per scene.
+   *
+   * @param[in] scene Valid pointer to the scene
    */
-  Graphics::UniquePtr<UniformBuffer> AllocateUniformBuffer(uint32_t size, uint32_t alignment = 256);
+  void RegisterScene(SceneGraph::Scene* scene);
 
   /**
-   * @brief Creates a view on UniformBuffer
+   * @brief Removes association with a scene
    *
-   * @param uniformBuffer
-   * @param size
-   * @return Uniform buffer view
+   * @param[in] scene Valid pointer to a scene object
    */
-  Graphics::UniquePtr<UniformBufferView> CreateUniformBufferView(UniformBuffer* uniformBuffer, uint32_t offset, uint32_t size);
+  void UnregisterScene(SceneGraph::Scene* scene);
 
   /**
-   * @brief Creates uniform buffer pool view
-   * @param size
-   * @return
+   * @brief Get the uniform buffer for the given scene, if current.
    */
-  Graphics::UniquePtr<UniformBufferViewPool> CreateUniformBufferViewPool();
+  UniformBufferV2* GetUniformBufferForScene(SceneGraph::Scene* scene, bool offscreen, bool emulated);
 
   /**
-   * @brief Returns Controller object
-   * @return controller object
+   * @brief must be called when rendering scene starts, this way the manager knows
+   * which UBO set we are going to use.
    */
-  [[nodiscard]] Graphics::Controller& GetController() const
-  {
-    return *mController;
-  }
+  void SetCurrentSceneRenderInfo(SceneGraph::Scene* scene, bool offscreen);
 
   /**
-   * @brief Returns embedded uniform buffer pool view for specified DAli buffer index
-   * @return Pointer to valid uniform buffer pool view
+   * @brief Rolls back UBO set matching conditions
+   * @param[in] scene Valid scene pointer
+   * @param[in] offscreen Offscreen or onscreen
    */
-  [[nodiscard]] UniformBufferViewPool* GetUniformBufferViewPool(uint32_t bufferIndex);
+  void Rollback(SceneGraph::Scene* scene, bool offscreen);
 
   /**
-   * @brief Prepare to lock the uniform buffer so we can write to the standalone uniform map directly.
-   * Uniform buffer will be locked at the first call of UniformBuffer::Write after call this API.
-   * @note After all write done, We should call UnlockUniformBuffer.
-   *
-   * @param bufferIndex current update/render buffer index
+   * @brief Flushes current UBO set
    */
-  void ReadyToLockUniformBuffer(uint32_t bufferIndex);
+  void Flush(SceneGraph::Scene* scene, bool offscreen);
 
   /**
-   * @brief Unlock the uniform buffer.
-   * @note We should call ReadyToLockUniformBuffer before call this.
-   *
-   * @param bufferIndex current update/render buffer index
+   * Gets the uniform block alignment.
+   * It will cache it locally, as this doesn't change during a graphics context's lifetime
+   * @param[in] emulated - True if this is to query CPU alignment, or false to query GPU
+   * alignment.
+   * @return the alignment
    */
-  void UnlockUniformBuffer(uint32_t bufferIndex);
+  uint32_t GetUniformBlockAlignment(bool emulated);
 
 private:
   Dali::Graphics::Controller* mController;
 
-  Graphics::UniquePtr<UniformBufferViewPool> mUniformBufferPoolStorage[2u]; ///< The pool view into UniformBuffer (double buffered)
+  /**
+   * Stores pointers to uniform buffers associated with the scene
+   * There may be up to 4 UBOs created per scene:
+   * - Emulated on-screen
+   * - GPU on-screen
+   * - Emulated off-screen
+   * - GPU off-screen
+   */
+  struct UBOSet
+  {
+    UBOSet()  = default;
+    ~UBOSet() = default;
+
+    UBOSet(UBOSet&& rhs);
+
+    enum class BufferType
+    {
+      CPU_ONSCREEN  = 0,
+      GPU_ONSCREEN  = 1,
+      CPU_OFFSCREEN = 2,
+      GPU_OFFSCREEN = 3,
+    };
+
+    static BufferType GetBufferType(bool offscreen, bool emulated)
+    {
+      if(offscreen)
+      {
+        return (emulated ? BufferType::CPU_OFFSCREEN : BufferType::GPU_OFFSCREEN);
+      }
+      return (emulated ? BufferType::CPU_ONSCREEN : BufferType::GPU_ONSCREEN);
+    }
+
+    Graphics::UniquePtr<UniformBufferV2> cpuBufferOnScreen;
+    Graphics::UniquePtr<UniformBufferV2> gpuBufferOnScreen;
+    Graphics::UniquePtr<UniformBufferV2> cpuBufferOffScreen;
+    Graphics::UniquePtr<UniformBufferV2> gpuBufferOffScreen;
+
+    Graphics::UniquePtr<UniformBufferV2>& GetBuffer(BufferType bufferType)
+    {
+      switch(bufferType)
+      {
+        case BufferType::CPU_ONSCREEN:
+        {
+          return cpuBufferOnScreen;
+        }
+        case BufferType::CPU_OFFSCREEN:
+        {
+          return cpuBufferOffScreen;
+        }
+        case BufferType::GPU_ONSCREEN:
+        {
+          return gpuBufferOnScreen;
+        }
+        case BufferType::GPU_OFFSCREEN:
+        {
+          return gpuBufferOffScreen;
+        }
+      }
+      // Appease the compiler
+      return cpuBufferOnScreen;
+    }
+  };
+
+  /**
+   * Find with caching, updates current cached set
+   */
+  UBOSet* GetUBOSetForScene(SceneGraph::Scene* scene);
+
+  /**
+   * Find without caching
+   */
+  UBOSet* FindSetForScene(SceneGraph::Scene* scene);
+
+  std::map<SceneGraph::Scene*, UBOSet> mUBOMap;
+  SceneGraph::Scene*                   mCurrentScene{nullptr};
+  UBOSet*                              mCurrentUBOSet{nullptr};
+  uint32_t                             mCachedUniformBlockAlignment{0u};
+  bool                                 mCurrentSceneOffscreen{false};
 };
 
 } // namespace Dali::Internal::Render
diff --git a/dali/internal/render/renderers/uniform-buffer-view-pool.cpp b/dali/internal/render/renderers/uniform-buffer-view-pool.cpp
deleted file mode 100644 (file)
index 6cc5150..0000000
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright (c) 2022 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/graphics-api/graphics-buffer-create-info.h>
-#include <dali/graphics-api/graphics-buffer.h>
-#include <dali/internal/render/renderers/uniform-buffer-manager.h>
-#include <dali/internal/render/renderers/uniform-buffer-view.h>
-#include <dali/internal/render/renderers/uniform-buffer.h>
-
-namespace Dali::Internal::Render
-{
-namespace
-{
-// Default UBO page size set to 32kb
-const uint32_t DEFAULT_UBO_PAGE_SIZE = 32768;
-} // namespace
-
-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;
-    if(mAlignment > 1 && newOffset < mCurrentOffset + size)
-    {
-      newOffset += mAlignment;
-    }
-
-    size_t increaseBufferStorageSize = DALI_LIKELY(size > 0) ? ((size - 1) / DEFAULT_UBO_PAGE_SIZE + 1) * DEFAULT_UBO_PAGE_SIZE : DEFAULT_UBO_PAGE_SIZE;
-
-    mUniformBufferStorage->Resize(mUniformBufferStorage->GetSize() + increaseBufferStorageSize, false);
-  }
-
-  // create buffer view from
-  Graphics::UniquePtr<UniformBufferView> uboView = mUboManager.CreateUniformBufferView(mUniformBufferStorage.get(), mCurrentOffset, size);
-
-  // adjust offset
-  mCurrentOffset = newOffset;
-
-  return uboView;
-}
-
-void UniformBufferViewPool::ReadyToLockUniformBuffer()
-{
-  mUniformBufferStorage->ReadyToLockUniformBuffer();
-}
-
-void UniformBufferViewPool::UnlockUniformBuffer()
-{
-  mUniformBufferStorage->UnlockUniformBuffer();
-}
-
-} // 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
deleted file mode 100644 (file)
index 8f7a1bd..0000000
+++ /dev/null
@@ -1,89 +0,0 @@
-#ifndef DALI_INTERNAL_UNIFORM_BUFFER_VIEW_POOL_H
-#define DALI_INTERNAL_UNIFORM_BUFFER_VIEW_POOL_H
-
-/*
- * Copyright (c) 2022 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();
-
-  /**
-   * @brief Rolls back allocation to the beginning of pool
-   */
-  void Rollback();
-
-  /**
-   * @brief Creates view for next free chunk of UBO memory of specified size.
-   */
-  Graphics::UniquePtr<UniformBufferView> CreateUniformBufferView(size_t size);
-
-  /**
-   * @copydoc Dali::Internal::Render::UniformBufferManager::ReadyToLockUniformBuffer
-   */
-  void ReadyToLockUniformBuffer();
-
-  /**
-   * @copydoc Dali::Internal::Render::UniformBufferManager::UnlockUniformBuffer
-   */
-  void UnlockUniformBuffer();
-
-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
index b8c00d0..1b1bf18 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 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.
 
 namespace Dali::Internal::Render
 {
-UniformBufferView::UniformBufferView( UniformBuffer& ubo, uint32_t offset, size_t size ) :
-  mUniformBuffer(&ubo),
+UniformBufferView::UniformBufferView(UniformBufferV2& ubo, uint32_t offset, size_t size)
+: mUniformBuffer(&ubo),
   mOffset(offset),
-  mSize (size)
+  mSize(size)
 {
 }
 
@@ -35,12 +35,12 @@ UniformBufferView::~UniformBufferView() = default;
 void UniformBufferView::Write(const void* data, uint32_t size, uint32_t offset)
 {
   // Write into mapped buffer
-  mUniformBuffer->Write( data, size, offset );
+  mUniformBuffer->Write(data, size, offset);
 }
 
-Graphics::Buffer* UniformBufferView::GetBuffer( uint32_t* relativeOffset )
+Graphics::Buffer* UniformBufferView::GetBuffer() const
 {
-  auto buffer = mUniformBuffer->GetBufferByOffset( mOffset, relativeOffset, nullptr );
-  return buffer ? buffer->buffer.get() : nullptr;
+  return mUniformBuffer->GetGraphicsBuffer();
 }
-} // Namespace Dali::Internal::Render
\ No newline at end of file
+
+} // Namespace Dali::Internal::Render
index dfe8c23..028e546 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_INTERNAL_UNIFORM_BUFFER_VIEW_H
 
 /*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 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.
@@ -21,6 +21,7 @@
 // EXTERNAL INCLUDES
 #include <stdint.h>
 #include <stdlib.h>
+#include <memory>
 
 namespace Dali
 {
@@ -30,7 +31,7 @@ class Buffer;
 }
 namespace Internal::Render
 {
-class UniformBuffer;
+class UniformBufferV2;
 
 /**
  * Class UniformBufferView
@@ -47,7 +48,7 @@ class UniformBuffer;
 class UniformBufferView
 {
 public:
-  UniformBufferView(UniformBuffer& ubo, uint32_t offset, size_t size);
+  UniformBufferView(UniformBufferV2& ubo, uint32_t offset, size_t size);
 
   ~UniformBufferView();
 
@@ -84,20 +85,15 @@ public:
   /**
    * @brief 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);
+  [[nodiscard]] Graphics::Buffer* GetBuffer() const;
 
 private:
-  UniformBuffer* mUniformBuffer{nullptr}; ///< UniformBuffer that the view views
-  uint32_t       mOffset{0u};             ///< Offset within the buffer
-  size_t         mSize{0u};               ///< Size of view
+  UniformBufferV2* 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
+#endif //DALI_INTERNAL_UNIFORM_BUFFER_VIEW_H
index 5787c68..a6c9502 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 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.
@@ -27,313 +27,335 @@ Debug::Filter* gUniformBufferLogFilter = Debug::Filter::New(Debug::NoLogging, fa
 
 namespace Dali::Internal::Render
 {
-namespace
+// GPU UBOs need to be double-buffered in order to avoid stalling the CPU during mapping/unmapping
+constexpr uint32_t INTERNAL_UBO_BUFFER_COUNT = 2u;
+
+Graphics::UniquePtr<UniformBufferV2> UniformBufferV2::New(Dali::Graphics::Controller* controller, bool emulated, uint32_t alignment)
 {
-static constexpr uint32_t INVALID_BUFFER_INDEX = std::numeric_limits<uint32_t>::max();
+  return Graphics::UniquePtr<UniformBufferV2>(new UniformBufferV2(controller, emulated, alignment));
 }
-UniformBuffer::UniformBuffer(Dali::Graphics::Controller*     controller,
-                             uint32_t                        sizeInBytes,
-                             uint32_t                        alignment,
-                             Graphics::BufferUsageFlags      usageFlags,
-                             Graphics::BufferPropertiesFlags propertiesFlags)
+
+UniformBufferV2::UniformBufferV2(Dali::Graphics::Controller* controller, bool emulated, uint32_t alignment)
 : mController(controller),
-  mSize(0u),
-  mUsageFlags(usageFlags),
-  mPropertiesFlags(propertiesFlags),
-  mLockedBufferIndex(INVALID_BUFFER_INDEX),
-  mLockedPtr(nullptr),
-  mReadyToBeLocked(false)
+  mBlockAlignment(alignment),
+  mCurrentGraphicsBufferIndex(0),
+  mEmulated(emulated)
 {
-  mAlignment = alignment;
-  if(sizeInBytes)
-  {
-    Resize(sizeInBytes, true);
-  }
+  mBufferList.resize(emulated ? 1 : INTERNAL_UBO_BUFFER_COUNT);
 }
 
-UniformBuffer::~UniformBuffer()
+void UniformBufferV2::ReSpecify(uint32_t sizeInBytes)
 {
-  // Unmap and flush all allocated buffers
-  for(auto i = 0u; i < mBuffers.size(); ++i)
+  DALI_LOG_INFO(gUniformBufferLogFilter, Debug::Verbose, "Respec(%p) [%d] BufferType:%s  newSize:%d\n", this, mCurrentGraphicsBufferIndex, mEmulated ? "CPU" : "GPU", sizeInBytes);
+  if(mEmulated)
   {
-    Flush(i);
-    Unmap(i);
+    ReSpecifyCPU(sizeInBytes);
   }
-}
-
-void UniformBuffer::Flush(uint32_t bufferIndex)
-{
-  DALI_LOG_INFO(gUniformBufferLogFilter, Debug::General, "Flush (bufferIndex : %d / %d [%d])\n", bufferIndex, mBuffers.size(), mSize);
-  const auto& buffer = mBuffers[bufferIndex];
-  if(buffer.buffer && buffer.memory)
+  else
   {
-    buffer.memory->Flush();
+    ReSpecifyGPU(sizeInBytes);
   }
 }
 
-void UniformBuffer::Resize(uint32_t newSize, bool invalidate)
+void UniformBufferV2::Write(const void* data, uint32_t size, uint32_t offset)
 {
-  // Adjust alignment, the alignment is needed for
-  // real UBOs (it should be given by the buffer requirements)
-  if(DALI_LIKELY(mAlignment && newSize > 0))
+  // Very verbose logging!
+  DALI_LOG_INFO(gUniformBufferLogFilter, Debug::LogLevel(4), "Write(%p) [%d] BufferType:%s  offset:%d size:%d\n", this, mCurrentGraphicsBufferIndex, mEmulated ? "CPU" : "GPU", offset, size);
+  if(mEmulated)
+  {
+    WriteCPU(data, size, offset);
+  }
+  else
   {
-    newSize = (((newSize - 1) / mAlignment) + 1) * mAlignment;
+    WriteGPU(data, size, offset);
   }
+}
 
-  // The buffer is already optimal
-  if(newSize == mSize && !invalidate)
+void UniformBufferV2::Map()
+{
+  DALI_LOG_INFO(gUniformBufferLogFilter, Debug::Verbose, "Map(%p) [%d] BufferType:%s\n", this, mCurrentGraphicsBufferIndex, mEmulated ? "CPU" : "GPU");
+  if(mEmulated)
   {
-    return;
+    MapCPU();
   }
-  if(invalidate && newSize == mSize && mBuffers.size() == 1)
+  else
   {
-    return;
+    MapGPU();
   }
+}
 
-  if(DALI_UNLIKELY(mReadyToBeLocked))
+void UniformBufferV2::Unmap()
+{
+  DALI_LOG_INFO(gUniformBufferLogFilter, Debug::Verbose, "Unmap(%p) [%d] BufferType:%s\n", this, mCurrentGraphicsBufferIndex, mEmulated ? "CPU" : "GPU");
+  if(mEmulated)
   {
-    DALI_LOG_INFO(gUniformBufferLogFilter, Debug::Verbose, "Resize %d --> %d with %s [mBuffers : %d] during lock (lockedBufferIndex : %d, lockedPtr : %p)\n", mSize, newSize, invalidate ? "Invalidate" : "Rendering", mBuffers.size(), mLockedBufferIndex, mLockedPtr);
+    UnmapCPU();
   }
   else
   {
-    DALI_LOG_INFO(gUniformBufferLogFilter, Debug::Verbose, "Resize %d --> %d with %s [mBuffers : %d]\n", mSize, newSize, invalidate ? "Invalidate" : "Rendering", mBuffers.size());
+    UnmapGPU();
   }
+}
 
-  // Throw away content
-  if(invalidate)
+void UniformBufferV2::Flush()
+{
+  DALI_LOG_INFO(gUniformBufferLogFilter, Debug::Verbose, "Flush(%p) [%d] BufferType:%s\n", this, mCurrentGraphicsBufferIndex, mEmulated ? "CPU" : "GPU");
+
+  // Flush only for GPU buffertype by unmapping
+  if(!mEmulated && mMappedPtr)
   {
-    if(mReadyToBeLocked)
-    {
-      UnlockUniformBuffer();
-      mReadyToBeLocked = true;
-    }
-    // Flush and unmap all allocated buffers
-    for(auto i = 0u; i < mBuffers.size(); ++i)
+    auto& buffer = mBufferList[mCurrentGraphicsBufferIndex];
+    if(buffer.graphicsMemory)
     {
-      Flush(i);
-      Unmap(i);
+      UnmapGPU();
+      // flush range?
     }
-    mBuffers.clear();
-    mSize = 0;
+
+    // Swap buffers for GPU UBOs
+    auto s                      = mBufferList.size();
+    mCurrentGraphicsBufferIndex = ((mCurrentGraphicsBufferIndex + 1) % s);
   }
+}
 
-  if(newSize > mSize)
+void UniformBufferV2::Rollback()
+{
+  DALI_LOG_INFO(gUniformBufferLogFilter, Debug::Verbose, "Rollback(%p) [%d]\n", this, mCurrentGraphicsBufferIndex);
+  if(!mBufferList.empty())
   {
-    auto createInfo = Graphics::BufferCreateInfo()
-                        .SetSize(newSize - mSize)
-                        .SetBufferPropertiesFlags(mPropertiesFlags)
-                        .SetUsage(mUsageFlags);
-
-    auto buffer = mController->CreateBuffer(createInfo, nullptr);
-
-    mBuffers.emplace_back(GfxBuffer(std::move(buffer), createInfo));
-
-    mSize = newSize;
+    mBufferList[mCurrentGraphicsBufferIndex].currentOffset = 0; // reset offset
   }
+}
 
-  // If invalidate during locked, begin lock again.
-  if(DALI_UNLIKELY(invalidate && mReadyToBeLocked))
+uint32_t UniformBufferV2::AlignSize(uint32_t size)
+{
+  if(size % mBlockAlignment != 0)
   {
-    mReadyToBeLocked = false;
-    ReadyToLockUniformBuffer();
+    size = ((size / mBlockAlignment) + 1) * mBlockAlignment;
   }
+  return size;
+}
 
-  if(DALI_UNLIKELY(mReadyToBeLocked))
+uint32_t UniformBufferV2::IncrementOffsetBy(uint32_t value)
+{
+  if(mEmulated && !mBufferList.empty())
   {
-    DALI_LOG_INFO(gUniformBufferLogFilter, Debug::Verbose, "Resize done as %d with %s [mBuffers : %d] during lock (lockedBufferIndex : %d, lockedPtr : %p)\n", newSize, invalidate ? "Invalidate" : "Rendering", mBuffers.size(), mLockedBufferIndex, mLockedPtr);
-  }
-  else
+    mBufferList[mCurrentGraphicsBufferIndex].currentOffset += value; // reset offset
+    return mBufferList[mCurrentGraphicsBufferIndex].currentOffset;
+  } // GPU
+  else if(!mBufferList.empty())
   {
-    DALI_LOG_INFO(gUniformBufferLogFilter, Debug::Verbose, "Resize done as %d with %s [mBuffers : %d]\n", newSize, invalidate ? "Invalidate" : "Rendering", mBuffers.size());
+    mBufferList[mCurrentGraphicsBufferIndex].currentOffset += value; // reset offset
+    return mBufferList[mCurrentGraphicsBufferIndex].currentOffset;
   }
+  DALI_LOG_INFO(gUniformBufferLogFilter, Debug::General, "Buffer should be allocated before incrementing offset\n");
+  return 0;
 }
 
-const UniformBuffer::GfxBuffer* UniformBuffer::GetBufferByOffset(uint32_t offset, uint32_t* newOffset, uint32_t* outBufferIndex) const
+bool UniformBufferV2::MemoryCompare(void* data, uint32_t offset, uint32_t size)
 {
-  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++;
-    }
-  }
+  return !memcmp(data, reinterpret_cast<uint8_t*>(mMappedPtr) + offset, size);
+}
 
-  auto& bufferDesc = mBuffers[bufferIndex];
+uint32_t UniformBufferV2::GetBlockAlignment() const
+{
+  return mBlockAlignment;
+}
 
-  if(outBufferIndex)
+uint32_t UniformBufferV2::GetCurrentOffset() const
+{
+  if(!mBufferList.empty())
   {
-    *outBufferIndex = bufferIndex;
+    return mBufferList[mCurrentGraphicsBufferIndex].currentOffset;
   }
+  DALI_LOG_INFO(gUniformBufferLogFilter, Debug::General, "Buffer should be allocated before getting offset\n");
+  return 0;
+}
 
-  if(newOffset)
+uint32_t UniformBufferV2::GetCurrentCapacity() const
+{
+  uint32_t capacity = 0;
+  if(!mBufferList.empty())
   {
-    *newOffset = bufferOffset;
+    DALI_ASSERT_DEBUG(mBufferList.size() > mCurrentGraphicsBufferIndex);
+    capacity = mBufferList[mCurrentGraphicsBufferIndex].capacity;
   }
+  return capacity;
+}
 
-  DALI_LOG_INFO(gUniformBufferLogFilter, Debug::Verbose, "GetBufferByOffset (bufferIndex : %d / %d [%d], bufferOffset : %d, Graphics::BufferPtr : %p)\n", bufferIndex, mBuffers.size(), mSize, bufferOffset, bufferDesc.buffer.get());
-
-  return &bufferDesc;
+Dali::Graphics::Buffer* UniformBufferV2::GetGraphicsBuffer() const
+{
+  return mBufferList[mCurrentGraphicsBufferIndex].graphicsBuffer.get();
 }
 
-void UniformBuffer::Write(const void* data, uint32_t size, uint32_t dstOffset)
+void UniformBufferV2::ReSpecifyCPU(uint32_t sizeInBytes)
 {
-  // find which buffer we want to write into
-  uint32_t bufferOffset = dstOffset;
-  uint32_t bufferIndex  = 0u;
+  GfxBuffer gfxBuffer;
+
+  uint32_t currentCapacity = GetCurrentCapacity();
 
-  // Find buffer if UBO is fragmented
-  if(mBuffers.size() > 1)
+  if(sizeInBytes > currentCapacity)
   {
-    for(const auto& buffer : mBuffers)
+    Graphics::UniquePtr<Graphics::Buffer> oldBuffer{nullptr};
+
+    // If the CPU buffer already exist use it when applying respec
+    if(!mBufferList.empty())
     {
-      if(bufferOffset >= buffer.createInfo.size)
-      {
-        bufferOffset -= buffer.createInfo.size;
-      }
-      else
-      {
-        break;
-      }
-      bufferIndex++;
+      mBufferList[0].graphicsMemory = nullptr;                                  // Discard mapped memory if exists
+      oldBuffer                     = std::move(mBufferList[0].graphicsBuffer); // Store old buffer for re-using
     }
-  }
+    Graphics::BufferPropertiesFlags flags = 0u | Graphics::BufferPropertiesFlagBit::CPU_ALLOCATED;
+
+    auto createInfo = Graphics::BufferCreateInfo()
+                        .SetSize(sizeInBytes)
+                        .SetBufferPropertiesFlags(flags)
+                        .SetUsage(0u | Graphics::BufferUsage::UNIFORM_BUFFER);
 
-  auto& bufferDesc = mBuffers[bufferIndex];
+    gfxBuffer.graphicsBuffer = mController->CreateBuffer(createInfo, std::move(oldBuffer));
+    gfxBuffer.capacity       = sizeInBytes;
+    gfxBuffer.currentOffset  = 0;
 
-  if(bufferDesc.needsUpdate)
-  {
+    mBufferList[0] = std::move(gfxBuffer);
+    // make sure buffer is created (move creation to run in parallel in the backed
+    // as this may be a major slowdown)
     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);
+  mMappedPtr = nullptr;
 
-  const bool locallyMapped = (bufferDesc.mappedPtr != nullptr);
-  if(!locallyMapped)
+  if(sizeInBytes)
   {
-    // Map once and keep it
-    Map(bufferIndex);
+    // After respecifying the buffer, we can map it persistently as it already exists
+    // in the CPU memory
+    MapCPU();
   }
+}
+
+void UniformBufferV2::ReSpecifyGPU(uint32_t sizeInBytes)
+{
+  uint32_t currentCapacity = GetCurrentCapacity();
 
-  if(bufferDesc.memory)
+  if(sizeInBytes > currentCapacity)
   {
-    // Rarely happened that we use over the locked memory
-    // Unlock previous buffer, and lock as current bufferIndex again
-    if(DALI_UNLIKELY(mLockedBufferIndex != bufferIndex))
+    GfxBuffer                             gfxBuffer;
+    Graphics::UniquePtr<Graphics::Buffer> oldBuffer{nullptr};
+
+    // If the GPU buffer already exist use it when applying respec
+    if(!mBufferList.empty())
     {
-      DALI_LOG_INFO(gUniformBufferLogFilter, Debug::General, "Unlock (lockedBufferIndex : %d / %d [%d], lockedPtr : %p)\n", mLockedBufferIndex, mBuffers.size(), mSize, mLockedPtr);
-
-      // mLockedBufferIndex == INVALID_BUFFER_INDEX only first time of current RenderScene.
-      if(DALI_LIKELY(mLockedBufferIndex != INVALID_BUFFER_INDEX))
-      {
-        // Unlock previous memory
-        if(mBuffers[mLockedBufferIndex].memory)
-        {
-          mBuffers[mLockedBufferIndex].memory->Unlock(true);
-        }
-      }
-      mLockedBufferIndex = bufferIndex;
-      mLockedPtr         = nullptr;
-
-      // Initial mapping done previously. Just lock and roll now.
-      if(mBuffers[mLockedBufferIndex].memory)
-      {
-        mLockedPtr = reinterpret_cast<uint8_t*>(mBuffers[mLockedBufferIndex].memory->LockRegion(0, mBuffers[mLockedBufferIndex].createInfo.size));
-      }
-      DALI_LOG_INFO(gUniformBufferLogFilter, Debug::General, "Lock (lockedBufferIndex : %d / %d [%d], lockedPtr : %p)\n", mLockedBufferIndex, mBuffers.size(), mSize, mLockedPtr);
+      mBufferList[mCurrentGraphicsBufferIndex].graphicsMemory = nullptr;                                                            // Discard mapped memory if exists
+      oldBuffer                                               = std::move(mBufferList[mCurrentGraphicsBufferIndex].graphicsBuffer); // Store old buffer for re-using
     }
+    Graphics::BufferPropertiesFlags flags = 0u;
 
-    DALI_LOG_INFO(gUniformBufferLogFilter, Debug::Verbose, "memcpy (lockedBufferIndex : %d / %d [%d], lockedPtr : %p, offset : %d, size : %d, lockedBufferSize : %d)\n", mLockedBufferIndex, mBuffers.size(), mSize, mLockedPtr, bufferOffset, size, mBuffers[mLockedBufferIndex].createInfo.size);
+    auto createInfo = Graphics::BufferCreateInfo()
+                        .SetSize(sizeInBytes)
+                        .SetBufferPropertiesFlags(flags)
+                        .SetUsage(0u | Graphics::BufferUsage::UNIFORM_BUFFER);
 
-    // We already check validation of buffer range. We can assume that bufferOffset + size <= mBuffers[mLockedBufferIndex].createInfo.size
-    if(mLockedPtr)
-    {
-      memcpy(mLockedPtr + bufferOffset, data, size);
-    }
+    gfxBuffer.graphicsBuffer = mController->CreateBuffer(createInfo, std::move(oldBuffer));
+    gfxBuffer.capacity       = sizeInBytes;
+    gfxBuffer.currentOffset  = 0;
+
+    mBufferList[mCurrentGraphicsBufferIndex] = std::move(gfxBuffer);
+    // make sure buffer is created (move creation to run in parallel in the backend
+    // as this may be a major slowdown)
+    mController->WaitIdle();
+    DALI_ASSERT_ALWAYS(mBufferList[mCurrentGraphicsBufferIndex].capacity == sizeInBytes && "std::move failed");
+  }
+
+  mMappedPtr = nullptr;
+
+  if(sizeInBytes)
+  {
+    MapGPU(); // Note, this will flush the creation buffer queues in the backend and initialize buffer.
   }
 }
 
-void UniformBuffer::Map(uint32_t bufferIndex)
+void UniformBufferV2::WriteCPU(const void* data, uint32_t size, uint32_t offset)
 {
-  DALI_LOG_INFO(gUniformBufferLogFilter, Debug::General, "Map (bufferIndex : %d / %d [%d])\n", bufferIndex, mBuffers.size(), mSize);
-  auto& buffer = mBuffers[bufferIndex];
-
-  if(buffer.needsUpdate)
+  // If not mapped
+  if(!mMappedPtr)
   {
-    mController->WaitIdle();
-    buffer.needsUpdate = false;
+    DALI_LOG_INFO(gUniformBufferLogFilter, Debug::Verbose, "Warning: CPU buffer should already be mapped!\n");
+    MapCPU();
   }
 
-  if(!buffer.memory)
+  DALI_ASSERT_DEBUG(offset + size <= mBufferList[mCurrentGraphicsBufferIndex].capacity);
+
+  // just copy whatever comes here
+  memcpy(reinterpret_cast<uint8_t*>(mMappedPtr) + offset, data, size);
+}
+
+void UniformBufferV2::WriteGPU(const void* data, uint32_t size, uint32_t offset)
+{
+  if(!mMappedPtr)
   {
-    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);
-    DALI_LOG_INFO(gUniformBufferLogFilter, Debug::Verbose, "GraphicsMemoryMapped (bufferIndex : %d / %d [%d], size : %d)\n", bufferIndex, mBuffers.size(), mSize, info.size);
+    DALI_LOG_INFO(gUniformBufferLogFilter, Debug::Verbose, "Warning: GPU buffer should already be mapped!\n");
+    MapGPU();
   }
+
+  DALI_ASSERT_DEBUG(offset + size <= mBufferList[mCurrentGraphicsBufferIndex].capacity);
+
+  memcpy(reinterpret_cast<uint8_t*>(mMappedPtr) + offset, data, size);
 }
 
-void UniformBuffer::Unmap(uint32_t bufferIndex)
+void UniformBufferV2::MapCPU()
 {
-  DALI_LOG_INFO(gUniformBufferLogFilter, Debug::General, "Unmap (bufferIndex : %d / %d [%d])\n", bufferIndex, mBuffers.size(), mSize);
-  auto& buffer = mBuffers[bufferIndex];
-  if(buffer.memory)
+  auto& buffer = mBufferList[0]; // CPU contains always one buffer
+  if(!buffer.graphicsMemory)
   {
-    mController->UnmapMemory(std::move(buffer.memory));
+    Graphics::MapBufferInfo info{};
+    info.buffer           = buffer.graphicsBuffer.get();
+    info.usage            = 0 | Graphics::MemoryUsageFlagBits::WRITE;
+    info.offset           = 0;
+    info.size             = buffer.capacity;
+    buffer.graphicsMemory = mController->MapBufferRange(info);
+  }
+
+  // obtain pointer instantly
+  if(buffer.graphicsMemory)
+  {
+    mMappedPtr = buffer.graphicsMemory->LockRegion(0, buffer.capacity);
+    DALI_LOG_INFO(gUniformBufferLogFilter, Debug::Verbose, "CPU buffer is mapped to %p\n", mMappedPtr);
   }
 }
 
-void UniformBuffer::ReadyToLockUniformBuffer()
+void UniformBufferV2::MapGPU()
 {
-  DALI_LOG_INFO(gUniformBufferLogFilter, Debug::General, "LockUniformBuffer\n");
-  if(DALI_UNLIKELY(mReadyToBeLocked && mLockedBufferIndex != INVALID_BUFFER_INDEX))
+  auto& buffer = mBufferList[mCurrentGraphicsBufferIndex];
+  if(!buffer.graphicsMemory)
   {
-    // Unlock previous locked buffer first
-    DALI_LOG_ERROR("Warning! : called LockUniformBuffer() before called UnlockUniformBuffer()!\n");
-    UnlockUniformBuffer();
+    Graphics::MapBufferInfo info{};
+    info.buffer           = buffer.graphicsBuffer.get();
+    info.usage            = 0 | Graphics::MemoryUsageFlagBits::WRITE;
+    info.offset           = 0;
+    info.size             = buffer.capacity;
+    buffer.graphicsMemory = mController->MapBufferRange(info);
   }
 
-  mReadyToBeLocked   = true;
-  mLockedBufferIndex = INVALID_BUFFER_INDEX;
-  mLockedPtr         = nullptr;
+  // obtain pointer instantly
+  if(buffer.graphicsMemory)
+  {
+    mMappedPtr = buffer.graphicsMemory->LockRegion(0, buffer.capacity);
+    DALI_LOG_INFO(gUniformBufferLogFilter, Debug::Verbose, "GPU buffer is mapped to %p\n", mMappedPtr);
+  }
 }
 
-void UniformBuffer::UnlockUniformBuffer()
+void UniformBufferV2::UnmapCPU()
 {
-  DALI_LOG_INFO(gUniformBufferLogFilter, Debug::General, "UnlockUniformBuffer (lockedBufferIndex : %d / %d [%d], lockedPtr : %p)\n", mLockedBufferIndex, mBuffers.size(), mSize, mLockedPtr);
-  if(mReadyToBeLocked && mLockedBufferIndex != INVALID_BUFFER_INDEX)
+  DALI_LOG_INFO(gUniformBufferLogFilter, Debug::Verbose, "CPU buffer is unmapped\n");
+}
+
+void UniformBufferV2::UnmapGPU()
+{
+  auto& buffer = mBufferList[mCurrentGraphicsBufferIndex];
+  if(buffer.graphicsMemory)
   {
-    auto& bufferDesc = mBuffers[mLockedBufferIndex];
-    if(bufferDesc.memory)
-    {
-      bufferDesc.memory->Unlock(true);
-    }
-    // Flush all allocated buffers
-    for(auto i = 0u; i < mBuffers.size(); ++i)
-    {
-      Flush(i);
-    }
+    mController->UnmapMemory(std::move(buffer.graphicsMemory));
+    buffer.graphicsMemory = nullptr;
   }
-  mLockedPtr         = nullptr;
-  mLockedBufferIndex = INVALID_BUFFER_INDEX;
-  mReadyToBeLocked   = false;
+  mMappedPtr = nullptr;
+  DALI_LOG_INFO(gUniformBufferLogFilter, Debug::Verbose, "GPU buffer is unmapped\n");
 }
 
-} // namespace Dali::Internal::Render
\ No newline at end of file
+} // namespace Dali::Internal::Render
index 4f83bbd..0ff24b9 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_INTERNAL_UNIFORM_BUFFER_H
 
 /*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 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.
@@ -36,164 +36,87 @@ namespace Dali::Internal::Render
  * From the client side, the UBO memory is continuous and individual
  * Graphics::Buffer objects are not visible.
  */
-class UniformBuffer
+class UniformBufferV2
 {
-  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] usageFlags type of usage ( Graphics::BufferUsage )
-   * @param[in] propertiesFlags buffer properties (Gracphis::BufferPropertiesFlags)
-   */
-  UniformBuffer(Dali::Graphics::Controller*     mController,
-                uint32_t                        sizeInBytes,
-                uint32_t                        alignment,
-                Graphics::BufferUsageFlags      usageFlags,
-                Graphics::BufferPropertiesFlags propertiesFlags);
-
 public:
   /**
-   * Destructor of UniformBuffer
+   * factory constructor method.
+   * @param[in] mController Pointer to the graphics controller
+   * @param[in] emulated True if this buffer is for standalone uniforms,
+   *            false if for uniform blocks
+   * @param[in] alignment The block alignment to use
    */
-  ~UniformBuffer();
+  static Graphics::UniquePtr<UniformBufferV2> New(Dali::Graphics::Controller* mController, bool emulated, uint32_t alignment);
 
   /**
-   * @brief Writes data into the buffer
-   * @note We prefer to call ReadyToLockUniformBuffer before call Write API.
-   * And also, prefer to call UnlockUniformBuffer if current frame's all Write API action done.
-   *
-   * @param[in] data pointer to the source data
-   * @param[in] size size of source data
-   * @param[in] offset destination offset
+   * Create the memory backing this buffer and map it.
    */
+  void ReSpecify(uint32_t sizeInBytes);
+
   void Write(const void* data, uint32_t size, uint32_t offset);
 
-  /**
-   * @brief Flushes whole buffer range
-   *
-   * @param[in] bufferIndex Index of Graphics::Buffer
-   */
-  void Flush(uint32_t bufferIndex = 0);
+  void Map();
 
-  /**
-   * @brief Returns allocated ( requested ) size
-   * @return size of buffer
-   */
-  [[nodiscard]] uint32_t GetSize() const
-  {
-    return mSize;
-  }
+  void Unmap();
 
-  /**
-   * @brief 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();
-  }
+  void Flush(); // We aren't guaranteeing creation with this call.
 
-  /**
-   * @brief Maps individual Graphics buffer memory
-   *
-   * @param[in] bufferIndex index of Graphics buffer
-   */
-  void Map(uint32_t bufferIndex = 0);
+  void Rollback();
 
   /**
-   * Unmaps individual Graphics buffer memory
-   *
-   * @param[in] bufferIndex index of Graphics buffer
+   * Align size to the current block size
    */
-  void Unmap(uint32_t bufferIndex = 0);
+  uint32_t AlignSize(uint32_t size);
 
-  /**
-   * @brief 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);
+  uint32_t IncrementOffsetBy(uint32_t value);
 
-  /**
-   * @copydoc Dali::Internal::Render::UniformBufferViewPool::ReadyToLockUniformBuffer
-   */
-  void ReadyToLockUniformBuffer();
+  bool MemoryCompare(void* data, uint32_t offset, uint32_t size);
 
-  /**
-   * @copydoc Dali::Internal::Render::UniformBufferViewPool::UnlockUniformBuffer
-   */
-  void UnlockUniformBuffer();
+  [[nodiscard]] uint32_t GetBlockAlignment() const;
+
+  [[nodiscard]] uint32_t GetCurrentOffset() const;
+
+  [[nodiscard]] uint32_t GetCurrentCapacity() const;
+
+  [[nodiscard]] Dali::Graphics::Buffer* GetGraphicsBuffer() const;
 
 private:
-  /**
-   * @brief 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
-  };
+  UniformBufferV2(Dali::Graphics::Controller* controller, bool emulated, uint32_t alignment);
 
-  /**
-   * @brief 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;
+  void ReSpecifyCPU(uint32_t sizeInBytes);
 
-  std::vector<GfxBuffer> mBuffers; ///< List of GfxBuffer objects
+  void ReSpecifyGPU(uint32_t sizeInBytes);
 
-  Dali::Graphics::Controller* mController; ///< Pointer to the controller
+  void WriteCPU(const void* data, uint32_t size, uint32_t offset);
 
-  uint32_t mSize;         ///< Current size of buffer
-  uint32_t mAlignment{0}; ///< Buffer alignment
+  void WriteGPU(const void* data, uint32_t size, uint32_t offset);
 
-  Graphics::BufferUsageFlags      mUsageFlags;
-  Graphics::BufferPropertiesFlags mPropertiesFlags;
+  void MapCPU();
 
-  uint32_t mLockedBufferIndex;   ///< Current locked buffer region index.
-  uint8_t* mLockedPtr;           ///< Current locked buffer pointer.
-  bool     mReadyToBeLocked : 1; ///< True if current uniform buffer is ready to be locked.
-};
+  void MapGPU();
 
-} // namespace Dali::Internal::Render
+  void UnmapCPU();
+
+  void UnmapGPU();
+
+private:
+  Graphics::Controller* mController{nullptr};
+
+  uint32_t mBlockAlignment{0u};
 
+  struct GfxBuffer
+  {
+    Graphics::UniquePtr<Graphics::Buffer> graphicsBuffer;
+    Graphics::UniquePtr<Graphics::Memory> graphicsMemory;
+    uint32_t                              capacity;
+    uint32_t                              currentOffset;
+  };
+
+  // List of buffers, in case of CPU one buffer will be sufficient
+  std::vector<GfxBuffer> mBufferList;
+  void*                  mMappedPtr{nullptr};
+  uint32_t               mCurrentGraphicsBufferIndex{0u};
+  bool                   mEmulated;
+};
+} // namespace Dali::Internal::Render
 #endif //DALI_INTERNAL_UNIFORM_BUFFER_H
index f6d9f97..1acafae 100644 (file)
@@ -30,6 +30,7 @@
 #include <dali/integration-api/debug.h>
 #include <dali/internal/common/shader-data.h>
 #include <dali/internal/render/common/performance-monitor.h>
+#include <dali/internal/render/renderers/uniform-buffer-manager.h>
 #include <dali/internal/render/shaders/program-cache.h>
 #include <dali/public-api/common/constants.h>
 #include <dali/public-api/common/dali-common.h>
@@ -62,11 +63,11 @@ size_t DEFAULT_UNIFORM_HASHTABLE[NUMBER_OF_DEFAULT_UNIFORMS] =
 /**
  * Helper function to calculate the correct alignment of data for uniform buffers
  * @param dataSize size of uniform buffer
- * @return aligned offset of data
+ * @return size of data aligned to given size
  */
-inline uint32_t GetUniformBufferDataAlignment(uint32_t dataSize)
+inline uint32_t AlignSize(uint32_t dataSize, uint32_t alignSize)
 {
-  return ((dataSize / 256u) + ((dataSize % 256u) ? 1u : 0u)) * 256u;
+  return ((dataSize / alignSize) + ((dataSize % alignSize) ? 1u : 0u)) * alignSize;
 }
 
 } // namespace
@@ -83,6 +84,7 @@ Program* Program::New(ProgramCache& cache, Internal::ShaderDataPtr shaderData, G
   {
     // program not found so create it
     program = new Program(cache, shaderData, gfxController);
+    DALI_LOG_RELEASE_INFO("Program::New() created a unique program\n");
     cache.AddProgram(shaderHash, program);
   }
 
@@ -101,7 +103,7 @@ Program::Program(ProgramCache& cache, Internal::ShaderDataPtr shaderData, Graphi
 
 Program::~Program() = default;
 
-void Program::BuildReflection(const Graphics::Reflection& graphicsReflection)
+void Program::BuildReflection(const Graphics::Reflection& graphicsReflection, Render::UniformBufferManager& uniformBufferManager)
 {
   mReflectionDefaultUniforms.clear();
   mReflectionDefaultUniforms.resize(NUMBER_OF_DEFAULT_UNIFORMS);
@@ -174,20 +176,36 @@ void Program::BuildReflection(const Graphics::Reflection& graphicsReflection)
     }
   }
 
-  // Calculate size of memory for uniform blocks
-  mUniformBlockRequirements.totalSizeRequired = 0;
-  mUniformBlockRequirements.blockCount        = graphicsReflection.GetUniformBlockCount();
-  for(auto i = 0u; i < mUniformBlockRequirements.blockCount; ++i)
+  mUniformBlockMemoryRequirements.blockSize.resize(uniformBlockCount);
+  mUniformBlockMemoryRequirements.blockSizeAligned.resize(uniformBlockCount);
+  mUniformBlockMemoryRequirements.blockCount           = uniformBlockCount;
+  mUniformBlockMemoryRequirements.totalSizeRequired    = 0u;
+  mUniformBlockMemoryRequirements.totalCpuSizeRequired = 0u;
+  mUniformBlockMemoryRequirements.totalGpuSizeRequired = 0u;
+
+  for(auto i = 0u; i < uniformBlockCount; ++i)
   {
-    auto blockSize = GetUniformBufferDataAlignment(graphicsReflection.GetUniformBlockSize(i));
-    mUniformBlockRequirements.totalSizeRequired += blockSize;
+    Graphics::UniformBlockInfo uboInfo;
+    graphicsReflection.GetUniformBlock(i, uboInfo);
+    bool standaloneUniformBlock = (i == 0);
+
+    auto     blockSize        = graphicsReflection.GetUniformBlockSize(i);
+    uint32_t blockAlignment   = uniformBufferManager.GetUniformBlockAlignment(standaloneUniformBlock);
+    auto     alignedBlockSize = AlignSize(blockSize, blockAlignment);
+
+    mUniformBlockMemoryRequirements.blockSize[i]        = blockSize;
+    mUniformBlockMemoryRequirements.blockSizeAligned[i] = alignedBlockSize;
+
+    mUniformBlockMemoryRequirements.totalSizeRequired += alignedBlockSize;
+    mUniformBlockMemoryRequirements.totalCpuSizeRequired += (standaloneUniformBlock) ? alignedBlockSize : 0;
+    mUniformBlockMemoryRequirements.totalGpuSizeRequired += (standaloneUniformBlock) ? 0 : alignedBlockSize;
   }
 }
 
-void Program::SetGraphicsProgram(Graphics::UniquePtr<Graphics::Program>&& program)
+void Program::SetGraphicsProgram(Graphics::UniquePtr<Graphics::Program>&& program, Render::UniformBufferManager& uniformBufferManager)
 {
   mGfxProgram = std::move(program);
-  BuildReflection(mGfxController.GetProgramReflection(*mGfxProgram.get()));
+  BuildReflection(mGfxController.GetProgramReflection(*mGfxProgram.get()), uniformBufferManager);
 }
 
 bool Program::GetUniform(const std::string_view& name, Hash hashedName, Hash hashedNameNoArray, Graphics::UniformInfo& out) const
index 09c5ff8..5e46c39 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_INTERNAL_PROGRAM_H
 
 /*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 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.
@@ -43,6 +43,11 @@ namespace Internal
 {
 class ProgramCache;
 
+namespace Render
+{
+class UniformBufferManager;
+}
+
 /**
  * A program contains a vertex & fragment shader.
  * It interfaces to the implementation program and it's reflection.
@@ -127,7 +132,10 @@ public:
     return mGfxProgram.get();
   }
 
-  void SetGraphicsProgram(Graphics::UniquePtr<Graphics::Program>&& program);
+  /**
+   * Setup the actual program, and ensure that it's reflection is generated.
+   */
+  void SetGraphicsProgram(Graphics::UniquePtr<Graphics::Program>&& program, Render::UniformBufferManager& uniformBufferManager);
 
   /**
    * Retrieves uniform data.
@@ -185,7 +193,7 @@ public:
    * Build optimized shader reflection of uniforms
    * @param graphicsReflection The graphics reflection
    */
-  void BuildReflection(const Graphics::Reflection& graphicsReflection);
+  void BuildReflection(const Graphics::Reflection& graphicsReflection, Render::UniformBufferManager& uniformBufferManager);
 
   /**
    * Struct UniformBlockMemoryRequirements
@@ -193,10 +201,15 @@ public:
    */
   struct UniformBlockMemoryRequirements
   {
-    uint32_t blockCount;
-    uint32_t totalSizeRequired;
+    uint32_t blockCount{0u};
+    uint32_t totalSizeRequired{0u};
+    uint32_t totalCpuSizeRequired{0u}; ///< requirements for CPU memory
+    uint32_t totalGpuSizeRequired{0u}; ///< requirements of hardware buffer
+
+    // Per block
+    std::vector<uint32_t> blockSize{};
+    std::vector<uint32_t> blockSizeAligned{};
   };
-
   /**
    * Retrieves uniform blocks requirements
    *
@@ -204,7 +217,7 @@ public:
    */
   [[nodiscard]] const UniformBlockMemoryRequirements& GetUniformBlocksMemoryRequirements() const
   {
-    return mUniformBlockRequirements;
+    return mUniformBlockMemoryRequirements;
   }
 
 private:                           // Data
@@ -221,9 +234,10 @@ private:                           // Data
 
   using UniformReflectionContainer = std::vector<ReflectionUniformInfo>;
 
-  UniformReflectionContainer     mReflection{};                ///< Contains reflection build per program
-  UniformReflectionContainer     mReflectionDefaultUniforms{}; ///< Contains default uniforms
-  UniformBlockMemoryRequirements mUniformBlockRequirements{};  ///< Memory requirements for uniform blocks
+  UniformReflectionContainer mReflection{};                ///< Contains reflection build per program
+  UniformReflectionContainer mReflectionDefaultUniforms{}; ///< Contains default uniforms
+
+  UniformBlockMemoryRequirements mUniformBlockMemoryRequirements; ///< Memory requirements per each block, block 0 = standalone/emulated
 };
 
 } // namespace Internal