[Tizen](Vector) Force call KeepRendering when animation stopped, or frame changed 20/315820/2
authorEunki, Hong <eunkiki.hong@samsung.com>
Fri, 12 Apr 2024 13:07:07 +0000 (22:07 +0900)
committerEunki, Hong <eunkiki.hong@samsung.com>
Thu, 8 Aug 2024 05:43:10 +0000 (14:43 +0900)
This is a combination of 5 commits.

[Tizen] Force call KeepRendering when lottie animation stopped, or frame changed

Change-Id: Ie9c44a8a3997a644080c934d36e99c8aee804bf2
Signed-off-by: Eunki, Hong <eunkiki.hong@samsung.com>
(Vector) Let we send finished signal only for matched sent play/stop from visual

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>
(Vector) Add NOTIFY_AFTER_RASTERIZATION property for low fps file + Use UpdateOnce instead of KeepRendering

Let we change renderer rendering behavior is IF_REQUIRED.

It will be useful if app try to render lottie file which has less than 60fps.

+

Let we use UpdateOnce() API for force-rendering instead of KeepRendering().

KeepRendering will make ProcessCoreEvents forcely, which might not be need for animated vector cases.

Change-Id: Iaece9a5ff9e135acfdac404f68bdc690e6a7be8c
Signed-off-by: Eunki, Hong <eunkiki.hong@samsung.com>
(Vector) Reduce the number of UpdateOnce call after rasterization

Previous code request UpdateOnce by each visuals.
But this don't need to spend the number of requests.
So let we just use only one single request API for it.

Change-Id: I5b11ee7ad19dc7c32945c0cfc637f7c65cfe3a77
Signed-off-by: Eunki, Hong <eunkiki.hong@samsung.com>
Fix minor code errror

Change-Id: Ib9bebb5945418fdc040a9828c3989e1a931bd443
Signed-off-by: Eunki, Hong <eunkiki.hong@samsung.com>
12 files changed:
automated-tests/src/dali-toolkit/dali-toolkit-test-utils/toolkit-adaptor-impl.h
automated-tests/src/dali-toolkit/dali-toolkit-test-utils/toolkit-adaptor.cpp
automated-tests/src/dali-toolkit/utc-Dali-AnimatedVectorImageVisual.cpp
dali-toolkit/devel-api/visuals/image-visual-properties-devel.h
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
dali-toolkit/internal/visuals/visual-string-constants.cpp
dali-toolkit/internal/visuals/visual-string-constants.h

index 4970dc4..2198b21 100644 (file)
@@ -74,7 +74,9 @@ public:
   void RemoveIdle( CallbackBase* callback );
   void RunIdles();
 
-  static Integration::Scene GetScene( Dali::Window window );
+  void RequestUpdateOnce();
+
+  static Integration::Scene GetScene(Dali::Window window);
 
   Dali::RenderSurfaceInterface& GetSurface();
   Dali::WindowContainer GetWindows();
index 8f3095f..6d178dc 100644 (file)
@@ -101,6 +101,19 @@ void Adaptor::RunIdles()
   mCallbacks.Clear();
 }
 
