[Tizen](Vector) Backport lottie relative patches from latest tizen 19/315819/1
authorHeeyong Song <heeyong.song@samsung.com>
Tue, 13 Jun 2023 07:33:48 +0000 (16:33 +0900)
committerEunki, Hong <eunkiki.hong@samsung.com>
Thu, 8 Aug 2024 04:29:37 +0000 (13:29 +0900)
This is a combination of 8 commits.

[Tizen] (Vector) Use one EventThreadCallback

EventThreadCallback uses an fd internally and some systems have a limit on the number of fd.
So reduce the number of fd

Change-Id: I55f6e7f5211d0bc567a1b2362c782e9fc2a74b90

(Vector) Fix invalid callback issue

Do not execute a callback with lock
Reset callback pointers after removing

Change-Id: I1f617af4ee89e43c8ab891efbd0beaea14feaea1

Keep reference when member callback excute

Change-Id: I406dc8206b6b156e390bab724820dc55e06437f6
Signed-off-by: Eunki, Hong <eunkiki.hong@samsung.com>
(Vector) Check adaptor validate before unregister

Change-Id: Iac603db90cee8caf0aefc0f15e536ecb3dd51590
Signed-off-by: Eunki, Hong <eunkiki.hong@samsung.com>
(Vector) Erase all EventThreadCallbacks with same pointer

Due to some logical issue, there might be happend that we apply multiple
EventThread callback into VectorAnimationThread.

And if VectorAnimationTask destructor called, we remove only single callback.

If error case occured, EventThread can execute dead callback.

Change-Id: I9e3895180916a292bbca838997ae3053ee8799b5
Signed-off-by: Eunki, Hong <eunkiki.hong@samsung.com>
(Vector) To prevent segmentation fault in AnimatedVectorImageVisual

Change-Id: I4c5c94eb9faeffd4727b5879254527692fe93be2
Signed-off-by: seungho baek <sbsh.baek@samsung.com>
(Vector) Use mutex instead of ConditionalWait for EventhThreadCallback

Since worker thread can call AddEventTriggerCallback freely,

we should make some lock between workerthread and
main thread(RemoveEventTriggerCallbacks, GetNextEventCallback, ~VectorAnimationThread).

ConditionalWait used only for AsyncTask rasterization, and sleep thread.
We'd better seperate those job locker.

Change-Id: I543ba7f0bdf54036a8127d1815dda18cafca54e7
Signed-off-by: Eunki Hong <eunkiki.hong@samsung.com>
(Vector) Change ConditionalWait as Mutex at lottie task

Change-Id: I1f652c440945a9b40a88a992018e14ae0746bc9d
Signed-off-by: Eunki, Hong <eunkiki.hong@samsung.com>
automated-tests/src/dali-toolkit/utc-Dali-AnimatedVectorImageVisual.cpp
dali-toolkit/internal/visuals/animated-vector-image/animated-vector-image-visual.cpp
dali-toolkit/internal/visuals/animated-vector-image/vector-animation-manager.cpp
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 3069b53..0a8a89a 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 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.
@@ -1056,8 +1056,7 @@ int UtcDaliAnimatedVectorImageVisualAnimationFinishedSignal(void)
 
   Property::Map propertyMap;
   propertyMap.Add(Toolkit::Visual::Property::TYPE, DevelVisual::ANIMATED_VECTOR_IMAGE)
-    .Add(ImageVisual::Property::URL, TEST_VECTOR_IMAGE_FILE_NAME)
-    .Add(DevelImageVisual::Property::LOOP_COUNT, 3);
+    .Add(ImageVisual::Property::URL, TEST_VECTOR_IMAGE_FILE_NAME);
 
   Visual::Base visual = VisualFactory::Get().CreateVisual(propertyMap);
   DALI_TEST_CHECK(visual);
@@ -1073,14 +1072,24 @@ int UtcDaliAnimatedVectorImageVisualAnimationFinishedSignal(void)
 
   application.GetScene().Add(actor);
 
