(Vector) Let we send finished signal only for matched sent play/stop from visual 09/309209/4
authorEunki, Hong <eunkiki.hong@samsung.com>
Fri, 5 Apr 2024 13:28:08 +0000 (22:28 +0900)
committerEunki, Hong <eunkiki.hong@samsung.com>
Mon, 8 Apr 2024 02:59:15 +0000 (11:59 +0900)
It is possible that AnimationFinished trigger comes "after" event thread Play action comes.

If then, user's Play action be skipped.

To avoid this case, let we give the id when play state changed to Play / Pause / Stop.
And send finished signal only if the id is matched what visual know.

Change-Id: I03409e65d2f6b947940ea5591959b106ff8de2bd
Signed-off-by: Eunki, Hong <eunkiki.hong@samsung.com>
dali-toolkit/internal/visuals/animated-vector-image/animated-vector-image-visual.cpp
dali-toolkit/internal/visuals/animated-vector-image/animated-vector-image-visual.h
dali-toolkit/internal/visuals/animated-vector-image/vector-animation-task.cpp
dali-toolkit/internal/visuals/animated-vector-image/vector-animation-task.h
dali-toolkit/internal/visuals/animated-vector-image/vector-animation-thread.cpp
dali-toolkit/internal/visuals/animated-vector-image/vector-animation-thread.h