+void Adaptor::RequestUpdateOnce()
+{
+  if(mTestApplication)
+  {
+    auto scene = mTestApplication->GetScene();
+    if(scene)
+    {
+      tet_printf("Adaptor::RequestUpdateOnce()\n");
+      scene.KeepRendering(0.0f);
+    }
+  }
+}
+
 Dali::RenderSurfaceInterface& Adaptor::GetSurface()
 {
   DALI_ASSERT_ALWAYS( ! mWindows.empty() );
index 0a8a89a..59559e3 100644 (file)
@@ -1189,6 +1189,9 @@ int UtcDaliAnimatedVectorImageVisualJumpTo(void)
 
   // Wait for animation finish
   DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+  // EventThread will be triggered after animation finished (For render forcibly).
+  // TODO : Is this logic will works well on server-side?
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
 
   // Jump to 3
   DevelControl::DoAction(actor, DummyControl::Property::TEST_VISUAL, Dali::Toolkit::DevelAnimatedVectorImageVisual::Action::JUMP_TO, 3);
index cb2e3f2..2c316ee 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_TOOLKIT_DEVEL_API_VISUALS_IMAGE_VISUAL_PROPERTIES_DEVEL_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.
@@ -175,6 +175,17 @@ enum Type
    * @note This property is read-only.
    */
   MARKER_INFO = ORIENTATION_CORRECTION + 15,
+
+  /**
+   * @brief Whether notify AnimatedVectorImageVisual to render thread after every rasterization or not.
+   * @details Name "notifyAfterRasterization", type Property::BOOLEAN.
+   * If this property is true, AnimatedVectorImageVisual send notify to render thread after every rasterization.
+   * If false, AnimatedVectorImageVisual set Renderer's Behaviour as Continouly (mean, always update the render thread.)
+   *
+   * This flag is useful if given resource has low fps, so we don't need to render every frame.
+   * @note It is used in the AnimatedVectorImageVisual. The default is false.
+   */
+  NOTIFY_AFTER_RASTERIZATION = ORIENTATION_CORRECTION + 17
 };
 
 } //namespace Property
index d836b44..a8d8dab 100644 (file)
@@ -22,6 +22,7 @@
 #include <dali/devel-api/adaptor-framework/window-devel.h>
 #include <dali/devel-api/common/stage.h>
 #include <dali/devel-api/rendering/renderer-devel.h>
+#include <dali/integration-api/adaptor-framework/adaptor.h>
 #include <dali/integration-api/debug.h>
 #include <dali/public-api/rendering/decorated-visual-renderer.h>
 
@@ -95,11 +96,13 @@ AnimatedVectorImageVisual::AnimatedVectorImageVisual(VisualFactoryCache& factory
   mPlacementActor(),
   mPlayState(DevelImageVisual::PlayState::STOPPED),
   mEventCallback(nullptr),
+  mLastSentPlayStateId(0u),
   mLoadFailed(false),
   mRendererAdded(false),
   mCoreShutdown(false),
   mRedrawInScalingDown(true),
-  mUseFixedCache(false)
+  mUseFixedCache(false),
+  mNotifyAfterRasterization(false)
 {
   // the rasterized image is with pre-multiplied alpha format
   mImpl->mFlags |= Visual::Base::Impl::IS_PREMULTIPLIED_ALPHA;
@@ -210,6 +213,7 @@ void AnimatedVectorImageVisual::DoCreatePropertyMap(Property::Map& map) const
   map.Insert(Toolkit::ImageVisual::Property::DESIRED_WIDTH, mDesiredSize.GetWidth());
   map.Insert(Toolkit::ImageVisual::Property::DESIRED_HEIGHT, mDesiredSize.GetHeight());
   map.Insert(Toolkit::DevelImageVisual::Property::USE_FIXED_CACHE, mUseFixedCache);
+  map.Insert(Toolkit::DevelImageVisual::Property::NOTIFY_AFTER_RASTERIZATION, mNotifyAfterRasterization);
 }
 
 void AnimatedVectorImageVisual::DoCreateInstancePropertyMap(Property::Map& map) const
@@ -274,6 +278,10 @@ void AnimatedVectorImageVisual::DoSetProperties(const Property::Map& propertyMap
       {
         DoSetProperty(Toolkit::DevelImageVisual::Property::USE_FIXED_CACHE, keyValue.second);
       }
+      else if(keyValue.first == NOTIFY_AFTER_RASTERIZATION)
+      {
+        DoSetProperty(Toolkit::DevelImageVisual::Property::NOTIFY_AFTER_RASTERIZATION, keyValue.second);
+      }
     }
   }
 
@@ -393,6 +401,22 @@ void AnimatedVectorImageVisual::DoSetProperty(Property::Index index, const Prope
       }
       break;
     }
