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