From 35a7fe4e9b45558b58e365c637e704e2a51a5212 Mon Sep 17 00:00:00 2001 From: Heeyong Song Date: Fri, 1 Jul 2022 18:37:48 +0900 Subject: [PATCH] Support SVG thread pool Change-Id: Icbd6fc81adc7790f27e11f62eeb48d9fb1a09ce7 --- .../internal/helpers/round-robin-container-view.h | 19 +- .../internal/visuals/svg/svg-rasterize-thread.cpp | 241 +++++++++++++++------ .../internal/visuals/svg/svg-rasterize-thread.h | 125 +++++++++-- dali-toolkit/internal/visuals/svg/svg-visual.cpp | 6 +- .../internal/visuals/visual-factory-cache.cpp | 14 +- .../internal/visuals/visual-factory-cache.h | 8 +- 6 files changed, 312 insertions(+), 101 deletions(-) diff --git a/dali-toolkit/internal/helpers/round-robin-container-view.h b/dali-toolkit/internal/helpers/round-robin-container-view.h index b5f97df..5a5f07c 100644 --- a/dali-toolkit/internal/helpers/round-robin-container-view.h +++ b/dali-toolkit/internal/helpers/round-robin-container-view.h @@ -3,7 +3,7 @@ #define DALI_TOOLKIT_INTERNAL_ROUND_ROBIN_CONTAINER_VIEW_H /* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. + * Copyright (c) 2022 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. @@ -56,6 +56,14 @@ public: } /** + * @brief Clear all elements. + */ + void Clear() + { + mElements.clear(); + } + + /** * @brief Reset the position of the iterator returned by GetNext() to the first element. */ void Reset() @@ -86,6 +94,15 @@ public: return mElements.cend(); } + /** + * @brief Returns the element count. + * @return The element count + */ + size_t GetElementCount() const + { + return mElements.size(); + } + // default members ~RoundRobinContainerView() = default; diff --git a/dali-toolkit/internal/visuals/svg/svg-rasterize-thread.cpp b/dali-toolkit/internal/visuals/svg/svg-rasterize-thread.cpp index 768f743..c1ebd67 100644 --- a/dali-toolkit/internal/visuals/svg/svg-rasterize-thread.cpp +++ b/dali-toolkit/internal/visuals/svg/svg-rasterize-thread.cpp @@ -19,6 +19,7 @@ #include "svg-rasterize-thread.h" // EXTERNAL INCLUDES +#include #include #include #include @@ -33,6 +34,26 @@ namespace Toolkit { namespace Internal { +namespace +{ +constexpr auto DEFAULT_NUMBER_OF_SVG_RASTERIZE_THREADS = size_t{4u}; +constexpr auto NUMBER_OF_SVG_RASTERIZE_THREADS_ENV = "DALI_SVG_RASTERIZE_THREADS"; + +size_t GetNumberOfThreads(const char* environmentVariable, size_t defaultValue) +{ + auto numberString = EnvironmentVariable::GetEnvironmentVariable(environmentVariable); + auto numberOfThreads = numberString ? std::strtoul(numberString, nullptr, 10) : 0; + constexpr auto MAX_NUMBER_OF_THREADS = 10u; + DALI_ASSERT_DEBUG(numberOfThreads < MAX_NUMBER_OF_THREADS); + return (numberOfThreads > 0 && numberOfThreads < MAX_NUMBER_OF_THREADS) ? numberOfThreads : defaultValue; +} + +#if defined(DEBUG_ENABLED) +Debug::Filter* gVectorImageLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_VECTOR_IMAGE"); +#endif + +} // unnamed namespace + SvgTask::SvgTask(SvgVisual* svgVisual, VectorImageRenderer vectorRenderer) : mSvgVisual(svgVisual), mVectorRenderer(vectorRenderer), @@ -124,6 +145,8 @@ void SvgRasterizingTask::Process() return; } + DALI_LOG_INFO(gVectorImageLogFilter, Debug::Verbose, "Rasterize: (%d x %d) [%p]\n", mWidth, mHeight, this); + Devel::PixelBuffer pixelBuffer = mVectorRenderer.Rasterize(mWidth, mHeight); if(!pixelBuffer) { @@ -135,50 +158,111 @@ void SvgRasterizingTask::Process() mHasSucceeded = true; } +bool SvgRasterizingTask::IsReady() +{ + return mVectorRenderer.IsLoaded(); +} + PixelData SvgRasterizingTask::GetPixelData() const { return mPixelData; } -SvgRasterizeThread::SvgRasterizeThread() -: mTrigger(new EventThreadCallback(MakeCallback(this, &SvgRasterizeThread::ApplyRasterizedSVGToSampler))), +SvgRasterizeThread::SvgRasterizeThread(SvgRasterizeManager& svgRasterizeManager) +: mConditionalWait(), mLogFactory(Dali::Adaptor::Get().GetLogFactory()), - mIsThreadWaiting(false), - mProcessorRegistered(false) + mSvgRasterizeManager(svgRasterizeManager), + mDestroyThread(false), + mIsThreadStarted(false), + mIsThreadIdle(true) { } SvgRasterizeThread::~SvgRasterizeThread() { - if(mProcessorRegistered) + // Stop the thread { - Adaptor::Get().UnregisterProcessor(*this); + ConditionalWait::ScopedLock lock(mConditionalWait); + mDestroyThread = true; + mConditionalWait.Notify(lock); } + + Join(); } -void SvgRasterizeThread::TerminateThread(SvgRasterizeThread*& thread) +bool SvgRasterizeThread::RequestRasterize() { - if(thread) + if(!mIsThreadStarted) { - // add an empty task would stop the thread from conditional wait. - thread->AddTask(SvgTaskPtr()); - // stop the thread - thread->Join(); - // delete the thread - delete thread; - thread = NULL; + Start(); + mIsThreadStarted = true; } + + { + // Lock while adding task to the queue + ConditionalWait::ScopedLock lock(mConditionalWait); + + if(mIsThreadIdle) + { + mIsThreadIdle = false; + + // wake up the thread + mConditionalWait.Notify(lock); + return true; + } + } + + return false; } -void SvgRasterizeThread::AddTask(SvgTaskPtr task) +void SvgRasterizeThread::Run() { - bool wasEmpty = false; + SetThreadName("SvgRasterizeThread"); + mLogFactory.InstallLogFunction(); + while(!mDestroyThread) + { + SvgTaskPtr task = mSvgRasterizeManager.NextTaskToProcess(); + if(!task) + { + ConditionalWait::ScopedLock lock(mConditionalWait); + mIsThreadIdle = true; + mConditionalWait.Wait(lock); + } + else + { + task->Process(); + + mSvgRasterizeManager.AddCompletedTask(task); + } + } +} + +SvgRasterizeManager::SvgRasterizeManager() +: mRasterizers(GetNumberOfThreads(NUMBER_OF_SVG_RASTERIZE_THREADS_ENV, DEFAULT_NUMBER_OF_SVG_RASTERIZE_THREADS), [&]() { return RasterizeHelper(*this); }), + mTrigger(new EventThreadCallback(MakeCallback(this, &SvgRasterizeManager::ApplyRasterizedSVGToSampler))), + mProcessorRegistered(false) +{ +} + +SvgRasterizeManager::~SvgRasterizeManager() +{ + if(mProcessorRegistered) + { + Adaptor::Get().UnregisterProcessor(*this); + } + + mRasterizers.Clear(); +} + +void SvgRasterizeManager::AddTask(SvgTaskPtr task) +{ { // Lock while adding task to the queue - ConditionalWait::ScopedLock lock(mConditionalWait); - wasEmpty = mRasterizeTasks.empty(); - if(!wasEmpty && task) + Mutex::ScopedLock lock(mMutex); + + // There are other tasks waiting for the rasterization + if(!mRasterizeTasks.empty()) { // Remove the tasks with the same renderer. // Older task which waiting to rasterize and apply the svg to the same renderer is expired. @@ -197,23 +281,34 @@ void SvgRasterizeThread::AddTask(SvgTaskPtr task) } } } + mRasterizeTasks.push_back(task); + } + + size_t count = mRasterizers.GetElementCount(); + size_t index = 0; + while(index++ < count) + { + auto rasterizerHelperIt = mRasterizers.GetNext(); + DALI_ASSERT_ALWAYS(rasterizerHelperIt != mRasterizers.End()); - if(!mProcessorRegistered) + if(rasterizerHelperIt->RequestRasterize()) { - Adaptor::Get().RegisterProcessor(*this); - mProcessorRegistered = true; + break; } + // If all threads are busy, then it's ok just to push the task because they will try to get the next job. } - if(wasEmpty) + if(!mProcessorRegistered) { - // wake up the image loading thread - mConditionalWait.Notify(); + Adaptor::Get().RegisterProcessor(*this); + mProcessorRegistered = true; } + + return; } -SvgTaskPtr SvgRasterizeThread::NextCompletedTask() +SvgTaskPtr SvgRasterizeManager::NextCompletedTask() { // Lock while popping task out from the queue Mutex::ScopedLock lock(mMutex); @@ -230,21 +325,23 @@ SvgTaskPtr SvgRasterizeThread::NextCompletedTask() return nextTask; } -void SvgRasterizeThread::RemoveTask(SvgVisual* visual) +void SvgRasterizeManager::RemoveTask(SvgVisual* visual) { - // Lock while remove task from the queue - ConditionalWait::ScopedLock lock(mConditionalWait); - if(!mRasterizeTasks.empty()) { - for(std::vector::iterator it = mRasterizeTasks.begin(); it != mRasterizeTasks.end();) + // Lock while remove task from the queue + Mutex::ScopedLock lock(mMutex); + if(!mRasterizeTasks.empty()) { - if((*it) && (*it)->GetSvgVisual() == visual) - { - it = mRasterizeTasks.erase(it); - } - else + for(std::vector::iterator it = mRasterizeTasks.begin(); it != mRasterizeTasks.end();) { - it++; + if((*it) && (*it)->GetSvgVisual() == visual) + { + it = mRasterizeTasks.erase(it); + } + else + { + it++; + } } } } @@ -252,28 +349,28 @@ void SvgRasterizeThread::RemoveTask(SvgVisual* visual) UnregisterProcessor(); } -SvgTaskPtr SvgRasterizeThread::NextTaskToProcess() +SvgTaskPtr SvgRasterizeManager::NextTaskToProcess() { // Lock while popping task out from the queue - ConditionalWait::ScopedLock lock(mConditionalWait); + Mutex::ScopedLock lock(mMutex); - // conditional wait - while(mRasterizeTasks.empty()) + // pop out the next task from the queue + SvgTaskPtr nextTask = nullptr; + + for(auto iter = mRasterizeTasks.begin(), endIter = mRasterizeTasks.end(); iter != endIter; ++iter) { - mIsThreadWaiting = true; - mConditionalWait.Wait(lock); + if((*iter)->IsReady()) + { + nextTask = *iter; + mRasterizeTasks.erase(iter); + break; + } } - mIsThreadWaiting = false; - - // pop out the next task from the queue - std::vector::iterator next = mRasterizeTasks.begin(); - SvgTaskPtr nextTask = *next; - mRasterizeTasks.erase(next); return nextTask; } -void SvgRasterizeThread::AddCompletedTask(SvgTaskPtr task) +void SvgRasterizeManager::AddCompletedTask(SvgTaskPtr task) { // Lock while adding task to the queue Mutex::ScopedLock lock(mMutex); @@ -283,35 +380,27 @@ void SvgRasterizeThread::AddCompletedTask(SvgTaskPtr task) mTrigger->Trigger(); } -void SvgRasterizeThread::Run() -{ - SetThreadName("SVGThread"); - mLogFactory.InstallLogFunction(); - - while(SvgTaskPtr task = NextTaskToProcess()) - { - task->Process(); - AddCompletedTask(task); - } -} - -void SvgRasterizeThread::ApplyRasterizedSVGToSampler() +void SvgRasterizeManager::ApplyRasterizedSVGToSampler() { while(SvgTaskPtr task = NextCompletedTask()) { + DALI_LOG_INFO(gVectorImageLogFilter, Debug::Verbose, "task = %p\n", task.Get()); + task->GetSvgVisual()->ApplyRasterizedImage(task->GetPixelData(), task->HasSucceeded()); } UnregisterProcessor(); } -void SvgRasterizeThread::Process(bool postProcessor) +void SvgRasterizeManager::Process(bool postProcessor) { ApplyRasterizedSVGToSampler(); } -void SvgRasterizeThread::UnregisterProcessor() +void SvgRasterizeManager::UnregisterProcessor() { + Mutex::ScopedLock lock(mMutex); + if(mProcessorRegistered) { if(mRasterizeTasks.empty() && mCompletedTasks.empty()) @@ -322,6 +411,26 @@ void SvgRasterizeThread::UnregisterProcessor() } } +SvgRasterizeManager::RasterizeHelper::RasterizeHelper(SvgRasterizeManager& svgRasterizeManager) +: RasterizeHelper(std::unique_ptr(new SvgRasterizeThread(svgRasterizeManager)), svgRasterizeManager) +{ +} + +SvgRasterizeManager::RasterizeHelper::RasterizeHelper(RasterizeHelper&& rhs) +: RasterizeHelper(std::move(rhs.mRasterizer), rhs.mSvgRasterizeManager) +{ +} + +SvgRasterizeManager::RasterizeHelper::RasterizeHelper(std::unique_ptr rasterizer, SvgRasterizeManager& svgRasterizeManager) +: mRasterizer(std::move(rasterizer)), + mSvgRasterizeManager(svgRasterizeManager) +{ +} + +bool SvgRasterizeManager::RasterizeHelper::RequestRasterize() +{ + return mRasterizer->RequestRasterize(); +} } // namespace Internal } // namespace Toolkit diff --git a/dali-toolkit/internal/visuals/svg/svg-rasterize-thread.h b/dali-toolkit/internal/visuals/svg/svg-rasterize-thread.h index 13d284b..c9c305b 100644 --- a/dali-toolkit/internal/visuals/svg/svg-rasterize-thread.h +++ b/dali-toolkit/internal/visuals/svg/svg-rasterize-thread.h @@ -32,6 +32,7 @@ #include // INTERNAL INCLUDES +#include #include namespace Dali @@ -44,6 +45,7 @@ class SvgVisual; typedef IntrusivePtr SvgVisualPtr; class SvgTask; typedef IntrusivePtr SvgTaskPtr; +class SvgRasterizeManager; /** * The svg rasterizing tasks to be processed in the worker thread. @@ -75,6 +77,15 @@ public: virtual void Process() = 0; /** + * Whether the task is ready to process. + * @return True if the task is ready to process. + */ + virtual bool IsReady() + { + return true; + } + + /** * Whether the task has succeeded. * @return True if the task has succeeded. */ @@ -161,6 +172,12 @@ public: void Process() override; /** + * Whether the task is ready to process. + * @return True if the task is ready to process. + */ + bool IsReady() override; + + /** * Get the rasterization result. * @return The pixel data with the rasterized pixels. */ @@ -182,7 +199,52 @@ private: /** * The worker thread for SVG rasterization. */ -class SvgRasterizeThread : public Thread, Integration::Processor +class SvgRasterizeThread : public Thread +{ +public: + /** + * Constructor. + */ + SvgRasterizeThread(SvgRasterizeManager& svgRasterizeManager); + + /** + * Destructor. + */ + ~SvgRasterizeThread() override; + + /** + * @brief Request the thread to rasterizes the task. + * @return True if the request succeeds, otherwise false. + */ + bool RequestRasterize(); + +protected: + /** + * The entry function of the worker thread. + * It rasterizes the image. + */ + void Run() override; + +private: + // Undefined + SvgRasterizeThread(const SvgRasterizeThread& thread) = delete; + + // Undefined + SvgRasterizeThread& operator=(const SvgRasterizeThread& thread) = delete; + +private: + ConditionalWait mConditionalWait; + const Dali::LogFactoryInterface& mLogFactory; + SvgRasterizeManager& mSvgRasterizeManager; + bool mDestroyThread; + bool mIsThreadStarted; + bool mIsThreadIdle; +}; + +/** + * The manager for SVG rasterization. + */ +class SvgRasterizeManager : Integration::Processor { public: /** @@ -190,12 +252,12 @@ public: * * @param[in] trigger The trigger to wake up the main thread. */ - SvgRasterizeThread(); + SvgRasterizeManager(); /** - * Terminate the svg rasterize thread, join and delete. + * Destructor. */ - static void TerminateThread(SvgRasterizeThread*& thread); + ~SvgRasterizeManager() override; /** * Add a rasterization task into the waiting queue, called by main thread. @@ -225,7 +287,6 @@ public: */ void Process(bool postProcessor) override; -private: /** * Pop the next task out from the queue. * @@ -240,6 +301,7 @@ private: */ void AddCompletedTask(SvgTaskPtr task); +private: /** * Applies the rasterized image to material */ @@ -251,34 +313,59 @@ private: */ void UnregisterProcessor(); -protected: - /** - * Destructor. - */ - ~SvgRasterizeThread() override; - +private: /** - * The entry function of the worker thread. - * It fetches task from the Queue, rasterizes the image and apply to the renderer. + * @brief Helper class to keep the relation between SvgRasterizeThread and corresponding container */ - void Run() override; + class RasterizeHelper + { + public: + /** + * @brief Create an RasterizeHelper. + * + * @param[in] svgRasterizeManager Reference to the SvgRasterizeManager + */ + RasterizeHelper(SvgRasterizeManager& svgRasterizeManager); + + /** + * @brief Request the thread to rasterizes the task. + * @return True if the request succeeds, otherwise false. + */ + bool RequestRasterize(); + + 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 rasterizer, SvgRasterizeManager& svgRasterizeManager); + + private: + std::unique_ptr mRasterizer; + SvgRasterizeManager& mSvgRasterizeManager; + }; private: // Undefined - SvgRasterizeThread(const SvgRasterizeThread& thread); + SvgRasterizeManager(const SvgRasterizeManager& thread); // Undefined - SvgRasterizeThread& operator=(const SvgRasterizeThread& thread); + SvgRasterizeManager& operator=(const SvgRasterizeManager& thread); private: std::vector mRasterizeTasks; //The queue of the tasks waiting to rasterize the SVG image std::vector mCompletedTasks; //The queue of the tasks with the SVG rasterization completed - ConditionalWait mConditionalWait; + RoundRobinContainerView mRasterizers; + Dali::Mutex mMutex; std::unique_ptr mTrigger; - const Dali::LogFactoryInterface& mLogFactory; - bool mIsThreadWaiting; bool mProcessorRegistered; }; diff --git a/dali-toolkit/internal/visuals/svg/svg-visual.cpp b/dali-toolkit/internal/visuals/svg/svg-visual.cpp index 9d0a2cc..a0ced87 100644 --- a/dali-toolkit/internal/visuals/svg/svg-visual.cpp +++ b/dali-toolkit/internal/visuals/svg/svg-visual.cpp @@ -100,7 +100,7 @@ void SvgVisual::OnInitialize() } else { - mFactoryCache.GetSVGRasterizationThread()->AddTask(newTask); + mFactoryCache.GetSVGRasterizationManager()->AddTask(newTask); } } @@ -191,7 +191,7 @@ void SvgVisual::DoSetOnScene(Actor& actor) void SvgVisual::DoSetOffScene(Actor& actor) { - mFactoryCache.GetSVGRasterizationThread()->RemoveTask(this); + mFactoryCache.GetSVGRasterizationManager()->RemoveTask(this); actor.RemoveRenderer(mImpl->mRenderer); mPlacementActor.Reset(); @@ -266,7 +266,7 @@ void SvgVisual::AddRasterizationTask(const Vector2& size) } else { - mFactoryCache.GetSVGRasterizationThread()->AddTask(newTask); + mFactoryCache.GetSVGRasterizationManager()->AddTask(newTask); } } } diff --git a/dali-toolkit/internal/visuals/visual-factory-cache.cpp b/dali-toolkit/internal/visuals/visual-factory-cache.cpp index 2f5d123..6b4bf10 100644 --- a/dali-toolkit/internal/visuals/visual-factory-cache.cpp +++ b/dali-toolkit/internal/visuals/visual-factory-cache.cpp @@ -43,8 +43,8 @@ const Vector4 FULL_TEXTURE_RECT(0.f, 0.f, 1.f, 1.f); } VisualFactoryCache::VisualFactoryCache(bool preMultiplyOnLoad) -: mSvgRasterizeThread(NULL), - mVectorAnimationManager(), +: mSvgRasterizeManager(nullptr), + mVectorAnimationManager(nullptr), mPreMultiplyOnLoad(preMultiplyOnLoad), mBrokenImageInfoContainer(), mDefaultBrokenImageUrl(""), @@ -54,7 +54,6 @@ VisualFactoryCache::VisualFactoryCache(bool preMultiplyOnLoad) VisualFactoryCache::~VisualFactoryCache() { - SvgRasterizeThread::TerminateThread(mSvgRasterizeThread); } Geometry VisualFactoryCache::GetGeometry(GeometryType type) @@ -131,14 +130,13 @@ NPatchLoader& VisualFactoryCache::GetNPatchLoader() return mNPatchLoader; } -SvgRasterizeThread* VisualFactoryCache::GetSVGRasterizationThread() +SvgRasterizeManager* VisualFactoryCache::GetSVGRasterizationManager() { - if(!mSvgRasterizeThread) + if(!mSvgRasterizeManager) { - mSvgRasterizeThread = new SvgRasterizeThread(); - mSvgRasterizeThread->Start(); + mSvgRasterizeManager = std::unique_ptr(new SvgRasterizeManager()); } - return mSvgRasterizeThread; + return mSvgRasterizeManager.get(); } VectorAnimationManager& VisualFactoryCache::GetVectorAnimationManager() diff --git a/dali-toolkit/internal/visuals/visual-factory-cache.h b/dali-toolkit/internal/visuals/visual-factory-cache.h index 52dbf61..6d9f28c 100644 --- a/dali-toolkit/internal/visuals/visual-factory-cache.h +++ b/dali-toolkit/internal/visuals/visual-factory-cache.h @@ -239,10 +239,10 @@ public: NPatchLoader& GetNPatchLoader(); /** - * Get the SVG rasterization thread. - * @return A raw pointer pointing to the SVG rasterization thread. + * Get the SVG rasterization manager. + * @return A raw pointer pointing to the SVG rasterization manager. */ - SvgRasterizeThread* GetSVGRasterizationThread(); + SvgRasterizeManager* GetSVGRasterizationManager(); /** * Get the vector animation manager. @@ -342,7 +342,7 @@ private: TextureManager mTextureManager; NPatchLoader mNPatchLoader; - SvgRasterizeThread* mSvgRasterizeThread; + std::unique_ptr mSvgRasterizeManager; std::unique_ptr mVectorAnimationManager; bool mPreMultiplyOnLoad; std::vector mBrokenImageInfoContainer; -- 2.7.4