From d6421139d559d6cc63ea4843eae8fe688df896f6 Mon Sep 17 00:00:00 2001 From: "Eunki, Hong" Date: Fri, 5 Apr 2024 22:28:08 +0900 Subject: [PATCH] (Vector) Let we send finished signal only for matched sent play/stop from visual It is possible that AnimationFinished trigger comes "after" event thread Play action comes. If then, user's Play action be skipped. To avoid this case, let we give the id when play state changed to Play / Pause / Stop. And send finished signal only if the id is matched what visual know. Change-Id: I03409e65d2f6b947940ea5591959b106ff8de2bd Signed-off-by: Eunki, Hong --- .../animated-vector-image-visual.cpp | 24 +++++++++++---- .../animated-vector-image-visual.h | 6 +++- .../vector-animation-task.cpp | 12 ++++---- .../animated-vector-image/vector-animation-task.h | 8 +++-- .../vector-animation-thread.cpp | 25 +++++++++------- .../vector-animation-thread.h | 35 +++++++++++----------- 6 files changed, 69 insertions(+), 41 deletions(-) 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 f078dc8..9c81c3b 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 @@ -97,6 +97,7 @@ AnimatedVectorImageVisual::AnimatedVectorImageVisual(VisualFactoryCache& factory mPlacementActor(), mPlayState(DevelImageVisual::PlayState::STOPPED), mEventCallback(nullptr), + mLastSentPlayStateId(0u), mLoadFailed(false), mRendererAdded(false), mCoreShutdown(false), @@ -565,11 +566,9 @@ void AnimatedVectorImageVisual::OnDoAction(const Property::Index actionId, const { if(IsOnScene() && mVisualSize != Vector2::ZERO) { - if(mAnimationData.playState != DevelImageVisual::PlayState::PLAYING) - { - mAnimationData.playState = DevelImageVisual::PlayState::PLAYING; - mAnimationData.resendFlag |= VectorAnimationTask::RESEND_PLAY_STATE; - } + // Always resend Playing state. If task is already playing, it will be ignored at Rasterize time. + mAnimationData.playState = DevelImageVisual::PlayState::PLAYING; + mAnimationData.resendFlag |= VectorAnimationTask::RESEND_PLAY_STATE; } mPlayState = DevelImageVisual::PlayState::PLAYING; break; @@ -695,8 +694,14 @@ void AnimatedVectorImageVisual::OnResourceReady(VectorAnimationTask::ResourceSta DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "status = %d [%p]\n", status, this); } -void AnimatedVectorImageVisual::OnAnimationFinished() +void AnimatedVectorImageVisual::OnAnimationFinished(uint32_t playStateId) { + // Only send event when animation is finished by the last Play/Pause/Stop request. + if(mLastSentPlayStateId != playStateId) + { + return; + } + AnimatedVectorImageVisualPtr self = this; // Keep reference until this API finished DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "AnimatedVectorImageVisual::OnAnimationFinished: action state = %d [%p]\n", mPlayState, this); @@ -723,6 +728,13 @@ void AnimatedVectorImageVisual::SendAnimationData() { if(mAnimationData.resendFlag) { + if(mAnimationData.resendFlag & VectorAnimationTask::RESEND_PLAY_STATE) + { + // Keep last sent playId. It will be used when we try to emit AnimationFinished signal. + // The OnAnimationFinished signal what before Play/Pause/Stop action send could be come after action sent. + // To ensure the OnAnimationFinished signal comes belong to what we sent, we need to keep last sent playId. + mAnimationData.playStateId = ++mLastSentPlayStateId; + } mVectorAnimationTask->SetAnimationData(mAnimationData); if(mImpl->mRenderer) 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 493e621..fc7d7a4 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 @@ -182,8 +182,10 @@ private: /** * @brief Event callback from rasterize thread. This is called after the animation is finished. + * + * @param[in] playStateId The play state id */ - void OnAnimationFinished(); + void OnAnimationFinished(uint32_t playStateId); /** * @brief Send animation data to the rasterize thread. @@ -250,6 +252,8 @@ private: DevelImageVisual::PlayState::Type mPlayState; CallbackBase* mEventCallback; // Not owned + uint32_t mLastSentPlayStateId; + bool mLoadFailed : 1; bool mRendererAdded : 1; bool mCoreShutdown : 1; 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 cb67bc7..4f4e97d 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 @@ -92,6 +92,7 @@ VectorAnimationTask::VectorAnimationTask(VisualFactoryCache& factoryCache) mWidth(0), mHeight(0), mAnimationDataIndex(0), + mAppliedPlayStateId(0u), mLoopCount(LOOP_FOREVER), mCurrentLoop(0), mForward(true), @@ -214,7 +215,7 @@ bool VectorAnimationTask::Load(bool synchronousLoading) Mutex::ScopedLock lock(mMutex); if(!synchronousLoading && mLoadCompletedCallback) { - mVectorAnimationThread.AddEventTriggerCallback(mLoadCompletedCallback.get()); + mVectorAnimationThread.AddEventTriggerCallback(mLoadCompletedCallback.get(), 0u); } } #ifdef TRACE_ENABLED @@ -245,7 +246,7 @@ bool VectorAnimationTask::Load(bool synchronousLoading) Mutex::ScopedLock lock(mMutex); if(!synchronousLoading && mLoadCompletedCallback) { - mVectorAnimationThread.AddEventTriggerCallback(mLoadCompletedCallback.get()); + mVectorAnimationThread.AddEventTriggerCallback(mLoadCompletedCallback.get(), 0u); } } @@ -290,7 +291,7 @@ void VectorAnimationTask::RequestLoad(const VisualUrl& url, EncodedImageBuffer e { Load(true); - OnLoadCompleted(); + OnLoadCompleted(0u); } } @@ -703,7 +704,7 @@ bool VectorAnimationTask::Rasterize() Mutex::ScopedLock lock(mMutex); if(mNeedAnimationFinishedTrigger && mAnimationFinishedCallback) { - mVectorAnimationThread.AddEventTriggerCallback(mAnimationFinishedCallback.get()); + mVectorAnimationThread.AddEventTriggerCallback(mAnimationFinishedCallback.get(), mAppliedPlayStateId); } } @@ -859,6 +860,7 @@ void VectorAnimationTask::ApplyAnimationData() if(animationData.resendFlag & VectorAnimationTask::RESEND_PLAY_STATE) { + mAppliedPlayStateId = animationData.playStateId; if(animationData.playState == DevelImageVisual::PlayState::PLAYING) { PlayAnimation(); @@ -883,7 +885,7 @@ void VectorAnimationTask::OnUploadCompleted() mResourceReadySignal.Emit(ResourceStatus::READY); } -void VectorAnimationTask::OnLoadCompleted() +void VectorAnimationTask::OnLoadCompleted(uint32_t /* not used */) { if(!mLoadFailed) { 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 ef89500..eb48a13 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 @@ -93,7 +93,8 @@ public: currentFrame(0), width(0), height(0), - loopCount(-1) + loopCount(-1), + playStateId(0) { } @@ -108,6 +109,7 @@ public: width = rhs.width; height = rhs.height; loopCount = rhs.loopCount; + playStateId = rhs.playStateId; dynamicProperties.insert(dynamicProperties.end(), rhs.dynamicProperties.begin(), rhs.dynamicProperties.end()); return *this; } @@ -122,6 +124,7 @@ public: uint32_t width; uint32_t height; int32_t loopCount; + uint32_t playStateId; }; /** @@ -365,7 +368,7 @@ private: /** * @brief Event callback from rasterize thread. This is called when the file loading is completed. */ - void OnLoadCompleted(); + void OnLoadCompleted(uint32_t argument); // Undefined VectorAnimationTask(const VectorAnimationTask& task) = delete; @@ -407,6 +410,7 @@ private: uint32_t mWidth; uint32_t mHeight; uint32_t mAnimationDataIndex; + uint32_t mAppliedPlayStateId; int32_t mLoopCount; int32_t mCurrentLoop; bool mForward : 1; 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 index 3ee2d5d..b07ba5b 100644 --- a/dali-toolkit/internal/visuals/animated-vector-image/vector-animation-thread.cpp +++ b/dali-toolkit/internal/visuals/animated-vector-image/vector-animation-thread.cpp @@ -159,12 +159,12 @@ void VectorAnimationThread::OnAwakeFromSleep() } } -void VectorAnimationThread::AddEventTriggerCallback(CallbackBase* callback) +void VectorAnimationThread::AddEventTriggerCallback(CallbackBase* callback, uint32_t argument) { Mutex::ScopedLock lock(mEventTriggerMutex); if(!mDestroyThread) { - mTriggerEventCallbacks.push_back(callback); + mTriggerEventCallbacks.emplace_back(callback, argument); if(!mEventTriggered) { @@ -179,7 +179,7 @@ void VectorAnimationThread::RemoveEventTriggerCallbacks(CallbackBase* callback) Mutex::ScopedLock lock(mEventTriggerMutex); if(!mDestroyThread) { - auto iter = std::remove(mTriggerEventCallbacks.begin(), mTriggerEventCallbacks.end(), callback); + auto iter = std::remove_if(mTriggerEventCallbacks.begin(), mTriggerEventCallbacks.end(), [&callback](std::pair& item) { return item.first == callback; }); mTriggerEventCallbacks.erase(iter, mTriggerEventCallbacks.end()); } } @@ -276,27 +276,32 @@ void VectorAnimationThread::Rasterize() void VectorAnimationThread::OnEventCallbackTriggered() { - while(CallbackBase* callback = GetNextEventCallback()) + while(true) { - CallbackBase::Execute(*callback); + auto callbackPair = GetNextEventCallback(); + if(callbackPair.first == nullptr) + { + break; + } + CallbackBase::Execute(*callbackPair.first, callbackPair.second); } } -CallbackBase* VectorAnimationThread::GetNextEventCallback() +std::pair VectorAnimationThread::GetNextEventCallback() { Mutex::ScopedLock lock(mEventTriggerMutex); if(!mDestroyThread) { if(!mTriggerEventCallbacks.empty()) { - auto iter = mTriggerEventCallbacks.begin(); - CallbackBase* callback = *iter; + auto iter = mTriggerEventCallbacks.begin(); + auto callbackIdPair = *iter; mTriggerEventCallbacks.erase(iter); - return callback; + return callbackIdPair; } mEventTriggered = false; } - return nullptr; + return std::make_pair(nullptr, 0u); } VectorAnimationThread::SleepThread::SleepThread(CallbackBase* callback) 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 index 7421cc8..b1f608a 100644 --- a/dali-toolkit/internal/visuals/animated-vector-image/vector-animation-thread.h +++ b/dali-toolkit/internal/visuals/animated-vector-image/vector-animation-thread.h @@ -2,7 +2,7 @@ #define DALI_TOOLKIT_VECTOR_ANIMATION_THREAD_H /* - * Copyright (c) 2023 Samsung Electronics Co., Ltd. + * Copyright (c) 2024 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. @@ -77,10 +77,11 @@ public: * @brief Add an event trigger callback. * * @param callback The callback to add + * @param argument The argument to pass to the callback * @note Ownership of the callback is NOT passed onto this class. * @note The callback will be excuted in the main thread. */ - void AddEventTriggerCallback(CallbackBase* callback); + void AddEventTriggerCallback(CallbackBase* callback, uint32_t argument); /** * @brief Remove event trigger callbacks what we added before. @@ -109,7 +110,7 @@ private: /** * @brief Gets next event callback to process. */ - CallbackBase* GetNextEventCallback(); + std::pair GetNextEventCallback(); /** * @brief The thread to sleep until the next frame time. @@ -160,20 +161,20 @@ private: VectorAnimationThread& operator=(const VectorAnimationThread& thread) = delete; private: - std::vector mAnimationTasks; - std::vector mCompletedTasks; - std::vector mWorkingTasks; - std::vector mTriggerEventCallbacks{}; // Callbacks are not owned - SleepThread mSleepThread; - ConditionalWait mConditionalWait; - Mutex mEventTriggerMutex; - std::unique_ptr mEventTrigger{}; - bool mNeedToSleep; - bool mDestroyThread; - bool mEventTriggered{false}; - const Dali::LogFactoryInterface& mLogFactory; - const Dali::TraceFactoryInterface& mTraceFactory; - Dali::AsyncTaskManager mAsyncTaskManager; + std::vector mAnimationTasks; + std::vector mCompletedTasks; + std::vector mWorkingTasks; + std::vector> mTriggerEventCallbacks{}; // Callbacks are not owned + SleepThread mSleepThread; + ConditionalWait mConditionalWait; + Mutex mEventTriggerMutex; + std::unique_ptr mEventTrigger{}; + bool mNeedToSleep; + bool mDestroyThread; + bool mEventTriggered{false}; + const Dali::LogFactoryInterface& mLogFactory; + const Dali::TraceFactoryInterface& mTraceFactory; + Dali::AsyncTaskManager mAsyncTaskManager; }; } // namespace Internal -- 2.7.4