+
+    case Toolkit::DevelImageVisual::Property::NOTIFY_AFTER_RASTERIZATION:
+    {
+      bool notifyAfterRasterization = false;
+      if(value.Get(notifyAfterRasterization))
+      {
+        if(mNotifyAfterRasterization != notifyAfterRasterization)
+        {
+          mNotifyAfterRasterization = notifyAfterRasterization;
+
+          mAnimationData.notifyAfterRasterization = mNotifyAfterRasterization;
+          mAnimationData.resendFlag |= VectorAnimationTask::RESEND_NOTIFY_AFTER_RASTERIZATION;
+        }
+      }
+      break;
+    }
   }
 }
 
@@ -541,11 +565,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;
@@ -640,8 +662,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);
@@ -658,7 +686,7 @@ void AnimatedVectorImageVisual::OnAnimationFinished()
     }
   }
 
-  if(mImpl->mRenderer)
+  if(!mNotifyAfterRasterization && mImpl->mRenderer)
   {
     mImpl->mRenderer.SetProperty(DevelRenderer::Property::RENDERING_BEHAVIOR, DevelRenderer::Rendering::IF_REQUIRED);
   }
@@ -668,16 +696,27 @@ 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)
+    if(mImpl->mRenderer &&
+       ((mAnimationData.resendFlag & VectorAnimationTask::RESEND_PLAY_STATE) ||
+        (mAnimationData.resendFlag & VectorAnimationTask::RESEND_NOTIFY_AFTER_RASTERIZATION)))
     {
-      if(mAnimationData.playState == DevelImageVisual::PlayState::PLAYING)
+      if(!mNotifyAfterRasterization && mPlayState == DevelImageVisual::PlayState::PLAYING)
       {
+        // Make rendering behaviour if we don't notify after rasterization, but animation playing.
         mImpl->mRenderer.SetProperty(DevelRenderer::Property::RENDERING_BEHAVIOR, DevelRenderer::Rendering::CONTINUOUSLY);
       }
       else
       {
+        // Otherwise, notify will be sended after rasterization. Make behaviour as required.
         mImpl->mRenderer.SetProperty(DevelRenderer::Property::RENDERING_BEHAVIOR, DevelRenderer::Rendering::IF_REQUIRED);
       }
     }
index f579c73..892d5d5 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.
@@ -249,11 +251,15 @@ private:
   WeakHandle<Actor>                  mPlacementActor;
   DevelImageVisual::PlayState::Type  mPlayState;
   CallbackBase*                      mEventCallback; // Not owned
-  bool                               mLoadFailed;
-  bool                               mRendererAdded;
-  bool                               mCoreShutdown;
-  bool                               mRedrawInScalingDown;
-  bool                               mUseFixedCache;
+
+  uint32_t mLastSentPlayStateId;
+
+  bool mLoadFailed : 1;
+  bool mRendererAdded : 1;
+  bool mCoreShutdown : 1;
+  bool mRedrawInScalingDown : 1;
+  bool mUseFixedCache : 1;
+  bool mNotifyAfterRasterization : 1;
 };
 
 } // namespace Internal
index 199f9f1..f3385f4 100644 (file)
@@ -67,16 +67,19 @@ VectorAnimationTask::VectorAnimationTask(VisualFactoryCache& factoryCache)
   mWidth(0),
   mHeight(0),
   mAnimationDataIndex(0),
+  mAppliedPlayStateId(0u),
   mLoopCount(LOOP_FOREVER),
   mCurrentLoop(0),
   mForward(true),
   mUpdateFrameNumber(false),
   mNeedAnimationFinishedTrigger(true),
+  mNeedForceRenderOnceTrigger(false),
   mAnimationDataUpdated(false),
   mDestroyTask(false),
   mLoadRequest(false),
   mLoadFailed(false),
   mUseFixedCache(false),
