From: Heeyong Song Date: Thu, 15 Apr 2021 08:49:31 +0000 (+0900) Subject: (Vector) Drop frames when the animation is delayed X-Git-Tag: dali_2.0.24~8^2 X-Git-Url: http://review.tizen.org/git/?p=platform%2Fcore%2Fuifw%2Fdali-toolkit.git;a=commitdiff_plain;h=0c8875977f6f8e8a026eacb7a9ef57d14ce8deac (Vector) Drop frames when the animation is delayed Change-Id: I644c34fa6b1bd008f830fe7c9a3af9860ab211f6 --- 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 18ce2ef..33a27b5 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 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019 Samsung Electronics Co., Ltd. + * Copyright (c) 2021 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. @@ -21,6 +21,8 @@ #include #include #include +#include +#include namespace Dali { @@ -31,6 +33,11 @@ namespace Internal namespace Adaptor { +namespace +{ +Dali::Internal::Adaptor::VectorAnimationRenderer* gVectorAnimationRenderer = nullptr; +} + class VectorAnimationRenderer: public Dali::BaseObject { public: @@ -40,8 +47,12 @@ public: mRenderer(), mWidth( 0 ), mHeight( 0 ), + mTotalFrameNumber(VECTOR_ANIMATION_TOTAL_FRAME_NUMBER), mPreviousFrame( 0 ), + mDelayTime(0), + mDroppedFrames(0), mFrameRate( 60.0f ), + mNeedDroppedFrames(false), mEventThreadCallback( new EventThreadCallback( MakeCallback( this, &VectorAnimationRenderer::OnTriggered ) ) ) { mCount++; @@ -64,6 +75,11 @@ public: { return false; } + else if(mUrl == "framedrop.json") + { + // Change total frame number for test + mTotalFrameNumber = 200; + } return true; } @@ -96,6 +112,19 @@ public: bool Render( uint32_t frameNumber ) { + if(mDelayTime != 0) + { + std::this_thread::sleep_for(std::chrono::milliseconds(static_cast(mDelayTime))); + mDelayTime = 0; + mNeedDroppedFrames = true; + } + else if(mNeedDroppedFrames) + { + mDroppedFrames = (frameNumber > mPreviousFrame) ? frameNumber - mPreviousFrame - 1: frameNumber + (mTotalFrameNumber - mPreviousFrame) - 1; + mNeedTrigger = true; + mNeedDroppedFrames = false; + } + if( mNeedTrigger ) { mEventThreadCallback->Trigger(); @@ -114,7 +143,7 @@ public: uint32_t GetTotalFrameNumber() const { - return VECTOR_ANIMATION_TOTAL_FRAME_NUMBER; + return mTotalFrameNumber; } float GetFrameRate() const @@ -165,8 +194,12 @@ public: Dali::Renderer mRenderer; uint32_t mWidth; uint32_t mHeight; + uint32_t mTotalFrameNumber; uint32_t mPreviousFrame; + uint32_t mDelayTime; + uint32_t mDroppedFrames; float mFrameRate; + bool mNeedDroppedFrames; Dali::VectorAnimationRenderer::UploadCompletedSignalType mUploadCompletedSignal; std::unique_ptr< EventThreadCallback > mEventThreadCallback; }; @@ -201,6 +234,8 @@ VectorAnimationRenderer VectorAnimationRenderer::New() { Internal::Adaptor::VectorAnimationRenderer* animationRenderer = new Internal::Adaptor::VectorAnimationRenderer(); + Internal::Adaptor::gVectorAnimationRenderer = animationRenderer; + return VectorAnimationRenderer( animationRenderer ); } @@ -293,6 +328,16 @@ void RequestTrigger() Dali::Internal::Adaptor::VectorAnimationRenderer::mNeedTrigger = true; } +void DelayRendering(uint32_t delay) +{ + Dali::Internal::Adaptor::gVectorAnimationRenderer->mDelayTime = delay; +} + +uint32_t GetDroppedFrames() +{ + return Dali::Internal::Adaptor::gVectorAnimationRenderer->mDroppedFrames; +} + } // VectorAnimationRenderer } // Test diff --git a/automated-tests/src/dali-toolkit/dali-toolkit-test-utils/toolkit-vector-animation-renderer.h b/automated-tests/src/dali-toolkit/dali-toolkit-test-utils/toolkit-vector-animation-renderer.h index 2213465..a1ea51b 100755 --- a/automated-tests/src/dali-toolkit/dali-toolkit-test-utils/toolkit-vector-animation-renderer.h +++ b/automated-tests/src/dali-toolkit/dali-toolkit-test-utils/toolkit-vector-animation-renderer.h @@ -2,7 +2,7 @@ #define DALI_TOOLKIT_TEST_VECTOR_ANIMATION_RENDERER_H /* - * Copyright (c) 2019 Samsung Electronics Co., Ltd. + * Copyright (c) 2021 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. @@ -32,6 +32,8 @@ namespace VectorAnimationRenderer #define VECTOR_ANIMATION_MARKER_END_FRAME_2 3 void RequestTrigger(); +void DelayRendering(uint32_t delay); +uint32_t GetDroppedFrames(); } // VectorAnimationRenderer } // Test diff --git a/automated-tests/src/dali-toolkit/utc-Dali-AnimatedVectorImageVisual.cpp b/automated-tests/src/dali-toolkit/utc-Dali-AnimatedVectorImageVisual.cpp index dfd0b72..edd3a86 100644 --- a/automated-tests/src/dali-toolkit/utc-Dali-AnimatedVectorImageVisual.cpp +++ b/automated-tests/src/dali-toolkit/utc-Dali-AnimatedVectorImageVisual.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Samsung Electronics Co., Ltd. + * Copyright (c) 2021 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. @@ -50,6 +50,7 @@ namespace { const char* TEST_VECTOR_IMAGE_FILE_NAME = TEST_RESOURCE_DIR "/insta_camera.json"; +const char* TEST_VECTOR_IMAGE_FILE_NAME_FRAME_DROP = "framedrop.json"; const char* TEST_VECTOR_IMAGE_INVALID_FILE_NAME = "invalid.json"; bool gAnimationFinishedSignalFired = false; @@ -1530,3 +1531,48 @@ int UtcDaliAnimatedVectorImageVisualInvalidFile(void) END_TEST; } + +int UtcDaliAnimatedVectorImageVisualFrameDrops(void) +{ + ToolkitTestApplication application; + tet_infoline("UtcDaliAnimatedVectorImageVisualFrameDrops"); + + Property::Map propertyMap; + propertyMap.Add(Toolkit::Visual::Property::TYPE, DevelVisual::ANIMATED_VECTOR_IMAGE) + .Add(ImageVisual::Property::URL, TEST_VECTOR_IMAGE_FILE_NAME_FRAME_DROP); + + Visual::Base visual = VisualFactory::Get().CreateVisual(propertyMap); + DALI_TEST_CHECK(visual); + + DummyControl actor = DummyControl::New(true); + DummyControlImpl& dummyImpl = static_cast(actor.GetImplementation()); + dummyImpl.RegisterVisual(DummyControl::Property::TEST_VISUAL, visual); + + Vector2 controlSize(20.f, 30.f); + actor.SetProperty(Actor::Property::SIZE, controlSize); + + application.GetScene().Add(actor); + + application.SendNotification(); + application.Render(); + + Property::Map attributes; + DevelControl::DoAction(actor, DummyControl::Property::TEST_VISUAL, Dali::Toolkit::DevelAnimatedVectorImageVisual::Action::PLAY, attributes); + + application.SendNotification(); + application.Render(); + + // Trigger count is 1 - render the first frame + DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION); + + // Make delay to drop frames + Test::VectorAnimationRenderer::DelayRendering(170); // longer than 16.6 * 10frames + + // Check dropped frame + DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION); + + uint32_t frames = Test::VectorAnimationRenderer::GetDroppedFrames(); + DALI_TEST_CHECK(frames >= 9); + + END_TEST; +} 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 index d9d6d5a..a1659bf 100644 --- a/dali-toolkit/internal/visuals/animated-vector-image/vector-animation-task.cpp +++ b/dali-toolkit/internal/visuals/animated-vector-image/vector-animation-task.cpp @@ -37,7 +37,7 @@ namespace Internal namespace { constexpr auto LOOP_FOREVER = -1; -constexpr auto NANOSECONDS_PER_SECOND(1e+9); +constexpr auto MICROSECONDS_PER_SECOND(1e+6); #if defined(DEBUG_ENABLED) Debug::Filter* gVectorAnimationLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_VECTOR_ANIMATION"); @@ -56,12 +56,13 @@ VectorAnimationTask::VectorAnimationTask(VisualFactoryCache& factoryCache) mStopBehavior(DevelImageVisual::StopBehavior::CURRENT_FRAME), mLoopingMode(DevelImageVisual::LoopingMode::RESTART), mNextFrameStartTime(), - mFrameDurationNanoSeconds(0), + mFrameDurationMicroSeconds(MICROSECONDS_PER_SECOND / 60.0f), mFrameRate(60.0f), mCurrentFrame(0), mTotalFrame(0), mStartFrame(0), mEndFrame(0), + mDroppedFrames(0), mWidth(0), mHeight(0), mAnimationDataIndex(0), @@ -109,8 +110,8 @@ bool VectorAnimationTask::Load(const std::string& url) mEndFrame = mTotalFrame - 1; - mFrameRate = mVectorRenderer.GetFrameRate(); - mFrameDurationNanoSeconds = NANOSECONDS_PER_SECOND / mFrameRate; + mFrameRate = mVectorRenderer.GetFrameRate(); + mFrameDurationMicroSeconds = MICROSECONDS_PER_SECOND / mFrameRate; uint32_t width, height; mVectorRenderer.GetDefaultSize(width, height); @@ -372,7 +373,7 @@ VectorAnimationTask::UploadCompletedSignalType& VectorAnimationTask::UploadCompl bool VectorAnimationTask::Rasterize() { bool stopped = false; - uint32_t currentFrame; + uint32_t currentFrame, droppedFrames = 0; { ConditionalWait::ScopedLock lock(mConditionalWait); @@ -381,13 +382,14 @@ bool VectorAnimationTask::Rasterize() // The task will be destroyed. We don't need rasterization. return false; } + droppedFrames = mDroppedFrames; } ApplyAnimationData(); if(mPlayState == PlayState::PLAYING && mUpdateFrameNumber) { - mCurrentFrame = mForward ? mCurrentFrame + 1 : mCurrentFrame - 1; + mCurrentFrame = mForward ? mCurrentFrame + droppedFrames + 1 : mCurrentFrame - droppedFrames - 1; Dali::ClampInPlace(mCurrentFrame, mStartFrame, mEndFrame); } @@ -521,22 +523,39 @@ uint32_t VectorAnimationTask::GetStoppedFrame(uint32_t startFrame, uint32_t endF return frame; } -std::chrono::time_point VectorAnimationTask::CalculateNextFrameTime(bool renderNow) +VectorAnimationTask::TimePoint VectorAnimationTask::CalculateNextFrameTime(bool renderNow) { + uint32_t droppedFrames = 0; + // std::chrono::time_point template has second parameter duration which defaults to the std::chrono::system_clock supported // duration. In some C++11 implementations it is a milliseconds duration, so it fails to compile unless mNextFrameStartTime // is casted to use the default duration. - mNextFrameStartTime = std::chrono::time_point_cast::duration>( - mNextFrameStartTime + std::chrono::nanoseconds(mFrameDurationNanoSeconds)); - auto current = std::chrono::system_clock::now(); - if(renderNow || mNextFrameStartTime < current) + mNextFrameStartTime = std::chrono::time_point_cast(mNextFrameStartTime + std::chrono::microseconds(mFrameDurationMicroSeconds)); + auto current = std::chrono::system_clock::now(); + if(renderNow) + { + mNextFrameStartTime = current; + } + else if(mNextFrameStartTime < current) { + while(current > std::chrono::time_point_cast(mNextFrameStartTime + std::chrono::microseconds(mFrameDurationMicroSeconds))) + { + droppedFrames++; + mNextFrameStartTime = std::chrono::time_point_cast(mNextFrameStartTime + std::chrono::microseconds(mFrameDurationMicroSeconds)); + } + + { + ConditionalWait::ScopedLock lock(mConditionalWait); + mDroppedFrames = droppedFrames; + } + mNextFrameStartTime = current; } + return mNextFrameStartTime; } -std::chrono::time_point VectorAnimationTask::GetNextFrameTime() +VectorAnimationTask::TimePoint VectorAnimationTask::GetNextFrameTime() { return mNextFrameStartTime; } 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 index 03b8a89..c14cc10 100644 --- a/dali-toolkit/internal/visuals/animated-vector-image/vector-animation-task.h +++ b/dali-toolkit/internal/visuals/animated-vector-image/vector-animation-task.h @@ -47,6 +47,8 @@ class VectorAnimationTask : public RefObject public: using UploadCompletedSignalType = Dali::VectorAnimationRenderer::UploadCompletedSignalType; + using TimePoint = std::chrono::time_point; + /** * Flags for re-sending data to the vector animation thread */ @@ -195,13 +197,13 @@ public: * @brief Calculates the time for the next frame rasterization. * @return The time for the next frame rasterization. */ - std::chrono::time_point CalculateNextFrameTime(bool renderNow); + TimePoint CalculateNextFrameTime(bool renderNow); /** * @brief Gets the time for the next frame rasterization. * @return The time for the next frame rasterization. */ - std::chrono::time_point GetNextFrameTime(); + TimePoint GetNextFrameTime(); private: /** @@ -284,32 +286,33 @@ private: PAUSED ///< The animation is paused }; - std::string mUrl; - VectorAnimationRenderer mVectorRenderer; - AnimationData mAnimationData[2]; - VectorAnimationThread& mVectorAnimationThread; - ConditionalWait mConditionalWait; - std::unique_ptr mAnimationFinishedTrigger; - PlayState mPlayState; - DevelImageVisual::StopBehavior::Type mStopBehavior; - DevelImageVisual::LoopingMode::Type mLoopingMode; - std::chrono::time_point mNextFrameStartTime; - int64_t mFrameDurationNanoSeconds; - float mFrameRate; - uint32_t mCurrentFrame; - uint32_t mTotalFrame; - uint32_t mStartFrame; - uint32_t mEndFrame; - uint32_t mWidth; - uint32_t mHeight; - uint32_t mAnimationDataIndex; - int32_t mLoopCount; - int32_t mCurrentLoop; - bool mForward; - bool mUpdateFrameNumber; - bool mNeedAnimationFinishedTrigger; - bool mAnimationDataUpdated; - bool mDestroyTask; + std::string mUrl; + VectorAnimationRenderer mVectorRenderer; + AnimationData mAnimationData[2]; + VectorAnimationThread& mVectorAnimationThread; + ConditionalWait mConditionalWait; + std::unique_ptr mAnimationFinishedTrigger; + PlayState mPlayState; + DevelImageVisual::StopBehavior::Type mStopBehavior; + DevelImageVisual::LoopingMode::Type mLoopingMode; + TimePoint mNextFrameStartTime; + int64_t mFrameDurationMicroSeconds; + float mFrameRate; + uint32_t mCurrentFrame; + uint32_t mTotalFrame; + uint32_t mStartFrame; + uint32_t mEndFrame; + uint32_t mDroppedFrames; + uint32_t mWidth; + uint32_t mHeight; + uint32_t mAnimationDataIndex; + int32_t mLoopCount; + int32_t mCurrentLoop; + bool mForward; + bool mUpdateFrameNumber; + bool mNeedAnimationFinishedTrigger; + bool mAnimationDataUpdated; + bool mDestroyTask; }; } // namespace Internal