X-Git-Url: http://review.tizen.org/git/?p=platform%2Fcore%2Fuifw%2Fdali-toolkit.git;a=blobdiff_plain;f=dali-toolkit%2Finternal%2Fvisuals%2Fanimated-vector-image%2Fvector-animation-task.cpp;h=01b6898403615ea15e9025dead7fee85c5f9e728;hp=c19a4407ad15e4baf76204763e6e5c42e41b9059;hb=HEAD;hpb=7ccf196270389e61d539d4600ccfbae1ca047224 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 c19a440..ac448c0 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 @@ -1,5 +1,5 @@ /* - * 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. @@ -19,6 +19,7 @@ #include // EXTERNAL INCLUDES +#include #include #include #include @@ -30,7 +31,10 @@ #include #ifdef TRACE_ENABLED +#include +#include #include +#include #endif namespace Dali @@ -50,17 +54,30 @@ Debug::Filter* gVectorAnimationLogFilter = Debug::Filter::New(Debug::NoLogging, DALI_INIT_TRACE_FILTER(gTraceFilter, DALI_TRACE_IMAGE_PERFORMANCE_MARKER, false); +#ifdef TRACE_ENABLED +uint64_t GetNanoseconds() +{ + // Get the time of a monotonic clock since its epoch. + auto epoch = std::chrono::steady_clock::now().time_since_epoch(); + auto duration = std::chrono::duration_cast(epoch); + return static_cast(duration.count()); +} +#endif + } // unnamed namespace VectorAnimationTask::VectorAnimationTask(VisualFactoryCache& factoryCache) : AsyncTask(MakeCallback(this, &VectorAnimationTask::TaskCompleted), AsyncTask::PriorityType::HIGH, AsyncTask::ThreadType::WORKER_THREAD), - mUrl(), + mImageUrl(), + mEncodedImageBuffer(), mVectorRenderer(VectorAnimationRenderer::New()), mAnimationData(), mVectorAnimationThread(factoryCache.GetVectorAnimationManager().GetVectorAnimationThread()), - mConditionalWait(), + mMutex(), mResourceReadySignal(), mLoadCompletedCallback(MakeCallback(this, &VectorAnimationTask::OnLoadCompleted)), + mCachedLayerInfo(), + mCachedMarkerInfo(), mPlayState(PlayState::STOPPED), mStopBehavior(DevelImageVisual::StopBehavior::CURRENT_FRAME), mLoopingMode(DevelImageVisual::LoopingMode::RESTART), @@ -75,17 +92,24 @@ VectorAnimationTask::VectorAnimationTask(VisualFactoryCache& factoryCache) mWidth(0), mHeight(0), mAnimationDataIndex(0), + mAppliedPlayStateId(0u), mLoopCount(LOOP_FOREVER), mCurrentLoop(0), mForward(true), mUpdateFrameNumber(false), mNeedAnimationFinishedTrigger(true), + mNeedForceRenderOnceTrigger(false), mAnimationDataUpdated(false), mDestroyTask(false), mLoadRequest(false), mLoadFailed(false), mRasterized(false), - mKeepAnimation(false) + mKeepAnimation(false), + mLayerInfoCached(false), + mMarkerInfoCached(false), + mEnableFrameCache(false), + mNotifyAfterRasterization(false), + mSizeUpdated(false) { mVectorRenderer.UploadCompletedSignal().Connect(this, &VectorAnimationTask::OnUploadCompleted); } @@ -107,23 +131,25 @@ bool VectorAnimationTask::IsReady() void VectorAnimationTask::Finalize() { - ConditionalWait::ScopedLock lock(mConditionalWait); - - // Release some objects in the main thread - if(mAnimationFinishedCallback) - { - mVectorAnimationThread.RemoveEventTriggerCallback(mAnimationFinishedCallback.get()); - mAnimationFinishedCallback.reset(); - } - if(mLoadCompletedCallback) { - mVectorAnimationThread.RemoveEventTriggerCallback(mLoadCompletedCallback.get()); - mLoadCompletedCallback.reset(); + Mutex::ScopedLock lock(mMutex); + + // Release some objects in the main thread + if(mAnimationFinishedCallback) + { + mVectorAnimationThread.RemoveEventTriggerCallbacks(mAnimationFinishedCallback.get()); + mAnimationFinishedCallback.reset(); + } + if(mLoadCompletedCallback) + { + mVectorAnimationThread.RemoveEventTriggerCallbacks(mLoadCompletedCallback.get()); + mLoadCompletedCallback.reset(); + } + + mDestroyTask = true; } mVectorRenderer.Finalize(); - - mDestroyTask = true; } void VectorAnimationTask::TaskCompleted(VectorAnimationTaskPtr task) @@ -143,17 +169,70 @@ bool VectorAnimationTask::IsAnimating() bool VectorAnimationTask::Load(bool synchronousLoading) { - DALI_TRACE_SCOPE(gTraceFilter, "DALI_LOTTIE_LOADING_TASK"); +#ifdef TRACE_ENABLED + uint64_t mStartTimeNanoSceonds = 0; + uint64_t mEndTimeNanoSceonds = 0; + if(gTraceFilter && gTraceFilter->IsTraceEnabled()) + { + mStartTimeNanoSceonds = GetNanoseconds(); + std::ostringstream oss; + oss << "[u:" << mImageUrl.GetEllipsedUrl() << "]"; + // DALI_TRACE_BEGIN(gTraceFilter, "DALI_LOTTIE_LOADING_TASK"); ///< TODO : Open it if we can control trace log level + DALI_LOG_RELEASE_INFO("BEGIN: DALI_LOTTIE_LOADING_TASK %s", oss.str().c_str()); + } +#endif + + if(mEncodedImageBuffer) + { + if(!mVectorRenderer.Load(mEncodedImageBuffer.GetRawBuffer())) + { + mLoadFailed = true; + } + + // We don't need to hold image buffer anymore. + mEncodedImageBuffer.Reset(); + } + else if(mImageUrl.IsLocalResource()) + { + if(!mVectorRenderer.Load(mImageUrl.GetUrl())) + { + mLoadFailed = true; + } + } + else + { + Dali::Vector remoteData; + if(!Dali::FileLoader::DownloadFileSynchronously(mImageUrl.GetUrl(), remoteData) || // Failed if we fail to download json file, + !mVectorRenderer.Load(remoteData)) // or download data is not valid vector animation file. + { + mLoadFailed = true; + } + } - if(!mVectorRenderer.Load(mUrl)) + if(mLoadFailed) { - DALI_LOG_ERROR("VectorAnimationTask::Load: Load failed [%s]\n", mUrl.c_str()); + DALI_LOG_ERROR("VectorAnimationTask::Load: Load failed [%s]\n", mImageUrl.GetUrl().c_str()); mLoadRequest = false; - mLoadFailed = true; - if(!synchronousLoading && mLoadCompletedCallback) { - mVectorAnimationThread.AddEventTriggerCallback(mLoadCompletedCallback.get()); + Mutex::ScopedLock lock(mMutex); + if(!synchronousLoading && mLoadCompletedCallback) + { + mVectorAnimationThread.AddEventTriggerCallback(mLoadCompletedCallback.get(), 0u); + } } +#ifdef TRACE_ENABLED + if(gTraceFilter && gTraceFilter->IsTraceEnabled()) + { + mEndTimeNanoSceonds = GetNanoseconds(); + std::ostringstream oss; + oss << std::fixed << std::setprecision(3); + oss << "["; + oss << "d:" << static_cast(mEndTimeNanoSceonds - mStartTimeNanoSceonds) / 1000000.0f << "ms "; + oss << "u:" << mImageUrl.GetEllipsedUrl() << "]"; + // DALI_TRACE_END(gTraceFilter, "DALI_LOTTIE_LOADING_TASK"); ///< TODO : Open it if we can control trace log level + DALI_LOG_RELEASE_INFO("END: DALI_LOTTIE_LOADING_TASK %s", oss.str().c_str()); + } +#endif return false; } @@ -165,28 +244,44 @@ bool VectorAnimationTask::Load(bool synchronousLoading) mFrameDurationMicroSeconds = MICROSECONDS_PER_SECOND / mFrameRate; mLoadRequest = false; - if(!synchronousLoading && mLoadCompletedCallback) { - mVectorAnimationThread.AddEventTriggerCallback(mLoadCompletedCallback.get()); + Mutex::ScopedLock lock(mMutex); + if(!synchronousLoading && mLoadCompletedCallback) + { + mVectorAnimationThread.AddEventTriggerCallback(mLoadCompletedCallback.get(), 0u); + } } - DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationTask::Load: file = %s [%d frames, %f fps] [%p]\n", mUrl.c_str(), mTotalFrame, mFrameRate, this); + DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationTask::Load: file = %s [%d frames, %f fps] [%p]\n", mImageUrl.GetUrl().c_str(), mTotalFrame, mFrameRate, this); + +#ifdef TRACE_ENABLED + if(gTraceFilter && gTraceFilter->IsTraceEnabled()) + { + mEndTimeNanoSceonds = GetNanoseconds(); + std::ostringstream oss; + oss << std::fixed << std::setprecision(3); + oss << "["; + oss << "d:" << static_cast(mEndTimeNanoSceonds - mStartTimeNanoSceonds) / 1000000.0f << "ms "; + oss << "u:" << mImageUrl.GetEllipsedUrl() << "]"; + // DALI_TRACE_END(gTraceFilter, "DALI_LOTTIE_LOADING_TASK"); ///< TODO : Open it if we can control trace log level + DALI_LOG_RELEASE_INFO("END: DALI_LOTTIE_LOADING_TASK %s", oss.str().c_str()); + } +#endif return true; } 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::RequestLoad(const std::string& url, bool synchronousLoading) +void VectorAnimationTask::RequestLoad(const VisualUrl& url, EncodedImageBuffer encodedImageBuffer, bool synchronousLoading) { - mUrl = url; + mImageUrl = url; + mEncodedImageBuffer = encodedImageBuffer; if(!synchronousLoading) { @@ -198,7 +293,7 @@ void VectorAnimationTask::RequestLoad(const std::string& url, bool synchronousLo { Load(true); - OnLoadCompleted(); + OnLoadCompleted(0u); } } @@ -209,13 +304,13 @@ bool VectorAnimationTask::IsLoadRequested() const void VectorAnimationTask::SetAnimationData(const AnimationData& data) { - ConditionalWait::ScopedLock lock(mConditionalWait); + Mutex::ScopedLock lock(mMutex); DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationTask::SetAnimationData [%p]\n", this); uint32_t index = mAnimationDataIndex == 0 ? 1 : 0; // Use the other buffer - mAnimationData[index] = data; + mAnimationData[index].push_back(data); mAnimationDataUpdated = true; if(data.resendFlag & VectorAnimationTask::RESEND_SIZE) @@ -236,6 +331,20 @@ void VectorAnimationTask::SetSize(uint32_t width, uint32_t height) mWidth = width; mHeight = height; + // If fixedCache is enabled, Call KeepRasterizedBuffer() + if(mEnableFrameCache) + { + if(mTotalFrame > 0 && !mLoadFailed) + { + mVectorRenderer.KeepRasterizedBuffer(); + } + else + { + // If Load is not yet, update the size later. + mSizeUpdated = true; + } + } + DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationTask::SetSize: width = %d, height = %d [%p]\n", width, height, this); } } @@ -269,13 +378,16 @@ void VectorAnimationTask::PauseAnimation() { mPlayState = PlayState::PAUSED; + // Ensure to render paused frame. + mNeedForceRenderOnceTrigger = true; + DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationTask::PauseAnimation: Pause [%p]\n", this); } } void VectorAnimationTask::SetAnimationFinishedCallback(CallbackBase* callback) { - ConditionalWait::ScopedLock lock(mConditionalWait); + Mutex::ScopedLock lock(mMutex); mAnimationFinishedCallback = std::unique_ptr(callback); } @@ -360,13 +472,25 @@ void VectorAnimationTask::SetPlayRange(const Property::Array& playRange) if(mStartFrame > mCurrentFrame) { mCurrentFrame = mStartFrame; + + if(mPlayState != PlayState::PLAYING) + { + // Ensure to render current frame. + mNeedForceRenderOnceTrigger = true; + } } else if(mEndFrame < mCurrentFrame) { mCurrentFrame = mEndFrame; + + if(mPlayState != PlayState::PLAYING) + { + // Ensure to render current frame. + mNeedForceRenderOnceTrigger = true; + } } - DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationTask::SetPlayRange: [%d, %d] [%s] [%p]\n", mStartFrame, mEndFrame, mUrl.c_str(), this); + DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationTask::SetPlayRange: [%d, %d] [%s] [%p]\n", mStartFrame, mEndFrame, mImageUrl.GetUrl().c_str(), this); } } @@ -389,6 +513,12 @@ void VectorAnimationTask::SetCurrentFrameNumber(uint32_t frameNumber) mCurrentFrame = frameNumber; mUpdateFrameNumber = false; + if(mPlayState != PlayState::PLAYING) + { + // Ensure to render current frame. + mNeedForceRenderOnceTrigger = true; + } + DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationTask::SetCurrentFrameNumber: frame number = %d [%p]\n", mCurrentFrame, this); } else @@ -428,7 +558,38 @@ void VectorAnimationTask::SetLoopingMode(DevelImageVisual::LoopingMode::Type loo void VectorAnimationTask::GetLayerInfo(Property::Map& map) const { - mVectorRenderer.GetLayerInfo(map); + // Fast-out if file is loading, or load failed. + if(mLoadFailed || IsLoadRequested()) + { + return; + } + + if(DALI_UNLIKELY(!mLayerInfoCached)) + { + // Update only 1 time. + mLayerInfoCached = true; + mVectorRenderer.GetLayerInfo(mCachedLayerInfo); + } + + map = mCachedLayerInfo; +} + +void VectorAnimationTask::GetMarkerInfo(Property::Map& map) const +{ + // Fast-out if file is loading, or load failed. + if(mLoadFailed || IsLoadRequested()) + { + return; + } + + if(DALI_UNLIKELY(!mMarkerInfoCached)) + { + // Update only 1 time. + mMarkerInfoCached = true; + mVectorRenderer.GetMarkerInfo(mCachedMarkerInfo); + } + + map = mCachedMarkerInfo; } VectorAnimationTask::ResourceReadySignalType& VectorAnimationTask::ResourceReadySignal() @@ -443,7 +604,7 @@ bool VectorAnimationTask::Rasterize() mKeepAnimation = false; { - ConditionalWait::ScopedLock lock(mConditionalWait); + Mutex::ScopedLock lock(mMutex); if(mDestroyTask) { // The task will be destroyed. We don't need rasterization. @@ -461,7 +622,15 @@ bool VectorAnimationTask::Rasterize() return false; } - DALI_TRACE_BEGIN(gTraceFilter, "DALI_LOTTIE_RASTERIZE_TASK"); +#ifdef TRACE_ENABLED + uint64_t mStartTimeNanoSceonds = 0; + uint64_t mEndTimeNanoSceonds = 0; +#endif + DALI_TRACE_BEGIN_WITH_MESSAGE_GENERATOR(gTraceFilter, "DALI_LOTTIE_RASTERIZE_TASK", [&](std::ostringstream& oss) { + mStartTimeNanoSceonds = GetNanoseconds(); + oss << "[s:" << mWidth << "x" << mHeight << " "; + oss << "u:" << mImageUrl.GetEllipsedUrl() << "]"; + }); ApplyAnimationData(); @@ -547,6 +716,8 @@ bool VectorAnimationTask::Rasterize() mForward = true; mCurrentLoop = 0; + mNeedForceRenderOnceTrigger = true; + if(mVectorRenderer) { // Notify the Renderer that rendering is stopped. @@ -555,32 +726,40 @@ bool VectorAnimationTask::Rasterize() // Animation is finished { - ConditionalWait::ScopedLock lock(mConditionalWait); + Mutex::ScopedLock lock(mMutex); if(mNeedAnimationFinishedTrigger && mAnimationFinishedCallback) { - mVectorAnimationThread.AddEventTriggerCallback(mAnimationFinishedCallback.get()); + mVectorAnimationThread.AddEventTriggerCallback(mAnimationFinishedCallback.get(), mAppliedPlayStateId); } } DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationTask::Rasterize: Animation is finished [current = %d] [%p]\n", currentFrame, this); } - if(mPlayState != PlayState::PAUSED && mPlayState != PlayState::STOPPED) + // Forcely trigger render once if need. + if(mNotifyAfterRasterization || mNeedForceRenderOnceTrigger) { - mKeepAnimation = true; + Mutex::ScopedLock lock(mMutex); + mVectorAnimationThread.RequestForceRenderOnce(); + mNeedForceRenderOnceTrigger = false; } -#ifdef TRACE_ENABLED - if(gTraceFilter && gTraceFilter->IsTraceEnabled()) + if(mPlayState != PlayState::PAUSED && mPlayState != PlayState::STOPPED) { - std::ostringstream oss; - oss << "[size: " << mWidth << " x " << mHeight << ", "; - oss << "frame: " << mCurrentFrame << ", "; - oss << "loop: " << mCurrentLoop << ", "; - oss << "state : " << mPlayState << "]"; - DALI_TRACE_END_WITH_MESSAGE(gTraceFilter, "DALI_LOTTIE_RASTERIZE_TASK", oss.str().c_str()); + mKeepAnimation = true; } -#endif + + DALI_TRACE_END_WITH_MESSAGE_GENERATOR(gTraceFilter, "DALI_LOTTIE_RASTERIZE_TASK", [&](std::ostringstream& oss) { + mEndTimeNanoSceonds = GetNanoseconds(); + oss << std::fixed << std::setprecision(3); + oss << "["; + oss << "d:" << static_cast(mEndTimeNanoSceonds - mStartTimeNanoSceonds) / 1000000.0f << "ms "; + oss << "s:" << mWidth << "x" << mHeight << " "; + oss << "f:" << mCurrentFrame << " "; + oss << "l:" << mCurrentLoop << " "; + oss << "p:" << mPlayState << " "; + oss << "u:" << mImageUrl.GetEllipsedUrl() << "]"; + }); return true; } @@ -658,9 +837,9 @@ void VectorAnimationTask::ApplyAnimationData() uint32_t index; { - ConditionalWait::ScopedLock lock(mConditionalWait); + Mutex::ScopedLock lock(mMutex); - if(!mAnimationDataUpdated || mAnimationData[mAnimationDataIndex].resendFlag != 0) + if(!mAnimationDataUpdated || mAnimationData[mAnimationDataIndex].size() != 0) { // Data is not updated or the previous data is not applied yet. return; @@ -672,63 +851,71 @@ void VectorAnimationTask::ApplyAnimationData() index = mAnimationDataIndex; } - if(mAnimationData[index].resendFlag & VectorAnimationTask::RESEND_LOOP_COUNT) - { - SetLoopCount(mAnimationData[index].loopCount); - } - - if(mAnimationData[index].resendFlag & VectorAnimationTask::RESEND_PLAY_RANGE) + for(const auto& animationData : mAnimationData[index]) { - SetPlayRange(mAnimationData[index].playRange); - } + if(animationData.resendFlag & VectorAnimationTask::RESEND_LOOP_COUNT) + { + SetLoopCount(animationData.loopCount); + } - if(mAnimationData[index].resendFlag & VectorAnimationTask::RESEND_STOP_BEHAVIOR) - { - SetStopBehavior(mAnimationData[index].stopBehavior); - } + if(animationData.resendFlag & VectorAnimationTask::RESEND_PLAY_RANGE) + { + SetPlayRange(animationData.playRange); + } - if(mAnimationData[index].resendFlag & VectorAnimationTask::RESEND_LOOPING_MODE) - { - SetLoopingMode(mAnimationData[index].loopingMode); - } + if(animationData.resendFlag & VectorAnimationTask::RESEND_STOP_BEHAVIOR) + { + SetStopBehavior(animationData.stopBehavior); + } - if(mAnimationData[index].resendFlag & VectorAnimationTask::RESEND_CURRENT_FRAME) - { - SetCurrentFrameNumber(mAnimationData[index].currentFrame); - } + if(animationData.resendFlag & VectorAnimationTask::RESEND_LOOPING_MODE) + { + SetLoopingMode(animationData.loopingMode); + } - if(mAnimationData[index].resendFlag & VectorAnimationTask::RESEND_NEED_RESOURCE_READY) - { - mVectorRenderer.InvalidateBuffer(); - } + if(animationData.resendFlag & VectorAnimationTask::RESEND_CURRENT_FRAME) + { + SetCurrentFrameNumber(animationData.currentFrame); + } - if(mAnimationData[index].resendFlag & VectorAnimationTask::RESEND_DYNAMIC_PROPERTY) - { - for(auto&& iter : mAnimationData[index].dynamicProperties) + if(animationData.resendFlag & VectorAnimationTask::RESEND_NOTIFY_AFTER_RASTERIZATION) { - mVectorRenderer.AddPropertyValueCallback(iter.keyPath, static_cast(iter.property), iter.callback, iter.id); + mNotifyAfterRasterization = animationData.notifyAfterRasterization; } - } - if(mAnimationData[index].resendFlag & VectorAnimationTask::RESEND_PLAY_STATE) - { - if(mAnimationData[index].playState == DevelImageVisual::PlayState::PLAYING) + if(animationData.resendFlag & VectorAnimationTask::RESEND_NEED_RESOURCE_READY) { - PlayAnimation(); + mVectorRenderer.InvalidateBuffer(); } - else if(mAnimationData[index].playState == DevelImageVisual::PlayState::PAUSED) + + if(animationData.resendFlag & VectorAnimationTask::RESEND_DYNAMIC_PROPERTY) { - PauseAnimation(); + for(auto&& iter : animationData.dynamicProperties) + { + mVectorRenderer.AddPropertyValueCallback(iter.keyPath, static_cast(iter.property), iter.callback, iter.id); + } } - else if(mAnimationData[index].playState == DevelImageVisual::PlayState::STOPPED) + + if(animationData.resendFlag & VectorAnimationTask::RESEND_PLAY_STATE) { - StopAnimation(); + mAppliedPlayStateId = animationData.playStateId; + if(animationData.playState == DevelImageVisual::PlayState::PLAYING) + { + PlayAnimation(); + } + else if(animationData.playState == DevelImageVisual::PlayState::PAUSED) + { + PauseAnimation(); + } + else if(animationData.playState == DevelImageVisual::PlayState::STOPPED) + { + StopAnimation(); + } } } - // reset data - mAnimationData[index].dynamicProperties.clear(); - mAnimationData[index].resendFlag = 0; + // reset data list + mAnimationData[index].clear(); } void VectorAnimationTask::OnUploadCompleted() @@ -736,10 +923,15 @@ void VectorAnimationTask::OnUploadCompleted() mResourceReadySignal.Emit(ResourceStatus::READY); } -void VectorAnimationTask::OnLoadCompleted() +void VectorAnimationTask::OnLoadCompleted(uint32_t /* not used */) { if(!mLoadFailed) { + if(mEnableFrameCache && mSizeUpdated) + { + mVectorRenderer.KeepRasterizedBuffer(); + mSizeUpdated = false; + } mResourceReadySignal.Emit(ResourceStatus::LOADED); } else