Merge "[AT-SPI] Update test DBusWrapper for ABI compatibility" into devel/master
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / visuals / animated-vector-image / vector-animation-thread.cpp
1 /*
2  * Copyright (c) 2022 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 constexpr auto DEFAULT_NUMBER_OF_RASTERIZE_THREADS = size_t{4u};
37 constexpr auto NUMBER_OF_RASTERIZE_THREADS_ENV     = "DALI_VECTOR_RASTERIZE_THREADS";
38
39 size_t GetNumberOfThreads(const char* environmentVariable, size_t defaultValue)
40 {
41   using Dali::EnvironmentVariable::GetEnvironmentVariable;
42   auto           numberString          = GetEnvironmentVariable(environmentVariable);
43   auto           numberOfThreads       = numberString ? std::strtoul(numberString, nullptr, 10) : 0;
44   constexpr auto MAX_NUMBER_OF_THREADS = 100u;
45   DALI_ASSERT_DEBUG(numberOfThreads < MAX_NUMBER_OF_THREADS);
46   return (numberOfThreads > 0 && numberOfThreads < MAX_NUMBER_OF_THREADS) ? numberOfThreads : defaultValue;
47 }
48
49 #if defined(DEBUG_ENABLED)
50 Debug::Filter* gVectorAnimationLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_VECTOR_ANIMATION");
51 #endif
52
53 } // unnamed namespace
54
55 VectorAnimationThread::VectorAnimationThread()
56 : mAnimationTasks(),
57   mCompletedTasks(),
58   mWorkingTasks(),
59   mRasterizers(GetNumberOfThreads(NUMBER_OF_RASTERIZE_THREADS_ENV, DEFAULT_NUMBER_OF_RASTERIZE_THREADS), [&]() { return RasterizeHelper(*this); }),
60   mSleepThread(MakeCallback(this, &VectorAnimationThread::OnAwakeFromSleep)),
61   mConditionalWait(),
62   mNeedToSleep(false),
63   mDestroyThread(false),
64   mLogFactory(Dali::Adaptor::Get().GetLogFactory())
65 {
66   mSleepThread.Start();
67 }
68
69 VectorAnimationThread::~VectorAnimationThread()
70 {
71   // Stop the thread
72   {
73     ConditionalWait::ScopedLock lock(mConditionalWait);
74     mDestroyThread = true;
75     mNeedToSleep   = false;
76     mConditionalWait.Notify(lock);
77   }
78
79   DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationThread::~VectorAnimationThread: Join [%p]\n", this);
80
81   Join();
82 }
83
84 void VectorAnimationThread::AddTask(VectorAnimationTaskPtr task)
85 {
86   ConditionalWait::ScopedLock lock(mConditionalWait);
87
88   if(mAnimationTasks.end() == std::find(mAnimationTasks.begin(), mAnimationTasks.end(), task))
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::Run()
163 {
164   SetThreadName("VectorAnimationThread");
165   mLogFactory.InstallLogFunction();
166
167   while(!mDestroyThread)
168   {
169     Rasterize();
170   }
171 }
172
173 void VectorAnimationThread::Rasterize()
174 {
175   // Lock while popping task out from the queue
176   ConditionalWait::ScopedLock lock(mConditionalWait);
177
178   // conditional wait
179   if(mNeedToSleep)
180   {
181     mConditionalWait.Wait(lock);
182   }
183
184   mNeedToSleep = true;
185
186   // Process completed tasks
187   for(auto&& task : mCompletedTasks)
188   {
189     if(mAnimationTasks.end() == std::find(mAnimationTasks.begin(), mAnimationTasks.end(), task))
190     {
191       // Should use the frame rate of the animation file
192       auto nextFrameTime = task->CalculateNextFrameTime(false);
193
194       bool inserted = false;
195       for(auto iter = mAnimationTasks.begin(); iter != mAnimationTasks.end(); ++iter)
196       {
197         auto time = (*iter)->GetNextFrameTime();
198         if(time > nextFrameTime)
199         {
200           mAnimationTasks.insert(iter, task);
201           inserted = true;
202           break;
203         }
204       }
205
206       if(!inserted)
207       {
208         mAnimationTasks.push_back(task);
209       }
210     }
211   }
212   mCompletedTasks.clear();
213
214   // pop out the next task from the queue
215   for(auto it = mAnimationTasks.begin(); it != mAnimationTasks.end();)
216   {
217     VectorAnimationTaskPtr nextTask = *it;
218
219     auto currentTime   = std::chrono::steady_clock::now();
220     auto nextFrameTime = nextTask->GetNextFrameTime();
221
222 #if defined(DEBUG_ENABLED)
223 //    auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(nextFrameTime - currentTime);
224
225 //    DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationThread::Rasterize: [next time = %lld]\n", duration.count());
226 #endif
227
228     if(nextFrameTime <= currentTime)
229     {
230       // If the task is not in the working list
231       if(std::find(mWorkingTasks.begin(), mWorkingTasks.end(), nextTask) == mWorkingTasks.end())
232       {
233         it = mAnimationTasks.erase(it);
234
235         // Add it to the working list
236         mWorkingTasks.push_back(nextTask);
237
238         auto rasterizerHelperIt = mRasterizers.GetNext();
239         DALI_ASSERT_ALWAYS(rasterizerHelperIt != mRasterizers.End());
240
241         rasterizerHelperIt->Rasterize(nextTask);
242       }
243       else
244       {
245         it++;
246       }
247     }
248     else
249     {
250       mSleepThread.SleepUntil(nextFrameTime);
251       break;
252     }
253   }
254 }
255
256 VectorAnimationThread::RasterizeHelper::RasterizeHelper(VectorAnimationThread& animationThread)
257 : RasterizeHelper(std::unique_ptr<VectorRasterizeThread>(new VectorRasterizeThread()), animationThread)
258 {
259 }
260
261 VectorAnimationThread::RasterizeHelper::RasterizeHelper(RasterizeHelper&& rhs)
262 : RasterizeHelper(std::move(rhs.mRasterizer), rhs.mAnimationThread)
263 {
264 }
265
266 VectorAnimationThread::RasterizeHelper::RasterizeHelper(std::unique_ptr<VectorRasterizeThread> rasterizer, VectorAnimationThread& animationThread)
267 : mRasterizer(std::move(rasterizer)),
268   mAnimationThread(animationThread)
269 {
270   mRasterizer->SetCompletedCallback(MakeCallback(&mAnimationThread, &VectorAnimationThread::OnTaskCompleted));
271 }
272
273 void VectorAnimationThread::RasterizeHelper::Rasterize(VectorAnimationTaskPtr task)
274 {
275   if(task)
276   {
277     mRasterizer->AddTask(task);
278   }
279 }
280
281 VectorAnimationThread::SleepThread::SleepThread(CallbackBase* callback)
282 : mConditionalWait(),
283   mAwakeCallback(std::unique_ptr<CallbackBase>(callback)),
284   mSleepTimePoint(),
285   mLogFactory(Dali::Adaptor::Get().GetLogFactory()),
286   mNeedToSleep(false),
287   mDestroyThread(false)
288 {
289 }
290
291 VectorAnimationThread::SleepThread::~SleepThread()
292 {
293   // Stop the thread
294   {
295     ConditionalWait::ScopedLock lock(mConditionalWait);
296     mDestroyThread = true;
297     mConditionalWait.Notify(lock);
298   }
299
300   Join();
301 }
302
303 void VectorAnimationThread::SleepThread::SleepUntil(std::chrono::time_point<std::chrono::steady_clock> timeToSleepUntil)
304 {
305   ConditionalWait::ScopedLock lock(mConditionalWait);
306   mSleepTimePoint = timeToSleepUntil;
307   mNeedToSleep    = true;
308   mConditionalWait.Notify(lock);
309 }
310
311 void VectorAnimationThread::SleepThread::Run()
312 {
313   SetThreadName("VectorSleepThread");
314   mLogFactory.InstallLogFunction();
315
316   while(!mDestroyThread)
317   {
318     bool                                               needToSleep;
319     std::chrono::time_point<std::chrono::steady_clock> sleepTimePoint;
320
321     {
322       ConditionalWait::ScopedLock lock(mConditionalWait);
323
324       needToSleep    = mNeedToSleep;
325       sleepTimePoint = mSleepTimePoint;
326
327       mNeedToSleep = false;
328     }
329
330     if(needToSleep)
331     {
332 #if defined(DEBUG_ENABLED)
333 //      auto sleepDuration = std::chrono::duration_cast<std::chrono::milliseconds>(mSleepTimePoint - std::chrono::steady_clock::now());
334
335 //      DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationThread::SleepThread::Run: [sleep duration = %lld]\n", sleepDuration.count());
336 #endif
337
338       std::this_thread::sleep_until(sleepTimePoint);
339
340       if(mAwakeCallback)
341       {
342         CallbackBase::Execute(*mAwakeCallback);
343       }
344     }
345
346     {
347       ConditionalWait::ScopedLock lock(mConditionalWait);
348       if(!mDestroyThread && !mNeedToSleep)
349       {
350         mConditionalWait.Wait(lock);
351       }
352     }
353   }
354 }
355
356 } // namespace Internal
357
358 } // namespace Toolkit
359
360 } // namespace Dali