Merge "Add logs to check the messages processed by the update thread" into tizen_4...
[platform/core/uifw/dali-core.git] / dali / internal / update / animation / scene-graph-animation.cpp
1 /*
2  * Copyright (c) 2017 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/internal/update/animation/scene-graph-animation.h>
20
21 // EXTERNAL INCLUDES
22 #include <cmath> // fmod
23
24 // INTERNAL INCLUDES
25 #include <dali/internal/common/memory-pool-object-allocator.h>
26 #include <dali/internal/render/common/performance-monitor.h>
27 #include <dali/public-api/math/math-utils.h>
28 #include <dali/integration-api/debug.h>
29
30 namespace //Unnamed namespace
31 {
32 //Memory pool used to allocate new animations. Memory used by this pool will be released when shutting down DALi
33 Dali::Internal::MemoryPoolObjectAllocator<Dali::Internal::SceneGraph::Animation> gAnimationMemoryPool;
34
35 inline void WrapInPlayRange( float& elapsed, const Dali::Vector2& playRangeSeconds)
36 {
37   if( elapsed > playRangeSeconds.y )
38   {
39     elapsed = playRangeSeconds.x + fmodf((elapsed-playRangeSeconds.x), (playRangeSeconds.y-playRangeSeconds.x));
40   }
41   else if( elapsed < playRangeSeconds.x )
42   {
43     elapsed = playRangeSeconds.y - fmodf( (playRangeSeconds.x - elapsed), (playRangeSeconds.y-playRangeSeconds.x) );
44   }
45 }
46
47 /// Compares the end times of the animators and if the end time is less, then it is moved earlier in the list. If end times are the same, then no change.
48 bool CompareAnimatorEndTimes( const Dali::Internal::SceneGraph::AnimatorBase* lhs, const Dali::Internal::SceneGraph::AnimatorBase* rhs )
49 {
50   return ( ( lhs->GetIntervalDelay() + lhs->GetDuration() ) < ( rhs->GetIntervalDelay() + rhs->GetDuration() ) );
51 }
52
53 } // unnamed namespace
54
55 namespace Dali
56 {
57
58 namespace Internal
59 {
60
61 namespace SceneGraph
62 {
63
64 Animation* Animation::New( float durationSeconds, float speedFactor, const Vector2& playRange, int loopCount, EndAction endAction, EndAction disconnectAction )
65 {
66   return new ( gAnimationMemoryPool.AllocateRawThreadSafe() ) Animation( durationSeconds, speedFactor, playRange, loopCount, endAction, disconnectAction );
67 }
68
69 Animation::Animation( float durationSeconds, float speedFactor, const Vector2& playRange, int loopCount, Dali::Animation::EndAction endAction, Dali::Animation::EndAction disconnectAction )
70 : mPlayRange( playRange ),
71   mDurationSeconds( durationSeconds ),
72   mDelaySeconds( 0.0f ),
73   mElapsedSeconds( playRange.x*mDurationSeconds ),
74   mSpeedFactor( speedFactor ),
75   mProgressMarker( 0.0f ),
76   mPlayedCount( 0 ),
77   mLoopCount(loopCount),
78   mCurrentLoop(0),
79   mEndAction(endAction),
80   mDisconnectAction(disconnectAction),
81   mState(Stopped),
82   mProgressReachedSignalRequired( false ),
83   mAutoReverseEnabled( false )
84 {
85 }
86
87 Animation::~Animation()
88 {
89 }
90
91 void Animation::operator delete( void* ptr )
92 {
93   gAnimationMemoryPool.FreeThreadSafe( static_cast<Animation*>( ptr ) );
94 }
95
96 void Animation::SetDuration(float durationSeconds)
97 {
98   mDurationSeconds = durationSeconds;
99 }
100
101 void Animation::SetProgressNotification( float progress )
102 {
103   mProgressMarker = progress;
104   if ( mProgressMarker > 0.0f )
105   {
106     mProgressReachedSignalRequired = true;
107   }
108 }
109
110 void Animation::SetLoopCount(int loopCount)
111 {
112   mLoopCount = loopCount;
113   mCurrentLoop = 0;
114 }
115
116 void Animation::SetEndAction(Dali::Animation::EndAction action)
117 {
118   mEndAction = action;
119 }
120
121 void Animation::SetDisconnectAction(Dali::Animation::EndAction action)
122 {
123   if ( mDisconnectAction != action )
124   {
125     mDisconnectAction = action;
126
127     for ( AnimatorIter iter = mAnimators.Begin(), endIter = mAnimators.End(); iter != endIter; ++iter )
128     {
129       (*iter)->SetDisconnectAction( action );
130     }
131   }
132 }
133
134 void Animation::SetPlayRange( const Vector2& range )
135 {
136   mPlayRange = range;
137
138   // Make sure mElapsedSeconds is within the new range
139
140   if( mState == Stopped )
141   {
142     // Ensure that the animation starts at the right place
143     mElapsedSeconds = mPlayRange.x * mDurationSeconds;
144   }
145   else
146   {
147     // If already past the end of the range, but before end of duration, then clamp will
148     // ensure that the animation stops on the next update.
149     // If not yet at the start of the range, clamping will jump to the start
150     mElapsedSeconds = Dali::Clamp(mElapsedSeconds, mPlayRange.x*mDurationSeconds , mPlayRange.y*mDurationSeconds );
151   }
152 }
153
154 void Animation::Play()
155 {
156   DALI_LOG_ERROR("Scene Graph Animation::Play: Before stable_sort\n");
157
158   // Sort according to end time with earlier end times coming first, if the end time is the same, then the animators are not moved
159   std::stable_sort( mAnimators.Begin(), mAnimators.End(), CompareAnimatorEndTimes );
160
161   DALI_LOG_ERROR("Scene Graph Animation::Play: After stable_sort\n");
162
163   mState = Playing;
164
165   if ( mSpeedFactor < 0.0f && mElapsedSeconds <= mPlayRange.x*mDurationSeconds )
166   {
167     mElapsedSeconds = mPlayRange.y * mDurationSeconds;
168   }
169
170   DALI_LOG_ERROR("Scene Graph Animation::Play: Before SetAnimatorsActive\n");
171
172   SetAnimatorsActive( true );
173
174   DALI_LOG_ERROR("Scene Graph Animation::Play: After SetAnimatorsActive\n");
175
176   mCurrentLoop = 0;
177 }
178
179 void Animation::PlayFrom( float progress )
180 {
181   // If the animation is already playing this has no effect
182   // Progress is guaranteed to be in range.
183   if( mState != Playing )
184   {
185     mElapsedSeconds = progress * mDurationSeconds;
186     mState = Playing;
187
188     SetAnimatorsActive( true );
189   }
190 }
191
192 void Animation::PlayAfter( float delaySeconds )
193 {
194   if( mState != Playing )
195   {
196     mDelaySeconds = delaySeconds;
197     mState = Playing;
198
199     if ( mSpeedFactor < 0.0f && mElapsedSeconds <= mPlayRange.x*mDurationSeconds )
200     {
201       mElapsedSeconds = mPlayRange.y * mDurationSeconds;
202     }
203
204     SetAnimatorsActive( true );
205
206     mCurrentLoop = 0;
207   }
208 }
209
210 void Animation::Pause()
211 {
212   if (mState == Playing)
213   {
214     mState = Paused;
215   }
216 }
217
218 void Animation::Bake(BufferIndex bufferIndex, EndAction action)
219 {
220   if( action == Dali::Animation::BakeFinal )
221   {
222     if( mSpeedFactor > 0.0f )
223     {
224       mElapsedSeconds = mPlayRange.y*mDurationSeconds + Math::MACHINE_EPSILON_1; // Force animation to reach it's end
225     }
226     else
227     {
228       mElapsedSeconds = mPlayRange.x*mDurationSeconds - Math::MACHINE_EPSILON_1; //Force animation to reach it's beginning
229     }
230   }
231
232   UpdateAnimators( bufferIndex, true/*bake the final result*/, true /*animation finished*/ );
233 }
234
235 void Animation::SetAnimatorsActive( bool active )
236 {
237   for ( AnimatorIter iter = mAnimators.Begin(), endIter = mAnimators.End(); iter != endIter; ++iter )
238   {
239     (*iter)->SetActive( active );
240   }
241 }
242
243 bool Animation::Stop(BufferIndex bufferIndex)
244 {
245   bool animationFinished(false);
246
247   if (mState == Playing || mState == Paused)
248   {
249     animationFinished = true; // The actor-thread should be notified of this
250
251     if( mEndAction != Dali::Animation::Discard )
252     {
253       Bake( bufferIndex, mEndAction );
254
255       // Animators are automatically set to inactive in Bake
256     }
257     else
258     {
259       SetAnimatorsActive( false );
260     }
261
262     // The animation has now been played to completion
263     ++mPlayedCount;
264     mCurrentLoop = 0;
265   }
266
267   mElapsedSeconds = mPlayRange.x*mDurationSeconds;
268   mState = Stopped;
269
270   return animationFinished;
271 }
272
273 void Animation::OnDestroy(BufferIndex bufferIndex)
274 {
275   if (mState == Playing || mState == Paused)
276   {
277     if (mEndAction != Dali::Animation::Discard)
278     {
279       Bake( bufferIndex, mEndAction );
280
281       // Animators are automatically set to inactive in Bake
282     }
283     else
284     {
285       SetAnimatorsActive( false );
286     }
287   }
288
289   mState = Destroyed;
290 }
291
292 void Animation::SetLoopingMode( bool loopingMode )
293 {
294   mAutoReverseEnabled = loopingMode;
295
296   for ( AnimatorIter iter = mAnimators.Begin(), endIter = mAnimators.End(); iter != endIter; ++iter )
297   {
298     // Send some variables together to figure out the Animation status
299     (*iter)->SetSpeedFactor( mSpeedFactor );
300     (*iter)->SetLoopCount( mLoopCount );
301
302     (*iter)->SetLoopingMode( loopingMode );
303   }
304 }
305
306 void Animation::AddAnimator( OwnerPointer<AnimatorBase>& animator )
307 {
308   animator->ConnectToSceneGraph();
309   animator->SetDisconnectAction( mDisconnectAction );
310
311   mAnimators.PushBack( animator.Release() );
312 }
313
314 void Animation::Update(BufferIndex bufferIndex, float elapsedSeconds, bool& looped, bool& finished, bool& progressReached )
315 {
316   looped = false;
317   finished = false;
318
319   if (mState == Stopped || mState == Destroyed)
320   {
321     // Short circuit when animation isn't running
322     return;
323   }
324
325   // The animation must still be applied when Paused/Stopping
326   if (mState == Playing)
327   {
328     // If there is delay time before Animation starts, wait the Animation until mDelaySeconds.
329     if( mDelaySeconds > 0.0f )
330     {
331       float reduceSeconds = fabsf( elapsedSeconds * mSpeedFactor );
332       if( reduceSeconds > mDelaySeconds )
333       {
334         if( mSpeedFactor < 0.0f )
335         {
336           mElapsedSeconds -= reduceSeconds - mDelaySeconds;
337         }
338         else
339         {
340           mElapsedSeconds += reduceSeconds - mDelaySeconds;
341         }
342         mDelaySeconds = 0.0f;
343       }
344       else
345       {
346         mDelaySeconds -= reduceSeconds;
347       }
348     }
349     else
350     {
351       mElapsedSeconds += ( elapsedSeconds * mSpeedFactor );
352     }
353
354     if ( mProgressReachedSignalRequired && ( mElapsedSeconds >= mProgressMarker ) )
355     {
356       // The application should be notified by NotificationManager, in another thread
357       progressReached = true;
358       mProgressReachedSignalRequired = false;
359     }
360   }
361
362   Vector2 playRangeSeconds = mPlayRange * mDurationSeconds;
363
364   if( 0 == mLoopCount || mCurrentLoop < mLoopCount - 1) // '-1' here so last loop iteration uses play once below
365   {
366     // looping
367     looped =  (mState == Playing                                                 &&
368                (( mSpeedFactor > 0.0f && mElapsedSeconds > playRangeSeconds.y )  ||
369                 ( mSpeedFactor < 0.0f && mElapsedSeconds < playRangeSeconds.x )) );
370
371     WrapInPlayRange( mElapsedSeconds, playRangeSeconds );
372
373     UpdateAnimators(bufferIndex, false, false );
374
375     if(looped)
376     {
377       if( mLoopCount != 0 )
378       {
379         ++mCurrentLoop;
380       }
381       mProgressReachedSignalRequired = mProgressMarker > 0.0f;
382       // don't increment mPlayedCount until the finished final loop
383     }
384   }
385   else
386   {
387     // playing once (and last mCurrentLoop)
388     finished = (mState == Playing                                                 &&
389                 (( mSpeedFactor > 0.0f && mElapsedSeconds > playRangeSeconds.y )  ||
390                  ( mSpeedFactor < 0.0f && mElapsedSeconds < playRangeSeconds.x )) );
391
392     // update with bake if finished
393     UpdateAnimators(bufferIndex, finished && (mEndAction != Dali::Animation::Discard), finished );
394
395     if(finished)
396     {
397       // The animation has now been played to completion
398       ++mPlayedCount;
399
400       // loop iterations come to this else branch for their final iterations
401       if( mCurrentLoop < mLoopCount)
402       {
403         ++mCurrentLoop;
404         DALI_ASSERT_DEBUG(mCurrentLoop == mLoopCount);
405       }
406
407       mProgressReachedSignalRequired = mProgressMarker > 0.0f;
408       mElapsedSeconds = playRangeSeconds.x;
409       mState = Stopped;
410     }
411   }
412 }
413
414 void Animation::UpdateAnimators( BufferIndex bufferIndex, bool bake, bool animationFinished )
415 {
416   const Vector2 playRange( mPlayRange * mDurationSeconds );
417   float elapsedSecondsClamped = Clamp( mElapsedSeconds, playRange.x, playRange.y );
418
419   //Loop through all animators
420   bool applied(true);
421   for ( AnimatorIter iter = mAnimators.Begin(); iter != mAnimators.End(); )
422   {
423     AnimatorBase *animator = *iter;
424
425     if( animator->Orphan() )
426     {
427       //Remove animators whose PropertyOwner has been destroyed
428       iter = mAnimators.Erase(iter);
429     }
430     else
431     {
432       if( animator->IsEnabled() )
433       {
434         const float intervalDelay( animator->GetIntervalDelay() );
435
436         if( elapsedSecondsClamped >= intervalDelay )
437         {
438           // Calculate a progress specific to each individual animator
439           float progress(1.0f);
440           const float animatorDuration = animator->GetDuration();
441           if (animatorDuration > 0.0f) // animators can be "immediate"
442           {
443             progress = Clamp((elapsedSecondsClamped - intervalDelay) / animatorDuration, 0.0f , 1.0f );
444           }
445           animator->Update(bufferIndex, progress, bake);
446         }
447         applied = true;
448       }
449       else
450       {
451         applied = false;
452       }
453
454       if ( animationFinished )
455       {
456         animator->SetActive( false );
457       }
458
459       if (applied)
460       {
461         INCREASE_COUNTER(PerformanceMonitor::ANIMATORS_APPLIED);
462       }
463
464       ++iter;
465     }
466   }
467
468 }
469
470 } // namespace SceneGraph
471
472 } // namespace Internal
473
474 } // namespace Dali