/*
- * 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.
#include "svg-rasterize-thread.h"
// EXTERNAL INCLUDES
+#include <dali/devel-api/adaptor-framework/environment-variable.h>
#include <dali/devel-api/adaptor-framework/file-loader.h>
#include <dali/devel-api/adaptor-framework/thread-settings.h>
#include <dali/integration-api/adaptor-framework/adaptor.h>
{
namespace Internal
{
-RasterizingTask::RasterizingTask(SvgVisual* svgRenderer, VectorImageRenderer vectorRenderer, const VisualUrl& url, float dpi, unsigned int width, unsigned int height)
-: mSvgVisual(svgRenderer),
+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();
+}
+
+bool SvgTask::HasSucceeded() const
+{
+ return mHasSucceeded;
+}
+
+SvgLoadingTask::SvgLoadingTask(SvgVisual* svgVisual, VectorImageRenderer vectorRenderer, const VisualUrl& url, float dpi)
+: SvgTask(svgVisual, vectorRenderer),
mUrl(url),
- mDpi(dpi),
- mWidth(width),
- mHeight(height),
- mLoadSuccess(false)
+ mDpi(dpi)
{
}
-RasterizingTask::~RasterizingTask()
+SvgLoadingTask::~SvgLoadingTask()
{
}
-void RasterizingTask::Load()
+void SvgLoadingTask::Process()
{
+ if(mVectorRenderer.IsLoaded())
+ {
+ // Already loaded
+ mHasSucceeded = true;
+ return;
+ }
+
+ Dali::Vector<uint8_t> buffer;
+
if(!mUrl.IsLocalResource())
{
- Dali::Vector<uint8_t> remoteBuffer;
- if(!Dali::FileLoader::DownloadFileSynchronously(mUrl.GetUrl(), remoteBuffer))
+ 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;
}
-
- mLoadSuccess = true;
}
- else
+
+ buffer.PushBack('\0');
+
+ if(!mVectorRenderer.Load(buffer, mDpi))
{
- mLoadSuccess = true;
+ DALI_LOG_ERROR("Failed to load data! [%s]\n", mUrl.GetUrl().c_str());
+ return;
}
+
+ mHasSucceeded = true;
+}
+
+SvgRasterizingTask::SvgRasterizingTask(SvgVisual* svgVisual, VectorImageRenderer vectorRenderer, unsigned int width, unsigned int height)
+: SvgTask(svgVisual, vectorRenderer),
+ mWidth(width),
+ mHeight(height)
+{
+}
+
+SvgRasterizingTask::~SvgRasterizingTask()
+{
}
-void RasterizingTask::Rasterize()
+void SvgRasterizingTask::Process()
{
- if(mWidth <= 0u || mHeight <= 0u)
+ 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<float>(mWidth) / static_cast<float>(defaultWidth);
- float scaleY = static_cast<float>(mHeight) / static_cast<float>(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 mLoadSuccess;
+ 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)
+{
+}
+
+SvgRasterizeManager::~SvgRasterizeManager()
{
- if(thread)
+ 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<SvgTaskPtr>::iterator it = mRasterizeTasks.begin(), endIt = mRasterizeTasks.end(); it != endIt; ++it)
{
if((*it) && (*it)->GetSvgVisual() == task->GetSvgVisual())
{
- mRasterizeTasks.erase(it);
- break;
+ SvgRasterizingTask* oldTask = dynamic_cast<SvgRasterizingTask*>(it->Get());
+ SvgRasterizingTask* newTask = dynamic_cast<SvgRasterizingTask*>(task.Get());
+ if(oldTask && newTask)
+ {
+ mRasterizeTasks.erase(it);
+ break;
+ }
}
}
}
+
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;
}
-RasterizingTaskPtr SvgRasterizeThread::NextCompletedTask()
+SvgTaskPtr SvgRasterizeManager::NextCompletedTask()
{
// Lock while popping task out from the queue
Mutex::ScopedLock lock(mMutex);
if(mCompletedTasks.empty())
{
- return RasterizingTaskPtr();
+ return SvgTaskPtr();
}
- std::vector<RasterizingTaskPtr>::iterator next = mCompletedTasks.begin();
- RasterizingTaskPtr nextTask = *next;
+ std::vector<SvgTaskPtr>::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<SvgTaskPtr>::iterator it = mRasterizeTasks.begin(); it != mRasterizeTasks.end();)
{
- mRasterizeTasks.erase(it);
- break;
+ if((*it) && (*it)->GetSvgVisual() == visual)
+ {
+ it = mRasterizeTasks.erase(it);
+ }
+ else
+ {
+ it++;
+ }
}
}
}
UnregisterProcessor();
}
-RasterizingTaskPtr SvgRasterizeThread::NextTaskToProcess()
+SvgTaskPtr SvgRasterizeManager::NextTaskToProcess()
{
// Lock while popping task out from the queue
- ConditionalWait::ScopedLock lock(mConditionalWait);
+ Mutex::ScopedLock lock(mMutex);
+
+ // 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);
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(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())
}
}
+SvgRasterizeManager::RasterizeHelper::RasterizeHelper(SvgRasterizeManager& svgRasterizeManager)
+: RasterizeHelper(std::unique_ptr<SvgRasterizeThread>(new SvgRasterizeThread(svgRasterizeManager)), svgRasterizeManager)
+{
+}
+
+SvgRasterizeManager::RasterizeHelper::RasterizeHelper(RasterizeHelper&& rhs)
+: RasterizeHelper(std::move(rhs.mRasterizer), rhs.mSvgRasterizeManager)
+{
+}
+
+SvgRasterizeManager::RasterizeHelper::RasterizeHelper(std::unique_ptr<SvgRasterizeThread> rasterizer, SvgRasterizeManager& svgRasterizeManager)
+: mRasterizer(std::move(rasterizer)),
+ mSvgRasterizeManager(svgRasterizeManager)
+{
+}
+
+bool SvgRasterizeManager::RasterizeHelper::RequestRasterize()
+{
+ return mRasterizer->RequestRasterize();
+}
} // namespace Internal
} // namespace Toolkit