(Vector) Change SetPlayRange and fix a crash
[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   // Find if the task is already in the list except loading task
89   auto iter = std::find_if(mAnimationTasks.begin(), mAnimationTasks.end(), [task](VectorAnimationTaskPtr& element) { return (element == task && !element->IsLoadRequested()); });
90   if(iter == mAnimationTasks.end())
91   {
92     auto currentTime = task->CalculateNextFrameTime(true); // Rasterize as soon as possible
93
94     bool inserted = false;
95     for(auto iter = mAnimationTasks.begin(); iter != mAnimationTasks.end(); ++iter)
96     {
97       auto nextTime = (*iter)->GetNextFrameTime();
98       if(nextTime > currentTime)
99       {
100         mAnimationTasks.insert(iter, task);
101         inserted = true;
102         break;
103       }
104     }
105
106     if(!inserted)
107     {
108       mAnimationTasks.push_back(task);
109     }
110
111     mNeedToSleep = false;
112     // wake up the animation thread
113     mConditionalWait.Notify(lock);
114   }
115 }
116
117 void VectorAnimationThread::OnTaskCompleted(VectorAnimationTaskPtr task, bool success, bool keepAnimation)
118 {
119   if(!mDestroyThread)
120   {
121     ConditionalWait::ScopedLock lock(mConditionalWait);
122     bool                        needRasterize = false;
123
124     auto workingTask = std::find(mWorkingTasks.begin(), mWorkingTasks.end(), task);
125     if(workingTask != mWorkingTasks.end())
126     {
127       mWorkingTasks.erase(workingTask);
128     }
129
130     // Check pending task
131     if(mAnimationTasks.end() != std::find(mAnimationTasks.begin(), mAnimationTasks.end(), task))
132     {
133       needRasterize = true;
134     }
135
136     if(keepAnimation && success)
137     {
138       if(mCompletedTasks.end() == std::find(mCompletedTasks.begin(), mCompletedTasks.end(), task))
139       {
140         mCompletedTasks.push_back(task);
141         needRasterize = true;
142       }
143     }
144
145     if(needRasterize)
146     {
147       mNeedToSleep = false;
148       // wake up the animation thread
149       mConditionalWait.Notify(lock);
150     }
151   }
152 }
153
154 void VectorAnimationThread::OnAwakeFromSleep()
155 {
156   if(!mDestroyThread)
157   {
158     mNeedToSleep = false;
159     // wake up the animation thread
160     mConditionalWait.Notify();
161   }
162 }
163
164 void VectorAnimationThread::Run()
165 {
166   SetThreadName("VectorAnimationThread");
167   mLogFactory.InstallLogFunction();
168
169   while(!mDestroyThread)
170   {
171     Rasterize();
172   }
173 }
174
175 void VectorAnimationThread::Rasterize()
176 {
177   // Lock while popping task out from the queue
178   ConditionalWait::ScopedLock lock(mConditionalWait);
179
180   // conditional wait
181   if(mNeedToSleep)
182   {
183     mConditionalWait.Wait(lock);
184   }
185
186   mNeedToSleep = true;
187
188   // Process completed tasks
189   for(auto&& task : mCompletedTasks)
190   {
191     if(mAnimationTasks.end() == std::find(mAnimationTasks.begin(), mAnimationTasks.end(), task))
192     {
193       // Should use the frame rate of the animation file
194       auto nextFrameTime = task->CalculateNextFrameTime(false);
195
196       bool inserted = false;
197       for(auto iter = mAnimationTasks.begin(); iter != mAnimationTasks.end(); ++iter)
198       {
199         auto time = (*iter)->GetNextFrameTime();
200         if(time > nextFrameTime)
201         {
202           mAnimationTasks.insert(iter, task);
203           inserted = true;
204           break;
205         }
206       }
207
208       if(!inserted)
209       {
210         mAnimationTasks.push_back(task);
211       }
212     }
213   }
214   mCompletedTasks.clear();
215
216   // pop out the next task from the queue
217   for(auto it = mAnimationTasks.begin(); it != mAnimationTasks.end();)
218   {
219     VectorAnimationTaskPtr nextTask = *it;
220
221     auto currentTime   = std::chrono::steady_clock::now();
222     auto nextFrameTime = nextTask->GetNextFrameTime();
223
224 #if defined(DEBUG_ENABLED)
225 //    auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(nextFrameTime - currentTime);
226
227 //    DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationThread::Rasterize: [next time = %lld]\n", duration.count());
228 #endif
229
230     if(nextFrameTime <= currentTime)
231     {
232       // If the task is not in the working list
233       if(std::find(mWorkingTasks.begin(), mWorkingTasks.end(), nextTask) == mWorkingTasks.end())
234       {
235         it = mAnimationTasks.erase(it);
236
237         // Add it to the working list
238         mWorkingTasks.push_back(nextTask);
239
240         auto rasterizerHelperIt = mRasterizers.GetNext();
241         DALI_ASSERT_ALWAYS(rasterizerHelperIt != mRasterizers.End());
242
243         rasterizerHelperIt->Rasterize(nextTask);
244       }
245       else
246       {
247         it++;
248       }
249     }
250     else
251     {
252       mSleepThread.SleepUntil(nextFrameTime);
253       break;
254     }
255   }
256 }
257
258 VectorAnimationThread::RasterizeHelper::RasterizeHelper(VectorAnimationThread& animationThread)
259 : RasterizeHelper(std::unique_ptr<VectorRasterizeThread>(new VectorRasterizeThread()), animationThread)
260 {
261 }
262
263 VectorAnimationThread::RasterizeHelper::RasterizeHelper(RasterizeHelper&& rhs)
264 : RasterizeHelper(std::move(rhs.mRasterizer), rhs.mAnimationThread)
265 {
266 }
267
268 VectorAnimationThread::RasterizeHelper::RasterizeHelper(std::unique_ptr<VectorRasterizeThread> rasterizer, VectorAnimationThread& animationThread)
269 : mRasterizer(std::move(rasterizer)),
270   mAnimationThread(animationThread)
271 {
272   mRasterizer->SetCompletedCallback(MakeCallback(&mAnimationThread, &VectorAnimationThread::OnTaskCompleted));
273 }
274
275 void VectorAnimationThread::RasterizeHelper::Rasterize(VectorAnimationTaskPtr task)
276 {
277   if(task)
278   {
279     mRasterizer->AddTask(task);
280   }
281 }
282
283 VectorAnimationThread::SleepThread::SleepThread(CallbackBase* callback)
284 : mConditionalWait(),
285   mAwakeCallback(std::unique_ptr<CallbackBase>(callback)),
286   mSleepTimePoint(),
287   mLogFactory(Dali::Adaptor::Get().GetLogFactory()),
288   mNeedToSleep(false),
289   mDestroyThread(false)
290 {
291 }
292
293 VectorAnimationThread::SleepThread::~SleepThread()
294 {
295   // Stop the thread
296   {
297     ConditionalWait::ScopedLock lock(mConditionalWait);
298     mDestroyThread = true;
299     mConditionalWait.Notify(lock);
300   }
301
302   Join();
303 }
304
305 void VectorAnimationThread::SleepThread::SleepUntil(std::chrono::time_point<std::chrono::steady_clock> timeToSleepUntil)
306 {
307   ConditionalWait::ScopedLock lock(mConditionalWait);
308   mSleepTimePoint = timeToSleepUntil;
309   mNeedToSleep    = true;
310   mConditionalWait.Notify(lock);
311 }
312
313 void VectorAnimationThread::SleepThread::Run()
314 {
315   SetThreadName("VectorSleepThread");
316   mLogFactory.InstallLogFunction();
317
318   while(!mDestroyThread)
319   {
320     bool                                               needToSleep;
321     std::chrono::time_point<std::chrono::steady_clock> sleepTimePoint;
322
323     {
324       ConditionalWait::ScopedLock lock(mConditionalWait);
325
326       needToSleep    = mNeedToSleep;
327       sleepTimePoint = mSleepTimePoint;
328
329       mNeedToSleep = false;
330     }
331
332     if(needToSleep)
333     {
334 #if defined(DEBUG_ENABLED)
335 //      auto sleepDuration = std::chrono::duration_cast<std::chrono::milliseconds>(mSleepTimePoint - std::chrono::steady_clock::now());
336
337 //      DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationThread::SleepThread::Run: [sleep duration = %lld]\n", sleepDuration.count());
338 #endif
339
340       std::this_thread::sleep_until(sleepTimePoint);
341
342       if(mAwakeCallback)
343       {
344         CallbackBase::Execute(*mAwakeCallback);
345       }
346     }
347
348     {
349       ConditionalWait::ScopedLock lock(mConditionalWait);
350       if(!mDestroyThread && !mNeedToSleep)
351       {
352         mConditionalWait.Wait(lock);
353       }
354     }
355   }
356 }
357
358 } // namespace Internal
359
360 } // namespace Toolkit
361
362 } // namespace Dali