[Tizen] Fix Animation with EndAction::DISCARD dont reset properties. 39/312239/2
authorEunki, Hong <eunkiki.hong@samsung.com>
Tue, 4 Jun 2024 12:38:38 +0000 (21:38 +0900)
committerEunki, Hong <eunkiki.hong@samsung.com>
Mon, 10 Jun 2024 06:36:11 +0000 (15:36 +0900)
If Animation::EndAction::DISCARD finisehd normally cases,
The mDirtyFlag was not matched with real world

For example

ResetToBaseValue[0](mDirtyFlag become 1)
Animate and finished (mValue[0] changed.)
(update)
ResetToBaseValue[1](mDirtyFlag become 0)
(update)
(ResetToBaseValue did not called. So mValue[0] is last frame value)
(update)
(ResetToBaseValue did not called. So mValue[1] is BaseValue)

Now, mValue become flickering.

To avoid this problem, let we call ResetToBaseValue at least 2 frames
if finished animation's EndAction is DISCARD.
(Note that we don't consider Stop() call cases, since Stop() will not Animate
Animator, so mValue[0] is BaseValue)

And also, There was some issue that visual renderer property changeness not updated to
the dirty rect infomation.
(Since SG::Renderer don't be mark as updated)

To fix this issue, let we ensure to check the visual renderer property dirty.

And also, for apply Animation::EndAction::DISCARD case,
Let we make visual renderer coefficient use double buffered flags,
and age down every frames.

TODO : UpdateManager need to ResetBaseValue at least 2 frames
if Finished animation is EndAction::DISCARD.

This will need update manager side fix. So just keep this bug and fix as another patch.

Change-Id: Ibf654f723e1f986843cda620bc741b1121ee95d7
Signed-off-by: Eunki, Hong <eunkiki.hong@samsung.com>
automated-tests/src/dali/utc-Dali-Animation.cpp
automated-tests/src/dali/utc-Dali-VisualRenderer.cpp
dali/internal/update/common/animatable-property.h
dali/internal/update/common/renderer-resetter.h
dali/internal/update/manager/update-manager.cpp
dali/internal/update/rendering/scene-graph-renderer.cpp
dali/internal/update/rendering/scene-graph-renderer.h
dali/internal/update/rendering/scene-graph-visual-renderer-property.h
dali/internal/update/rendering/scene-graph-visual-renderer.cpp
dali/internal/update/rendering/scene-graph-visual-renderer.h

index 55ca82f..6adfc79 100644 (file)
@@ -890,8 +890,9 @@ int UtcDaliAnimationIsLoopingP(void)
   END_TEST;
 }
 
