Adding UniformBlock for shared uniforms 46/315546/39
authorDavid Steele <david.steele@samsung.com>
Mon, 26 Jun 2023 08:46:13 +0000 (09:46 +0100)
committerEunki, Hong <eunkiki.hong@samsung.com>
Tue, 8 Apr 2025 01:51:43 +0000 (10:51 +0900)
Let we make UniformBlock class and connect it to Shader.

The UniformBlock who have same name with at shader, will ignore
the value of Actor - Renderer - Shader propert chain. And will use
UniformBlock itself's value.

All renderer who has same shader with connected uniform block, will have
same uniform values at rendering time.

It will reduce the number of UniformWrite call at render thread.

Note : Since Uniform have dependency with Program, we should clear
cached Program at program-controller whenerver we try to connect uniform blocks
to shader.

TODO : We should notify to Graphics::Program s.t. given uniform block is shared.

The binding value of shared uniform block
 - glUniformBlockBinding(~~, ~~, binding);

should not call BindBufferRange in future, match as

 - glBindBufferRange(~~, binding, ~~~~);

Cherry-pick of patchset : https://archive.tizen.org/gerrit/c/platform/core/uifw/dali-core/+/294788

Change-Id: I9b0dab139a7064bb8889db9693e4f84ad74185ad

44 files changed:
automated-tests/src/dali-internal/utc-Dali-Internal-Shader.cpp
automated-tests/src/dali/CMakeLists.txt
automated-tests/src/dali/utc-Dali-DecoratedVisualRenderer.cpp
automated-tests/src/dali/utc-Dali-UniformBlock.cpp [new file with mode: 0644]
automated-tests/src/dali/utc-Dali-VisualRenderer.cpp
dali/integration-api/shader-integ.cpp
dali/integration-api/shader-integ.h
dali/internal/event/rendering/decorated-visual-renderer-impl.cpp
dali/internal/event/rendering/renderer-impl.cpp
dali/internal/event/rendering/renderer-impl.h
dali/internal/event/rendering/shader-impl.cpp
dali/internal/event/rendering/shader-impl.h
dali/internal/event/rendering/uniform-block-impl.cpp [new file with mode: 0644]
dali/internal/event/rendering/uniform-block-impl.h [new file with mode: 0644]
dali/internal/event/rendering/visual-renderer-impl.cpp
dali/internal/event/rendering/visual-renderer-impl.h
dali/internal/file.list
dali/internal/render/common/render-manager.cpp
dali/internal/render/common/render-manager.h
dali/internal/render/common/shared-uniform-buffer-view-container.cpp [new file with mode: 0644]
dali/internal/render/common/shared-uniform-buffer-view-container.h [new file with mode: 0644]
dali/internal/render/data-providers/render-data-provider.h
dali/internal/render/renderers/render-renderer.cpp
dali/internal/render/renderers/render-renderer.h
dali/internal/render/renderers/render-uniform-block.cpp [new file with mode: 0644]
dali/internal/render/renderers/render-uniform-block.h [new file with mode: 0644]
dali/internal/render/shaders/program-controller.cpp
dali/internal/render/shaders/program-controller.h
dali/internal/render/shaders/program.cpp
dali/internal/render/shaders/program.h
dali/internal/render/shaders/render-shader.cpp
dali/internal/render/shaders/render-shader.h
dali/internal/update/manager/update-manager.cpp
dali/internal/update/manager/update-manager.h
dali/internal/update/rendering/scene-graph-renderer.cpp
dali/internal/update/rendering/scene-graph-renderer.h
dali/public-api/dali-core.h
dali/public-api/file.list
dali/public-api/rendering/decorated-visual-renderer.h
dali/public-api/rendering/shader.cpp
dali/public-api/rendering/uniform-block.cpp [new file with mode: 0644]
dali/public-api/rendering/uniform-block.h [new file with mode: 0644]
dali/public-api/rendering/visual-renderer.cpp
dali/public-api/rendering/visual-renderer.h

