2 * Copyright (c) 2024 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <dali-toolkit/internal/visuals/animated-vector-image/vector-animation-thread.h>
22 #include <dali/devel-api/adaptor-framework/environment-variable.h>
23 #include <dali/devel-api/adaptor-framework/thread-settings.h>
24 #include <dali/integration-api/adaptor-framework/adaptor.h>
25 #include <dali/integration-api/debug.h>
36 #if defined(DEBUG_ENABLED)
37 Debug::Filter* gVectorAnimationLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_VECTOR_ANIMATION");
40 } // unnamed namespace
42 VectorAnimationThread::VectorAnimationThread()
46 mSleepThread(MakeCallback(this, &VectorAnimationThread::OnAwakeFromSleep)),
49 mLogFactory(Dali::Adaptor::Get().GetLogFactory()),
50 mTraceFactory(Dali::Adaptor::Get().GetTraceFactory()),
52 mDestroyThread(false),
53 mEventTriggered(false),
54 mForceRenderOnce(false)
56 mAsyncTaskManager = Dali::AsyncTaskManager::Get();
59 mEventTrigger = std::unique_ptr<EventThreadCallback>(new EventThreadCallback(MakeCallback(this, &VectorAnimationThread::OnEventCallbackTriggered)));
62 VectorAnimationThread::~VectorAnimationThread()
66 ConditionalWait::ScopedLock lock(mConditionalWait);
67 // Wait until some event thread trigger relative job finished.
69 Mutex::ScopedLock lock(mEventTriggerMutex);
70 mDestroyThread = true;
73 mConditionalWait.Notify(lock);
77 mEventTrigger.reset();
79 DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationThread::~VectorAnimationThread: Join [%p]\n", this);
84 void VectorAnimationThread::AddTask(VectorAnimationTaskPtr task)
86 ConditionalWait::ScopedLock lock(mConditionalWait);
88 // Find if the task is already in the list except loading task
89 auto iter = std::find_if(mAnimationTasks.begin(), mAnimationTasks.end(), [task](VectorAnimationTaskPtr& element) { return (element == task && !element->IsLoadRequested()); });
90 if(iter == mAnimationTasks.end())
92 auto currentTime = task->CalculateNextFrameTime(true); // Rasterize as soon as possible
94 bool inserted = false;
95 for(auto iter = mAnimationTasks.begin(); iter != mAnimationTasks.end(); ++iter)
97 auto nextTime = (*iter)->GetNextFrameTime();
98 if(nextTime > currentTime)
100 mAnimationTasks.insert(iter, task);
108 mAnimationTasks.push_back(task);
111 mNeedToSleep = false;
112 // wake up the animation thread
113 mConditionalWait.Notify(lock);
117 void VectorAnimationThread::OnTaskCompleted(VectorAnimationTaskPtr task, bool success, bool keepAnimation)
121 ConditionalWait::ScopedLock lock(mConditionalWait);
122 bool needRasterize = false;
124 auto workingTask = std::find(mWorkingTasks.begin(), mWorkingTasks.end(), task);
125 if(workingTask != mWorkingTasks.end())
127 mWorkingTasks.erase(workingTask);
130 // Check pending task
131 if(mAnimationTasks.end() != std::find(mAnimationTasks.begin(), mAnimationTasks.end(), task))
133 needRasterize = true;
136 if(keepAnimation && success)
138 if(mCompletedTasks.end() == std::find(mCompletedTasks.begin(), mCompletedTasks.end(), task))
140 mCompletedTasks.push_back(task);
141 needRasterize = true;
147 mNeedToSleep = false;
148 // wake up the animation thread
149 mConditionalWait.Notify(lock);
154 void VectorAnimationThread::OnAwakeFromSleep()
158 mNeedToSleep = false;
159 // wake up the animation thread
160 mConditionalWait.Notify();
164 void VectorAnimationThread::AddEventTriggerCallback(CallbackBase* callback, uint32_t argument)
166 Mutex::ScopedLock lock(mEventTriggerMutex);
169 mTriggerEventCallbacks.emplace_back(callback, argument);
173 mEventTrigger->Trigger();
174 mEventTriggered = true;
179 void VectorAnimationThread::RemoveEventTriggerCallbacks(CallbackBase* callback)
181 Mutex::ScopedLock lock(mEventTriggerMutex);
184 auto iter = std::remove_if(mTriggerEventCallbacks.begin(), mTriggerEventCallbacks.end(), [&callback](std::pair<CallbackBase*, uint32_t>& item) { return item.first == callback; });
185 mTriggerEventCallbacks.erase(iter, mTriggerEventCallbacks.end());
189 void VectorAnimationThread::RequestForceRenderOnce()
191 Mutex::ScopedLock lock(mEventTriggerMutex);
194 mForceRenderOnce = true;
198 mEventTrigger->Trigger();
199 mEventTriggered = true;
204 void VectorAnimationThread::Run()
206 SetThreadName("VectorAnimationThread");
207 mLogFactory.InstallLogFunction();
208 mTraceFactory.InstallTraceFunction();
210 while(!mDestroyThread)
216 void VectorAnimationThread::Rasterize()
218 // Lock while popping task out from the queue
219 ConditionalWait::ScopedLock lock(mConditionalWait);
224 mConditionalWait.Wait(lock);
229 // Process completed tasks
230 for(auto&& task : mCompletedTasks)
232 if(mAnimationTasks.end() == std::find(mAnimationTasks.begin(), mAnimationTasks.end(), task))
234 // Should use the frame rate of the animation file
235 auto nextFrameTime = task->CalculateNextFrameTime(false);
237 bool inserted = false;
238 for(auto iter = mAnimationTasks.begin(); iter != mAnimationTasks.end(); ++iter)
240 auto time = (*iter)->GetNextFrameTime();
241 if(time > nextFrameTime)
243 mAnimationTasks.insert(iter, task);
251 mAnimationTasks.push_back(task);
255 mCompletedTasks.clear();
257 // pop out the next task from the queue
258 for(auto it = mAnimationTasks.begin(); it != mAnimationTasks.end();)
260 VectorAnimationTaskPtr nextTask = *it;
262 auto currentTime = std::chrono::steady_clock::now();
263 auto nextFrameTime = nextTask->GetNextFrameTime();
265 #if defined(DEBUG_ENABLED)
266 // auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(nextFrameTime - currentTime);
268 // DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationThread::Rasterize: [next time = %lld]\n", duration.count());
270 if(nextFrameTime <= currentTime)
272 // If the task is not in the working list
273 if(std::find(mWorkingTasks.begin(), mWorkingTasks.end(), nextTask) == mWorkingTasks.end())
275 it = mAnimationTasks.erase(it);
277 // Add it to the working list
278 mWorkingTasks.push_back(nextTask);
279 mAsyncTaskManager.AddTask(nextTask);
288 mSleepThread.SleepUntil(nextFrameTime);
294 void VectorAnimationThread::OnEventCallbackTriggered()
298 auto callbackPair = GetNextEventCallback();
299 if(callbackPair.first == nullptr)
303 CallbackBase::Execute(*callbackPair.first, callbackPair.second);
305 // Request update once if we need.
307 Mutex::ScopedLock lock(mEventTriggerMutex);
308 if(!mDestroyThread && mForceRenderOnce)
310 mForceRenderOnce = false;
311 if(Dali::Adaptor::IsAvailable())
313 Dali::Adaptor::Get().UpdateOnce();
319 std::pair<CallbackBase*, uint32_t> VectorAnimationThread::GetNextEventCallback()
321 Mutex::ScopedLock lock(mEventTriggerMutex);
324 if(!mTriggerEventCallbacks.empty())
326 auto iter = mTriggerEventCallbacks.begin();
327 auto callbackIdPair = *iter;
328 mTriggerEventCallbacks.erase(iter);
329 return callbackIdPair;
331 mEventTriggered = false;
333 return std::make_pair(nullptr, 0u);
336 VectorAnimationThread::SleepThread::SleepThread(CallbackBase* callback)
337 : mConditionalWait(),
338 mAwakeCallback(std::unique_ptr<CallbackBase>(callback)),
340 mLogFactory(Dali::Adaptor::Get().GetLogFactory()),
341 mTraceFactory(Dali::Adaptor::Get().GetTraceFactory()),
343 mDestroyThread(false)
347 VectorAnimationThread::SleepThread::~SleepThread()
351 ConditionalWait::ScopedLock lock(mConditionalWait);
352 mDestroyThread = true;
353 mConditionalWait.Notify(lock);
359 void VectorAnimationThread::SleepThread::SleepUntil(std::chrono::time_point<std::chrono::steady_clock> timeToSleepUntil)
361 ConditionalWait::ScopedLock lock(mConditionalWait);
362 mSleepTimePoint = timeToSleepUntil;
364 mConditionalWait.Notify(lock);
367 void VectorAnimationThread::SleepThread::Run()
369 SetThreadName("VectorSleepThread");
370 mLogFactory.InstallLogFunction();
371 mTraceFactory.InstallTraceFunction();
373 while(!mDestroyThread)
376 std::chrono::time_point<std::chrono::steady_clock> sleepTimePoint;
379 ConditionalWait::ScopedLock lock(mConditionalWait);
381 needToSleep = mNeedToSleep;
382 sleepTimePoint = mSleepTimePoint;
384 mNeedToSleep = false;
389 #if defined(DEBUG_ENABLED)
390 // auto sleepDuration = std::chrono::duration_cast<std::chrono::milliseconds>(mSleepTimePoint - std::chrono::steady_clock::now());
392 // DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationThread::SleepThread::Run: [sleep duration = %lld]\n", sleepDuration.count());
395 std::this_thread::sleep_until(sleepTimePoint);
399 CallbackBase::Execute(*mAwakeCallback);
404 ConditionalWait::ScopedLock lock(mConditionalWait);
405 if(!mDestroyThread && !mNeedToSleep)
407 mConditionalWait.Wait(lock);
413 } // namespace Internal
415 } // namespace Toolkit