Add CanvasView thread rasterization 50/259050/33
authorJunsuChoi <jsuya.choi@samsung.com>
Mon, 31 May 2021 05:44:59 +0000 (14:44 +0900)
committerJunsuChoi <jsuya.choi@samsung.com>
Mon, 26 Jul 2021 08:28:23 +0000 (17:28 +0900)
When a rasterize request occurs, create a task and rasterize on thread.

Change-Id: I8f9117490fb242c91d0124759e0a1f7df9ed701a

automated-tests/src/dali-toolkit/dali-toolkit-test-utils/toolkit-canvas-renderer.cpp
automated-tests/src/dali-toolkit/utc-Dali-CanvasView.cpp
dali-toolkit/internal/controls/canvas-view/canvas-view-impl.cpp
dali-toolkit/internal/controls/canvas-view/canvas-view-impl.h
dali-toolkit/internal/controls/canvas-view/canvas-view-rasterize-thread.cpp [new file with mode: 0644]
dali-toolkit/internal/controls/canvas-view/canvas-view-rasterize-thread.h [new file with mode: 0644]
dali-toolkit/internal/file.list

index 0d6b87e..8db2a84 100644 (file)
@@ -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();
index 1c5128e..46a2643 100644 (file)
 #include <dali-toolkit-test-suite-utils.h>
 #include <dali-toolkit/dali-toolkit.h>
 #include <test-application.h>
+#include <dali/public-api/images/pixel-data.h>
+#include <toolkit-event-thread-callback.h>
 #include <dali-toolkit/devel-api/controls/canvas-view/canvas-view.h>
+#include <dali/devel-api/adaptor-framework/canvas-renderer.h>
 #include <dali/devel-api/adaptor-framework/canvas-renderer-shape.h>
-
+#include <dali-toolkit/internal/controls/canvas-view/canvas-view-rasterize-thread.h>
 
 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<Dali::Toolkit::Internal::CanvasRendererRasterizingTask> 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<Dali::Toolkit::Internal::CanvasRendererRasterizingTask> 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<Dali::Toolkit::Internal::CanvasRendererRasterizingTask> 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<Dali::Toolkit::Internal::CanvasRendererRasterizingTask> 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<Dali::Toolkit::Internal::CanvasRendererRasterizingTask> 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<Dali::Toolkit::Internal::CanvasRendererRasterizingTask> task = new Dali::Toolkit::Internal::CanvasRendererRasterizingTask(dummyInternalCanvasView, dummyCanvasRenderer);
+  DALI_TEST_CHECK( task );
+
+  IntrusivePtr<Dali::Toolkit::Internal::CanvasRendererRasterizingTask> 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<Dali::Toolkit::Internal::CanvasRendererRasterizingTask> 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<Dali::Toolkit::Internal::CanvasRendererRasterizingTask> 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<Dali::Toolkit::Internal::CanvasRendererRasterizingTask> 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<unsigned char*>( 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<Dali::Toolkit::Internal::CanvasRendererRasterizingTask> 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<Dali::Toolkit::Internal::CanvasRendererRasterizingTask> 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<float>(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;
+}
index 6d310cf..0132e3c 100644 (file)
@@ -25,8 +25,8 @@
 #include <dali/public-api/object/type-registry.h>
 
 // INTERNAL INCLUDES
-#include <dali-toolkit/devel-api/controls/canvas-view/canvas-view.h>
 #include <dali-toolkit/devel-api/controls/control-devel.h>
+#include <dali-toolkit/internal/controls/canvas-view/canvas-view-rasterize-thread.h>
 #include <dali-toolkit/internal/controls/control/control-data-impl.h>
 #include <dali-toolkit/internal/graphics/builtin-shader-extern-gen.h>
 #include <dali-toolkit/internal/visuals/visual-factory-cache.h>
@@ -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)
index b2d5775..1a468b0 100644 (file)
@@ -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 (file)
index 0000000..97835c5
--- /dev/null
@@ -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 <dali/devel-api/adaptor-framework/thread-settings.h>
+#include <dali/integration-api/adaptor-framework/adaptor.h>
+
+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<CanvasRendererRasterizingTaskPtr>::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<CanvasRendererRasterizingTaskPtr>::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<CanvasRendererRasterizingTaskPtr>::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<CanvasRendererRasterizingTaskPtr>::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 (file)
index 0000000..0c16fd9
--- /dev/null
@@ -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 <dali/devel-api/adaptor-framework/canvas-renderer.h>
+#include <dali/devel-api/adaptor-framework/event-thread-callback.h>
+#include <dali/devel-api/threading/conditional-wait.h>
+#include <dali/devel-api/threading/mutex.h>
+#include <dali/devel-api/threading/thread.h>
+#include <dali/integration-api/adaptor-framework/log-factory-interface.h>
+#include <dali/public-api/common/intrusive-ptr.h>
+#include <dali/public-api/images/pixel-data.h>
+#include <dali/public-api/object/ref-object.h>
+#include <dali/public-api/rendering/texture-set.h>
+#include <memory>
+#include <vector>
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/internal/controls/canvas-view/canvas-view-impl.h>
+
+namespace Dali
+{
+namespace Toolkit
+{
+namespace Internal
+{
+using CanvasViewPtr = IntrusivePtr<CanvasView>;
+class CanvasRendererRasterizingTask;
+using CanvasRendererRasterizingTaskPtr = IntrusivePtr<CanvasRendererRasterizingTask>;
+
+/**
+ * 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<void(PixelData)>;
+
+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<CanvasRendererRasterizingTaskPtr> mRasterizeTasks; //The queue of the tasks waiting to rasterize the CanvasView.
+  std::vector<CanvasRendererRasterizingTaskPtr> mCompletedTasks; //The queue of the tasks with the SVG rasterization completed
+
+  ConditionalWait                      mConditionalWait;
+  Dali::Mutex                          mMutex;
+  std::unique_ptr<EventThreadCallback> mTrigger;
+  const Dali::LogFactoryInterface&     mLogFactory;
+  bool                                 mProcessorRegistered;
+  RasterizationCompletedSignalType     mRasterizationCompletedSignal;
+};
+
+} // namespace Internal
+
+} // namespace Toolkit
+
+} // namespace Dali
+
+#endif // DALI_TOOLKIT_CANVAS_VIEW_RASTERIZE_THREAD_H
index a661176..33b594a 100644 (file)
@@ -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