2 * Copyright (c) 2019 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include <dali-toolkit/internal/visuals/animated-vector-image/vector-animation-thread.h>
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>
26 #include <dali/integration-api/adaptor-framework/adaptor.h>
40 constexpr auto DEFAULT_NUMBER_OF_RASTERIZE_THREADS = size_t{ 4u };
41 constexpr auto NUMBER_OF_RASTERIZE_THREADS_ENV = "DALI_VECTOR_RASTERIZE_THREADS";
43 size_t GetNumberOfThreads( const char* environmentVariable, size_t defaultValue )
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;
53 #if defined(DEBUG_ENABLED)
54 Debug::Filter* gVectorAnimationLogFilter = Debug::Filter::New( Debug::NoLogging, false, "LOG_VECTOR_ANIMATION" );
57 } // unnamed namespace
59 VectorAnimationThread::VectorAnimationThread()
62 mRasterizers( GetNumberOfThreads( NUMBER_OF_RASTERIZE_THREADS_ENV, DEFAULT_NUMBER_OF_RASTERIZE_THREADS ), [&]() { return RasterizeHelper( *this ); } ),
63 mSleepThread( MakeCallback( this, &VectorAnimationThread::OnAwakeFromSleep ) ),
65 mNeedToSleep( false ),
66 mDestroyThread( false ),
67 mLogFactory( Dali::Adaptor::Get().GetLogFactory() )
72 VectorAnimationThread::~VectorAnimationThread()
76 ConditionalWait::ScopedLock lock( mConditionalWait );
77 mDestroyThread = true;
78 mConditionalWait.Notify( lock );
81 DALI_LOG_INFO( gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationThread::~VectorAnimationThread: Join [%p]\n", this );
86 void VectorAnimationThread::AddTask( VectorAnimationTaskPtr task )
88 ConditionalWait::ScopedLock lock( mConditionalWait );
90 if( mAnimationTasks.end() == std::find( mAnimationTasks.begin(), mAnimationTasks.end(), task ) )
92 auto currentTime = task->CalculateNextFrameTime( true ); // Rasterize as soon as possible
94 bool inserted = false;
95 for( auto iter = mAnimationTasks.begin(); iter != mAnimationTasks.end(); ++iter )
97 auto nextTime = (*iter)->GetNextFrameTime();
98 if( nextTime > currentTime )
100 mAnimationTasks.insert( iter, task );
108 mAnimationTasks.push_back( task );
111 // wake up the animation thread
112 mConditionalWait.Notify( lock );
116 void VectorAnimationThread::OnTaskCompleted( VectorAnimationTaskPtr task, bool keepAnimation )
118 if( keepAnimation && !mDestroyThread )
120 ConditionalWait::ScopedLock lock( mConditionalWait );
122 if( mCompletedTasks.end() == std::find( mCompletedTasks.begin(), mCompletedTasks.end(), task ) )
124 mCompletedTasks.push_back( task );
126 // wake up the animation thread
127 mConditionalWait.Notify( lock );
132 void VectorAnimationThread::OnAwakeFromSleep()
134 if( !mDestroyThread )
136 mNeedToSleep = false;
137 // wake up the animation thread
138 mConditionalWait.Notify();
142 void VectorAnimationThread::Run()
144 SetThreadName( "VectorAnimationThread" );
145 mLogFactory.InstallLogFunction();
147 while( !mDestroyThread )
153 void VectorAnimationThread::Rasterize()
155 // Lock while popping task out from the queue
156 ConditionalWait::ScopedLock lock( mConditionalWait );
159 if( (mAnimationTasks.empty() && mCompletedTasks.empty() ) || mNeedToSleep )
161 mConditionalWait.Wait( lock );
164 mNeedToSleep = false;
166 // Process completed tasks
167 for( auto&& task : mCompletedTasks )
169 if( mAnimationTasks.end() == std::find( mAnimationTasks.begin(), mAnimationTasks.end(), task ) )
171 // Should use the frame rate of the animation file
172 auto nextFrameTime = task->CalculateNextFrameTime( false );
174 bool inserted = false;
175 for( auto iter = mAnimationTasks.begin(); iter != mAnimationTasks.end(); ++iter )
177 auto time = (*iter)->GetNextFrameTime();
178 if( time > nextFrameTime )
180 mAnimationTasks.insert( iter, task );
188 mAnimationTasks.push_back( task );
192 mCompletedTasks.clear();
194 // pop out the next task from the queue
195 while( !mAnimationTasks.empty() && !mNeedToSleep )
197 std::vector< VectorAnimationTaskPtr >::iterator next = mAnimationTasks.begin();
198 VectorAnimationTaskPtr nextTask = *next;
200 auto currentTime = std::chrono::system_clock::now();
201 auto nextFrameTime = nextTask->GetNextFrameTime();
203 #if defined(DEBUG_ENABLED)
204 auto duration = std::chrono::duration_cast< std::chrono::milliseconds >( nextFrameTime - currentTime );
206 DALI_LOG_INFO( gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationThread::Rasterize: [next time = %lld]\n", duration.count() );
209 if( nextFrameTime <= currentTime )
211 mAnimationTasks.erase( next );
213 auto rasterizerHelperIt = mRasterizers.GetNext();
214 DALI_ASSERT_ALWAYS( rasterizerHelperIt != mRasterizers.End() );
216 rasterizerHelperIt->Rasterize( nextTask );
221 mSleepThread.SleepUntil( nextFrameTime );
226 VectorAnimationThread::RasterizeHelper::RasterizeHelper( VectorAnimationThread& animationThread )
227 : RasterizeHelper( std::unique_ptr< VectorRasterizeThread >( new VectorRasterizeThread() ), animationThread )
231 VectorAnimationThread::RasterizeHelper::RasterizeHelper( RasterizeHelper&& rhs )
232 : RasterizeHelper( std::move( rhs.mRasterizer ), rhs.mAnimationThread )
236 VectorAnimationThread::RasterizeHelper::RasterizeHelper( std::unique_ptr< VectorRasterizeThread > rasterizer, VectorAnimationThread& animationThread )
237 : mRasterizer( std::move( rasterizer ) ),
238 mAnimationThread( animationThread )
240 mRasterizer->SetCompletedCallback( MakeCallback( &mAnimationThread, &VectorAnimationThread::OnTaskCompleted ) );
243 void VectorAnimationThread::RasterizeHelper::Rasterize( VectorAnimationTaskPtr task )
247 mRasterizer->AddTask( task );
251 VectorAnimationThread::SleepThread::SleepThread( CallbackBase* callback )
252 : mConditionalWait(),
253 mAwakeCallback( std::unique_ptr< CallbackBase >( callback ) ),
255 mLogFactory( Dali::Adaptor::Get().GetLogFactory() ),
256 mNeedToSleep( false ),
257 mDestroyThread( false )
261 VectorAnimationThread::SleepThread::~SleepThread()
265 ConditionalWait::ScopedLock lock( mConditionalWait );
266 mDestroyThread = true;
267 mConditionalWait.Notify( lock );
273 void VectorAnimationThread::SleepThread::SleepUntil( std::chrono::time_point< std::chrono::system_clock > timeToSleepUntil )
275 ConditionalWait::ScopedLock lock( mConditionalWait );
276 mSleepTimePoint = timeToSleepUntil;
278 mConditionalWait.Notify( lock );
281 void VectorAnimationThread::SleepThread::Run()
283 SetThreadName( "VectorSleepThread" );
284 mLogFactory.InstallLogFunction();
286 while( !mDestroyThread )
289 std::chrono::time_point< std::chrono::system_clock > sleepTimePoint;
292 ConditionalWait::ScopedLock lock( mConditionalWait );
294 needToSleep = mNeedToSleep;
295 sleepTimePoint = mSleepTimePoint;
297 mNeedToSleep = false;
302 #if defined(DEBUG_ENABLED)
303 auto sleepDuration = std::chrono::duration_cast< std::chrono::milliseconds >( mSleepTimePoint - std::chrono::system_clock::now() );
305 DALI_LOG_INFO( gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationThread::SleepThread::Run: [sleep duration = %lld]\n", sleepDuration.count() );
308 std::this_thread::sleep_until( sleepTimePoint );
312 CallbackBase::Execute( *mAwakeCallback );
317 ConditionalWait::ScopedLock lock( mConditionalWait );
318 if( !mDestroyThread && !mNeedToSleep )
320 mConditionalWait.Wait( lock );
326 } // namespace Internal
328 } // namespace Toolkit