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
/*
-* 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.
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;
+}
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
/*
- * 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.
DecoratedVisualRenderer renderer = DecoratedVisualRenderer::New(geometry, shader);
// Add all uniform mappings
+ renderer.RegisterVisualTransformUniform();
renderer.RegisterCornerRadiusUniform();
renderer.RegisterCornerSquarenessUniform();
renderer.RegisterBorderlineUniform();
--- /dev/null
+/*
+ * 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;
+}
/*
- * 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.
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));
/*
- * 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);
#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.
/*
- * 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.
// 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);
/*
- * 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.
#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>
#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.
}
class Renderer;
+class UniformBlock;
+
using RendererPtr = IntrusivePtr<Renderer>;
/**
/*
- * 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.
#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>
} // 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();
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;
}
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()
{
#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.
#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
{
{
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()
*/
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()
--- /dev/null
+/*
+ * 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
--- /dev/null
+#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
/*
- * 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.
// 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();
}
VisualRenderer::VisualRenderer(const SceneGraph::Renderer* sceneObject)
-: Renderer(sceneObject)
+: Renderer(sceneObject),
+ mPropertyCache(),
+ mUniformMapped(false)
{
}
{
const SceneGraph::Renderer& sceneObject = GetVisualRendererSceneObject();
auto visualProperties = sceneObject.GetVisualProperties();
-
if(visualProperties)
{
BakeMessage<Vector2>(GetEventThreadServices(), *mUpdateObject, visualProperties->mTransformOffset, mPropertyCache.mTransformOffset);
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
#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.
*/
const PropertyInputImpl* GetSceneObjectInputProperty(Property::Index index) const override;
+public:
+ /**
+ * @copydoc Dali::VisualRenderer::RegisterVisualTransformUniform()
+ */
+ void RegisterVisualTransformUniform();
+
protected: // implementation
/**
* @brief Constructor.
*/
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()
private:
VisualPropertyCache mPropertyCache;
+
+ bool mUniformMapped : 1;
};
} // namespace Internal
${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
${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
${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
#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>
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)
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
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);
}
mImpl->RemoveRenderTracker(renderTracker);
}
+void RenderManager::ClearProgramCache()
+{
+ mImpl->ClearProgramCache();
+}
+
void RenderManager::PreRender(Integration::RenderStatus& status, bool forceClear)
{
DALI_PRINT_RENDER_START(mImpl->renderBufferIndex);
// 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
{
}
#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)
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)
*/
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()
/**
--- /dev/null
+/*
+ * 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
--- /dev/null
+#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
#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.
*
*/
+// 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>
{
class Texture;
class Sampler;
+class UniformBlock;
} // namespace Render
namespace SceneGraph
#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>
mRenderDataProvider(dataProvider),
mGeometry(geometry),
mProgramCache(nullptr),
+ mSharedUniformBufferViewContainer(nullptr),
mStencilParameters(stencilParameters),
mBlendingOptions(),
mIndexedDrawFirstElement(0),
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)
Program* program = Program::New(*mProgramCache,
shaderData,
+ shader.GetSharedUniformNamesHash(),
*mGraphicsController);
if(!program)
{
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
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))
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());
+ }
}
}
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.
{
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;
uniform.uniformNameHash,
uniform.uniformNameHashNoArray,
uniformInfo);
+
if(!uniformFound)
{
continue;
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);
}
else
{
+ const auto typeSize = propertyValue->GetValueSize();
ubo->Write(valueAddress, typeSize, dest);
}
}
{
class Texture;
class ProgramCache;
+class SharedUniformBufferViewContainer;
namespace SceneGraph
{
* @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
ProgramCache* mProgramCache{nullptr};
Render::UniformBufferManager* mUniformBufferManager{};
+ SharedUniformBufferViewContainer* mSharedUniformBufferViewContainer{};
std::vector<Graphics::UniformBufferBinding> mUniformBufferBindings{};
Render::PipelineCache* mPipelineCache{nullptr};
--- /dev/null
+/*
+ * 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
--- /dev/null
+#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
/*
- * 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.
mClearCacheIterator = mProgramCache.Begin();
}
-bool ProgramController::ClearUnusedCacheIncrementally(bool fullCollect)
+bool ProgramController::ClearUnusedCacheIncrementally(bool fullCollect, bool forceClearAll)
{
if(mProgramCacheAdded)
{
// 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);
}
#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.
* @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
/*
- * 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.
#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>
// 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);
// 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);
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)
{
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
#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.
// EXTERNAL INCLUDES
#include <cstdint> // int32_t, uint32_t
+#include <memory>
#include <string>
#include <unordered_map>
#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
namespace Render
{
+class UniformBlock;
class UniformBufferManager;
+class UniformBufferView;
} // namespace Render
/**
* @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()
{
/**
* 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.
* 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
#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
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
*
*/
+// EXTERNAL INCLUDES
+#include <unordered_map>
+
// INTERNAL INCLUDES
#include <dali/internal/common/shader-data.h>
#include <dali/internal/event/common/event-thread-services.h>
{
class Program;
+namespace Render
+{
+class UniformBlock;
+}
+
namespace SceneGraph
{
class SceneController;
class Shader : public PropertyOwner
{
public:
+ using UniformBlockContainer = std::unordered_map<size_t, Render::UniformBlock*>;
+
Shader() = default;
/**
*/
[[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)
#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
ANIMATION = 0x10,
PROPERTY_NOTIFICATION = 0x20,
CUSTOM_OBJECT = 0x40,
+ UNIFORM_BLOCKS = 0x80,
};
/**
renderers(),
textureSets(),
shaders(),
+ uniformBlocks(),
panGestureProcessor(nullptr),
messageQueue(renderController, sceneGraphBuffers),
frameCallbackProcessor(nullptr),
// Ensure to clear renderers
renderers.Clear();
shaders.Clear();
+ uniformBlocks.Clear();
}
/**
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.
#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);
postPropertyOwners.PushBack(shader);
}
}
+ for(auto&& uniformBlock : mImpl->uniformBlocks)
+ {
+ ConstrainPropertyOwner(*uniformBlock, bufferIndex);
+ if(!uniformBlock->GetPostConstraints().Empty())
+ {
+ postPropertyOwners.PushBack(uniformBlock);
+ }
+ }
}
void UpdateManager::ProcessPropertyNotifications(BufferIndex bufferIndex)
// 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);
{
mImpl->customObjects.ShrinkToFitIfNeeded();
}
+ if(mImpl->containerRemovedFlags & ContainerRemovedFlagBits::UNIFORM_BLOCKS)
+ {
+ mImpl->uniformBlocks.ShrinkToFitIfNeeded();
+ }
// Reset flag
mImpl->containerRemovedFlags = ContainerRemovedFlagBits::NOTHING;
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;
#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>
*/
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
/**
*/
void RequestRendering();
+ /**
+ * Request to clear the program cache at RenderManager.
+ */
+ void RequestClearProgramCache();
+
/**
* @brief Get the active Node pointer by node id.
*
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
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
#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>
{
class Renderer;
class Geometry;
+class UniformBlock;
} // namespace Render
namespace SceneGraph
#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>
${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
${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
)
#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.
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
/*
- * 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.
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());
}
--- /dev/null
+/*
+ * 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
--- /dev/null
+#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
/*
- * 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.
VisualRenderer& VisualRenderer::operator=(VisualRenderer&& rhs) noexcept = default;
+void VisualRenderer::RegisterVisualTransformUniform()
+{
+ GetImplementation(*this).RegisterVisualTransformUniform();
+}
+
VisualRenderer::VisualRenderer(Internal::VisualRenderer* pointer)
: Dali::Renderer(pointer)
{
#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.
*/
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
/**