index f078dc8..9c81c3b 100644 (file)
@@ -97,6 +97,7 @@ AnimatedVectorImageVisual::AnimatedVectorImageVisual(VisualFactoryCache& factory
   mPlacementActor(),
   mPlayState(DevelImageVisual::PlayState::STOPPED),
   mEventCallback(nullptr),
+  mLastSentPlayStateId(0u),
   mLoadFailed(false),
   mRendererAdded(false),
   mCoreShutdown(false),
@@ -565,11 +566,9 @@ void AnimatedVectorImageVisual::OnDoAction(const Property::Index actionId, const
     {
       if(IsOnScene() && mVisualSize != Vector2::ZERO)
       {
-        if(mAnimationData.playState != DevelImageVisual::PlayState::PLAYING)
-        {
-          mAnimationData.playState = DevelImageVisual::PlayState::PLAYING;
-          mAnimationData.resendFlag |= VectorAnimationTask::RESEND_PLAY_STATE;
-        }
+        // Always resend Playing state. If task is already playing, it will be ignored at Rasterize time.
+        mAnimationData.playState = DevelImageVisual::PlayState::PLAYING;
+        mAnimationData.resendFlag |= VectorAnimationTask::RESEND_PLAY_STATE;
       }
       mPlayState = DevelImageVisual::PlayState::PLAYING;
       break;
@@ -695,8 +694,14 @@ void AnimatedVectorImageVisual::OnResourceReady(VectorAnimationTask::ResourceSta
   DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "status = %d [%p]\n", status, this);
 }
 
-void AnimatedVectorImageVisual::OnAnimationFinished()
+void AnimatedVectorImageVisual::OnAnimationFinished(uint32_t playStateId)
 {
+  // Only send event when animation is finished by the last Play/Pause/Stop request.
+  if(mLastSentPlayStateId != playStateId)
+  {
+    return;
+  }
+
   AnimatedVectorImageVisualPtr self = this; // Keep reference until this API finished
 
   DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "AnimatedVectorImageVisual::OnAnimationFinished: action state = %d [%p]\n", mPlayState, this);
@@ -723,6 +728,13 @@ void AnimatedVectorImageVisual::SendAnimationData()
 {
   if(mAnimationData.resendFlag)
   {
+    if(mAnimationData.resendFlag & VectorAnimationTask::RESEND_PLAY_STATE)
+    {
+      // Keep last sent playId. It will be used when we try to emit AnimationFinished signal.
+      // The OnAnimationFinished signal what before Play/Pause/Stop action send could be come after action sent.
+      // To ensure the OnAnimationFinished signal comes belong to what we sent, we need to keep last sent playId.
+      mAnimationData.playStateId = ++mLastSentPlayStateId;
+    }
     mVectorAnimationTask->SetAnimationData(mAnimationData);
 
     if(mImpl->mRenderer)
index 493e621..fc7d7a4 100644 (file)
@@ -182,8 +182,10 @@ private:
 
   /**
    * @brief Event callback from rasterize thread. This is called after the animation is finished.
+   *
+   * @param[in] playStateId The play state id
    */
-  void OnAnimationFinished();
+  void OnAnimationFinished(uint32_t playStateId);
 
   /**
    * @brief Send animation data to the rasterize thread.
@@ -250,6 +252,8 @@ private:
   DevelImageVisual::PlayState::Type  mPlayState;
   CallbackBase*                      mEventCallback; // Not owned
 
+  uint32_t mLastSentPlayStateId;
+
   bool mLoadFailed : 1;
   bool mRendererAdded : 1;
   bool mCoreShutdown : 1;
index cb67bc7..4f4e97d 100644 (file)
@@ -92,6 +92,7 @@ VectorAnimationTask::VectorAnimationTask(VisualFactoryCache& factoryCache)
   mWidth(0),
   mHeight(0),
   mAnimationDataIndex(0),
+  mAppliedPlayStateId(0u),
   mLoopCount(LOOP_FOREVER),
   mCurrentLoop(0),
   mForward(true),
@@ -214,7 +215,7 @@ bool VectorAnimationTask::Load(bool synchronousLoading)
       Mutex::ScopedLock lock(mMutex);
       if(!synchronousLoading && mLoadCompletedCallback)
       {
-        mVectorAnimationThread.AddEventTriggerCallback(mLoadCompletedCallback.get());
+        mVectorAnimationThread.AddEventTriggerCallback(mLoadCompletedCallback.get(), 0u);
       }
     }
 #ifdef TRACE_ENABLED
@@ -245,7 +246,7 @@ bool VectorAnimationTask::Load(bool synchronousLoading)
     Mutex::ScopedLock lock(mMutex);
     if(!synchronousLoading && mLoadCompletedCallback)
     {
-      mVectorAnimationThread.AddEventTriggerCallback(mLoadCompletedCallback.get());
+      mVectorAnimationThread.AddEventTriggerCallback(mLoadCompletedCallback.get(), 0u);
     }
   }
 
@@ -290,7 +291,7 @@ void VectorAnimationTask::RequestLoad(const VisualUrl& url, EncodedImageBuffer e
   {
     Load(true);
 
-    OnLoadCompleted();
+    OnLoadCompleted(0u);
   }
 }
 
@@ -703,7 +704,7 @@ bool VectorAnimationTask::Rasterize()
       Mutex::ScopedLock lock(mMutex);
       if(mNeedAnimationFinishedTrigger && mAnimationFinishedCallback)
       {
-        mVectorAnimationThread.AddEventTriggerCallback(mAnimationFinishedCallback.get());
+        mVectorAnimationThread.AddEventTriggerCallback(mAnimationFinishedCallback.get(), mAppliedPlayStateId);
       }
     }
 
@@ -859,6 +860,7 @@ void VectorAnimationTask::ApplyAnimationData()
 
     if(animationData.resendFlag & VectorAnimationTask::RESEND_PLAY_STATE)
     {
+      mAppliedPlayStateId = animationData.playStateId;
       if(animationData.playState == DevelImageVisual::PlayState::PLAYING)
       {
         PlayAnimation();
@@ -883,7 +885,7 @@ void VectorAnimationTask::OnUploadCompleted()
   mResourceReadySignal.Emit(ResourceStatus::READY);
 }
 
-void VectorAnimationTask::OnLoadCompleted()
+void VectorAnimationTask::OnLoadCompleted(uint32_t /* not used */)
 {
   if(!mLoadFailed)
   {
index ef89500..eb48a13 100644 (file)
@@ -93,7 +93,8 @@ public:
       currentFrame(0),
       width(0),
       height(0),
-      loopCount(-1)
+      loopCount(-1),
+      playStateId(0)
     {
     }
 
@@ -108,6 +109,7 @@ public:
       width        = rhs.width;
       height       = rhs.height;
       loopCount    = rhs.loopCount;
+      playStateId  = rhs.playStateId;
       dynamicProperties.insert(dynamicProperties.end(), rhs.dynamicProperties.begin(), rhs.dynamicProperties.end());
       return *this;
     }
@@ -122,6 +124,7 @@ public:
     uint32_t                             width;
     uint32_t                             height;
     int32_t                              loopCount;
+    uint32_t                             playStateId;
   };
 
   /**
@@ -365,7 +368,7 @@ private:
   /**
    * @brief Event callback from rasterize thread. This is called when the file loading is completed.
    */
-  void OnLoadCompleted();
+  void OnLoadCompleted(uint32_t argument);
 
   // Undefined
   VectorAnimationTask(const VectorAnimationTask& task) = delete;
@@ -407,6 +410,7 @@ private:
   uint32_t                             mWidth;
   uint32_t                             mHeight;
   uint32_t                             mAnimationDataIndex;
+  uint32_t                             mAppliedPlayStateId;
   int32_t                              mLoopCount;
   int32_t                              mCurrentLoop;
   bool                                 mForward : 1;
index 3ee2d5d..b07ba5b 100644 (file)
@@ -159,12 +159,12 @@ void VectorAnimationThread::OnAwakeFromSleep()
   }
 }
 
-void VectorAnimationThread::AddEventTriggerCallback(CallbackBase* callback)
+void VectorAnimationThread::AddEventTriggerCallback(CallbackBase* callback, uint32_t argument)
 {
   Mutex::ScopedLock lock(mEventTriggerMutex);
   if(!mDestroyThread)
   {
-    mTriggerEventCallbacks.push_back(callback);
+    mTriggerEventCallbacks.emplace_back(callback, argument);
 
     if(!mEventTriggered)
     {
@@ -179,7 +179,7 @@ void VectorAnimationThread::RemoveEventTriggerCallbacks(CallbackBase* callback)
   Mutex::ScopedLock lock(mEventTriggerMutex);
   if(!mDestroyThread)
   {
-    auto iter = std::remove(mTriggerEventCallbacks.begin(), mTriggerEventCallbacks.end(), callback);
+    auto iter = std::remove_if(mTriggerEventCallbacks.begin(), mTriggerEventCallbacks.end(), [&callback](std::pair<CallbackBase*, uint32_t>& item) { return item.first == callback; });
     mTriggerEventCallbacks.erase(iter, mTriggerEventCallbacks.end());
   }
 }
@@ -276,27 +276,32 @@ void VectorAnimationThread::Rasterize()
 
 void VectorAnimationThread::OnEventCallbackTriggered()
 {
-  while(CallbackBase* callback = GetNextEventCallback())
+  while(true)
   {
-    CallbackBase::Execute(*callback);
+    auto callbackPair = GetNextEventCallback();
+    if(callbackPair.first == nullptr)
+    {
+      break;
+    }
+    CallbackBase::Execute(*callbackPair.first, callbackPair.second);
   }
 }
 
-CallbackBase* VectorAnimationThread::GetNextEventCallback()
+std::pair<CallbackBase*, uint32_t> VectorAnimationThread::GetNextEventCallback()
 {
   Mutex::ScopedLock lock(mEventTriggerMutex);
   if(!mDestroyThread)
   {
     if(!mTriggerEventCallbacks.empty())
     {
-      auto          iter     = mTriggerEventCallbacks.begin();
-      CallbackBase* callback = *iter;
+      auto iter           = mTriggerEventCallbacks.begin();
+      auto callbackIdPair = *iter;
       mTriggerEventCallbacks.erase(iter);
-      return callback;
+      return callbackIdPair;
     }
     mEventTriggered = false;
   }
-  return nullptr;
+  return std::make_pair(nullptr, 0u);
 }
 
 VectorAnimationThread::SleepThread::SleepThread(CallbackBase* callback)
index 7421cc8..b1f608a 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_TOOLKIT_VECTOR_ANIMATION_THREAD_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.
@@ -77,10 +77,11 @@ public:
    * @brief Add an event trigger callback.
    *
    * @param callback The callback to add
+   * @param argument The argument to pass to the callback
    * @note Ownership of the callback is NOT passed onto this class.
    * @note The callback will be excuted in the main thread.
    */
-  void AddEventTriggerCallback(CallbackBase* callback);
+  void AddEventTriggerCallback(CallbackBase* callback, uint32_t argument);
 
   /**
    * @brief Remove event trigger callbacks what we added before.
@@ -109,7 +110,7 @@ private:
   /**
    * @brief Gets next event callback to process.
    */
-  CallbackBase* GetNextEventCallback();
+  std::pair<CallbackBase*, uint32_t> GetNextEventCallback();
 
   /**
    * @brief The thread to sleep until the next frame time.
@@ -160,20 +161,20 @@ private:
   VectorAnimationThread& operator=(const VectorAnimationThread& thread) = delete;
 
 private:
-  std::vector<VectorAnimationTaskPtr>  mAnimationTasks;
-  std::vector<VectorAnimationTaskPtr>  mCompletedTasks;
-  std::vector<VectorAnimationTaskPtr>  mWorkingTasks;
-  std::vector<CallbackBase*>           mTriggerEventCallbacks{}; // Callbacks are not owned
-  SleepThread                          mSleepThread;
-  ConditionalWait                      mConditionalWait;
-  Mutex                                mEventTriggerMutex;
-  std::unique_ptr<EventThreadCallback> mEventTrigger{};
-  bool                                 mNeedToSleep;
-  bool                                 mDestroyThread;
-  bool                                 mEventTriggered{false};
-  const Dali::LogFactoryInterface&     mLogFactory;
-  const Dali::TraceFactoryInterface&   mTraceFactory;
-  Dali::AsyncTaskManager               mAsyncTaskManager;
+  std::vector<VectorAnimationTaskPtr>             mAnimationTasks;
+  std::vector<VectorAnimationTaskPtr>             mCompletedTasks;
+  std::vector<VectorAnimationTaskPtr>             mWorkingTasks;
+  std::vector<std::pair<CallbackBase*, uint32_t>> mTriggerEventCallbacks{}; // Callbacks are not owned
+  SleepThread                                     mSleepThread;
+  ConditionalWait                                 mConditionalWait;
+  Mutex                                           mEventTriggerMutex;
+  std::unique_ptr<EventThreadCallback>            mEventTrigger{};
+  bool                                            mNeedToSleep;
+  bool                                            mDestroyThread;
+  bool                                            mEventTriggered{false};
+  const Dali::LogFactoryInterface&                mLogFactory;
+  const Dali::TraceFactoryInterface&              mTraceFactory;
+  Dali::AsyncTaskManager                          mAsyncTaskManager;
 };
 
 } // namespace Internal