From 0a079815dfdc1dc941a0741d8c1e390528ac80ef Mon Sep 17 00:00:00 2001 From: JunsuChoi Date: Mon, 31 May 2021 14:44:59 +0900 Subject: [PATCH] Add CanvasView thread rasterization When a rasterize request occurs, create a task and rasterize on thread. Change-Id: I8f9117490fb242c91d0124759e0a1f7df9ed701a --- .../toolkit-canvas-renderer.cpp | 20 ++ .../src/dali-toolkit/utc-Dali-CanvasView.cpp | 287 ++++++++++++++++++++- .../controls/canvas-view/canvas-view-impl.cpp | 107 +++++--- .../controls/canvas-view/canvas-view-impl.h | 31 ++- .../canvas-view/canvas-view-rasterize-thread.cpp | 255 ++++++++++++++++++ .../canvas-view/canvas-view-rasterize-thread.h | 230 +++++++++++++++++ dali-toolkit/internal/file.list | 1 + 7 files changed, 879 insertions(+), 52 deletions(-) create mode 100644 dali-toolkit/internal/controls/canvas-view/canvas-view-rasterize-thread.cpp create mode 100644 dali-toolkit/internal/controls/canvas-view/canvas-view-rasterize-thread.h diff --git a/automated-tests/src/dali-toolkit/dali-toolkit-test-utils/toolkit-canvas-renderer.cpp b/automated-tests/src/dali-toolkit/dali-toolkit-test-utils/toolkit-canvas-renderer.cpp index 0d6b87e..8db2a84 100644 --- a/automated-tests/src/dali-toolkit/dali-toolkit-test-utils/toolkit-canvas-renderer.cpp +++ b/automated-tests/src/dali-toolkit/dali-toolkit-test-utils/toolkit-canvas-renderer.cpp @@ -52,6 +52,16 @@ public: return true; } + bool IsCanvasChanged() const + { + return true; + } + + bool Rasterize() + { + return true; + } + bool AddDrawable(Dali::CanvasRenderer::Drawable& drawable) { if (!drawable) @@ -138,6 +148,16 @@ bool CanvasRenderer::Commit() return Internal::Adaptor::GetImplementation(*this).Commit(); } +bool CanvasRenderer::IsCanvasChanged() const +{ + return Internal::Adaptor::GetImplementation(*this).IsCanvasChanged(); +} + +bool CanvasRenderer::Rasterize() +{ + return Internal::Adaptor::GetImplementation(*this).Rasterize(); +} + Devel::PixelBuffer CanvasRenderer::GetPixelBuffer() { return Internal::Adaptor::GetImplementation(*this).GetPixelBuffer(); diff --git a/automated-tests/src/dali-toolkit/utc-Dali-CanvasView.cpp b/automated-tests/src/dali-toolkit/utc-Dali-CanvasView.cpp index 1c5128e..46a2643 100644 --- a/automated-tests/src/dali-toolkit/utc-Dali-CanvasView.cpp +++ b/automated-tests/src/dali-toolkit/utc-Dali-CanvasView.cpp @@ -20,9 +20,12 @@ #include #include #include +#include +#include #include +#include #include - +#include using namespace Dali; using namespace Toolkit; @@ -163,8 +166,6 @@ int UtcDaliCanvasViewChangeSizeP(void) DALI_TEST_CHECK( canvasView ); application.GetScene().Add(canvasView); - application.SendNotification(); - application.Render(); canvasView.SetProperty(Actor::Property::SIZE, Vector2(300, 300)); @@ -187,8 +188,6 @@ int UtcDaliCanvasViewSizeN(void) DALI_TEST_CHECK( canvasView ); application.GetScene().Add(canvasView); - application.SendNotification(); - application.Render(); canvasView.SetProperty(Actor::Property::SIZE, Vector2(-999, -999)); @@ -202,3 +201,281 @@ int UtcDaliCanvasViewSizeN(void) END_TEST; } + +int UtcDaliCanvasViewRasterizeTaskP(void) +{ + ToolkitTestApplication application; + + Dali::Toolkit::Internal::CanvasView* dummyInternalCanvasView = new Dali::Toolkit::Internal::CanvasView(Vector2(100,100)); + DALI_TEST_CHECK( dummyInternalCanvasView ); + + Dali::CanvasRenderer dummyCanvasRenderer = Dali::CanvasRenderer::New(Vector2(100, 100)); + DALI_TEST_CHECK( dummyCanvasRenderer ); + + IntrusivePtr task = new Dali::Toolkit::Internal::CanvasRendererRasterizingTask(dummyInternalCanvasView, dummyCanvasRenderer); + DALI_TEST_CHECK( task ); + + END_TEST; +} + +int UtcDaliCanvasViewRasterizeTaskGetCanvasViewP(void) +{ + ToolkitTestApplication application; + + Dali::Toolkit::Internal::CanvasView* dummyInternalCanvasView = new Dali::Toolkit::Internal::CanvasView(Vector2(100,100)); + DALI_TEST_CHECK( dummyInternalCanvasView ); + + Dali::CanvasRenderer dummyCanvasRenderer = Dali::CanvasRenderer::New(Vector2(100, 100)); + DALI_TEST_CHECK( dummyCanvasRenderer ); + + IntrusivePtr task = new Dali::Toolkit::Internal::CanvasRendererRasterizingTask(dummyInternalCanvasView, dummyCanvasRenderer); + DALI_TEST_CHECK( task ); + + DALI_TEST_EQUALS( task->GetCanvasView(), dummyInternalCanvasView, TEST_LOCATION ); + + END_TEST; +} + +int UtcDaliCanvasViewRasterizeTaskGetBufferSizeP(void) +{ + ToolkitTestApplication application; + + Dali::Toolkit::Internal::CanvasView* dummyInternalCanvasView = new Dali::Toolkit::Internal::CanvasView(Vector2(100,100)); + DALI_TEST_CHECK( dummyInternalCanvasView ); + + Dali::CanvasRenderer dummyCanvasRenderer = Dali::CanvasRenderer::New(Vector2(100, 100)); + DALI_TEST_CHECK( dummyCanvasRenderer ); + + IntrusivePtr task = new Dali::Toolkit::Internal::CanvasRendererRasterizingTask(dummyInternalCanvasView, dummyCanvasRenderer); + DALI_TEST_CHECK( task ); + + //There is no rasterized buffer. + DALI_TEST_EQUALS( task->GetBufferSize(), Vector2(0, 0), TEST_LOCATION ); + + END_TEST; +} + +int UtcDaliCanvasViewRasterizeTaskGetPixelDataP(void) +{ + + Dali::Toolkit::Internal::CanvasView* dummyInternalCanvasView = new Dali::Toolkit::Internal::CanvasView(Vector2(100,100)); + DALI_TEST_CHECK( dummyInternalCanvasView ); + + Dali::CanvasRenderer dummyCanvasRenderer = Dali::CanvasRenderer::New(Vector2(100, 100)); + DALI_TEST_CHECK( dummyCanvasRenderer ); + + IntrusivePtr task = new Dali::Toolkit::Internal::CanvasRendererRasterizingTask(dummyInternalCanvasView, dummyCanvasRenderer); + DALI_TEST_CHECK( task ); + + DALI_TEST_EQUALS( task->GetPixelData(), PixelData(), TEST_LOCATION ); + + END_TEST; +} + +int UtcDaliCanvasViewRasterizeThreadP(void) +{ + ToolkitTestApplication application; + + Dali::Toolkit::Internal::CanvasView* dummyInternalCanvasView = new Dali::Toolkit::Internal::CanvasView(Vector2(100,100)); + DALI_TEST_CHECK( dummyInternalCanvasView ); + + Dali::CanvasRenderer dummyCanvasRenderer = Dali::CanvasRenderer::New(Vector2(100, 100)); + DALI_TEST_CHECK( dummyCanvasRenderer ); + + IntrusivePtr task = new Dali::Toolkit::Internal::CanvasRendererRasterizingTask(dummyInternalCanvasView, dummyCanvasRenderer); + DALI_TEST_CHECK( task ); + + Dali::Toolkit::Internal::CanvasViewRasterizeThread *dummyThread = new Dali::Toolkit::Internal::CanvasViewRasterizeThread(); + DALI_TEST_CHECK( dummyThread ); + + END_TEST; +} + +int UtcDaliCanvasViewRasterizeThreadAddTaskP(void) +{ + ToolkitTestApplication application; + + Dali::Toolkit::Internal::CanvasView* dummyInternalCanvasView = new Dali::Toolkit::Internal::CanvasView(Vector2(100,100)); + DALI_TEST_CHECK( dummyInternalCanvasView ); + + Dali::CanvasRenderer dummyCanvasRenderer = Dali::CanvasRenderer::New(Vector2(100, 100)); + DALI_TEST_CHECK( dummyCanvasRenderer ); + + IntrusivePtr task = new Dali::Toolkit::Internal::CanvasRendererRasterizingTask(dummyInternalCanvasView, dummyCanvasRenderer); + DALI_TEST_CHECK( task ); + + IntrusivePtr task2 = new Dali::Toolkit::Internal::CanvasRendererRasterizingTask(dummyInternalCanvasView, dummyCanvasRenderer); + DALI_TEST_CHECK( task2 ); + + Dali::Toolkit::Internal::CanvasViewRasterizeThread *dummyThread = new Dali::Toolkit::Internal::CanvasViewRasterizeThread(); + DALI_TEST_CHECK( dummyThread ); + + dummyThread->AddTask(task); + dummyThread->AddTask(task2); + + END_TEST; +} + +int UtcDaliCanvasViewRasterizeThreadAddRemoveTaskP(void) +{ + ToolkitTestApplication application; + + Dali::Toolkit::Internal::CanvasView* dummyInternalCanvasView = new Dali::Toolkit::Internal::CanvasView(Vector2(100,100)); + DALI_TEST_CHECK( dummyInternalCanvasView ); + + Dali::CanvasRenderer dummyCanvasRenderer = Dali::CanvasRenderer::New(Vector2(100, 100)); + DALI_TEST_CHECK( dummyCanvasRenderer ); + + IntrusivePtr task = new Dali::Toolkit::Internal::CanvasRendererRasterizingTask(dummyInternalCanvasView, dummyCanvasRenderer); + DALI_TEST_CHECK( task ); + + Dali::Toolkit::Internal::CanvasViewRasterizeThread *dummyThread = new Dali::Toolkit::Internal::CanvasViewRasterizeThread(); + DALI_TEST_CHECK( dummyThread ); + + dummyThread->AddTask(task); + + dummyThread->RemoveTask(dummyInternalCanvasView); + + END_TEST; +} + +int UtcDaliCanvasViewRasterizeThreadApplyRasterizedP(void) +{ + ToolkitTestApplication application; + + Dali::Toolkit::Internal::CanvasView* dummyInternalCanvasView = new Dali::Toolkit::Internal::CanvasView(Vector2(100,100)); + DALI_TEST_CHECK( dummyInternalCanvasView ); + + Dali::CanvasRenderer dummyCanvasRenderer = Dali::CanvasRenderer::New(Vector2(100, 100)); + DALI_TEST_CHECK( dummyCanvasRenderer ); + + IntrusivePtr task = new Dali::Toolkit::Internal::CanvasRendererRasterizingTask(dummyInternalCanvasView, dummyCanvasRenderer); + DALI_TEST_CHECK( task ); + + Dali::Toolkit::Internal::CanvasViewRasterizeThread *dummyThread = new Dali::Toolkit::Internal::CanvasViewRasterizeThread(); + DALI_TEST_CHECK( dummyThread ); + + dummyThread->AddTask(task); + + dummyThread->ApplyRasterized(); + + END_TEST; +} + +int UtcDaliCanvasViewRasterizeThreadTerminateThreadP(void) +{ + ToolkitTestApplication application; + + Dali::Toolkit::Internal::CanvasView* dummyInternalCanvasView = new Dali::Toolkit::Internal::CanvasView(Vector2(100,100)); + DALI_TEST_CHECK( dummyInternalCanvasView ); + + Dali::CanvasRenderer dummyCanvasRenderer = Dali::CanvasRenderer::New(Vector2(100, 100)); + DALI_TEST_CHECK( dummyCanvasRenderer ); + + IntrusivePtr task = new Dali::Toolkit::Internal::CanvasRendererRasterizingTask(dummyInternalCanvasView, dummyCanvasRenderer); + DALI_TEST_CHECK( task ); + + Dali::Toolkit::Internal::CanvasViewRasterizeThread *dummyThread = new Dali::Toolkit::Internal::CanvasViewRasterizeThread(); + DALI_TEST_CHECK( dummyThread ); + + dummyThread->AddTask(task); + + Dali::Toolkit::Internal::CanvasViewRasterizeThread::TerminateThread(dummyThread); + + END_TEST; +} + +PixelData CreatePixelData( unsigned int width, unsigned int height ) +{ + unsigned int bufferSize = width*height*Pixel::GetBytesPerPixel( Pixel::RGBA8888 ); + + unsigned char* buffer= reinterpret_cast( malloc( bufferSize ) ); + PixelData pixelData = PixelData::New( buffer, bufferSize, width, height, Pixel::RGBA8888, PixelData::FREE ); + + return pixelData; +} + +int UtcDaliCanvasViewRasterizeThreadCallProcessP(void) +{ + ToolkitTestApplication application; + + Dali::Toolkit::Internal::CanvasView* dummyInternalCanvasView = new Dali::Toolkit::Internal::CanvasView(Vector2(100,100)); + DALI_TEST_CHECK( dummyInternalCanvasView ); + + Dali::CanvasRenderer dummyCanvasRenderer = Dali::CanvasRenderer::New(Vector2(100, 100)); + DALI_TEST_CHECK( dummyCanvasRenderer ); + + IntrusivePtr task = new Dali::Toolkit::Internal::CanvasRendererRasterizingTask(dummyInternalCanvasView, dummyCanvasRenderer); + DALI_TEST_CHECK( task ); + + Dali::Toolkit::Internal::CanvasViewRasterizeThread *dummyThread = new Dali::Toolkit::Internal::CanvasViewRasterizeThread(); + DALI_TEST_CHECK( dummyThread ); + + dummyThread->AddTask(task); + + dummyThread->Process(false); + + END_TEST; +} + +int UtcDaliCanvasViewRasterizeThreadRasterizationCompletedSignalP(void) +{ + ToolkitTestApplication application; + + Dali::Toolkit::Internal::CanvasView* dummyInternalCanvasView = new Dali::Toolkit::Internal::CanvasView(Vector2(100,100)); + DALI_TEST_CHECK( dummyInternalCanvasView ); + + Dali::CanvasRenderer dummyCanvasRenderer = Dali::CanvasRenderer::New(Vector2(100, 100)); + DALI_TEST_CHECK( dummyCanvasRenderer ); + + IntrusivePtr task = new Dali::Toolkit::Internal::CanvasRendererRasterizingTask(dummyInternalCanvasView, dummyCanvasRenderer); + DALI_TEST_CHECK( task ); + + Dali::Toolkit::Internal::CanvasViewRasterizeThread *dummyThread = new Dali::Toolkit::Internal::CanvasViewRasterizeThread(); + DALI_TEST_CHECK( dummyThread ); + + dummyThread->AddTask(task); + + dummyThread->Process(false); + + PixelData pixelData = CreatePixelData( 100, 100 ); + + dummyThread->RasterizationCompletedSignal().Connect(dummyInternalCanvasView, &Dali::Toolkit::Internal::CanvasView::ApplyRasterizedImage); + dummyThread->RasterizationCompletedSignal().Emit(pixelData); + + application.SendNotification(); + application.Render(); + + END_TEST; +} + +int UtcDaliCanvasViewSetSizeAndAddDrawable(void) +{ + ToolkitTestApplication application; + + CanvasView canvasView = CanvasView::New(Vector2(100,100)); + DALI_TEST_CHECK( canvasView ); + + application.GetScene().Add(canvasView); + + canvasView.SetProperty(Actor::Property::SIZE, Vector2(300, 300)); + + application.SendNotification(); + application.Render(); + + Dali::CanvasRenderer::Shape shape = Dali::CanvasRenderer::Shape::New(); + + shape.AddRect(Rect(10, 10, 10, 10), Vector2(0, 0)); + + canvasView.AddDrawable(shape); + + application.SendNotification(); + application.Render(); + + DALI_TEST_EQUALS( Test::WaitForEventThreadTrigger( 1 ), true, TEST_LOCATION ); + + application.SendNotification(); + application.Render(); + + END_TEST; +} diff --git a/dali-toolkit/internal/controls/canvas-view/canvas-view-impl.cpp b/dali-toolkit/internal/controls/canvas-view/canvas-view-impl.cpp index 6d310cf..0132e3c 100644 --- a/dali-toolkit/internal/controls/canvas-view/canvas-view-impl.cpp +++ b/dali-toolkit/internal/controls/canvas-view/canvas-view-impl.cpp @@ -25,8 +25,8 @@ #include // INTERNAL INCLUDES -#include #include +#include #include #include #include @@ -54,12 +54,21 @@ CanvasView::CanvasView(const Vector2& viewBox) : Control(ControlBehaviour(CONTROL_BEHAVIOUR_DEFAULT)), mCanvasRenderer(CanvasRenderer::New(viewBox)), mTexture(), - mChanged(false) + mTextureSet(), + mSize(viewBox), + mCanvasViewRasterizeThread(nullptr) { } CanvasView::~CanvasView() { + if(mCanvasViewRasterizeThread) + { + mCanvasViewRasterizeThread->RemoveTask(this); + + CanvasViewRasterizeThread::TerminateThread(mCanvasViewRasterizeThread); + } + if(Adaptor::IsAvailable()) { Adaptor::Get().UnregisterProcessor(*this); @@ -97,12 +106,11 @@ void CanvasView::OnInitialize() void CanvasView::OnRelayout(const Vector2& size, RelayoutContainer& container) { if(!mCanvasRenderer || - mCanvasRenderer.GetSize() == size || !mCanvasRenderer.SetSize(size)) { return; } - mChanged = true; + mSize = size; } void CanvasView::OnSizeSet(const Vector3& targetSize) @@ -110,58 +118,83 @@ void CanvasView::OnSizeSet(const Vector3& targetSize) Control::OnSizeSet(targetSize); if(!mCanvasRenderer || - mCanvasRenderer.GetSize() == Vector2(targetSize) || !mCanvasRenderer.SetSize(Vector2(targetSize))) { return; } - mChanged = true; + mSize.width = targetSize.width; + mSize.height = targetSize.height; } void CanvasView::Process(bool postProcessor) { - if(!mCanvasRenderer) + if(mCanvasRenderer && mCanvasRenderer.IsCanvasChanged() && mSize.width > 0 && mSize.height > 0) { - return; + AddRasterizationTask(); } - Commit(); } -void CanvasView::Commit() +void CanvasView::AddRasterizationTask() { - if(mCanvasRenderer && mCanvasRenderer.Commit()) + CanvasRendererRasterizingTaskPtr newTask = new CanvasRendererRasterizingTask(this, mCanvasRenderer); + + if(!mCanvasViewRasterizeThread) { - Devel::PixelBuffer pixbuf = mCanvasRenderer.GetPixelBuffer(); - auto width = pixbuf.GetWidth(); - auto height = pixbuf.GetHeight(); + mCanvasViewRasterizeThread = new CanvasViewRasterizeThread(); + mCanvasViewRasterizeThread->RasterizationCompletedSignal().Connect(this, &CanvasView::ApplyRasterizedImage); + mCanvasViewRasterizeThread->Start(); + } - Dali::PixelData pixelData = Devel::PixelBuffer::Convert(pixbuf); - if(!pixelData) - { - return; - } + if(mCanvasRenderer.Commit()) + { + mCanvasViewRasterizeThread->AddTask(newTask); + } +} - if(!mTexture || mChanged) - { - mTexture = Texture::New(TextureType::TEXTURE_2D, Dali::Pixel::RGBA8888, width, height); - mTexture.Upload(pixelData); - TextureSet textureSet = TextureSet::New(); - textureSet.SetTexture(0, mTexture); - Geometry geometry = VisualFactoryCache::CreateQuadGeometry(); - Shader shader = Shader::New(SHADER_CANVAS_VIEW_VERT, SHADER_CANVAS_VIEW_FRAG); - Renderer renderer = Renderer::New(geometry, shader); - renderer.SetTextures(textureSet); - renderer.SetProperty(Renderer::Property::BLEND_PRE_MULTIPLIED_ALPHA, true); - - Self().AddRenderer(renderer); - mChanged = false; - } - else +void CanvasView::ApplyRasterizedImage(PixelData rasterizedPixelData) +{ + if(rasterizedPixelData) + { + auto rasterizedPixelDataWidth = rasterizedPixelData.GetWidth(); + auto rasterizedPixelDataHeight = rasterizedPixelData.GetHeight(); + + if(rasterizedPixelDataWidth > 0 && rasterizedPixelDataHeight > 0) { - //Update texture - mTexture.Upload(pixelData); + if(!mTexture || mTexture.GetWidth() != rasterizedPixelDataWidth || mTexture.GetHeight() != rasterizedPixelDataHeight) + { + mTexture = Texture::New(TextureType::TEXTURE_2D, Dali::Pixel::RGBA8888, rasterizedPixelDataWidth, rasterizedPixelDataHeight); + mTexture.Upload(rasterizedPixelData); + + if(!mTextureSet) + { + mTextureSet = TextureSet::New(); + Geometry geometry = VisualFactoryCache::CreateQuadGeometry(); + Shader shader = Shader::New(SHADER_CANVAS_VIEW_VERT, SHADER_CANVAS_VIEW_FRAG); + Renderer renderer = Renderer::New(geometry, shader); + renderer.SetTextures(mTextureSet); + renderer.SetProperty(Renderer::Property::BLEND_PRE_MULTIPLIED_ALPHA, true); + + Actor actor = Self(); + if(actor) + { + actor.AddRenderer(renderer); + } + } + mTextureSet.SetTexture(0, mTexture); + } + else + { + //Update texture + mTexture.Upload(rasterizedPixelData); + } } } + + //If there are accumulated changes to CanvasRenderer during Rasterize, Rasterize once again. + if(mCanvasRenderer && mCanvasRenderer.IsCanvasChanged()) + { + AddRasterizationTask(); + } } bool CanvasView::AddDrawable(Dali::CanvasRenderer::Drawable& drawable) diff --git a/dali-toolkit/internal/controls/canvas-view/canvas-view-impl.h b/dali-toolkit/internal/controls/canvas-view/canvas-view-impl.h index b2d5775..1a468b0 100644 --- a/dali-toolkit/internal/controls/canvas-view/canvas-view-impl.h +++ b/dali-toolkit/internal/controls/canvas-view/canvas-view-impl.h @@ -33,10 +33,11 @@ namespace Dali { namespace Toolkit { -class CanvasView; - namespace Internal { +class CanvasView; +class CanvasViewRasterizeThread; + class CanvasView : public Control, public Integration::Processor { public: @@ -78,27 +79,37 @@ private: // From Control */ void OnInitialize() override; -protected: // Implementation of Processor + /** + * @bried Rasterize the canvas, and add it to the view. + * + * @param[in] size The target size of the canvas view rasterization. + */ + void AddRasterizationTask(); + +protected: /** * @copydoc Dali::Integration::Processor::Process() */ void Process(bool postProcessor) override; -private: +public: /** - * @brief Draw drawables added to CanvasView on inner canvas. - * Then make that buffer into a texture and add it to renderer. + * @bried Apply the rasterized image to the canvas view + * + * @param[in] rasterizedPixelData The pixel buffer with the rasterized pixels */ - void Commit(); + void ApplyRasterizedImage(PixelData rasterizedPixelData); private: CanvasView(const CanvasView&) = delete; CanvasView& operator=(const CanvasView&) = delete; private: - CanvasRenderer mCanvasRenderer; - Dali::Texture mTexture; - bool mChanged; + CanvasRenderer mCanvasRenderer; + Dali::Texture mTexture; + TextureSet mTextureSet; + Vector2 mSize; + CanvasViewRasterizeThread* mCanvasViewRasterizeThread; }; } // namespace Internal diff --git a/dali-toolkit/internal/controls/canvas-view/canvas-view-rasterize-thread.cpp b/dali-toolkit/internal/controls/canvas-view/canvas-view-rasterize-thread.cpp new file mode 100644 index 0000000..97835c5 --- /dev/null +++ b/dali-toolkit/internal/controls/canvas-view/canvas-view-rasterize-thread.cpp @@ -0,0 +1,255 @@ +/* + * Copyright (c) 2021 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// CLASS HEADER +#include "canvas-view-rasterize-thread.h" + +// EXTERNAL INCLUDES +#include +#include + +namespace Dali +{ +namespace Toolkit +{ +namespace Internal +{ +CanvasRendererRasterizingTask::CanvasRendererRasterizingTask(CanvasView* canvasView, CanvasRenderer canvasRenderer) +: mCanvasView(canvasView), + mCanvasRenderer(canvasRenderer), + mPixelData(PixelData()), + mBufferSize(0, 0) +{ +} + +bool CanvasRendererRasterizingTask::Rasterize() +{ + if(mCanvasRenderer && mCanvasRenderer.Rasterize()) + { + Devel::PixelBuffer pixbuf = mCanvasRenderer.GetPixelBuffer(); + auto width = pixbuf.GetWidth(); + auto height = pixbuf.GetHeight(); + if(width > 0 && height > 0) + { + mBufferSize.width = width; + mBufferSize.height = height; + + mPixelData = Devel::PixelBuffer::Convert(pixbuf); + return true; + } + } + return false; +} + +CanvasView* CanvasRendererRasterizingTask::GetCanvasView() const +{ + return mCanvasView.Get(); +} + +PixelData CanvasRendererRasterizingTask::GetPixelData() const +{ + return mPixelData; +} + +Vector2 CanvasRendererRasterizingTask::GetBufferSize() const +{ + return mBufferSize; +} + +CanvasViewRasterizeThread::CanvasViewRasterizeThread() +: mTrigger(new EventThreadCallback(MakeCallback(this, &CanvasViewRasterizeThread::ApplyRasterized))), + mLogFactory(Dali::Adaptor::Get().GetLogFactory()), + mProcessorRegistered(false), + mRasterizationCompletedSignal() +{ +} + +CanvasViewRasterizeThread::~CanvasViewRasterizeThread() +{ + if(mProcessorRegistered && Adaptor::IsAvailable()) + { + Adaptor::Get().UnregisterProcessor(*this); + } +} + +void CanvasViewRasterizeThread::TerminateThread(CanvasViewRasterizeThread*& thread) +{ + if(thread) + { + // add an empty task would stop the thread from conditional wait. + thread->AddTask(CanvasRendererRasterizingTaskPtr()); + // stop the thread + thread->Join(); + // delete the thread + delete thread; + thread = NULL; + } +} + +void CanvasViewRasterizeThread::AddTask(CanvasRendererRasterizingTaskPtr task) +{ + bool wasEmpty = false; + + { + // Lock while adding task to the queue + ConditionalWait::ScopedLock lock(mConditionalWait); + wasEmpty = mRasterizeTasks.empty(); + if(!wasEmpty && task != NULL) + { + // 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::iterator it = mRasterizeTasks.begin(), endIt = mRasterizeTasks.end(); it != endIt; ++it) + { + if((*it) && (*it)->GetCanvasView() == task->GetCanvasView()) //Need + { + mRasterizeTasks.erase(it); + break; + } + } + } + mRasterizeTasks.push_back(task); + + if(!mProcessorRegistered && Adaptor::IsAvailable()) + { + Adaptor::Get().RegisterProcessor(*this); + mProcessorRegistered = true; + } + } + + if(wasEmpty) + { + // wake up the image loading thread + mConditionalWait.Notify(); + } +} + +CanvasRendererRasterizingTaskPtr CanvasViewRasterizeThread::NextCompletedTask() +{ + // Lock while popping task out from the queue + Mutex::ScopedLock lock(mMutex); + + if(mCompletedTasks.empty()) + { + return CanvasRendererRasterizingTaskPtr(); + } + + std::vector::iterator next = mCompletedTasks.begin(); + CanvasRendererRasterizingTaskPtr nextTask = *next; + mCompletedTasks.erase(next); + + return nextTask; +} + +void CanvasViewRasterizeThread::RemoveTask(CanvasView* canvasView) +{ + // Lock while remove task from the queue + ConditionalWait::ScopedLock lock(mConditionalWait); + if(!mRasterizeTasks.empty()) + { + for(std::vector::iterator it = mRasterizeTasks.begin(), endIt = mRasterizeTasks.end(); it != endIt; ++it) + { + if((*it) && (*it)->GetCanvasView() == canvasView) + { + mRasterizeTasks.erase(it); + break; + } + } + } + + UnregisterProcessor(); +} + +CanvasRendererRasterizingTaskPtr CanvasViewRasterizeThread::NextTaskToProcess() +{ + // Lock while popping task out from the queue + ConditionalWait::ScopedLock lock(mConditionalWait); + + // conditional wait + while(mRasterizeTasks.empty()) + { + mConditionalWait.Wait(lock); + } + + // pop out the next task from the queue + std::vector::iterator next = mRasterizeTasks.begin(); + CanvasRendererRasterizingTaskPtr nextTask = *next; + mRasterizeTasks.erase(next); + + return nextTask; +} + +void CanvasViewRasterizeThread::AddCompletedTask(CanvasRendererRasterizingTaskPtr task) +{ + // Lock while adding task to the queue + Mutex::ScopedLock lock(mMutex); + mCompletedTasks.push_back(task); + + // wake up the main thread + mTrigger->Trigger(); +} + +void CanvasViewRasterizeThread::Run() +{ + SetThreadName("CanvasViewThread"); + mLogFactory.InstallLogFunction(); + + while(CanvasRendererRasterizingTaskPtr task = NextTaskToProcess()) + { + if(task->Rasterize()) + { + AddCompletedTask(task); + } + } +} + +void CanvasViewRasterizeThread::ApplyRasterized() +{ + while(CanvasRendererRasterizingTaskPtr task = NextCompletedTask()) + { + RasterizationCompletedSignal().Emit(task->GetPixelData()); + } + + UnregisterProcessor(); +} + +void CanvasViewRasterizeThread::Process(bool postProcessor) +{ + ApplyRasterized(); +} + +CanvasViewRasterizeThread::RasterizationCompletedSignalType& CanvasViewRasterizeThread::RasterizationCompletedSignal() +{ + return mRasterizationCompletedSignal; +} + +void CanvasViewRasterizeThread::UnregisterProcessor() +{ + if(mProcessorRegistered) + { + if(mRasterizeTasks.empty() && mCompletedTasks.empty() && Adaptor::IsAvailable()) + { + Adaptor::Get().UnregisterProcessor(*this); + mProcessorRegistered = false; + } + } +} + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/controls/canvas-view/canvas-view-rasterize-thread.h b/dali-toolkit/internal/controls/canvas-view/canvas-view-rasterize-thread.h new file mode 100644 index 0000000..0c16fd9 --- /dev/null +++ b/dali-toolkit/internal/controls/canvas-view/canvas-view-rasterize-thread.h @@ -0,0 +1,230 @@ +#ifndef DALI_TOOLKIT_CANVAS_VIEW_RASTERIZE_THREAD_H +#define DALI_TOOLKIT_CANVAS_VIEW_RASTERIZE_THREAD_H + +/* + * Copyright (c) 2021 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// EXTERNAL INCLUDES +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// INTERNAL INCLUDES +#include + +namespace Dali +{ +namespace Toolkit +{ +namespace Internal +{ +using CanvasViewPtr = IntrusivePtr; +class CanvasRendererRasterizingTask; +using CanvasRendererRasterizingTaskPtr = IntrusivePtr; + +/** + * The canvasview rasterizing tasks to be processed in the worker thread. + * + * Life cycle of a rasterizing task is as follows: + * 1. Created by CanvasView in the main thread + * 2. Queued in the worked thread waiting to be processed. + * 3. If this task gets its turn to do the rasterization, it triggers main thread to apply the rasterized image to material then been deleted in main thread call back. + * Or if this task is been removed before its turn to be processed, it then been deleted in the worker thread. + */ +class CanvasRendererRasterizingTask : public RefObject +{ +public: + /** + * Constructor + * @param[in] canvasRenderer The renderer which the rasterized canvas to be applied. + */ + CanvasRendererRasterizingTask(CanvasView* canvasView, CanvasRenderer canvasRenderer); + + /** + * Destructor. + */ + ~CanvasRendererRasterizingTask() = default; + + /** + * Do the rasterization with the mRasterizer. + * @return Returns True when it's successful. False otherwise. + */ + bool Rasterize(); + + /** + * Get the CanvasView + * @return The CanvasView pointer. + */ + CanvasView* GetCanvasView() const; + + /** + * Get the rasterization result. + * @return The pixel data with the rasterized pixels. + */ + PixelData GetPixelData() const; + + /** + * Get size of rasterization result. + * @return The size of the pixel data. + */ + Vector2 GetBufferSize() const; + +private: + // Undefined + CanvasRendererRasterizingTask(const CanvasRendererRasterizingTask& task); + + // Undefined + CanvasRendererRasterizingTask& operator=(const CanvasRendererRasterizingTask& task); + +private: + CanvasViewPtr mCanvasView; + CanvasRenderer mCanvasRenderer; + PixelData mPixelData; + Vector2 mBufferSize; +}; + +/** + * The worker thread for CanvasView rasterization. + */ +class CanvasViewRasterizeThread : public Thread, Integration::Processor +{ +public: + /// @brief ApplyRasterizedImage Event signal type + using RasterizationCompletedSignalType = Signal; + +public: + /** + * Constructor. + * + * @param[in] trigger The trigger to wake up the main thread. + */ + CanvasViewRasterizeThread(); + + /** + * Terminate the CanvasView rasterize thread, join and delete. + * + * @param[in] thread The rasterize thread. + */ + static void TerminateThread(CanvasViewRasterizeThread*& thread); + + /** + * Add a rasterization task into the waiting queue, called by main thread. + * + * @param[in] task The task added to the queue. + */ + void AddTask(CanvasRendererRasterizingTaskPtr task); + + /** + * Remove the task with the given CanvasView from the waiting queue, called by main thread. + * + * Typically called when the actor is put off stage, so the renderer is not needed anymore. + * + * @param[in] canvasView The CanvasView pointer. + */ + void RemoveTask(CanvasView* canvasView); + + /** + * Applies the rasterized image to material + */ + void ApplyRasterized(); + + /** + * @copydoc Dali::Integration::Processor::Process() + */ + void Process(bool postProcessor) override; + + /** + * @brief This signal is emitted when rasterized image is applied. + * + * @return The signal to connect to + */ + RasterizationCompletedSignalType& RasterizationCompletedSignal(); + +private: + /** + * Pop the next task out from the queue. + * + * @return The next task to be processed. + */ + CanvasRendererRasterizingTaskPtr NextTaskToProcess(); + + /** + * Pop the next task out from the completed queue, called by main thread. + * + * @return The next task in the completed queue. + */ + CanvasRendererRasterizingTaskPtr NextCompletedTask(); + + /** + * Add a task in to the queue + * + * @param[in] task The task added to the queue. + */ + void AddCompletedTask(CanvasRendererRasterizingTaskPtr task); + + /** + * @brief Unregister a previously registered processor + * + */ + void UnregisterProcessor(); + +protected: + /** + * Destructor. + */ + ~CanvasViewRasterizeThread() override; + + /** + * The entry function of the worker thread. + * It fetches task from the Queue, rasterizes the image and apply to the renderer. + */ + void Run() override; + +private: + // Undefined + CanvasViewRasterizeThread(const CanvasViewRasterizeThread& thread); + + // Undefined + CanvasViewRasterizeThread& operator=(const CanvasViewRasterizeThread& thread); + +private: + std::vector mRasterizeTasks; //The queue of the tasks waiting to rasterize the CanvasView. + std::vector mCompletedTasks; //The queue of the tasks with the SVG rasterization completed + + ConditionalWait mConditionalWait; + Dali::Mutex mMutex; + std::unique_ptr mTrigger; + const Dali::LogFactoryInterface& mLogFactory; + bool mProcessorRegistered; + RasterizationCompletedSignalType mRasterizationCompletedSignal; +}; + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali + +#endif // DALI_TOOLKIT_CANVAS_VIEW_RASTERIZE_THREAD_H diff --git a/dali-toolkit/internal/file.list b/dali-toolkit/internal/file.list index a661176..33b594a 100644 --- a/dali-toolkit/internal/file.list +++ b/dali-toolkit/internal/file.list @@ -62,6 +62,7 @@ SET( toolkit_src_files ${toolkit_src_dir}/controls/buttons/radio-button-impl.cpp ${toolkit_src_dir}/controls/buttons/toggle-button-impl.cpp ${toolkit_src_dir}/controls/canvas-view/canvas-view-impl.cpp + ${toolkit_src_dir}/controls/canvas-view/canvas-view-rasterize-thread.cpp ${toolkit_src_dir}/controls/control/control-data-impl.cpp ${toolkit_src_dir}/controls/control/control-debug.cpp ${toolkit_src_dir}/controls/control/control-renderers.cpp -- 2.7.4