[4.0] (AnimatedVectorImageVisual) Prevent properties from being updated during rendering
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / visuals / animated-vector-image / vector-rasterize-thread.cpp
1 /*
2  * Copyright (c) 2018 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-rasterize-thread.h>
20
21 // EXTERNAL INCLUDES
22 #include <dali/integration-api/adaptors/adaptor.h>
23 #include <dali/integration-api/debug.h>
24 #include <chrono>
25 #include <thread>
26
27 namespace Dali
28 {
29
30 namespace Toolkit
31 {
32
33 namespace Internal
34 {
35
36 namespace
37 {
38
39 constexpr auto LOOP_FOREVER = -1;
40 constexpr auto NANOSECONDS_PER_SECOND( 1e+9 );
41
42 #if defined(DEBUG_ENABLED)
43 Debug::Filter* gVectorAnimationLogFilter = Debug::Filter::New( Debug::NoLogging, false, "LOG_VECTOR_ANIMATION" );
44 #endif
45
46 inline void ResetToStart( bool& updated, uint32_t& value, uint32_t startValue, ConditionalWait& conditionalWait )
47 {
48   ConditionalWait::ScopedLock lock( conditionalWait );
49   if( !updated )
50   {
51     value = startValue;
52   }
53 }
54
55 } // unnamed namespace
56
57 VectorRasterizeThread::VectorRasterizeThread( const std::string& url )
58 : mUrl( url ),
59   mVectorRenderer(),
60   mConditionalWait(),
61   mResourceReadyTrigger(),
62   mAnimationFinishedTrigger(),
63   mPlayRange( 0.0f, 1.0f ),
64   mPlayState( DevelImageVisual::PlayState::STOPPED ),
65   mFrameDurationNanoSeconds( 0 ),
66   mProgress( 0.0f ),
67   mFrameRate( 60.0f ),
68   mCurrentFrame( 0 ),
69   mTotalFrame( 0 ),
70   mStartFrame( 0 ),
71   mEndFrame( 0 ),
72   mWidth( 0 ),
73   mHeight( 0 ),
74   mLoopCount( LOOP_FOREVER ),
75   mCurrentLoop( 0 ),
76   mNeedRender( false ),
77   mDestroyThread( false ),
78   mResourceReady( false ),
79   mCurrentFrameUpdated( false ),
80   mLogFactory( Dali::Adaptor::Get().GetLogFactory() )
81 {
82   mVectorRenderer = VectorAnimationRenderer::New( mUrl );
83 }
84
85 VectorRasterizeThread::~VectorRasterizeThread()
86 {
87   // Stop the thread
88   {
89     ConditionalWait::ScopedLock lock( mConditionalWait );
90     mDestroyThread = true;
91     mConditionalWait.Notify( lock );
92
93     // This should be called in the main thread to stop waiting for the dequeuable buffer.
94     mVectorRenderer.StopRender();
95   }
96
97   DALI_LOG_INFO( gVectorAnimationLogFilter, Debug::Verbose, "VectorRasterizeThread::~VectorRasterizeThread: Join\n" );
98
99   Join();
100 }
101
102 void VectorRasterizeThread::Run()
103 {
104   mLogFactory.InstallLogFunction();
105
106   //TODO: check the return value
107   StartRender();
108
109   while( !mDestroyThread )
110   {
111     Rasterize();
112   }
113 }
114
115 void VectorRasterizeThread::SetRenderer( Renderer renderer )
116 {
117   ConditionalWait::ScopedLock lock( mConditionalWait );
118
119   mVectorRenderer.SetRenderer( renderer );
120
121   DALI_LOG_INFO( gVectorAnimationLogFilter, Debug::Verbose, "VectorRasterizeThread::SetRenderer\n" );
122 }
123
124 void VectorRasterizeThread::SetSize( uint32_t width, uint32_t height )
125 {
126   if( mWidth != width || mHeight != height )
127   {
128     ConditionalWait::ScopedLock lock( mConditionalWait );
129     mVectorRenderer.SetSize( width, height );
130
131     mWidth = width;
132     mHeight = height;
133
134     mResourceReady = false;
135
136     DALI_LOG_INFO( gVectorAnimationLogFilter, Debug::Verbose, "VectorRasterizeThread::SetSize: width = %d, height = %d\n", width, height );
137   }
138 }
139
140 void VectorRasterizeThread::PlayAnimation()
141 {
142   ConditionalWait::ScopedLock lock( mConditionalWait );
143   if( mPlayState != DevelImageVisual::PlayState::PLAYING )
144   {
145     mPlayState = DevelImageVisual::PlayState::PLAYING;
146     mConditionalWait.Notify( lock );
147
148     DALI_LOG_INFO( gVectorAnimationLogFilter, Debug::Verbose, "VectorRasterizeThread::PlayAnimation: Start\n" );
149   }
150 }
151
152 void VectorRasterizeThread::StopAnimation()
153 {
154   ConditionalWait::ScopedLock lock( mConditionalWait );
155   if( mPlayState != DevelImageVisual::PlayState::STOPPED )
156   {
157     mPlayState = DevelImageVisual::PlayState::STOPPED;
158
159     DALI_LOG_INFO( gVectorAnimationLogFilter, Debug::Verbose, "VectorRasterizeThread::StopAnimation: Stop\n" );
160   }
161 }
162
163 void VectorRasterizeThread::PauseAnimation()
164 {
165   ConditionalWait::ScopedLock lock( mConditionalWait );
166   if( mPlayState == DevelImageVisual::PlayState::PLAYING )
167   {
168     mPlayState = DevelImageVisual::PlayState::PAUSED;
169
170     DALI_LOG_INFO( gVectorAnimationLogFilter, Debug::Verbose, "VectorRasterizeThread::PauseAnimation: Pause\n" );
171   }
172 }
173
174 void VectorRasterizeThread::RenderFrame()
175 {
176   ConditionalWait::ScopedLock lock( mConditionalWait );
177
178   if( !mResourceReady )
179   {
180     mNeedRender = true;
181     mConditionalWait.Notify( lock );
182
183     DALI_LOG_INFO( gVectorAnimationLogFilter, Debug::Verbose, "VectorRasterizeThread::RenderFrame: Render\n" );
184   }
185 }
186
187 void VectorRasterizeThread::SetResourceReadyCallback( EventThreadCallback* callback )
188 {
189   ConditionalWait::ScopedLock lock( mConditionalWait );
190   mResourceReadyTrigger = std::unique_ptr< EventThreadCallback >( callback );
191 }
192
193 void VectorRasterizeThread::SetAnimationFinishedCallback( EventThreadCallback* callback )
194 {
195   ConditionalWait::ScopedLock lock( mConditionalWait );
196   mAnimationFinishedTrigger = std::unique_ptr< EventThreadCallback >( callback );
197 }
198
199 void VectorRasterizeThread::SetLoopCount( int32_t count )
200 {
201   if( mLoopCount != count )
202   {
203     ConditionalWait::ScopedLock lock( mConditionalWait );
204
205     mLoopCount = count;
206   }
207 }
208
209 int32_t VectorRasterizeThread::GetLoopCount() const
210 {
211   return mLoopCount;
212 }
213
214 void VectorRasterizeThread::SetPlayRange( Vector2 range )
215 {
216   // Make sure the range specified is between 0.0 and 1.0
217   if( range.x >= 0.0f && range.x <= 1.0f && range.y >= 0.0f && range.y <= 1.0f )
218   {
219     Vector2 orderedRange( range );
220     // If the range is not in order swap values
221     if( range.x > range.y )
222     {
223       orderedRange = Vector2( range.y, range.x );
224     }
225
226     if( mPlayRange != orderedRange )
227     {
228       ConditionalWait::ScopedLock lock( mConditionalWait );
229
230       mPlayRange = orderedRange;
231
232       if( mTotalFrame != 0 )
233       {
234         mStartFrame = static_cast< uint32_t >( mPlayRange.x * mTotalFrame + 0.5f );
235         mEndFrame = static_cast< uint32_t >( mPlayRange.y * mTotalFrame + 0.5f );
236
237         // If the current frame is out of the range, change the current frame also.
238         if( mStartFrame > mCurrentFrame )
239         {
240           mCurrentFrame = mStartFrame;
241
242           mCurrentFrameUpdated = true;
243           mResourceReady = false;
244         }
245         else if( mEndFrame < mCurrentFrame )
246         {
247           mCurrentFrame = mEndFrame;
248
249           mCurrentFrameUpdated = true;
250           mResourceReady = false;
251         }
252       }
253     }
254   }
255 }
256
257 Vector2 VectorRasterizeThread::GetPlayRange() const
258 {
259   return mPlayRange;
260 }
261
262 void VectorRasterizeThread::SetCurrentProgress( float progress )
263 {
264   ConditionalWait::ScopedLock lock( mConditionalWait );
265
266   if( progress >= mPlayRange.x && progress <= mPlayRange.y )
267   {
268     mProgress = progress;
269
270     if( mTotalFrame != 0 )
271     {
272       mCurrentFrame = static_cast< uint32_t >( mTotalFrame * progress + 0.5f );
273       mCurrentFrameUpdated = true;
274     }
275
276     mResourceReady = false;
277
278     DALI_LOG_INFO( gVectorAnimationLogFilter, Debug::Verbose, "VectorRasterizeThread::SetCurrentProgress: progress = %f (%d)\n", progress, mCurrentFrame );
279   }
280 }
281
282 float VectorRasterizeThread::GetCurrentProgress() const
283 {
284   return ( static_cast< float >( mCurrentFrame ) / static_cast< float >( mTotalFrame ) );
285 }
286
287 DevelImageVisual::PlayState VectorRasterizeThread::GetPlayState() const
288 {
289   return mPlayState;
290 }
291
292 bool VectorRasterizeThread::IsResourceReady() const
293 {
294   return mResourceReady;
295 }
296
297 bool VectorRasterizeThread::StartRender()
298 {
299   //TODO: check the return value
300   mVectorRenderer.StartRender();
301
302   mTotalFrame = mVectorRenderer.GetTotalFrameNumber();
303
304   mStartFrame = static_cast< uint32_t >( mPlayRange.x * mTotalFrame + 0.5f );
305   mEndFrame = static_cast< uint32_t >( mPlayRange.y * mTotalFrame + 0.5f );
306
307   mCurrentFrame = std::max( static_cast< uint32_t >( mTotalFrame * mProgress + 0.5f ), mStartFrame );
308
309   mFrameRate = mVectorRenderer.GetFrameRate();
310   mFrameDurationNanoSeconds = NANOSECONDS_PER_SECOND / mFrameRate;
311
312   DALI_LOG_INFO( gVectorAnimationLogFilter, Debug::Verbose, "VectorRasterizeThread::StartRender: Renderer is started [%d, %f fps]\n", mTotalFrame, mFrameRate );
313
314   return true;
315 }
316
317 void VectorRasterizeThread::Rasterize()
318 {
319   bool resourceReady;
320   uint32_t currentFrame, startFrame, endFrame;
321   int32_t loopCount;
322   DevelImageVisual::PlayState playState;
323
324   {
325     ConditionalWait::ScopedLock lock( mConditionalWait );
326
327     if( mPlayState != DevelImageVisual::PlayState::PLAYING && !mNeedRender && !mDestroyThread )
328     {
329       DALI_LOG_INFO( gVectorAnimationLogFilter, Debug::Verbose, "VectorRasterizeThread::Rasterize: Wait\n" );
330
331       if( mPlayState == DevelImageVisual::PlayState::STOPPED )
332       {
333         // Reset the current frame and the current loop
334         mCurrentFrame = mStartFrame;
335         mCurrentLoop = 0;
336       }
337       mConditionalWait.Wait( lock );
338     }
339
340     resourceReady = mResourceReady;
341     currentFrame = mCurrentFrame++;
342     startFrame = mStartFrame;
343     endFrame = mEndFrame;
344     loopCount = mLoopCount;
345     playState = mPlayState;
346
347     mNeedRender = false;
348     mResourceReady = true;
349     mCurrentFrameUpdated = false;
350   }
351
352   auto currentFrameStartTime = std::chrono::system_clock::now();
353
354   // Rasterize
355   mVectorRenderer.Render( currentFrame );
356
357   if( playState == DevelImageVisual::PlayState::PLAYING )
358   {
359     if( currentFrame >= endFrame )
360     {
361       if( loopCount < 0 )
362       {
363         // repeat forever
364         ResetToStart( mCurrentFrameUpdated, mCurrentFrame, startFrame, mConditionalWait );  // If the current frame is changed in the event thread, don't overwrite it.
365       }
366       else
367       {
368         mCurrentLoop++;
369         if( mCurrentLoop >= loopCount )
370         {
371           mPlayState = DevelImageVisual::PlayState::STOPPED;
372
373           // Animation is finished
374           mAnimationFinishedTrigger->Trigger();
375
376           DALI_LOG_INFO( gVectorAnimationLogFilter, Debug::Verbose, "VectorRasterizeThread::Rasterize: Animation is finished\n" );
377         }
378         else
379         {
380           ResetToStart( mCurrentFrameUpdated, mCurrentFrame, startFrame, mConditionalWait );
381         }
382       }
383     }
384   }
385
386   if( !resourceReady )
387   {
388     DALI_LOG_INFO( gVectorAnimationLogFilter, Debug::Verbose, "VectorRasterizeThread::Rasterize: Resource ready trigger\n" );
389
390     mResourceReadyTrigger->Trigger();
391   }
392
393   auto timeToSleepUntil = currentFrameStartTime + std::chrono::nanoseconds( mFrameDurationNanoSeconds );
394
395 #if defined(DEBUG_ENABLED)
396   auto sleepDuration = std::chrono::duration_cast< std::chrono::milliseconds >( timeToSleepUntil - std::chrono::system_clock::now() );
397
398   DALI_LOG_INFO( gVectorAnimationLogFilter, Debug::Verbose, "VectorRasterizeThread::Rasterize: [current = %d, sleep duration = %lld]\n", mCurrentFrame, sleepDuration.count() );
399 #endif
400
401   std::this_thread::sleep_until( timeToSleepUntil );
402 }
403
404 } // namespace Internal
405
406 } // namespace Toolkit
407
408 } // namespace Dali