+  application.SendNotification();
+  application.Render();
+
+  // Trigger count is 1 - render a frame
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+
+  propertyMap.Clear();
+  propertyMap.Add(DevelImageVisual::Property::LOOP_COUNT, 3);
+  DevelControl::DoAction(actor, DummyControl::Property::TEST_VISUAL, Dali::Toolkit::DevelVisual::Action::UPDATE_PROPERTY, propertyMap);
+
   Property::Map attributes;
   DevelControl::DoAction(actor, DummyControl::Property::TEST_VISUAL, Dali::Toolkit::DevelAnimatedVectorImageVisual::Action::PLAY, attributes);
 
   application.SendNotification();
   application.Render();
 
-  // Wait for animation finish - render, finish
-  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(2), true, TEST_LOCATION);
+  // Wait for animation finish
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
 
   Property::Map    map   = actor.GetProperty<Property::Map>(DummyControl::Property::TEST_VISUAL);
   Property::Value* value = map.Find(DevelImageVisual::Property::PLAY_STATE);
@@ -1351,8 +1360,6 @@ int UtcDaliAnimatedVectorImageVisualStopBehavior(void)
   Property::Map propertyMap;
   propertyMap.Add(Toolkit::Visual::Property::TYPE, DevelVisual::ANIMATED_VECTOR_IMAGE)
     .Add(ImageVisual::Property::URL, TEST_VECTOR_IMAGE_FILE_NAME)
-    .Add(DevelImageVisual::Property::LOOP_COUNT, 3)
-    .Add(DevelImageVisual::Property::STOP_BEHAVIOR, DevelImageVisual::StopBehavior::FIRST_FRAME)
     .Add(ImageVisual::Property::SYNCHRONOUS_LOADING, false);
 
   Visual::Base visual = VisualFactory::Get().CreateVisual(propertyMap);
@@ -1367,14 +1374,25 @@ int UtcDaliAnimatedVectorImageVisualStopBehavior(void)
 
   application.GetScene().Add(actor);
 
+  application.SendNotification();
+  application.Render();
+
+  // Trigger count is 2 - load & render a frame
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(2), true, TEST_LOCATION);
+
+  propertyMap.Clear();
+  propertyMap.Add(DevelImageVisual::Property::LOOP_COUNT, 3);
+  propertyMap.Add(DevelImageVisual::Property::STOP_BEHAVIOR, DevelImageVisual::StopBehavior::FIRST_FRAME);
+  DevelControl::DoAction(actor, DummyControl::Property::TEST_VISUAL, Dali::Toolkit::DevelVisual::Action::UPDATE_PROPERTY, propertyMap);
+
   Property::Map attributes;
   DevelControl::DoAction(actor, DummyControl::Property::TEST_VISUAL, Dali::Toolkit::DevelAnimatedVectorImageVisual::Action::PLAY, attributes);
 
   application.SendNotification();
   application.Render();
 
-  // Trigger count is 3 - load, render, animation finished
-  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(3), true, TEST_LOCATION);
+  // Trigger count is 1 - animation finished
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
 
   Property::Map    map   = actor.GetProperty<Property::Map>(DummyControl::Property::TEST_VISUAL);
   Property::Value* value = map.Find(DevelImageVisual::Property::CURRENT_FRAME_NUMBER);
@@ -1459,9 +1477,6 @@ int UtcDaliAnimatedVectorImageVisualLoopingMode(void)
   Property::Map propertyMap;
   propertyMap.Add(Toolkit::Visual::Property::TYPE, DevelVisual::ANIMATED_VECTOR_IMAGE)
     .Add(ImageVisual::Property::URL, TEST_VECTOR_IMAGE_FILE_NAME)