-int UtcDaliAnimationSetEndActionN(void)
+int UtcDaliAnimationSetEndActionP01(void)
 {
+  tet_infoline("Test Animation::EndAction with Transform\n");
   TestApplication application;
 
   Actor actor = Actor::New();
@@ -913,7 +914,8 @@ int UtcDaliAnimationSetEndActionN(void)
   animation.FinishedSignal().Connect(&application, finishCheck);
 
   application.SendNotification();
-  application.Render(static_cast<unsigned int>(durationSeconds * 1000.0f) + 1u /*just beyond the animation duration*/);
+  application.Render(static_cast<unsigned int>(durationSeconds * 500.0f));
+  application.Render(static_cast<unsigned int>(durationSeconds * 500.0f) + 1u /*just beyond the animation duration*/);
 
   // We did expect the animation to finish
   application.SendNotification();
@@ -926,6 +928,12 @@ int UtcDaliAnimationSetEndActionN(void)
   application.Render(0);
   DALI_TEST_EQUALS(Vector3::ZERO, actor.GetCurrentProperty<Vector3>(Actor::Property::POSITION), TEST_LOCATION);
 
+  application.SendNotification();
+  application.Render(0);
+  application.SendNotification();
+  application.Render(0);
+
+  tet_printf("Set EndAction::BAKE_FINAL\n");
   // Test BakeFinal, animate again, for half the duration
   finishCheck.Reset();
   animation.SetEndAction(Animation::BAKE_FINAL);
@@ -938,12 +946,14 @@ int UtcDaliAnimationSetEndActionN(void)
   // Stop the animation early
   animation.Stop();
 
+  tet_printf("EndAction::BAKE_FINAL Animation stopped\n");
   // We did NOT expect the animation to finish
   application.SendNotification();
   finishCheck.CheckSignalNotReceived();
   DALI_TEST_EQUALS(targetPosition * 0.5f, actor.GetCurrentProperty<Vector3>(Actor::Property::POSITION), VECTOR4_EPSILON, TEST_LOCATION);
 
   // The position should be same with target position in the next frame
+  tet_printf("Check current value return well\n");
   application.Render(0);
   DALI_TEST_EQUALS(targetPosition, actor.GetCurrentProperty<Vector3>(Actor::Property::POSITION), TEST_LOCATION);
 
@@ -953,6 +963,12 @@ int UtcDaliAnimationSetEndActionN(void)
   application.Render(0);
   DALI_TEST_EQUALS(Vector3::ZERO, actor.GetCurrentProperty<Vector3>(Actor::Property::POSITION), TEST_LOCATION);
 
+  application.SendNotification();
+  application.Render(0);
+  application.SendNotification();
+  application.Render(0);
+
+  tet_printf("Set EndAction::Discard\n");
   // Test EndAction::Discard, animate again, but don't bake this time
   finishCheck.Reset();
   animation.SetEndAction(Animation::DISCARD);
@@ -960,22 +976,154 @@ int UtcDaliAnimationSetEndActionN(void)
   animation.Play();
 
   application.SendNotification();
-  application.Render(static_cast<unsigned int>(durationSeconds * 1000.0f) + 1u /*just beyond the animation duration*/);
+  application.Render(static_cast<unsigned int>(durationSeconds * 500.0f));
+  application.Render(static_cast<unsigned int>(durationSeconds * 500.0f) + 1u /*just beyond the animation duration*/);
 
+  // Check whether we need to keep update at least 2 frames after discard-animation finished.
+  DALI_TEST_CHECK((application.GetUpdateStatus() & Integration::KeepUpdating::ANIMATIONS_RUNNING) != 0u);
+
+  tet_printf("EndAction::Discard Animation finished\n");
   // We did expect the animation to finish
   application.SendNotification();
   finishCheck.CheckSignalReceived();
   DALI_TEST_EQUALS(targetPosition, actor.GetCurrentProperty<Vector3>(Actor::Property::POSITION), TEST_LOCATION);
 
   // The position should be discarded in the next frame
+  // And also, check whether we need to keep update next frames after discard-animation finished.
+  tet_printf("Check current value return well\n");
   application.Render(0);
   DALI_TEST_EQUALS(Vector3::ZERO /*discarded*/, actor.GetCurrentProperty<Vector3>(Actor::Property::POSITION), TEST_LOCATION);
+  DALI_TEST_CHECK((application.GetUpdateStatus() & Integration::KeepUpdating::ANIMATIONS_RUNNING) != 0u);
 
   // Check that nothing has changed after a couple of buffer swaps
+  // After 2 frames rendered, UpdateStatus will not mark as animation runing.
   application.Render(0);
   DALI_TEST_EQUALS(Vector3::ZERO, actor.GetCurrentProperty<Vector3>(Actor::Property::POSITION), TEST_LOCATION);
+  DALI_TEST_CHECK((application.GetUpdateStatus() & Integration::KeepUpdating::ANIMATIONS_RUNNING) == 0u);
+
   application.Render(0);
   DALI_TEST_EQUALS(Vector3::ZERO, actor.GetCurrentProperty<Vector3>(Actor::Property::POSITION), TEST_LOCATION);
+  DALI_TEST_CHECK((application.GetUpdateStatus() & Integration::KeepUpdating::ANIMATIONS_RUNNING) == 0u);
+  END_TEST;
+}
+
+int UtcDaliAnimationSetEndActionP02(void)
+{
+  tet_infoline("Test Animation::EndAction with non-Transform\n");
+  TestApplication application;
+
+  Actor actor = Actor::New();
+  application.GetScene().Add(actor);
+
+  Vector4 initialColor(0.0f, 0.0f, 0.0f, 1.0f);
+  actor.SetProperty(Actor::Property::COLOR, initialColor);
+
+  // Build the animation
+  float     durationSeconds(1.0f);
+  Animation animation = Animation::New(durationSeconds);
+  DALI_TEST_CHECK(animation.GetEndAction() == Animation::BAKE);
+
+  Vector4 targetColor(1.0f, 1.0f, 1.0f, 1.0f);
+  animation.AnimateTo(Property(actor, Actor::Property::COLOR), targetColor, AlphaFunction::LINEAR);
+
+  // Start the animation
+  animation.Play();
+
+  bool                 signalReceived(false);
+  AnimationFinishCheck finishCheck(signalReceived);
+  animation.FinishedSignal().Connect(&application, finishCheck);
+
+  application.SendNotification();
+  application.Render(static_cast<unsigned int>(durationSeconds * 500.0f));
+  application.Render(static_cast<unsigned int>(durationSeconds * 500.0f) + 1u /*just beyond the animation duration*/);
+
+  // We did expect the animation to finish
+  application.SendNotification();
+  finishCheck.CheckSignalReceived();
+  DALI_TEST_EQUALS(targetColor, actor.GetCurrentProperty<Vector4>(Actor::Property::COLOR), TEST_LOCATION);
+
+  // Go back to the start
+  actor.SetProperty(Actor::Property::COLOR, initialColor);
+  application.SendNotification();
+  application.Render(0);
+  DALI_TEST_EQUALS(initialColor, actor.GetCurrentProperty<Vector4>(Actor::Property::COLOR), TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render(0);
+  application.SendNotification();
+  application.Render(0);
+
+  tet_printf("Set EndAction::BAKE_FINAL\n");
+  // Test BakeFinal, animate again, for half the duration
+  finishCheck.Reset();
+  animation.SetEndAction(Animation::BAKE_FINAL);
+  DALI_TEST_CHECK(animation.GetEndAction() == Animation::BAKE_FINAL);
+  animation.Play();
+
+  application.SendNotification();
+  application.Render(static_cast<unsigned int>(durationSeconds * 1000.0f * 0.5f) /*half of the animation duration*/);
+
+  // Stop the animation early
+  animation.Stop();
+
+  tet_printf("EndAction::BAKE_FINAL Animation stopped\n");
+  // We did NOT expect the animation to finish
+  application.SendNotification();
+  finishCheck.CheckSignalNotReceived();
+  DALI_TEST_EQUALS((initialColor + targetColor) * 0.5f, actor.GetCurrentProperty<Vector4>(Actor::Property::COLOR), VECTOR4_EPSILON, TEST_LOCATION);
+
+  // The position should be same with target position in the next frame
+  tet_printf("Check current value return well\n");
+  application.Render(0);
+  DALI_TEST_EQUALS(targetColor, actor.GetCurrentProperty<Vector4>(Actor::Property::COLOR), TEST_LOCATION);
+
+  // Go back to the start
+  actor.SetProperty(Actor::Property::COLOR, initialColor);
+  application.SendNotification();
+  application.Render(0);
+  DALI_TEST_EQUALS(initialColor, actor.GetCurrentProperty<Vector4>(Actor::Property::COLOR), TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render(0);
+  application.SendNotification();
+  application.Render(0);
+
+  tet_printf("Set EndAction::Discard\n");
+  // Test EndAction::Discard, animate again, but don't bake this time
+  finishCheck.Reset();
+  animation.SetEndAction(Animation::DISCARD);
+  DALI_TEST_CHECK(animation.GetEndAction() == Animation::DISCARD);
+  animation.Play();
+
+  application.SendNotification();
+  application.Render(static_cast<unsigned int>(durationSeconds * 500.0f));
+  application.Render(static_cast<unsigned int>(durationSeconds * 500.0f) + 1u /*just beyond the animation duration*/);
+
+  // Check whether we need to keep update at least 2 frames after discard-animation finished.
+  DALI_TEST_CHECK((application.GetUpdateStatus() & Integration::KeepUpdating::ANIMATIONS_RUNNING) != 0u);
+
+  tet_printf("EndAction::Discard Animation finished\n");
+  // We did expect the animation to finish
+  application.SendNotification();
+  finishCheck.CheckSignalReceived();
+  DALI_TEST_EQUALS(targetColor, actor.GetCurrentProperty<Vector4>(Actor::Property::COLOR), TEST_LOCATION);
+
+  // The position should be discarded in the next frame
+  // And also, check whether we need to keep update next frames after discard-animation finished.
+  tet_printf("Check current value return well\n");
+  application.Render(0);
+  DALI_TEST_EQUALS(initialColor /*discarded*/, actor.GetCurrentProperty<Vector4>(Actor::Property::COLOR), TEST_LOCATION);
+  DALI_TEST_CHECK((application.GetUpdateStatus() & Integration::KeepUpdating::ANIMATIONS_RUNNING) != 0u);
+
+  // Check that nothing has changed after a couple of buffer swaps
+  // After 2 frames rendered, UpdateStatus will not mark as animation runing.
+  application.Render(0);
+  DALI_TEST_EQUALS(initialColor, actor.GetCurrentProperty<Vector4>(Actor::Property::COLOR), TEST_LOCATION);
+  DALI_TEST_CHECK((application.GetUpdateStatus() & Integration::KeepUpdating::ANIMATIONS_RUNNING) == 0u);
+
+  application.Render(0);
+  DALI_TEST_EQUALS(initialColor, actor.GetCurrentProperty<Vector4>(Actor::Property::COLOR), TEST_LOCATION);
+  DALI_TEST_CHECK((application.GetUpdateStatus() & Integration::KeepUpdating::ANIMATIONS_RUNNING) == 0u);
   END_TEST;
 }
 
index c86b503..ebe18d0 100644 (file)
@@ -914,3 +914,462 @@ int UtcDaliVisualRendererPartialUpdate(void)
 
   END_TEST;
 }
+
+int UtcDaliVisualRendererPartialUpdate02(void)
+{
+  TestApplication application(
+    TestApplication::DEFAULT_SURFACE_WIDTH,
+    TestApplication::DEFAULT_SURFACE_HEIGHT,
+    TestApplication::DEFAULT_HORIZONTAL_DPI,
+    TestApplication::DEFAULT_VERTICAL_DPI,
+    true,
+    true);
+
+  tet_infoline("Test that partial update works well when actor has multiple renderer, and we change only actor's transform");
+
+  const TestGlAbstraction::ScissorParams& glScissorParams(application.GetGlAbstraction().GetScissorParams());
+
+  Shader         shader    = Shader::New("VertexSource", "FragmentSource");
+  Geometry       geometry  = CreateQuadGeometry();
+  VisualRenderer renderer1 = VisualRenderer::New(geometry, shader);
+  VisualRenderer renderer2 = VisualRenderer::New(geometry, shader);
+
+  Actor actor = Actor::New();
+  actor.AddRenderer(renderer1);
+  actor.AddRenderer(renderer2);
+  actor[Actor::Property::ANCHOR_POINT] = AnchorPoint::TOP_LEFT;
+  actor[Actor::Property::POSITION]     = Vector3(64.0f, 64.0f, 0.0f);
+  actor[Actor::Property::SIZE]         = Vector3(64.0f, 64.0f, 0.0f);
+  actor.SetResizePolicy(ResizePolicy::FIXED, Dimension::ALL_DIMENSIONS);
+  application.GetScene().Add(actor);
+
+  application.SendNotification();
+
+  std::vector<Rect<int>> damagedRects;
+
+  // Actor added, damaged rect is added size of actor
+  application.PreRenderWithPartialUpdate(TestApplication::RENDER_FRAME_INTERVAL, nullptr, damagedRects);
+  DALI_TEST_EQUALS(damagedRects.size(), 2, TEST_LOCATION);
+
+  // Set clippingRect as full surface now. TODO : Set valid rect if we can.
+  Rect<int> clippingRect = TestApplication::DEFAULT_SURFACE_RECT;
+
+  // Aligned by 16
+  DirtyRectChecker(damagedRects,
+                   {
+                     Rect<int>(64, 672, 80, 80),
+                     Rect<int>(64, 672, 80, 80),
+                   },
+                   true,
+                   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);
+
+  damagedRects.clear();
+  application.PreRenderWithPartialUpdate(TestApplication::RENDER_FRAME_INTERVAL, nullptr, damagedRects);
+  application.RenderWithPartialUpdate(damagedRects, clippingRect);
+
+  // Ensure the damaged rect is empty
+  DALI_TEST_EQUALS(damagedRects.size(), 0, TEST_LOCATION);
+
+  // Change the renderer1 and renderer2 transform property.
+  // To avoid useless float point error, make renderer size as 30x30
+  renderer1.SetProperty(VisualRenderer::Property::TRANSFORM_SIZE, Vector2(0.5f - 2.0f / 64.0f, 0.5f - 2.0f / 64.0f));
+  renderer1.SetProperty(VisualRenderer::Property::TRANSFORM_OFFSET, Vector2(-0.25f, -0.25f));
+  renderer2.SetProperty(VisualRenderer::Property::TRANSFORM_SIZE, Vector2(0.5f - 2.0f / 64.0f, 0.5f - 2.0f / 64.0f));
+  renderer2.SetProperty(VisualRenderer::Property::TRANSFORM_OFFSET, Vector2(0.25f, 0.25f));
+
+  // Now current actor show two 30x30 rectangle, one center position is (80, 80) and other is (112, 112).
+  // So, first rectangle's top left position is (65, 65), and seoncd rectangle's bottom right position is (127, 127).
+
+  application.SendNotification();
+  damagedRects.clear();
+  application.PreRenderWithPartialUpdate(TestApplication::RENDER_FRAME_INTERVAL, nullptr, damagedRects);
+  DALI_TEST_EQUALS(damagedRects.size(), 2, TEST_LOCATION);
+  // Aligned by 16
+  // Note, this damagedRect is combine of previous rect and current rect
+  DirtyRectChecker(damagedRects,
+                   {
+                     Rect<int>(64, 672, 80, 80),
+                     Rect<int>(64, 672, 80, 80),
+                   },
+                   true,
+                   TEST_LOCATION);
+
+  application.RenderWithPartialUpdate(damagedRects, clippingRect);
+
+  application.SendNotification();
+  damagedRects.clear();
+  application.PreRenderWithPartialUpdate(TestApplication::RENDER_FRAME_INTERVAL, nullptr, damagedRects);
+  application.RenderWithPartialUpdate(damagedRects, clippingRect);
+
+  // Update dummy property to damangeRect buffer aging
+  actor.SetProperty(Actor::Property::COLOR, Color::RED);
+
+  application.SendNotification();
+  damagedRects.clear();
+  application.PreRenderWithPartialUpdate(TestApplication::RENDER_FRAME_INTERVAL, nullptr, damagedRects);
+  application.RenderWithPartialUpdate(damagedRects, clippingRect);
+  DALI_TEST_EQUALS(damagedRects.size(), 2, TEST_LOCATION);
+
+  // Update dummy property to damangeRect buffer aging
+  actor.SetProperty(Actor::Property::COLOR, Color::BLUE);
+
+  application.SendNotification();
+  damagedRects.clear();
+  application.PreRenderWithPartialUpdate(TestApplication::RENDER_FRAME_INTERVAL, nullptr, damagedRects);
+  application.RenderWithPartialUpdate(damagedRects, clippingRect);
+  DALI_TEST_EQUALS(damagedRects.size(), 2, TEST_LOCATION);
+
+  // Aligned by 16
+  // Note, this damagedRect don't contain previous rect now.
+  // So, first rectangle's top left position is (65, 65), and seoncd rectangle's bottom right position is (127, 127).
+  DirtyRectChecker(damagedRects,
+                   {
+                     Rect<int>(64, 704, 32, 32),
+                     Rect<int>(96, 672, 32, 32),
+                   },
+                   true,
+                   TEST_LOCATION);
+
+  application.SendNotification();
+  damagedRects.clear();
+  application.PreRenderWithPartialUpdate(TestApplication::RENDER_FRAME_INTERVAL, nullptr, damagedRects);
+  application.RenderWithPartialUpdate(damagedRects, clippingRect);
+
+  application.SendNotification();
+  damagedRects.clear();
+  application.PreRenderWithPartialUpdate(TestApplication::RENDER_FRAME_INTERVAL, nullptr, damagedRects);
+  application.RenderWithPartialUpdate(damagedRects, clippingRect);
+
+  application.SendNotification();
+  damagedRects.clear();
+  application.PreRenderWithPartialUpdate(TestApplication::RENDER_FRAME_INTERVAL, nullptr, damagedRects);
+  application.RenderWithPartialUpdate(damagedRects, clippingRect);
+
+  // 3 frame spended after change actor property. Ensure the damaged rect is empty
+  DALI_TEST_EQUALS(damagedRects.size(), 0, TEST_LOCATION);
+
+  actor[Actor::Property::POSITION_Y] = 96.0f;
+  // Change the y position of actor.
+  // Now current actor show two 32x32 rectangle, one center position is (80, 96) and other is (112, 128).
+  // So, rectangle's top left position is (64, 80), and bottom right position is (128, 144).
+
+  application.SendNotification();
+  damagedRects.clear();
+  application.PreRenderWithPartialUpdate(TestApplication::RENDER_FRAME_INTERVAL, nullptr, damagedRects);
+  DALI_TEST_EQUALS(damagedRects.size(), 2, TEST_LOCATION);
+  // Aligned by 16
+  // Note, this damagedRect is combine of previous rect and current rect
+  DirtyRectChecker(damagedRects,
+                   {
+                     Rect<int>(64, 672, 32, 64),
+                     Rect<int>(96, 640, 32, 64),
+                   },
+                   true,
+                   TEST_LOCATION);
+
+  application.RenderWithPartialUpdate(damagedRects, clippingRect);
+
+  application.SendNotification();
+  damagedRects.clear();
+  application.PreRenderWithPartialUpdate(TestApplication::RENDER_FRAME_INTERVAL, nullptr, damagedRects);
+  application.RenderWithPartialUpdate(damagedRects, clippingRect);
+
+  // Update dummy property to damangeRect buffer aging
+  actor.SetProperty(Actor::Property::COLOR, Color::RED);
+
+  application.SendNotification();
+  damagedRects.clear();
+  application.PreRenderWithPartialUpdate(TestApplication::RENDER_FRAME_INTERVAL, nullptr, damagedRects);
+  application.RenderWithPartialUpdate(damagedRects, clippingRect);
+  DALI_TEST_EQUALS(damagedRects.size(), 2, TEST_LOCATION);
+
+  // Update dummy property to damangeRect buffer aging
+  actor.SetProperty(Actor::Property::COLOR, Color::BLUE);
+
+  application.SendNotification();
+  damagedRects.clear();
+  application.PreRenderWithPartialUpdate(TestApplication::RENDER_FRAME_INTERVAL, nullptr, damagedRects);
+  application.RenderWithPartialUpdate(damagedRects, clippingRect);
+  DALI_TEST_EQUALS(damagedRects.size(), 2, TEST_LOCATION);
+
+  // Aligned by 16
+  // Note, this damagedRect don't contain previous rect now.
+  // Current rectangle's top left position is (64, 80), and bottom right position is (128, 144).
+  DirtyRectChecker(damagedRects,
+                   {
+                     Rect<int>(64, 672, 32, 32),
+                     Rect<int>(96, 640, 32, 32),
+                   },
+                   true,
+                   TEST_LOCATION);
+
+  application.SendNotification();
+  damagedRects.clear();
+  application.PreRenderWithPartialUpdate(TestApplication::RENDER_FRAME_INTERVAL, nullptr, damagedRects);
+  application.RenderWithPartialUpdate(damagedRects, clippingRect);
+
+  application.SendNotification();
+  damagedRects.clear();
+  application.PreRenderWithPartialUpdate(TestApplication::RENDER_FRAME_INTERVAL, nullptr, damagedRects);
+  application.RenderWithPartialUpdate(damagedRects, clippingRect);
+
+  application.SendNotification();
+  damagedRects.clear();
+  application.PreRenderWithPartialUpdate(TestApplication::RENDER_FRAME_INTERVAL, nullptr, damagedRects);
+  application.RenderWithPartialUpdate(damagedRects, clippingRect);
+
+  // 3 frame spended after change actor property. Ensure the damaged rect is empty
+  DALI_TEST_EQUALS(damagedRects.size(), 0, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliVisualRendererPartialUpdate03(void)
+{
+  TestApplication application(
+    TestApplication::DEFAULT_SURFACE_WIDTH,
+    TestApplication::DEFAULT_SURFACE_HEIGHT,
+    TestApplication::DEFAULT_HORIZONTAL_DPI,
+    TestApplication::DEFAULT_VERTICAL_DPI,
+    true,
+    true);
+
+  tet_infoline("Test that partial update works well when we animate visual renderer's animated properties with EndAction::DISCARD");
+
+  const TestGlAbstraction::ScissorParams& glScissorParams(application.GetGlAbstraction().GetScissorParams());
+
+  Shader         shader   = Shader::New("VertexSource", "FragmentSource");
+  Geometry       geometry = CreateQuadGeometry();
+  VisualRenderer renderer = VisualRenderer::New(geometry, shader);
+
+  Actor actor = Actor::New();
+  actor.AddRenderer(renderer);
+  actor[Actor::Property::ANCHOR_POINT] = AnchorPoint::TOP_LEFT;
+  actor[Actor::Property::POSITION]     = Vector3(64.0f, 64.0f, 0.0f);
+  actor[Actor::Property::SIZE]         = Vector3(64.0f, 64.0f, 0.0f);
+  actor.SetResizePolicy(ResizePolicy::FIXED, Dimension::ALL_DIMENSIONS);
+  application.GetScene().Add(actor);
+
+  application.SendNotification();
+
+  std::vector<Rect<int>> damagedRects;
+
+  // Actor added, damaged rect is added size of actor
+  application.PreRenderWithPartialUpdate(TestApplication::RENDER_FRAME_INTERVAL, nullptr, damagedRects);
+  DALI_TEST_EQUALS(damagedRects.size(), 1, TEST_LOCATION);
+
+  // Aligned by 16
+  Rect<int> clippingRect = Rect<int>(64, 672, 80, 80); // in screen coordinates
+  DALI_TEST_EQUALS<Rect<int>>(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);
+
+  damagedRects.clear();
+  application.PreRenderWithPartialUpdate(TestApplication::RENDER_FRAME_INTERVAL, nullptr, damagedRects);
+  application.RenderWithPartialUpdate(damagedRects, clippingRect);
+
+  // Ensure the damaged rect is empty
+  DALI_TEST_EQUALS(damagedRects.size(), 0, TEST_LOCATION);
+
+  // Set clippingRect as full surface now. TODO : Set valid rect if we can.
+  clippingRect = TestApplication::DEFAULT_SURFACE_RECT;
+
+  Property::Index index = VisualRenderer::Property::TRANSFORM_OFFSET;
+
+  uint32_t  durationMilliseconds = 1000u;
+  Animation animation            = Animation::New(durationMilliseconds / 1000.0f);
+  animation.SetEndAction(Dali::Animation::EndAction::DISCARD); ///< Discard the animation when it ends.
+  animation.AnimateTo(Dali::Property(renderer, index), Vector2(1.0f, 1.0f));
+  animation.Play();
+
+  // Now current actor show as 64x64 rectangle, with center position (96, 96) + (64, 64) * time.
+
+  /// Progress 25%
+  /// Current actor show as 64x64 rectangle, with center position (112, 112).
+
+  application.SendNotification();
+  damagedRects.clear();
+  application.PreRenderWithPartialUpdate(durationMilliseconds / 4, nullptr, damagedRects);
+  DALI_TEST_EQUALS(damagedRects.size(), 1, TEST_LOCATION);
+  // Aligned by 16
+  // Note, this damagedRect is combine of previous rect and current rect
+  DALI_TEST_EQUALS<Rect<int>>(Rect<int>(64, 656, 96, 96), damagedRects[0], TEST_LOCATION);
+
+  application.RenderWithPartialUpdate(damagedRects, clippingRect);
+
+  application.SendNotification();
+  damagedRects.clear();
+  application.PreRenderWithPartialUpdate(0u, nullptr, damagedRects);
+  application.RenderWithPartialUpdate(damagedRects, clippingRect);
+
+  // Update dummy property to damangeRect buffer aging
+  actor.SetProperty(Actor::Property::COLOR, Color::RED);
+
+  application.SendNotification();
+  damagedRects.clear();
+  application.PreRenderWithPartialUpdate(0u, nullptr, damagedRects);
+  application.RenderWithPartialUpdate(damagedRects, clippingRect);
+  DALI_TEST_EQUALS(damagedRects.size(), 1, TEST_LOCATION);
+
+  // Update dummy property to damangeRect buffer aging
+  actor.SetProperty(Actor::Property::COLOR, Color::BLUE);
+
+  application.SendNotification();
+  damagedRects.clear();
+  application.PreRenderWithPartialUpdate(0u, nullptr, damagedRects);
+  application.RenderWithPartialUpdate(damagedRects, clippingRect);
+  DALI_TEST_EQUALS(damagedRects.size(), 1, TEST_LOCATION);
+
+  // Aligned by 16
+  // Note, this damagedRect don't contain previous rect now.
+  // Current rectangle's top left position is (80, 80), and bottom right position is (144, 144).
+  DALI_TEST_EQUALS<Rect<int>>(Rect<int>(80, 656, 80, 80), damagedRects[0], TEST_LOCATION);
+
+  // Update dummy property to damangeRect buffer aging
+  actor.SetProperty(Actor::Property::COLOR, Color::GREEN);
+
+  /// Progress 50%
+  /// Current actor show as 64x64 rectangle, with center position (128, 128).
+
+  application.SendNotification();
+  damagedRects.clear();
+  application.PreRenderWithPartialUpdate(durationMilliseconds / 4, nullptr, damagedRects);
+  DALI_TEST_EQUALS(damagedRects.size(), 1, TEST_LOCATION);
+  // Aligned by 16
+  // Note, this damagedRect is combine of previous rect and current rect
+  DALI_TEST_EQUALS<Rect<int>>(Rect<int>(80, 640, 96, 96), damagedRects[0], TEST_LOCATION);
+
+  application.RenderWithPartialUpdate(damagedRects, clippingRect);
+
+  application.SendNotification();
+  damagedRects.clear();
+  application.PreRenderWithPartialUpdate(0u, nullptr, damagedRects);
+  application.RenderWithPartialUpdate(damagedRects, clippingRect);
+
+  // Update dummy property to damangeRect buffer aging
+  actor.SetProperty(Actor::Property::COLOR, Color::RED);
+
+  application.SendNotification();
+  damagedRects.clear();
+  application.PreRenderWithPartialUpdate(0u, nullptr, damagedRects);
+  application.RenderWithPartialUpdate(damagedRects, clippingRect);
+  DALI_TEST_EQUALS(damagedRects.size(), 1, TEST_LOCATION);
+
+  // Update dummy property to damangeRect buffer aging
+  actor.SetProperty(Actor::Property::COLOR, Color::BLUE);
+
+  application.SendNotification();
+  damagedRects.clear();
+  application.PreRenderWithPartialUpdate(0u, nullptr, damagedRects);
+  application.RenderWithPartialUpdate(damagedRects, clippingRect);
+  DALI_TEST_EQUALS(damagedRects.size(), 1, TEST_LOCATION);
+
+  // Aligned by 16
+  // Note, this damagedRect don't contain previous rect now.
+  // Current rectangle's top left position is (96, 96), and bottom right position is (160, 160).
+  DALI_TEST_EQUALS<Rect<int>>(Rect<int>(96, 640, 80, 80), damagedRects[0], TEST_LOCATION);
+
+  // Update dummy property to damangeRect buffer aging
+  actor.SetProperty(Actor::Property::COLOR, Color::GREEN);
+
+  /// Progress 75%
+  /// Current actor show as 64x64 rectangle, with center position (144, 144).
+
+  application.SendNotification();
+  damagedRects.clear();
+  application.PreRenderWithPartialUpdate(durationMilliseconds / 4, nullptr, damagedRects);
+  DALI_TEST_EQUALS(damagedRects.size(), 1, TEST_LOCATION);
+  // Aligned by 16
+  // Note, this damagedRect is combine of previous rect and current rect
+  DALI_TEST_EQUALS<Rect<int>>(Rect<int>(96, 624, 96, 96), damagedRects[0], TEST_LOCATION);
+
+  application.RenderWithPartialUpdate(damagedRects, clippingRect);
+
+  application.SendNotification();
+  damagedRects.clear();
+  application.PreRenderWithPartialUpdate(0u, nullptr, damagedRects);
+  application.RenderWithPartialUpdate(damagedRects, clippingRect);
+
+  // Update dummy property to damangeRect buffer aging
+  actor.SetProperty(Actor::Property::COLOR, Color::RED);
+
+  application.SendNotification();
+  damagedRects.clear();
+  application.PreRenderWithPartialUpdate(0u, nullptr, damagedRects);
+  application.RenderWithPartialUpdate(damagedRects, clippingRect);
+  DALI_TEST_EQUALS(damagedRects.size(), 1, TEST_LOCATION);
+
+  // Update dummy property to damangeRect buffer aging
+  actor.SetProperty(Actor::Property::COLOR, Color::BLUE);
+
+  application.SendNotification();
+  damagedRects.clear();
+  application.PreRenderWithPartialUpdate(0u, nullptr, damagedRects);
+  application.RenderWithPartialUpdate(damagedRects, clippingRect);
+  DALI_TEST_EQUALS(damagedRects.size(), 1, TEST_LOCATION);
+
+  // Aligned by 16
+  // Note, this damagedRect don't contain previous rect now.
+  // Current rectangle's top left position is (112, 112), and bottom right position is (176, 176).
+  DALI_TEST_EQUALS<Rect<int>>(Rect<int>(112, 624, 80, 80), damagedRects[0], TEST_LOCATION);
+
+  // Update dummy property to damangeRect buffer aging
+  actor.SetProperty(Actor::Property::COLOR, Color::GREEN);
+
+  /// Progress 100%
+  /// Current actor show as 64x64 rectangle, with center position (96, 96).
+  /// Note. Animation end action is DISCARD.
+
+  application.SendNotification();
+  damagedRects.clear();
+  application.PreRenderWithPartialUpdate(durationMilliseconds / 4 + 1u /* Over the animation */, nullptr, damagedRects);
+  DALI_TEST_EQUALS(damagedRects.size(), 1, TEST_LOCATION);
+  // Aligned by 16
+  // Note, this damagedRect is combine of previous rect and current rect
+  DALI_TEST_EQUALS<Rect<int>>(Rect<int>(112, 608, 96, 96), damagedRects[0], TEST_LOCATION);
+
+  application.RenderWithPartialUpdate(damagedRects, clippingRect);
+
+  application.SendNotification();
+  damagedRects.clear();
+  application.PreRenderWithPartialUpdate(0u, nullptr, damagedRects);
+  application.RenderWithPartialUpdate(damagedRects, clippingRect);
+
+  // Update dummy property to damangeRect buffer aging
+  actor.SetProperty(Actor::Property::COLOR, Color::RED);
+
+  application.SendNotification();
+  damagedRects.clear();
+  application.PreRenderWithPartialUpdate(0u, nullptr, damagedRects);
+  application.RenderWithPartialUpdate(damagedRects, clippingRect);
+  DALI_TEST_EQUALS(damagedRects.size(), 1, TEST_LOCATION);
+
+  // Update dummy property to damangeRect buffer aging
+  actor.SetProperty(Actor::Property::COLOR, Color::BLUE);
+
+  application.SendNotification();
+  damagedRects.clear();
+  application.PreRenderWithPartialUpdate(0u, nullptr, damagedRects);
+  application.RenderWithPartialUpdate(damagedRects, clippingRect);
+  DALI_TEST_EQUALS(damagedRects.size(), 1, TEST_LOCATION);
+
+  // Aligned by 16
+  // Note, this damagedRect don't contain previous rect now.
+  // Current rectangle's top left position is (64, 64), and bottom right position is (128, 128).
+  DALI_TEST_EQUALS<Rect<int>>(Rect<int>(64, 672, 80, 80), damagedRects[0], TEST_LOCATION);
+
+  // Update dummy property to damangeRect buffer aging
+  actor.SetProperty(Actor::Property::COLOR, Color::GREEN);
+
+  END_TEST;
+}
index c2bb6fe..a5471ed 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_INTERNAL_SCENE_GRAPH_ANIMATABLE_PROPERTY_H
 
 /*
- * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 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.
@@ -81,7 +81,7 @@ protected: // for derived classes
    */
   virtual void OnSet()
   {
-    mDirtyFlags = SET_FLAG;
+    mDirtyFlags |= SET_FLAG;
   }
 
   /**
@@ -89,7 +89,7 @@ protected: // for derived classes
    */
   virtual void OnBake()
   {
-    mDirtyFlags = BAKED_FLAG;
+    mDirtyFlags |= BAKED_FLAG;
   }
 
 public:
@@ -98,7 +98,7 @@ public:
    */
   void MarkAsDirty()
   {
-    mDirtyFlags = RESET_FLAG;
+    mDirtyFlags |= RESET_FLAG;
   }
 
 public: // From PropertyBase
@@ -118,8 +118,8 @@ public: // From PropertyBase
     return true; // Animatable properties are always valid
   }
 
-protected:              // so that ResetToBaseValue can set it directly
-  uint32_t mDirtyFlags; ///< Flag whether value changed during previous 2 frames
+protected:                 // so that ResetToBaseValue can set it directly
+  uint8_t mDirtyFlags : 2; ///< Flag whether value changed during previous 2 frames
 };
 
 /**
index 2883440..fae6dc5 100644 (file)
@@ -148,7 +148,6 @@ protected:
   : mRenderer(renderer),
     mActive(AGING) // Since we make this resetter only initialize case now.
   {
-    mRenderer->MarkAsDirty();
   }
 
   Renderer* mRenderer; ///< The renderer that owns the properties
index 130ed26..d279c55 100644 (file)
@@ -195,6 +195,7 @@ struct UpdateManager::Impl
     nodeDirtyFlags(NodePropertyFlags::TRANSFORM), // set to TransformFlag to ensure full update the first time through Update()
     frameCounter(0),
     renderingBehavior(DevelStage::Rendering::IF_REQUIRED),
+    discardAnimationFinishedAge(0u),
     animationFinishedDuringUpdate(false),
     previousUpdateScene(false),
     renderTaskWaiting(false),
@@ -317,11 +318,13 @@ struct UpdateManager::Impl
   uint32_t                 frameCounter;      ///< Frame counter used in debugging to choose which frame to debug and which to ignore.
   DevelStage::Rendering    renderingBehavior; ///< Set via DevelStage::SetRenderingBehavior
 
-  bool animationFinishedDuringUpdate; ///< Flag whether any animations finished during the Update()
-  bool previousUpdateScene;           ///< True if the scene was updated in the previous frame (otherwise it was optimized out)
-  bool renderTaskWaiting;             ///< A REFRESH_ONCE render task is waiting to be rendered
-  bool renderersAdded;                ///< Flag to keep track when renderers have been added to avoid unnecessary processing
-  bool renderingRequired;             ///< True if required to render the current frame
+  uint8_t discardAnimationFinishedAge : 2; ///< Age of EndAction::Discard animation Stop/Finished. It will make we call ResetToBaseValue at least 2 frames.
+
+  bool animationFinishedDuringUpdate : 1; ///< Flag whether any animations finished during the Update()
+  bool previousUpdateScene : 1;           ///< True if the scene was updated in the previous frame (otherwise it was optimized out)
+  bool renderTaskWaiting : 1;             ///< A REFRESH_ONCE render task is waiting to be rendered
+  bool renderersAdded : 1;                ///< Flag to keep track when renderers have been added to avoid unnecessary processing
+  bool renderingRequired : 1;             ///< True if required to render the current frame
 
 private:
   Impl(const Impl&);            ///< Undefined
@@ -772,6 +775,9 @@ void UpdateManager::ResetProperties(BufferIndex bufferIndex)
   // Clear the "animations finished" flag; This should be set if any (previously playing) animation is stopped
   mImpl->animationFinishedDuringUpdate = false;
 
+  // Age down discard animations.
+  mImpl->discardAnimationFinishedAge >>= 1;
+
   if(mImpl->nodeResetters.Count() > 0u)
   {
     // Reset node properties
@@ -873,6 +879,12 @@ bool UpdateManager::Animate(BufferIndex bufferIndex, float elapsedSeconds)
 
     mImpl->animationFinishedDuringUpdate = mImpl->animationFinishedDuringUpdate || finished;
 
+    // Check whether finished animation is Discard type. If then, we should update scene at least 2 frames.
+    if(finished && animation->GetEndAction() == Animation::EndAction::DISCARD)
+    {
+      mImpl->discardAnimationFinishedAge |= 2u;
+    }
+
     // queue the notification on finished or stopped
     if(finished || stopped)
     {
@@ -1107,6 +1119,7 @@ uint32_t UpdateManager::Update(float    elapsedSeconds,
     isAnimationRunning ||                              // ..at least one animation is running OR
     mImpl->messageQueue.IsSceneUpdateRequired() ||     // ..a message that modifies the scene graph node tree is queued OR
     mImpl->frameCallbackProcessor ||                   // ..a frame callback processor is existed OR
+    mImpl->discardAnimationFinishedAge > 0u ||         // ..at least one animation with EndAction::DISCARD finished
     gestureUpdated;                                    // ..a gesture property was updated
 
   uint32_t keepUpdating          = 0;
@@ -1378,7 +1391,7 @@ uint32_t UpdateManager::KeepUpdatingCheck(float elapsedSeconds) const
 
   // If the rendering behavior is set to continuously render, then continue to render.
   // Keep updating until no messages are received and no animations are running.
-  // If an animation has just finished, update at least once more for Discard end-actions.
+  // If an animation has just finished, update at least two frames more for Discard end-actions.
   // No need to check for renderQueue as there is always a render after update and if that
   // render needs another update it will tell the adaptor to call update again
 
@@ -1388,7 +1401,8 @@ uint32_t UpdateManager::KeepUpdatingCheck(float elapsedSeconds) const
   }
 
   if(IsAnimationRunning() ||
-     mImpl->animationFinishedDuringUpdate)
+     mImpl->animationFinishedDuringUpdate ||
+     mImpl->discardAnimationFinishedAge > 0u)
   {
     keepUpdatingRequest |= KeepUpdating::ANIMATIONS_RUNNING;
   }
index 1d404aa..3fbc62d 100644 (file)
@@ -326,6 +326,12 @@ bool Renderer::PrepareRender(BufferIndex updateBufferIndex)
     mResendFlag = 0;
   }
 
+  // Age down visual properties dirty flag
+  if(mVisualProperties)
+  {
+    rendererUpdated |= mVisualProperties->PrepareProperties();
+  }
+
   // Ensure collected map is up to date
   UpdateUniformMap(updateBufferIndex);
 
@@ -829,15 +835,6 @@ void Renderer::ResetToBaseValues(BufferIndex updateBufferIndex)
   }
 }
 
-void Renderer::MarkAsDirty()
-{
-  mOpacity.MarkAsDirty();
-  if(mVisualProperties)
-  {
-    mVisualProperties->MarkAsDirty();
-  }
-}
-
 uint32_t Renderer::GetMemoryPoolCapacity()
 {
   return GetRendererMemoryPool().GetCapacity();
@@ -861,7 +858,12 @@ const CollectedUniformMap& Renderer::GetCollectedUniformMap() const
 
 bool Renderer::IsUpdated() const
 {
-  if(Updated() || (mShader && mShader->Updated()))
+  // We should check Whether
+  // 1. Renderer itself's property changed
+  // 2. Renderer's opacity changed
+  // 3. Shader's propperties are changed
+  // 4. Visual properties are changed
+  if(IsDirty() || (mShader && mShader->Updated()) || (mVisualProperties && mVisualProperties->Updated()))
   {
     return true;
   }
index 5dabf05..a6ad912 100644 (file)
@@ -495,11 +495,6 @@ public:
   void ResetToBaseValues(BufferIndex updateBufferIndex);
 
   /**
-   * @brief Mark all animatable properties as dirty.
-   */
-  void MarkAsDirty();
-
-  /**
    * Get the capacity of the memory pools
    * @return the capacity of the memory pools
    */
index 5360173..7fc74a6 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_INTERNAL_SCENE_GRAPH_VISUAL_RENDERER_PROPERTY_H
 
 /*
- * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -30,35 +30,66 @@ namespace Dali::Internal::SceneGraph::VisualRenderer
 struct VisualRendererCoefficientCacheBase
 {
   VisualRendererCoefficientCacheBase()
-  : mUpdated(true)
+  : mUpdatedFlag(Dali::Internal::SceneGraph::BAKED_FLAG),
+    mUpdateCurrentFrame(true),
+    mCoefficientCalculated(false)
   {
   }
 
   virtual ~VisualRendererCoefficientCacheBase() = default;
 
   /**
-   * @brief Check whether this cache need to be update.
-   * After call this API, update flag will be reset.
+   * @brief Check whether this cache is updated or not.
    *
-   * @return True if this coefficient updated. False otherwise.
    */
-  bool IsUpdated()
+  bool IsUpdated() const
   {
-    bool ret = mUpdated;
-    mUpdated = false;
-    return ret;
+    return mUpdateCurrentFrame;
   }
 
   /**
    * @brief Mark update flag as true.
+   * @param[in] bake Whether this coefficient updated by OnBake API, or not.
    */
-  void Update()
+  void Update(bool bake)
   {
-    mUpdated = true;
+    mUpdateCurrentFrame = true;
+    mUpdatedFlag |= bake ? Dali::Internal::SceneGraph::BAKED_FLAG : Dali::Internal::SceneGraph::SET_FLAG;
+  }
+
+  /**
+   * @brief Get whether we already calculate coefficient at this frame or not.
+   * @return True if we already calculate coefficient at this frame, false otherwise.
+   */
+  bool IsCoefficientCalculated() const
+  {
+    return mCoefficientCalculated;
+  }
+
+  /**
+   * @brief Mark as we calculate coefficient already at this frame.
+   */
+  void MarkCoefficientCalculated()
+  {
+    mCoefficientCalculated = true;
+  }
+
+  /**
+   * @brief Reset update flag.
+   */
+  void ResetFlag()
+  {
+    mUpdateCurrentFrame    = (mUpdatedFlag != Dali::Internal::SceneGraph::CLEAN_FLAG); ///< Keep the flag whether it was updated or not.
+    mCoefficientCalculated = (!mUpdateCurrentFrame);                                   ///< Re-calculate coefficient only if previous update flag was not clean.
+
+    mUpdatedFlag >>= 1;
   }
 
 private:
-  bool mUpdated; ///< Updated flag for this coefficient cache.
+  uint8_t mUpdatedFlag : 2; ///< Updated flag for this coefficient cache.
+
+  bool mUpdateCurrentFrame : 1;    ///< Whether we need to update this frame or not.
+  bool mCoefficientCalculated : 1; ///< Whether we need to re-calculate coefficient or not.
 };
 
 /**
@@ -105,7 +136,7 @@ public:
    */
   void OnSet() override
   {
-    GetCacheBaseData()->Update();
+    GetCacheBaseData()->Update(false);
     AnimatablePropertyBase::OnSet();
   }
 