index 5f294a4d15f30ee3ed0026725ab7b848bfbceab1..5e8df61c2a9290747cb287a2dce3e40b33f356d6 100644 (file)
@@ -1,5 +1,5 @@
 /*
-* Copyright (c) 2024 Samsung Electronics Co., Ltd.
+* Copyright (c) 2025 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.
@@ -380,3 +380,38 @@ int UtcDaliInternalShaderSaveAndLoad02(void)
   DALI_TEST_EQUALS(platformAbstraction.WasCalled(TestPlatformAbstraction::LoadShaderBinaryFileFunc), true, TEST_LOCATION);
   END_TEST;
 }
+
+int UtcDaliInternalShaderNewWithUniformBlock(void)
+{
+  TestApplication application;
+
+  std::string vertexShader1   = "some vertex code\n";
+  std::string fragmentShader1 = "some fragment code\n";
+
+  std::string vertexShader2   = "some another vertex code\n";
+  std::string fragmentShader2 = "some another fragment code\n";
+
+  UniformBlock sharedUniformBlock1 = UniformBlock::New("ubo1");
+  UniformBlock sharedUniformBlock2 = UniformBlock::New("ubo2");
+  UniformBlock sharedUniformBlock3 = UniformBlock::New("ubo3");
+
+  Shader shader1 = Dali::Integration::ShaderNewWithUniformBlock(vertexShader1, fragmentShader1, Shader::Hint::NONE, "", {sharedUniformBlock1});
+  Shader shader2 = Dali::Integration::ShaderNewWithUniformBlock(vertexShader1, fragmentShader1, Shader::Hint::NONE, "", {sharedUniformBlock1, sharedUniformBlock2, sharedUniformBlock3});
+  Shader shader3 = Dali::Integration::ShaderNewWithUniformBlock(vertexShader1, fragmentShader1, Shader::Hint::NONE, "", {});
+
+  Shader shader4 = Dali::Integration::ShaderNewWithUniformBlock(vertexShader2, fragmentShader2, Shader::Hint::NONE, "", {sharedUniformBlock1});
+
+  DALI_TEST_CHECK(shader1);
+  DALI_TEST_CHECK(shader2);
+  DALI_TEST_CHECK(shader3);
+  DALI_TEST_CHECK(shader4);
+
+  DALI_TEST_CHECK(shader1 != shader2);
+  DALI_TEST_CHECK(shader1 != shader3);
+  DALI_TEST_CHECK(shader1 != shader4);
+  DALI_TEST_CHECK(shader2 != shader3);
+  DALI_TEST_CHECK(shader2 != shader4);
+  DALI_TEST_CHECK(shader3 != shader4);
+
+  END_TEST;
+}
index aa77e33177dbecc04072c930463b38684b97c635..6301dce746b7219200ea87a27417b402f6adf4ea 100644 (file)
@@ -100,6 +100,7 @@ SET(TC_SOURCES
         utc-Dali-TouchProcessing.cpp
         utc-Dali-TypeRegistry.cpp
         utc-Dali-CSharp-TypeRegistry.cpp
+        utc-Dali-UniformBlock.cpp
         utc-Dali-Vector.cpp
         utc-Dali-Vector2.cpp
         utc-Dali-Vector3.cpp
index 92b775bf73e9320a779e09273621f4f434e874bb..12e49fbd770b19667172b5d9fe171f539a777681 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2025 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.
@@ -688,6 +688,7 @@ int UtcDaliDecoratedVisualRendererAnimatedProperty03(void)
   DecoratedVisualRenderer renderer = DecoratedVisualRenderer::New(geometry, shader);
 
   // Add all uniform mappings
+  renderer.RegisterVisualTransformUniform();
   renderer.RegisterCornerRadiusUniform();
   renderer.RegisterCornerSquarenessUniform();
   renderer.RegisterBorderlineUniform();
diff --git a/automated-tests/src/dali/utc-Dali-UniformBlock.cpp b/automated-tests/src/dali/utc-Dali-UniformBlock.cpp
new file mode 100644 (file)
index 0000000..93842d6
--- /dev/null
@@ -0,0 +1,538 @@
+/*
+ * Copyright (c) 2025 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <dali-test-suite-utils.h>
+#include <dali/devel-api/threading/thread.h>
+#include <dali/public-api/dali-core.h>
+#include <mesh-builder.h>
+#include <stdlib.h>
+
+#include <iostream>
+
+#include <test-platform-abstraction.h>
+
+using namespace Dali;
+
+void utc_dali_uniform_block_startup(void)
+{
+  test_return_value = TET_UNDEF;
+}
+
+void utc_dali_uniform_block_cleanup(void)
+{
+  test_return_value = TET_PASS;
+}
+
+namespace
+{
+static const char* VertexSource =
+  "This is a custom vertex shader\n"
+  "made on purpose to look nothing like a normal vertex shader inside dali\n";
+
+static const char* FragmentSource =
+  "This is a custom fragment shader\n"
+  "made on purpose to look nothing like a normal fragment shader inside dali\n";
+
+void TestConstraintNoBlue(Vector4& current, const PropertyInputContainer& inputs)
+{
+  current.b = 0.0f;
+}
+
+} // namespace
+
+int UtcDaliUniformBlockMethodNewP1(void)
+{
+  TestApplication application;
+
+  UniformBlock uniformBlock = UniformBlock::New("testBlock");
+  DALI_TEST_EQUALS((bool)uniformBlock, true, TEST_LOCATION);
+  END_TEST;
+}
+
+int UtcDaliUniformBlockMethodNewN1(void)
+{
+  TestApplication application;
+
+  UniformBlock uniformBlock;
+  DALI_TEST_EQUALS((bool)uniformBlock, false, TEST_LOCATION);
+  END_TEST;
+}
+
+int UtcDaliUniformBlockAssignmentOperator(void)
+{
+  TestApplication application;
+
+  UniformBlock uniformBlock1 = UniformBlock::New("testBlock");
+
+  UniformBlock uniformBlock2;
+
+  DALI_TEST_CHECK(!(uniformBlock1 == uniformBlock2));
+
+  uniformBlock2 = uniformBlock1;
+
+  DALI_TEST_CHECK(uniformBlock1 == uniformBlock2);
+
+  uniformBlock2 = UniformBlock::New("testBlock");
+
+  DALI_TEST_CHECK(!(uniformBlock1 == uniformBlock2));
+
+  END_TEST;
+}
+
+int UtcDaliUniformBlockMoveConstructor(void)
+{
+  TestApplication application;
+
+  UniformBlock uniformBlock = UniformBlock::New("testBlock");
+  DALI_TEST_CHECK(uniformBlock);
+  DALI_TEST_EQUALS(1, uniformBlock.GetBaseObject().ReferenceCount(), TEST_LOCATION);
+
+  // Register a custom property
+  Vector2         vec(1.0f, 2.0f);
+  Property::Index customIndex = uniformBlock.RegisterProperty("custom", vec);
+  DALI_TEST_EQUALS(uniformBlock.GetProperty<Vector2>(customIndex), vec, TEST_LOCATION);
+
+  UniformBlock move = std::move(uniformBlock);
+  DALI_TEST_CHECK(move);
+  DALI_TEST_EQUALS(1, move.GetBaseObject().ReferenceCount(), TEST_LOCATION);
+  DALI_TEST_EQUALS(move.GetProperty<Vector2>(customIndex), vec, TEST_LOCATION);
+  DALI_TEST_CHECK(!uniformBlock);
+
+  END_TEST;
+}
+
+int UtcDaliUniformBlockMoveAssignment(void)
+{
+  TestApplication application;
+
+  UniformBlock uniformBlock = UniformBlock::New("testBlock");
+  DALI_TEST_CHECK(uniformBlock);
+  DALI_TEST_EQUALS(1, uniformBlock.GetBaseObject().ReferenceCount(), TEST_LOCATION);
+
+  // Register a custom property
+  Vector2         vec(1.0f, 2.0f);
+  Property::Index customIndex = uniformBlock.RegisterProperty("custom", vec);
+  DALI_TEST_EQUALS(uniformBlock.GetProperty<Vector2>(customIndex), vec, TEST_LOCATION);
+
+  UniformBlock move;
+  move = std::move(uniformBlock);
+  DALI_TEST_CHECK(move);
+  DALI_TEST_EQUALS(1, move.GetBaseObject().ReferenceCount(), TEST_LOCATION);
+  DALI_TEST_EQUALS(move.GetProperty<Vector2>(customIndex), vec, TEST_LOCATION);
+  DALI_TEST_CHECK(!uniformBlock);
+
+  END_TEST;
+}
+
+int UtcDaliUniformBlockDownCast01(void)
+{
+  TestApplication application;
+
+  UniformBlock uniformBlock1 = UniformBlock::New("testBlock");
+
+  BaseHandle   handle(uniformBlock1);
+  UniformBlock uniformBlock2 = UniformBlock::DownCast(handle);
+  DALI_TEST_EQUALS((bool)uniformBlock2, true, TEST_LOCATION);
+  DALI_TEST_CHECK(uniformBlock1 == uniformBlock2);
+  END_TEST;
+}
+
+int UtcDaliUniformBlockDownCast02(void)
+{
+  TestApplication application;
+
+  Handle       handle       = Handle::New(); // Create a custom object
+  UniformBlock uniformBlock = UniformBlock::DownCast(handle);
+  DALI_TEST_EQUALS((bool)uniformBlock, false, TEST_LOCATION);
+  END_TEST;
+}
+
+int UtcDaliUniformBlockGetUniformBlockNameP(void)
+{
+  TestApplication application;
+
+  UniformBlock uniformBlock1 = UniformBlock::New("testBlock");
+  UniformBlock uniformBlock2 = UniformBlock::New("testBlock2");
+
+  DALI_TEST_EQUALS(uniformBlock1.GetUniformBlockName(), std::string_view("testBlock"), TEST_LOCATION);
+  DALI_TEST_EQUALS(uniformBlock2.GetUniformBlockName(), std::string_view("testBlock2"), TEST_LOCATION);
+
+  UniformBlock move;
+  move = std::move(uniformBlock1);
+  DALI_TEST_CHECK(move);
+  DALI_TEST_EQUALS(move.GetUniformBlockName(), std::string_view("testBlock"), TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliUniformBlockGetUniformBlockNameN(void)
+{
+  TestApplication application;
+
+  UniformBlock uniformBlock;
+  try
+  {
+    std::string name = std::string(uniformBlock.GetUniformBlockName());
+    DALI_TEST_CHECK(false); // Should not get here
+  }
+  catch(...)
+  {
+    DALI_TEST_CHECK(true); // We expect an assert
+  }
+
+  END_TEST;
+}
+
+int UtcDaliUniformBlockConnectToShader(void)
+{
+  TestApplication application;
+
+  Shader   shader   = Shader::New(VertexSource, FragmentSource);
+  Geometry geometry = CreateQuadGeometry();
+  Renderer renderer = Renderer::New(geometry, shader);
+
+  Actor actor = Actor::New();
+  actor.AddRenderer(renderer);
+  actor.SetProperty(Actor::Property::SIZE, Vector2(400.0f, 400.0f));
+  application.GetScene().Add(actor);
+
+  UniformBlock uniformBlock = UniformBlock::New("testBlock");
+
+  tet_printf("Connect to shader\n");
+  bool ret = uniformBlock.ConnectToShader(shader);
+  DALI_TEST_EQUALS(ret, true, TEST_LOCATION);
+
+  tet_printf("Re-connect to already connected uniform block will be failed\n");
+  ret = uniformBlock.ConnectToShader(shader);
+  DALI_TEST_EQUALS(ret, false, TEST_LOCATION);
+
+  Shader shader2 = Shader::New(VertexSource, FragmentSource);
+  tet_printf("Connect to new shader with same source code.\n");
+  ret = uniformBlock.ConnectToShader(shader2);
+  DALI_TEST_EQUALS(ret, true, TEST_LOCATION);
+
+  tet_printf("connect empty shader handle will be failed\n");
+  ret = uniformBlock.ConnectToShader(Dali::Shader());
+  DALI_TEST_EQUALS(ret, false, TEST_LOCATION);
+
+  tet_printf("disconnect from shader\n");
+  uniformBlock.DisconnectFromShader(shader);
+  uniformBlock.DisconnectFromShader(Dali::Shader());
+
+  tet_printf("Connect to shader\n");
+  ret = uniformBlock.ConnectToShader(shader);
+  DALI_TEST_EQUALS(ret, true, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render(0);
+
+  shader2.Reset();
+
+  application.SendNotification();
+  application.Render(0);
+
+  END_TEST;
+}
+
+int UtcDaliUniformBlockGetPropertyFromGraphics(void)
+{
+  TestApplication application;
+
+  const std::string uniformBlockName("testBlock");
+  const std::string uniformValue1Name("uValue1");
+  const std::string uniformValue2Name("uValue2");
+
+  // Values for actor
+  const float   value1ForActor = 1.0f;
+  const Vector2 value2ForActor(-2.0f, -3.0f);
+
+  // Values for UniformBlock
+  const float   value1ForUniformBlock = 10.0f;
+  const Vector2 value2ForUniformBlock(20.0f, 30.0f);
+
+  const uint32_t uniformAlign     = sizeof(float) * 4;
+  const uint32_t uniformBlockSize = (uniformAlign)*2;
+
+  tet_infoline("Prepare graphics to check UTC for testBlock\n");
+  auto& graphics = application.GetGraphicsController();
+  auto& gl       = application.GetGlAbstraction();
+  gl.mBufferTrace.EnableLogging(true);
+
+  const uint32_t UNIFORM_BLOCK_ALIGNMENT(512);
+  gl.SetUniformBufferOffsetAlignment(UNIFORM_BLOCK_ALIGNMENT);
+
+  // Add custom uniform block
+  TestGraphicsReflection::TestUniformBlockInfo block{
+    uniformBlockName,
+    0,
+    0,
+    uniformBlockSize,
+    {
+      {uniformValue1Name, Graphics::UniformClass::UNIFORM, 0, 0, {0}, {1}, 0, Property::Type::FLOAT, uniformAlign, 0},
+      {uniformValue2Name, Graphics::UniformClass::UNIFORM, 0, 0, {uniformAlign}, {2}, 0, Property::Type::VECTOR2, uniformAlign, 0},
+    }};
+  graphics.AddCustomUniformBlock(block);
+  tet_infoline("Prepare done\n");
+
+  Shader   shader   = Shader::New(VertexSource, FragmentSource);
+  Geometry geometry = CreateQuadGeometry();
+  Renderer renderer = Renderer::New(geometry, shader);
+
+  Actor actor = Actor::New();
+  actor.AddRenderer(renderer);
+  actor.SetProperty(Actor::Property::SIZE, Vector2(400.0f, 400.0f));
+  application.GetScene().Add(actor);
+
+  // Register a custom property
+  actor.RegisterProperty(uniformValue1Name, value1ForActor);
+  actor.RegisterProperty(uniformValue2Name, value2ForActor);
+
+  UniformBlock uniformBlock = UniformBlock::New("testBlock");
+  DALI_TEST_CHECK(uniformBlock);
+  DALI_TEST_EQUALS(uniformBlock.ConnectToShader(shader), true, TEST_LOCATION);
+
+  // Register a custom property
+  uniformBlock.RegisterProperty(uniformValue1Name, value1ForUniformBlock);
+  uniformBlock.RegisterProperty(uniformValue2Name, value2ForUniformBlock);
+
+  // TODO : For now, we should connect to shader before first rendering.
+  // We should resolve this bug in future.
+
+  TraceCallStack& graphicsTrace = graphics.mCallStack;
+  TraceCallStack& cmdTrace      = graphics.mCommandBufferCallStack;
+  graphicsTrace.EnableLogging(true);
+  cmdTrace.EnableLogging(true);
+
+  application.SendNotification();
+  application.Render(0);
+
+  DALI_TEST_EQUALS(cmdTrace.CountMethod("BindUniformBuffers"), 1, TEST_LOCATION);
+  DALI_TEST_CHECK(graphics.mLastUniformBinding.buffer != nullptr);
+  DALI_TEST_CHECK(graphics.mLastUniformBinding.emulated == false);
+
+  auto TestRawBuffer = [&](const float expectValue1, const Vector2& expectValue2) {
+    DALI_TEST_CHECK(graphics.mLastUniformBinding.buffer != nullptr);
+    DALI_TEST_CHECK(graphics.mLastUniformBinding.emulated == false);
+
+    tet_printf("Expect value : %f, %fx%f\n", expectValue1, expectValue2.x, expectValue2.y);
+
+    auto data = graphics.mLastUniformBinding.buffer->memory.data();
+    data += graphics.mLastUniformBinding.offset;
+    const float* fdata = reinterpret_cast<const float*>(data);
+    for(uint32_t i = 0u; i < uniformBlockSize / sizeof(float); i++)
+    {
+      tet_printf("%f ", fdata[i]);
+    }
+    tet_printf("\n");
+
+    DALI_TEST_EQUALS(fdata[0], expectValue1, TEST_LOCATION);
+    DALI_TEST_EQUALS(fdata[(uniformAlign / sizeof(float))], expectValue2.x, TEST_LOCATION);
+    DALI_TEST_EQUALS(fdata[(uniformAlign / sizeof(float)) + 1], expectValue2.y, TEST_LOCATION);
+  };
+
+  // Test the value
+  {
+    tet_printf("The result after connected!\n");
+    TestRawBuffer(value1ForUniformBlock, value2ForUniformBlock);
+  }
+
+  uniformBlock.DisconnectFromShader(shader);
+
+  application.SendNotification();
+  application.Render(0);
+
+  // Test the value
+  {
+    tet_printf("The result after disconnected!\n");
+    TestRawBuffer(value1ForActor, value2ForActor);
+  }
+
+  uniformBlock.ConnectToShader(shader);
+
+  application.SendNotification();
+  application.Render(0);
+
+  // Test the value
+  {
+    tet_printf("The result after connected again\n");
+    TestRawBuffer(value1ForUniformBlock, value2ForUniformBlock);
+  }
+  actor.RegisterProperty(uniformValue1Name, value1ForActor * 3.0f);
+  actor.RegisterProperty(uniformValue2Name, value2ForActor * 3.0f);
+
+  application.SendNotification();
+  application.Render(0);
+
+  // Test the value
+  {
+    tet_printf("The result when we change actor property\n");
+    TestRawBuffer(value1ForUniformBlock, value2ForUniformBlock);
+  }
+
+  uniformBlock.DisconnectFromShader(shader);
+
+  application.SendNotification();
+  application.Render(0);
+
+  // Test the value
+  {
+    tet_printf("The result after disconnected after change actor property\n");
+    TestRawBuffer(value1ForActor * 3.0f, value2ForActor * 3.0f);
+  }
+
+  uniformBlock.ConnectToShader(shader);
+
+  application.SendNotification();
+  application.Render(0);
+
+  // Test the value
+  {
+    tet_printf("The result after connected again\n");
+    TestRawBuffer(value1ForUniformBlock, value2ForUniformBlock);
+  }
+
+  uniformBlock.Reset();
+
+  application.SendNotification();
+  application.Render(0);
+
+  // Test the value
+  {
+    tet_printf("Destroy uniform block without disconnect shader. It will disconnect from shader automatically\n");
+    TestRawBuffer(value1ForActor * 3.0f, value2ForActor * 3.0f);
+  }
+
+  END_TEST;
+}
+
+int UtcDaliUniformBlockConstraint01(void)
+{
+  TestApplication application;
+
+  tet_infoline("Test that a uniform block property can be constrained");
+
+  Shader   shader   = Shader::New(VertexSource, FragmentSource);
+  Geometry geometry = CreateQuadGeometry();
+  Renderer renderer = Renderer::New(geometry, shader);
+
+  Actor actor = Actor::New();
+  actor.AddRenderer(renderer);
+  actor.SetProperty(Actor::Property::SIZE, Vector2(400.0f, 400.0f));
+  application.GetScene().Add(actor);
+
+  UniformBlock uniformBlock = UniformBlock::New("testBlock");
+  uniformBlock.ConnectToShader(shader);
+
+  Vector4         initialColor = Color::WHITE;
+  Property::Index colorIndex   = uniformBlock.RegisterProperty("uFadeColor", initialColor);
+
+  application.SendNotification();
+  application.Render(0);
+  DALI_TEST_EQUALS(uniformBlock.GetProperty<Vector4>(colorIndex), initialColor, TEST_LOCATION);
+
+  // Apply constraint
+  Constraint constraint = Constraint::New<Vector4>(uniformBlock, colorIndex, TestConstraintNoBlue);
+  constraint.Apply();
+  application.SendNotification();
+  application.Render(0);
+
+  // Expect no blue component in either buffer - yellow
+  DALI_TEST_EQUALS(uniformBlock.GetCurrentProperty<Vector4>(colorIndex), Color::YELLOW, TEST_LOCATION);
+  application.Render(0);
+  DALI_TEST_EQUALS(uniformBlock.GetCurrentProperty<Vector4>(colorIndex), Color::YELLOW, TEST_LOCATION);
+
+  uniformBlock.RemoveConstraints();
+  uniformBlock.SetProperty(colorIndex, Color::WHITE);
+  application.SendNotification();
+  application.Render(0);
+  DALI_TEST_EQUALS(uniformBlock.GetCurrentProperty<Vector4>(colorIndex), Color::WHITE, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliUniformBlockAnimatedProperty01(void)
+{
+  TestApplication application;
+
+  tet_infoline("Test that a uniform block property can be animated");
+
+  UniformBlock uniformBlock = UniformBlock::New("testBlock");
+
+  Vector4         initialColor = Color::WHITE;
+  Property::Index colorIndex   = uniformBlock.RegisterProperty("uFadeColor", initialColor);
+
+  application.SendNotification();
+  application.Render(0);
+  DALI_TEST_EQUALS(uniformBlock.GetProperty<Vector4>(colorIndex), initialColor, TEST_LOCATION);
+
+  Animation animation = Animation::New(1.0f);
+  KeyFrames keyFrames = KeyFrames::New();
+  keyFrames.Add(0.0f, initialColor);
+  keyFrames.Add(1.0f, Color::TRANSPARENT);
+  animation.AnimateBetween(Property(uniformBlock, colorIndex), keyFrames);
+  animation.Play();
+
+  application.SendNotification();
+  application.Render(500);
+
+  DALI_TEST_EQUALS(uniformBlock.GetCurrentProperty<Vector4>(colorIndex), Color::WHITE * 0.5f, TEST_LOCATION);
+
+  application.Render(500);
+
+  DALI_TEST_EQUALS(uniformBlock.GetCurrentProperty<Vector4>(colorIndex), Color::TRANSPARENT, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliUniformBlockDestructWorkerThreadN(void)
+{
+  TestApplication application;
+  tet_infoline("UtcDaliUniformBlockDestructWorkerThreadN Test, for line coverage");
+
+  try
+  {
+    class TestThread : public Thread
+    {
+    public:
+      virtual void Run()
+      {
+        tet_printf("Run TestThread\n");
+        // Destruct at worker thread.
+        mUniformBlock.Reset();
+      }
+
+      Dali::UniformBlock mUniformBlock;
+    };
+    TestThread thread;
+
+    Dali::UniformBlock uniformBlock = Dali::UniformBlock::New("testBlock");
+    thread.mUniformBlock            = std::move(uniformBlock);
+    uniformBlock.Reset();
+
+    thread.Start();
+
+    thread.Join();
+  }
+  catch(...)
+  {
+  }
+
+  // Always success
+  DALI_TEST_CHECK(true);
+
+  END_TEST;
+}
index 7e3d251ae5767e93c617d42cbe838e1b1a79ff31..a3af691eee625b41ac11db77c9a6519ae43105f2 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2025 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.
@@ -504,6 +504,9 @@ int UtcDaliVisualRendererAnimatedProperty03(void)
   Geometry       geometry = CreateQuadGeometry();
   VisualRenderer renderer = VisualRenderer::New(geometry, shader);
 
+  // Add all uniform mappings
+  renderer.RegisterVisualTransformUniform();
+
   Actor actor = Actor::New();
   actor.AddRenderer(renderer);
   actor.SetProperty(Actor::Property::SIZE, Vector2(400.0f, 400.0f));
index 421939533dadb02fdf6d087a1dfa2f52d1c2c4c5..dee7594ea1dda804450e287642757497a7f7cf5f 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2025 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::Integration
 {
+Dali::Shader ShaderNewWithUniformBlock(std::string_view vertexShader, std::string_view fragmentShader, Shader::Hint::Value hints, std::string_view shaderName, std::vector<Dali::UniformBlock> uniformBlocks)
+{
+  Internal::ShaderPtr shader = Dali::Internal::Shader::New(vertexShader, fragmentShader, hints, shaderName, std::move(uniformBlocks));
+  return Shader(shader.Get());
+}
+
 std::string GenerateTaggedShaderPrefix(const std::string& shaderPrefix)
 {
   return Dali::Internal::Shader::GenerateTaggedShaderPrefix(shaderPrefix);
index fea64a8ce7c7152141fb15baa3c0bbde0cef8e92..a0d41fa5c9745fbfd7886ae88810088a8e11e4f2 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_SHADER_INTEG_H
 
 /*
- * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2025 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.
  */
 
 // EXTERNAL INCLUDES
-#include <string> // std::string
+#include <string>      // std::string
+#include <string_view> // std::string_view
 
 // INTERNAL INCLUDES
+#include <dali/public-api/common/vector-wrapper.h>
 #include <dali/public-api/rendering/shader.h>
