[dali_2.3.21] 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) 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, uint32_t argument)
163 {
164   Mutex::ScopedLock lock(mEventTriggerMutex);
165   if(!mDestroyThread)
166   {
167     mTriggerEventCallbacks.emplace_back(callback, argument);
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_if(mTriggerEventCallbacks.begin(), mTriggerEventCallbacks.end(), [&callback](std::pair<CallbackBase*, uint32_t>& item) { return item.first == 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(true)
280   {
281     auto callbackPair = GetNextEventCallback();
282     if(callbackPair.first == nullptr)
283     {
284       break;
285     }
286     CallbackBase::Execute(*callbackPair.first, callbackPair.second);
287   }
288 }
289
290 std::pair<CallbackBase*, uint32_t> VectorAnimationThread::GetNextEventCallback()
291 {
292   Mutex::ScopedLock lock(mEventTriggerMutex);
293   if(!mDestroyThread)
294   {
295     if(!mTriggerEventCallbacks.empty())
296     {
297       auto iter           = mTriggerEventCallbacks.begin();
298       auto callbackIdPair = *iter;
299       mTriggerEventCallbacks.erase(iter);
300       return callbackIdPair;
301     }
302     mEventTriggered = false;
303   }
304   return std::make_pair(nullptr, 0u);
305 }
306
307 VectorAnimationThread::SleepThread::SleepThread(CallbackBase* callback)
308 : mConditionalWait(),
309   mAwakeCallback(std::unique_ptr<CallbackBase>(callback)),
310   mSleepTimePoint(),
311   mLogFactory(Dali::Adaptor::Get().GetLogFactory()),
312   mTraceFactory(Dali::Adaptor::Get().GetTraceFactory()),
313   mNeedToSleep(false),
314   mDestroyThread(false)
315 {
316 }
317
318 VectorAnimationThread::SleepThread::~SleepThread()
319 {
320   // Stop the thread
321   {
322     ConditionalWait::ScopedLock lock(mConditionalWait);
323     mDestroyThread = true;
324     mConditionalWait.Notify(lock);
325   }
326
327   Join();
328 }
329
330 void VectorAnimationThread::SleepThread::SleepUntil(std::chrono::time_point<std::chrono::steady_clock> timeToSleepUntil)
331 {
332   ConditionalWait::ScopedLock lock(mConditionalWait);
333   mSleepTimePoint = timeToSleepUntil;
334   mNeedToSleep    = true;
335   mConditionalWait.Notify(lock);
336 }
337
338 void VectorAnimationThread::SleepThread::Run()
339 {
340   SetThreadName("VectorSleepThread");
341   mLogFactory.InstallLogFunction();
342   mTraceFactory.InstallTraceFunction();
343
344   while(!mDestroyThread)
345   {
346     bool                                               needToSleep;
347     std::chrono::time_point<std::chrono::steady_clock> sleepTimePoint;
348
349     {
350       ConditionalWait::ScopedLock lock(mConditionalWait);
351
352       needToSleep    = mNeedToSleep;
353       sleepTimePoint = mSleepTimePoint;
354
355       mNeedToSleep = false;
356     }
357
358     if(needToSleep)
359     {
360 #if defined(DEBUG_ENABLED)
361 //      auto sleepDuration = std::chrono::duration_cast<std::chrono::milliseconds>(mSleepTimePoint - std::chrono::steady_clock::now());
362
363 //      DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationThread::SleepThread::Run: [sleep duration = %lld]\n", sleepDuration.count());
364 #endif
365
366       std::this_thread::sleep_until(sleepTimePoint);
367
368       if(mAwakeCallback)
369       {
370         CallbackBase::Execute(*mAwakeCallback);
371       }
372     }
373
374     {
375       ConditionalWait::ScopedLock lock(mConditionalWait);
376       if(!mDestroyThread && !mNeedToSleep)
377       {
378         mConditionalWait.Wait(lock);
379       }
380     }
381   }
382 }
383
384 } // namespace Internal
385
386 } // namespace Toolkit
387
388 } // namespace Dali