-    .Add(DevelImageVisual::Property::LOOP_COUNT, 3)
-    .Add(DevelImageVisual::Property::STOP_BEHAVIOR, DevelImageVisual::StopBehavior::LAST_FRAME)
-    .Add(DevelImageVisual::Property::LOOPING_MODE, DevelImageVisual::LoopingMode::AUTO_REVERSE)
     .Add(ImageVisual::Property::SYNCHRONOUS_LOADING, false);
 
   Visual::Base visual = VisualFactory::Get().CreateVisual(propertyMap);
@@ -1476,14 +1491,26 @@ int UtcDaliAnimatedVectorImageVisualLoopingMode(void)
 
   application.GetScene().Add(actor);
 
+  application.SendNotification();
+  application.Render();
+
+  // Trigger count is 2 - load, render
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(2), true, TEST_LOCATION);
+
+  propertyMap.Clear();
+  propertyMap.Add(DevelImageVisual::Property::LOOP_COUNT, 3);
+  propertyMap.Add(DevelImageVisual::Property::STOP_BEHAVIOR, DevelImageVisual::StopBehavior::LAST_FRAME);
+  propertyMap.Add(DevelImageVisual::Property::LOOPING_MODE, DevelImageVisual::LoopingMode::AUTO_REVERSE);
+  DevelControl::DoAction(actor, DummyControl::Property::TEST_VISUAL, Dali::Toolkit::DevelVisual::Action::UPDATE_PROPERTY, propertyMap);
+
   Property::Map attributes;
   DevelControl::DoAction(actor, DummyControl::Property::TEST_VISUAL, Dali::Toolkit::DevelAnimatedVectorImageVisual::Action::PLAY, attributes);
 
   application.SendNotification();
   application.Render();
 
-  // Trigger count is 3 - load, render, animation finished
-  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(3), true, TEST_LOCATION);
+  // Trigger count is 1 - animation finished
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
 
   Property::Map    map   = actor.GetProperty<Property::Map>(DummyControl::Property::TEST_VISUAL);
   Property::Value* value = map.Find(DevelImageVisual::Property::CURRENT_FRAME_NUMBER);
@@ -1648,6 +1675,12 @@ int UtcDaliAnimatedVectorImageVisualMultipleInstances(void)
 
   application.GetScene().Add(actor1);
 
+  application.SendNotification();
+  application.Render();
+
+  // Trigger count is 2 - load & render a frame
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(2), true, TEST_LOCATION);
+
   propertyMap.Clear();
   propertyMap.Add(Toolkit::Visual::Property::TYPE, DevelVisual::ANIMATED_VECTOR_IMAGE)
     .Add(ImageVisual::Property::URL, TEST_VECTOR_IMAGE_FILE_NAME)
@@ -1667,8 +1700,8 @@ int UtcDaliAnimatedVectorImageVisualMultipleInstances(void)
   application.SendNotification();
   application.Render();
 
-  // Trigger count is 4 - load & render a frame for each instance
-  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(4), true, TEST_LOCATION);
+  // Trigger count is 2 - load & render a frame
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(2), true, TEST_LOCATION);
 
   DevelControl::DoAction(actor2, DummyControl::Property::TEST_VISUAL, Dali::Toolkit::DevelAnimatedVectorImageVisual::Action::PLAY, Property::Map());
 
