X-Git-Url: http://review.tizen.org/git/?p=platform%2Fcore%2Fuifw%2Fdali-toolkit.git;a=blobdiff_plain;f=dali-toolkit%2Finternal%2Fvisuals%2Fsvg%2Fsvg-rasterize-thread.cpp;h=c1ebd67efe7047ba6ffb63edf5e146ac22891f7a;hp=8bd9f0a02ed8e91e1be6cc469eea81eb17366709;hb=59b714d41e4cd4d07875def8befc59c5399ab4b6;hpb=09502f5f09f1294ef5555e4efbf0e7e8115bfbc3 diff --git a/dali-toolkit/internal/visuals/svg/svg-rasterize-thread.cpp b/dali-toolkit/internal/visuals/svg/svg-rasterize-thread.cpp index 8bd9f0a..c1ebd67 100644 --- a/dali-toolkit/internal/visuals/svg/svg-rasterize-thread.cpp +++ b/dali-toolkit/internal/visuals/svg/svg-rasterize-thread.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 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. @@ -19,8 +19,9 @@ #include "svg-rasterize-thread.h" // EXTERNAL INCLUDES -#include +#include #include +#include #include #include @@ -29,194 +30,318 @@ namespace Dali { - 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), + mHasSucceeded(false) +{ +} + +SvgVisual* SvgTask::GetSvgVisual() const +{ + return mSvgVisual.Get(); +} + +PixelData SvgTask::GetPixelData() const +{ + return PixelData(); +} -RasterizingTask::RasterizingTask( SvgVisual* svgRenderer, VectorImageRenderer vectorRenderer, const VisualUrl& url, float dpi, unsigned int width, unsigned int height, bool loaded) -: mSvgVisual( svgRenderer ), - mVectorRenderer( vectorRenderer ), - mUrl( url ), - mDpi( dpi ), - mWidth( width ), - mHeight( height ), - mLoaded( loaded ) +bool SvgTask::HasSucceeded() const { + return mHasSucceeded; +} +SvgLoadingTask::SvgLoadingTask(SvgVisual* svgVisual, VectorImageRenderer vectorRenderer, const VisualUrl& url, float dpi) +: SvgTask(svgVisual, vectorRenderer), + mUrl(url), + mDpi(dpi) +{ } -RasterizingTask::~RasterizingTask() +SvgLoadingTask::~SvgLoadingTask() { } -void RasterizingTask::Load() +void SvgLoadingTask::Process() { - if(!mLoaded && !mUrl.IsLocalResource()) + if(mVectorRenderer.IsLoaded()) { - Dali::Vector remoteBuffer; - if(!Dali::FileLoader::DownloadFileSynchronously(mUrl.GetUrl(), remoteBuffer)) + // Already loaded + mHasSucceeded = true; + return; + } + + Dali::Vector buffer; + + if(!mUrl.IsLocalResource()) + { + if(!Dali::FileLoader::DownloadFileSynchronously(mUrl.GetUrl(), buffer)) { - DALI_LOG_ERROR("RasterizingTask::Load: Failed to download file! [%s]\n", mUrl.GetUrl().c_str()); + DALI_LOG_ERROR("Failed to download file! [%s]\n", mUrl.GetUrl().c_str()); return; } - - remoteBuffer.PushBack('\0'); - - if(!mVectorRenderer.Load(remoteBuffer, mDpi)) + } + else + { + if(!Dali::FileLoader::ReadFile(mUrl.GetUrl(), buffer)) { - DALI_LOG_ERROR("RasterizingTask::Load:Failed to load data! [%s]\n", mUrl.GetUrl().c_str()); + DALI_LOG_ERROR("Failed to read file! [%s]\n", mUrl.GetUrl().c_str()); return; } + } - mLoaded = true; + buffer.PushBack('\0'); + + if(!mVectorRenderer.Load(buffer, mDpi)) + { + DALI_LOG_ERROR("Failed to load data! [%s]\n", mUrl.GetUrl().c_str()); + return; } + + mHasSucceeded = true; } -void RasterizingTask::Rasterize() +SvgRasterizingTask::SvgRasterizingTask(SvgVisual* svgVisual, VectorImageRenderer vectorRenderer, unsigned int width, unsigned int height) +: SvgTask(svgVisual, vectorRenderer), + mWidth(width), + mHeight(height) { - if(mWidth <= 0u || mHeight <= 0u) +} + +SvgRasterizingTask::~SvgRasterizingTask() +{ +} + +void SvgRasterizingTask::Process() +{ + if(!mVectorRenderer.IsLoaded()) { - DALI_LOG_ERROR("RasterizingTask::Rasterize: Size is zero!\n"); + DALI_LOG_ERROR("File is not loaded!\n"); return; } - Devel::PixelBuffer pixelBuffer = Devel::PixelBuffer::New(mWidth, mHeight, Dali::Pixel::RGBA8888); + DALI_LOG_INFO(gVectorImageLogFilter, Debug::Verbose, "Rasterize: (%d x %d) [%p]\n", mWidth, mHeight, this); - uint32_t defaultWidth, defaultHeight; - mVectorRenderer.GetDefaultSize(defaultWidth, defaultHeight); - - float scaleX = static_cast(mWidth) / static_cast(defaultWidth); - float scaleY = static_cast(mHeight) / static_cast(defaultHeight); - float scale = scaleX < scaleY ? scaleX : scaleY; - - if(!mVectorRenderer.Rasterize(pixelBuffer, scale)) + Devel::PixelBuffer pixelBuffer = mVectorRenderer.Rasterize(mWidth, mHeight); + if(!pixelBuffer) { - DALI_LOG_ERROR("RasterizingTask::Rasterize: Rasterize is failed! [%s]\n", mUrl.GetUrl().c_str()); + DALI_LOG_ERROR("Rasterize is failed!\n"); return; } - mPixelData = Devel::PixelBuffer::Convert(pixelBuffer); + mPixelData = Devel::PixelBuffer::Convert(pixelBuffer); + mHasSucceeded = true; } -VectorImageRenderer RasterizingTask::GetVectorRenderer() const +bool SvgRasterizingTask::IsReady() { - return mVectorRenderer; + return mVectorRenderer.IsLoaded(); } -bool RasterizingTask::IsLoaded() const +PixelData SvgRasterizingTask::GetPixelData() const { - return mLoaded; + return mPixelData; } -SvgVisual* RasterizingTask::GetSvgVisual() const +SvgRasterizeThread::SvgRasterizeThread(SvgRasterizeManager& svgRasterizeManager) +: mConditionalWait(), + mLogFactory(Dali::Adaptor::Get().GetLogFactory()), + mSvgRasterizeManager(svgRasterizeManager), + mDestroyThread(false), + mIsThreadStarted(false), + mIsThreadIdle(true) { - return mSvgVisual.Get(); } -PixelData RasterizingTask::GetPixelData() const +SvgRasterizeThread::~SvgRasterizeThread() { - return mPixelData; + // Stop the thread + { + ConditionalWait::ScopedLock lock(mConditionalWait); + mDestroyThread = true; + mConditionalWait.Notify(lock); + } + + Join(); } -SvgRasterizeThread::SvgRasterizeThread() -: mTrigger( new EventThreadCallback( MakeCallback( this, &SvgRasterizeThread::ApplyRasterizedSVGToSampler ) ) ), - mLogFactory( Dali::Adaptor::Get().GetLogFactory() ), - mIsThreadWaiting( false ), - mProcessorRegistered( false ) +bool SvgRasterizeThread::RequestRasterize() { + if(!mIsThreadStarted) + { + 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; } -SvgRasterizeThread::~SvgRasterizeThread() +void SvgRasterizeThread::Run() { - if( mProcessorRegistered ) + SetThreadName("SvgRasterizeThread"); + mLogFactory.InstallLogFunction(); + + while(!mDestroyThread) { - Adaptor::Get().UnregisterProcessor( *this ); + SvgTaskPtr task = mSvgRasterizeManager.NextTaskToProcess(); + if(!task) + { + ConditionalWait::ScopedLock lock(mConditionalWait); + mIsThreadIdle = true; + mConditionalWait.Wait(lock); + } + else + { + task->Process(); + + mSvgRasterizeManager.AddCompletedTask(task); + } } } -void SvgRasterizeThread::TerminateThread( SvgRasterizeThread*& thread ) +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) { - if( thread ) +} + +SvgRasterizeManager::~SvgRasterizeManager() +{ + if(mProcessorRegistered) { - // add an empty task would stop the thread from conditional wait. - thread->AddTask( RasterizingTaskPtr() ); - // stop the thread - thread->Join(); - // delete the thread - delete thread; - thread = NULL; + Adaptor::Get().UnregisterProcessor(*this); } + + mRasterizers.Clear(); } -void SvgRasterizeThread::AddTask( RasterizingTaskPtr task ) +void SvgRasterizeManager::AddTask(SvgTaskPtr task) { - bool wasEmpty = false; - { // Lock while adding task to the queue - ConditionalWait::ScopedLock lock( mConditionalWait ); - wasEmpty = mRasterizeTasks.empty(); - if( !wasEmpty && task != NULL) + 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. - for( std::vector< RasterizingTaskPtr >::iterator it = mRasterizeTasks.begin(), endIt = mRasterizeTasks.end(); it != endIt; ++it ) + // Rasterizing task only, loading task is not duplicated. + for(std::vector::iterator it = mRasterizeTasks.begin(), endIt = mRasterizeTasks.end(); it != endIt; ++it) { - if( (*it) && (*it)->GetSvgVisual() == task->GetSvgVisual() ) + if((*it) && (*it)->GetSvgVisual() == task->GetSvgVisual()) { - mRasterizeTasks.erase( it ); - break; + SvgRasterizingTask* oldTask = dynamic_cast(it->Get()); + SvgRasterizingTask* newTask = dynamic_cast(task.Get()); + if(oldTask && newTask) + { + mRasterizeTasks.erase(it); + break; + } } } } - mRasterizeTasks.push_back( task ); - if( !mProcessorRegistered ) + 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(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; } -RasterizingTaskPtr SvgRasterizeThread::NextCompletedTask() +SvgTaskPtr SvgRasterizeManager::NextCompletedTask() { // Lock while popping task out from the queue - Mutex::ScopedLock lock( mMutex ); + Mutex::ScopedLock lock(mMutex); - if( mCompletedTasks.empty() ) + if(mCompletedTasks.empty()) { - return RasterizingTaskPtr(); + return SvgTaskPtr(); } - std::vector< RasterizingTaskPtr >::iterator next = mCompletedTasks.begin(); - RasterizingTaskPtr nextTask = *next; - mCompletedTasks.erase( next ); + std::vector::iterator next = mCompletedTasks.begin(); + SvgTaskPtr nextTask = *next; + mCompletedTasks.erase(next); 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< RasterizingTaskPtr >::iterator it = mRasterizeTasks.begin(), endIt = mRasterizeTasks.end(); it != endIt; ++it ) + // Lock while remove task from the queue + Mutex::ScopedLock lock(mMutex); + if(!mRasterizeTasks.empty()) { - if( (*it) && (*it)->GetSvgVisual() == visual ) + for(std::vector::iterator it = mRasterizeTasks.begin(); it != mRasterizeTasks.end();) { - mRasterizeTasks.erase( it ); - break; + if((*it) && (*it)->GetSvgVisual() == visual) + { + it = mRasterizeTasks.erase(it); + } + else + { + it++; + } } } } @@ -224,99 +349,88 @@ void SvgRasterizeThread::RemoveTask( SvgVisual* visual ) UnregisterProcessor(); } -void SvgRasterizeThread::DeleteImage( VectorImageRenderer vectorRenderer ) -{ - // Lock while adding image to the delete queue - ConditionalWait::ScopedLock lock( mConditionalWait ); - - if( mIsThreadWaiting ) // no rasterization is ongoing, save to delete - { - // TODO: what? - } - else // wait to delete until current rasterization completed. - { - mDeleteSvg.PushBack( &vectorRenderer ); - } -} - -RasterizingTaskPtr SvgRasterizeThread::NextTaskToProcess() +SvgTaskPtr SvgRasterizeManager::NextTaskToProcess() { // Lock while popping task out from the queue - ConditionalWait::ScopedLock lock( mConditionalWait ); + Mutex::ScopedLock lock(mMutex); - // Delete the image here to make sure that it is not used in the nsvgRasterize() - if( !mDeleteSvg.Empty() ) - { - mDeleteSvg.Clear(); - } + // pop out the next task from the queue + SvgTaskPtr nextTask = nullptr; - // conditional wait - while( mRasterizeTasks.empty() ) + 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< RasterizingTaskPtr >::iterator next = mRasterizeTasks.begin(); - RasterizingTaskPtr nextTask = *next; - mRasterizeTasks.erase( next ); return nextTask; } -void SvgRasterizeThread::AddCompletedTask( RasterizingTaskPtr task ) +void SvgRasterizeManager::AddCompletedTask(SvgTaskPtr task) { // Lock while adding task to the queue - Mutex::ScopedLock lock( mMutex ); - mCompletedTasks.push_back( task ); + Mutex::ScopedLock lock(mMutex); + mCompletedTasks.push_back(task); // wake up the main thread mTrigger->Trigger(); } -void SvgRasterizeThread::Run() +void SvgRasterizeManager::ApplyRasterizedSVGToSampler() { - SetThreadName( "SVGThread" ); - mLogFactory.InstallLogFunction(); - - while( RasterizingTaskPtr task = NextTaskToProcess() ) + while(SvgTaskPtr task = NextCompletedTask()) { - task->Load( ); - task->Rasterize( ); - AddCompletedTask( task ); - } -} + DALI_LOG_INFO(gVectorImageLogFilter, Debug::Verbose, "task = %p\n", task.Get()); -void SvgRasterizeThread::ApplyRasterizedSVGToSampler() -{ - while( RasterizingTaskPtr task = NextCompletedTask() ) - { - task->GetSvgVisual()->ApplyRasterizedImage(task->GetVectorRenderer(), task->GetPixelData(), task->IsLoaded()); + task->GetSvgVisual()->ApplyRasterizedImage(task->GetPixelData(), task->HasSucceeded()); } UnregisterProcessor(); } -void SvgRasterizeThread::Process() +void SvgRasterizeManager::Process(bool postProcessor) { ApplyRasterizedSVGToSampler(); } -void SvgRasterizeThread::UnregisterProcessor() +void SvgRasterizeManager::UnregisterProcessor() { - if ( mProcessorRegistered ) + Mutex::ScopedLock lock(mMutex); + + if(mProcessorRegistered) { - if( mRasterizeTasks.empty() && mCompletedTasks.empty() ) + if(mRasterizeTasks.empty() && mCompletedTasks.empty()) { - Adaptor::Get().UnregisterProcessor( *this ); + Adaptor::Get().UnregisterProcessor(*this); mProcessorRegistered = false; } } } +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