+#include <dali/public-api/rendering/uniform-block.h>
 
 namespace Dali::Integration
 {
+/**
+ * @brief Creates Shader and connect by given uniform blocks
+ *
+ * @SINCE_2_4.14
+ * @param[in] vertexShader Vertex shader code for the effect.
+ * @param[in] fragmentShader Fragment Shader code for the effect.
+ * @param[in] hints Hints to define the geometry of the rendered object
+ * @param[in] shaderName The name of this shader.
+ * @param[in] uniformBlocks Uniform blocks to be connected to shader.
+ * @return A handle to a shader effect
+ */
+DALI_CORE_API Dali::Shader ShaderNewWithUniformBlock(std::string_view vertexShader, std::string_view fragmentShader, Shader::Hint::Value hints, std::string_view shaderName, std::vector<Dali::UniformBlock> uniformBlocks);
+
 /**
  * @brief Generates tag 'legacy-prefix-end' with end position of
  * prefix text to make shader code parsing easier.
index 32c6f92763478e752935bc8b93f1adf8349ed70d..340578a8b56f8d7c932ac57225297203cc92c527 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2025 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.
@@ -90,8 +90,6 @@ DecoratedVisualRendererPtr DecoratedVisualRenderer::New()
   // pass the pointer to base for message passing
   DecoratedVisualRendererPtr rendererPtr(new DecoratedVisualRenderer(sceneObjectKey.Get()));
 
-  rendererPtr->AddUniformMappings(); // Ensure properties are mapped to uniforms
-
   EventThreadServices&       eventThreadServices = rendererPtr->GetEventThreadServices();
   SceneGraph::UpdateManager& updateManager       = eventThreadServices.GetUpdateManager();
   AddRendererMessage(updateManager, sceneObjectKey);
index 0d29bc26cda55216aac9708d319f287535b8e1e2..14fcda25e913eecf9027748f84be6a1a37477556 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2025 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.
@@ -22,7 +22,9 @@
 #include <dali/devel-api/scripting/scripting.h>
 #include <dali/internal/event/common/property-helper.h> // DALI_PROPERTY_TABLE_BEGIN, DALI_PROPERTY, DALI_PROPERTY_TABLE_END
 #include <dali/internal/event/common/property-input-impl.h>
+#include <dali/internal/event/rendering/uniform-block-impl.h>
 #include <dali/internal/render/renderers/render-geometry.h>
+#include <dali/internal/render/renderers/render-uniform-block.h>
 #include <dali/internal/update/manager/update-manager.h>
 #include <dali/internal/update/rendering/scene-graph-renderer-messages.h>
 #include <dali/internal/update/rendering/scene-graph-renderer.h>
index f382094ec91130d177541405b851023ef6196097..ad5b226ad6381863c09a16a72ec50ed4d0fc5b0c 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_INTERNAL_RENDERER_H
 
 /*
- * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2025 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.
@@ -40,6 +40,8 @@ class Renderer;
 }
 
 class Renderer;
+class UniformBlock;
+
 using RendererPtr = IntrusivePtr<Renderer>;
 
 /**
index fb32ee0d8b372dae318ff74f60cfb1a583bc5f9d..a6ff3c6bfa8b437594247f7b47c3fbd3134d63f8 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2025 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.
@@ -23,6 +23,8 @@
 #include <dali/internal/event/common/property-helper.h> // DALI_PROPERTY_TABLE_BEGIN, DALI_PROPERTY, DALI_PROPERTY_TABLE_END
 #include <dali/internal/event/common/thread-local-storage.h>
 #include <dali/internal/event/effects/shader-factory.h>
+#include <dali/internal/event/rendering/uniform-block-impl.h>
+#include <dali/internal/render/renderers/render-uniform-block.h>
 #include <dali/internal/update/manager/update-manager.h>
 #include <dali/public-api/object/type-registry.h>
 
@@ -135,10 +137,11 @@ void GetShaderData(const Property::Map& shaderMap, std::string& vertexShader, st
 
 } // unnamed namespace
 
-ShaderPtr Shader::New(std::string_view          vertexShader,
-                      std::string_view          fragmentShader,
-                      Dali::Shader::Hint::Value hints,
-                      std::string_view          shaderName)
+ShaderPtr Shader::New(std::string_view                vertexShader,
+                      std::string_view                fragmentShader,
+                      Dali::Shader::Hint::Value       hints,
+                      std::string_view                shaderName,
+                      std::vector<Dali::UniformBlock> uniformBlocks)
 {
   // create scene object first so it's guaranteed to exist for the event side
   auto                             sceneObject = new SceneGraph::Shader();
@@ -153,6 +156,15 @@ ShaderPtr Shader::New(std::string_view          vertexShader,
   services.RegisterObject(shader.Get());
   shader->UpdateShaderData(vertexShader, fragmentShader, DEFAULT_RENDER_PASS_TAG, hints, shaderName);
 
+  // Connect UniformBlock without clean up cache
+  for(auto&& uniformBlock : uniformBlocks)
+  {
+    if(uniformBlock)
+    {
+      GetImplementation(uniformBlock).ConnectToShader(shader.Get(), false);
+    }
+  }
+
   return shader;
 }
 
@@ -315,6 +327,52 @@ void Shader::SetShaderProperty(const Dali::Property::Value& shaderMap)
     DALI_LOG_ERROR("Shader program property should be a map or array of map.\n");
   }
 }
+void Shader::ConnectUniformBlock(UniformBlock& uniformBlock, bool programCacheCleanRequired)
+{
+  AddObserver(uniformBlock);
+  if(DALI_LIKELY(EventThreadServices::IsCoreRunning()))
+  {
+    auto& eventThreadServices = GetEventThreadServices();
+
+    {
+      using LocalType = MessageValue1<SceneGraph::Shader, Render::UniformBlock*>;
+      uint32_t* slot  = eventThreadServices.ReserveMessageSlot(sizeof(LocalType));
+
+      new(slot) LocalType(&GetShaderSceneObject(), &SceneGraph::Shader::ConnectUniformBlock, const_cast<Render::UniformBlock*>(&uniformBlock.GetUniformBlockSceneObject()));
+    }
+    if(programCacheCleanRequired)
+    {
+      RequestClearProgramCacheMessage(eventThreadServices.GetUpdateManager());
+    }
+  }
+}
+
+void Shader::DisconnectUniformBlock(UniformBlock& uniformBlock)
+{
+  RemoveObserver(uniformBlock);
+  if(DALI_LIKELY(EventThreadServices::IsCoreRunning()))
+  {
+    auto& eventThreadServices = GetEventThreadServices();
+
+    {
+      using LocalType = MessageValue1<SceneGraph::Shader, Render::UniformBlock*>;
+      uint32_t* slot  = eventThreadServices.ReserveMessageSlot(sizeof(LocalType));
+
+      new(slot) LocalType(&GetShaderSceneObject(), &SceneGraph::Shader::DisconnectUniformBlock, const_cast<Render::UniformBlock*>(&uniformBlock.GetUniformBlockSceneObject()));
+    }
+    RequestClearProgramCacheMessage(eventThreadServices.GetUpdateManager());
+  }
+}
+
+void Shader::ObjectDestroyed(Object& object)
+{
+  auto* uniformBlock = dynamic_cast<UniformBlock*>(&object);
+  if(uniformBlock)
+  {
+    // Disconnect uniform block connection automatically if UniformBlock is destroyed.
+    DisconnectUniformBlock(*uniformBlock);
+  }
+}
 
 Shader::~Shader()
 {
index 1b46fc234d7d947b1cee92e6092d6c47878f7e0c..6ffc6f158428eaa090eb1830ea139fb3ccc8ecee 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_INTERNAL_SHADER_H
 
 /*
- * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2025 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.
@@ -24,7 +24,9 @@
 #include <dali/internal/event/common/object-impl.h>      // Dali::Internal::Object
 #include <dali/public-api/common/dali-common.h>          // DALI_ASSERT_ALWAYS
 #include <dali/public-api/common/intrusive-ptr.h>        // Dali::IntrusivePtr
+#include <dali/public-api/common/vector-wrapper.h>       // std::vector<>
 #include <dali/public-api/rendering/shader.h>            // Dali::Shader
+#include <dali/public-api/rendering/uniform-block.h>     // Dali::UniformBlock
 
 namespace Dali
 {
@@ -34,24 +36,26 @@ namespace SceneGraph
 {
 class Shader;
 }
-
 class Shader;
+class UniformBlock;
+
 using ShaderPtr = IntrusivePtr<Shader>;
 
 /**
  * Shader is an object that contains an array of structures of values that
  * can be accessed as properties.
  */
-class Shader : public Object
+class Shader : public Object, public Object::Observer
 {
 public:
   /**
    * @copydoc Dali::Shader::New()
    */
-  static ShaderPtr New(std::string_view          vertexShader,
-                       std::string_view          fragmentShader,
-                       Dali::Shader::Hint::Value hints,
-                       std::string_view          shaderName);
+  static ShaderPtr New(std::string_view                vertexShader,
+                       std::string_view                fragmentShader,
+                       Dali::Shader::Hint::Value       hints,
+                       std::string_view                shaderName,
+                       std::vector<Dali::UniformBlock> uniformBlocks);
 
   /**
    * @copydoc Dali::Shader::New()
@@ -107,6 +111,43 @@ private: // implementation
    */
   void SetShaderProperty(const Dali::Property::Value& shaderMap);
 
+public:
+  /**
+   * @brief Connects to the uniform block.
+   *
+   * @param[in] uniformBlock The uniform block to connect.
+   * @param[in] programCacheCleanRequired Whether program cache clean is required or not.
+   * Could be false only if the shader never be rendered before. (e.g. shader constructor.)
+   */
+  void ConnectUniformBlock(UniformBlock& uniformBlock, bool programCacheCleanRequired = true);
+
+  /**
+   * @brief Disconnects to the uniform block.
+   *
+   * @param[in] uniformBlock The uniform block to connect.
+   */
+  void DisconnectUniformBlock(UniformBlock& uniformBlock);
+
+protected: ///< From Dali::Internal::Object::Observer
+  /**
+   * @copydoc Dali::Internal::Object::Observer::SceneObjectAdded()
+   */
+  void SceneObjectAdded(Object& object) override
+  {
+    // Do nothing
+  }
+  /**
+   * @copydoc Dali::Internal::Object::Observer::SceneObjectRemoved()
+   */
+  void SceneObjectRemoved(Object& object) override
+  {
+    // Do nothing
+  }
+  /**
+   * @copydoc Dali::Internal::Object::Observer::ObjectDestroyed()
+   */
+  void ObjectDestroyed(Object& object) override;
+
 protected:
   /**
    * A reference counted object may only be deleted by calling Unreference()
diff --git a/dali/internal/event/rendering/uniform-block-impl.cpp b/dali/internal/event/rendering/uniform-block-impl.cpp
new file mode 100644 (file)
index 0000000..30fbdbd
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2025 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/event/rendering/uniform-block-impl.h>
+
+// INTERNAL INCLUDES
+#include <dali/internal/event/rendering/renderer-impl.h>
+#include <dali/internal/event/rendering/shader-impl.h>
+#include <dali/internal/render/renderers/render-uniform-block.h>
+#include <dali/internal/update/manager/update-manager.h> ///< for AddUniformBlockMessage
+
+namespace Dali::Internal
+{
+UniformBlockPtr UniformBlock::New(std::string&& name)
+{
+  auto sceneObject = new Render::UniformBlock(std::move(name)); // In Update/Render side, only 1 object.
+
+  OwnerPointer<Render::UniformBlock> transferOwnership(sceneObject);
+  UniformBlockPtr                    uniformBlock(new UniformBlock(sceneObject));
+
+  auto&& services = uniformBlock->GetEventThreadServices();
+  AddUniformBlockMessage(services.GetUpdateManager(), transferOwnership);
+  services.RegisterObject(uniformBlock.Get());
+
+  return uniformBlock;
+}
+
+const Render::UniformBlock& UniformBlock::GetUniformBlockSceneObject() const
+{
+  return static_cast<const Render::UniformBlock&>(GetSceneObject());
+}
+
+bool UniformBlock::ConnectToShader(Shader* shader, bool programCacheCleanRequired)
+{
+  if(shader != nullptr)
+  {
+    if(mShaderContainer.find(shader) == mShaderContainer.end())
+    {
+      mShaderContainer.insert(shader);
+      AddObserver(*shader);
+      shader->ConnectUniformBlock(*this, programCacheCleanRequired);
+      return true;
+    }
+  }
+  return false;
+}
+
+void UniformBlock::DisconnectFromShader(Shader* shader)
+{
+  if(shader != nullptr)
+  {
+    auto iter = mShaderContainer.find(shader);
+    if(iter != mShaderContainer.end())
+    {
+      mShaderContainer.erase(iter);
+      RemoveObserver(*shader);
+      shader->DisconnectUniformBlock(*this);
+    }
+  }
+}
+
+void UniformBlock::ObjectDestroyed(Object& object)
+{
+  auto* shader = dynamic_cast<Shader*>(&object);
+  if(shader)
+  {
+    DisconnectFromShader(shader);
+  }
+}
+
+UniformBlock::UniformBlock(const Render::UniformBlock* sceneObject)
+: Object(sceneObject),
+  mUniformBlockName(sceneObject->GetName())
+{
+}
+
+UniformBlock::~UniformBlock()
+{
+  if(EventThreadServices::IsCoreRunning())
+  {
+    auto& services = GetEventThreadServices();
+    RemoveUniformBlockMessage(services.GetUpdateManager(), const_cast<Render::UniformBlock&>(GetUniformBlockSceneObject()));
+    services.UnregisterObject(this);
+  }
+}
+
+}; //namespace Dali::Internal
diff --git a/dali/internal/event/rendering/uniform-block-impl.h b/dali/internal/event/rendering/uniform-block-impl.h
new file mode 100644 (file)
index 0000000..42b4170
--- /dev/null
@@ -0,0 +1,162 @@
+#ifndef DALI_INTERNAL_UNIFORM_BLOCK_H
+#define DALI_INTERNAL_UNIFORM_BLOCK_H
+
+/*
+ * Copyright (c) 2025 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// EXTERNAL INCLUDES
+#include <string_view>
+#include <unordered_set>
+
+// INTERNAL INCLUDES
+#include <dali/internal/event/common/object-impl.h> // Dali::Internal::Object
+#include <dali/public-api/common/intrusive-ptr.h>   // Dali::IntrusivePtr
+#include <dali/public-api/rendering/uniform-block.h>
+
+namespace Dali::Internal
+{
+namespace Render
+{
+class UniformBlock;
+}
+
+class UniformBlock;
+using UniformBlockPtr = IntrusivePtr<UniformBlock>;
+
+/**
+ * @copydoc Dali::UniformBlock
+ */
+class UniformBlock : public Object, public Object::Observer
+{
+public:
+  /**
+   * @brief Construct a new UniformBlock resource.
+   *
+   * @param[in] name The name of the uniform block in the shader
+   * @return A new UniformBlock resource
+   */
+  static UniformBlockPtr New(std::string&& name);
+
+  /**
+   * @brief Get the scene object associated with this proxy object.
+   */
+  const Render::UniformBlock& GetUniformBlockSceneObject() const;
+
+public:
+  /**
+   * @copydoc Dali::UniformBlock::GetUniformBlockName
+   */
+  std::string_view GetUniformBlockName() const
+  {
+    return mUniformBlockName;
+  }
+
+  /**
+   * @copydoc Dali::UniformBlock::ConnectToShader
+   */
+  bool ConnectToShader(Shader* shader)
+  {
+    return ConnectToShader(shader, true);
+  }
+
+  /**
+   * @copydoc Dali::UniformBlock::DisconnectFromShader
+   */
+  void DisconnectFromShader(Shader* shader);
+
+public:
+  /**
+   * @brief Be connecteds to the shader.
+   *
+   * @param[in] shader The shader to be connected.
+   * @param[in] programCacheCleanRequired Whether program cache clean is required or not.
+   * Could be false only if the shader never be rendered before. (e.g. shader constructor.)
+   */
+  bool ConnectToShader(Shader* shader, bool programCacheCleanRequired);
+
+protected: ///< From Dali::Internal::Object::Observer
+  /**
+   * @copydoc Dali::Internal::Object::Observer::SceneObjectAdded()
+   */
+  void SceneObjectAdded(Object& object) override
+  {
+    // Do nothing
+  }
+
+  /**
+   * @copydoc Dali::Internal::Object::Observer::SceneObjectRemoved()
+   */
+  void SceneObjectRemoved(Object& object) override
+  {
+    // Do nothing
+  }
+
+  /**
+   * @copydoc Dali::Internal::Object::Observer::ObjectDestroyed()
+   */
+  void ObjectDestroyed(Object& object) override;
+
+protected: // implementation
+  /**
+   * @brief Constructor.
+   *
+   * @param sceneObject the scene graph uniformBlock
+   */
+  UniformBlock(const Render::UniformBlock* sceneObject);
+
+  /**
+   * A reference counted object may only be deleted by calling Unreference()
+   */
+  ~UniformBlock() override;
+
+private:
+  UniformBlock(const UniformBlock&) = delete;            ///< Deleted copy constructor
+  UniformBlock& operator=(const UniformBlock&) = delete; ///< Deleted assignment operator
+
+private:
+  using ShaderContainer = std::unordered_set<Shader*>;
+
+  ShaderContainer mShaderContainer; // List of shaders that are connected to this UniformBlock (not owned)
+
+  std::string_view mUniformBlockName; ///< The name of uniform blocks. String owned by scene object.
+};
+
+}; //namespace Dali::Internal
+
+namespace Dali
+{
+// Helpers for public-api forwarding methods
+inline Internal::UniformBlock& GetImplementation(Dali::UniformBlock& handle)
+{
+  DALI_ASSERT_ALWAYS(handle && "UniformBlock handle is empty");
+
+  BaseObject& object = handle.GetBaseObject();
+
+  return static_cast<Internal::UniformBlock&>(object);
+}
+
+inline const Internal::UniformBlock& GetImplementation(const Dali::UniformBlock& handle)
+{
+  DALI_ASSERT_ALWAYS(handle && "UniformBlock handle is empty");
+
+  const BaseObject& object = handle.GetBaseObject();
+
+  return static_cast<const Internal::UniformBlock&>(object);
+}
+
+} // namespace Dali
+
+#endif //DALI_INTERNAL_UNIFORM_BLOCK_H
index 2b4dc5068b5a3ebb3a8ee6690a4069b43d4fb30a..c70d61f0be10278011d225c823ec62d213707c4c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2025 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.
@@ -74,8 +74,6 @@ VisualRendererPtr VisualRenderer::New()
   // pass the pointer to base for message passing
   VisualRendererPtr rendererPtr(new VisualRenderer(sceneObjectKey.Get()));
 
-  rendererPtr->AddUniformMappings(); // Ensure properties are mapped to uniforms
-
   // transfer scene object ownership to update manager
   EventThreadServices&       eventThreadServices = rendererPtr->GetEventThreadServices();
   SceneGraph::UpdateManager& updateManager       = eventThreadServices.GetUpdateManager();
@@ -86,7 +84,9 @@ VisualRendererPtr VisualRenderer::New()
 }
 
 VisualRenderer::VisualRenderer(const SceneGraph::Renderer* sceneObject)
