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()
63 mRasterizers( GetNumberOfThreads( NUMBER_OF_RASTERIZE_THREADS_ENV, DEFAULT_NUMBER_OF_RASTERIZE_THREADS ), [&]() { return RasterizeHelper( *this ); } ),
64 mSleepThread( MakeCallback( this, &VectorAnimationThread::OnAwakeFromSleep ) ),
66 mNeedToSleep( false ),
67 mDestroyThread( false ),
68 mLogFactory( Dali::Adaptor::Get().GetLogFactory() )
73 VectorAnimationThread::~VectorAnimationThread()
77 ConditionalWait::ScopedLock lock( mConditionalWait );
78 mDestroyThread = true;
80 mConditionalWait.Notify( lock );
83 DALI_LOG_INFO( gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationThread::~VectorAnimationThread: Join [%p]\n", this );
88 void VectorAnimationThread::AddTask( VectorAnimationTaskPtr task )
90 ConditionalWait::ScopedLock lock( mConditionalWait );
92 if( mAnimationTasks.end() == std::find( mAnimationTasks.begin(), mAnimationTasks.end(), task ) )
94 auto currentTime = task->CalculateNextFrameTime( true ); // Rasterize as soon as possible
96 bool inserted = false;
97 for( auto iter = mAnimationTasks.begin(); iter != mAnimationTasks.end(); ++iter )
99 auto nextTime = (*iter)->GetNextFrameTime();
100 if( nextTime > currentTime )
102 mAnimationTasks.insert( iter, task );
110 mAnimationTasks.push_back( task );
113 mNeedToSleep = false;
114 // wake up the animation thread
115 mConditionalWait.Notify( lock );
119 void VectorAnimationThread::OnTaskCompleted( VectorAnimationTaskPtr task, bool keepAnimation )
121 if( !mDestroyThread )
123 ConditionalWait::ScopedLock lock( mConditionalWait );
124 bool needRasterize = false;
126 auto workingTask = std::find( mWorkingTasks.begin(), mWorkingTasks.end(), task );
127 if( workingTask != mWorkingTasks.end() )
129 mWorkingTasks.erase( workingTask );
132 // Check pending task
133 if( mAnimationTasks.end() != std::find( mAnimationTasks.begin(), mAnimationTasks.end(), task ) )
135 needRasterize = true;
140 if( mCompletedTasks.end() == std::find( mCompletedTasks.begin(), mCompletedTasks.end(), task ) )
142 mCompletedTasks.push_back( task );
143 needRasterize = true;
149 mNeedToSleep = false;
150 // wake up the animation thread
151 mConditionalWait.Notify( lock );
156 void VectorAnimationThread::OnAwakeFromSleep()
158 if( !mDestroyThread )
160 mNeedToSleep = false;
161 // wake up the animation thread
162 mConditionalWait.Notify();
166 void VectorAnimationThread::Run()
168 SetThreadName( "VectorAnimationThread" );
169 mLogFactory.InstallLogFunction();
171 while( !mDestroyThread )
177 void VectorAnimationThread::Rasterize()
179 // Lock while popping task out from the queue
180 ConditionalWait::ScopedLock lock( mConditionalWait );
185 mConditionalWait.Wait( lock );
190 // Process completed tasks
191 for( auto&& task : mCompletedTasks )
193 if( mAnimationTasks.end() == std::find( mAnimationTasks.begin(), mAnimationTasks.end(), task ) )
195 // Should use the frame rate of the animation file
196 auto nextFrameTime = task->CalculateNextFrameTime( false );
198 bool inserted = false;
199 for( auto iter = mAnimationTasks.begin(); iter != mAnimationTasks.end(); ++iter )
201 auto time = (*iter)->GetNextFrameTime();
202 if( time > nextFrameTime )
204 mAnimationTasks.insert( iter, task );
212 mAnimationTasks.push_back( task );
216 mCompletedTasks.clear();
218 // pop out the next task from the queue
219 for( auto it = mAnimationTasks.begin(); it != mAnimationTasks.end(); )
221 VectorAnimationTaskPtr nextTask = *it;
223 auto currentTime = std::chrono::system_clock::now();
224 auto nextFrameTime = nextTask->GetNextFrameTime();
226 #if defined(DEBUG_ENABLED)
227 auto duration = std::chrono::duration_cast< std::chrono::milliseconds >( nextFrameTime - currentTime );
229 DALI_LOG_INFO( gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationThread::Rasterize: [next time = %lld]\n", duration.count() );
232 if( nextFrameTime <= currentTime )
234 // If the task is not in the working list
235 if( std::find( mWorkingTasks.begin(), mWorkingTasks.end(), nextTask ) == mWorkingTasks.end() )
237 it = mAnimationTasks.erase( it );
239 // Add it to the working list
240 mWorkingTasks.push_back( nextTask );
242 auto rasterizerHelperIt = mRasterizers.GetNext();
243 DALI_ASSERT_ALWAYS( rasterizerHelperIt != mRasterizers.End() );
245 rasterizerHelperIt->Rasterize( nextTask );
254 mSleepThread.SleepUntil( nextFrameTime );
260 VectorAnimationThread::RasterizeHelper::RasterizeHelper( VectorAnimationThread& animationThread )
261 : RasterizeHelper( std::unique_ptr< VectorRasterizeThread >( new VectorRasterizeThread() ), animationThread )
265 VectorAnimationThread::RasterizeHelper::RasterizeHelper( RasterizeHelper&& rhs )
266 : RasterizeHelper( std::move( rhs.mRasterizer ), rhs.mAnimationThread )
270 VectorAnimationThread::RasterizeHelper::RasterizeHelper( std::unique_ptr< VectorRasterizeThread > rasterizer, VectorAnimationThread& animationThread )
271 : mRasterizer( std::move( rasterizer ) ),
272 mAnimationThread( animationThread )
274 mRasterizer->SetCompletedCallback( MakeCallback( &mAnimationThread, &VectorAnimationThread::OnTaskCompleted ) );
277 void VectorAnimationThread::RasterizeHelper::Rasterize( VectorAnimationTaskPtr task )
281 mRasterizer->AddTask( task );
285 VectorAnimationThread::SleepThread::SleepThread( CallbackBase* callback )
286 : mConditionalWait(),
287 mAwakeCallback( std::unique_ptr< CallbackBase >( callback ) ),
289 mLogFactory( Dali::Adaptor::Get().GetLogFactory() ),
290 mNeedToSleep( false ),
291 mDestroyThread( false )
295 VectorAnimationThread::SleepThread::~SleepThread()
299 ConditionalWait::ScopedLock lock( mConditionalWait );
300 mDestroyThread = true;
301 mConditionalWait.Notify( lock );
307 void VectorAnimationThread::SleepThread::SleepUntil( std::chrono::time_point< std::chrono::system_clock > timeToSleepUntil )
309 ConditionalWait::ScopedLock lock( mConditionalWait );
310 mSleepTimePoint = timeToSleepUntil;
312 mConditionalWait.Notify( lock );
315 void VectorAnimationThread::SleepThread::Run()
317 SetThreadName( "VectorSleepThread" );
318 mLogFactory.InstallLogFunction();
320 while( !mDestroyThread )
323 std::chrono::time_point< std::chrono::system_clock > sleepTimePoint;
326 ConditionalWait::ScopedLock lock( mConditionalWait );
328 needToSleep = mNeedToSleep;
329 sleepTimePoint = mSleepTimePoint;
331 mNeedToSleep = false;
336 #if defined(DEBUG_ENABLED)
337 auto sleepDuration = std::chrono::duration_cast< std::chrono::milliseconds >( mSleepTimePoint - std::chrono::system_clock::now() );
339 DALI_LOG_INFO( gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationThread::SleepThread::Run: [sleep duration = %lld]\n", sleepDuration.count() );
342 std::this_thread::sleep_until( sleepTimePoint );
346 CallbackBase::Execute( *mAwakeCallback );
351 ConditionalWait::ScopedLock lock( mConditionalWait );
352 if( !mDestroyThread && !mNeedToSleep )
354 mConditionalWait.Wait( lock );
360 } // namespace Internal
362 } // namespace Toolkit