Ensure cleared animation not emit finished signal during finished signal emit 21/312021/1
authorEunki, Hong <eunkiki.hong@samsung.com>
Mon, 3 Jun 2024 01:51:16 +0000 (10:51 +0900)
committerEunki, Hong <eunkiki.hong@samsung.com>
Mon, 3 Jun 2024 03:13:40 +0000 (12:13 +0900)
Since we collect all finished animations and emit after,
It is possible that finisehd signal emitted what we should not
(e.g. Animation::Clear() called during another animation's finished signal)

To avoid it, let we ensure to check whether given animation is ignored or not.

Change-Id: I681c2c8e0f1624e0846409eb862862e5392ab7ec
Signed-off-by: Eunki, Hong <eunkiki.hong@samsung.com>
automated-tests/src/dali/utc-Dali-Animation.cpp
dali/internal/event/animation/animation-playlist.cpp

index 50c93ed4814ce488d48195c72a52c7d52cdeebf5..7293256053fee25d106522c791bb9459d69335cf 100644 (file)
@@ -54,6 +54,7 @@ struct AnimationFinishCheck
 
   void operator()(Animation& animation)
   {
+    tet_printf("emitted animation [%u]\n", animation.GetAnimationId());
     mSignalReceived = true;
   }
 
@@ -16027,6 +16028,8 @@ int UtcDaliAnimationReferenceCountCheck01(void)
   AnimationFinishCheck finishCheck(signalReceived);
   animation.FinishedSignal().Connect(&application, finishCheck);
 
+  animation.Play();
+  animation.Play();
   animation.Play();
 
   application.SendNotification();
@@ -16077,6 +16080,8 @@ int UtcDaliAnimationReferenceCountCheck02(void)
   AnimationFinishCheck finishCheck(signalReceived);
   animation.FinishedSignal().Connect(&application, finishCheck);
 
+  animation.Play();
+  animation.Play();
   animation.Play();
 
   application.SendNotification();
@@ -16130,6 +16135,8 @@ int UtcDaliAnimationReferenceCountCheck03(void)
   AnimationFinishCheck finishCheck(signalReceived);
   animation.FinishedSignal().Connect(&application, finishCheck);
 
+  animation.Play();
+  animation.Play();
   animation.Play();
 
   application.SendNotification();
@@ -16163,5 +16170,141 @@ int UtcDaliAnimationReferenceCountCheck03(void)
   animationCount = Dali::DevelAnimation::GetAnimationCount();
   DALI_TEST_EQUALS(animationCount, 0, TEST_LOCATION);
 
+  END_TEST;
+}
+
+namespace
+{
+// Functor to test clear another animation during animation finished signal.
+struct AnimationClearCheck
+{
+  AnimationClearCheck(bool& signalReceived)
+  : mSignalReceived(signalReceived),
+    mClearRequiredAnimations()
+  {
+  }
+
+  void AddClearAnimation(Animation animation)
+  {
+    mClearRequiredAnimations.emplace_back(animation);
+    tet_printf("Add clear animation [%u], clear?[%zu]\n", animation.GetAnimationId(), mClearRequiredAnimations.size());
+  }
+
+  void operator()(Animation& animation)
+  {
+    tet_printf("emitted animation [%u], clear?[%zu]\n", animation.GetAnimationId(), mClearRequiredAnimations.size());
+    mSignalReceived = true;
+    for(auto clearRequiredAnimation : mClearRequiredAnimations)
+    {
+      if(clearRequiredAnimation)
+      {
+        tet_printf("clear animation [%u]\n", clearRequiredAnimation.GetAnimationId());
+        clearRequiredAnimation.Clear();
+      }
+    }
+  }
+
+  void Reset()
+  {
+    mSignalReceived = false;
+  }
+
+  void CheckSignalReceived()
+  {
+    if(!mSignalReceived)
+    {
+      tet_printf("Expected Finish signal was not received\n");
+      tet_result(TET_FAIL);
+    }
+    else
+    {
+      tet_result(TET_PASS);
+    }
+  }
+
+  void CheckSignalNotReceived()
+  {
+    if(mSignalReceived)
+    {
+      tet_printf("Unexpected Finish signal was received\n");
+      tet_result(TET_FAIL);
+    }
+    else
+    {
+      tet_result(TET_PASS);
+    }
+  }
+
+  bool& mSignalReceived; // owned by individual tests
+
+  std::vector<Animation> mClearRequiredAnimations;
+};
+
+} // namespace
+int UtcDaliAnimationClearDuringAnimationFinished(void)
+{
+  tet_infoline("UtcDaliAnimationClearDuringAnimationFinished");
+
+  TestApplication application;
+
+  auto actor = Actor::New();
+  actor.SetProperty(Actor::Property::POSITION, Vector2(100.0f, 100.0f));
+  application.GetScene().Add(actor);
+
+  auto animation1 = Animation::New(1.0f);
+  auto animation2 = Animation::New(1.0f);
+  auto animation3 = Animation::New(1.0f);
+  animation1.AnimateTo(Property(actor, Actor::Property::POSITION_X), 150.0f);
+  animation2.AnimateTo(Property(actor, Actor::Property::POSITION_Y), 200.0f);
+  animation3.AnimateTo(Property(actor, Actor::Property::POSITION_Z), 250.0f);
+
+  bool                 signal1Received(false);
+  AnimationFinishCheck finish1Check(signal1Received);
+
+  bool                signal2Received(false);
+  AnimationClearCheck finish2Check(signal2Received);
+
+  bool                 signal3Received(false);
+  AnimationFinishCheck finish3Check(signal3Received);
+
+  // Set clear finish signals.
+  finish2Check.AddClearAnimation(animation1);
+  finish2Check.AddClearAnimation(animation2);
+  finish2Check.AddClearAnimation(animation3);
+
+  animation1.FinishedSignal().Connect(&application, finish1Check);
+  animation2.FinishedSignal().Connect(&application, finish2Check);
+  animation3.FinishedSignal().Connect(&application, finish3Check);
+
+  animation1.Play();
+  animation2.Play();
+  animation3.Play();
+
+  application.SendNotification();
+  application.Render(500);
+
+  uint32_t animationCount = Dali::DevelAnimation::GetAnimationCount();
+  DALI_TEST_EQUALS(animationCount, 3, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render(509); // Animation finished
+
+  animationCount = Dali::DevelAnimation::GetAnimationCount();
+  DALI_TEST_EQUALS(animationCount, 3, TEST_LOCATION);
+
+  finish1Check.CheckSignalNotReceived();
+  finish2Check.CheckSignalNotReceived();
+  finish3Check.CheckSignalNotReceived();
+
+  // Notify animation finished signal.
+  application.SendNotification();
+
+  tet_printf("Check animation 1 and 2 receive, and 3 not.\n");
+  tet_printf("Since current Animation finished signal emitted ordered by\n 1. Finished frame.\n 2. Creation time.\n");
+
+  finish1Check.CheckSignalReceived();
+  finish2Check.CheckSignalReceived();
+  finish3Check.CheckSignalNotReceived();
+
   END_TEST;
 }
\ No newline at end of file
index bb99d4281f6310e093907a4c33bef2c25569fba4..70fc0b6e3bcda7f33bbdd946c69b33c2bd1b5747 100644 (file)
@@ -132,7 +132,7 @@ void AnimationPlaylist::NotifyCompleted(CompleteNotificationInterface::Parameter
 #endif
 
   DALI_TRACE_BEGIN_WITH_MESSAGE_GENERATOR(gTraceFilter, "DALI_ANIMATION_FINISHED", [&](std::ostringstream& oss) {
-    oss << "[n:" << notifierIdList.Count() << ", i:" << mIgnoredAnimations.size() << "]";
+    oss << "[n:" << notifierIdList.Count() << ",i:" << mIgnoredAnimations.size() << "]";
   });
 
   for(const auto& notifierId : notifierIdList)
@@ -161,24 +161,28 @@ void AnimationPlaylist::NotifyCompleted(CompleteNotificationInterface::Parameter
   // Now it's safe to emit the signals
   for(auto& animation : finishedAnimations)
   {
-#ifdef TRACE_ENABLED
-    if(gTraceFilter && gTraceFilter->IsTraceEnabled())
+    // Check whether given animation still available (Since it could be cleared during finished signal emitted).
+    if(DALI_LIKELY(mIgnoredAnimations.find(animation.GetAnimationId()) == mIgnoredAnimations.end()))
     {
-      start = GetNanoseconds();
-    }
+#ifdef TRACE_ENABLED
+      if(gTraceFilter && gTraceFilter->IsTraceEnabled())
+      {
+        start = GetNanoseconds();
+      }
 #endif
-    GetImplementation(animation).EmitSignalFinish();
+      GetImplementation(animation).EmitSignalFinish();
 #ifdef TRACE_ENABLED
-    if(gTraceFilter && gTraceFilter->IsTraceEnabled())
-    {
-      end = GetNanoseconds();
-      animationFinishedTimeChecker.emplace_back(end - start, GetImplementation(animation).GetSceneObject()->GetNotifyId());
-    }
+      if(gTraceFilter && gTraceFilter->IsTraceEnabled())
+      {
+        end = GetNanoseconds();
+        animationFinishedTimeChecker.emplace_back(end - start, GetImplementation(animation).GetSceneObject()->GetNotifyId());
+      }
 #endif
+    }
   }
 
   DALI_TRACE_END_WITH_MESSAGE_GENERATOR(gTraceFilter, "DALI_ANIMATION_FINISHED", [&](std::ostringstream& oss) {
-    oss << "[f:" << finishedAnimations.size();
+    oss << "[f:" << finishedAnimations.size() << ",i:" << mIgnoredAnimations.size();
 
     if(finishedAnimations.size() > 0u)
     {