index c02f128..d836b44 100644 (file)
@@ -399,7 +399,7 @@ void AnimatedVectorImageVisual::DoSetProperty(Property::Index index, const Prope
 void AnimatedVectorImageVisual::OnInitialize(void)
 {
   mVectorAnimationTask->ResourceReadySignal().Connect(this, &AnimatedVectorImageVisual::OnResourceReady);
-  mVectorAnimationTask->SetAnimationFinishedCallback(new EventThreadCallback(MakeCallback(this, &AnimatedVectorImageVisual::OnAnimationFinished)));
+  mVectorAnimationTask->SetAnimationFinishedCallback(MakeCallback(this, &AnimatedVectorImageVisual::OnAnimationFinished));
 
   mVectorAnimationTask->KeepRasterizedBuffer(mUseFixedCache);
   mVectorAnimationTask->RequestLoad(mUrl.GetUrl(), IsSynchronousLoadingRequired());
@@ -603,6 +603,8 @@ void AnimatedVectorImageVisual::OnDoActionExtension(const Property::Index action
 
 void AnimatedVectorImageVisual::OnResourceReady(VectorAnimationTask::ResourceStatus status)
 {
+  AnimatedVectorImageVisualPtr self = this; // Keep reference until this API finished
+
   if(status == VectorAnimationTask::ResourceStatus::LOADED)
   {
     if(mImpl->mEventObserver)
@@ -640,6 +642,8 @@ void AnimatedVectorImageVisual::OnResourceReady(VectorAnimationTask::ResourceSta
 
 void AnimatedVectorImageVisual::OnAnimationFinished()
 {
+  AnimatedVectorImageVisualPtr self = this; // Keep reference until this API finished
+
   DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "AnimatedVectorImageVisual::OnAnimationFinished: action state = %d [%p]\n", mPlayState, this);
 
   if(mPlayState != DevelImageVisual::PlayState::STOPPED)
index 9074f36..11c6f22 100644 (file)
@@ -55,7 +55,7 @@ VectorAnimationManager::~VectorAnimationManager()
   }
   mEventCallbacks.clear();
 
-  if(mProcessorRegistered)
+  if(mProcessorRegistered && Adaptor::IsAvailable())
   {
     Adaptor::Get().UnregisterProcessor(*this, true);
   }
index 5c595f9..199f9f1 100644 (file)
@@ -50,10 +50,9 @@ VectorAnimationTask::VectorAnimationTask(VisualFactoryCache& factoryCache)
   mVectorRenderer(VectorAnimationRenderer::New()),
   mAnimationData(),
   mVectorAnimationThread(factoryCache.GetVectorAnimationManager().GetVectorAnimationThread()),
-  mConditionalWait(),
+  mMutex(),
   mResourceReadySignal(),
-  mAnimationFinishedTrigger(),
-  mLoadCompletedTrigger(new EventThreadCallback(MakeCallback(this, &VectorAnimationTask::OnLoadCompleted))),
+  mLoadCompletedCallback(MakeCallback(this, &VectorAnimationTask::OnLoadCompleted)),
   mPlayState(PlayState::STOPPED),
   mStopBehavior(DevelImageVisual::StopBehavior::CURRENT_FRAME),
   mLoopingMode(DevelImageVisual::LoopingMode::RESTART),
@@ -90,21 +89,25 @@ VectorAnimationTask::~VectorAnimationTask()
 
 void VectorAnimationTask::Finalize()
 {
-  ConditionalWait::ScopedLock lock(mConditionalWait);
-
-  // Release some objects in the main thread
-  if(mAnimationFinishedTrigger)
-  {
-    mAnimationFinishedTrigger.reset();
-  }
-  if(mLoadCompletedTrigger)
   {
-    mLoadCompletedTrigger.reset();
+    Mutex::ScopedLock lock(mMutex);
+
+    // Release some objects in the main thread
+    if(mAnimationFinishedCallback)
+    {
+      mVectorAnimationThread.RemoveEventTriggerCallbacks(mAnimationFinishedCallback.get());
+      mAnimationFinishedCallback.reset();
+    }
+    if(mLoadCompletedCallback)
+    {
+      mVectorAnimationThread.RemoveEventTriggerCallbacks(mLoadCompletedCallback.get());
+      mLoadCompletedCallback.reset();
+    }
+
+    mDestroyTask = true;
   }
 
   mVectorRenderer.Finalize();
-
-  mDestroyTask = true;
 }
 
 bool VectorAnimationTask::Load(bool synchronousLoading)
@@ -113,10 +116,12 @@ bool VectorAnimationTask::Load(bool synchronousLoading)
   {
     DALI_LOG_ERROR("VectorAnimationTask::Load: Load failed [%s]\n", mUrl.c_str());
     mLoadRequest = false;
-    mLoadFailed  = true;
-    if(!synchronousLoading)
     {
-      mLoadCompletedTrigger->Trigger();
+      Mutex::ScopedLock lock(mMutex);
+      if(!synchronousLoading && mLoadCompletedCallback)
+      {
+        mVectorAnimationThread.AddEventTriggerCallback(mLoadCompletedCallback.get());
+      }
     }
     return false;
   }
@@ -129,9 +134,12 @@ bool VectorAnimationTask::Load(bool synchronousLoading)
   mFrameDurationMicroSeconds = MICROSECONDS_PER_SECOND / mFrameRate;
 
   mLoadRequest = false;
-  if(!synchronousLoading)
   {
-    mLoadCompletedTrigger->Trigger();
+    Mutex::ScopedLock lock(mMutex);
+    if(!synchronousLoading && mLoadCompletedCallback)
+    {
+      mVectorAnimationThread.AddEventTriggerCallback(mLoadCompletedCallback.get());
+    }
   }
 
   DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationTask::Load: file = %s [%d frames, %f fps] [%p]\n", mUrl.c_str(), mTotalFrame, mFrameRate, this);
@@ -141,8 +149,6 @@ bool VectorAnimationTask::Load(bool synchronousLoading)
 
 void VectorAnimationTask::SetRenderer(Renderer renderer)
 {
-  ConditionalWait::ScopedLock lock(mConditionalWait);
-
   mVectorRenderer.SetRenderer(renderer);
 
   DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationTask::SetRenderer [%p]\n", this);
@@ -173,7 +179,7 @@ bool VectorAnimationTask::IsLoadRequested() const
 
 void VectorAnimationTask::SetAnimationData(const AnimationData& data)
 {
-  ConditionalWait::ScopedLock lock(mConditionalWait);
+  Mutex::ScopedLock lock(mMutex);
 
   DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationTask::SetAnimationData [%p]\n", this);
 
@@ -249,13 +255,10 @@ void VectorAnimationTask::PauseAnimation()
   }
 }
 
-void VectorAnimationTask::SetAnimationFinishedCallback(EventThreadCallback* callback)
+void VectorAnimationTask::SetAnimationFinishedCallback(CallbackBase* callback)
 {
-  ConditionalWait::ScopedLock lock(mConditionalWait);
-  if(callback)
-  {
-    mAnimationFinishedTrigger = std::unique_ptr<EventThreadCallback>(callback);
-  }
+  Mutex::ScopedLock lock(mMutex);
+  mAnimationFinishedCallback = std::unique_ptr<CallbackBase>(callback);
 }
 
 void VectorAnimationTask::SetLoopCount(int32_t count)
@@ -427,7 +430,7 @@ bool VectorAnimationTask::Rasterize(bool& keepAnimation)
   keepAnimation = false;
 
   {
-    ConditionalWait::ScopedLock lock(mConditionalWait);
+    Mutex::ScopedLock lock(mMutex);
     if(mDestroyTask)
     {
       // The task will be destroyed. We don't need rasterization.
@@ -537,10 +540,10 @@ bool VectorAnimationTask::Rasterize(bool& keepAnimation)
 
     // Animation is finished
     {
-      ConditionalWait::ScopedLock lock(mConditionalWait);
-      if(mNeedAnimationFinishedTrigger && mAnimationFinishedTrigger)
+      Mutex::ScopedLock lock(mMutex);
+      if(mNeedAnimationFinishedTrigger && mAnimationFinishedCallback)
       {
-        mAnimationFinishedTrigger->Trigger();
+        mVectorAnimationThread.AddEventTriggerCallback(mAnimationFinishedCallback.get());
       }
     }
 
@@ -628,7 +631,7 @@ void VectorAnimationTask::ApplyAnimationData()
   uint32_t index;
 
   {
-    ConditionalWait::ScopedLock lock(mConditionalWait);
+    Mutex::ScopedLock lock(mMutex);
 
     if(!mAnimationDataUpdated || mAnimationData[mAnimationDataIndex].resendFlag != 0)
     {
index 5acd518..b865b25 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_TOOLKIT_VECTOR_ANIMATION_TASK_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.
@@ -20,7 +20,7 @@
 // EXTERNAL INCLUDES
 #include <dali/devel-api/adaptor-framework/event-thread-callback.h>
 #include <dali/devel-api/adaptor-framework/vector-animation-renderer.h>
-#include <dali/devel-api/threading/conditional-wait.h>
+#include <dali/devel-api/threading/mutex.h>
 #include <dali/public-api/common/vector-wrapper.h>
 #include <dali/public-api/object/property-array.h>
 #include <chrono>
@@ -169,7 +169,7 @@ public:
    * @brief This callback is called after the animation is finished.
    * @param[in] callback The animation finished callback
    */
-  void SetAnimationFinishedCallback(EventThreadCallback* callback);
+  void SetAnimationFinishedCallback(CallbackBase* callback);
 
   /**
    * @brief Gets the playing range in frame number.
@@ -346,10 +346,10 @@ private:
   VectorAnimationRenderer              mVectorRenderer;
   AnimationData                        mAnimationData[2];
   VectorAnimationThread&               mVectorAnimationThread;
-  ConditionalWait                      mConditionalWait;
+  Mutex                                mMutex;
   ResourceReadySignalType              mResourceReadySignal;
-  std::unique_ptr<EventThreadCallback> mAnimationFinishedTrigger;
-  std::unique_ptr<EventThreadCallback> mLoadCompletedTrigger;
+  std::unique_ptr<CallbackBase>        mAnimationFinishedCallback{};
+  std::unique_ptr<CallbackBase>        mLoadCompletedCallback{};
   PlayState                            mPlayState;
   DevelImageVisual::StopBehavior::Type mStopBehavior;
   DevelImageVisual::LoopingMode::Type  mLoopingMode;
index c6dbdb6..2e3d668 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * 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.
@@ -59,11 +59,14 @@ VectorAnimationThread::VectorAnimationThread()
   mRasterizers(GetNumberOfThreads(NUMBER_OF_RASTERIZE_THREADS_ENV, DEFAULT_NUMBER_OF_RASTERIZE_THREADS), [&]() { return RasterizeHelper(*this); }),
   mSleepThread(MakeCallback(this, &VectorAnimationThread::OnAwakeFromSleep)),
   mConditionalWait(),
+  mEventTriggerMutex(),
   mNeedToSleep(false),
   mDestroyThread(false),
   mLogFactory(Dali::Adaptor::Get().GetLogFactory())
 {
   mSleepThread.Start();
+
+  mEventTrigger = std::unique_ptr<EventThreadCallback>(new EventThreadCallback(MakeCallback(this, &VectorAnimationThread::OnEventCallbackTriggered)));
 }
 
 VectorAnimationThread::~VectorAnimationThread()
@@ -71,11 +74,18 @@ VectorAnimationThread::~VectorAnimationThread()
   // Stop the thread
   {
     ConditionalWait::ScopedLock lock(mConditionalWait);
-    mDestroyThread = true;
-    mNeedToSleep   = false;
+    // Wait until some event thread trigger relative job finished.
+    {
+      Mutex::ScopedLock lock(mEventTriggerMutex);
+      mDestroyThread = true;
+    }
+    mNeedToSleep = false;
     mConditionalWait.Notify(lock);
   }
 
+  // Stop event trigger
+  mEventTrigger.reset();
+
   DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationThread::~VectorAnimationThread: Join [%p]\n", this);
 
   Join();
@@ -161,6 +171,31 @@ void VectorAnimationThread::OnAwakeFromSleep()
   }
 }
 
+void VectorAnimationThread::AddEventTriggerCallback(CallbackBase* callback)
+{
+  Mutex::ScopedLock lock(mEventTriggerMutex);
+  if(!mDestroyThread)
+  {
+    mTriggerEventCallbacks.push_back(callback);
+
+    if(!mEventTriggered)
+    {
+      mEventTrigger->Trigger();
+      mEventTriggered = true;
+    }
+  }
+}
+
+void VectorAnimationThread::RemoveEventTriggerCallbacks(CallbackBase* callback)
+{
+  Mutex::ScopedLock lock(mEventTriggerMutex);
+  if(!mDestroyThread)
+  {
+    auto iter = std::remove(mTriggerEventCallbacks.begin(), mTriggerEventCallbacks.end(), callback);
+    mTriggerEventCallbacks.erase(iter, mTriggerEventCallbacks.end());
+  }
+}
+
 void VectorAnimationThread::Run()
 {
   SetThreadName("VectorAnimationThread");
@@ -255,6 +290,31 @@ void VectorAnimationThread::Rasterize()
   }
 }
 
+void VectorAnimationThread::OnEventCallbackTriggered()
+{
+  while(CallbackBase* callback = GetNextEventCallback())
+  {
+    CallbackBase::Execute(*callback);
+  }
+}
+
+CallbackBase* VectorAnimationThread::GetNextEventCallback()
+{
+  Mutex::ScopedLock lock(mEventTriggerMutex);
+  if(!mDestroyThread)
+  {
+    if(!mTriggerEventCallbacks.empty())
+    {
+      auto          iter     = mTriggerEventCallbacks.begin();
+      CallbackBase* callback = *iter;
+      mTriggerEventCallbacks.erase(iter);
+      return callback;
+    }
+    mEventTriggered = false;
+  }
+  return nullptr;
+}
+
 VectorAnimationThread::RasterizeHelper::RasterizeHelper(VectorAnimationThread& animationThread)
 : RasterizeHelper(std::unique_ptr<VectorRasterizeThread>(new VectorRasterizeThread()), animationThread)
 {
index b752105..6ea254d 100644 (file)
@@ -19,6 +19,7 @@
 
 // EXTERNAL INCLUDES
 #include <dali/devel-api/threading/conditional-wait.h>
+#include <dali/devel-api/threading/mutex.h>
 #include <dali/devel-api/threading/thread.h>
 #include <dali/integration-api/adaptor-framework/log-factory-interface.h>
 #include <dali/public-api/signals/connection-tracker.h>
@@ -52,7 +53,7 @@ public:
   ~VectorAnimationThread() override;
 
   /**
-   * Add a animation task into the vector animation thread, called by main thread.
+   * @brief Add a animation task into the vector animation thread, called by main thread.
    *
    * @param[in] task The task added to the thread.
    */
@@ -60,6 +61,7 @@ public:
 
   /**
    * @brief Called when the rasterization is completed from the rasterize thread.
+   *
    * @param[in] task The completed task
    * @param[in] success true if the task succeeded, false otherwise.
    * @param[in] keepAnimation true if the animation is running, false otherwise.
@@ -71,6 +73,22 @@ public:
    */
   void OnAwakeFromSleep();
 
+  /**
+   * @brief Add an event trigger callback.
+   *
+   * @param callback The callback to add
+   * @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);
+
+  /**
+   * @brief Remove event trigger callbacks what we added before.
+   *
+   * @param callback The callback to remove
+   */
+  void RemoveEventTriggerCallbacks(CallbackBase* callback);
+
 protected:
   /**
    * @brief The entry function of the animation thread.
@@ -79,7 +97,7 @@ protected:
 
 private:
   /**
-   * Rasterizes the tasks.
+   * @brief Rasterizes the tasks.
    */
   void Rasterize();
 
@@ -123,6 +141,16 @@ private:
   };
 
   /**
+   * @brief Called when the event callback is triggered.
+   */
+  void OnEventCallbackTriggered();
+
+  /**
+   * @brief Gets next event callback to process.
+   */
+  CallbackBase* GetNextEventCallback();
+
+  /**
    * @brief The thread to sleep until the next frame time.
    */
   class SleepThread : public Thread
@@ -174,10 +202,14 @@ private:
   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;
 };