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