+  mNotifyAfterRasterization(false),
   mSizeUpdated(false)
 {
   mVectorRenderer.UploadCompletedSignal().Connect(this, &VectorAnimationTask::OnUploadCompleted);
@@ -120,7 +123,7 @@ bool VectorAnimationTask::Load(bool synchronousLoading)
       Mutex::ScopedLock lock(mMutex);
       if(!synchronousLoading && mLoadCompletedCallback)
       {
-        mVectorAnimationThread.AddEventTriggerCallback(mLoadCompletedCallback.get());
+        mVectorAnimationThread.AddEventTriggerCallback(mLoadCompletedCallback.get(), 0u);
       }
     }
     return false;
@@ -138,7 +141,7 @@ bool VectorAnimationTask::Load(bool synchronousLoading)
     Mutex::ScopedLock lock(mMutex);
     if(!synchronousLoading && mLoadCompletedCallback)
     {
-      mVectorAnimationThread.AddEventTriggerCallback(mLoadCompletedCallback.get());
+      mVectorAnimationThread.AddEventTriggerCallback(mLoadCompletedCallback.get(), 0u);
     }
   }
 
@@ -168,7 +171,7 @@ void VectorAnimationTask::RequestLoad(const std::string& url, bool synchronousLo
   {
     Load(true);
 
-    OnLoadCompleted();
+    OnLoadCompleted(0u);
   }
 }
 
@@ -251,6 +254,9 @@ void VectorAnimationTask::PauseAnimation()
   {
     mPlayState = PlayState::PAUSED;
 
+    // Ensure to render paused frame.
+    mNeedForceRenderOnceTrigger = true;
+
     DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationTask::PauseAnimation: Pause [%p]\n", this);
   }
 }
@@ -342,10 +348,22 @@ void VectorAnimationTask::SetPlayRange(const Property::Array& playRange)
     if(mStartFrame > mCurrentFrame)
     {
       mCurrentFrame = mStartFrame;
+
+      if(mPlayState != PlayState::PLAYING)
+      {
+        // Ensure to render current frame.
+        mNeedForceRenderOnceTrigger = true;
+      }
     }
     else if(mEndFrame < mCurrentFrame)
     {
       mCurrentFrame = mEndFrame;
+
+      if(mPlayState != PlayState::PLAYING)
+      {
+        // Ensure to render current frame.
+        mNeedForceRenderOnceTrigger = true;
+      }
     }
 
     DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationTask::SetPlayRange: [%d, %d] [%s] [%p]\n", mStartFrame, mEndFrame, mUrl.c_str(), this);
@@ -371,6 +389,12 @@ void VectorAnimationTask::SetCurrentFrameNumber(uint32_t frameNumber)
     mCurrentFrame      = frameNumber;
     mUpdateFrameNumber = false;
 
+    if(mPlayState != PlayState::PLAYING)
+    {
+      // Ensure to render current frame.
+      mNeedForceRenderOnceTrigger = true;
+    }
+
     DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationTask::SetCurrentFrameNumber: frame number = %d [%p]\n", mCurrentFrame, this);
   }
   else
@@ -532,6 +556,8 @@ bool VectorAnimationTask::Rasterize(bool& keepAnimation)
     mForward     = true;
     mCurrentLoop = 0;
 
+    mNeedForceRenderOnceTrigger = true;
+
     if(mVectorRenderer)
     {
       // Notify the Renderer that rendering is stopped.
@@ -543,13 +569,21 @@ bool VectorAnimationTask::Rasterize(bool& keepAnimation)
       Mutex::ScopedLock lock(mMutex);
       if(mNeedAnimationFinishedTrigger && mAnimationFinishedCallback)
       {
-        mVectorAnimationThread.AddEventTriggerCallback(mAnimationFinishedCallback.get());
+        mVectorAnimationThread.AddEventTriggerCallback(mAnimationFinishedCallback.get(), mAppliedPlayStateId);
       }
     }
 
     DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationTask::Rasterize: Animation is finished [current = %d] [%p]\n", currentFrame, this);
   }
 