@@ -114,7 +145,7 @@ public:
    */
   void OnBake() override
   {
-    GetCacheBaseData()->Update();
+    GetCacheBaseData()->Update(true);
     AnimatablePropertyBase::OnBake();
   }
 };
index 560fd25..904bb2c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 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.
@@ -45,21 +45,9 @@ void AnimatableVisualProperties::ResetToBaseValues(BufferIndex updateBufferIndex
   }
 }
 
-void AnimatableVisualProperties::MarkAsDirty()
+bool AnimatableVisualProperties::Updated() const
 {
-  mTransformOffset.MarkAsDirty();
-  mTransformSize.MarkAsDirty();
-  mTransformOrigin.MarkAsDirty();
-  mTransformAnchorPoint.MarkAsDirty();
-  mTransformOffsetSizeMode.MarkAsDirty();
-  mExtraSize.MarkAsDirty();
-  mMixColor.MarkAsDirty();
-  mPreMultipliedAlpha.MarkAsDirty();
-  if(mExtendedProperties)
-  {
-    auto* decoratedVisualProperties = static_cast<VisualRenderer::AnimatableDecoratedVisualProperties*>(mExtendedProperties);
-    decoratedVisualProperties->MarkAsDirty();
-  }
+  return mCoefficient.IsUpdated() || (mExtendedProperties && static_cast<VisualRenderer::AnimatableDecoratedVisualProperties*>(mExtendedProperties)->Updated());
 }
 
 Vector4 AnimatableVisualProperties::GetVisualTransformedUpdateArea(BufferIndex updateBufferIndex, const Vector4& originalUpdateArea) noexcept