-: Renderer(sceneObject)
+: Renderer(sceneObject),
+  mPropertyCache(),
+  mUniformMapped(false)
 {
 }
 
@@ -117,7 +117,6 @@ void VisualRenderer::SetDefaultProperty(Property::Index        index,
         {
           const SceneGraph::Renderer& sceneObject      = GetVisualRendererSceneObject();
           auto                        visualProperties = sceneObject.GetVisualProperties();
-
           if(visualProperties)
           {
             BakeMessage<Vector2>(GetEventThreadServices(), *mUpdateObject, visualProperties->mTransformOffset, mPropertyCache.mTransformOffset);
@@ -519,14 +518,18 @@ const PropertyInputImpl* VisualRenderer::GetSceneObjectInputProperty(Property::I
   return nullptr;
 }
 
-void VisualRenderer::AddUniformMappings()
+void VisualRenderer::RegisterVisualTransformUniform()
 {
-  AddUniformMapping(Dali::VisualRenderer::Property::TRANSFORM_OFFSET, ConstString("offset"));
-  AddUniformMapping(Dali::VisualRenderer::Property::TRANSFORM_SIZE, ConstString("size"));
-  AddUniformMapping(Dali::VisualRenderer::Property::TRANSFORM_OFFSET_SIZE_MODE, ConstString("offsetSizeMode"));
-  AddUniformMapping(Dali::VisualRenderer::Property::TRANSFORM_ORIGIN, ConstString("origin"));
-  AddUniformMapping(Dali::VisualRenderer::Property::TRANSFORM_ANCHOR_POINT, ConstString("anchorPoint"));
-  AddUniformMapping(Dali::VisualRenderer::Property::EXTRA_SIZE, ConstString("extraSize"));
+  if(!mUniformMapped)
+  {
+    mUniformMapped = true;
+    AddUniformMapping(Dali::VisualRenderer::Property::TRANSFORM_OFFSET, ConstString("offset"));
+    AddUniformMapping(Dali::VisualRenderer::Property::TRANSFORM_SIZE, ConstString("size"));
+    AddUniformMapping(Dali::VisualRenderer::Property::TRANSFORM_OFFSET_SIZE_MODE, ConstString("offsetSizeMode"));
+    AddUniformMapping(Dali::VisualRenderer::Property::TRANSFORM_ORIGIN, ConstString("origin"));
+    AddUniformMapping(Dali::VisualRenderer::Property::TRANSFORM_ANCHOR_POINT, ConstString("anchorPoint"));
+    AddUniformMapping(Dali::VisualRenderer::Property::EXTRA_SIZE, ConstString("extraSize"));
+  }
 }
 
 } // namespace Internal
index 103c5770ec427c2aa5597515263d427bae9d8f5b..a35f97469e88149168430757abf8ac521ed367ba 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_INTERNAL_VISUAL_RENDERER_H
 
 /*
- * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2025 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.
@@ -84,6 +84,12 @@ public: // Default property extensions from Object
    */
   const PropertyInputImpl* GetSceneObjectInputProperty(Property::Index index) const override;
 
+public:
+  /**
+   * @copydoc Dali::VisualRenderer::RegisterVisualTransformUniform()
+   */
+  void RegisterVisualTransformUniform();
+
 protected: // implementation
   /**
    * @brief Constructor.
@@ -100,11 +106,6 @@ protected: // implementation
    */
   bool GetCurrentPropertyValue(Property::Index index, Property::Value& value) const;
 
-  /**
-   * @brief Ensure that properties are mapped to uniforms
-   */
-  void AddUniformMappings();
-
 protected:
   /**
    * A reference counted object may only be deleted by calling Unreference()
@@ -129,6 +130,8 @@ public:
 
 private:
   VisualPropertyCache mPropertyCache;
+
+  bool mUniformMapped : 1;
 };
 
 } // namespace Internal
index 32a8204e2493c366f9e89df5092e52df87ade280..e8956ec52d147427e216ad630b95f975135a9dda 100644 (file)
@@ -103,6 +103,7 @@ SET( internal_src_files
   ${internal_src_dir}/event/rendering/visual-renderer-impl.cpp
   ${internal_src_dir}/event/rendering/sampler-impl.cpp
   ${internal_src_dir}/event/rendering/shader-impl.cpp
+  ${internal_src_dir}/event/rendering/uniform-block-impl.cpp
   ${internal_src_dir}/event/rendering/vertex-buffer-impl.cpp
   ${internal_src_dir}/event/size-negotiation/memory-pool-relayout-container.cpp
   ${internal_src_dir}/event/size-negotiation/relayout-controller-impl.cpp
@@ -114,6 +115,7 @@ SET( internal_src_files
   ${internal_src_dir}/render/common/render-item.cpp
   ${internal_src_dir}/render/common/render-tracker.cpp
   ${internal_src_dir}/render/common/render-manager.cpp
+  ${internal_src_dir}/render/common/shared-uniform-buffer-view-container.cpp
   ${internal_src_dir}/render/queue/render-queue.cpp
   ${internal_src_dir}/render/renderers/gpu-buffer.cpp
   ${internal_src_dir}/render/renderers/pipeline-cache.cpp
@@ -123,6 +125,7 @@ SET( internal_src_files
   ${internal_src_dir}/render/renderers/render-sampler.cpp
   ${internal_src_dir}/render/renderers/render-texture.cpp
   ${internal_src_dir}/render/renderers/render-vertex-buffer.cpp
+  ${internal_src_dir}/render/renderers/render-uniform-block.cpp
   ${internal_src_dir}/render/renderers/uniform-buffer.cpp
   ${internal_src_dir}/render/renderers/uniform-buffer-manager.cpp
   ${internal_src_dir}/render/renderers/uniform-buffer-view.cpp
index d9d6bac3456e8340bdbeac586e7e9fa3931bc58d..9e065da337911d6c5a5bd1377e9eb5806b3a9d13 100644 (file)
 #include <dali/internal/render/common/render-debug.h>
 #include <dali/internal/render/common/render-instruction.h>
 #include <dali/internal/render/common/render-tracker.h>
+#include <dali/internal/render/common/shared-uniform-buffer-view-container.h>
 #include <dali/internal/render/queue/render-queue.h>
 #include <dali/internal/render/renderers/pipeline-cache.h>
 #include <dali/internal/render/renderers/render-frame-buffer.h>
 #include <dali/internal/render/renderers/render-texture.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>
 #include <dali/internal/render/shaders/program-controller.h>
 
@@ -199,6 +201,15 @@ struct RenderManager::Impl
     mRenderTrackers.EraseObject(renderTracker);
   }
 
+  void ClearProgramCache()
+  {
+    DALI_LOG_RELEASE_INFO("Clear ProgramCache! program : [%u]\n", programController.GetCachedProgramCount());
+    programController.ClearCache();
+
+    // Reset incremental cache cleanup.
+    programCacheCleanRequestedFrame = 0u;
+  }
+
   void UpdateTrackers()
   {
     for(auto&& iter : mRenderTrackers)
@@ -315,6 +326,8 @@ struct RenderManager::Impl
   std::unique_ptr<Render::UniformBufferManager> uniformBufferManager; ///< The uniform buffer manager
   std::unique_ptr<Render::PipelineCache>        pipelineCache;
 
+  SharedUniformBufferViewContainer sharedUniformBufferViewContainer; ///< Shared Uniform Buffer Views
+
 #if defined(LOW_SPEC_MEMORY_MANAGEMENT_ENABLED)
   ContainerRemovedFlags containerRemovedFlags; ///< cumulative container removed flags during current frame
 #endif
@@ -399,7 +412,7 @@ void RenderManager::SetShaderSaver(ShaderSaver& upstream)
 void RenderManager::AddRenderer(const Render::RendererKey& renderer)
 {
   // Initialize the renderer as we are now in render thread
-  renderer->Initialize(mImpl->graphicsController, mImpl->programController, *(mImpl->uniformBufferManager.get()), *(mImpl->pipelineCache.get()));
+  renderer->Initialize(mImpl->graphicsController, mImpl->programController, *(mImpl->uniformBufferManager.get()), *(mImpl->pipelineCache.get()), mImpl->sharedUniformBufferViewContainer);
 
   mImpl->rendererContainer.PushBack(renderer);
 }
@@ -636,6 +649,11 @@ void RenderManager::RemoveRenderTracker(Render::RenderTracker* renderTracker)
   mImpl->RemoveRenderTracker(renderTracker);
 }
 
+void RenderManager::ClearProgramCache()
+{
+  mImpl->ClearProgramCache();
+}
+
 void RenderManager::PreRender(Integration::RenderStatus& status, bool forceClear)
 {
   DALI_PRINT_RENDER_START(mImpl->renderBufferIndex);
@@ -1056,9 +1074,25 @@ void RenderManager::RenderScene(Integration::RenderStatus& status, Integration::
               // collect how many programs we use in this frame
               auto key = &program->GetGraphicsProgram();
               auto it  = programUsageCount.find(key);
+
               if(it == programUsageCount.end())
               {
                 programUsageCount[key] = Graphics::ProgramResourceBindingInfo{.program = key, .count = 1};
+
+                if(memoryRequirements.sharedGpuSizeRequired > 0u)
+                {
+                  totalSizeGPU += memoryRequirements.sharedGpuSizeRequired; ///< Add it only 1 times.
+
+                  // TODO : Prepare to create UBOView for each UBO Blocks.
+                  for(uint32_t i = 1u; i < memoryRequirements.sharedBlock.size(); ++i)
+                  {
+                    auto* uniformBlock = memoryRequirements.sharedBlock[i];
+                    if(uniformBlock)
+                    {
+                      mImpl->sharedUniformBufferViewContainer.RegisterSharedUniformBlockAndPrograms(*program, *uniformBlock, memoryRequirements.blockSize[i]);
+                    }
+                  }
+                }
               }
               else
               {
@@ -1134,6 +1168,9 @@ void RenderManager::RenderScene(Integration::RenderStatus& status, Integration::
   }
 #endif
 
+  // Create UniformBufferView and Write shared UBO value here.
+  mImpl->sharedUniformBufferViewContainer.Initialize(mImpl->renderBufferIndex, *uboManager);
+
   if(renderToFbo)
   {
     for(uint32_t i = 0; i < instructionCount; ++i)
@@ -1385,6 +1422,9 @@ void RenderManager::RenderScene(Integration::RenderStatus& status, Integration::
     mImpl->graphicsController.PresentRenderTarget(renderTarget);
     DALI_TRACE_END(gTraceFilter, "DALI_RENDER_FINISHED");
   }
+
+  // RollBack Shared UBO view list to avoid leak.
+  mImpl->sharedUniformBufferViewContainer.Finalize();
 }
 
 void RenderManager::ClearScene(Integration::Scene scene)
index 2f7ab509439b45775db08ca3c892b8e063dcd392..4c460d453ac2d20e0ca26b91cce0a52d3f1f6300 100644 (file)
@@ -362,6 +362,12 @@ public:
    */
   void RemoveRenderTracker(Render::RenderTracker* renderTracker);
 
+  /**
+   * Clear all cached programs and shaders from the cache. This will be used
+   * when we need recreate the Graphics::Program forcibly.
+   */
+  void ClearProgramCache();
+
   // This method should be called from Core::PreRender()
 
   /**
diff --git a/dali/internal/render/common/shared-uniform-buffer-view-container.cpp b/dali/internal/render/common/shared-uniform-buffer-view-container.cpp
new file mode 100644 (file)
index 0000000..4951569
--- /dev/null
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) 2025 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/common/shared-uniform-buffer-view-container.h>
+
+// EXTERNAL INCLUDES
+#include <dali/integration-api/debug.h>
+#include <memory>
+#include <string>
+#include <unordered_map>
+#include <unordered_set>
+
+// INTERNAL INCLUDES
+#include <dali/internal/render/renderers/render-uniform-block.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>
+#include <dali/internal/render/shaders/program.h>
+
+namespace Dali::Internal
+{
+#if defined(DEBUG_ENABLED)
+namespace
+{
+Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_RENDER_MANAGER");
+} // unnamed namespace
+#endif
+
+struct SharedUniformBufferViewContainer::Impl
+{
+public:
+  Impl()
+  : mSharedUniformBlockBufferViews()
+  {
+  }
+  ~Impl() = default;
+
+  struct ProgramUniformBlockPair
+  {
+    ProgramUniformBlockPair(const Program* program, Render::UniformBlock* sharedUniformBlock, uint32_t blockSize)
+    : program(program),
+      sharedUniformBlock(sharedUniformBlock),
+      blockSize(blockSize)
+    {
+    }
+
+    ProgramUniformBlockPair() = default;
+
+    bool operator==(const ProgramUniformBlockPair& rhs) const
+    {
+      return program == rhs.program && sharedUniformBlock == rhs.sharedUniformBlock;
+    }
+
+    struct ProgramUniformBlockPairHash
+    {
+      // Reference by : https://stackoverflow.com/a/21062236
+      std::size_t operator()(ProgramUniformBlockPair const& key) const noexcept
+      {
+        constexpr std::size_t programShift      = Dali::Log<1 + sizeof(Program)>::value;
+        constexpr std::size_t uniformBlockShift = Dali::Log<1 + sizeof(Render::UniformBlock)>::value;
+        constexpr std::size_t zitterShift       = sizeof(std::size_t) * 4; // zitter shift to avoid hash collision
+
+        return ((reinterpret_cast<std::size_t>(key.program) >> programShift) << zitterShift) ^
+               (reinterpret_cast<std::size_t>(key.sharedUniformBlock) >> uniformBlockShift);
+      }
+    };
+
+    const Program*        program{nullptr};
+    Render::UniformBlock* sharedUniformBlock{nullptr};
+
+    const uint32_t blockSize{0u}; ///< Size of block for given pair. We don't need to compare this value for check hash.
+  };
+
+  using UniformBufferViewContainer = std::unordered_map<ProgramUniformBlockPair, Dali::Graphics::UniquePtr<Render::UniformBufferView>, ProgramUniformBlockPair::ProgramUniformBlockPairHash>;
+  UniformBufferViewContainer mSharedUniformBlockBufferViews{};
+};
+
+SharedUniformBufferViewContainer::SharedUniformBufferViewContainer()
+: mImpl(new Impl())
+{
+}
+
+SharedUniformBufferViewContainer::~SharedUniformBufferViewContainer() = default;
+
+void SharedUniformBufferViewContainer::RegisterSharedUniformBlockAndPrograms(const Program& program, Render::UniformBlock& sharedUniformBlock, uint32_t blockSize)
+{
+  mImpl->mSharedUniformBlockBufferViews.insert({Impl::ProgramUniformBlockPair(&program, &sharedUniformBlock, blockSize), nullptr});
+}
+
+void SharedUniformBufferViewContainer::Initialize(BufferIndex renderBufferIndex, Render::UniformBufferManager& uniformBufferManager)
+{
+#if defined(DEBUG_ENABLED)
+  uint32_t totalSize                   = 0u;
+  uint32_t totalUniformBufferViewCount = 0u;
+#endif
+
+  for(auto& item : mImpl->mSharedUniformBlockBufferViews)
+  {
+    const auto& program            = *item.first.program;
+    auto&       sharedUniformBlock = *item.first.sharedUniformBlock;
+    uint32_t    blockSize          = item.first.blockSize;
+
+    item.second = uniformBufferManager.CreateUniformBufferView(blockSize, false);
+
+    if(DALI_UNLIKELY(!item.second))
+    {
+      DALI_LOG_ERROR("Fail to create shared uniform buffer view!\n");
+      continue;
+    }
+
+#if defined(DEBUG_ENABLED)
+    totalSize += blockSize;
+    ++totalUniformBufferViewCount;
+#endif
+
+    auto& ubo = *(item.second.get());
+
+    // Write to the buffer view here, by value at sharedUniformBlock.
+    sharedUniformBlock.WriteUniforms(renderBufferIndex, program, ubo);
+  }
+  DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Registerd : %zu, SharedUniformBufferView count : %u, total block size:%u\n", mImpl->mSharedUniformBlockBufferViews.size(), totalUniformBufferViewCount, totalSize);
+}
+
+Render::UniformBufferView* SharedUniformBufferViewContainer::GetSharedUniformBlockBufferView(const Program& program, Render::UniformBlock& sharedUniformBlock) const
+{
+  auto iter = mImpl->mSharedUniformBlockBufferViews.find(Impl::ProgramUniformBlockPair(&program, &sharedUniformBlock, 0u));
+  if(iter != mImpl->mSharedUniformBlockBufferViews.end())
+  {
+    return iter->second.get();
+  }
+  return nullptr;
+}
+
+void SharedUniformBufferViewContainer::Finalize()
+{
+  mImpl->mSharedUniformBlockBufferViews.clear();
+  mImpl->mSharedUniformBlockBufferViews.rehash(0);
+}
+
+} // namespace Dali::Internal
diff --git a/dali/internal/render/common/shared-uniform-buffer-view-container.h b/dali/internal/render/common/shared-uniform-buffer-view-container.h
new file mode 100644 (file)
index 0000000..34c50b9
--- /dev/null
@@ -0,0 +1,117 @@
+#ifndef DALI_INTERNAL_SHARED_UNIFORM_BUFFER_VIEW_CONTAINER_H
+#define DALI_INTERNAL_SHARED_UNIFORM_BUFFER_VIEW_CONTAINER_H
+
+/*
+ * Copyright (c) 2025 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// EXTERNAL INCLUDES
+#include <memory>
+
+// INTERNAL INCLUDES
+#include <dali/graphics-api/graphics-types.h>
+#include <dali/internal/common/buffer-index.h>
+#include <dali/public-api/math/compile-time-math.h>
+
+namespace Dali::Internal
+{
+class Program;
+
+namespace Render
+{
+class UniformBlock;
+class UniformBufferManager;
+class UniformBufferView;
+} // namespace Render
+
+/**
+ * Container of shared uniform blocks and pairwised programs.
+ * Before initialize, should register whole programs and uniform blocks pair.
+ * At the end of scene rendering, We should call Finalize to ensure UBO memory flushed.
+ * @code
+ * // Frame begin
+ * for each program :
+ *   RegisterSharedUniformBlockAndPrograms(program, uniformBlock, blockSize);
+ *
+ * Initialize(uboManager); ///< Write uniformBlock's uniforms here.
+ *
+ * for each renderer :
+ *   GetSharedUniformBlockBufferView(program, uniformBlock);
+ *
+ * Finalize();
+ * // Frame end
+ * @endcode
+ */
+class SharedUniformBufferViewContainer
+{
+public:
+  /**
+   * Constructor
+   */
+  SharedUniformBufferViewContainer();
+
+public:
+  SharedUniformBufferViewContainer(const SharedUniformBufferViewContainer&) = delete;            ///< copy constructor, not defined
+  SharedUniformBufferViewContainer& operator=(const SharedUniformBufferViewContainer&) = delete; ///< assignment operator, not defined
+
+  /**
+   * Destructor, non virtual as no virtual methods or inheritance
+   */
+  ~SharedUniformBufferViewContainer();
+
+public:
+  /**
+   * @brief Register the pair of program and shared uniform blocks.
+   * It will create and write uniform informations at Initialize timing.
+   * @pre Finialize() should be called before beginning of register.
+   * @post Initialize() should be called at the end of register.
+   *
+   * @param[in] program Program who using shared uniform block
+   * @param[in] sharedUniformBlock UniformBlock who has the uniform informations.
+   * @param[in] blockSize Size of uniform block.
+   */
+  void RegisterSharedUniformBlockAndPrograms(const Program& program, Render::UniformBlock& sharedUniformBlock, uint32_t blockSize);
+
+  /**
+   * @brief Create UniformBufferView for each pair of program and shared uniform blocks.
+   * It will also write uniforms now.
+   *
+   * @param[in] renderBufferIndex Buffer index of current frame
+   * @param[in] uniformBufferManager UBO manager to handle the GPU memories.
+   */
+  void Initialize(BufferIndex renderBufferIndex, Render::UniformBufferManager& uniformBufferManager);
+
+  /**
+   * @brief Get UniformBufferView for pair of program and shared uniform blocks.
+   *
+   * @param[in] program Program who using shared uniform block
+   * @param[in] sharedUniformBlock UniformBlock who has the uniform informations.
+   * @return Pointer of shared uniform block's UniformBufferView (not owned)
+   */
+  Render::UniformBufferView* GetSharedUniformBlockBufferView(const Program& program, Render::UniformBlock& sharedUniformBlock) const;
+
+  /**
+   * @brief Release all UniformBufferView and registered shared uniform blocks infomations.
+   */
+  void Finalize();
+
+private:
+  struct Impl;
+  std::unique_ptr<Impl> mImpl{nullptr};
+};
+} // namespace Dali::Internal
+
+#endif // DALI_INTERNAL_SHARED_UNIFORM_BUFFER_VIEW_CONTAINER_H
index 10a3be8a81d34fc38e6a64f2eda83dcd9af528bb..19488dca405f53c5a999ecc3c00e65f7bc207c38 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_INTERNAL_SCENE_GRAPH_RENDER_DATA_PROVIDER_H
 
 /*
- * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2025 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.
@@ -18,6 +18,8 @@
  *
  */
 
+// INTERNAL INCLUDES
+#include <dali/internal/common/hash-utils.h>
 #include <dali/internal/render/data-providers/uniform-map-data-provider.h>
 #include <dali/internal/render/renderers/render-texture-key.h>
 #include <dali/public-api/common/dali-vector.h>
@@ -32,6 +34,7 @@ namespace Render
 {
 class Texture;
 class Sampler;
+class UniformBlock;
 } // namespace Render
 
 namespace SceneGraph
index 0902a22969f8f83842433a7b16256884605c45dc..a5bbd90d18bda037160d2ddbea75e47eb792c1f3 100644 (file)
@@ -26,6 +26,7 @@
 #include <dali/internal/common/memory-pool-object-allocator.h>
 #include <dali/internal/event/rendering/texture-impl.h>
 #include <dali/internal/render/common/render-instruction.h>
+#include <dali/internal/render/common/shared-uniform-buffer-view-container.h>
 #include <dali/internal/render/data-providers/node-data-provider.h>
 #include <dali/internal/render/data-providers/uniform-map-data-provider.h>
 #include <dali/internal/render/renderers/pipeline-cache.h>
@@ -132,6 +133,7 @@ Renderer::Renderer(SceneGraph::RenderDataProvider* dataProvider,
   mRenderDataProvider(dataProvider),
   mGeometry(geometry),
   mProgramCache(nullptr),
+  mSharedUniformBufferViewContainer(nullptr),
   mStencilParameters(stencilParameters),
   mBlendingOptions(),
   mIndexedDrawFirstElement(0),
@@ -152,12 +154,13 @@ Renderer::Renderer(SceneGraph::RenderDataProvider* dataProvider,
   mBlendingOptions.SetBlendColor(blendColor);
 }
 
-void Renderer::Initialize(Graphics::Controller& graphicsController, ProgramCache& programCache, Render::UniformBufferManager& uniformBufferManager, Render::PipelineCache& pipelineCache)
+void Renderer::Initialize(Graphics::Controller& graphicsController, ProgramCache& programCache, Render::UniformBufferManager& uniformBufferManager, Render::PipelineCache& pipelineCache, SharedUniformBufferViewContainer& sharedUniformBufferViewContainer)
 {
-  mGraphicsController   = &graphicsController;
-  mProgramCache         = &programCache;
-  mUniformBufferManager = &uniformBufferManager;
-  mPipelineCache        = &pipelineCache;
+  mGraphicsController               = &graphicsController;
+  mProgramCache                     = &programCache;
+  mUniformBufferManager             = &uniformBufferManager;
+  mPipelineCache                    = &pipelineCache;
+  mSharedUniformBufferViewContainer = &sharedUniformBufferViewContainer;
 
   // Add Observer now
   if(mGeometry)
@@ -444,6 +447,7 @@ Program* Renderer::PrepareProgram(const SceneGraph::RenderInstruction& instructi
 
   Program* program = Program::New(*mProgramCache,
                                   shaderData,
+                                  shader.GetSharedUniformNamesHash(),
                                   *mGraphicsController);
   if(!program)
   {
@@ -489,7 +493,10 @@ Program* Renderer::PrepareProgram(const SceneGraph::RenderInstruction& instructi
     createInfo.SetShaderState(shaderStates);
     createInfo.SetName(shaderData->GetName());
     auto graphicsProgram = mGraphicsController->CreateProgram(createInfo, nullptr);
-    program->SetGraphicsProgram(std::move(graphicsProgram), *mUniformBufferManager); // generates reflection
+    program->SetGraphicsProgram(std::move(graphicsProgram), *mUniformBufferManager, shader.GetConnectedUniformBlocks()); // generates reflection, defines memory reqs
+
+    // DevNode : We always clear the program caches whenever shared uniform blocks information changed to some shader.
+    //           So we can always assume that current Graphics::Program could be use current shader's connected uniform blocks.
   }
 
   // Set prefetched program to be used during rendering
@@ -638,6 +645,7 @@ bool Renderer::Render(Graphics::CommandBuffer&                             comma
       std::size_t nodeIndex = BuildUniformIndexMap(bufferIndex, node, *program);
       WriteUniformBuffer(bufferIndex, commandBuffer, program, instruction, modelMatrix, modelViewMatrix, viewMatrix, projectionMatrix, worldColor, scale, size, nodeIndex);
     }
+
     // @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))
@@ -804,12 +812,36 @@ void Renderer::WriteUniformBuffer(
     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 : reflection.GetUniformBlockBinding(i);
       mUniformBufferBindings[i].dataSize = reflection.GetUniformBlockSize(i);
-      uboViews[i].reset(uniformBufferView.release());
+
+      bool useSharedBlock = !standaloneUniforms &&
+                            programRequirements.sharedBlock[i];
+
+      if(useSharedBlock)
+      {
+        // If this block IS shared, Get uboView from SharedUniformBufferViewContainer,
+        // which all uniform values are written already.
+        // Write GPU buffer and offset to unfirom buffer bindings.
+        auto sharedUniformBufferViewPtr = mSharedUniformBufferViewContainer->GetSharedUniformBlockBufferView(*program, *programRequirements.sharedBlock[i]);
+
+        DALI_ASSERT_ALWAYS(sharedUniformBufferViewPtr && "SharedUniformBufferView not exist!");
+
+        mUniformBufferBindings[i].buffer = sharedUniformBufferViewPtr->GetBuffer();
+        mUniformBufferBindings[i].offset = sharedUniformBufferViewPtr->GetOffset();
+
+        uboViews[i].reset(nullptr);
+      }
+      else
+      {
+        // If this block is NOT shared, create new uboView.
+        auto uniformBufferView = mUniformBufferManager->CreateUniformBufferView(programRequirements.blockSize[i], standaloneUniforms);
+
+        mUniformBufferBindings[i].buffer = uniformBufferView->GetBuffer();
+        mUniformBufferBindings[i].offset = uniformBufferView->GetOffset();
+
+        uboViews[i].reset(uniformBufferView.release());
+      }
     }
   }
 
@@ -856,6 +888,7 @@ void Renderer::WriteUniformBuffer(
     WriteDefaultUniformV2(program->GetDefaultUniform(Program::DefaultUniformIndex::ACTOR_COLOR), uboViews, worldColor);
 
     // Write uniforms from the uniform map
+    // Uniforms for the Shared UniformBlock should not be in this map. If they are, they should be ignored.
     FillUniformBuffer(*program, instruction, uboViews, bufferIndex, nodeIndex);
 
     // Write uSize in the end, as it shouldn't be overridable by dynamic properties.
@@ -881,7 +914,15 @@ bool Renderer::WriteDefaultUniformV2(const Graphics::UniformInfo* uniformInfo, c
 {
   if(uniformInfo && !uniformInfo->name.empty())
   {
-    WriteUniform(*uboViews[uniformInfo->bufferIndex], *uniformInfo, data);
+    // Test for non-null view first
+    auto ubo = uboViews[uniformInfo->bufferIndex].get();
+
+    if(ubo == nullptr) // Uniform belongs to shared UniformBlock, can't overwrite
+    {
+      return false;
+    }
+
+    WriteUniform(*ubo, *uniformInfo, data);
     return true;
   }
   return false;
@@ -932,6 +973,7 @@ void Renderer::FillUniformBuffer(Program&
                                              uniform.uniformNameHash,
                                              uniform.uniformNameHashNoArray,
                                              uniformInfo);
+
       if(!uniformFound)
       {
         continue;
@@ -961,11 +1003,16 @@ void Renderer::WriteDynUniform(
   const std::vector<std::unique_ptr<Render::UniformBufferView>>& uboViews,
   BufferIndex                                                    updateBufferIndex)
 {
-  const auto         typeSize   = propertyValue->GetValueSize();
-  UniformBufferView* ubo        = uboViews[uniform.uniformBlockIndex].get();
-  int                arrayIndex = uniform.arrayIndex;
-  auto               dst        = ubo->GetOffset() + uniform.uniformOffset;
-  const auto         dest       = dst + uniform.arrayElementStride * arrayIndex;
+  UniformBufferView* ubo = uboViews[uniform.uniformBlockIndex].get();
+
+  if(ubo == nullptr) // Uniform belongs to shared UniformBlock, can't overwrite
+  {
+    return;
+  }
+
+  int        arrayIndex = uniform.arrayIndex;
+  auto       dst        = ubo->GetOffset() + uniform.uniformOffset;
+  const auto dest       = dst + uniform.arrayElementStride * arrayIndex;
 
   const auto valueAddress = propertyValue->GetValueAddress(updateBufferIndex);
 
@@ -984,6 +1031,7 @@ void Renderer::WriteDynUniform(
   }
   else
   {
+    const auto typeSize = propertyValue->GetValueSize();
     ubo->Write(valueAddress, typeSize, dest);
   }
 }
index d8da43a0b14688a1f0b1f8404ae01eafe8ff61b5..ce572b6b4ed5f91bcfd9e2013085afe7282ba34b 100644 (file)
@@ -45,6 +45,7 @@ namespace Internal
 {
 class Texture;
 class ProgramCache;
+class SharedUniformBufferViewContainer;
 
 namespace SceneGraph
 {
@@ -186,11 +187,13 @@ public:
    * @param[in] programCache Cache of program objects
    * @param[in] uniformBufferManager Uniform buffer manager
    * @param[in] pipelineCache Cache of pipelines
+   * @param[in] sharedUniformBufferViewContainer Container of shared uniform buffer views
    */
-  void Initialize(Graphics::Controller&         graphicsController,
-                  ProgramCache&                 programCache,
-                  Render::UniformBufferManager& uniformBufferManager,
-                  Render::PipelineCache&        pipelineCache);
+  void Initialize(Graphics::Controller&             graphicsController,
+                  ProgramCache&                     programCache,
+                  Render::UniformBufferManager&     uniformBufferManager,
+                  Render::PipelineCache&            pipelineCache,
+                  SharedUniformBufferViewContainer& sharedUniformBufferViewContainer);
 
   /**
    * Destructor
@@ -663,6 +666,7 @@ private:
   ProgramCache* mProgramCache{nullptr};
 
   Render::UniformBufferManager*               mUniformBufferManager{};
+  SharedUniformBufferViewContainer*           mSharedUniformBufferViewContainer{};
   std::vector<Graphics::UniformBufferBinding> mUniformBufferBindings{};
 
   Render::PipelineCache* mPipelineCache{nullptr};
diff --git a/dali/internal/render/renderers/render-uniform-block.cpp b/dali/internal/render/renderers/render-uniform-block.cpp
new file mode 100644 (file)
index 0000000..ba94770
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2025 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/render-uniform-block.h>
+
+// EXTERNAL HEADER
+#include <dali/devel-api/common/hash.h>
+#include <dali/integration-api/debug.h>
+
+// INTERNAL HEADER
+#include <dali/internal/render/renderers/uniform-buffer-view.h>
+#include <dali/internal/render/shaders/program.h>
+
+namespace Dali::Internal::Render
+{
+UniformBlock::~UniformBlock() = default;
+
+void UniformBlock::WriteUniforms(BufferIndex renderBufferIndex, const Program& program, Render::UniformBufferView& ubo)
+{
+  if(mUpdateMaps)
+  {
+    auto& uniformMap = GetUniformMap();
+
+    const uint32_t mapCount = uniformMap.Count();
+    mUniformIndexMap.clear();
+    mUniformIndexMap.resize(mapCount);
+
+    // Copy uniform map into mUniformIndexMap
+    uint32_t mapIndex = 0;
+    for(; mapIndex < mapCount; ++mapIndex)
+    {
+      mUniformIndexMap[mapIndex].propertyValue          = uniformMap[mapIndex].propertyPtr;
+      mUniformIndexMap[mapIndex].uniformName            = uniformMap[mapIndex].uniformName;
+      mUniformIndexMap[mapIndex].uniformNameHash        = uniformMap[mapIndex].uniformNameHash;
+      mUniformIndexMap[mapIndex].uniformNameHashNoArray = uniformMap[mapIndex].uniformNameHashNoArray;
+      mUniformIndexMap[mapIndex].arrayIndex             = uniformMap[mapIndex].arrayIndex;
+    }
+
+    mUpdateMaps = false;
+  }
+
+  for(auto& iter : mUniformIndexMap)
+  {
+    auto& uniform = iter;
+
+    if(!uniform.initialized)
+    {
+      auto uniformInfo  = Graphics::UniformInfo{};
+      auto uniformFound = program.GetUniform(uniform.uniformName.GetStringView(),
+                                             uniform.uniformNameHash,
+                                             uniform.uniformNameHashNoArray,
+                                             uniformInfo);
+
+      if(!uniformFound)
+      {
+        continue;
+      }
+
+      uniform.uniformOffset     = uniformInfo.offset;
+      uniform.uniformLocation   = int16_t(uniformInfo.location);
+      uniform.uniformBlockIndex = uniformInfo.bufferIndex;
+      uniform.initialized       = true;
+
+      const auto typeSize        = iter.propertyValue->GetValueSize();
+      uniform.arrayElementStride = uniformInfo.elementCount > 0 ? (uniformInfo.elementStride ? uniformInfo.elementStride : typeSize) : typeSize;
+      uniform.matrixStride       = uniformInfo.matrixStride;
+
+      WriteDynUniform(iter.propertyValue, uniform, ubo, renderBufferIndex);
+    }
+    else
+    {
+      WriteDynUniform(iter.propertyValue, uniform, ubo, renderBufferIndex);
+    }
+  }
+}
+
+void UniformBlock::OnMappingChanged()
+{
+  mUpdateMaps = true;
+}
+
+void UniformBlock::WriteDynUniform(
+  const PropertyInputImpl* propertyValue,
+  UniformIndexMap&         uniform,
+  UniformBufferView&       ubo,
+  BufferIndex              renderBufferIndex)
+{
+  int        arrayIndex = uniform.arrayIndex;
+  auto       dst        = ubo.GetOffset() + uniform.uniformOffset;
+  const auto dest       = dst + uniform.arrayElementStride * arrayIndex;
+
+  const auto valueAddress = propertyValue->GetValueAddress(renderBufferIndex);
+
+  if((propertyValue->GetType() == Property::MATRIX3 || propertyValue->GetType() == Property::VECTOR4) &&
+     uniform.matrixStride != uint32_t(-1) &&
+     uniform.matrixStride > 0)
+  {
+    // If the property is Vector4 type and matrixStride is valid integer, then we should treat it as mat2 type uniforms.
+    const uint32_t matrixRow = (propertyValue->GetType() == Property::MATRIX3) ? 3 : 2;
+    for(uint32_t i = 0; i < matrixRow; ++i)
+    {
+      ubo.Write(reinterpret_cast<const float*>(valueAddress) + i * matrixRow,
+                sizeof(float) * matrixRow,
+                dest + (i * uniform.matrixStride));
+    }
+  }
+  else
+  {
+    const auto typeSize = propertyValue->GetValueSize();
+    ubo.Write(valueAddress, typeSize, dest);
+  }
+}
+
+} // namespace Dali::Internal::Render
diff --git a/dali/internal/render/renderers/render-uniform-block.h b/dali/internal/render/renderers/render-uniform-block.h
new file mode 100644 (file)
index 0000000..11c1d68
--- /dev/null
@@ -0,0 +1,104 @@
+#ifndef DALI_INTERNAL_RENDER_RENDER_UNIFORM_BLOCK_H
+#define DALI_INTERNAL_RENDER_RENDER_UNIFORM_BLOCK_H
+
+/*
+ * Copyright (c) 2025 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/devel-api/common/hash.h>
+#include <dali/internal/update/common/property-owner.h>
+
+namespace Dali::Internal
+{
+class Program;
+namespace Render
+{
+class UniformBufferView;
+/**
+ * This property owner enables registration of properties as uniforms,
+ * specifically within a named uniform block of a shader program.
+ *
+ * The named uniform block can be connected to any number of shaders,
+ * and if the name matches the program reflection, then a singular
+ * area of memory is reserved in the current frame's uniform buffer
+ * for this block, and only properties registered with this uniform
+ * block are written to that memory area.
+ */
+class UniformBlock : public SceneGraph::PropertyOwner
+{
+public:
+  UniformBlock(std::string&& blockName)
+  : mName(std::move(blockName)),
+    mNameHash(CalculateHash(std::string_view(mName))),
+    mUniformIndexMap(),
+    mUpdateMaps(true)
+  {
+  }
+  ~UniformBlock() override;
+
+  const std::string& GetName() const
+  {
+    return mName;
+  }
+
+  size_t GetHash() const
+  {
+    return mNameHash;
+  }
+
+  void WriteUniforms(BufferIndex renderBufferIndex, const Program& program, UniformBufferView& ubo);
+
+protected: // From SceneGraph::PropertyOwner
+  void OnMappingChanged() override;
+
+private:
+  // Copy of Render::Renderer::UniformIndexMap;
+  using Hash = std::size_t;
+  struct UniformIndexMap
+  {
+    ConstString              uniformName;            ///< The uniform name
+    const PropertyInputImpl* propertyValue{nullptr}; ///< The property value
+    Hash                     uniformNameHash{0u};
+    Hash                     uniformNameHashNoArray{0u};
+    int32_t                  arrayIndex{-1};         ///< The array index
+    uint32_t                 arrayElementStride{0u}; ///< The stride for element of an array (0 - tightly packed)
+    uint32_t                 matrixStride{0u};       ///< The stride for a matrix row
+
+    int16_t  uniformLocation{0u};
+    uint16_t uniformOffset{0u};
+    uint16_t uniformBlockIndex{0u};
+    bool     initialized{false};
+  };
+
+  void WriteDynUniform(const PropertyInputImpl* propertyValue,
+                       UniformIndexMap&         uniform,
+                       UniformBufferView&       ubo,
+                       BufferIndex              renderBufferIndex);
+
+private:
+  std::string mName;
+  const Hash  mNameHash{0u};
+
+  using UniformIndexMappings = std::vector<UniformIndexMap>;
+  UniformIndexMappings mUniformIndexMap;
+
+  bool mUpdateMaps : 1;
+};
+} // namespace Render
+
+} // namespace Dali::Internal
+
+#endif //DALI_INTERNAL_RENDER_RENDER_UNIFORM_BLOCK_H
index 830421a4a0243dad49e3921ff5c45905ceaf7bc0..8ea3d814b32f12c626377a129ad0043f379eaa1a 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2025 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.
@@ -52,7 +52,7 @@ void ProgramController::ResetUsedFlag()
   mClearCacheIterator = mProgramCache.Begin();
 }
 
-bool ProgramController::ClearUnusedCacheIncrementally(bool fullCollect)
+bool ProgramController::ClearUnusedCacheIncrementally(bool fullCollect, bool forceClearAll)
 {
   if(mProgramCacheAdded)
   {
@@ -68,7 +68,7 @@ bool ProgramController::ClearUnusedCacheIncrementally(bool fullCollect)
   // TODO : Could we check running time here, instead of check counter?
   for(; mClearCacheIterator != mProgramCache.End() && (fullCollect || ++checkedCount <= MAXIMUM_COLLECTING_ITEM_COUNTS_PER_GC_CALL);)
   {
-    if(!((*mClearCacheIterator)->IsUsed()))
+    if(forceClearAll || !((*mClearCacheIterator)->IsUsed()))
     {
       mClearCacheIterator = mProgramCache.Erase(mClearCacheIterator);
     }
index ed21fdc079474644077aa1816cb724addd47f514..d1f1cd30d69b4cb3c08905c4998ae54b3c165d84 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_INTERNAL_PROGRAM_CONTROLLER_H
 
 /*
- * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2025 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.
@@ -130,9 +130,18 @@ public: // API
    * @brief Clear program incrementally who are not be used.
    *
    * @param[in] fullCollect True if we want to clear whole items.
+   * @param[in] forceClearAll True if we want to clear all items forcibly. (Default as false)
    * @return True if we need to iterate more item to check used count. False if we clear cache completely.
    */
-  bool ClearUnusedCacheIncrementally(bool fullCollect);
+  bool ClearUnusedCacheIncrementally(bool fullCollect, bool forceClearAll = false);
+
+  /**
+   * @brief Clear all program cache.
+   */
+  void ClearCache()
+  {
+    ClearUnusedCacheIncrementally(true, true);
+  }
 
   /**
    * @brief Get the number of cached program
index d4e271196d5bcf25732614748354bb68f2cd5bce..b97caac4f729e9f90c7e1381eaffe7168430abb3 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2025 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.
@@ -30,7 +30,9 @@
 #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/render-uniform-block.h>
 #include <dali/internal/render/renderers/uniform-buffer-manager.h>
+#include <dali/internal/render/renderers/uniform-buffer-view.h>
 #include <dali/internal/render/shaders/program-cache.h>
 #include <dali/public-api/common/constants.h>
 #include <dali/public-api/common/dali-common.h>
@@ -75,9 +77,9 @@ inline uint32_t AlignSize(uint32_t dataSize, uint32_t alignSize)
 
 // IMPLEMENTATION
 
-Program* Program::New(ProgramCache& cache, const Internal::ShaderDataPtr& shaderData, Graphics::Controller& gfxController)
+Program* Program::New(ProgramCache& cache, const Internal::ShaderDataPtr& shaderData, std::size_t sharedUniformNamesHash, Graphics::Controller& gfxController)
 {
-  size_t shaderHash = shaderData->GetHashValue();
+  size_t shaderHash = shaderData->GetHashValue() ^ sharedUniformNamesHash;
 
   Program* program = cache.GetProgram(shaderHash);
 
@@ -116,7 +118,10 @@ Program::~Program()
   // If someone call AddObserver / RemoveObserver after this, assert.
 }
 
-void Program::BuildReflection(const Graphics::Reflection& graphicsReflection, Render::UniformBufferManager& uniformBufferManager)
+void Program::BuildRequirements(
+  const Graphics::Reflection&                      graphicsReflection,
+  Render::UniformBufferManager&                    uniformBufferManager,
+  const SceneGraph::Shader::UniformBlockContainer& sharedUniformBlockContainer)
 {
   mReflectionDefaultUniforms.clear();
   mReflectionDefaultUniforms.resize(NUMBER_OF_DEFAULT_UNIFORMS);
@@ -191,10 +196,13 @@ void Program::BuildReflection(const Graphics::Reflection& graphicsReflection, Re
 
   mUniformBlockMemoryRequirements.blockSize.resize(uniformBlockCount);
   mUniformBlockMemoryRequirements.blockSizeAligned.resize(uniformBlockCount);
-  mUniformBlockMemoryRequirements.blockCount           = uniformBlockCount;
-  mUniformBlockMemoryRequirements.totalSizeRequired    = 0u;
-  mUniformBlockMemoryRequirements.totalCpuSizeRequired = 0u;
-  mUniformBlockMemoryRequirements.totalGpuSizeRequired = 0u;
+  mUniformBlockMemoryRequirements.sharedBlock.resize(uniformBlockCount);
+
+  mUniformBlockMemoryRequirements.blockCount            = uniformBlockCount;
+  mUniformBlockMemoryRequirements.totalSizeRequired     = 0u;
+  mUniformBlockMemoryRequirements.totalCpuSizeRequired  = 0u;
+  mUniformBlockMemoryRequirements.totalGpuSizeRequired  = 0u;
+  mUniformBlockMemoryRequirements.sharedGpuSizeRequired = 0u;
 
   for(auto i = 0u; i < uniformBlockCount; ++i)
   {
@@ -209,16 +217,41 @@ void Program::BuildReflection(const Graphics::Reflection& graphicsReflection, Re
     mUniformBlockMemoryRequirements.blockSize[i]        = blockSize;
     mUniformBlockMemoryRequirements.blockSizeAligned[i] = alignedBlockSize;
 
-    mUniformBlockMemoryRequirements.totalSizeRequired += alignedBlockSize;
-    mUniformBlockMemoryRequirements.totalCpuSizeRequired += (standaloneUniformBlock) ? alignedBlockSize : 0;
-    mUniformBlockMemoryRequirements.totalGpuSizeRequired += (standaloneUniformBlock) ? 0 : alignedBlockSize;
+    bool sharedUniformUsed = false;
+
+    // Compare and find block name in sharedUniformBlockContainer
+    if(!standaloneUniformBlock)
+    {
+      auto uboNameHash = Dali::CalculateHash(std::string_view(uboInfo.name));
+      auto iter        = sharedUniformBlockContainer.find(uboNameHash);
+      if(iter != sharedUniformBlockContainer.end())
+      {
+        mUniformBlockMemoryRequirements.sharedBlock[i] = iter->second;
+        mUniformBlockMemoryRequirements.totalSizeRequired     += alignedBlockSize;
+        mUniformBlockMemoryRequirements.sharedGpuSizeRequired += alignedBlockSize;
+
+        sharedUniformUsed = true;
+      }
+    }
+
+    if(!sharedUniformUsed)
+    {
+      mUniformBlockMemoryRequirements.sharedBlock[i] = nullptr;
+
+      mUniformBlockMemoryRequirements.totalSizeRequired += alignedBlockSize;
+      mUniformBlockMemoryRequirements.totalCpuSizeRequired += (standaloneUniformBlock) ? alignedBlockSize : 0;
+      mUniformBlockMemoryRequirements.totalGpuSizeRequired += (standaloneUniformBlock) ? 0 : alignedBlockSize;
+    }
   }
 }
 
-void Program::SetGraphicsProgram(Graphics::UniquePtr<Graphics::Program>&& program, Render::UniformBufferManager& uniformBufferManager)
+void Program::SetGraphicsProgram(
+  Graphics::UniquePtr<Graphics::Program>&&         program,
+  Render::UniformBufferManager&                    uniformBufferManager,
+  const SceneGraph::Shader::UniformBlockContainer& uniformBlockContainer)
 {
   mGfxProgram = std::move(program);
-  BuildReflection(mGfxController.GetProgramReflection(*mGfxProgram.get()), uniformBufferManager);
+  BuildRequirements(mGfxController.GetProgramReflection(*mGfxProgram.get()), uniformBufferManager, uniformBlockContainer);
 }
 
 bool Program::GetUniform(const std::string_view& name, Hash hashedName, Hash hashedNameNoArray, Graphics::UniformInfo& out) const
index f738435277c29a733ff3e5ad41fd0036ecc27699..50219cd6b7a71ff178a5db7ec25b8a6e41ff1a6e 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_INTERNAL_PROGRAM_H
 
 /*
- * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2025 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.
@@ -20,6 +20,7 @@
 
 // EXTERNAL INCLUDES
 #include <cstdint> // int32_t, uint32_t
+#include <memory>
 #include <string>
 #include <unordered_map>
 
@@ -29,6 +30,8 @@
 #include <dali/public-api/common/vector-wrapper.h>
 #include <dali/public-api/object/ref-object.h>
 
+#include <dali/internal/render/shaders/render-shader.h>
+
 namespace Dali
 {
 namespace Graphics
@@ -46,7 +49,9 @@ class ProgramCache;
 
 namespace Render
 {
+class UniformBlock;
 class UniformBufferManager;
+class UniformBufferView;
 } // namespace Render
 
 /**
@@ -101,10 +106,11 @@ public:
    * @param[in] shaderData  A pointer to a data structure containing the program source
    *                        and optionally precompiled binary. If the binary is empty the program bytecode
    *                        is copied into it after compilation and linking)
-   * @param[in]  gfxController Reference to valid graphics Controller object
+   * @param[in] sharedUniformNamesHash Hash value for list of shared uniform buffers.
+   * @param[in] gfxController Reference to valid graphics Controller object
    * @return pointer to the program
    */
-  static Program* New(ProgramCache& cache, const Internal::ShaderDataPtr& shaderData, Graphics::Controller& gfxController);
+  static Program* New(ProgramCache& cache, const Internal::ShaderDataPtr& shaderData, std::size_t sharedUniformNamesHash, Graphics::Controller& gfxController);
 
   Internal::ShaderDataPtr GetShaderData()
   {
@@ -124,7 +130,7 @@ public:
   /**
    * Setup the actual program, and ensure that it's reflection is generated.
    */
-  void SetGraphicsProgram(Graphics::UniquePtr<Graphics::Program>&& program, Render::UniformBufferManager& uniformBufferManager);
+  void SetGraphicsProgram(Graphics::UniquePtr<Graphics::Program>&& program, Render::UniformBufferManager& uniformBufferManager, const SceneGraph::Shader::UniformBlockContainer& sharedUniformBlockContainer);
 
   /**
    * Retrieves uniform data.
@@ -221,22 +227,27 @@ public:
    * Build optimized shader reflection of uniforms
    * @param graphicsReflection The graphics reflection
    */
-  void BuildReflection(const Graphics::Reflection& graphicsReflection, Render::UniformBufferManager& uniformBufferManager);
+  void BuildRequirements(const Graphics::Reflection& graphicsReflection, Render::UniformBufferManager& uniformBufferManager, const SceneGraph::Shader::UniformBlockContainer& sharedUniformBlockContainer);
 
   /**
    * Struct UniformBlockMemoryRequirements
-   * Contains details of a uniform blocks memory requirements
+   * Contains details of the memory requirements for a RenderItem
    */
   struct UniformBlockMemoryRequirements
   {
     uint32_t blockCount{0u};
+
+    // Ignores explictly allocated blocks
     uint32_t totalSizeRequired{0u};
     uint32_t totalCpuSizeRequired{0u}; ///< requirements for CPU memory
-    uint32_t totalGpuSizeRequired{0u}; ///< requirements of hardware buffer
+    uint32_t totalGpuSizeRequired{0u}; ///< requirements of hardware buffer for RenderItem
+
+    uint32_t sharedGpuSizeRequired{0u}; ///< requirements of explicitly allocated blocks
 
     // Per block
-    std::vector<uint32_t> blockSize{};
-    std::vector<uint32_t> blockSizeAligned{};
+    std::vector<uint32_t>              blockSize{};
+    std::vector<uint32_t>              blockSizeAligned{};
+    std::vector<Render::UniformBlock*> sharedBlock{};
   };
   /**
    * Retrieves uniform blocks requirements
index 84da446c16e58d1d0cf7ce9f24ff7942752e72c5..a602a33fd244be0f7d7a401e8dbbbf2cd3d6c700 100644 (file)
@@ -23,6 +23,7 @@
 #include <dali/internal/common/image-sampler.h>
 #include <dali/internal/render/common/render-debug.h>
 #include <dali/internal/render/queue/render-queue.h>
+#include <dali/internal/render/renderers/render-uniform-block.h>
 #include <dali/internal/render/shaders/program.h>
 
 namespace Dali
@@ -76,6 +77,42 @@ const ShaderDataPtr& Shader::GetShaderData(uint32_t renderPassTag) const
   return mDefaultShaderData;
 }
 
+void Shader::ConnectUniformBlock(Render::UniformBlock* uniformBlock)
+{
+  if(uniformBlock != nullptr)
+  {
+    // We may end up with: hashmap per shader, whereas, we could just store hash per ub
+    // and relatively quickly search vec of hashes, which will take less space if ubs
+    // are shared between shaders.
+
+    auto uniformBlockNameHash = uniformBlock->GetHash();
+    DALI_ASSERT_DEBUG(mBlocks.find(mBlockNamesHash) == mBlocks.end() && "Duplicated name of uniform connected!");
+    mBlockNamesHash ^= uniformBlockNameHash;
+    mBlocks.insert(std::make_pair(uniformBlockNameHash, uniformBlock));
+  }
+}
+
+void Shader::DisconnectUniformBlock(Render::UniformBlock* uniformBlock)
+{
+  if(uniformBlock != nullptr)
+  {
+    auto uniformBlockNameHash = uniformBlock->GetHash();
+    DALI_ASSERT_DEBUG(mBlocks.find(mBlockNamesHash) != mBlocks.end() && "Unconnected uniform disconnect!");
+    mBlockNamesHash ^= uniformBlockNameHash;
+    mBlocks.erase(uniformBlockNameHash);
+  }
+}
+
+const Shader::UniformBlockContainer& Shader::GetConnectedUniformBlocks() const
+{
+  return mBlocks;
+}
+
+std::size_t Shader::GetSharedUniformNamesHash() const
+{
+  return mBlockNamesHash;
+}
+
 } // namespace SceneGraph
 
 } // namespace Internal
index e33103c653351d2bbb05d09847b5de4a16a55cce..bdaae2bb8a02bd6417167ff562b51da76463d03c 100644 (file)
@@ -18,6 +18,9 @@
  *
  */
 
+// EXTERNAL INCLUDES
+#include <unordered_map>
+
 // INTERNAL INCLUDES
 #include <dali/internal/common/shader-data.h>
 #include <dali/internal/event/common/event-thread-services.h>
@@ -29,6 +32,11 @@ namespace Internal
 {
 class Program;
 
+namespace Render
+{
+class UniformBlock;
+}
+
 namespace SceneGraph
 {
 class SceneController;
@@ -49,6 +57,8 @@ class SceneController;
 class Shader : public PropertyOwner
 {
 public:
+  using UniformBlockContainer = std::unordered_map<size_t, Render::UniformBlock*>;
+
   Shader() = default;
 
   /**
@@ -68,9 +78,35 @@ public:
    */
   [[nodiscard]] const ShaderDataPtr& GetShaderData(uint32_t renderPassTag) const;
 
+  /**
+   * @brief Connect uniform blocks.
+   * @param[in] uniformBlock Uniform block to be connected.
+   */
+  void ConnectUniformBlock(Render::UniformBlock* uniformBlock);
+
+  /**
+   * @brief Connect uniform blocks.
+   * @param[in] uniformBlock Uniform block to be disconnected.
+   */
+  void DisconnectUniformBlock(Render::UniformBlock* uniformBlock);
+
+  /**
+   * @brief Get the set of connected uniform blocks.
+   * @return The set of connected uniform blocks.
+   */
+  const UniformBlockContainer& GetConnectedUniformBlocks() const;
+
+  /**
+   * @brief Get the hash value of connected uniform blocks name.
+   * @return Hash value of all connected uniform blocks name.
+   */
+  std::size_t GetSharedUniformNamesHash() const;
+
 private: // Data
   ShaderDataPtr              mDefaultShaderData{nullptr};
   std::vector<ShaderDataPtr> mShaderDataList{};
+  UniformBlockContainer      mBlocks{};           ///< List of connected uniform blocks (not owned)
+  std::size_t                mBlockNamesHash{0u}; ///< Simple hash of all connected uniform blocks name.
 };
 
 inline void UpdateShaderDataMessage(EventThreadServices& eventThreadServices, const Shader& shader, ShaderDataPtr shaderData)
index 4aa3d794522f79327a2af35901eceb237fd7ec5d..01242942add2dfa6347d3f70c9e57385e37529c0 100644 (file)
@@ -52,6 +52,7 @@
 
 #include <dali/internal/render/common/render-manager.h>
 #include <dali/internal/render/queue/render-queue.h>
+#include <dali/internal/render/renderers/render-uniform-block.h>
 #include <dali/internal/render/renderers/render-vertex-buffer.h>
 
 // Un-comment to enable node tree debug logging
@@ -113,6 +114,7 @@ enum ContainerRemovedFlagBits
   ANIMATION             = 0x10,
   PROPERTY_NOTIFICATION = 0x20,
   CUSTOM_OBJECT         = 0x40,
+  UNIFORM_BLOCKS        = 0x80,
 };
 
 /**
@@ -217,6 +219,7 @@ struct UpdateManager::Impl
     renderers(),
     textureSets(),
     shaders(),
+    uniformBlocks(),
     panGestureProcessor(nullptr),
     messageQueue(renderController, sceneGraphBuffers),
     frameCallbackProcessor(nullptr),
@@ -280,6 +283,7 @@ struct UpdateManager::Impl
     // Ensure to clear renderers
     renderers.Clear();
     shaders.Clear();
+    uniformBlocks.Clear();
   }
 
   /**
@@ -369,11 +373,13 @@ struct UpdateManager::Impl
   OwnerKeyContainer<Renderer>           renderers;             ///< A container of owned renderers
   OwnerContainer<TextureSet*>           textureSets;           ///< A container of owned texture sets
   OwnerContainer<Shader*>               shaders;               ///< A container of owned shaders
+  OwnerContainer<Render::UniformBlock*> uniformBlocks;         ///< A container of owned uniformBlocks
 
-  DiscardQueue<Node*, OwnerContainer<Node*>>                         nodeDiscardQueue; ///< Nodes are added here when disconnected from the scene-graph.
-  DiscardQueue<Shader*, OwnerContainer<Shader*>>                     shaderDiscardQueue;
-  DiscardQueue<MemoryPoolKey<Renderer>, OwnerKeyContainer<Renderer>> rendererDiscardQueue;
-  DiscardQueue<Scene*, OwnerContainer<Scene*>>                       sceneDiscardQueue;
+  DiscardQueue<Node*, OwnerContainer<Node*>>                                 nodeDiscardQueue; ///< Nodes are added here when disconnected from the scene-graph.
+  DiscardQueue<Shader*, OwnerContainer<Shader*>>                             shaderDiscardQueue;
+  DiscardQueue<Render::UniformBlock*, OwnerContainer<Render::UniformBlock*>> uniformBlockDiscardQueue;
+  DiscardQueue<MemoryPoolKey<Renderer>, OwnerKeyContainer<Renderer>>         rendererDiscardQueue;
+  DiscardQueue<Scene*, OwnerContainer<Scene*>>                               sceneDiscardQueue;
 
   CompleteNotificationInterface::ParameterList notifyRequiredAnimations; ///< A temperal container of complete notify required animations, like animation finished, stopped, or loop completed.
 
@@ -874,6 +880,19 @@ void UpdateManager::RemoveTextureSet(TextureSet* textureSet)
 #endif
 }
 
+void UpdateManager::AddUniformBlock(OwnerPointer<Render::UniformBlock>& uniformBlock)
+{
+  mImpl->uniformBlocks.PushBack(uniformBlock.Release());
+}
+
+void UpdateManager::RemoveUniformBlock(Render::UniformBlock* uniformBlock)
+{
+  EraseUsingDiscardQueue(mImpl->uniformBlocks, uniformBlock, mImpl->uniformBlockDiscardQueue, mSceneGraphBuffers.GetUpdateBufferIndex());
+#if defined(LOW_SPEC_MEMORY_MANAGEMENT_ENABLED)
+  mImpl->containerRemovedFlags |= ContainerRemovedFlagBits::UNIFORM_BLOCKS;
+#endif
+}
+
 uint32_t* UpdateManager::ReserveMessageSlot(uint32_t size, bool updateScene)
 {
   return mImpl->messageQueue.ReserveMessageSlot(size, updateScene);
@@ -1080,6 +1099,14 @@ void UpdateManager::ConstrainShaders(PropertyOwnerContainer& postPropertyOwners,
       postPropertyOwners.PushBack(shader);
     }
   }
+  for(auto&& uniformBlock : mImpl->uniformBlocks)
+  {
+    ConstrainPropertyOwner(*uniformBlock, bufferIndex);
+    if(!uniformBlock->GetPostConstraints().Empty())
+    {
+      postPropertyOwners.PushBack(uniformBlock);
+    }
+  }
 }
 
 void UpdateManager::ProcessPropertyNotifications(BufferIndex bufferIndex)
@@ -1271,7 +1298,7 @@ uint32_t UpdateManager::Update(float    elapsedSeconds,
 
     // Apply constraints to RenderTasks, shaders
     ConstrainRenderTasks(postPropertyOwners, bufferIndex);
-    ConstrainShaders(postPropertyOwners, bufferIndex);
+    ConstrainShaders(postPropertyOwners, bufferIndex); // shaders & uniform blocks
 
     // Update renderers and apply constraints
     UpdateRenderers(postPropertyOwners, bufferIndex);
@@ -1437,6 +1464,10 @@ uint32_t UpdateManager::Update(float    elapsedSeconds,
   {
     mImpl->customObjects.ShrinkToFitIfNeeded();
   }
+  if(mImpl->containerRemovedFlags & ContainerRemovedFlagBits::UNIFORM_BLOCKS)
+  {
+    mImpl->uniformBlocks.ShrinkToFitIfNeeded();
+  }
 
   // Reset flag
   mImpl->containerRemovedFlags = ContainerRemovedFlagBits::NOTHING;
@@ -1544,6 +1575,18 @@ void UpdateManager::RequestRendering()
   mImpl->renderingRequired = true;
 }
 
+void UpdateManager::RequestClearProgramCache()
+{
+  // Message has ownership of format while in transit from update -> render
+  using DerivedType = Message<RenderManager>;
+
+  // Reserve some memory inside the render queue
+  uint32_t* slot = mImpl->renderQueue.ReserveMessageSlot(mSceneGraphBuffers.GetUpdateBufferIndex(), sizeof(DerivedType));
+
+  // Construct message in the render queue memory; note that delete should not be called on the return value
+  new(slot) DerivedType(&mImpl->renderManager, &RenderManager::ClearProgramCache);
+}
+
 Node* UpdateManager::GetNodePointerById(uint32_t nodeId) const
 {
   Node* foundNodePointer = nullptr;
index c977e00619c71dde2d3278a7ac9e0273c9e40b73..91941a30aeed9c6a4e4d02f53b5e72c86397984d 100644 (file)
@@ -29,8 +29,9 @@
 #include <dali/internal/common/type-abstraction-enums.h>
 #include <dali/internal/event/common/event-thread-services.h>
 #include <dali/internal/event/rendering/texture-impl.h>
-#include <dali/internal/render/renderers/render-texture-key.h> // For RenderTextureKey
-#include <dali/internal/render/renderers/render-texture.h>     // For OwnerPointer<Render::Texture>
+#include <dali/internal/render/renderers/render-texture-key.h>   // For RenderTextureKey
+#include <dali/internal/render/renderers/render-texture.h>       // For OwnerPointer<Render::Texture>
+#include <dali/internal/render/renderers/render-uniform-block.h> // for OwnerPointer<Render::UniformBlock>
 #include <dali/internal/render/renderers/render-vertex-buffer.h>
 #include <dali/internal/render/shaders/render-shader.h> // for OwnerPointer< Shader >
 #include <dali/internal/update/animation/scene-graph-animation.h>
@@ -392,6 +393,20 @@ public:
    */
   void RemoveTextureSet(TextureSet* textureSet);
 
+  // UniformBlock
+
+  /**
+   * Add uniform block.
+   * @param[in] uniformBlock The uniform block.
+   */
+  void AddUniformBlock(OwnerPointer<Render::UniformBlock>& uniformBlock);
+
+  /**
+   * Remove uniform block.
+   * @param[in] uniformBlock The uniform block.
+   */
+  void RemoveUniformBlock(Render::UniformBlock* uniformBlock);
+
   // Render tasks
 
   /**
@@ -707,6 +722,11 @@ public:
    */
   void RequestRendering();
 
+  /**
+   * Request to clear the program cache at RenderManager.
+   */
+  void RequestClearProgramCache();
+
   /**
    * @brief Get the active Node pointer by node id.
    *
@@ -1206,6 +1226,17 @@ inline void RequestRenderingMessage(UpdateManager& manager)
   new(slot) LocalType(&manager, &UpdateManager::RequestRendering);
 }
 
+inline void RequestClearProgramCacheMessage(UpdateManager& manager)
+{
+  using LocalType = Message<UpdateManager>;
+
+  // Reserve some memory inside the message queue
+  uint32_t* slot = manager.ReserveMessageSlot(sizeof(LocalType));
+
+  // Construct message in the message queue memory; note that delete should not be called on the return value
+  new(slot) LocalType(&manager, &UpdateManager::RequestClearProgramCache);
+}
+
 /**
  * Create a message for setting the depth of a layer
  * @param[in] manager The update manager
@@ -1728,6 +1759,20 @@ inline void NotifyFrameCallbackMessage(UpdateManager& manager, FrameCallbackInte
   new(slot) LocalType(&manager, &UpdateManager::NotifyFrameCallback, frameCallback, syncPoint);
 }
 
+inline void AddUniformBlockMessage(UpdateManager& manager, OwnerPointer<Render::UniformBlock>& uniformBlock)
+{
+  using LocalType = MessageValue1<UpdateManager, OwnerPointer<Render::UniformBlock>>;
+  uint32_t* slot  = manager.ReserveMessageSlot(sizeof(LocalType));
+  new(slot) LocalType(&manager, &UpdateManager::AddUniformBlock, uniformBlock);
+}
+
+inline void RemoveUniformBlockMessage(UpdateManager& manager, Render::UniformBlock& uniformBlock)
+{
+  using LocalType = MessageValue1<UpdateManager, Render::UniformBlock*>;
+  uint32_t* slot  = manager.ReserveMessageSlot(sizeof(LocalType));
+  new(slot) LocalType(&manager, &UpdateManager::RemoveUniformBlock, &uniformBlock);
+}
+
 } // namespace SceneGraph
 
 } // namespace Internal
index eef4926ebca64cfdae16a553a925181ed0b072ac..474f20555d0269aaf893e16741840a0e51d6d36c 100644 (file)
@@ -25,6 +25,7 @@
 #include <dali/internal/render/data-providers/render-data-provider.h>
 #include <dali/internal/render/queue/render-queue.h>
 #include <dali/internal/render/renderers/render-geometry.h>
+#include <dali/internal/render/renderers/render-uniform-block.h>
 #include <dali/internal/render/shaders/program.h>
 #include <dali/internal/render/shaders/render-shader.h>
 #include <dali/internal/update/controllers/render-message-dispatcher.h>
index 83d5ce28dae352dd2c1e6fc613ad6e99e3383b18..9f6aae8ea29306550d10799ffc708cbfcb908db0 100644 (file)
@@ -38,6 +38,7 @@ namespace Render
 {
 class Renderer;
 class Geometry;
+class UniformBlock;
 } // namespace Render
 
 namespace SceneGraph
index a3a18a85ba5dfc0d03b112f1566040e36db4bea1..9dbafe6c7a310efd69cb9ec49d09d389e6d25176 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_CORE_H
 
 /*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2025 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/rendering/shader.h>
 #include <dali/public-api/rendering/texture-set.h>
 #include <dali/public-api/rendering/texture.h>
+#include <dali/public-api/rendering/uniform-block.h>
 #include <dali/public-api/rendering/vertex-buffer.h>
 #include <dali/public-api/rendering/visual-renderer.h>
 
index 08b1cf5f4a72716cbe6de3835eae7efa5103dc5b..8c80033483b68914fd1f455f9f678cfa5a591ed8 100644 (file)
@@ -68,13 +68,14 @@ SET( public_api_src_files
   ${public_api_src_dir}/rendering/decorated-visual-renderer.cpp
   ${public_api_src_dir}/rendering/frame-buffer.cpp
   ${public_api_src_dir}/rendering/geometry.cpp
-  ${public_api_src_dir}/rendering/vertex-buffer.cpp
-  ${public_api_src_dir}/rendering/texture.cpp
-  ${public_api_src_dir}/rendering/texture-set.cpp
   ${public_api_src_dir}/rendering/renderer.cpp
-  ${public_api_src_dir}/rendering/visual-renderer.cpp
   ${public_api_src_dir}/rendering/sampler.cpp
   ${public_api_src_dir}/rendering/shader.cpp
+  ${public_api_src_dir}/rendering/texture.cpp
+  ${public_api_src_dir}/rendering/texture-set.cpp
+  ${public_api_src_dir}/rendering/uniform-block.cpp
+  ${public_api_src_dir}/rendering/vertex-buffer.cpp
+  ${public_api_src_dir}/rendering/visual-renderer.cpp
   ${public_api_src_dir}/signals/callback.cpp
   ${public_api_src_dir}/signals/connection-tracker.cpp
   ${public_api_src_dir}/signals/connection-tracker-interface.cpp
@@ -221,13 +222,14 @@ SET( public_api_core_rendering_header_files
   ${public_api_src_dir}/rendering/decorated-visual-renderer.h
   ${public_api_src_dir}/rendering/frame-buffer.h
   ${public_api_src_dir}/rendering/geometry.h
-  ${public_api_src_dir}/rendering/vertex-buffer.h
-  ${public_api_src_dir}/rendering/texture.h
-  ${public_api_src_dir}/rendering/texture-set.h
   ${public_api_src_dir}/rendering/renderer.h
-  ${public_api_src_dir}/rendering/visual-renderer.h
   ${public_api_src_dir}/rendering/sampler.h
   ${public_api_src_dir}/rendering/shader.h
+  ${public_api_src_dir}/rendering/texture.h
+  ${public_api_src_dir}/rendering/texture-set.h
+  ${public_api_src_dir}/rendering/uniform-block.h
+  ${public_api_src_dir}/rendering/vertex-buffer.h
+  ${public_api_src_dir}/rendering/visual-renderer.h
 )
 
 
index 6e1b04e89ba0ed1fcfa3e6aa4d14820b186d345d..e3dbb85eaf53d552be397608c19881299d249435 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_DECOREATED_VISUAL_RENDERER_H
 
 /*
- * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2025 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.
@@ -185,49 +185,50 @@ public:
   DecoratedVisualRenderer& operator=(const DecoratedVisualRenderer& handle);
 
   /**
-   * @brief Register relate with corner radius uniforms so we can use it as uniform properties.
+   * @brief Move constructor.
    *
    * @SINCE_2_1.21
+   * @param[in] rhs A reference to the moved handle
    */
-  void RegisterCornerRadiusUniform();
+  DecoratedVisualRenderer(DecoratedVisualRenderer&& rhs) noexcept;
 
   /**
-   * @brief Register relate with corner squareness uniforms so we can use it as uniform properties.
+   * @brief Move assignment operator.
    *
-   * @SINCE_2_3.48
+   * @SINCE_2_1.21
+   * @param[in] rhs A reference to the moved handle
+   * @return A reference to this handle
    */
-  void RegisterCornerSquarenessUniform();
+  DecoratedVisualRenderer& operator=(DecoratedVisualRenderer&& rhs) noexcept;
 
+public:
   /**
-   * @brief Register relate with borderline uniforms so we can use it as uniform properties.
+   * @brief Register relate with corner radius uniforms so we can use it as uniform properties.
    *
    * @SINCE_2_1.21
    */
-  void RegisterBorderlineUniform();
+  void RegisterCornerRadiusUniform();
 
   /**
-   * @brief Register relate with blur radius uniforms so we can use it as uniform properties.
+   * @brief Register relate with corner squareness uniforms so we can use it as uniform properties.
    *
-   * @SINCE_2_1.21
+   * @SINCE_2_3.48
    */
-  void RegisterBlurRadiusUniform();
+  void RegisterCornerSquarenessUniform();
 
   /**
-   * @brief Move constructor.
+   * @brief Register relate with borderline uniforms so we can use it as uniform properties.
    *
    * @SINCE_2_1.21
-   * @param[in] rhs A reference to the moved handle
    */
-  DecoratedVisualRenderer(DecoratedVisualRenderer&& rhs) noexcept;
+  void RegisterBorderlineUniform();
 
   /**
-   * @brief Move assignment operator.
+   * @brief Register relate with blur radius uniforms so we can use it as uniform properties.
    *
    * @SINCE_2_1.21
-   * @param[in] rhs A reference to the moved handle
-   * @return A reference to this handle
    */
-  DecoratedVisualRenderer& operator=(DecoratedVisualRenderer&& rhs) noexcept;
+  void RegisterBlurRadiusUniform();
 
 public:
   /// @cond internal
index 9445b7e71d4890c4670ee0a41db4f4482ce51b72..77890471cc195259740b83e4b2f7063a1825d11e 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2025 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.
@@ -28,7 +28,7 @@ Shader Shader::New(std::string_view vertexShader,
                    Hint::Value      hints,
                    std::string_view shaderName)
 {
-  Internal::ShaderPtr shader = Internal::Shader::New(vertexShader, fragmentShader, hints, shaderName);
+  Internal::ShaderPtr shader = Internal::Shader::New(vertexShader, fragmentShader, hints, shaderName, {});
   return Shader(shader.Get());
 }
 
diff --git a/dali/public-api/rendering/uniform-block.cpp b/dali/public-api/rendering/uniform-block.cpp
new file mode 100644 (file)
index 0000000..25a3995
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2025 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/public-api/rendering/uniform-block.h>
+
+// INTERNAL INCLUDES
+#include <dali/internal/event/rendering/shader-impl.h>
+#include <dali/internal/event/rendering/uniform-block-impl.h>
+
+namespace Dali
+{
+UniformBlock UniformBlock::New(std::string blockName)
+{
+  Internal::UniformBlockPtr object = Internal::UniformBlock::New(std::move(blockName));
+  return UniformBlock(object.Get());
+}
+
+UniformBlock::UniformBlock() = default;
+
+UniformBlock::~UniformBlock() = default;
+
+UniformBlock::UniformBlock(const UniformBlock& handle) = default;
+
+UniformBlock& UniformBlock::operator=(const UniformBlock& handle) = default;
+
+UniformBlock::UniformBlock(UniformBlock&& rhs) noexcept = default;
+
+UniformBlock& UniformBlock::operator=(UniformBlock&& handle) noexcept = default;
+
+UniformBlock UniformBlock::DownCast(BaseHandle handle)
+{
+  return UniformBlock(dynamic_cast<Dali::Internal::UniformBlock*>(handle.GetObjectPtr()));
+}
+
+std::string_view UniformBlock::GetUniformBlockName() const
+{
+  return GetImplementation(*this).GetUniformBlockName();
+}
+
+bool UniformBlock::ConnectToShader(Shader shader)
+{
+  if(DALI_LIKELY(shader))
+  {
+    return GetImplementation(*this).ConnectToShader(&GetImplementation(shader));
+  }
+  return false;
+}
+
+void UniformBlock::DisconnectFromShader(Shader shader)
+{
+  if(DALI_LIKELY(shader))
+  {
+    GetImplementation(*this).DisconnectFromShader(&GetImplementation(shader));
+  }
+}
+
+UniformBlock::UniformBlock(Internal::UniformBlock* object)
+: Handle(object)
+{
+}
+
+} // namespace Dali
diff --git a/dali/public-api/rendering/uniform-block.h b/dali/public-api/rendering/uniform-block.h
new file mode 100644 (file)
index 0000000..6f643d4
--- /dev/null
@@ -0,0 +1,171 @@
+#ifndef DALI_RENDERING_UNIFORM_BLOCK_H
+#define DALI_RENDERING_UNIFORM_BLOCK_H
+
+/*
+ * Copyright (c) 2025 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <dali/public-api/object/handle.h>
+#include <dali/public-api/rendering/shader.h>
+
+namespace Dali
+{
+/**
+ * @addtogroup dali_core_rendering_effects
+ * @{
+ */
+
+namespace Internal DALI_INTERNAL
+{
+class UniformBlock;
+}
+
+/**
+ * Uniform block is a property owning object that can hold a number
+ * of properties mapping onto a uniform block in the shader.
+ *
+ * Mapping is done automatically through shader reflection, the structure
+ * doesn't need to be defined here. (Order of property registration is not
+ * relevant)
+ *
+ * Uniform Properties can be animated / constrained as normal.
+ *
+ * The underlying code requires the name of the uniform block to match;
+ * consequently, it's a construct only property.
+ *
+ * When the uniform block object is connected to a shader, it will be
+ * used to populate the uniforms for that shader, no matter what renderer
+ * + actor that shader is connected to. Consequently, the client does not
+ * need to declare the block's properties on the shader, renderer or actor.
+ *
+ * Furthermore, any such declared properties will not override those
+ * in the Uniform Block.
+ *
+ * @SINCE_2_4.14
+ */
+class DALI_CORE_API UniformBlock : public Handle
+{
+public:
+  /**
+   * Construct a named uniform block.
+   *
+   * @SINCE_2_4.14
+   * @param[in] blockName The block name must match a named structure in the shader
+   */
+  static UniformBlock New(std::string blockName);
+
+  /**
+   * @brief Constructor
+   *
+   * @SINCE_2_4.14
+   */
+  UniformBlock();
+
+  /**
+   * @brief Destructor
+   *
+   * @SINCE_2_4.14
+   */
+  ~UniformBlock();
+
+  /**
+   * @brief Copy Constructor
+   *
+   * @SINCE_2_4.14
+   * @param[in] handle The handle to copy
+   */
+  UniformBlock(const UniformBlock& handle);
+
+  /**
+   * @brief Assignment operator
+   *
+   * @SINCE_2_4.14
+   * @param[in] handle The handle to copy
+   */
+  UniformBlock& operator=(const UniformBlock& handle);
+
+  /**
+   * @brief Move constructor
+   *
+   * @SINCE_2_4.14
+   * @param[in] handle The handle to move
+   */
+  UniformBlock(UniformBlock&& handle) noexcept;
+
+  /**
+   * @brief Move assign
+   *
+   * @SINCE_2_4.14
+   * @param[in] handle The handle to move
+   * @return this handle
+   */
+  UniformBlock& operator=(UniformBlock&& handle) noexcept;
+
+  /**
+   * @brief Attempt to downcast from the base type
+   *
+   * @SINCE_2_4.14
+   * @param[in] handle The handle to downcast
+   * @return A UniformBlock handle to a valid resource, or empty handle if invalid.
+   */
+  static UniformBlock DownCast(BaseHandle handle);
+
+public:
+  /**
+   * @brief Retrieve the block name.
+   *
+   * @SINCE_2_4.14
+   * @return The block name.
+   */
+  std::string_view GetUniformBlockName() const;
+
+  /**
+   * @brief Weakly connect to a shader.
+   *
+   * Return false if the UniformBlock cannot be connected to the shader.
+   * (e.g. Shader is invalid, or the UniformBlock has already been connected to given shader)
+   *
+   * @SINCE_2_4.14
+   * @param[in] shader A shader to connect to. Multiple shaders can be connected.
+   * @return True if we successfully connected to the shader, false otherwise.
+   */
+  bool ConnectToShader(Shader shader);
+
+  /**
+   * @brief Disconnect from a shader.
+   *
+   * @SINCE_2_4.14
+   * @param[in] shader A shader to disconnect from.
+   */
+  void DisconnectFromShader(Shader shader);
+
+public:
+  /// @cond internal
+  /**
+   * @brief Constructor
+   * @note Not intended for application developers
+   *
+   * @param[in] object A pointer to a newly allocated UniformBlock
+   */
+  explicit DALI_INTERNAL UniformBlock(Internal::UniformBlock* object);
+  /// @endcond
+};
+
+/**
+ * @}
+ */
+} // namespace Dali
+
+#endif //DALI_RENDERING_UNIFORM_BLOCK_H
index 96db442591de46752731d55fc7a5b50f78ed1745..2d9bb0f0530ef7971cf4ea3472fe4721cd8aea0f 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2025 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.
@@ -48,6 +48,11 @@ VisualRenderer::VisualRenderer(VisualRenderer&& rhs) noexcept = default;
 
 VisualRenderer& VisualRenderer::operator=(VisualRenderer&& rhs) noexcept = default;
 
+void VisualRenderer::RegisterVisualTransformUniform()
+{
+  GetImplementation(*this).RegisterVisualTransformUniform();
+}
+
 VisualRenderer::VisualRenderer(Internal::VisualRenderer* pointer)
 : Dali::Renderer(pointer)
 {
index 6493991d737ae5e2736406b404dfdf3df8b41f82..e55e53a6bc3b151d595003d69e35f33b5215bc5d 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_VISUAL_RENDERER_H
 
 /*
- * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2025 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.
@@ -217,6 +217,14 @@ public:
    */
   VisualRenderer& operator=(VisualRenderer&& rhs) noexcept;
 
+public:
+  /**
+   * @brief Register visual transform uniforms so we can use it as uniform properties.
+   *
+   * @SINCE_2_4.14
+   */
+  void RegisterVisualTransformUniform();
+
 public:
   /// @cond internal
   /**