From: Heeyong Song Date: Fri, 20 Sep 2019 09:23:48 +0000 (+0900) Subject: (Vector) Refactor vector to use thread pool X-Git-Tag: dali_1.4.42~3 X-Git-Url: http://review.tizen.org/git/?p=platform%2Fcore%2Fuifw%2Fdali-toolkit.git;a=commitdiff_plain;h=refs%2Fchanges%2F32%2F214332%2F8 (Vector) Refactor vector to use thread pool Change-Id: If9701ae9b938f4a21cf30f7536d441aedd7d9f08 --- diff --git a/automated-tests/src/dali-toolkit/dali-toolkit-test-utils/toolkit-vector-animation-renderer.cpp b/automated-tests/src/dali-toolkit/dali-toolkit-test-utils/toolkit-vector-animation-renderer.cpp index 558cdec..344280a 100755 --- a/automated-tests/src/dali-toolkit/dali-toolkit-test-utils/toolkit-vector-animation-renderer.cpp +++ b/automated-tests/src/dali-toolkit/dali-toolkit-test-utils/toolkit-vector-animation-renderer.cpp @@ -37,8 +37,20 @@ public: mRenderer(), mWidth( 0 ), mHeight( 0 ), - mPreviousFrame( 0 ) + mPreviousFrame( 0 ), + mFrameRate( 60.0f ) { + mCount++; + + if( mCount == 2 ) + { + mFrameRate = 0.1f; + } + } + + ~VectorAnimationRenderer() + { + mCount--; } void SetRenderer( Dali::Renderer renderer ) @@ -87,7 +99,7 @@ public: float GetFrameRate() const { - return 60.0f; + return mFrameRate; } void GetDefaultSize( uint32_t& width, uint32_t& height ) const @@ -103,14 +115,19 @@ public: public: + static uint32_t mCount; + std::string mUrl; Dali::Renderer mRenderer; uint32_t mWidth; uint32_t mHeight; uint32_t mPreviousFrame; + float mFrameRate; Dali::VectorAnimationRenderer::UploadCompletedSignalType mUploadCompletedSignal; }; +uint32_t VectorAnimationRenderer::mCount = 0; + inline VectorAnimationRenderer& GetImplementation( Dali::VectorAnimationRenderer& renderer ) { DALI_ASSERT_ALWAYS( renderer && "VectorAnimationRenderer handle is empty." ); diff --git a/automated-tests/src/dali-toolkit/utc-Dali-AnimatedVectorImageVisual.cpp b/automated-tests/src/dali-toolkit/utc-Dali-AnimatedVectorImageVisual.cpp index 87ff376..98a2d3d 100644 --- a/automated-tests/src/dali-toolkit/utc-Dali-AnimatedVectorImageVisual.cpp +++ b/automated-tests/src/dali-toolkit/utc-Dali-AnimatedVectorImageVisual.cpp @@ -251,8 +251,20 @@ int UtcDaliAnimatedVectorImageVisualGetPropertyMap01(void) VisualFactory factory = VisualFactory::Get(); Visual::Base visual = factory.CreateVisual( propertyMap ); + DummyControl actor = DummyControl::New( true ); + DummyControlImpl& dummyImpl = static_cast< DummyControlImpl& >( actor.GetImplementation() ); + dummyImpl.RegisterVisual( DummyControl::Property::TEST_VISUAL, visual ); + + Vector2 controlSize( 20.f, 30.f ); + actor.SetSize( controlSize ); + + Stage::GetCurrent().Add( actor ); + + application.SendNotification(); + application.Render(); + Property::Map resultMap; - visual.CreatePropertyMap( resultMap ); + resultMap = actor.GetProperty< Property::Map >( DummyControl::Property::TEST_VISUAL ); // check the property values from the returned map from a visual Property::Value* value = resultMap.Find( Toolkit::Visual::Property::TYPE, Property::INTEGER ); @@ -564,10 +576,10 @@ int UtcDaliAnimatedVectorImageVisualPlayRange(void) ToolkitTestApplication application; tet_infoline( "UtcDaliAnimatedVectorImageVisualPlayRange" ); - int startFrame = 3, endFrame = 1; + int startFrame = 1, endFrame = 3; Property::Array array; - array.PushBack( startFrame ); array.PushBack( endFrame ); + array.PushBack( startFrame ); Property::Map propertyMap; propertyMap.Add( Toolkit::Visual::Property::TYPE, DevelVisual::ANIMATED_VECTOR_IMAGE ) @@ -610,7 +622,7 @@ int UtcDaliAnimatedVectorImageVisualPlayRange(void) // Set invalid play range array.Clear(); - array.PushBack( -1 ); + array.PushBack( 1 ); array.PushBack( 100 ); attributes.Clear(); @@ -630,6 +642,44 @@ int UtcDaliAnimatedVectorImageVisualPlayRange(void) DALI_TEST_EQUALS( startFrame, resultStartFrame, TEST_LOCATION ); // Should not be changed DALI_TEST_EQUALS( endFrame, resultEndFrame, TEST_LOCATION ); + DevelControl::DoAction( actor, DummyControl::Property::TEST_VISUAL, Dali::Toolkit::DevelAnimatedVectorImageVisual::Action::PAUSE, Property::Map() ); + + application.SendNotification(); + application.Render(); + + DevelControl::DoAction( actor, DummyControl::Property::TEST_VISUAL, Dali::Toolkit::DevelAnimatedVectorImageVisual::Action::JUMP_TO, 3 ); + + application.SendNotification(); + application.Render(); + + map = actor.GetProperty< Property::Map >( DummyControl::Property::TEST_VISUAL ); + value = map.Find( DevelImageVisual::Property::CURRENT_FRAME_NUMBER ); + DALI_TEST_EQUALS( value->Get< int >(), 3, TEST_LOCATION ); + + array.Clear(); + array.PushBack( 0 ); + array.PushBack( 2 ); + + attributes.Clear(); + attributes.Add( DevelImageVisual::Property::PLAY_RANGE, array ); + DevelControl::DoAction( actor, DummyControl::Property::TEST_VISUAL, Dali::Toolkit::DevelAnimatedVectorImageVisual::Action::UPDATE_PROPERTY, attributes ); + + application.SendNotification(); + application.Render(); + + map = actor.GetProperty< Property::Map >( DummyControl::Property::TEST_VISUAL ); + value = map.Find( DevelImageVisual::Property::PLAY_RANGE ); + + result = value->GetArray(); + result->GetElementAt( 0 ).Get( resultStartFrame ); + result->GetElementAt( 1 ).Get( resultEndFrame ); + + DALI_TEST_EQUALS( 0, resultStartFrame, TEST_LOCATION ); + DALI_TEST_EQUALS( 2, resultEndFrame, TEST_LOCATION ); + + value = map.Find( DevelImageVisual::Property::CURRENT_FRAME_NUMBER ); + DALI_TEST_EQUALS( value->Get< int >(), 2, TEST_LOCATION ); // CURRENT_FRAME_NUMBER should be changed also. + END_TEST; } @@ -856,6 +906,24 @@ int UtcDaliAnimatedVectorImageVisualUpdateProperty(void) DALI_TEST_CHECK( result->GetElementAt( 0 ).Get< int >() == startFrame ); DALI_TEST_CHECK( result->GetElementAt( 1 ).Get< int >() == endFrame ); + // Play and update property + attributes.Clear(); + DevelControl::DoAction( actor, DummyControl::Property::TEST_VISUAL, Dali::Toolkit::DevelAnimatedVectorImageVisual::Action::PLAY, attributes ); + + application.SendNotification(); + application.Render(); + + attributes.Add( DevelImageVisual::Property::LOOP_COUNT, 10 ); + + DevelControl::DoAction( actor, DummyControl::Property::TEST_VISUAL, Dali::Toolkit::DevelAnimatedVectorImageVisual::Action::UPDATE_PROPERTY, attributes ); + + application.SendNotification(); + application.Render(); + + map = actor.GetProperty< Property::Map >( DummyControl::Property::TEST_VISUAL ); + value = map.Find( DevelImageVisual::Property::LOOP_COUNT ); + DALI_TEST_EQUALS( value->Get< int >(), 10, TEST_LOCATION ); + END_TEST; } @@ -1093,3 +1161,67 @@ int UtcDaliAnimatedVectorImageVisualPropertyNotification(void) END_TEST; } + +int UtcDaliAnimatedVectorImageVisualMultipleInstances(void) +{ + ToolkitTestApplication application; + tet_infoline( "UtcDaliAnimatedVectorImageVisualMultipleInstances" ); + + Property::Map propertyMap; + propertyMap.Add( Toolkit::Visual::Property::TYPE, DevelVisual::ANIMATED_VECTOR_IMAGE ) + .Add( ImageVisual::Property::URL, TEST_VECTOR_IMAGE_FILE_NAME ); + + Visual::Base visual1 = VisualFactory::Get().CreateVisual( propertyMap ); + DALI_TEST_CHECK( visual1 ); + + DummyControl actor1 = DummyControl::New( true ); + DummyControlImpl& dummyImpl1 = static_cast< DummyControlImpl& >( actor1.GetImplementation() ); + dummyImpl1.RegisterVisual( DummyControl::Property::TEST_VISUAL, visual1 ); + + Vector2 controlSize( 20.f, 30.f ); + actor1.SetSize( controlSize ); + + Stage::GetCurrent().Add( actor1 ); + + propertyMap.Clear(); + propertyMap.Add( Toolkit::Visual::Property::TYPE, DevelVisual::ANIMATED_VECTOR_IMAGE ) + .Add( ImageVisual::Property::URL, TEST_VECTOR_IMAGE_FILE_NAME ); + + Visual::Base visual2 = VisualFactory::Get().CreateVisual( propertyMap ); + DALI_TEST_CHECK( visual2 ); + + DummyControl actor2 = DummyControl::New( true ); + DummyControlImpl& dummyImpl2 = static_cast< DummyControlImpl& >( actor2.GetImplementation() ); + dummyImpl2.RegisterVisual( DummyControl::Property::TEST_VISUAL, visual2 ); + + actor2.SetSize( controlSize ); + + Stage::GetCurrent().Add( actor2 ); + + DevelControl::DoAction( actor2, DummyControl::Property::TEST_VISUAL, Dali::Toolkit::DevelAnimatedVectorImageVisual::Action::PLAY, Property::Map() ); + + application.SendNotification(); + application.Render(); + + std::this_thread::sleep_for( std::chrono::milliseconds( 200 ) ); + + Property::Map attributes; + attributes.Add( DevelImageVisual::Property::STOP_BEHAVIOR, DevelImageVisual::StopBehavior::LAST_FRAME ); + + DevelControl::DoAction( actor1, DummyControl::Property::TEST_VISUAL, Dali::Toolkit::DevelAnimatedVectorImageVisual::Action::UPDATE_PROPERTY, attributes ); + DevelControl::DoAction( actor2, DummyControl::Property::TEST_VISUAL, Dali::Toolkit::DevelAnimatedVectorImageVisual::Action::UPDATE_PROPERTY, attributes ); + + DevelControl::DoAction( actor1, DummyControl::Property::TEST_VISUAL, Dali::Toolkit::DevelAnimatedVectorImageVisual::Action::PLAY, Property::Map() ); + + // renderer is added to actor + DALI_TEST_CHECK( actor1.GetRendererCount() == 1u ); + Renderer renderer1 = actor1.GetRendererAt( 0u ); + DALI_TEST_CHECK( renderer1 ); + + // renderer is added to actor + DALI_TEST_CHECK( actor2.GetRendererCount() == 1u ); + Renderer renderer2 = actor2.GetRendererAt( 0u ); + DALI_TEST_CHECK( renderer2 ); + + END_TEST; +} diff --git a/dali-toolkit/internal/file.list b/dali-toolkit/internal/file.list index a7b1b78..9642ef8 100644 --- a/dali-toolkit/internal/file.list +++ b/dali-toolkit/internal/file.list @@ -19,6 +19,8 @@ SET( toolkit_src_files ${toolkit_src_dir}/visuals/animated-image/rolling-image-cache.cpp ${toolkit_src_dir}/visuals/animated-image/rolling-gif-image-cache.cpp ${toolkit_src_dir}/visuals/animated-vector-image/animated-vector-image-visual.cpp + ${toolkit_src_dir}/visuals/animated-vector-image/vector-animation-task.cpp + ${toolkit_src_dir}/visuals/animated-vector-image/vector-animation-thread.cpp ${toolkit_src_dir}/visuals/animated-vector-image/vector-rasterize-thread.cpp ${toolkit_src_dir}/visuals/border/border-visual.cpp ${toolkit_src_dir}/visuals/color/color-visual.cpp diff --git a/dali-toolkit/internal/visuals/animated-vector-image/animated-vector-image-visual.cpp b/dali-toolkit/internal/visuals/animated-vector-image/animated-vector-image-visual.cpp index 5b0c1c4..070836c 100644 --- a/dali-toolkit/internal/visuals/animated-vector-image/animated-vector-image-visual.cpp +++ b/dali-toolkit/internal/visuals/animated-vector-image/animated-vector-image-visual.cpp @@ -94,9 +94,9 @@ AnimatedVectorImageVisualPtr AnimatedVectorImageVisual::New( VisualFactoryCache& AnimatedVectorImageVisual::AnimatedVectorImageVisual( VisualFactoryCache& factoryCache, ImageVisualShaderFactory& shaderFactory, const VisualUrl& imageUrl ) : Visual::Base( factoryCache, Visual::FittingMode::FILL ), - mImageVisualShaderFactory( shaderFactory ), mUrl( imageUrl ), - mVectorRasterizeThread( imageUrl.GetUrl() ), + mVectorAnimationTask( new VectorAnimationTask( factoryCache, imageUrl.GetUrl() ) ), + mImageVisualShaderFactory( shaderFactory ), mVisualSize(), mVisualScale( Vector2::ONE ), mPlacementActor(), @@ -112,10 +112,8 @@ AnimatedVectorImageVisual::AnimatedVectorImageVisual( VisualFactoryCache& factor // the rasterized image is with pre-multiplied alpha format mImpl->mFlags |= Impl::IS_PREMULTIPLIED_ALPHA; - mVectorRasterizeThread.UploadCompletedSignal().Connect( this, &AnimatedVectorImageVisual::OnUploadCompleted ); - mVectorRasterizeThread.SetAnimationFinishedCallback( new EventThreadCallback( MakeCallback( this, &AnimatedVectorImageVisual::OnAnimationFinished ) ) ); - - mVectorRasterizeThread.Start(); + mVectorAnimationTask->UploadCompletedSignal().Connect( this, &AnimatedVectorImageVisual::OnUploadCompleted ); + mVectorAnimationTask->SetAnimationFinishedCallback( new EventThreadCallback( MakeCallback( this, &AnimatedVectorImageVisual::OnAnimationFinished ) ) ); } AnimatedVectorImageVisual::~AnimatedVectorImageVisual() @@ -131,7 +129,7 @@ void AnimatedVectorImageVisual::GetNaturalSize( Vector2& naturalSize ) else { uint32_t width, height; - mVectorRasterizeThread.GetDefaultSize( width, height ); + mVectorAnimationTask->GetDefaultSize( width, height ); naturalSize.x = width; naturalSize.y = height; } @@ -149,14 +147,17 @@ void AnimatedVectorImageVisual::DoCreatePropertyMap( Property::Map& map ) const } map.Insert( Toolkit::DevelImageVisual::Property::LOOP_COUNT, mLoopCount ); + uint32_t startFrame, endFrame; + mVectorAnimationTask->GetPlayRange( startFrame, endFrame ); + Property::Array playRange; - playRange.PushBack( mStartFrame ); - playRange.PushBack( mEndFrame ); + playRange.PushBack( static_cast< int32_t >( startFrame ) ); + playRange.PushBack( static_cast< int32_t >( endFrame ) ); map.Insert( Toolkit::DevelImageVisual::Property::PLAY_RANGE, playRange ); - map.Insert( Toolkit::DevelImageVisual::Property::PLAY_STATE, static_cast< int >( mVectorRasterizeThread.GetPlayState() ) ); - map.Insert( Toolkit::DevelImageVisual::Property::CURRENT_FRAME_NUMBER, static_cast< int32_t >( mVectorRasterizeThread.GetCurrentFrameNumber() ) ); - map.Insert( Toolkit::DevelImageVisual::Property::TOTAL_FRAME_NUMBER, static_cast< int32_t >( mVectorRasterizeThread.GetTotalFrameNumber() ) ); + map.Insert( Toolkit::DevelImageVisual::Property::PLAY_STATE, static_cast< int32_t >( mVectorAnimationTask->GetPlayState() ) ); + map.Insert( Toolkit::DevelImageVisual::Property::CURRENT_FRAME_NUMBER, static_cast< int32_t >( mVectorAnimationTask->GetCurrentFrameNumber() ) ); + map.Insert( Toolkit::DevelImageVisual::Property::TOTAL_FRAME_NUMBER, static_cast< int32_t >( mVectorAnimationTask->GetTotalFrameNumber() ) ); map.Insert( Toolkit::DevelImageVisual::Property::STOP_BEHAVIOR, mStopBehavior ); map.Insert( Toolkit::DevelImageVisual::Property::LOOPING_MODE, mLoopingMode ); @@ -222,7 +223,7 @@ void AnimatedVectorImageVisual::DoSetProperty( Property::Index index, const Prop if( count >= 2 ) { int startFrame, endFrame; - int totalFrame = mVectorRasterizeThread.GetTotalFrameNumber(); + int totalFrame = mVectorAnimationTask->GetTotalFrameNumber(); array->GetElementAt( 0 ).Get( startFrame ); array->GetElementAt( 1 ).Get( endFrame ); @@ -295,7 +296,7 @@ void AnimatedVectorImageVisual::DoSetOnStage( Actor& actor ) // Hold the weak handle of the placement actor and delay the adding of renderer until the rasterization is finished. mPlacementActor = actor; - mVectorRasterizeThread.SetRenderer( mImpl->mRenderer ); + mVectorAnimationTask->SetRenderer( mImpl->mRenderer ); // Add property notification for scaling & size mScaleNotification = actor.AddPropertyNotification( Actor::Property::WORLD_SCALE, StepCondition( 0.1f, 1.0f ) ); @@ -309,7 +310,7 @@ void AnimatedVectorImageVisual::DoSetOnStage( Actor& actor ) void AnimatedVectorImageVisual::DoSetOffStage( Actor& actor ) { - mVectorRasterizeThread.PauseAnimation(); + mVectorAnimationTask->PauseAnimation(); mActionStatus = DevelAnimatedVectorImageVisual::Action::PAUSE; @@ -352,14 +353,14 @@ void AnimatedVectorImageVisual::OnSetTransform() if( mActionStatus == DevelAnimatedVectorImageVisual::Action::PLAY ) { - mVectorRasterizeThread.PlayAnimation(); + mVectorAnimationTask->PlayAnimation(); mImpl->mRenderer.SetProperty( DevelRenderer::Property::RENDERING_BEHAVIOR, DevelRenderer::Rendering::CONTINUOUSLY ); } else { // Render one frame - mVectorRasterizeThread.RenderFrame(); + mVectorAnimationTask->RenderFrame(); } } } @@ -373,7 +374,7 @@ void AnimatedVectorImageVisual::OnDoAction( const Property::Index actionId, cons { if( IsOnStage() && mVisualSize != Vector2::ZERO ) { - mVectorRasterizeThread.PlayAnimation(); + mVectorAnimationTask->PlayAnimation(); mImpl->mRenderer.SetProperty( DevelRenderer::Property::RENDERING_BEHAVIOR, DevelRenderer::Rendering::CONTINUOUSLY ); } @@ -382,7 +383,7 @@ void AnimatedVectorImageVisual::OnDoAction( const Property::Index actionId, cons } case DevelAnimatedVectorImageVisual::Action::PAUSE: { - mVectorRasterizeThread.PauseAnimation(); + mVectorAnimationTask->PauseAnimation(); if( mImpl->mRenderer ) { @@ -394,9 +395,9 @@ void AnimatedVectorImageVisual::OnDoAction( const Property::Index actionId, cons } case DevelAnimatedVectorImageVisual::Action::STOP: { - if( mVectorRasterizeThread.GetPlayState() != DevelImageVisual::PlayState::STOPPED ) + if( mVectorAnimationTask->GetPlayState() != DevelImageVisual::PlayState::STOPPED ) { - mVectorRasterizeThread.StopAnimation(); + mVectorAnimationTask->StopAnimation(); } if( mImpl->mRenderer ) @@ -412,11 +413,11 @@ void AnimatedVectorImageVisual::OnDoAction( const Property::Index actionId, cons int32_t frameNumber; if( attributes.Get( frameNumber ) ) { - mVectorRasterizeThread.SetCurrentFrameNumber( frameNumber ); + mVectorAnimationTask->SetCurrentFrameNumber( frameNumber ); - if( IsOnStage() && mVectorRasterizeThread.GetPlayState() != DevelImageVisual::PlayState::PLAYING ) + if( IsOnStage() && mVectorAnimationTask->GetPlayState() != DevelImageVisual::PlayState::PLAYING ) { - mVectorRasterizeThread.RenderFrame(); + mVectorAnimationTask->RenderFrame(); Stage::GetCurrent().KeepRendering( 0.0f ); // Trigger rendering } } @@ -476,41 +477,41 @@ void AnimatedVectorImageVisual::SendAnimationData() if( mResendFlag ) { bool isPlaying = false; - if( mVectorRasterizeThread.GetPlayState() == DevelImageVisual::PlayState::PLAYING ) + if( mVectorAnimationTask->GetPlayState() == DevelImageVisual::PlayState::PLAYING ) { - mVectorRasterizeThread.PauseAnimation(); + mVectorAnimationTask->PauseAnimation(); isPlaying = true; } if( mResendFlag & RESEND_LOOP_COUNT ) { - mVectorRasterizeThread.SetLoopCount( mLoopCount ); + mVectorAnimationTask->SetLoopCount( mLoopCount ); } if( mResendFlag & RESEND_PLAY_RANGE ) { - mVectorRasterizeThread.SetPlayRange( mStartFrame, mEndFrame ); + mVectorAnimationTask->SetPlayRange( mStartFrame, mEndFrame ); } if( mResendFlag & RESEND_STOP_BEHAVIOR ) { - mVectorRasterizeThread.SetStopBehavior( mStopBehavior ); + mVectorAnimationTask->SetStopBehavior( mStopBehavior ); } if( mResendFlag & RESEND_LOOPING_MODE ) { - mVectorRasterizeThread.SetLoopingMode( mLoopingMode ); + mVectorAnimationTask->SetLoopingMode( mLoopingMode ); } if( IsOnStage() ) { if( isPlaying ) { - mVectorRasterizeThread.PlayAnimation(); + mVectorAnimationTask->PlayAnimation(); } else { - mVectorRasterizeThread.RenderFrame(); + mVectorAnimationTask->RenderFrame(); Stage::GetCurrent().KeepRendering( 0.0f ); } } @@ -524,11 +525,11 @@ void AnimatedVectorImageVisual::SetVectorImageSize() uint32_t width = static_cast< uint32_t >( mVisualSize.width * mVisualScale.width ); uint32_t height = static_cast< uint32_t >( mVisualSize.height * mVisualScale.height ); - mVectorRasterizeThread.SetSize( width, height ); + mVectorAnimationTask->SetSize( width, height ); - if( IsOnStage() && mVectorRasterizeThread.GetPlayState() != DevelImageVisual::PlayState::PLAYING ) + if( IsOnStage() && mVectorAnimationTask->GetPlayState() != DevelImageVisual::PlayState::PLAYING ) { - mVectorRasterizeThread.RenderFrame(); + mVectorAnimationTask->RenderFrame(); Stage::GetCurrent().KeepRendering( 0.0f ); // Trigger rendering } } diff --git a/dali-toolkit/internal/visuals/animated-vector-image/animated-vector-image-visual.h b/dali-toolkit/internal/visuals/animated-vector-image/animated-vector-image-visual.h index b6837b5..674cb1b 100644 --- a/dali-toolkit/internal/visuals/animated-vector-image/animated-vector-image-visual.h +++ b/dali-toolkit/internal/visuals/animated-vector-image/animated-vector-image-visual.h @@ -27,7 +27,7 @@ #include #include #include -#include +#include namespace Dali { @@ -39,7 +39,6 @@ namespace Internal { class ImageVisualShaderFactory; -class VectorRasterizeThread; class AnimatedVectorImageVisual; using AnimatedVectorImageVisualPtr = IntrusivePtr< AnimatedVectorImageVisual >; @@ -183,17 +182,17 @@ private: AnimatedVectorImageVisual& operator=( const AnimatedVectorImageVisual& visual ) = delete; private: - ImageVisualShaderFactory& mImageVisualShaderFactory; VisualUrl mUrl; - VectorRasterizeThread mVectorRasterizeThread; + VectorAnimationTaskPtr mVectorAnimationTask; + ImageVisualShaderFactory& mImageVisualShaderFactory; PropertyNotification mScaleNotification; PropertyNotification mSizeNotification; Vector2 mVisualSize; Vector2 mVisualScale; WeakHandle< Actor > mPlacementActor; int32_t mLoopCount; - int32_t mStartFrame; - int32_t mEndFrame; + uint32_t mStartFrame; + uint32_t mEndFrame; uint32_t mResendFlag; DevelAnimatedVectorImageVisual::Action::Type mActionStatus; DevelImageVisual::StopBehavior::Type mStopBehavior; diff --git a/dali-toolkit/internal/visuals/animated-vector-image/vector-animation-task.cpp b/dali-toolkit/internal/visuals/animated-vector-image/vector-animation-task.cpp new file mode 100644 index 0000000..0445df7 --- /dev/null +++ b/dali-toolkit/internal/visuals/animated-vector-image/vector-animation-task.cpp @@ -0,0 +1,516 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// CLASS HEADER +#include + +// EXTERNAL INCLUDES +#include + +// INTERNAL INCLUDES +#include +#include + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +namespace +{ + +constexpr auto LOOP_FOREVER = -1; +constexpr auto NANOSECONDS_PER_SECOND( 1e+9 ); + +#if defined(DEBUG_ENABLED) +Debug::Filter* gVectorAnimationLogFilter = Debug::Filter::New( Debug::NoLogging, false, "LOG_VECTOR_ANIMATION" ); +#endif + +template< typename T > +inline void ResetValue( bool& updated, T& value, T newValue, ConditionalWait& conditionalWait ) +{ + ConditionalWait::ScopedLock lock( conditionalWait ); + if( !updated ) + { + value = newValue; + updated = true; + } +} + +} // unnamed namespace + +VectorAnimationTask::VectorAnimationTask( VisualFactoryCache& factoryCache, const std::string& url ) +: mUrl( url ), + mVectorRenderer(), + mVectorAnimationThread( factoryCache.GetVectorAnimationThread() ), + mConditionalWait(), + mAnimationFinishedTrigger(), + mPlayState( PlayState::STOPPED ), + mStopBehavior( DevelImageVisual::StopBehavior::CURRENT_FRAME ), + mLoopingMode( DevelImageVisual::LoopingMode::RESTART ), + mNextFrameStartTime(), + mFrameDurationNanoSeconds( 0 ), + mFrameRate( 60.0f ), + mCurrentFrame( 0 ), + mTotalFrame( 0 ), + mStartFrame( 0 ), + mEndFrame( 0 ), + mWidth( 0 ), + mHeight( 0 ), + mLoopCount( LOOP_FOREVER ), + mCurrentLoop( 0 ), + mResourceReady( false ), + mCurrentFrameUpdated( false ), + mCurrentLoopUpdated( false ), + mForward( true ), + mUpdateFrameNumber( false ), + mNeedAnimationFinishedTrigger( true ) +{ + Initialize(); +} + +VectorAnimationTask::~VectorAnimationTask() +{ + DALI_LOG_INFO( gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationTask::~VectorAnimationTask: destructor [%p]\n", this ); +} + +void VectorAnimationTask::SetRenderer( Renderer renderer ) +{ + ConditionalWait::ScopedLock lock( mConditionalWait ); + + mVectorRenderer.SetRenderer( renderer ); + + DALI_LOG_INFO( gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationTask::SetRenderer [%p]\n", this ); +} + +void VectorAnimationTask::SetSize( uint32_t width, uint32_t height ) +{ + if( mWidth != width || mHeight != height ) + { + ConditionalWait::ScopedLock lock( mConditionalWait ); + mVectorRenderer.SetSize( width, height ); + + mWidth = width; + mHeight = height; + + mResourceReady = false; + + DALI_LOG_INFO( gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationTask::SetSize: width = %d, height = %d [%p]\n", width, height, this ); + } +} + +void VectorAnimationTask::PlayAnimation() +{ + ConditionalWait::ScopedLock lock( mConditionalWait ); + + if( mPlayState != PlayState::PLAYING ) + { + mUpdateFrameNumber = false; + mPlayState = PlayState::PLAYING; + + mVectorAnimationThread.AddTask( this ); + + DALI_LOG_INFO( gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationTask::PlayAnimation: Play [%p]\n", this ); + } +} + +void VectorAnimationTask::StopAnimation() +{ + ConditionalWait::ScopedLock lock( mConditionalWait ); + if( mPlayState != PlayState::STOPPED && mPlayState != PlayState::STOPPING ) + { + mNeedAnimationFinishedTrigger = false; + mPlayState = PlayState::STOPPING; + + DALI_LOG_INFO( gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationTask::StopAnimation: Stop [%p]\n", this ); + } +} + +void VectorAnimationTask::PauseAnimation() +{ + ConditionalWait::ScopedLock lock( mConditionalWait ); + if( mPlayState == PlayState::PLAYING ) + { + mPlayState = PlayState::PAUSED; + + DALI_LOG_INFO( gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationTask::PauseAnimation: Pause [%p]\n", this ); + } +} + +void VectorAnimationTask::RenderFrame() +{ + ConditionalWait::ScopedLock lock( mConditionalWait ); + + if( !mResourceReady ) + { + mVectorAnimationThread.AddTask( this ); + + DALI_LOG_INFO( gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationTask::RenderFrame: Render [%p]\n", this ); + } +} + +void VectorAnimationTask::SetAnimationFinishedCallback( EventThreadCallback* callback ) +{ + ConditionalWait::ScopedLock lock( mConditionalWait ); + mAnimationFinishedTrigger = std::unique_ptr< EventThreadCallback >( callback ); +} + +void VectorAnimationTask::SetLoopCount( int32_t count ) +{ + if( mLoopCount != count ) + { + ConditionalWait::ScopedLock lock( mConditionalWait ); + + mLoopCount = count; + mCurrentLoop = 0; + mCurrentLoopUpdated = true; + + DALI_LOG_INFO( gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationTask::SetLoopCount: [%d] [%p]\n", count, this ); + } +} + +void VectorAnimationTask::SetPlayRange( uint32_t startFrame, uint32_t endFrame ) +{ + // Make sure the range specified is between 0 and the total frame number + if( ( startFrame < mTotalFrame ) && ( endFrame < mTotalFrame ) ) + { + // If the range is not in order swap values + if( startFrame > endFrame ) + { + uint32_t temp = startFrame; + startFrame = endFrame; + endFrame = temp; + } + + if( startFrame != mStartFrame || endFrame != mEndFrame ) + { + ConditionalWait::ScopedLock lock( mConditionalWait ); + + mStartFrame = startFrame; + mEndFrame = endFrame; + + // If the current frame is out of the range, change the current frame also. + if( mStartFrame > mCurrentFrame ) + { + mCurrentFrame = mStartFrame; + + mCurrentFrameUpdated = true; + mResourceReady = false; + } + else if( mEndFrame < mCurrentFrame ) + { + mCurrentFrame = mEndFrame; + + mCurrentFrameUpdated = true; + mResourceReady = false; + } + + DALI_LOG_INFO( gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationTask::SetPlayRange: [%d, %d] [%p]\n", mStartFrame, mEndFrame, this ); + } + } +} + +void VectorAnimationTask::GetPlayRange( uint32_t& startFrame, uint32_t& endFrame ) +{ + startFrame = mStartFrame; + endFrame = mEndFrame; +} + +DevelImageVisual::PlayState::Type VectorAnimationTask::GetPlayState() const +{ + DevelImageVisual::PlayState::Type state = DevelImageVisual::PlayState::STOPPED; + + switch( mPlayState ) + { + case PlayState::PLAYING: + { + state = DevelImageVisual::PlayState::PLAYING; + break; + } + case PlayState::PAUSED: + { + state = DevelImageVisual::PlayState::PAUSED; + break; + } + case PlayState::STOPPING: + case PlayState::STOPPED: + { + state = DevelImageVisual::PlayState::STOPPED; + break; + } + } + + return state; +} + +void VectorAnimationTask::SetCurrentFrameNumber( uint32_t frameNumber ) +{ + ConditionalWait::ScopedLock lock( mConditionalWait ); + + if( mCurrentFrame == frameNumber ) + { + DALI_LOG_INFO( gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationTask::SetCurrentFrameNumber: Set same frame [%d] [%p]\n", frameNumber, this ); + return; + } + + if( frameNumber >= mStartFrame && frameNumber <= mEndFrame ) + { + mCurrentFrame = frameNumber; + mCurrentFrameUpdated = true; + + mResourceReady = false; + + DALI_LOG_INFO( gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationTask::SetCurrentFrameNumber: frame number = %d [%p]\n", mCurrentFrame, this ); + } + else + { + DALI_LOG_ERROR( "Invalid frame number [%d (%d, %d)]\n", frameNumber, mStartFrame, mEndFrame ); + } +} + +uint32_t VectorAnimationTask::GetCurrentFrameNumber() const +{ + return mCurrentFrame; +} + +uint32_t VectorAnimationTask::GetTotalFrameNumber() const +{ + return mTotalFrame; +} + +void VectorAnimationTask::GetDefaultSize( uint32_t& width, uint32_t& height ) const +{ + mVectorRenderer.GetDefaultSize( width, height ); +} + +void VectorAnimationTask::SetStopBehavior( DevelImageVisual::StopBehavior::Type stopBehavior ) +{ + ConditionalWait::ScopedLock lock( mConditionalWait ); + mStopBehavior = stopBehavior; + + DALI_LOG_INFO( gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationTask::SetStopBehavior: stop behavor = %d [%p]\n", mStopBehavior, this ); +} + +void VectorAnimationTask::SetLoopingMode( DevelImageVisual::LoopingMode::Type loopingMode ) +{ + ConditionalWait::ScopedLock lock( mConditionalWait ); + mLoopingMode = loopingMode; + + DALI_LOG_INFO( gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationTask::SetLoopingMode: looping mode = %d [%p]\n", mLoopingMode, this ); +} + +VectorAnimationTask::UploadCompletedSignalType& VectorAnimationTask::UploadCompletedSignal() +{ + return mVectorRenderer.UploadCompletedSignal(); +} + +void VectorAnimationTask::Initialize() +{ + mVectorRenderer = VectorAnimationRenderer::New( mUrl ); + + mTotalFrame = mVectorRenderer.GetTotalFrameNumber(); + + mEndFrame = mTotalFrame - 1; + + mFrameRate = mVectorRenderer.GetFrameRate(); + mFrameDurationNanoSeconds = NANOSECONDS_PER_SECOND / mFrameRate; + + uint32_t width, height; + mVectorRenderer.GetDefaultSize( width, height ); + + SetSize( width, height ); + + DALI_LOG_INFO( gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationTask::Initialize: file = %s [%d frames, %f fps] [%p]\n", mUrl.c_str(), mTotalFrame, mFrameRate, this ); +} + +bool VectorAnimationTask::Rasterize() +{ + bool stopped = false, needAnimationFinishedTrigger; + uint32_t currentFrame, startFrame, endFrame; + int32_t loopCount, currentLoopCount; + PlayState playState; + + { + ConditionalWait::ScopedLock lock( mConditionalWait ); + + if( mPlayState == PlayState::PLAYING && mUpdateFrameNumber ) + { + mCurrentFrame = mForward ? mCurrentFrame + 1 : mCurrentFrame - 1; + } + + currentFrame = mCurrentFrame; + startFrame = mStartFrame; + endFrame = mEndFrame; + loopCount = mLoopCount; + currentLoopCount = mCurrentLoop; + needAnimationFinishedTrigger = mNeedAnimationFinishedTrigger; + playState = mPlayState; + + mResourceReady = true; + mCurrentFrameUpdated = false; + mCurrentLoopUpdated = false; + mUpdateFrameNumber = true; + mNeedAnimationFinishedTrigger = true; + } + + if( playState == PlayState::STOPPING ) + { + currentFrame = GetStoppedFrame( startFrame, endFrame, currentFrame ); + ResetValue( mCurrentFrameUpdated, mCurrentFrame, currentFrame, mConditionalWait ); + + stopped = true; + } + else if( playState == PlayState::PLAYING ) + { + bool animationFinished = false; + + if( currentFrame >= endFrame ) // last frame + { + if( mLoopingMode == DevelImageVisual::LoopingMode::AUTO_REVERSE ) + { + mForward = false; + } + else + { + if( loopCount < 0 || ++currentLoopCount < loopCount ) // repeat forever or before the last loop + { + ResetValue( mCurrentFrameUpdated, mCurrentFrame, startFrame, mConditionalWait ); // If the current frame is changed in the event thread, don't overwrite it. + mUpdateFrameNumber = false; + } + else + { + animationFinished = true; // end of animation + } + ResetValue( mCurrentLoopUpdated, mCurrentLoop, currentLoopCount, mConditionalWait ); + } + } + else if( currentFrame == startFrame && !mForward ) // first frame + { + if( loopCount < 0 || ++currentLoopCount < loopCount ) // repeat forever or before the last loop + { + mForward = true; + } + else + { + animationFinished = true; // end of animation + } + ResetValue( mCurrentLoopUpdated, mCurrentLoop, currentLoopCount, mConditionalWait ); + } + + if( animationFinished ) + { + if( mStopBehavior == DevelImageVisual::StopBehavior::CURRENT_FRAME ) + { + stopped = true; + } + else + { + mPlayState = PlayState::STOPPING; + } + } + } + + // Rasterize + bool renderSuccess = mVectorRenderer.Render( currentFrame ); + if( !renderSuccess ) + { + DALI_LOG_INFO( gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationTask::Rasterize: Rendering failed. Try again later.[%d] [%p]\n", currentFrame, this ); + mUpdateFrameNumber = false; + } + + if( stopped && renderSuccess ) + { + mPlayState = PlayState::STOPPED; + mForward = true; + mCurrentLoop = 0; + + // Animation is finished + if( needAnimationFinishedTrigger && mAnimationFinishedTrigger ) + { + mAnimationFinishedTrigger->Trigger(); + } + + DALI_LOG_INFO( gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationTask::Rasterize: Animation is finished [current = %d] [%p]\n", currentFrame, this ); + } + + bool keepAnimation = true; + if( playState == PlayState::PAUSED || playState == PlayState::STOPPED ) + { + keepAnimation = false; + } + + return keepAnimation; +} + +uint32_t VectorAnimationTask::GetStoppedFrame( uint32_t startFrame, uint32_t endFrame, uint32_t currentFrame ) +{ + uint32_t frame = currentFrame; + + switch( mStopBehavior ) + { + case DevelImageVisual::StopBehavior::FIRST_FRAME: + { + frame = startFrame; + break; + } + case DevelImageVisual::StopBehavior::LAST_FRAME: + { + if( mLoopingMode == DevelImageVisual::LoopingMode::AUTO_REVERSE ) + { + frame = startFrame; + } + else + { + frame = endFrame; + } + break; + } + case DevelImageVisual::StopBehavior::CURRENT_FRAME: + { + frame = currentFrame; + break; + } + } + + return frame; +} + +std::chrono::time_point< std::chrono::system_clock > VectorAnimationTask::CalculateNextFrameTime( bool renderNow ) +{ + mNextFrameStartTime = mNextFrameStartTime + std::chrono::nanoseconds( mFrameDurationNanoSeconds ); + auto current = std::chrono::system_clock::now(); + if( renderNow || mNextFrameStartTime < current ) + { + mNextFrameStartTime = current; + } + return mNextFrameStartTime; +} + +std::chrono::time_point< std::chrono::system_clock > VectorAnimationTask::GetNextFrameTime() +{ + return mNextFrameStartTime; +} + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/visuals/animated-vector-image/vector-animation-task.h b/dali-toolkit/internal/visuals/animated-vector-image/vector-animation-task.h new file mode 100644 index 0000000..c13b4d3 --- /dev/null +++ b/dali-toolkit/internal/visuals/animated-vector-image/vector-animation-task.h @@ -0,0 +1,258 @@ +#ifndef DALI_TOOLKIT_VECTOR_ANIMATION_TASK_H +#define DALI_TOOLKIT_VECTOR_ANIMATION_TASK_H + +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// EXTERNAL INCLUDES +#include +#include +#include +#include +#include + +// INTERNAL INCLUDES +#include + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +class VisualFactoryCache; +class VectorAnimationThread; +class VectorAnimationTask; +typedef IntrusivePtr< VectorAnimationTask > VectorAnimationTaskPtr; + +/** + * The task of the vector animation. + */ +class VectorAnimationTask : public RefObject +{ +public: + + using UploadCompletedSignalType = Dali::VectorAnimationRenderer::UploadCompletedSignalType; + + /** + * @brief Constructor. + * + * @param[in] factoryCache A pointer pointing to the VisualFactoryCache object + * @param[in] url The url of the vector animation file + */ + VectorAnimationTask( VisualFactoryCache& factoryCache, const std::string& url ); + + /** + * @brief Destructor. + */ + virtual ~VectorAnimationTask(); + + /** + * @brief Sets the renderer used to display the result image. + * + * @param[in] renderer The renderer used to display the result image + */ + void SetRenderer( Renderer renderer ); + + /** + * @brief Sets the target image size. + * + * @param[in] width The target image width + * @param[in] height The target image height + */ + void SetSize( uint32_t width, uint32_t height ); + + /** + * @brief Play the vector animation. + */ + void PlayAnimation(); + + /** + * @brief Stop the vector animation. + */ + void StopAnimation(); + + /** + * @brief Pause the vector animation. + */ + void PauseAnimation(); + + /** + * @brief Render one frame. The current frame number will be increased. + */ + void RenderFrame(); + + /** + * @brief This callback is called after the animation is finished. + * @param[in] callback The animation finished callback + */ + void SetAnimationFinishedCallback( EventThreadCallback* callback ); + + /** + * @brief Enable looping for 'count' repeats. -1 means to repeat forever. + * @param[in] count The number of times to loop + */ + void SetLoopCount( int32_t count ); + + /** + * @brief Set the playing range in frame number. + * @param[in] startFrame The frame number to specify minimum progress. + * @param[in] endFrame The frame number to specify maximum progress. + * The animation will play between those values. + */ + void SetPlayRange( uint32_t startFrame, uint32_t endFrame ); + + /** + * @brief Gets the playing range in frame number. + * @param[out] startFrame The frame number to specify minimum progress. + * @param[out] endFrame The frame number to specify maximum progress. + */ + void GetPlayRange( uint32_t& startFrame, uint32_t& endFrame ); + + /** + * @brief Get the play state + * @return The play state + */ + DevelImageVisual::PlayState::Type GetPlayState() const; + + /** + * @brief Sets the current frame number of the animation. + * @param[in] frameNumber The new frame number between [0, the maximum frame number] or between the play range if specified. + */ + void SetCurrentFrameNumber( uint32_t frameNumber ); + + /** + * @brief Retrieves the current frame number of the animation. + * @return The current frame number + */ + uint32_t GetCurrentFrameNumber() const; + + /** + * @brief Retrieves the total frame number of the animation. + * @return The total frame number + */ + uint32_t GetTotalFrameNumber() const; + + /** + * @brief Gets the default size of the file,. + * @return The default size of the file + */ + void GetDefaultSize( uint32_t& width, uint32_t& height ) const; + + /** + * @brief Sets the stop behavior of the animation. This is performed when the animation is stopped. + * @param[in] stopBehavior The stop behavior + */ + void SetStopBehavior( DevelImageVisual::StopBehavior::Type stopBehavior ); + + /** + * @brief Sets the looping mode. + * Animation plays forwards and then restarts from the beginning or runs backwards again. + * @param[in] loopingMode The looping mode + */ + void SetLoopingMode( DevelImageVisual::LoopingMode::Type loopingMode ); + + /** + * @brief Connect to this signal to be notified when the texture upload is completed. + * @return The signal to connect to. + */ + UploadCompletedSignalType& UploadCompletedSignal(); + + /** + * @brief Rasterizes the current frame. + * @return true if the animation is running, false otherwise. + */ + bool Rasterize(); + + /** + * @brief Calculates the time for the next frame rasterization. + * @return The time for the next frame rasterization. + */ + std::chrono::time_point< std::chrono::system_clock > CalculateNextFrameTime( bool renderNow ); + + /** + * @brief Gets the time for the next frame rasterization. + * @return The time for the next frame rasterization. + */ + std::chrono::time_point< std::chrono::system_clock > GetNextFrameTime(); + +private: + + /** + * @brief Initializes the vector renderer. + */ + void Initialize(); + + /** + * @brief Gets the frame number when the animation is stopped according to the stop behavior. + */ + uint32_t GetStoppedFrame( uint32_t startFrame, uint32_t endFrame, uint32_t currentFrame ); + + // Undefined + VectorAnimationTask( const VectorAnimationTask& task ) = delete; + + // Undefined + VectorAnimationTask& operator=( const VectorAnimationTask& task ) = delete; + +private: + + enum class PlayState + { + STOPPING, ///< The animation is stopping + STOPPED, ///< The animation has stopped + PLAYING, ///< The animation is playing + PAUSED ///< The animation is paused + }; + + std::string mUrl; + VectorAnimationRenderer mVectorRenderer; + VectorAnimationThread& mVectorAnimationThread; + ConditionalWait mConditionalWait; + std::unique_ptr< EventThreadCallback > mAnimationFinishedTrigger; + Vector2 mPlayRange; + PlayState mPlayState; + DevelImageVisual::StopBehavior::Type mStopBehavior; + DevelImageVisual::LoopingMode::Type mLoopingMode; + std::chrono::time_point< std::chrono::system_clock > mNextFrameStartTime; + int64_t mFrameDurationNanoSeconds; + float mFrameRate; + uint32_t mCurrentFrame; + uint32_t mTotalFrame; + uint32_t mStartFrame; + uint32_t mEndFrame; + uint32_t mWidth; + uint32_t mHeight; + int32_t mLoopCount; + int32_t mCurrentLoop; + bool mResourceReady; + bool mCurrentFrameUpdated; + bool mCurrentLoopUpdated; + bool mForward; + bool mUpdateFrameNumber; + bool mNeedAnimationFinishedTrigger; + +}; + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali + +#endif // DALI_TOOLKIT_VECTOR_ANIMATION_TASK_H diff --git a/dali-toolkit/internal/visuals/animated-vector-image/vector-animation-thread.cpp b/dali-toolkit/internal/visuals/animated-vector-image/vector-animation-thread.cpp new file mode 100644 index 0000000..a4a2a16 --- /dev/null +++ b/dali-toolkit/internal/visuals/animated-vector-image/vector-animation-thread.cpp @@ -0,0 +1,330 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// CLASS HEADER +#include + +// EXTERNAL INCLUDES +#include +#include +#include +#include +#include + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +namespace +{ + +constexpr auto DEFAULT_NUMBER_OF_RASTERIZE_THREADS = size_t{ 4u }; +constexpr auto NUMBER_OF_RASTERIZE_THREADS_ENV = "DALI_VECTOR_RASTERIZE_THREADS"; + +size_t GetNumberOfThreads( const char* environmentVariable, size_t defaultValue ) +{ + using Dali::EnvironmentVariable::GetEnvironmentVariable; + auto numberString = GetEnvironmentVariable( environmentVariable ); + auto numberOfThreads = numberString ? std::strtoul( numberString, nullptr, 10 ) : 0; + constexpr auto MAX_NUMBER_OF_THREADS = 100u; + DALI_ASSERT_DEBUG( numberOfThreads < MAX_NUMBER_OF_THREADS ); + return ( numberOfThreads > 0 && numberOfThreads < MAX_NUMBER_OF_THREADS ) ? numberOfThreads : defaultValue; +} + +#if defined(DEBUG_ENABLED) +Debug::Filter* gVectorAnimationLogFilter = Debug::Filter::New( Debug::NoLogging, false, "LOG_VECTOR_ANIMATION" ); +#endif + +} // unnamed namespace + +VectorAnimationThread::VectorAnimationThread() +: mAnimationTasks(), + mCompletedTasks(), + mRasterizers( GetNumberOfThreads( NUMBER_OF_RASTERIZE_THREADS_ENV, DEFAULT_NUMBER_OF_RASTERIZE_THREADS ), [&]() { return RasterizeHelper( *this ); } ), + mSleepThread( MakeCallback( this, &VectorAnimationThread::OnAwakeFromSleep ) ), + mConditionalWait(), + mNeedToSleep( false ), + mDestroyThread( false ), + mLogFactory( Dali::Adaptor::Get().GetLogFactory() ) +{ + mSleepThread.Start(); +} + +VectorAnimationThread::~VectorAnimationThread() +{ + // Stop the thread + { + ConditionalWait::ScopedLock lock( mConditionalWait ); + mDestroyThread = true; + mConditionalWait.Notify( lock ); + } + + DALI_LOG_INFO( gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationThread::~VectorAnimationThread: Join [%p]\n", this ); + + Join(); +} + +void VectorAnimationThread::AddTask( VectorAnimationTaskPtr task ) +{ + ConditionalWait::ScopedLock lock( mConditionalWait ); + + if( mAnimationTasks.end() == std::find( mAnimationTasks.begin(), mAnimationTasks.end(), task ) ) + { + auto currentTime = task->CalculateNextFrameTime( true ); // Rasterize as soon as possible + + bool inserted = false; + for( auto iter = mAnimationTasks.begin(); iter != mAnimationTasks.end(); ++iter ) + { + auto nextTime = (*iter)->GetNextFrameTime(); + if( nextTime > currentTime ) + { + mAnimationTasks.insert( iter, task ); + inserted = true; + break; + } + } + + if( !inserted ) + { + mAnimationTasks.push_back( task ); + } + + // wake up the animation thread + mConditionalWait.Notify( lock ); + } +} + +void VectorAnimationThread::OnTaskCompleted( VectorAnimationTaskPtr task, bool keepAnimation ) +{ + if( keepAnimation && !mDestroyThread ) + { + ConditionalWait::ScopedLock lock( mConditionalWait ); + + if( mAnimationTasks.end() == std::find( mAnimationTasks.begin(), mAnimationTasks.end(), task ) ) + { + mCompletedTasks.push_back( task ); + + // wake up the animation thread + mConditionalWait.Notify( lock ); + } + } +} + +void VectorAnimationThread::OnAwakeFromSleep() +{ + if( !mDestroyThread ) + { + mNeedToSleep = false; + // wake up the animation thread + mConditionalWait.Notify(); + } +} + +void VectorAnimationThread::Run() +{ + SetThreadName( "VectorAnimationThread" ); + mLogFactory.InstallLogFunction(); + + while( !mDestroyThread ) + { + Rasterize(); + } +} + +void VectorAnimationThread::Rasterize() +{ + // Lock while popping task out from the queue + ConditionalWait::ScopedLock lock( mConditionalWait ); + + // conditional wait + if( (mAnimationTasks.empty() && mCompletedTasks.empty() ) || mNeedToSleep ) + { + mConditionalWait.Wait( lock ); + } + + mNeedToSleep = false; + + // Process completed tasks + for( auto&& task : mCompletedTasks ) + { + if( mAnimationTasks.end() == std::find( mAnimationTasks.begin(), mAnimationTasks.end(), task ) ) + { + // Should use the frame rate of the animation file + auto nextFrameTime = task->CalculateNextFrameTime( false ); + + bool inserted = false; + for( auto iter = mAnimationTasks.begin(); iter != mAnimationTasks.end(); ++iter ) + { + auto time = (*iter)->GetNextFrameTime(); + if( time > nextFrameTime ) + { + mAnimationTasks.insert( iter, task ); + inserted = true; + break; + } + } + + if( !inserted ) + { + mAnimationTasks.push_back( task ); + } + } + } + mCompletedTasks.clear(); + + // pop out the next task from the queue + if( !mAnimationTasks.empty() && !mNeedToSleep ) + { + std::vector< VectorAnimationTaskPtr >::iterator next = mAnimationTasks.begin(); + VectorAnimationTaskPtr nextTask = *next; + + auto currentTime = std::chrono::system_clock::now(); + auto nextFrameTime = nextTask->GetNextFrameTime(); + +#if defined(DEBUG_ENABLED) + auto duration = std::chrono::duration_cast< std::chrono::milliseconds >( nextFrameTime - currentTime ); + + DALI_LOG_INFO( gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationThread::Rasterize: [next time = %lld]\n", duration.count() ); +#endif + + if( nextFrameTime <= currentTime ) + { + mAnimationTasks.erase( next ); + + auto rasterizerHelperIt = mRasterizers.GetNext(); + DALI_ASSERT_ALWAYS( rasterizerHelperIt != mRasterizers.End() ); + + rasterizerHelperIt->Rasterize( nextTask ); + } + else + { + mNeedToSleep = true; + mSleepThread.SleepUntil( nextFrameTime ); + } + } +} + +VectorAnimationThread::RasterizeHelper::RasterizeHelper( VectorAnimationThread& animationThread ) +: RasterizeHelper( std::unique_ptr< VectorRasterizeThread >( new VectorRasterizeThread() ), animationThread ) +{ +} + +VectorAnimationThread::RasterizeHelper::RasterizeHelper( RasterizeHelper&& rhs ) +: RasterizeHelper( std::move( rhs.mRasterizer ), rhs.mAnimationThread ) +{ +} + +VectorAnimationThread::RasterizeHelper::RasterizeHelper( std::unique_ptr< VectorRasterizeThread > rasterizer, VectorAnimationThread& animationThread ) +: mRasterizer( std::move( rasterizer ) ), + mAnimationThread( animationThread ) +{ + mRasterizer->SetCompletedCallback( MakeCallback( &mAnimationThread, &VectorAnimationThread::OnTaskCompleted ) ); +} + +void VectorAnimationThread::RasterizeHelper::Rasterize( VectorAnimationTaskPtr task ) +{ + if( task ) + { + mRasterizer->AddTask( task ); + } +} + +VectorAnimationThread::SleepThread::SleepThread( CallbackBase* callback ) +: mConditionalWait(), + mAwakeCallback( std::unique_ptr< CallbackBase >( callback ) ), + mSleepTimePoint(), + mLogFactory( Dali::Adaptor::Get().GetLogFactory() ), + mNeedToSleep( false ), + mDestroyThread( false ) +{ +} + +VectorAnimationThread::SleepThread::~SleepThread() +{ + // Stop the thread + { + ConditionalWait::ScopedLock lock( mConditionalWait ); + mDestroyThread = true; + mConditionalWait.Notify( lock ); + } + + Join(); +} + +void VectorAnimationThread::SleepThread::SleepUntil( std::chrono::time_point< std::chrono::system_clock > timeToSleepUntil ) +{ + ConditionalWait::ScopedLock lock( mConditionalWait ); + mSleepTimePoint = timeToSleepUntil; + mNeedToSleep = true; + mConditionalWait.Notify( lock ); +} + +void VectorAnimationThread::SleepThread::Run() +{ + SetThreadName( "VectorSleepThread" ); + mLogFactory.InstallLogFunction(); + + while( !mDestroyThread ) + { + bool needToSleep; + std::chrono::time_point< std::chrono::system_clock > sleepTimePoint; + + { + ConditionalWait::ScopedLock lock( mConditionalWait ); + + needToSleep = mNeedToSleep; + sleepTimePoint = mSleepTimePoint; + + mNeedToSleep = false; + } + + if( needToSleep ) + { +#if defined(DEBUG_ENABLED) + auto sleepDuration = std::chrono::duration_cast< std::chrono::milliseconds >( mSleepTimePoint - std::chrono::system_clock::now() ); + + DALI_LOG_INFO( gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationThread::SleepThread::Run: [sleep duration = %lld]\n", sleepDuration.count() ); +#endif + + std::this_thread::sleep_until( sleepTimePoint ); + + if( mAwakeCallback ) + { + CallbackBase::Execute( *mAwakeCallback ); + } + } + + { + ConditionalWait::ScopedLock lock( mConditionalWait ); + if( !mDestroyThread && !mNeedToSleep ) + { + mConditionalWait.Wait( lock ); + } + } + } +} + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/visuals/animated-vector-image/vector-animation-thread.h b/dali-toolkit/internal/visuals/animated-vector-image/vector-animation-thread.h new file mode 100755 index 0000000..63e3525 --- /dev/null +++ b/dali-toolkit/internal/visuals/animated-vector-image/vector-animation-thread.h @@ -0,0 +1,201 @@ +#ifndef DALI_TOOLKIT_VECTOR_ANIMATION_THREAD_H +#define DALI_TOOLKIT_VECTOR_ANIMATION_THREAD_H + +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// EXTERNAL INCLUDES +#include +#include +#include +#include +#include + +// INTERNAL INCLUDES +#include +#include +#include + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +/** + * The main animation thread for vector animations + */ +class VectorAnimationThread : public Thread +{ +public: + + /** + * @brief Constructor. + */ + VectorAnimationThread(); + + /** + * @brief Destructor. + */ + virtual ~VectorAnimationThread(); + + /** + * Add a animation task into the vector animation thread, called by main thread. + * + * @param[in] task The task added to the thread. + */ + void AddTask( VectorAnimationTaskPtr task ); + + /** + * @brief Called when the rasterization is completed from the rasterize thread. + * @param task The completed task + */ + void OnTaskCompleted( VectorAnimationTaskPtr task, bool stopped ); + + /** + * @brief Called when the sleep thread is awaken. + */ + void OnAwakeFromSleep(); + +protected: + + /** + * @brief The entry function of the animation thread. + */ + void Run() override; + +private: + + /** + * Rasterizes the tasks. + */ + void Rasterize(); + +private: + + /** + * @brief Helper class to keep the relation between VectorRasterizeThread and corresponding container + */ + class RasterizeHelper : public ConnectionTracker + { + public: + /** + * @brief Create an RasterizeHelper. + * + * @param[in] animationThread Reference to the VectorAnimationThread + */ + RasterizeHelper( VectorAnimationThread& animationThread ); + + /** + * @brief Rasterizes the task. + * + * @param[in] task The task to rasterize. + */ + void Rasterize( VectorAnimationTaskPtr task ); + + public: + RasterizeHelper( const RasterizeHelper& ) = delete; + RasterizeHelper& operator=( const RasterizeHelper& ) = delete; + + RasterizeHelper( RasterizeHelper&& rhs ); + RasterizeHelper& operator=( RasterizeHelper&& rhs ) = delete; + + private: + + /** + * @brief Main constructor that used by all other constructors + */ + RasterizeHelper( std::unique_ptr< VectorRasterizeThread > rasterizer, VectorAnimationThread& animationThread ); + + private: + std::unique_ptr< VectorRasterizeThread > mRasterizer; + VectorAnimationThread& mAnimationThread; + }; + + /** + * @brief The thread to sleep until the next frame time. + */ + class SleepThread : public Thread + { + public: + + /** + * @brief Constructor. + */ + SleepThread( CallbackBase* callback ); + + /** + * @brief Destructor. + */ + virtual ~SleepThread(); + + /** + * @brief Sleeps untile the specified time point. + */ + void SleepUntil( std::chrono::time_point< std::chrono::system_clock > timeToSleepUntil ); + + protected: + + /** + * @brief The entry function of the animation thread. + */ + void Run() override; + + private: + + SleepThread( const SleepThread& thread ) = delete; + SleepThread& operator=( const SleepThread& thread ) = delete; + + private: + ConditionalWait mConditionalWait; + std::unique_ptr< CallbackBase > mAwakeCallback; + std::chrono::time_point< std::chrono::system_clock > mSleepTimePoint; + const Dali::LogFactoryInterface& mLogFactory; + bool mNeedToSleep; + bool mDestroyThread; + }; + +private: + + // Undefined + VectorAnimationThread( const VectorAnimationThread& thread ) = delete; + + // Undefined + VectorAnimationThread& operator=( const VectorAnimationThread& thread ) = delete; + +private: + + std::vector< VectorAnimationTaskPtr > mAnimationTasks; + std::vector< VectorAnimationTaskPtr > mCompletedTasks; + RoundRobinContainerView< RasterizeHelper > mRasterizers; + SleepThread mSleepThread; + ConditionalWait mConditionalWait; + bool mNeedToSleep; + bool mDestroyThread; + const Dali::LogFactoryInterface& mLogFactory; + +}; + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali + +#endif // #endif // DALI_TOOLKIT_VECTOR_ANIMATION_THREAD_H diff --git a/dali-toolkit/internal/visuals/animated-vector-image/vector-rasterize-thread.cpp b/dali-toolkit/internal/visuals/animated-vector-image/vector-rasterize-thread.cpp index 2c39333..a640b19 100644 --- a/dali-toolkit/internal/visuals/animated-vector-image/vector-rasterize-thread.cpp +++ b/dali-toolkit/internal/visuals/animated-vector-image/vector-rasterize-thread.cpp @@ -37,55 +37,20 @@ namespace Internal namespace { -constexpr auto LOOP_FOREVER = -1; -constexpr auto NANOSECONDS_PER_SECOND( 1e+9 ); - #if defined(DEBUG_ENABLED) Debug::Filter* gVectorAnimationLogFilter = Debug::Filter::New( Debug::NoLogging, false, "LOG_VECTOR_ANIMATION" ); #endif -template< typename T > -inline void ResetValue( bool& updated, T& value, T newValue, ConditionalWait& conditionalWait ) -{ - ConditionalWait::ScopedLock lock( conditionalWait ); - if( !updated ) - { - value = newValue; - updated = true; - } -} - } // unnamed namespace -VectorRasterizeThread::VectorRasterizeThread( const std::string& url ) -: mUrl( url ), - mVectorRenderer(), +VectorRasterizeThread::VectorRasterizeThread() +: mRasterizeTasks(), mConditionalWait(), - mAnimationFinishedTrigger(), - mPlayState( PlayState::STOPPED ), - mStopBehavior( DevelImageVisual::StopBehavior::CURRENT_FRAME ), - mLoopingMode( DevelImageVisual::LoopingMode::RESTART ), - mFrameDurationNanoSeconds( 0 ), - mFrameRate( 60.0f ), - mCurrentFrame( 0 ), - mTotalFrame( 0 ), - mStartFrame( 0 ), - mEndFrame( 0 ), - mWidth( 0 ), - mHeight( 0 ), - mLoopCount( LOOP_FOREVER ), - mCurrentLoop( 0 ), - mNeedRender( false ), + mCompletedCallback(), mDestroyThread( false ), - mResourceReady( false ), - mCurrentFrameUpdated( false ), - mCurrentLoopUpdated( false ), - mForward( true ), - mUpdateFrameNumber( false ), - mNeedAnimationFinishedTrigger( true ), + mIsThreadStarted( false ), mLogFactory( Dali::Adaptor::Get().GetLogFactory() ) { - Initialize(); } VectorRasterizeThread::~VectorRasterizeThread() @@ -102,428 +67,75 @@ VectorRasterizeThread::~VectorRasterizeThread() Join(); } -void VectorRasterizeThread::Run() -{ - SetThreadName( "VectorImageThread" ); - mLogFactory.InstallLogFunction(); - - while( !mDestroyThread ) - { - Rasterize(); - } - - DALI_LOG_INFO( gVectorAnimationLogFilter, Debug::Verbose, "VectorRasterizeThread::Run: End of thread [%p]\n", this ); -} - -void VectorRasterizeThread::SetRenderer( Renderer renderer ) +void VectorRasterizeThread::SetCompletedCallback( CallbackBase* callback ) { ConditionalWait::ScopedLock lock( mConditionalWait ); - mVectorRenderer.SetRenderer( renderer ); - - DALI_LOG_INFO( gVectorAnimationLogFilter, Debug::Verbose, "VectorRasterizeThread::SetRenderer [%p]\n", this ); -} - -void VectorRasterizeThread::SetSize( uint32_t width, uint32_t height ) -{ - if( mWidth != width || mHeight != height ) - { - ConditionalWait::ScopedLock lock( mConditionalWait ); - mVectorRenderer.SetSize( width, height ); - - mWidth = width; - mHeight = height; - - mResourceReady = false; - - DALI_LOG_INFO( gVectorAnimationLogFilter, Debug::Verbose, "VectorRasterizeThread::SetSize: width = %d, height = %d [%p]\n", width, height, this ); - } + mCompletedCallback = std::unique_ptr< CallbackBase >( callback ); } -void VectorRasterizeThread::PlayAnimation() +void VectorRasterizeThread::AddTask( VectorAnimationTaskPtr task ) { - ConditionalWait::ScopedLock lock( mConditionalWait ); - - if( mPlayState != PlayState::PLAYING ) + if( !mIsThreadStarted ) { - mNeedRender = true; - mUpdateFrameNumber = false; - mPlayState = PlayState::PLAYING; - mConditionalWait.Notify( lock ); - - DALI_LOG_INFO( gVectorAnimationLogFilter, Debug::Verbose, "VectorRasterizeThread::PlayAnimation: Play [%p]\n", this ); + Start(); + mIsThreadStarted = true; } -} -void VectorRasterizeThread::StopAnimation() -{ + // Lock while adding task to the queue ConditionalWait::ScopedLock lock( mConditionalWait ); - if( mPlayState != PlayState::STOPPED && mPlayState != PlayState::STOPPING ) - { - mNeedRender = true; - mNeedAnimationFinishedTrigger = false; - mPlayState = PlayState::STOPPING; - mConditionalWait.Notify( lock ); - - DALI_LOG_INFO( gVectorAnimationLogFilter, Debug::Verbose, "VectorRasterizeThread::StopAnimation: Stop [%p]\n", this ); - } -} -void VectorRasterizeThread::PauseAnimation() -{ - ConditionalWait::ScopedLock lock( mConditionalWait ); - if( mPlayState == PlayState::PLAYING ) + if( mRasterizeTasks.end() == std::find( mRasterizeTasks.begin(), mRasterizeTasks.end(), task ) ) { - mPlayState = PlayState::PAUSED; - - DALI_LOG_INFO( gVectorAnimationLogFilter, Debug::Verbose, "VectorRasterizeThread::PauseAnimation: Pause [%p]\n", this ); - } -} - -void VectorRasterizeThread::RenderFrame() -{ - ConditionalWait::ScopedLock lock( mConditionalWait ); + mRasterizeTasks.push_back( task ); - if( !mResourceReady ) - { - mNeedRender = true; + // wake up the animation thread mConditionalWait.Notify( lock ); - - DALI_LOG_INFO( gVectorAnimationLogFilter, Debug::Verbose, "VectorRasterizeThread::RenderFrame: Render [%p]\n", this ); - } -} - -void VectorRasterizeThread::SetAnimationFinishedCallback( EventThreadCallback* callback ) -{ - ConditionalWait::ScopedLock lock( mConditionalWait ); - mAnimationFinishedTrigger = std::unique_ptr< EventThreadCallback >( callback ); -} - -void VectorRasterizeThread::SetLoopCount( int32_t count ) -{ - if( mLoopCount != count ) - { - ConditionalWait::ScopedLock lock( mConditionalWait ); - - mLoopCount = count; - mCurrentLoop = 0; - mCurrentLoopUpdated = true; - - DALI_LOG_INFO( gVectorAnimationLogFilter, Debug::Verbose, "VectorRasterizeThread::SetLoopCount: [%d] [%p]\n", count, this ); - } -} - -void VectorRasterizeThread::SetPlayRange( uint32_t startFrame, uint32_t endFrame ) -{ - // Make sure the range specified is between 0 and the total frame number - if( ( startFrame < mTotalFrame ) && ( endFrame < mTotalFrame ) ) - { - // If the range is not in order swap values - if( startFrame > endFrame ) - { - uint32_t temp = startFrame; - startFrame = endFrame; - endFrame = temp; - } - - if( startFrame != mStartFrame || endFrame != mEndFrame ) - { - ConditionalWait::ScopedLock lock( mConditionalWait ); - - mStartFrame = startFrame; - mEndFrame = endFrame; - - // If the current frame is out of the range, change the current frame also. - if( mStartFrame > mCurrentFrame ) - { - mCurrentFrame = mStartFrame; - - mCurrentFrameUpdated = true; - mResourceReady = false; - } - else if( mEndFrame < mCurrentFrame ) - { - mCurrentFrame = mEndFrame; - - mCurrentFrameUpdated = true; - mResourceReady = false; - } - - DALI_LOG_INFO( gVectorAnimationLogFilter, Debug::Verbose, "VectorRasterizeThread::SetPlayRange: [%d, %d] [%p]\n", mStartFrame, mEndFrame, this ); - } - } -} - -DevelImageVisual::PlayState::Type VectorRasterizeThread::GetPlayState() const -{ - DevelImageVisual::PlayState::Type state = DevelImageVisual::PlayState::STOPPED; - - switch( mPlayState ) - { - case PlayState::PLAYING: - { - state = DevelImageVisual::PlayState::PLAYING; - break; - } - case PlayState::PAUSED: - { - state = DevelImageVisual::PlayState::PAUSED; - break; - } - case PlayState::STOPPING: - case PlayState::STOPPED: - { - state = DevelImageVisual::PlayState::STOPPED; - break; - } } - - return state; } -bool VectorRasterizeThread::IsResourceReady() const -{ - return mResourceReady; -} - -void VectorRasterizeThread::SetCurrentFrameNumber( uint32_t frameNumber ) +void VectorRasterizeThread::Run() { - ConditionalWait::ScopedLock lock( mConditionalWait ); - - if( mCurrentFrame == frameNumber ) - { - DALI_LOG_INFO( gVectorAnimationLogFilter, Debug::Verbose, "VectorRasterizeThread::SetCurrentFrameNumber: Set same frame [%d] [%p]\n", frameNumber, this ); - return; - } - - if( frameNumber >= mStartFrame && frameNumber <= mEndFrame ) - { - mCurrentFrame = frameNumber; - mCurrentFrameUpdated = true; - - mResourceReady = false; + SetThreadName( "VectorRasterizeThread" ); + mLogFactory.InstallLogFunction(); - DALI_LOG_INFO( gVectorAnimationLogFilter, Debug::Verbose, "VectorRasterizeThread::SetCurrentFrameNumber: frame number = %d [%p]\n", mCurrentFrame, this ); - } - else + while( !mDestroyThread ) { - DALI_LOG_ERROR( "Invalid frame number [%d (%d, %d)]\n", frameNumber, mStartFrame, mEndFrame ); + Rasterize(); } } -uint32_t VectorRasterizeThread::GetCurrentFrameNumber() const -{ - return mCurrentFrame; -} - -uint32_t VectorRasterizeThread::GetTotalFrameNumber() const -{ - return mTotalFrame; -} - -void VectorRasterizeThread::GetDefaultSize( uint32_t& width, uint32_t& height ) const -{ - mVectorRenderer.GetDefaultSize( width, height ); -} - -void VectorRasterizeThread::SetStopBehavior( DevelImageVisual::StopBehavior::Type stopBehavior ) -{ - ConditionalWait::ScopedLock lock( mConditionalWait ); - mStopBehavior = stopBehavior; - - DALI_LOG_INFO( gVectorAnimationLogFilter, Debug::Verbose, "VectorRasterizeThread::SetStopBehavior: stop behavor = %d [%p]\n", mStopBehavior, this ); -} - -void VectorRasterizeThread::SetLoopingMode( DevelImageVisual::LoopingMode::Type loopingMode ) -{ - ConditionalWait::ScopedLock lock( mConditionalWait ); - mLoopingMode = loopingMode; - - DALI_LOG_INFO( gVectorAnimationLogFilter, Debug::Verbose, "VectorRasterizeThread::SetLoopingMode: looping mode = %d [%p]\n", mLoopingMode, this ); -} - -VectorRasterizeThread::UploadCompletedSignalType& VectorRasterizeThread::UploadCompletedSignal() -{ - return mVectorRenderer.UploadCompletedSignal(); -} - -void VectorRasterizeThread::Initialize() -{ - mVectorRenderer = VectorAnimationRenderer::New( mUrl ); - - mTotalFrame = mVectorRenderer.GetTotalFrameNumber(); - - mEndFrame = mTotalFrame - 1; - - mFrameRate = mVectorRenderer.GetFrameRate(); - mFrameDurationNanoSeconds = NANOSECONDS_PER_SECOND / mFrameRate; - - uint32_t width, height; - mVectorRenderer.GetDefaultSize( width, height ); - - SetSize( width, height ); - - DALI_LOG_INFO( gVectorAnimationLogFilter, Debug::Verbose, "VectorRasterizeThread::Initialize: file = %s [%d frames, %f fps] [%p]\n", mUrl.c_str(), mTotalFrame, mFrameRate, this ); -} - void VectorRasterizeThread::Rasterize() { - bool stopped = false, needAnimationFinishedTrigger; - uint32_t currentFrame, startFrame, endFrame; - int32_t loopCount, currentLoopCount; - + VectorAnimationTaskPtr nextTask; { + // Lock while popping task out from the queue ConditionalWait::ScopedLock lock( mConditionalWait ); - if( ( mPlayState == PlayState::PAUSED || mPlayState == PlayState::STOPPED ) && !mNeedRender && !mDestroyThread ) + // conditional wait + if( mRasterizeTasks.empty() ) { - DALI_LOG_INFO( gVectorAnimationLogFilter, Debug::Verbose, "VectorRasterizeThread::Rasterize: Wait [%p]\n", this ); mConditionalWait.Wait( lock ); } - if( mPlayState == PlayState::PLAYING && mUpdateFrameNumber ) + // pop out the next task from the queue + if( !mRasterizeTasks.empty() ) { - mCurrentFrame = mForward ? mCurrentFrame + 1 : mCurrentFrame - 1; + std::vector< VectorAnimationTaskPtr >::iterator next = mRasterizeTasks.begin(); + nextTask = *next; + mRasterizeTasks.erase( next ); } - - currentFrame = mCurrentFrame; - startFrame = mStartFrame; - endFrame = mEndFrame; - loopCount = mLoopCount; - currentLoopCount = mCurrentLoop; - needAnimationFinishedTrigger = mNeedAnimationFinishedTrigger; - - mResourceReady = true; - mNeedRender = false; - mCurrentFrameUpdated = false; - mCurrentLoopUpdated = false; - mUpdateFrameNumber = true; - mNeedAnimationFinishedTrigger = true; } - auto currentFrameStartTime = std::chrono::system_clock::now(); - - if( mPlayState == PlayState::STOPPING ) + if( nextTask ) { - currentFrame = GetStoppedFrame( startFrame, endFrame, currentFrame ); - ResetValue( mCurrentFrameUpdated, mCurrentFrame, currentFrame, mConditionalWait ); - - stopped = true; - } - else if( mPlayState == PlayState::PLAYING ) - { - bool animationFinished = false; - - if( currentFrame >= endFrame ) // last frame - { - if( mLoopingMode == DevelImageVisual::LoopingMode::AUTO_REVERSE ) - { - mForward = false; - } - else - { - if( loopCount < 0 || ++currentLoopCount < loopCount ) // repeat forever or before the last loop - { - ResetValue( mCurrentFrameUpdated, mCurrentFrame, startFrame, mConditionalWait ); // If the current frame is changed in the event thread, don't overwrite it. - mUpdateFrameNumber = false; - } - else - { - animationFinished = true; // end of animation - } - ResetValue( mCurrentLoopUpdated, mCurrentLoop, currentLoopCount, mConditionalWait ); - } - } - else if( currentFrame == startFrame && !mForward ) // first frame - { - if( loopCount < 0 || ++currentLoopCount < loopCount ) // repeat forever or before the last loop - { - mForward = true; - } - else - { - animationFinished = true; // end of animation - } - ResetValue( mCurrentLoopUpdated, mCurrentLoop, currentLoopCount, mConditionalWait ); - } + bool keepAnimation = nextTask->Rasterize(); - if( animationFinished ) + if( mCompletedCallback ) { - if( mStopBehavior == DevelImageVisual::StopBehavior::CURRENT_FRAME ) - { - stopped = true; - } - else - { - mPlayState = PlayState::STOPPING; - } + CallbackBase::Execute( *mCompletedCallback, nextTask, keepAnimation ); } } - - // Rasterize - if( !mVectorRenderer.Render( currentFrame ) ) - { - DALI_LOG_INFO( gVectorAnimationLogFilter, Debug::Verbose, "VectorRasterizeThread::Rasterize: Rendering failed. Try again later.[%d] [%p]\n", currentFrame, this ); - mUpdateFrameNumber = false; - } - - if( stopped ) - { - mPlayState = PlayState::STOPPED; - mForward = true; - mCurrentLoop = 0; - - // Animation is finished - if( needAnimationFinishedTrigger ) - { - mAnimationFinishedTrigger->Trigger(); - } - - DALI_LOG_INFO( gVectorAnimationLogFilter, Debug::Verbose, "VectorRasterizeThread::Rasterize: Animation is finished [current = %d] [%p]\n", currentFrame, this ); - } - - auto timeToSleepUntil = currentFrameStartTime + std::chrono::nanoseconds( mFrameDurationNanoSeconds ); - -#if defined(DEBUG_ENABLED) - auto sleepDuration = std::chrono::duration_cast< std::chrono::milliseconds >( timeToSleepUntil - std::chrono::system_clock::now() ); - - DALI_LOG_INFO( gVectorAnimationLogFilter, Debug::Verbose, "VectorRasterizeThread::Rasterize: [current = %d, sleep duration = %lld] [%p]\n", currentFrame, sleepDuration.count(), this ); -#endif - - std::this_thread::sleep_until( timeToSleepUntil ); -} - -uint32_t VectorRasterizeThread::GetStoppedFrame( uint32_t startFrame, uint32_t endFrame, uint32_t currentFrame ) -{ - uint32_t frame = currentFrame; - - switch( mStopBehavior ) - { - case DevelImageVisual::StopBehavior::FIRST_FRAME: - { - frame = startFrame; - break; - } - case DevelImageVisual::StopBehavior::LAST_FRAME: - { - if( mLoopingMode == DevelImageVisual::LoopingMode::AUTO_REVERSE ) - { - frame = startFrame; - } - else - { - frame = endFrame; - } - break; - } - case DevelImageVisual::StopBehavior::CURRENT_FRAME: - { - frame = currentFrame; - break; - } - } - - return frame; } } // namespace Internal diff --git a/dali-toolkit/internal/visuals/animated-vector-image/vector-rasterize-thread.h b/dali-toolkit/internal/visuals/animated-vector-image/vector-rasterize-thread.h index f50a66d..e21809e 100644 --- a/dali-toolkit/internal/visuals/animated-vector-image/vector-rasterize-thread.h +++ b/dali-toolkit/internal/visuals/animated-vector-image/vector-rasterize-thread.h @@ -2,7 +2,7 @@ #define DALI_TOOLKIT_VECTOR_IMAGE_RASTERIZE_THREAD_H /* - * Copyright (c) 2018 Samsung Electronics Co., Ltd. + * Copyright (c) 2019 Samsung Electronics Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,17 +18,16 @@ */ // EXTERNAL INCLUDES -#include -#include +//#include #include -#include #include #include -#include +#include #include +//#include // INTERNAL INCLUDES -#include +#include namespace Dali { @@ -46,14 +45,10 @@ class VectorRasterizeThread : public Thread { public: - using UploadCompletedSignalType = Dali::VectorAnimationRenderer::UploadCompletedSignalType; - /** * @brief Constructor. - * - * @param[in] url The url of the vector animation file */ - VectorRasterizeThread( const std::string& url ); + VectorRasterizeThread(); /** * @brief Destructor. @@ -61,114 +56,17 @@ public: virtual ~VectorRasterizeThread(); /** - * @brief Sets the renderer used to display the result image. - * - * @param[in] renderer The renderer used to display the result image + * The callback is called from the rasterize thread after the rasterization is completed. + * @param[in] callBack The function to call. */ - void SetRenderer( Renderer renderer ); + void SetCompletedCallback( CallbackBase* callback ); /** - * @brief Sets the target image size. + * Add a task to rasterize. * - * @param[in] width The target image width - * @param[in] height The target image height - */ - void SetSize( uint32_t width, uint32_t height ); - - /** - * @brief Play the vector animation. - */ - void PlayAnimation(); - - /** - * @brief Stop the vector animation. - */ - void StopAnimation(); - - /** - * @brief Pause the vector animation. - */ - void PauseAnimation(); - - /** - * @brief Render one frame. The current frame number will be increased. + * @param[in] task The task to rasterize */ - void RenderFrame(); - - /** - * @brief This callback is called after the animation is finished. - * @param[in] callback The animation finished callback - */ - void SetAnimationFinishedCallback( EventThreadCallback* callback ); - - /** - * @brief Enable looping for 'count' repeats. -1 means to repeat forever. - * @param[in] count The number of times to loop - */ - void SetLoopCount( int32_t count ); - - /** - * @brief Set the playing range in frame number. - * @param[in] startFrame The frame number to specify minimum progress. - * @param[in] endFrame The frame number to specify maximum progress. - * The animation will play between those values. - */ - void SetPlayRange( uint32_t startFrame, uint32_t endFrame ); - - /** - * @brief Get the play state - * @return The play state - */ - DevelImageVisual::PlayState::Type GetPlayState() const; - - /** - * @brief Queries whether the resource is ready. - * @return true if ready, false otherwise - */ - bool IsResourceReady() const; - - /** - * @brief Sets the current frame number of the animation. - * @param[in] frameNumber The new frame number between [0, the maximum frame number] or between the play range if specified. - */ - void SetCurrentFrameNumber( uint32_t frameNumber ); - - /** - * @brief Retrieves the current frame number of the animation. - * @return The current frame number - */ - uint32_t GetCurrentFrameNumber() const; - - /** - * @brief Retrieves the total frame number of the animation. - * @return The total frame number - */ - uint32_t GetTotalFrameNumber() const; - - /** - * @brief Gets the default size of the file,. - * @return The default size of the file - */ - void GetDefaultSize( uint32_t& width, uint32_t& height ) const; - - /** - * @brief Sets the stop behavior of the animation. This is performed when the animation is stopped. - * @param[in] stopBehavior The stop behavior - */ - void SetStopBehavior( DevelImageVisual::StopBehavior::Type stopBehavior ); - - /** - * @brief Sets the looping mode. - * Animation plays forwards and then restarts from the beginning or runs backwards again. - * @param[in] loopingMode The looping mode - */ - void SetLoopingMode( DevelImageVisual::LoopingMode::Type loopingMode ); - - /** - * @brief Connect to this signal to be notified when the texture upload is completed. - * @return The signal to connect to. - */ - UploadCompletedSignalType& UploadCompletedSignal(); + void AddTask( VectorAnimationTaskPtr task ); protected: @@ -181,19 +79,11 @@ protected: private: /** - * @brief Initializes the vector renderer. - */ - void Initialize(); - - /** - * @brief Rasterizes the current frame. + * Rasterizes the tasks. */ void Rasterize(); - /** - * @brief Gets the frame number when the animation is stopped according to the stop behavior. - */ - uint32_t GetStoppedFrame( uint32_t startFrame, uint32_t endFrame, uint32_t currentFrame ); +private: // Undefined VectorRasterizeThread( const VectorRasterizeThread& thread ) = delete; @@ -203,41 +93,12 @@ private: private: - enum class PlayState - { - STOPPING, ///< The animation is stopping - STOPPED, ///< The animation has stopped - PLAYING, ///< The animation is playing - PAUSED ///< The animation is paused - }; - - std::string mUrl; - VectorAnimationRenderer mVectorRenderer; - ConditionalWait mConditionalWait; - std::unique_ptr< EventThreadCallback > mAnimationFinishedTrigger; - Vector2 mPlayRange; - PlayState mPlayState; - DevelImageVisual::StopBehavior::Type mStopBehavior; - DevelImageVisual::LoopingMode::Type mLoopingMode; - int64_t mFrameDurationNanoSeconds; - float mFrameRate; - uint32_t mCurrentFrame; - uint32_t mTotalFrame; - uint32_t mStartFrame; - uint32_t mEndFrame; - uint32_t mWidth; - uint32_t mHeight; - int32_t mLoopCount; - int32_t mCurrentLoop; - bool mNeedRender; - bool mDestroyThread; ///< Whether the thread be destroyed - bool mResourceReady; - bool mCurrentFrameUpdated; - bool mCurrentLoopUpdated; - bool mForward; - bool mUpdateFrameNumber; - bool mNeedAnimationFinishedTrigger; - const Dali::LogFactoryInterface& mLogFactory; ///< The log factory + std::vector< VectorAnimationTaskPtr > mRasterizeTasks; + ConditionalWait mConditionalWait; + std::unique_ptr< CallbackBase > mCompletedCallback; + bool mDestroyThread; ///< Whether the thread be destroyed + bool mIsThreadStarted; + const Dali::LogFactoryInterface& mLogFactory; ///< The log factory }; diff --git a/dali-toolkit/internal/visuals/visual-factory-cache.cpp b/dali-toolkit/internal/visuals/visual-factory-cache.cpp index 101e696..3852361 100644 --- a/dali-toolkit/internal/visuals/visual-factory-cache.cpp +++ b/dali-toolkit/internal/visuals/visual-factory-cache.cpp @@ -43,6 +43,7 @@ namespace Internal VisualFactoryCache::VisualFactoryCache( bool preMultiplyOnLoad ) : mSvgRasterizeThread( NULL ), + mVectorAnimationThread(), mBrokenImageUrl(""), mPreMultiplyOnLoad( preMultiplyOnLoad ) { @@ -135,6 +136,16 @@ SvgRasterizeThread* VisualFactoryCache::GetSVGRasterizationThread() return mSvgRasterizeThread; } +VectorAnimationThread& VisualFactoryCache::GetVectorAnimationThread() +{ + if( !mVectorAnimationThread ) + { + mVectorAnimationThread = std::unique_ptr< VectorAnimationThread >( new VectorAnimationThread() ); + mVectorAnimationThread->Start(); + } + return *mVectorAnimationThread; +} + void VisualFactoryCache::ApplyRasterizedSVGToSampler() { while( RasterizingTaskPtr task = mSvgRasterizeThread->NextCompletedTask() ) diff --git a/dali-toolkit/internal/visuals/visual-factory-cache.h b/dali-toolkit/internal/visuals/visual-factory-cache.h index f2c8120..e080d52 100644 --- a/dali-toolkit/internal/visuals/visual-factory-cache.h +++ b/dali-toolkit/internal/visuals/visual-factory-cache.h @@ -28,6 +28,7 @@ #include #include #include +#include namespace Dali { @@ -203,6 +204,12 @@ public: */ SvgRasterizeThread* GetSVGRasterizationThread(); + /** + * Get the vector animation thread. + * @return A raw pointer pointing to the vector animation thread. + */ + VectorAnimationThread& GetVectorAnimationThread(); + private: // for svg rasterization thread /** @@ -226,12 +233,13 @@ private: Geometry mGeometry[GEOMETRY_TYPE_MAX+1]; Shader mShader[SHADER_TYPE_MAX+1]; - ImageAtlasManagerPtr mAtlasManager; - TextureManager mTextureManager; - NPatchLoader mNPatchLoader; - SvgRasterizeThread* mSvgRasterizeThread; - std::string mBrokenImageUrl; - bool mPreMultiplyOnLoad; + ImageAtlasManagerPtr mAtlasManager; + TextureManager mTextureManager; + NPatchLoader mNPatchLoader; + SvgRasterizeThread* mSvgRasterizeThread; + std::unique_ptr< VectorAnimationThread > mVectorAnimationThread; + std::string mBrokenImageUrl; + bool mPreMultiplyOnLoad; }; } // namespace Internal