Merge "(Vector) Ensure not to add duplicated task" into devel/master
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / visuals / animated-vector-image / vector-animation-thread.cpp
1 /*
2  * Copyright (c) 2019 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/thread-settings.h>
23 #include <dali/devel-api/adaptor-framework/environment-variable.h>
24 #include <dali/integration-api/debug.h>
25 #include <thread>
26 #include <dali/integration-api/adaptor-framework/adaptor.h>
27
28 namespace Dali
29 {
30
31 namespace Toolkit
32 {
33
34 namespace Internal
35 {
36
37 namespace
38 {
39
40 constexpr auto DEFAULT_NUMBER_OF_RASTERIZE_THREADS = size_t{ 4u };
41 constexpr auto NUMBER_OF_RASTERIZE_THREADS_ENV = "DALI_VECTOR_RASTERIZE_THREADS";
42
43 size_t GetNumberOfThreads( const char* environmentVariable, size_t defaultValue )
44 {
45   using Dali::EnvironmentVariable::GetEnvironmentVariable;
46   auto numberString = GetEnvironmentVariable( environmentVariable );
47   auto numberOfThreads = numberString ? std::strtoul( numberString, nullptr, 10 ) : 0;
48   constexpr auto MAX_NUMBER_OF_THREADS = 100u;
49   DALI_ASSERT_DEBUG( numberOfThreads < MAX_NUMBER_OF_THREADS );
50   return ( numberOfThreads > 0 && numberOfThreads < MAX_NUMBER_OF_THREADS ) ? numberOfThreads : defaultValue;
51 }
52
53 #if defined(DEBUG_ENABLED)
54 Debug::Filter* gVectorAnimationLogFilter = Debug::Filter::New( Debug::NoLogging, false, "LOG_VECTOR_ANIMATION" );
55 #endif
56
57 } // unnamed namespace
58
59 VectorAnimationThread::VectorAnimationThread()
60 : mAnimationTasks(),
61   mCompletedTasks(),
62   mWorkingTasks(),
63   mRasterizers( GetNumberOfThreads( NUMBER_OF_RASTERIZE_THREADS_ENV, DEFAULT_NUMBER_OF_RASTERIZE_THREADS ), [&]() { return RasterizeHelper( *this ); } ),
64   mSleepThread( MakeCallback( this, &VectorAnimationThread::OnAwakeFromSleep ) ),
65   mConditionalWait(),
66   mNeedToSleep( false ),
67   mDestroyThread( false ),
68   mLogFactory( Dali::Adaptor::Get().GetLogFactory() )
69 {
70   mSleepThread.Start();
71 }
72
73 VectorAnimationThread::~VectorAnimationThread()
74 {
75   // Stop the thread
76   {
77     ConditionalWait::ScopedLock lock( mConditionalWait );
78     mDestroyThread = true;
79     mNeedToSleep = false;
80     mConditionalWait.Notify( lock );
81   }
82
83   DALI_LOG_INFO( gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationThread::~VectorAnimationThread: Join [%p]\n", this );
84
85   Join();
86 }
87
88 void VectorAnimationThread::AddTask( VectorAnimationTaskPtr task )
89 {
90   ConditionalWait::ScopedLock lock( mConditionalWait );
91
92   if( mAnimationTasks.end() == std::find( mAnimationTasks.begin(), mAnimationTasks.end(), task ) )
93   {
94     auto currentTime = task->CalculateNextFrameTime( true );  // Rasterize as soon as possible
95
96     bool inserted = false;
97     for( auto iter = mAnimationTasks.begin(); iter != mAnimationTasks.end(); ++iter )
98     {
99       auto nextTime = (*iter)->GetNextFrameTime();
100       if( nextTime > currentTime )
101       {
102         mAnimationTasks.insert( iter, task );
103         inserted = true;
104         break;
105       }
106     }
107
108     if( !inserted )
109     {
110       mAnimationTasks.push_back( task );
111     }
112
113     mNeedToSleep = false;
114     // wake up the animation thread
115     mConditionalWait.Notify( lock );
116   }
117 }
118
119 void VectorAnimationThread::OnTaskCompleted( VectorAnimationTaskPtr task, bool keepAnimation )
120 {
121   if( !mDestroyThread )
122   {
123     ConditionalWait::ScopedLock lock( mConditionalWait );
124     bool needRasterize = false;
125
126     auto workingTask = std::find( mWorkingTasks.begin(), mWorkingTasks.end(), task );
127     if( workingTask != mWorkingTasks.end() )
128     {
129       mWorkingTasks.erase( workingTask );
130     }
131
132     // Check pending task
133     if( mAnimationTasks.end() != std::find( mAnimationTasks.begin(), mAnimationTasks.end(), task ) )
134     {
135       needRasterize = true;
136     }
137
138     if( keepAnimation )
139     {
140       if( mCompletedTasks.end() == std::find( mCompletedTasks.begin(), mCompletedTasks.end(), task ) )
141       {
142         mCompletedTasks.push_back( task );
143         needRasterize = true;
144       }
145     }
146
147     if( needRasterize )
148     {
149       mNeedToSleep = false;
150       // wake up the animation thread
151       mConditionalWait.Notify( lock );
152     }
153   }
154 }
155
156 void VectorAnimationThread::OnAwakeFromSleep()
157 {
158   if( !mDestroyThread )
159   {
160     mNeedToSleep = false;
161     // wake up the animation thread
162     mConditionalWait.Notify();
163   }
164 }
165
166 void VectorAnimationThread::Run()
167 {
168   SetThreadName( "VectorAnimationThread" );
169   mLogFactory.InstallLogFunction();
170
171   while( !mDestroyThread )
172   {
173     Rasterize();
174   }
175 }
176
177 void VectorAnimationThread::Rasterize()
178 {
179   // Lock while popping task out from the queue
180   ConditionalWait::ScopedLock lock( mConditionalWait );
181
182   // conditional wait
183   if( mNeedToSleep )
184   {
185     mConditionalWait.Wait( lock );
186   }
187
188   mNeedToSleep = true;
189
190   // Process completed tasks
191   for( auto&& task : mCompletedTasks )
192   {
193     if( mAnimationTasks.end() == std::find( mAnimationTasks.begin(), mAnimationTasks.end(), task ) )
194     {
195       // Should use the frame rate of the animation file
196       auto nextFrameTime = task->CalculateNextFrameTime( false );
197
198       bool inserted = false;
199       for( auto iter = mAnimationTasks.begin(); iter != mAnimationTasks.end(); ++iter )
200       {
201         auto time = (*iter)->GetNextFrameTime();
202         if( time > nextFrameTime )
203         {
204           mAnimationTasks.insert( iter, task );
205           inserted = true;
206           break;
207         }
208       }
209
210       if( !inserted )
211       {
212         mAnimationTasks.push_back( task );
213       }
214     }
215   }
216   mCompletedTasks.clear();
217
218   // pop out the next task from the queue
219   for( auto it = mAnimationTasks.begin(); it != mAnimationTasks.end(); )
220   {
221     VectorAnimationTaskPtr nextTask = *it;
222
223     auto currentTime = std::chrono::system_clock::now();
224     auto nextFrameTime = nextTask->GetNextFrameTime();
225
226 #if defined(DEBUG_ENABLED)
227     auto duration = std::chrono::duration_cast< std::chrono::milliseconds >( nextFrameTime - currentTime );
228
229     DALI_LOG_INFO( gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationThread::Rasterize: [next time = %lld]\n", duration.count() );
230 #endif
231
232     if( nextFrameTime <= currentTime )
233     {
234       // If the task is not in the working list
235       if( std::find( mWorkingTasks.begin(), mWorkingTasks.end(), nextTask ) == mWorkingTasks.end() )
236       {
237         it = mAnimationTasks.erase( it );
238
239         // Add it to the working list
240         mWorkingTasks.push_back( nextTask );
241
242         auto rasterizerHelperIt = mRasterizers.GetNext();
243         DALI_ASSERT_ALWAYS( rasterizerHelperIt != mRasterizers.End() );
244
245         rasterizerHelperIt->Rasterize( nextTask );
246       }
247       else
248       {
249         it++;
250       }
251     }
252     else
253     {
254       mSleepThread.SleepUntil( nextFrameTime );
255       break;
256     }
257   }
258 }
259
260 VectorAnimationThread::RasterizeHelper::RasterizeHelper( VectorAnimationThread& animationThread )
261 : RasterizeHelper( std::unique_ptr< VectorRasterizeThread >( new VectorRasterizeThread() ), animationThread )
262 {
263 }
264
265 VectorAnimationThread::RasterizeHelper::RasterizeHelper( RasterizeHelper&& rhs )
266 : RasterizeHelper( std::move( rhs.mRasterizer ), rhs.mAnimationThread )
267 {
268 }
269
270 VectorAnimationThread::RasterizeHelper::RasterizeHelper( std::unique_ptr< VectorRasterizeThread > rasterizer, VectorAnimationThread& animationThread )
271 : mRasterizer( std::move( rasterizer ) ),
272   mAnimationThread( animationThread )
273 {
274   mRasterizer->SetCompletedCallback( MakeCallback( &mAnimationThread, &VectorAnimationThread::OnTaskCompleted ) );
275 }
276
277 void VectorAnimationThread::RasterizeHelper::Rasterize( VectorAnimationTaskPtr task )
278 {
279   if( task )
280   {
281     mRasterizer->AddTask( task );
282   }
283 }
284
285 VectorAnimationThread::SleepThread::SleepThread( CallbackBase* callback )
286 : mConditionalWait(),
287   mAwakeCallback( std::unique_ptr< CallbackBase >( callback ) ),
288   mSleepTimePoint(),
289   mLogFactory( Dali::Adaptor::Get().GetLogFactory() ),
290   mNeedToSleep( false ),
291   mDestroyThread( false )
292 {
293 }
294
295 VectorAnimationThread::SleepThread::~SleepThread()
296 {
297   // Stop the thread
298   {
299     ConditionalWait::ScopedLock lock( mConditionalWait );
300     mDestroyThread = true;
301     mConditionalWait.Notify( lock );
302   }
303
304   Join();
305 }
306
307 void VectorAnimationThread::SleepThread::SleepUntil( std::chrono::time_point< std::chrono::system_clock > timeToSleepUntil )
308 {
309   ConditionalWait::ScopedLock lock( mConditionalWait );
310   mSleepTimePoint = timeToSleepUntil;
311   mNeedToSleep = true;
312   mConditionalWait.Notify( lock );
313 }
314
315 void VectorAnimationThread::SleepThread::Run()
316 {
317   SetThreadName( "VectorSleepThread" );
318   mLogFactory.InstallLogFunction();
319
320   while( !mDestroyThread )
321   {
322     bool needToSleep;
323     std::chrono::time_point< std::chrono::system_clock > sleepTimePoint;
324
325     {
326       ConditionalWait::ScopedLock lock( mConditionalWait );
327
328       needToSleep = mNeedToSleep;
329       sleepTimePoint = mSleepTimePoint;
330
331       mNeedToSleep = false;
332     }
333
334     if( needToSleep )
335     {
336 #if defined(DEBUG_ENABLED)
337       auto sleepDuration = std::chrono::duration_cast< std::chrono::milliseconds >( mSleepTimePoint - std::chrono::system_clock::now() );
338
339       DALI_LOG_INFO( gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationThread::SleepThread::Run: [sleep duration = %lld]\n", sleepDuration.count() );
340 #endif
341
342       std::this_thread::sleep_until( sleepTimePoint );
343
344       if( mAwakeCallback )
345       {
346         CallbackBase::Execute( *mAwakeCallback );
347       }
348     }
349
350     {
351       ConditionalWait::ScopedLock lock( mConditionalWait );
352       if( !mDestroyThread && !mNeedToSleep )
353       {
354         mConditionalWait.Wait( lock );
355       }
356     }
357   }
358 }
359
360 } // namespace Internal
361
362 } // namespace Toolkit
363
364 } // namespace Dali