8403e6a818c679ecdddf9ea27589a38e4a2aad0f
[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   mRasterizers( GetNumberOfThreads( NUMBER_OF_RASTERIZE_THREADS_ENV, DEFAULT_NUMBER_OF_RASTERIZE_THREADS ), [&]() { return RasterizeHelper( *this ); } ),
63   mSleepThread( MakeCallback( this, &VectorAnimationThread::OnAwakeFromSleep ) ),
64   mConditionalWait(),
65   mNeedToSleep( false ),
66   mDestroyThread( false ),
67   mLogFactory( Dali::Adaptor::Get().GetLogFactory() )
68 {
69   mSleepThread.Start();
70 }
71
72 VectorAnimationThread::~VectorAnimationThread()
73 {
74   // Stop the thread
75   {
76     ConditionalWait::ScopedLock lock( mConditionalWait );
77     mDestroyThread = true;
78     mConditionalWait.Notify( lock );
79   }
80
81   DALI_LOG_INFO( gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationThread::~VectorAnimationThread: Join [%p]\n", this );
82
83   Join();
84 }
85
86 void VectorAnimationThread::AddTask( VectorAnimationTaskPtr task )
87 {
88   ConditionalWait::ScopedLock lock( mConditionalWait );
89
90   if( mAnimationTasks.end() == std::find( mAnimationTasks.begin(), mAnimationTasks.end(), task ) )
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     // wake up the animation thread
112     mConditionalWait.Notify( lock );
113   }
114 }
115
116 void VectorAnimationThread::OnTaskCompleted( VectorAnimationTaskPtr task, bool keepAnimation )
117 {
118   if( keepAnimation && !mDestroyThread )
119   {
120     ConditionalWait::ScopedLock lock( mConditionalWait );
121
122     if( mCompletedTasks.end() == std::find( mCompletedTasks.begin(), mCompletedTasks.end(), task ) )
123     {
124       mCompletedTasks.push_back( task );
125
126       // wake up the animation thread
127       mConditionalWait.Notify( lock );
128     }
129   }
130 }
131
132 void VectorAnimationThread::OnAwakeFromSleep()
133 {
134   if( !mDestroyThread )
135   {
136     mNeedToSleep = false;
137     // wake up the animation thread
138     mConditionalWait.Notify();
139   }
140 }
141
142 void VectorAnimationThread::Run()
143 {
144   SetThreadName( "VectorAnimationThread" );
145   mLogFactory.InstallLogFunction();
146
147   while( !mDestroyThread )
148   {
149     Rasterize();
150   }
151 }
152
153 void VectorAnimationThread::Rasterize()
154 {
155   // Lock while popping task out from the queue
156   ConditionalWait::ScopedLock lock( mConditionalWait );
157
158   // conditional wait
159   if( (mAnimationTasks.empty() && mCompletedTasks.empty() ) || mNeedToSleep )
160   {
161     mConditionalWait.Wait( lock );
162   }
163
164   mNeedToSleep = false;
165
166   // Process completed tasks
167   for( auto&& task : mCompletedTasks )
168   {
169     if( mAnimationTasks.end() == std::find( mAnimationTasks.begin(), mAnimationTasks.end(), task ) )
170     {
171       // Should use the frame rate of the animation file
172       auto nextFrameTime = task->CalculateNextFrameTime( false );
173
174       bool inserted = false;
175       for( auto iter = mAnimationTasks.begin(); iter != mAnimationTasks.end(); ++iter )
176       {
177         auto time = (*iter)->GetNextFrameTime();
178         if( time > nextFrameTime )
179         {
180           mAnimationTasks.insert( iter, task );
181           inserted = true;
182           break;
183         }
184       }
185
186       if( !inserted )
187       {
188         mAnimationTasks.push_back( task );
189       }
190     }
191   }
192   mCompletedTasks.clear();
193
194   // pop out the next task from the queue
195   while( !mAnimationTasks.empty() && !mNeedToSleep )
196   {
197     std::vector< VectorAnimationTaskPtr >::iterator next = mAnimationTasks.begin();
198     VectorAnimationTaskPtr nextTask = *next;
199
200     auto currentTime = std::chrono::system_clock::now();
201     auto nextFrameTime = nextTask->GetNextFrameTime();
202
203 #if defined(DEBUG_ENABLED)
204     auto duration = std::chrono::duration_cast< std::chrono::milliseconds >( nextFrameTime - currentTime );
205
206     DALI_LOG_INFO( gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationThread::Rasterize: [next time = %lld]\n", duration.count() );
207 #endif
208
209     if( nextFrameTime <= currentTime )
210     {
211       mAnimationTasks.erase( next );
212
213       auto rasterizerHelperIt = mRasterizers.GetNext();
214       DALI_ASSERT_ALWAYS( rasterizerHelperIt != mRasterizers.End() );
215
216       rasterizerHelperIt->Rasterize( nextTask );
217     }
218     else
219     {
220       mNeedToSleep = true;
221       mSleepThread.SleepUntil( nextFrameTime );
222     }
223   }
224 }
225
226 VectorAnimationThread::RasterizeHelper::RasterizeHelper( VectorAnimationThread& animationThread )
227 : RasterizeHelper( std::unique_ptr< VectorRasterizeThread >( new VectorRasterizeThread() ), animationThread )
228 {
229 }
230
231 VectorAnimationThread::RasterizeHelper::RasterizeHelper( RasterizeHelper&& rhs )
232 : RasterizeHelper( std::move( rhs.mRasterizer ), rhs.mAnimationThread )
233 {
234 }
235
236 VectorAnimationThread::RasterizeHelper::RasterizeHelper( std::unique_ptr< VectorRasterizeThread > rasterizer, VectorAnimationThread& animationThread )
237 : mRasterizer( std::move( rasterizer ) ),
238   mAnimationThread( animationThread )
239 {
240   mRasterizer->SetCompletedCallback( MakeCallback( &mAnimationThread, &VectorAnimationThread::OnTaskCompleted ) );
241 }
242
243 void VectorAnimationThread::RasterizeHelper::Rasterize( VectorAnimationTaskPtr task )
244 {
245   if( task )
246   {
247     mRasterizer->AddTask( task );
248   }
249 }
250
251 VectorAnimationThread::SleepThread::SleepThread( CallbackBase* callback )
252 : mConditionalWait(),
253   mAwakeCallback( std::unique_ptr< CallbackBase >( callback ) ),
254   mSleepTimePoint(),
255   mLogFactory( Dali::Adaptor::Get().GetLogFactory() ),
256   mNeedToSleep( false ),
257   mDestroyThread( false )
258 {
259 }
260
261 VectorAnimationThread::SleepThread::~SleepThread()
262 {
263   // Stop the thread
264   {
265     ConditionalWait::ScopedLock lock( mConditionalWait );
266     mDestroyThread = true;
267     mConditionalWait.Notify( lock );
268   }
269
270   Join();
271 }
272
273 void VectorAnimationThread::SleepThread::SleepUntil( std::chrono::time_point< std::chrono::system_clock > timeToSleepUntil )
274 {
275   ConditionalWait::ScopedLock lock( mConditionalWait );
276   mSleepTimePoint = timeToSleepUntil;
277   mNeedToSleep = true;
278   mConditionalWait.Notify( lock );
279 }
280
281 void VectorAnimationThread::SleepThread::Run()
282 {
283   SetThreadName( "VectorSleepThread" );
284   mLogFactory.InstallLogFunction();
285
286   while( !mDestroyThread )
287   {
288     bool needToSleep;
289     std::chrono::time_point< std::chrono::system_clock > sleepTimePoint;
290
291     {
292       ConditionalWait::ScopedLock lock( mConditionalWait );
293
294       needToSleep = mNeedToSleep;
295       sleepTimePoint = mSleepTimePoint;
296
297       mNeedToSleep = false;
298     }
299
300     if( needToSleep )
301     {
302 #if defined(DEBUG_ENABLED)
303       auto sleepDuration = std::chrono::duration_cast< std::chrono::milliseconds >( mSleepTimePoint - std::chrono::system_clock::now() );
304
305       DALI_LOG_INFO( gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationThread::SleepThread::Run: [sleep duration = %lld]\n", sleepDuration.count() );
306 #endif
307
308       std::this_thread::sleep_until( sleepTimePoint );
309
310       if( mAwakeCallback )
311       {
312         CallbackBase::Execute( *mAwakeCallback );
313       }
314     }
315
316     {
317       ConditionalWait::ScopedLock lock( mConditionalWait );
318       if( !mDestroyThread && !mNeedToSleep )
319       {
320         mConditionalWait.Wait( lock );
321       }
322     }
323   }
324 }
325
326 } // namespace Internal
327
328 } // namespace Toolkit
329
330 } // namespace Dali