Merge "Fix Gradient Visual texturing issue" into 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   ConditionalWait::ScopedLock lock(mConditionalWait);
267
268   for(auto&& iter : mTriggerEventCallbacks)
269   {
270     CallbackBase::Execute(*iter);
271   }
272
273   mTriggerEventCallbacks.clear();
274   mEventTriggered = false;
275 }
276
277 VectorAnimationThread::SleepThread::SleepThread(CallbackBase* callback)
278 : mConditionalWait(),
279   mAwakeCallback(std::unique_ptr<CallbackBase>(callback)),
280   mSleepTimePoint(),
281   mLogFactory(Dali::Adaptor::Get().GetLogFactory()),
282   mNeedToSleep(false),
283   mDestroyThread(false)
284 {
285 }
286
287 VectorAnimationThread::SleepThread::~SleepThread()
288 {
289   // Stop the thread
290   {
291     ConditionalWait::ScopedLock lock(mConditionalWait);
292     mDestroyThread = true;
293     mConditionalWait.Notify(lock);
294   }
295
296   Join();
297 }
298
299 void VectorAnimationThread::SleepThread::SleepUntil(std::chrono::time_point<std::chrono::steady_clock> timeToSleepUntil)
300 {
301   ConditionalWait::ScopedLock lock(mConditionalWait);
302   mSleepTimePoint = timeToSleepUntil;
303   mNeedToSleep    = true;
304   mConditionalWait.Notify(lock);
305 }
306
307 void VectorAnimationThread::SleepThread::Run()
308 {
309   SetThreadName("VectorSleepThread");
310   mLogFactory.InstallLogFunction();
311
312   while(!mDestroyThread)
313   {
314     bool                                               needToSleep;
315     std::chrono::time_point<std::chrono::steady_clock> sleepTimePoint;
316
317     {
318       ConditionalWait::ScopedLock lock(mConditionalWait);
319
320       needToSleep    = mNeedToSleep;
321       sleepTimePoint = mSleepTimePoint;
322
323       mNeedToSleep = false;
324     }
325
326     if(needToSleep)
327     {
328 #if defined(DEBUG_ENABLED)
329 //      auto sleepDuration = std::chrono::duration_cast<std::chrono::milliseconds>(mSleepTimePoint - std::chrono::steady_clock::now());
330
331 //      DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationThread::SleepThread::Run: [sleep duration = %lld]\n", sleepDuration.count());
332 #endif
333
334       std::this_thread::sleep_until(sleepTimePoint);
335
336       if(mAwakeCallback)
337       {
338         CallbackBase::Execute(*mAwakeCallback);
339       }
340     }
341
342     {
343       ConditionalWait::ScopedLock lock(mConditionalWait);
344       if(!mDestroyThread && !mNeedToSleep)
345       {
346         mConditionalWait.Wait(lock);
347       }
348     }
349   }
350 }
351
352 } // namespace Internal
353
354 } // namespace Toolkit
355
356 } // namespace Dali