+  // Forcely trigger render once if need.
+  if(mNotifyAfterRasterization || mNeedForceRenderOnceTrigger)
+  {
+    Mutex::ScopedLock lock(mMutex);
+    mVectorAnimationThread.RequestForceRenderOnce();
+    mNeedForceRenderOnceTrigger = false;
+  }
+
   if(mPlayState != PlayState::PAUSED && mPlayState != PlayState::STOPPED)
   {
     keepAnimation = true;
@@ -670,6 +704,11 @@ void VectorAnimationTask::ApplyAnimationData()
     SetCurrentFrameNumber(mAnimationData[index].currentFrame);
   }
 
+  if(mAnimationData[index].resendFlag & VectorAnimationTask::RESEND_NOTIFY_AFTER_RASTERIZATION)
+  {
+    mNotifyAfterRasterization = mAnimationData[index].notifyAfterRasterization;
+  }
+
   if(mAnimationData[index].resendFlag & VectorAnimationTask::RESEND_NEED_RESOURCE_READY)
   {
     mVectorRenderer.InvalidateBuffer();
@@ -685,6 +724,7 @@ void VectorAnimationTask::ApplyAnimationData()
 
   if(mAnimationData[index].resendFlag & VectorAnimationTask::RESEND_PLAY_STATE)
   {
+    mAppliedPlayStateId = mAnimationData[index].playStateId;
     if(mAnimationData[index].playState == DevelImageVisual::PlayState::PLAYING)
     {
       PlayAnimation();
@@ -709,7 +749,7 @@ void VectorAnimationTask::OnUploadCompleted()
   mResourceReadySignal.Emit(ResourceStatus::READY);
 }
 
-void VectorAnimationTask::OnLoadCompleted()
+void VectorAnimationTask::OnLoadCompleted(uint32_t /* not used */)
 {
   if(!mLoadFailed)
   {
index b865b25..32806a9 100644 (file)
@@ -64,15 +64,16 @@ public:
    */
   enum ResendFlags
   {
-    RESEND_PLAY_RANGE          = 1 << 0,
-    RESEND_LOOP_COUNT          = 1 << 1,
-    RESEND_STOP_BEHAVIOR       = 1 << 2,
-    RESEND_LOOPING_MODE        = 1 << 3,
-    RESEND_CURRENT_FRAME       = 1 << 4,
-    RESEND_SIZE                = 1 << 5,
-    RESEND_PLAY_STATE          = 1 << 6,
-    RESEND_NEED_RESOURCE_READY = 1 << 7,
-    RESEND_DYNAMIC_PROPERTY    = 1 << 8
+    RESEND_PLAY_RANGE                 = 1 << 0,
+    RESEND_LOOP_COUNT                 = 1 << 1,
+    RESEND_STOP_BEHAVIOR              = 1 << 2,
+    RESEND_LOOPING_MODE               = 1 << 3,
+    RESEND_CURRENT_FRAME              = 1 << 4,
+    RESEND_SIZE                       = 1 << 5,
+    RESEND_PLAY_STATE                 = 1 << 6,
+    RESEND_NEED_RESOURCE_READY        = 1 << 7,
+    RESEND_DYNAMIC_PROPERTY           = 1 << 8,
+    RESEND_NOTIFY_AFTER_RASTERIZATION = 1 << 9,
   };
 
   /**
@@ -90,21 +91,25 @@ public:
       currentFrame(0),
       width(0),
       height(0),
-      loopCount(-1)
+      loopCount(-1),
+      playStateId(0),
+      notifyAfterRasterization(false)
     {
     }
 
     AnimationData& operator=(const AnimationData& rhs)
     {
       resendFlag |= rhs.resendFlag; // OR resend flag
-      playRange    = rhs.playRange;
-      playState    = rhs.playState;
-      stopBehavior = rhs.stopBehavior;
-      loopingMode  = rhs.loopingMode;
-      currentFrame = rhs.currentFrame;
-      width        = rhs.width;
-      height       = rhs.height;
-      loopCount    = rhs.loopCount;
+      playRange                = rhs.playRange;
+      playState                = rhs.playState;
+      stopBehavior             = rhs.stopBehavior;
+      loopingMode              = rhs.loopingMode;
+      currentFrame             = rhs.currentFrame;
+      width                    = rhs.width;
+      height                   = rhs.height;
+      loopCount                = rhs.loopCount;
+      playStateId              = rhs.playStateId;
+      notifyAfterRasterization = rhs.notifyAfterRasterization;
       dynamicProperties.insert(dynamicProperties.end(), rhs.dynamicProperties.begin(), rhs.dynamicProperties.end());
       return *this;
     }
@@ -119,6 +124,8 @@ public:
     uint32_t                             width;
     uint32_t                             height;
     int32_t                              loopCount;
+    uint32_t                             playStateId;
+    bool                                 notifyAfterRasterization;
   };
 
   /**
@@ -233,6 +240,24 @@ public:
    */
   TimePoint GetNextFrameTime();
 
+  /**
+   * @brief Called when the rasterization is completed from the asyncTaskManager
+   * @param[in] task The completed task
+   */
+  void TaskCompleted(VectorAnimationTaskPtr task);
+
+  /**
+   * @brief Check the rasterization succeeded
+   * @return true if the rasterization succeeded, false otherwise.
+   */
+  bool IsRasterized();
+
+  /**
+   * @brief Check the animation is running
+   * @return true if the animation is running, false otherwise.
+   */
+  bool IsAnimating();
+
   void KeepRasterizedBuffer(bool useFixedCache)
   {
     mUseFixedCache = useFixedCache;
@@ -325,7 +350,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;
@@ -364,17 +389,20 @@ private:
   uint32_t                             mWidth;
   uint32_t                             mHeight;
   uint32_t                             mAnimationDataIndex;
+  uint32_t                             mAppliedPlayStateId;
   int32_t                              mLoopCount;
   int32_t                              mCurrentLoop;
-  bool                                 mForward;
-  bool                                 mUpdateFrameNumber;
-  bool                                 mNeedAnimationFinishedTrigger;
-  bool                                 mAnimationDataUpdated;
-  bool                                 mDestroyTask;
-  bool                                 mLoadRequest;
-  bool                                 mLoadFailed;
-  bool                                 mUseFixedCache;
-  bool                                 mSizeUpdated;
+  bool                                 mForward : 1;
+  bool                                 mUpdateFrameNumber : 1;
+  bool                                 mNeedAnimationFinishedTrigger : 1;
+  bool                                 mNeedForceRenderOnceTrigger : 1;
+  bool                                 mAnimationDataUpdated : 1;
+  bool                                 mDestroyTask : 1;
+  bool                                 mLoadRequest : 1;
+  bool                                 mLoadFailed : 1;
+  bool                                 mUseFixedCache : 1;
+  bool                                 mNotifyAfterRasterization : 1;
+  bool                                 mSizeUpdated : 1;
 };
 
 } // namespace Internal
index 2e3d668..9fbd846 100644 (file)
@@ -60,9 +60,11 @@ VectorAnimationThread::VectorAnimationThread()
   mSleepThread(MakeCallback(this, &VectorAnimationThread::OnAwakeFromSleep)),
   mConditionalWait(),
   mEventTriggerMutex(),
+  mLogFactory(Dali::Adaptor::Get().GetLogFactory()),
   mNeedToSleep(false),
   mDestroyThread(false),
-  mLogFactory(Dali::Adaptor::Get().GetLogFactory())
+  mEventTriggered(false),
+  mForceRenderOnce(false)
 {
   mSleepThread.Start();
 
@@ -171,12 +173,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)
     {
@@ -191,11 +193,26 @@ 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());
   }
 }
 
+void VectorAnimationThread::RequestForceRenderOnce()
+{
+  Mutex::ScopedLock lock(mEventTriggerMutex);
+  if(!mDestroyThread)
+  {
+    mForceRenderOnce = true;
+
+    if(!mEventTriggered)
+    {
+      mEventTrigger->Trigger();
+      mEventTriggered = true;
+    }
+  }
+}
+
 void VectorAnimationThread::Run()
 {
   SetThreadName("VectorAnimationThread");
@@ -292,27 +309,44 @@ 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);
+  }
+  // Request update once if we need.
+  {
+    Mutex::ScopedLock lock(mEventTriggerMutex);
+    if(!mDestroyThread && mForceRenderOnce)
+    {
+      mForceRenderOnce = false;
+      if(Dali::Adaptor::IsAvailable())
+      {
+        Dali::Adaptor::Get().UpdateOnce();
+      }
+    }
   }
 }
 