@@ -67,7 +55,7 @@ Vector4 AnimatableVisualProperties::GetVisualTransformedUpdateArea(BufferIndex u
   auto& coefficient = mCoefficient;
 
   // Recalculate only if coefficient need to be updated.
-  if(coefficient.IsUpdated())
+  if(!coefficient.IsCoefficientCalculated())
   {
     // VisualProperty
     const Vector2 transformOffset         = mTransformOffset.Get(updateBufferIndex);
@@ -119,6 +107,8 @@ Vector4 AnimatableVisualProperties::GetVisualTransformedUpdateArea(BufferIndex u
     coefficient.coefXB = coefficient.coefXA * transformAnchorPoint + transformOffset * Vector2(1.0f - transformOffsetSizeMode.x, 1.0f - transformOffsetSizeMode.y) + transformOrigin;
     coefficient.coefCA = transformSize * Vector2(transformOffsetSizeMode.z, transformOffsetSizeMode.w) + extraSize;
     coefficient.coefCB = coefficient.coefCA * transformAnchorPoint + transformOffset * Vector2(transformOffsetSizeMode.x, transformOffsetSizeMode.y);
+
+    coefficient.MarkCoefficientCalculated();
   }
 
   float coefD = 0.0f; ///< Default as 0.0f when we don't use decorated renderer.
@@ -130,7 +120,7 @@ Vector4 AnimatableVisualProperties::GetVisualTransformedUpdateArea(BufferIndex u
     auto& decoratedCoefficient = decoratedVisualProperties->mCoefficient;
 
     // Recalculate only if coefficient need to be updated.
-    if(decoratedCoefficient.IsUpdated())
+    if(!decoratedCoefficient.IsCoefficientCalculated())
     {
       // DecoratedVisualProperty
       const float borderlineWidth  = decoratedVisualProperties->mBorderlineWidth.Get(updateBufferIndex);
@@ -147,6 +137,8 @@ Vector4 AnimatableVisualProperties::GetVisualTransformedUpdateArea(BufferIndex u
       // D coefficients be used only decoratedVisual.
       // It can be calculated parallely with visual transform.
       decoratedCoefficient.coefD = std::max((1.0f + Dali::Clamp(borderlineOffset, -1.0f, 1.0f)) * borderlineWidth, 2.0f * blurRadius) + extraPadding;
+
+      decoratedCoefficient.MarkCoefficientCalculated();
     }
 
     // Update coefD so we can use this value out of this scope.
@@ -185,6 +177,20 @@ Vector4 AnimatableVisualProperties::GetVisualTransformedUpdateArea(BufferIndex u
   return resultArea;
 }
 
+bool AnimatableVisualProperties::PrepareProperties()
+{
+  bool rendererUpdated = mCoefficient.IsUpdated();
+  mCoefficient.ResetFlag();
+
+  if(mExtendedProperties)
+  {
+    auto* decoratedVisualProperties = static_cast<VisualRenderer::AnimatableDecoratedVisualProperties*>(mExtendedProperties);
+    rendererUpdated |= (decoratedVisualProperties->PrepareProperties());
+  }
+
+  return rendererUpdated;
+}
+
 void AnimatableDecoratedVisualProperties::ResetToBaseValues(BufferIndex updateBufferIndex)
 {
   mCornerRadius.ResetToBaseValue(updateBufferIndex);
@@ -195,14 +201,16 @@ void AnimatableDecoratedVisualProperties::ResetToBaseValues(BufferIndex updateBu
   mBlurRadius.ResetToBaseValue(updateBufferIndex);
 }
 
-void AnimatableDecoratedVisualProperties::MarkAsDirty()
+bool AnimatableDecoratedVisualProperties::Updated() const
+{
+  return mCoefficient.IsUpdated();
+}
+
+bool AnimatableDecoratedVisualProperties::PrepareProperties()
 {
-  mCornerRadius.MarkAsDirty();
-  mCornerRadiusPolicy.MarkAsDirty();
-  mBorderlineWidth.MarkAsDirty();
-  mBorderlineColor.MarkAsDirty();
-  mBorderlineOffset.MarkAsDirty();
-  mBlurRadius.MarkAsDirty();
+  bool rendererUpdated = mCoefficient.IsUpdated();
+  mCoefficient.ResetFlag();
+  return rendererUpdated;
 }
 
 } // namespace VisualRenderer
index c90c735..02244c7 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_INTERNAL_SCENE_GRAPH_VISUAL_RENDERER_H
 
 /*
- * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 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.
@@ -56,9 +56,9 @@ public: // Public API
   void ResetToBaseValues(BufferIndex updateBufferIndex);
 
   /**
-   * @copydoc Dali::Internal::SceneGraph::Renderer::MarkAsDirty
+   * @copydoc Dali::Internal::SceneGraph::Renderer::Updated
    */
-  void MarkAsDirty();
+  bool Updated() const;
 
   /**
    * @copydoc RenderDataProvider::GetVisualTransformedUpdateArea()
@@ -67,6 +67,14 @@ public: // Public API
 
 public:
   /**
+   * @brief Prepare properties and ready to render sequence
+   *
+   * @return True if we need to render this frame.
+   */
+  bool PrepareProperties();
+
+public:
+  /**
    * @brief Cached coefficient value when we calculate visual transformed update size.
    * It can reduce complexity of calculate the vertex position.
    *
@@ -140,9 +148,17 @@ public: // Public API
   void ResetToBaseValues(BufferIndex updateBufferIndex);
 
   /**
-   * @copydoc Dali::Internal::SceneGraph::Renderer::MarkAsDirty
+   * @copydoc Dali::Internal::SceneGraph::Renderer::Updated
+   */
+  bool Updated() const;
+
+public:
+  /**
+   * @brief Prepare properties and ready to render sequence
+   *
+   * @return True if we need to render this frame.
    */
-  void MarkAsDirty();
+  bool PrepareProperties();
 
 public:
   /**