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