From 4b809c65539437afb7810b59118657eeb5673c5b Mon Sep 17 00:00:00 2001 From: seungho baek Date: Fri, 9 Jun 2023 22:00:09 +0900 Subject: [PATCH] [Tizen] Support multi pass shading with different shaders Change-Id: I34ac0d2dd0d63b316816bfa58954c739e7460e55 Signed-off-by: seungho baek --- automated-tests/src/dali/utc-Dali-RenderTask.cpp | 520 ++++++++++++++++++++- automated-tests/src/dali/utc-Dali-Shader.cpp | 176 +++++++ dali/internal/common/shader-data.h | 48 +- dali/internal/event/effects/shader-factory.cpp | 4 +- dali/internal/event/effects/shader-factory.h | 3 +- .../event/render-tasks/render-task-impl.cpp | 14 + .../internal/event/render-tasks/render-task-impl.h | 36 +- dali/internal/event/rendering/shader-impl.cpp | 194 +++++--- dali/internal/event/rendering/shader-impl.h | 28 +- dali/internal/render/common/render-instruction.h | 11 +- dali/internal/render/renderers/render-renderer.cpp | 7 +- dali/internal/render/shaders/render-shader.cpp | 46 +- dali/internal/render/shaders/render-shader.h | 30 +- .../manager/render-instruction-processor.cpp | 11 +- .../render-tasks/scene-graph-render-task.cpp | 6 + .../update/render-tasks/scene-graph-render-task.h | 32 +- .../update/rendering/scene-graph-renderer.cpp | 11 +- .../update/rendering/scene-graph-renderer.h | 3 +- dali/public-api/render-tasks/render-task.cpp | 10 + dali/public-api/render-tasks/render-task.h | 15 + dali/public-api/rendering/shader.cpp | 10 +- dali/public-api/rendering/shader.h | 19 +- 22 files changed, 1093 insertions(+), 141 deletions(-) diff --git a/automated-tests/src/dali/utc-Dali-RenderTask.cpp b/automated-tests/src/dali/utc-Dali-RenderTask.cpp index e3857dc..79cb0ed 100644 --- a/automated-tests/src/dali/utc-Dali-RenderTask.cpp +++ b/automated-tests/src/dali/utc-Dali-RenderTask.cpp @@ -78,10 +78,74 @@ void utc_dali_render_task_cleanup(void) * FinishedSignal 1+ve */ -namespace // unnamed namespace +namespace // unnamed namespace { const int RENDER_FRAME_INTERVAL = 16; ///< Duration of each frame in ms. (at approx 60FPS) +// Test shader codes +const std::string_view SHADER_COLOR_TEST_SHADER_VERT1{ + R"(INPUT mediump vec2 aPosition; +uniform highp mat4 uMvpMatrix; +uniform highp vec3 uSize; + +//Visual size and offset +uniform mediump vec2 offset; +uniform highp vec2 size; +uniform mediump vec4 offsetSizeMode; +uniform mediump vec2 origin; +uniform mediump vec2 anchorPoint; +uniform mediump vec2 extraSize; + +vec4 ComputeVertexPosition() +{ + vec2 visualSize = mix(size * uSize.xy, size, offsetSizeMode.zw ) + extraSize; + vec2 visualOffset = mix(offset * uSize.xy, offset, offsetSizeMode.xy); + mediump vec2 vPosition = aPosition * visualSize; + return vec4(vPosition + anchorPoint * visualSize + visualOffset + origin * uSize.xy, 0.0, 1.0); +} + +void main() +{ + gl_Position = uMvpMatrix * ComputeVertexPosition(); +} +)"}; + +// Test shader codes +const std::string_view SHADER_COLOR_TEST_SHADER_VERT2{ + R"(INPUT mediump vec2 aPosition; +uniform highp mat4 uMvpMatrix; +uniform highp vec3 uSize; + +//Visual size and offset +uniform mediump vec2 offset; +uniform highp vec2 size; +uniform mediump vec4 offsetSizeMode; +uniform mediump vec2 origin; +uniform mediump vec2 anchorPoint; +uniform mediump vec2 extraSize; + +vec4 ComputeVertexPosition2() +{ + vec2 visualSize = mix(size * uSize.xy, size, offsetSizeMode.zw ) + extraSize; + vec2 visualOffset = mix(offset * uSize.xy, offset, offsetSizeMode.xy); + mediump vec2 vPosition = aPosition * visualSize; + return vec4(vPosition + anchorPoint * visualSize + visualOffset + origin * uSize.xy, 0.0, 1.0); +} + +void main() +{ + gl_Position = uMvpMatrix * ComputeVertexPosition2(); +} +)"}; + +const std::string_view SHADER_COLOR_TEST_SHADER_FRAG{ + R"( +void main() +{ + OUT_COLOR = vec4(0.0, 0.0, 1.0, 1.0); +} +)"}; + /* * Simulate time passed by. * @@ -2432,12 +2496,12 @@ int UtcDaliRenderTaskOnceChain01(void) application.SendNotification(); - //Both render tasks are executed. + // Both render tasks are executed. DALI_TEST_CHECK(UpdateRender(application, drawTrace, true, firstFinished, false, true, __LINE__)); DALI_TEST_CHECK(firstFinished == false); DALI_TEST_CHECK(secondFinished == false); - //Nothing else to render and both render task should have finished now + // Nothing else to render and both render task should have finished now DALI_TEST_CHECK(UpdateRender(application, drawTrace, false, firstFinished, true, false, __LINE__)); DALI_TEST_CHECK(firstFinished == true); DALI_TEST_CHECK(secondFinished == true); @@ -3683,3 +3747,453 @@ int UtcDaliRenderTaskViewportGuideActor02(void) END_TEST; } + +int UtcDaliRenderTaskViewportGuideActor03(void) +{ + TestApplication application( + TestApplication::DEFAULT_SURFACE_WIDTH, + TestApplication::DEFAULT_SURFACE_HEIGHT, + TestApplication::DEFAULT_HORIZONTAL_DPI, + TestApplication::DEFAULT_VERTICAL_DPI, + true, + true); + + TestGlAbstraction& glAbstraction = application.GetGlAbstraction(); + TraceCallStack& callStack = glAbstraction.GetViewportTrace(); + glAbstraction.EnableViewportCallTrace(true); + tet_infoline("Testing that adding a viewport guide actor to RenderTask will change the viewport"); + + Stage stage = Stage::GetCurrent(); + Vector2 stageSize(stage.GetSize()); + + // Render and notify + application.SendNotification(); + application.Render(16); + glAbstraction.ResetViewportCallStack(); + + Geometry geometry = Geometry::New(); + Shader shader = Shader::New("vertexSrc", "fragmentSrc"); + Renderer renderer = Renderer::New(geometry, shader); + + Actor blue = Actor::New(); + blue[Dali::Actor::Property::NAME] = "Blue"; + blue[Dali::Actor::Property::ANCHOR_POINT] = AnchorPoint::TOP_LEFT; + blue[Dali::Actor::Property::PARENT_ORIGIN] = ParentOrigin::TOP_LEFT; + blue[Dali::Actor::Property::SIZE] = Vector2(400, 300); + blue[Dali::Actor::Property::POSITION] = Vector2(100, 50); + blue.AddRenderer(renderer); + stage.Add(blue); + + Actor green = Actor::New(); + green[Dali::Actor::Property::NAME] = "Green"; + green[Dali::Actor::Property::ANCHOR_POINT] = AnchorPoint::TOP_LEFT; + green[Dali::Actor::Property::PARENT_ORIGIN] = ParentOrigin::TOP_LEFT; + green[Dali::Actor::Property::SIZE] = Vector2(400, 300); + green[Dali::Actor::Property::POSITION] = Vector2(100, 50); + green.AddRenderer(renderer); + stage.Add(green); + + RenderTaskList renderTaskList = stage.GetRenderTaskList(); + RenderTask renderTask = renderTaskList.CreateTask(); + + Dali::CameraActor cameraActor = Dali::CameraActor::New(stageSize); + cameraActor[Dali::Actor::Property::ANCHOR_POINT] = AnchorPoint::CENTER; + cameraActor[Dali::Actor::Property::PARENT_ORIGIN] = ParentOrigin::CENTER; + stage.Add(cameraActor); + + renderTask.SetExclusive(true); + renderTask.SetInputEnabled(true); + renderTask.SetCameraActor(cameraActor); + renderTask.SetSourceActor(green); + + Viewport viewport(75, 55, 150, 250); + renderTask.SetViewport(viewport); + + // Render and notify + application.SendNotification(); + application.Render(16); + + // Note Y pos: 800 - (250+55) = 495 + std::string viewportParams1("75, 495, 150, 250"); + DALI_TEST_CHECK(callStack.FindIndexFromMethodAndParams("Viewport", viewportParams1) >= 0); + glAbstraction.ResetViewportCallStack(); + + // Update to use viewport guide actor instead. + renderTask.SetViewportGuideActor(blue); + + // Render and notify + application.SendNotification(); + application.Render(16); + + // Note: Y pos: 800 - (300+50) = 450 + std::string viewportParams2("100, 450, 400, 300"); + DALI_TEST_CHECK(callStack.FindIndexFromMethodAndParams("Viewport", viewportParams2) >= 0); + tet_infoline("Testing that removing viewport guide actor from RenderTask will revert the viewport"); + glAbstraction.ResetViewportCallStack(); + + // Remove guide actor, expect that the viewport is reset to its original values + renderTask.SetViewportGuideActor(Actor()); + application.SendNotification(); + application.Render(16); + + // Currently, update manager does not consider that added Resetters should cause another + // update; this is probably right. But, we have to then force another update for the resetter to trigger, and this will register as un-necessary in the test output. + // + application.SendNotification(); + application.Render(16); + + DALI_TEST_CHECK(callStack.FindIndexFromMethodAndParams("Viewport", viewportParams1) >= 0); + + END_TEST; +} + +int UtcDaliRenderTaskViewportGuideActor04(void) +{ + TestApplication application( + TestApplication::DEFAULT_SURFACE_WIDTH, + TestApplication::DEFAULT_SURFACE_HEIGHT, + TestApplication::DEFAULT_HORIZONTAL_DPI, + TestApplication::DEFAULT_VERTICAL_DPI, + true, + true); + + TestGlAbstraction& glAbstraction = application.GetGlAbstraction(); + TraceCallStack& callStack = glAbstraction.GetViewportTrace(); + glAbstraction.EnableViewportCallTrace(true); + tet_infoline("Testing that adding a viewport guide actor to RenderTask will change the viewport"); + + Stage stage = Stage::GetCurrent(); + Vector2 stageSize(stage.GetSize()); + + // Render and notify + application.SendNotification(); + application.Render(16); + glAbstraction.ResetViewportCallStack(); + + Geometry geometry = Geometry::New(); + Shader shader = Shader::New("vertexSrc", "fragmentSrc"); + Renderer renderer = Renderer::New(geometry, shader); + + Actor blue = Actor::New(); + blue[Dali::Actor::Property::NAME] = "Blue"; + blue[Dali::Actor::Property::ANCHOR_POINT] = AnchorPoint::TOP_LEFT; + blue[Dali::Actor::Property::PARENT_ORIGIN] = ParentOrigin::TOP_LEFT; + blue[Dali::Actor::Property::SIZE] = Vector2(400, 300); + blue[Dali::Actor::Property::POSITION] = Vector2(100, 50); + blue.AddRenderer(renderer); + stage.Add(blue); + + Actor green = Actor::New(); + green[Dali::Actor::Property::NAME] = "Green"; + green[Dali::Actor::Property::ANCHOR_POINT] = AnchorPoint::TOP_LEFT; + green[Dali::Actor::Property::PARENT_ORIGIN] = ParentOrigin::TOP_LEFT; + green[Dali::Actor::Property::SIZE] = Vector2(400, 300); + green[Dali::Actor::Property::POSITION] = Vector2(100, 50); + green.AddRenderer(renderer); + stage.Add(green); + + RenderTaskList renderTaskList = stage.GetRenderTaskList(); + RenderTask renderTask = renderTaskList.CreateTask(); + + Dali::CameraActor cameraActor = Dali::CameraActor::New(stageSize); + cameraActor[Dali::Actor::Property::ANCHOR_POINT] = AnchorPoint::CENTER; + cameraActor[Dali::Actor::Property::PARENT_ORIGIN] = ParentOrigin::CENTER; + stage.Add(cameraActor); + + renderTask.SetExclusive(true); + renderTask.SetInputEnabled(true); + renderTask.SetCameraActor(cameraActor); + renderTask.SetSourceActor(green); + + Viewport viewport(75, 55, 150, 250); + renderTask.SetViewport(viewport); + + // Render and notify + application.SendNotification(); + application.Render(16); + + // Note Y pos: 800 - (250+55) = 495 + std::string viewportParams1("75, 495, 150, 250"); + DALI_TEST_CHECK(callStack.FindIndexFromMethodAndParams("Viewport", viewportParams1) >= 0); + glAbstraction.ResetViewportCallStack(); + + // Update to use viewport guide actor instead. + renderTask.SetViewportGuideActor(blue); + + // Render and notify + application.SendNotification(); + application.Render(16); + + std::string viewportParams2("100, 450, 400, 300"); + DALI_TEST_CHECK(callStack.FindIndexFromMethodAndParams("Viewport", viewportParams2) >= 0); + tet_infoline("Testing that removing viewport guide actor from RenderTask will revert the viewport"); + + glAbstraction.ResetViewportCallStack(); + + // Remove guide actor, expect that the viewport is reset to it's original values + renderTask.ResetViewportGuideActor(); + application.SendNotification(); + application.Render(16); + + // Currently, update manager does not consider that added Resetters should cause another + // update; this is probably right. But, we have to then force another update for the resetter + // to trigger, and this will register as un-necessary in the test output. + application.SendNotification(); + application.Render(16); + + DALI_TEST_CHECK(callStack.FindIndexFromMethodAndParams("Viewport", viewportParams1) >= 0); + + // This should remove the baking resetters, but is again going to show up + // as unnecessary. Also try and figure out if we can test the dirty flags + // here, somehow (Can at least check the property's dirty flags in the debugger). + application.SendNotification(); + application.Render(16); + + END_TEST; +} + +int UtcDaliRenderTaskSetPartialUpdate(void) +{ + TestApplication application( + TestApplication::DEFAULT_SURFACE_WIDTH, + TestApplication::DEFAULT_SURFACE_HEIGHT, + TestApplication::DEFAULT_HORIZONTAL_DPI, + TestApplication::DEFAULT_VERTICAL_DPI, + true, + true); + + tet_infoline("Check the damaged rects with render task"); + + const TestGlAbstraction::ScissorParams& glScissorParams(application.GetGlAbstraction().GetScissorParams()); + + Actor actor = CreateRenderableActor(); + actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT); + actor.SetProperty(Actor::Property::POSITION, Vector3(16.0f, 16.0f, 0.0f)); + actor.SetProperty(Actor::Property::SIZE, Vector3(16.0f, 16.0f, 0.0f)); + actor.SetResizePolicy(ResizePolicy::FIXED, Dimension::ALL_DIMENSIONS); + application.GetScene().Add(actor); + + Actor rootActor = CreateRenderableActor(); + rootActor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT); + rootActor.SetProperty(Actor::Property::POSITION, Vector3(16.0f, 16.0f, 0.0f)); + rootActor.SetProperty(Actor::Property::SIZE, Vector3(16.0f, 16.0f, 0.0f)); + rootActor.SetResizePolicy(ResizePolicy::FIXED, Dimension::ALL_DIMENSIONS); + application.GetScene().Add(rootActor); + + CameraActor cameraActor = CameraActor::New(Size(TestApplication::DEFAULT_SURFACE_WIDTH, TestApplication::DEFAULT_SURFACE_HEIGHT)); + cameraActor.SetProperty(Dali::Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER); + cameraActor.SetProperty(Dali::Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER); + application.GetScene().Add(cameraActor); + + Texture frameBufferTexture = Texture::New(TextureType::TEXTURE_2D, Pixel::RGB888, 16, 16); + FrameBuffer frameBuffer = FrameBuffer::New(frameBufferTexture.GetWidth(), frameBufferTexture.GetHeight()); + frameBuffer.AttachColorTexture(frameBufferTexture); + + // Create a RenderTask and set a framebuffer + RenderTaskList taskList = application.GetScene().GetRenderTaskList(); + RenderTask newTask = taskList.CreateTask(); + newTask.SetCameraActor(cameraActor); + newTask.SetSourceActor(rootActor); + newTask.SetInputEnabled(false); + newTask.SetClearColor(Vector4(0.f, 0.f, 0.f, 0.f)); + newTask.SetClearEnabled(true); + newTask.SetExclusive(true); + newTask.SetRefreshRate(RenderTask::REFRESH_ALWAYS); + newTask.SetFrameBuffer(frameBuffer); + + application.SendNotification(); + + std::vector> damagedRects; + Rect clippingRect; + + application.PreRenderWithPartialUpdate(TestApplication::RENDER_FRAME_INTERVAL, nullptr, damagedRects); + + // Full update if there is off-screen rendering + clippingRect = Rect(0, 0, TestApplication::DEFAULT_SURFACE_WIDTH, TestApplication::DEFAULT_SURFACE_HEIGHT); + DALI_TEST_EQUALS(damagedRects.size(), 1, TEST_LOCATION); + DALI_TEST_EQUALS>(clippingRect, damagedRects[0], TEST_LOCATION); + + application.RenderWithPartialUpdate(damagedRects, clippingRect); + DALI_TEST_EQUALS(clippingRect.x, glScissorParams.x, TEST_LOCATION); + DALI_TEST_EQUALS(clippingRect.y, glScissorParams.y, TEST_LOCATION); + DALI_TEST_EQUALS(clippingRect.width, glScissorParams.width, TEST_LOCATION); + DALI_TEST_EQUALS(clippingRect.height, glScissorParams.height, TEST_LOCATION); + + // Remove framebuffer + newTask.SetFrameBuffer(FrameBuffer()); + + application.SendNotification(); + + damagedRects.clear(); + application.PreRenderWithPartialUpdate(TestApplication::RENDER_FRAME_INTERVAL, nullptr, damagedRects); + + // Full update + clippingRect = Rect(0, 0, TestApplication::DEFAULT_SURFACE_WIDTH, TestApplication::DEFAULT_SURFACE_HEIGHT); + DALI_TEST_EQUALS(damagedRects.size(), 1, TEST_LOCATION); + DALI_TEST_EQUALS>(clippingRect, damagedRects[0], TEST_LOCATION); + + application.RenderWithPartialUpdate(damagedRects, clippingRect); + + // Set invalid viewport of the render task + newTask.SetViewportSize(Vector2(-100.0f, -100.0f)); + + application.SendNotification(); + + damagedRects.clear(); + application.PreRenderWithPartialUpdate(TestApplication::RENDER_FRAME_INTERVAL, nullptr, damagedRects); + + // Full update because the camera orientation is changed + clippingRect = Rect(0, 0, TestApplication::DEFAULT_SURFACE_WIDTH, TestApplication::DEFAULT_SURFACE_HEIGHT); + DALI_TEST_EQUALS(damagedRects.size(), 1, TEST_LOCATION); + DALI_TEST_EQUALS>(clippingRect, damagedRects[0], TEST_LOCATION); + + application.RenderWithPartialUpdate(damagedRects, clippingRect); + + newTask.SetViewportSize(Vector2(0.0f, 0.0f)); + + // Change orientation of offscreen camera + cameraActor.SetProperty(Actor::Property::ORIENTATION, Quaternion(Degree(90.0f), Vector3::XAXIS)); + + application.SendNotification(); + + damagedRects.clear(); + application.PreRenderWithPartialUpdate(TestApplication::RENDER_FRAME_INTERVAL, nullptr, damagedRects); + + // Full update because the camera orientation is changed + clippingRect = Rect(0, 0, TestApplication::DEFAULT_SURFACE_WIDTH, TestApplication::DEFAULT_SURFACE_HEIGHT); + DALI_TEST_EQUALS(damagedRects.size(), 1, TEST_LOCATION); + DALI_TEST_EQUALS>(clippingRect, damagedRects[0], TEST_LOCATION); + + application.RenderWithPartialUpdate(damagedRects, clippingRect); + + // Change camera target + cameraActor.SetTargetPosition(Vector3(10.0f, 10.0f, 0.0f)); + + application.SendNotification(); + + damagedRects.clear(); + application.PreRenderWithPartialUpdate(TestApplication::RENDER_FRAME_INTERVAL, nullptr, damagedRects); + + // Full update because the camera is moved + clippingRect = Rect(0, 0, TestApplication::DEFAULT_SURFACE_WIDTH, TestApplication::DEFAULT_SURFACE_HEIGHT); + DALI_TEST_EQUALS(damagedRects.size(), 1, TEST_LOCATION); + DALI_TEST_EQUALS>(clippingRect, damagedRects[0], TEST_LOCATION); + + application.RenderWithPartialUpdate(damagedRects, clippingRect); + + END_TEST; +} + +int UtcDaliRenderTaskRenderPass(void) +{ + TestApplication application; + tet_infoline("Testing RenderTask with RenderPass"); + + Stage stage = Stage::GetCurrent(); + Vector2 stageSize(stage.GetSize()); + + Actor blue = Actor::New(); + blue[Dali::Actor::Property::NAME] = "Blue"; + blue[Dali::Actor::Property::ANCHOR_POINT] = AnchorPoint::CENTER; + blue[Dali::Actor::Property::PARENT_ORIGIN] = ParentOrigin::CENTER; + blue[Dali::Actor::Property::SIZE] = Vector2(300, 300); + blue[Dali::Actor::Property::POSITION] = Vector2(0, 0); + + Geometry geometry = Geometry::New(); + + Property::Map map[2]; + map[0]["vertex"] = SHADER_COLOR_TEST_SHADER_VERT1.data(); + map[0]["fragment"] = SHADER_COLOR_TEST_SHADER_FRAG.data(); + map[0]["renderPass"] = 0; + + map[1]["vertex"] = SHADER_COLOR_TEST_SHADER_VERT2.data(); + map[1]["fragment"] = SHADER_COLOR_TEST_SHADER_FRAG.data(); + map[1]["renderPass"] = 1; + + Property::Array array; + array.PushBack(map[0]); + array.PushBack(map[1]); + + Shader shader = Shader::New(array); + Renderer renderer = Renderer::New(geometry, shader); + blue.AddRenderer(renderer); + + stage.Add(blue); + + auto& gfx = application.GetGraphicsController(); + + RenderTaskList renderTaskList = stage.GetRenderTaskList(); + DALI_TEST_EQUALS(0u, renderTaskList.GetTask(0u).GetRenderPass(), TEST_LOCATION); + // Render and notify + application.SendNotification(); + application.Render(16); + DALI_TEST_CHECK(gfx.mCallStack.FindMethod("CreatePipeline")); + gfx.mCallStack.Reset(); + DALI_TEST_EQUALS(0u, renderTaskList.GetTask(0u).GetRenderPass(), TEST_LOCATION); + + renderTaskList.GetTask(0u).SetRenderPass(1u); + DALI_TEST_EQUALS(1u, renderTaskList.GetTask(0u).GetRenderPass(), TEST_LOCATION); + // Render and notify + application.SendNotification(); + application.Render(16); + DALI_TEST_CHECK(gfx.mCallStack.FindMethod("CreatePipeline")); + gfx.mCallStack.Reset(); + DALI_TEST_EQUALS(1u, renderTaskList.GetTask(0u).GetRenderPass(), TEST_LOCATION); + + renderTaskList.GetTask(0u).SetRenderPass(0u); + DALI_TEST_EQUALS(0u, renderTaskList.GetTask(0u).GetRenderPass(), TEST_LOCATION); + // Render and notify + application.SendNotification(); + application.Render(16); + DALI_TEST_CHECK(!gfx.mCallStack.FindMethod("CreatePipeline")); + gfx.mCallStack.Reset(); + DALI_TEST_EQUALS(0u, renderTaskList.GetTask(0u).GetRenderPass(), TEST_LOCATION); + + renderTaskList.GetTask(0u).SetRenderPass(1u); + DALI_TEST_EQUALS(1u, renderTaskList.GetTask(0u).GetRenderPass(), TEST_LOCATION); + // Render and notify + application.SendNotification(); + application.Render(16); + DALI_TEST_CHECK(!gfx.mCallStack.FindMethod("CreatePipeline")); + gfx.mCallStack.Reset(); + DALI_TEST_EQUALS(1u, renderTaskList.GetTask(0u).GetRenderPass(), TEST_LOCATION); + + END_TEST; +} + +int UtcDaliRenderTaskWithWrongShaderData(void) +{ + TestApplication application; + tet_infoline("Testing RenderTask with wrong shader data"); + + Stage stage = Stage::GetCurrent(); + Vector2 stageSize(stage.GetSize()); + + Actor blue = Actor::New(); + blue[Dali::Actor::Property::NAME] = "Blue"; + blue[Dali::Actor::Property::ANCHOR_POINT] = AnchorPoint::CENTER; + blue[Dali::Actor::Property::PARENT_ORIGIN] = ParentOrigin::CENTER; + blue[Dali::Actor::Property::SIZE] = Vector2(300, 300); + blue[Dali::Actor::Property::POSITION] = Vector2(0, 0); + + Geometry geometry = Geometry::New(); + + Shader shader = Shader::New(Property::Value(10.0f)); + Renderer renderer = Renderer::New(geometry, shader); + blue.AddRenderer(renderer); + + stage.Add(blue); + + auto& gfx = application.GetGraphicsController(); + + RenderTaskList renderTaskList = stage.GetRenderTaskList(); + DALI_TEST_EQUALS(0u, renderTaskList.GetTask(0u).GetRenderPass(), TEST_LOCATION); + // Render and notify + application.SendNotification(); + application.Render(16); + DALI_TEST_CHECK(!gfx.mCallStack.FindMethod("CreatePipeline")); + gfx.mCallStack.Reset(); + DALI_TEST_EQUALS(0u, renderTaskList.GetTask(0u).GetRenderPass(), TEST_LOCATION); + + END_TEST; +} diff --git a/automated-tests/src/dali/utc-Dali-Shader.cpp b/automated-tests/src/dali/utc-Dali-Shader.cpp index 9d51d27..3833963 100644 --- a/automated-tests/src/dali/utc-Dali-Shader.cpp +++ b/automated-tests/src/dali/utc-Dali-Shader.cpp @@ -44,6 +44,14 @@ 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"; +static const char* VertexSource2 = + "This is a custom vertex shader2\n" + "made on purpose to look nothing like a normal vertex shader inside dali\n"; + +static const char* FragmentSource2 = + "This is a custom fragment shader2\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; @@ -64,6 +72,26 @@ int UtcDaliShaderMethodNew02(void) { TestApplication application; + Property::Map map; + Shader shader = Shader::New(map); + DALI_TEST_EQUALS((bool)shader, true, TEST_LOCATION); + END_TEST; +} + +int UtcDaliShaderMethodNew03(void) +{ + TestApplication application; + + Property::Map array; + Shader shader = Shader::New(array); + DALI_TEST_EQUALS((bool)shader, true, TEST_LOCATION); + END_TEST; +} + +int UtcDaliShaderMethodNew04(void) +{ + TestApplication application; + Shader shader; DALI_TEST_EQUALS((bool)shader, false, TEST_LOCATION); END_TEST; @@ -469,3 +497,151 @@ int UtcDaliShaderProgramProperty(void) END_TEST; } + +int UtcDaliShaderPropertyValueConstructorMap(void) +{ + TestApplication application; + + tet_infoline("UtcDaliShaderPropertyValueConstructorMap"); + + std::string hintSet = "MODIFIES_GEOMETRY"; + Property::Map map; + map["vertex"] = VertexSource; + map["fragment"] = FragmentSource; + map["renderPass"] = 0; + map["hints"] = hintSet; + + Shader shader = Shader::New(map); + + Property::Value value = shader.GetProperty(Shader::Property::PROGRAM); + DALI_TEST_CHECK(value.GetType() == Property::MAP); + + const Property::Map* outMap = value.GetMap(); + std::string v = (*outMap)["vertex"].Get(); + std::string f = (*outMap)["fragment"].Get(); + std::string h = (*outMap)["hints"].Get(); + int32_t r = (*outMap)["renderPass"].Get(); + + DALI_TEST_CHECK(v == map["vertex"].Get()); + DALI_TEST_CHECK(f == map["fragment"].Get()); + DALI_TEST_CHECK(h == map["hints"].Get()); + DALI_TEST_CHECK(r == map["renderPass"].Get()); + + END_TEST; +} + +int UtcDaliShaderPropertyValueConstructorArray(void) +{ + TestApplication application; + + tet_infoline("UtcDaliShaderPropertyValueConstructorArray"); + + std::string hintSet = "MODIFIES_GEOMETRY"; + Property::Map map[2]; + map[0]["vertex"] = VertexSource; + map[0]["fragment"] = FragmentSource; + map[0]["renderPass"] = 0; + map[0]["hints"] = hintSet; + + map[1]["vertex"] = VertexSource2; + map[1]["fragment"] = FragmentSource2; + map[1]["renderPass"] = 1; + map[1]["hints"] = hintSet; + + Property::Array array; + array.PushBack(map[0]); + array.PushBack(map[1]); + + Shader shader = Shader::New(array); + + Property::Value value = shader.GetProperty(Shader::Property::PROGRAM); + DALI_TEST_CHECK(value.GetType() == Property::ARRAY); + + const Property::Array* outArray = value.GetArray(); + uint32_t arrayCount = outArray->Size(); + DALI_TEST_CHECK(arrayCount == 2u); + + for(uint32_t i = 0; i < arrayCount; ++i) + { + const Property::Map* outMap = outArray->GetElementAt(i).GetMap(); + std::string v = (*outMap)["vertex"].Get(); + std::string f = (*outMap)["fragment"].Get(); + std::string h = (*outMap)["hints"].Get(); + int32_t r = (*outMap)["renderPass"].Get(); + + DALI_TEST_CHECK(v == map[i]["vertex"].Get()); + DALI_TEST_CHECK(f == map[i]["fragment"].Get()); + DALI_TEST_CHECK(h == map[i]["hints"].Get()); + DALI_TEST_CHECK(r == map[i]["renderPass"].Get()); + } + + END_TEST; +} + +int UtcDaliShaderProgramPropertyArray(void) +{ + TestApplication application; + + tet_infoline("Test get/set progam property array"); + + Shader shader = Shader::New("", ""); + std::string hintSet = "MODIFIES_GEOMETRY"; + + Property::Map map[2]; + map[0]["vertex"] = VertexSource; + map[0]["fragment"] = FragmentSource; + map[0]["renderPass"] = 0; + map[0]["hints"] = hintSet; + + map[1]["vertex"] = VertexSource2; + map[1]["fragment"] = FragmentSource2; + map[1]["renderPass"] = 1; + map[1]["hints"] = hintSet; + + Property::Array array; + array.PushBack(map[0]); + array.PushBack(map[1]); + + shader.SetProperty(Shader::Property::PROGRAM, Property::Value(array)); + + Property::Value value = shader.GetProperty(Shader::Property::PROGRAM); + DALI_TEST_CHECK(value.GetType() == Property::ARRAY); + + const Property::Array* outArray = value.GetArray(); + uint32_t arrayCount = outArray->Size(); + DALI_TEST_CHECK(arrayCount == 2u); + + for(uint32_t i = 0; i < arrayCount; ++i) + { + const Property::Map* outMap = outArray->GetElementAt(i).GetMap(); + std::string v = (*outMap)["vertex"].Get(); + std::string f = (*outMap)["fragment"].Get(); + std::string h = (*outMap)["hints"].Get(); + int32_t r = (*outMap)["renderPass"].Get(); + + DALI_TEST_CHECK(v == map[i]["vertex"].Get()); + DALI_TEST_CHECK(f == map[i]["fragment"].Get()); + DALI_TEST_CHECK(h == map[i]["hints"].Get()); + DALI_TEST_CHECK(r == map[i]["renderPass"].Get()); + } + + END_TEST; +} + +int UtcDaliShaderWrongData(void) +{ + TestApplication application; + + tet_infoline("Test get/set wrong data"); + + Shader shader = Shader::New(Property::Value(1.0f)); + + Property::Value value = shader.GetProperty(Shader::Property::PROGRAM); + DALI_TEST_CHECK(value.GetType() == Property::ARRAY); + + const Property::Array* outArray = value.GetArray(); + uint32_t arrayCount = outArray->Size(); + DALI_TEST_CHECK(arrayCount == 0u); + + END_TEST; +} diff --git a/dali/internal/common/shader-data.h b/dali/internal/common/shader-data.h index 3d50cfa..5b69a9f 100644 --- a/dali/internal/common/shader-data.h +++ b/dali/internal/common/shader-data.h @@ -61,12 +61,13 @@ public: * @param[in] fragmentSource Source code for fragment program * @param[in] hints Hints for rendering */ - ShaderData(std::string vertexSource, std::string fragmentSource, const Dali::Shader::Hint::Value hints) + ShaderData(std::string vertexSource, std::string fragmentSource, const Dali::Shader::Hint::Value hints, uint32_t renderPass) : mShaderHash(-1), mVertexShader(StringToVector(vertexSource)), mFragmentShader(StringToVector(fragmentSource)), mHints(hints), - mSourceMode(Graphics::ShaderSourceMode::TEXT) + mSourceMode(Graphics::ShaderSourceMode::TEXT), + mRenderPass(renderPass) { } @@ -76,15 +77,30 @@ public: * @param[in] fragmentSource Source code for fragment program * @param[in] hints Hints for rendering */ - ShaderData(std::vector& vertexSource, std::vector& fragmentSource, const Dali::Shader::Hint::Value hints) + ShaderData(std::vector& vertexSource, std::vector& fragmentSource, const Dali::Shader::Hint::Value hints, uint32_t renderPass) : mShaderHash(-1), mVertexShader(vertexSource), mFragmentShader(fragmentSource), mHints(hints), - mSourceMode(Graphics::ShaderSourceMode::BINARY) + mSourceMode(Graphics::ShaderSourceMode::BINARY), + mRenderPass(renderPass) { } + /** + * Query whether a shader hint is set. + * + * @warning This method is called from Update Algorithms. + * + * @pre The shader has been initialized. + * @param[in] hint The hint to check. + * @return True if the given hint is set. + */ + [[nodiscard]] bool HintEnabled(Dali::Shader::Hint::Value hint) const + { + return mHints & hint; + } + protected: /** * Protected Destructor @@ -216,17 +232,27 @@ public: // API return mSourceMode; } + /** + * Get Render Pass of shader data + * @return Render Pass of this shader data, Default value is 0. + */ + uint32_t GetRenderPass() const + { + return mRenderPass; + } + private: // Not implemented ShaderData(const ShaderData& other); ///< no copying of this object ShaderData& operator=(const ShaderData& rhs); ///< no copying of this object -private: // Data - std::size_t mShaderHash; ///< hash key created with vertex and fragment shader code - std::vector mVertexShader; ///< source code for vertex program - std::vector mFragmentShader; ///< source code for fragment program - Dali::Shader::Hint::Value mHints; ///< take a hint - Dali::Vector mBuffer; ///< buffer containing compiled binary bytecode - Graphics::ShaderSourceMode mSourceMode; ///< Source mode of shader data ( text or binary ) +private: // Data + std::size_t mShaderHash; ///< hash key created with vertex and fragment shader code + std::vector mVertexShader; ///< source code for vertex program + std::vector mFragmentShader; ///< source code for fragment program + Dali::Shader::Hint::Value mHints; ///< take a hint + Dali::Vector mBuffer; ///< buffer containing compiled binary bytecode + Graphics::ShaderSourceMode mSourceMode; ///< Source mode of shader data ( text or binary ) + uint32_t mRenderPass{0u}; ///< Render Pass for this shader }; } // namespace Internal diff --git a/dali/internal/event/effects/shader-factory.cpp b/dali/internal/event/effects/shader-factory.cpp index a2e7ca4..bb1be31 100644 --- a/dali/internal/event/effects/shader-factory.cpp +++ b/dali/internal/event/effects/shader-factory.cpp @@ -71,7 +71,7 @@ ShaderFactory::~ShaderFactory() } } -ShaderDataPtr ShaderFactory::Load(std::string_view vertexSource, std::string_view fragmentSource, const Dali::Shader::Hint::Value hints, size_t& shaderHash) +ShaderDataPtr ShaderFactory::Load(std::string_view vertexSource, std::string_view fragmentSource, const Dali::Shader::Hint::Value hints, uint32_t renderPass, size_t& shaderHash) { // Work out the filename for the binary that the glsl source will be compiled and linked to: shaderHash = CalculateHash(vertexSource.data(), fragmentSource.data()); @@ -96,7 +96,7 @@ ShaderDataPtr ShaderFactory::Load(std::string_view vertexSource, std::string_vie if(shaderData.Get() == nullptr) { // Allocate the structure that returns the loaded shader: - shaderData = new ShaderData(std::string(vertexSource), std::string(fragmentSource), hints); + shaderData = new ShaderData(std::string(vertexSource), std::string(fragmentSource), hints, renderPass); shaderData->SetHashValue(shaderHash); shaderData->GetBuffer().Clear(); diff --git a/dali/internal/event/effects/shader-factory.h b/dali/internal/event/effects/shader-factory.h index 36931d1..744f2f6 100644 --- a/dali/internal/event/effects/shader-factory.h +++ b/dali/internal/event/effects/shader-factory.h @@ -59,12 +59,13 @@ public: * * @param [in] vertexSource The vertex shader source code * @param [in] fragmentSource The fragment shader source code + * @param [in] renderPass RenderPass the shaders are executed * @param [out] shaderHash Hash key created from vertex and fragment shader code * @return ShaderData containing the source and hash value, and additionally, * a compiled shader program binary if one could be found, else an * empty binary buffer cleared to size zero. */ - Internal::ShaderDataPtr Load(std::string_view vertexSource, std::string_view fragmentSource, const Dali::Shader::Hint::Value hints, size_t& shaderHash); + Internal::ShaderDataPtr Load(std::string_view vertexSource, std::string_view fragmentSource, const Dali::Shader::Hint::Value hints, uint32_t renderPass, size_t& shaderHash); /** * @brief Saves shader to memory cache and filesystem. diff --git a/dali/internal/event/render-tasks/render-task-impl.cpp b/dali/internal/event/render-tasks/render-task-impl.cpp index a74bba0..f6fa40b 100644 --- a/dali/internal/event/render-tasks/render-task-impl.cpp +++ b/dali/internal/event/render-tasks/render-task-impl.cpp @@ -495,6 +495,20 @@ bool RenderTask::ViewportToLocal(Actor* actor, float viewportX, float viewportY, return actor->ScreenToLocal(*this, localX, localY, viewportX, viewportY); } +void RenderTask::SetRenderPass(uint32_t renderPass) +{ + if(mRenderPass != renderPass) + { + mRenderPass = renderPass; + SetRenderPassMessage(GetEventThreadServices(), GetRenderTaskSceneObject(), renderPass); + } +} + +uint32_t RenderTask::GetRenderPass() const +{ + return mRenderPass; +} + const SceneGraph::RenderTask& RenderTask::GetRenderTaskSceneObject() const { return *static_cast(mUpdateObject); diff --git a/dali/internal/event/render-tasks/render-task-impl.h b/dali/internal/event/render-tasks/render-task-impl.h index c859227..4b4e318 100644 --- a/dali/internal/event/render-tasks/render-task-impl.h +++ b/dali/internal/event/render-tasks/render-task-impl.h @@ -103,13 +103,13 @@ public: CameraActor* GetCameraActor() const; /** - * @copydoc Dali::RenderTask::SetFrameBuffer() - */ + * @copydoc Dali::RenderTask::SetFrameBuffer() + */ void SetFrameBuffer(FrameBufferPtr frameBuffer); /** - * @copydoc Dali::RenderTask::GetFrameBuffer - */ + * @copydoc Dali::RenderTask::GetFrameBuffer + */ FrameBuffer* GetFrameBuffer() const; /** @@ -253,6 +253,16 @@ public: */ bool ViewportToLocal(Actor* actor, float viewportX, float viewportY, float& localX, float& localY) const; + /** + * @copydoc Dali::RenderTask::SetRenderPass() + */ + void SetRenderPass(uint32_t renderPass); + + /** + * @copydoc Dali::RenderTask::GetRenderPass() + */ + uint32_t GetRenderPass() const; + public: // Used by RenderTaskList, which owns the SceneGraph::RenderTasks /** * Retrieve the scene-graph RenderTask object. @@ -297,7 +307,7 @@ public: // Implementation of Object */ const PropertyInputImpl* GetSceneObjectInputProperty(Property::Index index) const override; -public: //signals +public: // signals /** * Query whether a Finished signal should be emitted for this render-task. * This should only be called by NotificationManager, before signals are emitted. @@ -343,8 +353,8 @@ protected: ~RenderTask() override; private: // not copyable - RenderTask() = delete; - RenderTask(const RenderTask&) = delete; + RenderTask() = delete; + RenderTask(const RenderTask&) = delete; RenderTask& operator=(const RenderTask&) = delete; private: @@ -354,12 +364,12 @@ private: WeakHandle mInputMappingActor; /// used to mapping screen to frame buffer coordinate, not kept alive by rendertask RenderTaskList& mRenderTaskList; ///< The render task list - Vector4 mClearColor; ///< Optional clear color + Vector4 mClearColor; ///< Optional clear color - Vector2 mViewportPosition; ///< The cached viewport position - Vector2 mViewportSize; ///< The cached viewport size + Vector2 mViewportPosition; ///< The cached viewport position + Vector2 mViewportSize; ///< The cached viewport size - uint32_t mRefreshRate; ///< Determines how often the task is processed. + uint32_t mRefreshRate; ///< Determines how often the task is processed. uint32_t mRefreshOnceCounter; @@ -367,13 +377,15 @@ private: Dali::RenderTask::ScreenToFrameBufferFunction mScreenToFrameBufferFunction; ///< Used to convert screen to frame-buffer coordinates + uint32_t mRenderPass{0u}; + bool mExclusive : 1; ///< True if the render-task has exclusive access to the source Nodes. bool mInputEnabled : 1; ///< True if the render-task should be considered for input handling. bool mClearEnabled : 1; ///< True if the render-task should be clear the color buffer. bool mCullMode : 1; ///< True if the render-task's actors should be culled bool mRequiresSync : 1; ///< True if the GL sync is required to track the render of. - //Signals + // Signals Dali::RenderTask::RenderTaskSignalType mSignalFinished; ///< Signal emmited when the render task has been processed. }; diff --git a/dali/internal/event/rendering/shader-impl.cpp b/dali/internal/event/rendering/shader-impl.cpp index 74a8ab9..1420375 100644 --- a/dali/internal/event/rendering/shader-impl.cpp +++ b/dali/internal/event/rendering/shader-impl.cpp @@ -46,6 +46,8 @@ Dali::Scripting::StringEnum ShaderHintsTable[] = const uint32_t ShaderHintsTableSize = static_cast(sizeof(ShaderHintsTable) / sizeof(ShaderHintsTable[0])); +static constexpr uint32_t DEFAULT_RENDER_PASS = 0u; + BaseHandle Create() { return Dali::BaseHandle(); @@ -86,6 +88,36 @@ Property::Value HintString(const Dali::Shader::Hint::Value& hints) return Property::Value(s); } +void GetShaderData(const Property::Map& shaderMap, std::string& vertexShader, std::string& fragmentShader, uint32_t& renderPass, Dali::Shader::Hint::Value& hints) +{ + hints = Dali::Shader::Hint::NONE; + renderPass = 0u; + + if(Property::Value* value = shaderMap.Find("vertex")) + { + vertexShader = value->Get(); + } + + if(Property::Value* value = shaderMap.Find("fragment")) + { + fragmentShader = value->Get(); + } + + if(Property::Value* value = shaderMap.Find("renderPass")) + { + renderPass = static_cast(value->Get()); + } + + if(Property::Value* value = shaderMap.Find("hints")) + { + static_cast( // ignore return + Scripting::GetEnumeration(value->Get().c_str(), + ShaderHintsTable, + ShaderHintsTableSize, + hints)); + } +} + } // unnamed namespace ShaderPtr Shader::New(std::string_view vertexShader, @@ -93,7 +125,7 @@ ShaderPtr Shader::New(std::string_view vertexShader, Dali::Shader::Hint::Value hints) { // create scene object first so it's guaranteed to exist for the event side - auto sceneObject = new SceneGraph::Shader(hints); + auto sceneObject = new SceneGraph::Shader(); OwnerPointer transferOwnership(sceneObject); // pass the pointer to base for message passing ShaderPtr shader(new Shader(sceneObject)); @@ -103,7 +135,25 @@ ShaderPtr Shader::New(std::string_view vertexShader, AddShaderMessage(updateManager, transferOwnership); services.RegisterObject(shader.Get()); - shader->SetShader(vertexShader, fragmentShader, hints); + shader->UpdateShaderData(vertexShader, fragmentShader, DEFAULT_RENDER_PASS, hints); + + return shader; +} + +ShaderPtr Shader::New(Dali::Property::Value shaderMap) +{ + // create scene object first so it's guaranteed to exist for the event side + auto sceneObject = new SceneGraph::Shader(); + OwnerPointer transferOwnership(sceneObject); + // pass the pointer to base for message passing + ShaderPtr shader(new Shader(sceneObject)); + // transfer scene object ownership to update manager + auto&& services = shader->GetEventThreadServices(); + SceneGraph::UpdateManager& updateManager = services.GetUpdateManager(); + AddShaderMessage(updateManager, transferOwnership); + + services.RegisterObject(shader.Get()); + shader->SetShaderProperty(shaderMap); return shader; } @@ -119,41 +169,7 @@ void Shader::SetDefaultProperty(Property::Index index, const Property::Value& pr { case Dali::Shader::Property::PROGRAM: { - if(propertyValue.GetType() == Property::MAP) - { - const Dali::Property::Map* map = propertyValue.GetMap(); - if(map) - { - std::string vertex; - std::string fragment; - Dali::Shader::Hint::Value hints(Dali::Shader::Hint::NONE); - - if(Property::Value* value = map->Find("vertex")) - { - vertex = value->Get(); - } - - if(Property::Value* value = map->Find("fragment")) - { - fragment = value->Get(); - } - - if(Property::Value* value = map->Find("hints")) - { - static_cast( // ignore return - Scripting::GetEnumeration(value->Get().c_str(), - ShaderHintsTable, - ShaderHintsTableSize, - hints)); - } - - SetShader(vertex, fragment, hints); - } - } - else - { - DALI_LOG_WARNING("Shader program property should be a map\n"); - } + SetShaderProperty(propertyValue); break; } } @@ -167,14 +183,32 @@ Property::Value Shader::GetDefaultProperty(Property::Index index) const { case Dali::Shader::Property::PROGRAM: { - Dali::Property::Map map; - if(mShaderData) + if(mShaderDataList.size() == 1u) { - map["vertex"] = Property::Value(mShaderData->GetVertexShader()); - map["fragment"] = Property::Value(mShaderData->GetFragmentShader()); - map["hints"] = HintString(mShaderData->GetHints()); + Dali::Property::Map map; + map["vertex"] = Property::Value(mShaderDataList.front()->GetVertexShader()); + map["fragment"] = Property::Value(mShaderDataList.front()->GetFragmentShader()); + map["renderPass"] = Property::Value(static_cast(mShaderDataList.front()->GetRenderPass())); + map["hints"] = HintString(mShaderDataList.front()->GetHints()); + value = map; + } + else + { + Dali::Property::Array array; + for(auto&& shaderData : mShaderDataList) + { + if(shaderData) + { + Dali::Property::Map map; + map["vertex"] = Property::Value(shaderData->GetVertexShader()); + map["fragment"] = Property::Value(shaderData->GetFragmentShader()); + map["renderPass"] = Property::Value(static_cast(shaderData->GetRenderPass())); + map["hints"] = HintString(shaderData->GetHints()); + array.PushBack(map); + } + } + value = array; } - value = map; break; } } @@ -188,24 +222,78 @@ Property::Value Shader::GetDefaultPropertyCurrentValue(Property::Index index) co } Shader::Shader(const SceneGraph::Shader* sceneObject) -: Object(sceneObject), - mShaderData(nullptr) +: Object(sceneObject) { } -void Shader::SetShader(std::string_view vertexSource, - std::string_view fragmentSource, - Dali::Shader::Hint::Value hints) +void Shader::UpdateShaderData(std::string_view vertexSource, + std::string_view fragmentSource, + uint32_t renderPass, + Dali::Shader::Hint::Value hints) { // Try to load a pre-compiled shader binary for the source pair: - ThreadLocalStorage& tls = ThreadLocalStorage::Get(); - ShaderFactory& shaderFactory = tls.GetShaderFactory(); - size_t shaderHash; - mShaderData = shaderFactory.Load(vertexSource, fragmentSource, hints, shaderHash); + ThreadLocalStorage& tls = ThreadLocalStorage::Get(); + ShaderFactory& shaderFactory = tls.GetShaderFactory(); + size_t shaderHash; + Internal::ShaderDataPtr shaderData = shaderFactory.Load(vertexSource, fragmentSource, hints, renderPass, shaderHash); + + std::vector::iterator shaderDataIterator = std::find_if(mShaderDataList.begin(), mShaderDataList.end(), [&shaderData](const Internal::ShaderDataPtr& shaderDataItem) + { return shaderDataItem->GetRenderPass() == shaderData->GetRenderPass(); }); + if(shaderDataIterator != mShaderDataList.end()) + { + *shaderDataIterator = shaderData; + } + else + { + mShaderDataList.push_back(shaderData); + } // Add shader data to scene-object + SceneGraph::UpdateShaderDataMessage(GetEventThreadServices(), GetShaderSceneObject(), shaderData); +} - SceneGraph::SetShaderDataMessage(GetEventThreadServices(), GetShaderSceneObject(), mShaderData); +void Shader::SetShaderProperty(const Dali::Property::Value& shaderMap) +{ + if(shaderMap.GetType() == Property::MAP) + { + const Dali::Property::Map* map = shaderMap.GetMap(); + if(map) + { + std::string vertex; + std::string fragment; + uint32_t renderPass{0u}; + Dali::Shader::Hint::Value hints(Dali::Shader::Hint::NONE); + GetShaderData(*map, vertex, fragment, renderPass, hints); + + UpdateShaderData(vertex, fragment, renderPass, hints); + } + } + else if(shaderMap.GetType() == Property::ARRAY) + { + const Dali::Property::Array* array = shaderMap.GetArray(); + if(array) + { + uint32_t arraySize = array->Count(); + for(uint32_t i = 0; i < arraySize; ++i) + { + const Dali::Property::Map* map = array->GetElementAt(i).GetMap(); + if(map) + { + std::string vertex; + std::string fragment; + uint32_t renderPass{0u}; + Dali::Shader::Hint::Value hints(Dali::Shader::Hint::NONE); + GetShaderData(*map, vertex, fragment, renderPass, hints); + + UpdateShaderData(vertex, fragment, renderPass, hints); + } + } + } + } + else + { + DALI_LOG_ERROR("Shader program property should be a map or array of map.\n"); + } } Shader::~Shader() diff --git a/dali/internal/event/rendering/shader-impl.h b/dali/internal/event/rendering/shader-impl.h index 64008c5..76b4283 100644 --- a/dali/internal/event/rendering/shader-impl.h +++ b/dali/internal/event/rendering/shader-impl.h @@ -53,6 +53,11 @@ public: Dali::Shader::Hint::Value hints); /** + * @copydoc Dali::Shader::New() + */ + static ShaderPtr New(Dali::Property::Value shaderMap); + + /** * Retrieve the scene-graph shader added by this object. * @return A pointer to the shader. */ @@ -83,9 +88,22 @@ private: // implementation Shader(const SceneGraph::Shader* sceneObject); /** - * Second stage initialization + * @brief Update Shader Data + * If a ShaderData of the same renderPass is already exist, it is replaced, + * if not, new ShaderData is added. + * @param[in] vertexShader Vertex shader code for the effect. + * @param[in] fragmentShader Fragment Shader code for the effect. + * @param[in] renderPass render pass of shader data + * @param[in] hints Hints to define the geometry of the rendered object + */ + void UpdateShaderData(std::string_view vertexShader, std::string_view fragmentShader, uint32_t renderPass, Dali::Shader::Hint::Value hints); + + /** + * @brief Sets shader data from shaderMap. + * The shaderMap should be Property::Map or Property::Array. + * @param[in] shaderMap shader property map. */ - void SetShader(std::string_view vertexShader, std::string_view fragmentShader, Dali::Shader::Hint::Value hints); + void SetShaderProperty(const Dali::Property::Value& shaderMap); protected: /** @@ -94,12 +112,12 @@ protected: ~Shader() override; private: // unimplemented methods - Shader() = delete; - Shader(const Shader&) = delete; + Shader() = delete; + Shader(const Shader&) = delete; Shader& operator=(const Shader&) = delete; private: - Internal::ShaderDataPtr mShaderData; + std::vector mShaderDataList; public: /** diff --git a/dali/internal/render/common/render-instruction.h b/dali/internal/render/common/render-instruction.h index 52e7e68..c27f7f6 100644 --- a/dali/internal/render/common/render-instruction.h +++ b/dali/internal/render/common/render-instruction.h @@ -132,13 +132,14 @@ public: public: // Data Render::RenderTracker* mRenderTracker; ///< Pointer to an optional tracker object (not owned) - Viewport mViewport; ///< Optional viewport - Vector4 mClearColor; ///< Optional color to clear with - bool mIsViewportSet : 1; ///< Flag to determine whether the viewport is set - bool mIsClearColorSet : 1; ///< Flag to determine whether the clearColor is set - bool mIgnoreRenderToFbo : 1; ///< Whether to ignore the render to FBO option (used to measure the performance above 60 fps) + Viewport mViewport; ///< Optional viewport + Vector4 mClearColor; ///< Optional color to clear with + bool mIsViewportSet : 1; ///< Flag to determine whether the viewport is set + bool mIsClearColorSet : 1; ///< Flag to determine whether the clearColor is set + bool mIgnoreRenderToFbo : 1; ///< Whether to ignore the render to FBO option (used to measure the performance above 60 fps) Render::FrameBuffer* mFrameBuffer; + uint32_t mRenderPass{0u}; private: // Data Camera* mCamera; ///< camera that is used diff --git a/dali/internal/render/renderers/render-renderer.cpp b/dali/internal/render/renderers/render-renderer.cpp index d4d327c..032a6eb 100644 --- a/dali/internal/render/renderers/render-renderer.cpp +++ b/dali/internal/render/renderers/render-renderer.cpp @@ -518,7 +518,12 @@ bool Renderer::Render(Graphics::CommandBuffer& comma } // Create Program - ShaderDataPtr shaderData = mRenderDataProvider->GetShader().GetShaderData(); + ShaderDataPtr shaderData = mRenderDataProvider->GetShader().GetShaderData(instruction.mRenderPass); + if(!shaderData) + { + DALI_LOG_ERROR("Failed to get shader data.\n"); + return false; + } Program* program = Program::New(*mProgramCache, shaderData, diff --git a/dali/internal/render/shaders/render-shader.cpp b/dali/internal/render/shaders/render-shader.cpp index 3b26fd5..6fedd4d 100644 --- a/dali/internal/render/shaders/render-shader.cpp +++ b/dali/internal/render/shaders/render-shader.cpp @@ -31,25 +31,57 @@ namespace Internal { namespace SceneGraph { -Shader::Shader(Dali::Shader::Hint::Value& hints) -: mHints(hints) + +namespace { +static constexpr uint32_t DEFAULT_RENDER_PASS = 0u; } Shader::~Shader() { } -void Shader::SetShaderData(ShaderDataPtr shaderData) +void Shader::UpdateShaderData(ShaderDataPtr shaderData) { DALI_LOG_TRACE_METHOD_FMT(Debug::Filter::gShader, "%d\n", shaderData->GetHashValue()); - - mShaderData = shaderData; + std::vector::iterator shaderDataIterator = std::find_if(mShaderDataList.begin(), mShaderDataList.end(), [&shaderData](const Internal::ShaderDataPtr& shaderDataItem) + { return shaderDataItem->GetRenderPass() == shaderData->GetRenderPass(); }); + if(shaderDataIterator != mShaderDataList.end()) + { + *shaderDataIterator = shaderData; + } + else + { + mShaderDataList.push_back(shaderData); + } } -ShaderDataPtr Shader::GetShaderData() const +ShaderDataPtr Shader::GetShaderData(uint32_t renderPass) const { - return mShaderData; + if(mShaderDataList.empty()) + { + return nullptr; + } + + Internal::ShaderDataPtr returnShaderData = nullptr; + for(auto && shaderData : mShaderDataList) + { + if(shaderData->GetRenderPass() == renderPass) + { + return shaderData; + } + if(shaderData->GetRenderPass() == DEFAULT_RENDER_PASS) + { + returnShaderData = shaderData; + } + } + + if(returnShaderData) + { + return returnShaderData; + } + + return mShaderDataList.front(); } } // namespace SceneGraph diff --git a/dali/internal/render/shaders/render-shader.h b/dali/internal/render/shaders/render-shader.h index ff768e5..fbd3ea2 100644 --- a/dali/internal/render/shaders/render-shader.h +++ b/dali/internal/render/shaders/render-shader.h @@ -50,11 +50,7 @@ class SceneController; class Shader : public PropertyOwner { public: - /** - * Constructor - * @param hints Shader hints - */ - explicit Shader(Dali::Shader::Hint::Value& hints); + Shader() = default; /** * Virtual destructor @@ -62,38 +58,24 @@ public: ~Shader() override; /** - * Query whether a shader hint is set. - * - * @warning This method is called from Update Algorithms. - * - * @pre The shader has been initialized. - * @param[in] hint The hint to check. - * @return True if the given hint is set. - */ - [[nodiscard]] bool HintEnabled(Dali::Shader::Hint::Value hint) const - { - return mHints & hint; - } - - /** * @brief Set the shader data for this shader. * @param[in] shaderData The program's vertex/fragment source and optionally pre-compiled shader binary. */ - void SetShaderData(ShaderDataPtr shaderData); + void UpdateShaderData(ShaderDataPtr shaderData); /** * Get the shader data for this shader. * @return The shader data. */ - [[nodiscard]] ShaderDataPtr GetShaderData() const; + [[nodiscard]] ShaderDataPtr GetShaderData(uint32_t renderPass) const; private: // Data Dali::Shader::Hint::Value mHints; - ShaderDataPtr mShaderData; + std::vector mShaderDataList; }; -inline void SetShaderDataMessage(EventThreadServices& eventThreadServices, const Shader& shader, ShaderDataPtr shaderData) +inline void UpdateShaderDataMessage(EventThreadServices& eventThreadServices, const Shader& shader, ShaderDataPtr shaderData) { using LocalType = MessageValue1; @@ -101,7 +83,7 @@ inline void SetShaderDataMessage(EventThreadServices& eventThreadServices, const uint32_t* slot = eventThreadServices.ReserveMessageSlot(sizeof(LocalType)); // Construct message in the message queue memory; note that delete should not be called on the return value - new(slot) LocalType(&shader, &Shader::SetShaderData, shaderData); + new(slot) LocalType(&shader, &Shader::UpdateShaderData, shaderData); } } // namespace SceneGraph diff --git a/dali/internal/update/manager/render-instruction-processor.cpp b/dali/internal/update/manager/render-instruction-processor.cpp index e5f55f6..bd0b35c 100644 --- a/dali/internal/update/manager/render-instruction-processor.cpp +++ b/dali/internal/update/manager/render-instruction-processor.cpp @@ -178,6 +178,7 @@ inline bool SetNodeUpdateArea(Node* node, bool isLayer3d, Matrix& nodeWorldMatri /** * Add a renderer to the list * @param updateBufferIndex to read the model matrix from + * @param renderPass render pass for this render instruction * @param renderList to add the item to * @param renderable Node-Renderer pair * @param viewMatrix used to calculate modelview matrix for the item @@ -188,6 +189,7 @@ inline bool SetNodeUpdateArea(Node* node, bool isLayer3d, Matrix& nodeWorldMatri * @param cull Whether frustum culling is enabled or not */ inline void AddRendererToRenderList(BufferIndex updateBufferIndex, + uint32_t renderPass, RenderList& renderList, Renderable& renderable, const Matrix& viewMatrix, @@ -210,7 +212,7 @@ inline void AddRendererToRenderList(BufferIndex updateBufferIndex, // Don't cull items which have render callback bool hasRenderCallback = (renderable.mRenderer && renderable.mRenderer->GetRenderCallback()); - if(cull && renderable.mRenderer && (hasRenderCallback || (!renderable.mRenderer->GetShader().HintEnabled(Dali::Shader::Hint::MODIFIES_GEOMETRY) && node->GetClippingMode() == ClippingMode::DISABLED))) + if(cull && renderable.mRenderer && (hasRenderCallback || (renderable.mRenderer->GetShader().GetShaderData(renderPass) && !renderable.mRenderer->GetShader().GetShaderData(renderPass)->HintEnabled(Dali::Shader::Hint::MODIFIES_GEOMETRY) && node->GetClippingMode() == ClippingMode::DISABLED))) { const Vector4& boundingSphere = node->GetBoundingSphere(); inside = (boundingSphere.w > Math::MACHINE_EPSILON_1000) && @@ -252,7 +254,7 @@ inline void AddRendererToRenderList(BufferIndex updateBufferIndex, bool isOpaque = true; if(!hasRenderCallback) { - Renderer::OpacityType opacityType = renderable.mRenderer ? renderable.mRenderer->GetOpacityType(updateBufferIndex, *node) : Renderer::OPAQUE; + Renderer::OpacityType opacityType = renderable.mRenderer ? renderable.mRenderer->GetOpacityType(updateBufferIndex, renderPass, *node) : Renderer::OPAQUE; // We can skip render when node is not clipping and transparent skipRender = (opacityType == Renderer::TRANSPARENT && node->GetClippingMode() == ClippingMode::DISABLED); @@ -363,6 +365,7 @@ inline void AddRendererToRenderList(BufferIndex updateBufferIndex, /** * Add all renderers to the list * @param updateBufferIndex to read the model matrix from + * @param renderPass render pass for this render instruction * @param renderList to add the items to * @param renderers to render * NodeRendererContainer Node-Renderer pairs @@ -372,6 +375,7 @@ inline void AddRendererToRenderList(BufferIndex updateBufferIndex, * @param cull Whether frustum culling is enabled or not */ inline void AddRenderersToRenderList(BufferIndex updateBufferIndex, + uint32_t renderPass, RenderList& renderList, RenderableContainer& renderers, const Matrix& viewMatrix, @@ -386,6 +390,7 @@ inline void AddRenderersToRenderList(BufferIndex updateBufferIndex for(auto&& renderer : renderers) { AddRendererToRenderList(updateBufferIndex, + renderPass, renderList, renderer, viewMatrix, @@ -590,6 +595,7 @@ void RenderInstructionProcessor::Prepare(BufferIndex updateBuffe { renderList->SetHasColorRenderItems(true); AddRenderersToRenderList(updateBufferIndex, + instruction.mRenderPass, *renderList, renderables, viewMatrix, @@ -614,6 +620,7 @@ void RenderInstructionProcessor::Prepare(BufferIndex updateBuffe { renderList->SetHasColorRenderItems(false); AddRenderersToRenderList(updateBufferIndex, + instruction.mRenderPass, *renderList, renderables, viewMatrix, diff --git a/dali/internal/update/render-tasks/scene-graph-render-task.cpp b/dali/internal/update/render-tasks/scene-graph-render-task.cpp index 90dca93..ea5793e 100644 --- a/dali/internal/update/render-tasks/scene-graph-render-task.cpp +++ b/dali/internal/update/render-tasks/scene-graph-render-task.cpp @@ -400,6 +400,7 @@ RenderInstruction& RenderTask::PrepareRenderInstruction(BufferIndex updateBuffer mRenderInstruction[updateBufferIndex].mRenderTracker = nullptr; } + mRenderInstruction[updateBufferIndex].mRenderPass = mRenderPass; return mRenderInstruction[updateBufferIndex]; } @@ -478,6 +479,11 @@ void RenderTask::SetSyncRequired(bool requiresSync) mRequiresSync = requiresSync; } +void RenderTask::SetRenderPass(uint32_t renderPass) +{ + mRenderPass = renderPass; +} + void RenderTask::PropertyOwnerConnected(PropertyOwner& owner) { // check if we've gone from inactive to active diff --git a/dali/internal/update/render-tasks/scene-graph-render-task.h b/dali/internal/update/render-tasks/scene-graph-render-task.h index 0d5142c..a2336f3 100644 --- a/dali/internal/update/render-tasks/scene-graph-render-task.h +++ b/dali/internal/update/render-tasks/scene-graph-render-task.h @@ -354,6 +354,15 @@ public: return mRenderInstruction[updateBufferIndex]; } + /** + * Sets Render Pass key for this RenderTask. + * Shader code that matches this render pass is used for rendering. + * If no matching shader is found, the code with a render pass of 0 is used. + * In other cases, operation is not guaranteed. + * @param[in] renderPass RenderPass value for this render task. + */ + void SetRenderPass(uint32_t renderPass); + private: // from PropertyOwner::Observer /** * @copydoc PropertyOwner::Observer::PropertyOwnerConnected( PropertyOwner& owner ) @@ -379,7 +388,7 @@ private: RenderTask(); // Undefined - RenderTask(const RenderTask&) = delete; + RenderTask(const RenderTask&) = delete; RenderTask& operator=(const RenderTask&) = delete; public: // Animatable Properties @@ -398,11 +407,13 @@ private: RenderInstruction mRenderInstruction[2]; ///< Owned double buffered render instruction. (Double buffered because this owns render commands for the currently drawn frame) - uint32_t mRefreshRate; ///< REFRESH_ONCE, REFRESH_ALWAYS or render every N frames - uint32_t mFrameCounter; ///< counter for rendering every N frames - uint32_t mRenderedOnceCounter; ///< Incremented whenever state changes to RENDERED_ONCE_AND_NOTIFIED + uint32_t mRefreshRate; ///< REFRESH_ONCE, REFRESH_ALWAYS or render every N frames + uint32_t mFrameCounter; ///< counter for rendering every N frames + uint32_t mRenderedOnceCounter; ///< Incremented whenever state changes to RENDERED_ONCE_AND_NOTIFIED + + State mState; ///< Render state. - State mState; ///< Render state. + uint32_t mRenderPass{0u}; bool mRequiresSync : 1; ///< Whether sync is needed to track the render bool mActive : 1; ///< True when the task is active, i.e. has valid source and camera @@ -565,6 +576,17 @@ inline void BakeViewportSizeMessage(EventThreadServices& eventThreadServices, co new(slot) LocalType(&task, &RenderTask::BakeViewportSize, value); } +inline void SetRenderPassMessage(EventThreadServices& eventThreadServices, const RenderTask& task, uint32_t renderPass) +{ + using LocalType = MessageValue1; + + // Reserve some memory inside the message queue + uint32_t* slot = eventThreadServices.ReserveMessageSlot(sizeof(LocalType)); + + // Construct message in the message queue memory; note that delete should not be called on the return value + new(slot) LocalType(&task, &RenderTask::SetRenderPass, renderPass); +} + } // namespace SceneGraph } // namespace Internal diff --git a/dali/internal/update/rendering/scene-graph-renderer.cpp b/dali/internal/update/rendering/scene-graph-renderer.cpp index 89eb545..4d73b0d 100644 --- a/dali/internal/update/rendering/scene-graph-renderer.cpp +++ b/dali/internal/update/rendering/scene-graph-renderer.cpp @@ -589,7 +589,7 @@ Render::Renderer& Renderer::GetRenderer() return *mRenderer; } -Renderer::OpacityType Renderer::GetOpacityType(BufferIndex updateBufferIndex, const Node& node) const +Renderer::OpacityType Renderer::GetOpacityType(BufferIndex updateBufferIndex, uint32_t renderPass, const Node& node) const { Renderer::OpacityType opacityType = Renderer::OPAQUE; @@ -626,10 +626,13 @@ Renderer::OpacityType Renderer::GetOpacityType(BufferIndex updateBufferIndex, co break; } - bool shaderRequiresBlending(mShader->HintEnabled(Dali::Shader::Hint::OUTPUT_IS_TRANSPARENT)); - if(shaderRequiresBlending || (mTextureSet && mTextureSet->HasAlpha())) + if(mShader->GetShaderData(renderPass)) { - opacityType = Renderer::TRANSLUCENT; + bool shaderRequiresBlending(mShader->GetShaderData(renderPass)->HintEnabled(Dali::Shader::Hint::OUTPUT_IS_TRANSPARENT)); + if(shaderRequiresBlending || (mTextureSet && mTextureSet->HasAlpha())) + { + opacityType = Renderer::TRANSLUCENT; + } } // renderer should determine opacity using the actor color diff --git a/dali/internal/update/rendering/scene-graph-renderer.h b/dali/internal/update/rendering/scene-graph-renderer.h index 1e672a0..0bef132 100644 --- a/dali/internal/update/rendering/scene-graph-renderer.h +++ b/dali/internal/update/rendering/scene-graph-renderer.h @@ -352,9 +352,10 @@ public: /** * Query whether the renderer is fully opaque, fully transparent or transparent. * @param[in] updateBufferIndex The current update buffer index. + * @param[in] renderPass render pass for this render instruction * @return OPAQUE if fully opaque, TRANSPARENT if fully transparent and TRANSLUCENT if in between */ - OpacityType GetOpacityType(BufferIndex updateBufferIndex, const Node& node) const; + OpacityType GetOpacityType(BufferIndex updateBufferIndex, uint32_t renderPass, const Node& node) const; /** * Connect the object to the scene graph diff --git a/dali/public-api/render-tasks/render-task.cpp b/dali/public-api/render-tasks/render-task.cpp index e59bd82..4fdc74c 100644 --- a/dali/public-api/render-tasks/render-task.cpp +++ b/dali/public-api/render-tasks/render-task.cpp @@ -273,6 +273,16 @@ bool RenderTask::ViewportToLocal(Actor actor, float viewportX, float viewportY, } } +void RenderTask::SetRenderPass(uint32_t renderPass) +{ + GetImplementation(*this).SetRenderPass(renderPass); +} + +uint32_t RenderTask::GetRenderPass() const +{ + return GetImplementation(*this).GetRenderPass(); +} + RenderTask::RenderTask(Internal::RenderTask* internal) : Handle(internal) { diff --git a/dali/public-api/render-tasks/render-task.h b/dali/public-api/render-tasks/render-task.h index 0832c4b..52c6764 100644 --- a/dali/public-api/render-tasks/render-task.h +++ b/dali/public-api/render-tasks/render-task.h @@ -535,6 +535,21 @@ public: */ bool ViewportToLocal(Actor actor, float viewportX, float viewportY, float& localX, float& localY) const; + /** + * Sets Render Pass key for this RenderTask. + * Shader code that matches this render pass is used for rendering. + * If no matching shader is found, the code with a render pass of 0 is used. + * In other cases, operation is not guaranteed. + * @param[in] renderPass RenderPass value for this render task. + */ + void SetRenderPass(uint32_t renderPass); + + /** + * Gets Render Pass key for this RenderTask. + * @return RenderPass value for this render task. + */ + uint32_t GetRenderPass() const; + public: // Signals /** * @brief If the refresh rate is REFRESH_ONCE, connect to this signal to be notified when a RenderTask has finished. diff --git a/dali/public-api/rendering/shader.cpp b/dali/public-api/rendering/shader.cpp index 7456d9e..02b95ff 100644 --- a/dali/public-api/rendering/shader.cpp +++ b/dali/public-api/rendering/shader.cpp @@ -27,8 +27,14 @@ Shader Shader::New(std::string_view vertexShader, std::string_view fragmentShader, Hint::Value hints) { - Internal::ShaderPtr renderer = Internal::Shader::New(vertexShader, fragmentShader, hints); - return Shader(renderer.Get()); + Internal::ShaderPtr shader = Internal::Shader::New(vertexShader, fragmentShader, hints); + return Shader(shader.Get()); +} + +Shader Shader::New(Dali::Property::Value shaderMap) +{ + Internal::ShaderPtr shader = Internal::Shader::New(shaderMap); + return Shader(shader.Get()); } Shader::Shader() = default; diff --git a/dali/public-api/rendering/shader.h b/dali/public-api/rendering/shader.h index 19e138d..c60b92f 100644 --- a/dali/public-api/rendering/shader.h +++ b/dali/public-api/rendering/shader.h @@ -24,6 +24,7 @@ // INTERNAL INCLUDES #include // Dali::Handle #include // DEFAULT_DERIVED_HANDLE_PROPERTY_START_INDEX +#include /** * @brief DALI_COMPOSE_SHADER macro provides a convenient way to write shader source code. @@ -106,12 +107,13 @@ public: enum { /** - * @brief Name: "program", Type: MAP. + * @brief Name: "program", Type: MAP or ARRAY. * @note The default value is empty. - * @note Format: {"vertex":"","fragment":"",hints:""} + * @note It is Property::Map or Property::Array of the map. + * @note Format: {"renderPass":"", "vertex":"", "fragment":"", "hints":""} * @SINCE_1_1.43 */ - PROGRAM = DEFAULT_OBJECT_PROPERTY_START_INDEX + PROGRAM = DEFAULT_OBJECT_PROPERTY_START_INDEX, }; }; @@ -129,6 +131,17 @@ public: Hint::Value hints = Hint::NONE); /** + * @brief Creates Shader. + * + * @SINCE_2_2.31 + * @param[in] shaderMap Property::Map of shader data or Property::Array of Property::Map. + * Property::Map format is {"renderPass":"", "vertex":"", "fragment":"", "hints":""} + * shaderMap can be Property::Array of Property::Map for multi pass shading. + * @return A handle to a shader effect + */ + static Shader New(Dali::Property::Value shaderMap); + + /** * @brief Default constructor, creates an empty handle. * * @SINCE_1_1.43 -- 2.7.4