5638ec3a0bb060c62c82cc0ac972a67845fff626
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / visuals / animated-vector-image / vector-animation-thread.cpp
1 /*
2  * Copyright (c) 2024 Samsung Electronics Co., Ltd.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  *
16  */
17
18 // CLASS HEADER
19 #include <dali-toolkit/internal/visuals/animated-vector-image/vector-animation-thread.h>
20
21 // EXTERNAL INCLUDES
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>
26 #include <thread>
27
28 namespace Dali
29 {
30 namespace Toolkit
31 {
32 namespace Internal
33 {
34 namespace
35 {
36 #if defined(DEBUG_ENABLED)
37 Debug::Filter* gVectorAnimationLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_VECTOR_ANIMATION");
38 #endif
39
40 } // unnamed namespace
41
42 VectorAnimationThread::VectorAnimationThread()
43 : mAnimationTasks(),
44   mCompletedTasks(),
45   mWorkingTasks(),
46   mSleepThread(MakeCallback(this, &VectorAnimationThread::OnAwakeFromSleep)),
47   mConditionalWait(),
48   mEventTriggerMutex(),
49   mLogFactory(Dali::Adaptor::Get().GetLogFactory()),
50   mTraceFactory(Dali::Adaptor::Get().GetTraceFactory()),
51   mNeedToSleep(false),
52   mDestroyThread(false),
53   mEventTriggered(false),
54   mForceRenderOnce(false)
55 {
56   mAsyncTaskManager = Dali::AsyncTaskManager::Get();
57   mSleepThread.Start();
58
59   mEventTrigger = std::unique_ptr<EventThreadCallback>(new EventThreadCallback(MakeCallback(this, &VectorAnimationThread::OnEventCallbackTriggered)));
60 }
61
62 VectorAnimationThread::~VectorAnimationThread()
63 {
64   // Stop the thread
65   {
66     ConditionalWait::ScopedLock lock(mConditionalWait);
67     // Wait until some event thread trigger relative job finished.
68     {
69       Mutex::ScopedLock lock(mEventTriggerMutex);
70       mDestroyThread = true;
71     }
72     mNeedToSleep = false;
73     mConditionalWait.Notify(lock);
74   }
75
76   // Stop event trigger
77   mEventTrigger.reset();
78
79   DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationThread::~VectorAnimationThread: Join [%p]\n", this);
80
81   Join();
82 }
83
84 void VectorAnimationThread::AddTask(VectorAnimationTaskPtr task)
85 {
86   ConditionalWait::ScopedLock lock(mConditionalWait);
87
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())
91   {
92     auto currentTime = task->CalculateNextFrameTime(true); // Rasterize as soon as possible
93
94     bool inserted = false;
95     for(auto iter = mAnimationTasks.begin(); iter != mAnimationTasks.end(); ++iter)
96     {
97       auto nextTime = (*iter)->GetNextFrameTime();
98       if(nextTime > currentTime)
99       {
100         mAnimationTasks.insert(iter, task);
101         inserted = true;
102         break;
103       }
104     }
105
106     if(!inserted)
107     {
108       mAnimationTasks.push_back(task);
109     }
110
111     mNeedToSleep = false;
112     // wake up the animation thread
113     mConditionalWait.Notify(lock);
114   }
115 }
116
117 void VectorAnimationThread::OnTaskCompleted(VectorAnimationTaskPtr task, bool success, bool keepAnimation)
118 {
119   if(!mDestroyThread)
120   {
121     ConditionalWait::ScopedLock lock(mConditionalWait);
122     bool                        needRasterize = false;
123
124     auto workingTask = std::find(mWorkingTasks.begin(), mWorkingTasks.end(), task);
125     if(workingTask != mWorkingTasks.end())
126     {
127       mWorkingTasks.erase(workingTask);
128     }
129
130     // Check pending task
131     if(mAnimationTasks.end() != std::find(mAnimationTasks.begin(), mAnimationTasks.end(), task))
132     {
133       needRasterize = true;
134     }
135
136     if(keepAnimation && success)
137     {
138       if(mCompletedTasks.end() == std::find(mCompletedTasks.begin(), mCompletedTasks.end(), task))
139       {
140         mCompletedTasks.push_back(task);
141         needRasterize = true;
142       }
143     }
144
145     if(needRasterize)
146     {
147       mNeedToSleep = false;
148       // wake up the animation thread
149       mConditionalWait.Notify(lock);
150     }
151   }
152 }
153
154 void VectorAnimationThread::OnAwakeFromSleep()
155 {
156   if(!mDestroyThread)
157   {
158     mNeedToSleep = false;
159     // wake up the animation thread
160     mConditionalWait.Notify();
161   }
162 }
163
164 void VectorAnimationThread::AddEventTriggerCallback(CallbackBase* callback, uint32_t argument)
165 {
166   Mutex::ScopedLock lock(mEventTriggerMutex);
167   if(!mDestroyThread)
168   {
169     mTriggerEventCallbacks.emplace_back(callback, argument);
170
171     if(!mEventTriggered)
172     {
173       mEventTrigger->Trigger();
174       mEventTriggered = true;
175     }
176   }
177 }
178
179 void VectorAnimationThread::RemoveEventTriggerCallbacks(CallbackBase* callback)
180 {
181   Mutex::ScopedLock lock(mEventTriggerMutex);
182   if(!mDestroyThread)
183   {
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());
186   }
187 }
188
189 void VectorAnimationThread::RequestForceRenderOnce()
190 {
191   Mutex::ScopedLock lock(mEventTriggerMutex);
192   if(!mDestroyThread)
193   {
194     mForceRenderOnce = true;
195
196     if(!mEventTriggered)
197     {
198       mEventTrigger->Trigger();
199       mEventTriggered = true;
200     }
201   }
202 }
203
204 void VectorAnimationThread::Run()
205 {
206   SetThreadName("VectorAnimationThread");
207   mLogFactory.InstallLogFunction();
208   mTraceFactory.InstallTraceFunction();
209
210   while(!mDestroyThread)
211   {
212     Rasterize();
213   }
214 }
215
216 void VectorAnimationThread::Rasterize()
217 {
218   // Lock while popping task out from the queue
219   ConditionalWait::ScopedLock lock(mConditionalWait);
220
221   // conditional wait
222   if(mNeedToSleep)
223   {
224     mConditionalWait.Wait(lock);
225   }
226
227   mNeedToSleep = true;
228
229   // Process completed tasks
230   for(auto&& task : mCompletedTasks)
231   {
232     if(mAnimationTasks.end() == std::find(mAnimationTasks.begin(), mAnimationTasks.end(), task))
233     {
234       // Should use the frame rate of the animation file
235       auto nextFrameTime = task->CalculateNextFrameTime(false);
236
237       bool inserted = false;
238       for(auto iter = mAnimationTasks.begin(); iter != mAnimationTasks.end(); ++iter)
239       {
240         auto time = (*iter)->GetNextFrameTime();
241         if(time > nextFrameTime)
242         {
243           mAnimationTasks.insert(iter, task);
244           inserted = true;
245           break;
246         }
247       }
248
249       if(!inserted)
250       {
251         mAnimationTasks.push_back(task);
252       }
253     }
254   }
255   mCompletedTasks.clear();
256
257   // pop out the next task from the queue
258   for(auto it = mAnimationTasks.begin(); it != mAnimationTasks.end();)
259   {
260     VectorAnimationTaskPtr nextTask = *it;
261
262     auto currentTime   = std::chrono::steady_clock::now();
263     auto nextFrameTime = nextTask->GetNextFrameTime();
264
265 #if defined(DEBUG_ENABLED)
266 //    auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(nextFrameTime - currentTime);
267
268 //    DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationThread::Rasterize: [next time = %lld]\n", duration.count());
269 #endif
270     if(nextFrameTime <= currentTime)
271     {
272       // If the task is not in the working list
273       if(std::find(mWorkingTasks.begin(), mWorkingTasks.end(), nextTask) == mWorkingTasks.end())
274       {
275         it = mAnimationTasks.erase(it);
276
277         // Add it to the working list
278         mWorkingTasks.push_back(nextTask);
279         mAsyncTaskManager.AddTask(nextTask);
280       }
281       else
282       {
283         it++;
284       }
285     }
286     else
287     {
288       mSleepThread.SleepUntil(nextFrameTime);
289       break;
290     }
291   }
292 }
293
294 void VectorAnimationThread::OnEventCallbackTriggered()
295 {
296   while(true)
297   {
298     auto callbackPair = GetNextEventCallback();
299     if(callbackPair.first == nullptr)
300     {
301       break;
302     }
303     CallbackBase::Execute(*callbackPair.first, callbackPair.second);
304   }
305   // Request update once if we need.
306   {
307     Mutex::ScopedLock lock(mEventTriggerMutex);
308     if(!mDestroyThread && mForceRenderOnce)
309     {
310       mForceRenderOnce = false;
311       if(Dali::Adaptor::IsAvailable())
312       {
313         Dali::Adaptor::Get().UpdateOnce();
314       }
315     }
316   }
317 }
318
319 std::pair<CallbackBase*, uint32_t> VectorAnimationThread::GetNextEventCallback()
320 {
321   Mutex::ScopedLock lock(mEventTriggerMutex);
322   if(!mDestroyThread)
323   {
324     if(!mTriggerEventCallbacks.empty())
325     {
326       auto iter           = mTriggerEventCallbacks.begin();
327       auto callbackIdPair = *iter;
328       mTriggerEventCallbacks.erase(iter);
329       return callbackIdPair;
330     }
331     mEventTriggered = false;
332   }
333   return std::make_pair(nullptr, 0u);
334 }
335
336 VectorAnimationThread::SleepThread::SleepThread(CallbackBase* callback)
337 : mConditionalWait(),
338   mAwakeCallback(std::unique_ptr<CallbackBase>(callback)),
339   mSleepTimePoint(),
340   mLogFactory(Dali::Adaptor::Get().GetLogFactory()),
341   mTraceFactory(Dali::Adaptor::Get().GetTraceFactory()),
342   mNeedToSleep(false),
343   mDestroyThread(false)
344 {
345 }
346
347 VectorAnimationThread::SleepThread::~SleepThread()
348 {
349   // Stop the thread
350   {
351     ConditionalWait::ScopedLock lock(mConditionalWait);
352     mDestroyThread = true;
353     mConditionalWait.Notify(lock);
354   }
355
356   Join();
357 }
358
359 void VectorAnimationThread::SleepThread::SleepUntil(std::chrono::time_point<std::chrono::steady_clock> timeToSleepUntil)
360 {
361   ConditionalWait::ScopedLock lock(mConditionalWait);
362   mSleepTimePoint = timeToSleepUntil;
363   mNeedToSleep    = true;
364   mConditionalWait.Notify(lock);
365 }
366
367 void VectorAnimationThread::SleepThread::Run()
368 {
369   SetThreadName("VectorSleepThread");
370   mLogFactory.InstallLogFunction();
371   mTraceFactory.InstallTraceFunction();
372
373   while(!mDestroyThread)
374   {
375     bool                                               needToSleep;
376     std::chrono::time_point<std::chrono::steady_clock> sleepTimePoint;
377
378     {
379       ConditionalWait::ScopedLock lock(mConditionalWait);
380
381       needToSleep    = mNeedToSleep;
382       sleepTimePoint = mSleepTimePoint;
383
384       mNeedToSleep = false;
385     }
386
387     if(needToSleep)
388     {
389 #if defined(DEBUG_ENABLED)
390 //      auto sleepDuration = std::chrono::duration_cast<std::chrono::milliseconds>(mSleepTimePoint - std::chrono::steady_clock::now());
391
392 //      DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationThread::SleepThread::Run: [sleep duration = %lld]\n", sleepDuration.count());
393 #endif
394
395       std::this_thread::sleep_until(sleepTimePoint);
396
397       if(mAwakeCallback)
398       {
399         CallbackBase::Execute(*mAwakeCallback);
400       }
401     }
402
403     {
404       ConditionalWait::ScopedLock lock(mConditionalWait);
405       if(!mDestroyThread && !mNeedToSleep)
406       {
407         mConditionalWait.Wait(lock);
408       }
409     }
410   }
411 }
412
413 } // namespace Internal
414
415 } // namespace Toolkit
416
417 } // namespace Dali