-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::RasterizeHelper::RasterizeHelper(VectorAnimationThread& animationThread)
index 6ea254d..7a001f0 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_TOOLKIT_VECTOR_ANIMATION_THREAD_H
 
 /*
- * Copyright (c) 2022 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.
@@ -89,6 +90,11 @@ public:
    */
   void RemoveEventTriggerCallbacks(CallbackBase* callback);
 
+  /**
+   * @brief Request to event callback from rasterize thread. This is called when we want to ensure rendering next frame.
+   */
+  void RequestForceRenderOnce();
+
 protected:
   /**
    * @brief The entry function of the animation thread.
@@ -148,7 +154,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.
@@ -186,8 +192,8 @@ private:
     std::unique_ptr<CallbackBase>                      mAwakeCallback;
     std::chrono::time_point<std::chrono::steady_clock> mSleepTimePoint;
     const Dali::LogFactoryInterface&                   mLogFactory;
-    bool                                               mNeedToSleep;
-    bool                                               mDestroyThread;
+    bool                                               mNeedToSleep : 1;
+    bool                                               mDestroyThread : 1;
   };
 
 private:
@@ -198,19 +204,21 @@ private:
   VectorAnimationThread& operator=(const VectorAnimationThread& thread) = delete;
 
 private:
-  std::vector<VectorAnimationTaskPtr>      mAnimationTasks;
-  std::vector<VectorAnimationTaskPtr>      mCompletedTasks;
-  std::vector<VectorAnimationTaskPtr>      mWorkingTasks;
-  RoundRobinContainerView<RasterizeHelper> mRasterizers;
-  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;
+  std::vector<VectorAnimationTaskPtr>             mAnimationTasks;
+  std::vector<VectorAnimationTaskPtr>             mCompletedTasks;
+  std::vector<VectorAnimationTaskPtr>             mWorkingTasks;
+  RoundRobinContainerView<RasterizeHelper>        mRasterizers;
+  std::vector<std::pair<CallbackBase*, uint32_t>> mTriggerEventCallbacks{}; // Callbacks are not owned
+  SleepThread                                     mSleepThread;
+  ConditionalWait                                 mConditionalWait;
+  Mutex                                           mEventTriggerMutex;
+  std::unique_ptr<EventThreadCallback>            mEventTrigger{};
+  const Dali::LogFactoryInterface&                mLogFactory;
+
+  bool mNeedToSleep : 1;
+  bool mDestroyThread : 1;
+  bool mEventTriggered : 1;
+  bool mForceRenderOnce : 1;
 };
 
 } // namespace Internal
index 0ef6515..556bb88 100644 (file)
@@ -125,6 +125,7 @@ const char* const REDRAW_IN_SCALING_DOWN_NAME("redrawInScalingDown");
 const char* const MASKING_TYPE_NAME("maskingType");
 const char* const MASK_TEXTURE_RATIO_NAME("maskTextureRatio");
 const char* const USE_FIXED_CACHE("useFixedCache");
+const char* const NOTIFY_AFTER_RASTERIZATION("notifyAfterRasterization");
 
 // Text visual
 const char* const TEXT_PROPERTY("text");
index 438c340..36ba364 100644 (file)
@@ -109,6 +109,7 @@ extern const char* const REDRAW_IN_SCALING_DOWN_NAME;
 extern const char* const MASKING_TYPE_NAME;
 extern const char* const MASK_TEXTURE_RATIO_NAME;
 extern const char* const USE_FIXED_CACHE;
+extern const char* const NOTIFY_AFTER_RASTERIZATION;
 
 // Text visual
 extern const char* const TEXT_PROPERTY;