3ee2d5da8aa17a24301b1ee3112867f2fb137f22
[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   mNeedToSleep(false),
50   mDestroyThread(false),
51   mLogFactory(Dali::Adaptor::Get().GetLogFactory()),
52   mTraceFactory(Dali::Adaptor::Get().GetTraceFactory())
53 {
54   mAsyncTaskManager = Dali::AsyncTaskManager::Get();
55   mSleepThread.Start();
56
57   mEventTrigger = std::unique_ptr<EventThreadCallback>(new EventThreadCallback(MakeCallback(this, &VectorAnimationThread::OnEventCallbackTriggered)));
58 }
59
60 VectorAnimationThread::~VectorAnimationThread()
61 {
62   // Stop the thread
63   {
64     ConditionalWait::ScopedLock lock(mConditionalWait);
65     // Wait until some event thread trigger relative job finished.
66     {
67       Mutex::ScopedLock lock(mEventTriggerMutex);
68       mDestroyThread = true;
69     }
70     mNeedToSleep = false;
71     mConditionalWait.Notify(lock);
72   }
73
74   // Stop event trigger
75   mEventTrigger.reset();
76
77   DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationThread::~VectorAnimationThread: Join [%p]\n", this);
78
79   Join();
80 }
81
82 void VectorAnimationThread::AddTask(VectorAnimationTaskPtr task)
83 {
84   ConditionalWait::ScopedLock lock(mConditionalWait);
85
86   // Find if the task is already in the list except loading task
87   auto iter = std::find_if(mAnimationTasks.begin(), mAnimationTasks.end(), [task](VectorAnimationTaskPtr& element) { return (element == task && !element->IsLoadRequested()); });
88   if(iter == mAnimationTasks.end())
89   {
90     auto currentTime = task->CalculateNextFrameTime(true); // Rasterize as soon as possible
91
92     bool inserted = false;
93     for(auto iter = mAnimationTasks.begin(); iter != mAnimationTasks.end(); ++iter)
94     {
95       auto nextTime = (*iter)->GetNextFrameTime();
96       if(nextTime > currentTime)
97       {
98         mAnimationTasks.insert(iter, task);
99         inserted = true;
100         break;
101       }
102     }
103
104     if(!inserted)
105     {
106       mAnimationTasks.push_back(task);
107     }
108
109     mNeedToSleep = false;
110     // wake up the animation thread
111     mConditionalWait.Notify(lock);
112   }
113 }
114
115 void VectorAnimationThread::OnTaskCompleted(VectorAnimationTaskPtr task, bool success, bool keepAnimation)
116 {
117   if(!mDestroyThread)
118   {
119     ConditionalWait::ScopedLock lock(mConditionalWait);
120     bool                        needRasterize = false;
121
122     auto workingTask = std::find(mWorkingTasks.begin(), mWorkingTasks.end(), task);
123     if(workingTask != mWorkingTasks.end())
124     {
125       mWorkingTasks.erase(workingTask);
126     }
127
128     // Check pending task
129     if(mAnimationTasks.end() != std::find(mAnimationTasks.begin(), mAnimationTasks.end(), task))
130     {
131       needRasterize = true;
132     }
133
134     if(keepAnimation && success)
135     {
136       if(mCompletedTasks.end() == std::find(mCompletedTasks.begin(), mCompletedTasks.end(), task))
137       {
138         mCompletedTasks.push_back(task);
139         needRasterize = true;
140       }
141     }
142
143     if(needRasterize)
144     {
145       mNeedToSleep = false;
146       // wake up the animation thread
147       mConditionalWait.Notify(lock);
148     }
149   }
150 }
151
152 void VectorAnimationThread::OnAwakeFromSleep()
153 {
154   if(!mDestroyThread)
155   {
156     mNeedToSleep = false;
157     // wake up the animation thread
158     mConditionalWait.Notify();
159   }
160 }
161
162 void VectorAnimationThread::AddEventTriggerCallback(CallbackBase* callback)
163 {
164   Mutex::ScopedLock lock(mEventTriggerMutex);
165   if(!mDestroyThread)
166   {
167     mTriggerEventCallbacks.push_back(callback);
168
169     if(!mEventTriggered)
170     {
171       mEventTrigger->Trigger();
172       mEventTriggered = true;
173     }
174   }
175 }
176
177 void VectorAnimationThread::RemoveEventTriggerCallbacks(CallbackBase* callback)
178 {
179   Mutex::ScopedLock lock(mEventTriggerMutex);
180   if(!mDestroyThread)
181   {
182     auto iter = std::remove(mTriggerEventCallbacks.begin(), mTriggerEventCallbacks.end(), callback);
183     mTriggerEventCallbacks.erase(iter, mTriggerEventCallbacks.end());
184   }
185 }
186
187 void VectorAnimationThread::Run()
188 {
189   SetThreadName("VectorAnimationThread");
190   mLogFactory.InstallLogFunction();
191   mTraceFactory.InstallTraceFunction();
192
193   while(!mDestroyThread)
194   {
195     Rasterize();
196   }
197 }
198
199 void VectorAnimationThread::Rasterize()
200 {
201   // Lock while popping task out from the queue
202   ConditionalWait::ScopedLock lock(mConditionalWait);
203
204   // conditional wait
205   if(mNeedToSleep)
206   {
207     mConditionalWait.Wait(lock);
208   }
209
210   mNeedToSleep = true;
211
212   // Process completed tasks
213   for(auto&& task : mCompletedTasks)
214   {
215     if(mAnimationTasks.end() == std::find(mAnimationTasks.begin(), mAnimationTasks.end(), task))
216     {
217       // Should use the frame rate of the animation file
218       auto nextFrameTime = task->CalculateNextFrameTime(false);
219
220       bool inserted = false;
221       for(auto iter = mAnimationTasks.begin(); iter != mAnimationTasks.end(); ++iter)
222       {
223         auto time = (*iter)->GetNextFrameTime();
224         if(time > nextFrameTime)
225         {
226           mAnimationTasks.insert(iter, task);
227           inserted = true;
228           break;
229         }
230       }
231
232       if(!inserted)
233       {
234         mAnimationTasks.push_back(task);
235       }
236     }
237   }
238   mCompletedTasks.clear();
239
240   // pop out the next task from the queue
241   for(auto it = mAnimationTasks.begin(); it != mAnimationTasks.end();)
242   {
243     VectorAnimationTaskPtr nextTask = *it;
244
245     auto currentTime   = std::chrono::steady_clock::now();
246     auto nextFrameTime = nextTask->GetNextFrameTime();
247
248 #if defined(DEBUG_ENABLED)
249 //    auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(nextFrameTime - currentTime);
250
251 //    DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationThread::Rasterize: [next time = %lld]\n", duration.count());
252 #endif
253     if(nextFrameTime <= currentTime)
254     {
255       // If the task is not in the working list
256       if(std::find(mWorkingTasks.begin(), mWorkingTasks.end(), nextTask) == mWorkingTasks.end())
257       {
258         it = mAnimationTasks.erase(it);
259
260         // Add it to the working list
261         mWorkingTasks.push_back(nextTask);
262         mAsyncTaskManager.AddTask(nextTask);
263       }
264       else
265       {
266         it++;
267       }
268     }
269     else
270     {
271       mSleepThread.SleepUntil(nextFrameTime);
272       break;
273     }
274   }
275 }
276
277 void VectorAnimationThread::OnEventCallbackTriggered()
278 {
279   while(CallbackBase* callback = GetNextEventCallback())
280   {
281     CallbackBase::Execute(*callback);
282   }
283 }
284
285 CallbackBase* VectorAnimationThread::GetNextEventCallback()
286 {
287   Mutex::ScopedLock lock(mEventTriggerMutex);
288   if(!mDestroyThread)
289   {
290     if(!mTriggerEventCallbacks.empty())
291     {
292       auto          iter     = mTriggerEventCallbacks.begin();
293       CallbackBase* callback = *iter;
294       mTriggerEventCallbacks.erase(iter);
295       return callback;
296     }
297     mEventTriggered = false;
298   }
299   return nullptr;
300 }
301
302 VectorAnimationThread::SleepThread::SleepThread(CallbackBase* callback)
303 : mConditionalWait(),
304   mAwakeCallback(std::unique_ptr<CallbackBase>(callback)),
305   mSleepTimePoint(),
306   mLogFactory(Dali::Adaptor::Get().GetLogFactory()),
307   mTraceFactory(Dali::Adaptor::Get().GetTraceFactory()),
308   mNeedToSleep(false),
309   mDestroyThread(false)
310 {
311 }
312
313 VectorAnimationThread::SleepThread::~SleepThread()
314 {
315   // Stop the thread
316   {
317     ConditionalWait::ScopedLock lock(mConditionalWait);
318     mDestroyThread = true;
319     mConditionalWait.Notify(lock);
320   }
321
322   Join();
323 }
324
325 void VectorAnimationThread::SleepThread::SleepUntil(std::chrono::time_point<std::chrono::steady_clock> timeToSleepUntil)
326 {
327   ConditionalWait::ScopedLock lock(mConditionalWait);
328   mSleepTimePoint = timeToSleepUntil;
329   mNeedToSleep    = true;
330   mConditionalWait.Notify(lock);
331 }
332
333 void VectorAnimationThread::SleepThread::Run()
334 {
335   SetThreadName("VectorSleepThread");
336   mLogFactory.InstallLogFunction();
337   mTraceFactory.InstallTraceFunction();
338
339   while(!mDestroyThread)
340   {
341     bool                                               needToSleep;
342     std::chrono::time_point<std::chrono::steady_clock> sleepTimePoint;
343
344     {
345       ConditionalWait::ScopedLock lock(mConditionalWait);
346
347       needToSleep    = mNeedToSleep;
348       sleepTimePoint = mSleepTimePoint;
349
350       mNeedToSleep = false;
351     }
352
353     if(needToSleep)
354     {
355 #if defined(DEBUG_ENABLED)
356 //      auto sleepDuration = std::chrono::duration_cast<std::chrono::milliseconds>(mSleepTimePoint - std::chrono::steady_clock::now());
357
358 //      DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationThread::SleepThread::Run: [sleep duration = %lld]\n", sleepDuration.count());
359 #endif
360
361       std::this_thread::sleep_until(sleepTimePoint);
362
363       if(mAwakeCallback)
364       {
365         CallbackBase::Execute(*mAwakeCallback);
366       }
367     }
368
369     {
370       ConditionalWait::ScopedLock lock(mConditionalWait);
371       if(!mDestroyThread && !mNeedToSleep)
372       {
373         mConditionalWait.Wait(lock);
374       }
375     }
376   }
377 }
378
379 } // namespace Internal
380
381 } // namespace Toolkit
382
383 } // namespace Dali