[Tizen] Keep rendered buffer in framebuffer 74/315574/1 accepted/tizen/9.0/unified/20241207.044225
authorSeungho Baek <sbsh.baek@samsung.com>
Fri, 15 Nov 2024 06:55:34 +0000 (15:55 +0900)
committerSeungho Baek <sbsh.baek@samsung.com>
Tue, 3 Dec 2024 06:58:26 +0000 (15:58 +0900)
 - Users can use RenderTask API to request and to use the buffer.

Change-Id: Iaf63a60f246df8dc48f152bc8a4b4c0b68cd1e1b
Signed-off-by: Seungho Baek <sbsh.baek@samsung.com>
15 files changed:
automated-tests/src/dali/dali-test-suite-utils/test-graphics-command-buffer.h
automated-tests/src/dali/utc-Dali-RenderTask.cpp
dali/graphics-api/graphics-command-buffer.h
dali/graphics-api/graphics-types.h
dali/internal/event/render-tasks/render-task-impl.cpp
dali/internal/event/render-tasks/render-task-impl.h
dali/internal/event/rendering/frame-buffer-impl.cpp
dali/internal/event/rendering/frame-buffer-impl.h
dali/internal/render/common/render-manager.cpp
dali/internal/render/renderers/render-frame-buffer.cpp
dali/internal/render/renderers/render-frame-buffer.h
dali/internal/update/manager/update-manager.cpp
dali/internal/update/manager/update-manager.h
dali/public-api/render-tasks/render-task.cpp
dali/public-api/render-tasks/render-task.h

index 9df2d0fafca901a7949ac785976274eb2fadffaf..147246f44e8001d97a347ddaeb9abc1e5894104f 100644 (file)
@@ -834,6 +834,10 @@ public:
     mCallStack.PushCall("EndRenderPass", namedParams.str(), namedParams);
   }
 
+  void ReadPixels(uint8_t* buffer) override
+  {
+  }
+
   void ExecuteCommandBuffers(std::vector<const CommandBuffer*>&& commandBuffers) override
   {
     mCommands.emplace_back();
index db59e42577f836f50dd93c9dded86deca3ba8e8b..401ffbfd4ad115246e7a7bf0e9cc5b8b6905c9e6 100644 (file)
@@ -5319,3 +5319,59 @@ int UtcDaliRenderTaskDestructWorkerThreadN(void)
 
   END_TEST;
 }
+
+int UtcDaliRenderTaskKeepRenderResult(void)
+{
+  TestApplication application;
+
+  tet_infoline("Testing RenderTask::SignalFinished()");
+
+  CameraActor offscreenCameraActor = CameraActor::New();
+
+  application.GetScene().Add(offscreenCameraActor);
+
+  Texture image     = CreateTexture(TextureType::TEXTURE_2D, Pixel::RGBA8888, 10, 10);
+  Actor   rootActor = CreateRenderableActor(image);
+  rootActor.SetProperty(Actor::Property::SIZE, Vector2(10.0f, 10.0f));
+  application.GetScene().Add(rootActor);
+
+  RenderTaskList          taskList           = application.GetScene().GetRenderTaskList();
+  Texture                 frameBufferTexture = Texture::New(TextureType::TEXTURE_2D, Pixel::Format::RGBA8888, 10, 10);
+  FrameBuffer             frameBuffer        = FrameBuffer::New(frameBufferTexture.GetWidth(), frameBufferTexture.GetHeight());
+  frameBuffer.AttachColorTexture(frameBufferTexture);
+
+  RenderTask newTask = taskList.CreateTask();
+  newTask.SetCameraActor(offscreenCameraActor);
+  newTask.SetSourceActor(rootActor);
+  newTask.SetInputEnabled(false);
+  newTask.SetClearColor(Vector4(0.f, 0.f, 0.f, 0.f));
+  newTask.SetClearEnabled(true);
+  newTask.SetExclusive(true);
+  newTask.SetRefreshRate(RenderTask::REFRESH_ONCE);
+  newTask.SetFrameBuffer(frameBuffer);
+  newTask.KeepRenderResult();
+
+  bool               finished = false;
+  RenderTaskFinished renderTaskFinished(finished);
+  newTask.FinishedSignal().Connect(&application, renderTaskFinished);
+
+  DALI_TEST_CHECK(!newTask.GetRenderResult());
+
+  // Flush the queue and render.
+  application.SendNotification();
+  application.Render();
+  application.Render();
+  application.SendNotification();
+  DALI_TEST_CHECK(finished);
+  DALI_TEST_CHECK(newTask.GetRenderResult());
+
+  newTask.ClearRenderResult();
+
+  DALI_TEST_CHECK(newTask.GetRenderResult());
+
+  application.SendNotification();
+  application.Render();
+  DALI_TEST_CHECK(!newTask.GetRenderResult());
+
+  END_TEST;
+}
index 34eec9a3688dd0de9558de827a188f13a2d8de4b..a85db34ddac05a75dca38c732afed625bcdf3616 100644 (file)
@@ -290,6 +290,12 @@ public:
    */
   virtual void EndRenderPass(Graphics::SyncObject* syncObject) = 0;
 
+  /**
+   * @brief Request to read pixels
+   * @param[out] buffer to load pixel data.
+   */
+  virtual void ReadPixels(uint8_t* buffer) = 0;
+
   /**
    * @brief Executes a list of secondary command buffers
    *
index 9922ead000b13f979ffcc1bcdd89ee15d9dda02e..acb86e849eec7c54a0f7572f52fb513a920c3746 100644 (file)
@@ -1081,10 +1081,11 @@ enum class TextureTiling
  */
 struct ColorAttachment
 {
-  uint32_t attachmentId;
-  Texture* texture;
-  uint32_t layerId;
-  uint32_t levelId;
+  uint32_t            attachmentId;
+  Texture*            texture;
+  uint32_t            layerId;
+  uint32_t            levelId;
+  Dali::Pixel::Format pixelFormat;
 };
 
 /**
index f1a2f9ec95b253415bb927eb7d49f2affc62bf5a..c894cf75e13436df5d75bca35d5828cf109ee21a 100644 (file)
@@ -184,11 +184,21 @@ CameraActor* RenderTask::GetCameraActor() const
 
 void RenderTask::SetFrameBuffer(FrameBufferPtr frameBuffer)
 {
+  if(mFrameBuffer)
+  {
+    mFrameBuffer->ClearRenderResult();
+  }
+
   mFrameBuffer = frameBuffer;
   Render::FrameBuffer* renderFrameBufferPtr(nullptr);
   if(frameBuffer)
   {
-    renderFrameBufferPtr = mFrameBuffer->GetRenderObject();
+    renderFrameBufferPtr = frameBuffer->GetRenderObject();
+  }
+
+  if(frameBuffer && mIsRequestedToKeepRenderResult)
+  {
+    frameBuffer->KeepRenderResult();
   }
 
   if(GetRenderTaskSceneObject())
@@ -443,6 +453,10 @@ void RenderTask::SetRefreshRate(uint32_t refreshRate)
   DALI_LOG_INFO(gLogRender, Debug::General, "RenderTask::SetRefreshRate(this:%p, %d)\n", this, refreshRate);
 
   mRefreshRate = refreshRate; // cached for GetRefreshRate()
+  if(mRefreshRate == Dali::RenderTask::REFRESH_ONCE)
+  {
+    ClearRenderResult();
+  }
 
   // Note - even when refreshRate is the same as mRefreshRate, a message should be sent
 
@@ -672,6 +686,40 @@ void RenderTask::RenderUntil(Actor* stopperActor)
   }
 }
 
+void RenderTask::KeepRenderResult()
+{
+  if(mRefreshRate == Dali::RenderTask::REFRESH_ONCE)
+  {
+    if(!mIsRequestedToKeepRenderResult)
+    {
+      mIsRequestedToKeepRenderResult = true;
+      if(mFrameBuffer)
+      {
+        mFrameBuffer->KeepRenderResult();
+      }
+    }
+  }
+}
+
+void RenderTask::ClearRenderResult()
+{
+  mIsRequestedToKeepRenderResult = false;
+  if(mFrameBuffer)
+  {
+    mFrameBuffer->ClearRenderResult();
+  }
+}
+
+Dali::PixelData RenderTask::GetRenderResult()
+{
+  Dali::PixelData pixelData;
+  if(mFrameBuffer)
+  {
+    pixelData = mFrameBuffer->GetRenderResult();
+  }
+  return pixelData;
+}
+
 const SceneGraph::RenderTask* RenderTask::GetRenderTaskSceneObject() const
 {
   return static_cast<const SceneGraph::RenderTask*>(mUpdateObject);
@@ -936,6 +984,7 @@ void RenderTask::EmitSignalFinish()
 {
   DALI_LOG_INFO(gLogRender, Debug::General, "RenderTask::EmitSignalFinish(this:%p)\n", this);
 
+  mIsRequestedToKeepRenderResult = false;
   if(!mSignalFinished.Empty())
   {
     Dali::RenderTask handle(this);
@@ -979,6 +1028,7 @@ RenderTask::RenderTask(const SceneGraph::RenderTask* sceneObject, RenderTaskList
   mRefreshRate(Dali::RenderTask::DEFAULT_REFRESH_RATE),
   mRefreshOnceCounter(0u),
   mScreenToFrameBufferFunction(Dali::RenderTask::DEFAULT_SCREEN_TO_FRAMEBUFFER_FUNCTION),
+  mIsRequestedToKeepRenderResult(false),
   mExclusive(Dali::RenderTask::DEFAULT_EXCLUSIVE),
   mInputEnabled(Dali::RenderTask::DEFAULT_INPUT_ENABLED),
   mClearEnabled(Dali::RenderTask::DEFAULT_CLEAR_ENABLED),
@@ -1002,6 +1052,8 @@ RenderTask::~RenderTask()
   DALI_LOG_INFO(gLogRender, Debug::General, "RenderTask::~RenderTask(this:%p)\n", this);
   // scene object deletion is handled by our parent
   // scene object handles observation of source and camera
+
+  ClearRenderResult();
 }
 
 } // namespace Internal
index efed82f3937407bcb8aa7d604e9fe9699cafa095..d9855412f7831d5aef764d5f50e7a0586ba36446 100644 (file)
@@ -22,6 +22,7 @@
 #include <dali/public-api/object/base-object.h>
 #include <dali/public-api/object/weak-handle.h>
 #include <dali/public-api/render-tasks/render-task.h>
+#include <dali/public-api/images/pixel-data.h>
 
 #include <dali/internal/event/common/object-impl.h>
 #include <dali/internal/event/events/actor-observer.h>
@@ -294,6 +295,21 @@ public:
    */
   void RenderUntil(Actor* stopperActor);
 
+  /**
+   * @copydoc Dali::RenderTask::KeepRenderResult
+   */
+  void KeepRenderResult();
+
+  /**
+   * @copydoc Dali::RenderTask::ClearRenderResult
+   */
+  void ClearRenderResult();
+
+  /**
+   * @copydoc Dali::RenderTask::GetRenderResult
+   */
+  Dali::PixelData GetRenderResult();
+
 public: // Used by RenderTaskList, which owns the SceneGraph::RenderTasks
   /**
    * Retrieve the scene-graph RenderTask object.
@@ -420,6 +436,7 @@ private:
 
   uint32_t mRenderTaskId{0u};
 
+  bool mIsRequestedToKeepRenderResult : 1;
   bool mExclusive : 1;    ///< True if the render-task has exclusive access to the source Nodes.
   bool mInputEnabled : 1; ///< True if the render-task should be considered for input handling.
   bool mClearEnabled : 1; ///< True if the render-task should be clear the color buffer.
index 317ffd4cc12a1c9b168931f67aa5c8703ea64d56..1136b1c401af5dead6733ef9f8adc3f697faabaf 100644 (file)
@@ -133,6 +133,24 @@ void FrameBuffer::SetSize(uint32_t width, uint32_t height)
   mHeight = height;
 }
 
+void FrameBuffer::KeepRenderResult()
+{
+  KeepRenderResultToFrameBuffer(GetEventThreadServices().GetUpdateManager(), *mRenderObject);
+}
+
+void FrameBuffer::ClearRenderResult()
+{
+  if(DALI_LIKELY(EventThreadServices::IsCoreRunning() && mRenderObject))
+  {
+    ClearRenderResultToFrameBuffer(GetEventThreadServices().GetUpdateManager(), *mRenderObject);
+  }
+}
+
+Dali::PixelData FrameBuffer::GetRenderResult()
+{
+  return mRenderObject->GetRenderResult();
+}
+
 FrameBuffer::~FrameBuffer()
 {
   if(DALI_UNLIKELY(!Dali::Stage::IsCoreThread()))
index 341e9de053cb81f7a6348b1cb480fd151ebd6389..6a3f7c85821935165f20b7cd4f3d0c935c13f358 100644 (file)
@@ -111,6 +111,24 @@ public:
    */
   void SetSize(uint32_t width, uint32_t height);
 
+  /**
+   * @brief Requests to Keep rendering result.
+   */
+  void KeepRenderResult();
+
+  /**
+   * @brief Requests to Clear the rendered result.
+   * @note Since the rendered result is kept in the render thread resource, this method asynchronously clears the result.
+   */
+  void ClearRenderResult();
+
+  /**
+   * @brief Retrieves the rendered result as PixelData.
+   * @return Dali::PixelData that contains rendered result.
+   * If the frame is not yet rendered, empty handle is returned.
+   */
+  Dali::PixelData GetRenderResult();
+
 private: // implementation
   /**
    * Constructor
index 9caf165fd29bda0fc04cd471f74fecbc7edcbf80..f4dc289eabc2b140bddd5665feddb8213bf6e23b 100644 (file)
@@ -25,6 +25,7 @@
 #include <dali/integration-api/core.h>
 #include <dali/integration-api/ordered-set.h>
 #include <dali/integration-api/trace.h>
+#include <dali/public-api/common/vector-wrapper.h>
 
 #include <dali/internal/event/common/scene-impl.h>
 
@@ -269,6 +270,7 @@ struct RenderManager::Impl
     sceneContainer.clear();
     renderAlgorithms.DestroyCommandBuffer();
 
+    renderedFrameBufferContainer.clear();
     samplerContainer.Clear();
     frameBufferContainer.Clear();
     vertexBufferContainer.Clear();
@@ -296,12 +298,13 @@ struct RenderManager::Impl
   std::vector<SceneGraph::Scene*> sceneContainer;   ///< List of pointers to the scene graph objects of the scenes
   Render::RenderAlgorithms        renderAlgorithms; ///< The RenderAlgorithms object is used to action the renders required by a RenderInstruction
 
-  Integration::OrderedSet<Render::Sampler>      samplerContainer;      ///< List of owned samplers
-  Integration::OrderedSet<Render::FrameBuffer>  frameBufferContainer;  ///< List of owned framebuffers
-  Integration::OrderedSet<Render::VertexBuffer> vertexBufferContainer; ///< List of owned vertex buffers
-  Integration::OrderedSet<Render::Geometry>     geometryContainer;     ///< List of owned Geometries
-  OwnerKeyContainer<Render::Renderer>           rendererContainer;     ///< List of owned renderers
-  OwnerKeyContainer<Render::Texture>            textureContainer;      ///< List of owned textures
+  std::vector<Render::FrameBuffer*>             renderedFrameBufferContainer; ///< List of rendered frame buffer
+  Integration::OrderedSet<Render::Sampler>      samplerContainer;             ///< List of owned samplers
+  Integration::OrderedSet<Render::FrameBuffer>  frameBufferContainer;         ///< List of owned framebuffers
+  Integration::OrderedSet<Render::VertexBuffer> vertexBufferContainer;        ///< List of owned vertex buffers
+  Integration::OrderedSet<Render::Geometry>     geometryContainer;            ///< List of owned Geometries
+  OwnerKeyContainer<Render::Renderer>           rendererContainer;            ///< List of owned renderers
+  OwnerKeyContainer<Render::Texture>            textureContainer;             ///< List of owned textures
 
   Integration::OrderedSet<Render::RenderTracker> mRenderTrackers; ///< List of owned render trackers
 
@@ -1299,6 +1302,12 @@ void RenderManager::RenderScene(Integration::RenderStatus& status, Integration::
       instruction.mRenderTracker = nullptr;
     }
     mainCommandBuffer->EndRenderPass(syncObject);
+
+    if(instruction.mFrameBuffer && instruction.mFrameBuffer->IsKeepingRenderResultRequested())
+    {
+      mainCommandBuffer->ReadPixels(instruction.mFrameBuffer->GetRenderResultBuffer());
+      mImpl->renderedFrameBufferContainer.push_back(instruction.mFrameBuffer);
+    }
   }
 
   if(targetsToPresent.size() > 0u)
@@ -1373,6 +1382,12 @@ void RenderManager::PostRender()
 
   mImpl->ClearUnusedProgramCacheIfNeed();
 
+  for(auto& framebuffer : mImpl->renderedFrameBufferContainer)
+  {
+    framebuffer->SetRenderResultDrawn();
+  }
+  mImpl->renderedFrameBufferContainer.clear();
+
 #if defined(LOW_SPEC_MEMORY_MANAGEMENT_ENABLED)
   // Shrink relevant containers if required.
   if(mImpl->containerRemovedFlags & ContainerRemovedFlagBits::RENDERER)
index 965d28b9ada794623087f13d2776e4361f98c1b6..af0029bf7d58e4631dcb2568d4d670bab0367566 100644 (file)
@@ -28,7 +28,8 @@ namespace Internal
 namespace Render
 {
 FrameBuffer::FrameBuffer(uint32_t width, uint32_t height, Mask attachments)
-: mWidth(width),
+: mRenderResult(nullptr),
+  mWidth(width),
   mHeight(height),
   mDepthBuffer(attachments & Dali::FrameBuffer::Attachment::DEPTH),
   mStencilBuffer(attachments & Dali::FrameBuffer::Attachment::STENCIL)
@@ -45,7 +46,10 @@ FrameBuffer::FrameBuffer(uint32_t width, uint32_t height, Mask attachments)
   }
 }
 
-FrameBuffer::~FrameBuffer() = default;
+FrameBuffer::~FrameBuffer()
+{
+  ClearRenderResult();
+}
 
 void FrameBuffer::Destroy()
 {
@@ -67,7 +71,7 @@ void FrameBuffer::AttachColorTexture(Render::Texture* texture, uint32_t mipmapLe
     }
 
     uint32_t                  attachmentId = mCreateInfo.colorAttachments.size();
-    Graphics::ColorAttachment colorAttachment{attachmentId, texture->GetGraphicsObject(), layer, mipmapLevel};
+    Graphics::ColorAttachment colorAttachment{attachmentId, texture->GetGraphicsObject(), layer, mipmapLevel, texture->GetPixelFormat()};
     mCreateInfo.colorAttachments.push_back(colorAttachment);
   }
 }
@@ -106,6 +110,66 @@ void FrameBuffer::SetMultiSamplingLevel(uint8_t multiSamplingLevel)
   mCreateInfo.multiSamplingLevel = multiSamplingLevel;
 }
 
+void FrameBuffer::KeepRenderResult()
+{
+  mIsKeepingRenderResultRequested = true;
+}
+
+void FrameBuffer::ClearRenderResult()
+{
+  if(mIsKeepingRenderResultRequested)
+  {
+    mIsKeepingRenderResultRequested = false;
+    delete[] mRenderResult;
+  }
+
+  Dali::Mutex::ScopedLock lock(mPixelDataMutex);
+  if(mRenderedPixelData)
+  {
+    mRenderedPixelData.Reset();
+  }
+}
+
+bool FrameBuffer::IsKeepingRenderResultRequested() const
+{
+  return mIsKeepingRenderResultRequested;
+}
+
+uint8_t* FrameBuffer::GetRenderResultBuffer()
+{
+  uint8_t* buffer = nullptr;
+  if(mIsKeepingRenderResultRequested)
+  {
+    if(mRenderResult)
+    {
+      delete[] mRenderResult;
+    }
+    mRenderResult = new uint8_t[mWidth * mHeight * Dali::Pixel::GetBytesPerPixel(mCreateInfo.colorAttachments.begin()->pixelFormat)];
+    buffer = mRenderResult;
+  }
+  return buffer;
+}
+
+void FrameBuffer::SetRenderResultDrawn()
+{
+  Dali::Mutex::ScopedLock lock(mPixelDataMutex);
+  mRenderedPixelData              = Dali::PixelData::New(mRenderResult, mWidth * mHeight * Dali::Pixel::GetBytesPerPixel(mCreateInfo.colorAttachments.begin()->pixelFormat), mWidth, mHeight, mCreateInfo.colorAttachments.begin()->pixelFormat, Dali::PixelData::DELETE_ARRAY);
+  mRenderResult                   = nullptr;
+  mIsKeepingRenderResultRequested = false;
+}
+
+// Called from Main thread.
+Dali::PixelData FrameBuffer::GetRenderResult()
+{
+  Dali::Mutex::ScopedLock lock(mPixelDataMutex);
+  Dali::PixelData pixelData;
+  if(!mIsKeepingRenderResultRequested && mRenderedPixelData)
+  {
+    pixelData = mRenderedPixelData;
+  }
+  return pixelData;
+}
+
 bool FrameBuffer::CreateGraphicsObjects()
 {
   bool created = false;
index e1eb65769158d8c334af5be3e06bdf04311b3c98..2020d2dd88dd0e743b376d21c3b0976399a04db6 100644 (file)
@@ -21,6 +21,9 @@
 #include <dali/devel-api/rendering/frame-buffer-devel.h>
 #include <dali/internal/render/renderers/render-sampler.h>
 #include <dali/public-api/rendering/frame-buffer.h>
+#include <dali/devel-api/threading/mutex.h>
+
+#include <dali/integration-api/debug.h>
 
 namespace Dali
 {
@@ -110,6 +113,41 @@ public:
    */
   void SetMultiSamplingLevel(uint8_t multiSamplingLevel);
 
+  /**
+   * @brief Requests to Keep rendering result.
+   */
+  void KeepRenderResult();
+
+  /**
+   * @brief Requests to Clear the rendered result.
+   * @note Since the rendered result is kept in the render thread resource, this method asynchronously clears the result.
+   */
+  void ClearRenderResult();
+
+  /**
+   * @brief Retrieves the rendered result as PixelData.
+   * @return Dali::PixelData that contains rendered result.
+   * If the frame is not yet rendered, empty handle is returned.
+   */
+  Dali::PixelData GetRenderResult();
+
+  /**
+   * @brief Retrieves whether keeping render result is requested or not.
+   * @return True if keeping render result is requested.
+   */
+  bool IsKeepingRenderResultRequested() const;
+
+  /**
+   * @brief Retrieves buffer pointer that the render result is stored.
+   * @return Buffer pointer of the render result buffer.
+   */
+  uint8_t* GetRenderResultBuffer();
+
+  /**
+   * @brief Notifies the drawing call of the frame is finished.
+   */
+  void SetRenderResultDrawn();
+
   /**
    * @brief Get the number of textures bound to this frame buffer as color attachments.
    * @return The number of color attachments.
@@ -172,6 +210,12 @@ private:
   // clear colors
   std::vector<Graphics::ClearValue> mClearValues{};
 
+  bool            mIsKeepingRenderResultRequested{false};
+  bool            mIsRenderResultDrawn{false};
+  uint8_t*        mRenderResult;
+  Dali::PixelData mRenderedPixelData;
+  Dali::Mutex     mPixelDataMutex;
+
   uint32_t mWidth;
   uint32_t mHeight;
 
index 2e329faa79c0e2b1a4a7bce67a1333447da77869..a7c5c31bb314547fa74e104867370879858b04e8 100644 (file)
@@ -1602,6 +1602,16 @@ void UpdateManager::NotifyFrameCallback(FrameCallbackInterface* frameCallback, D
   mImpl->GetFrameCallbackProcessor(*this).NotifyFrameCallback(frameCallback, syncPoint);
 }
 
+void UpdateManager::KeepRenderResultToFrameBuffer(Render::FrameBuffer* frameBuffer)
+{
+  frameBuffer->KeepRenderResult();
+}
+
+void UpdateManager::ClearRenderResultToFrameBuffer(Render::FrameBuffer* frameBuffer)
+{
+  frameBuffer->ClearRenderResult();
+}
+
 void UpdateManager::AddSampler(OwnerPointer<Render::Sampler>& sampler)
 {
   // Message has ownership of Sampler while in transit from update to render
index 241a75ae2b259a31d484872bc2aa6ca3e99038f0..dc23eaf615261f6b82e99a8c68a7a2bfd28ab399 100644 (file)
@@ -739,6 +739,18 @@ public:
    */
   void NotifyFrameCallback(FrameCallbackInterface* frameCallback, Dali::UpdateProxy::NotifySyncPoint syncPoint);
 
+  /**
+   * Requests to keep render result of the framebuffer.
+   * @param[in] frameBuffer The FrameBuffer
+   */
+  void KeepRenderResultToFrameBuffer(Render::FrameBuffer* frameBuffer);
+
+  /**
+   * Requests to clear kept render result of the framebuffer.
+   * @param[in] frameBuffer The FrameBuffer
+   */
+  void ClearRenderResultToFrameBuffer(Render::FrameBuffer* frameBuffer);
+
   /**
    * Get the update message queue capacity (mutex locked)
    */
@@ -1613,6 +1625,28 @@ inline void SetMultiSamplingLevelToFrameBuffer(UpdateManager& manager, Render::F
   new(slot) LocalType(&manager, &UpdateManager::SetMultiSamplingLevelToFrameBuffer, &frameBuffer, multiSamplingLevel);
 }
 
+inline void KeepRenderResultToFrameBuffer(UpdateManager& manager, Render::FrameBuffer& frameBuffer)
+{
+  using LocalType = MessageValue1<UpdateManager, Render::FrameBuffer*>;
+
+  // Reserve some memory inside the render queue
+  uint32_t* slot = manager.ReserveMessageSlot(sizeof(LocalType));
+
+  // Construct message in the render queue memory; note that delete should not be called on the return value
+  new(slot) LocalType(&manager, &UpdateManager::KeepRenderResultToFrameBuffer, &frameBuffer);
+}
+
+inline void ClearRenderResultToFrameBuffer(UpdateManager& manager, Render::FrameBuffer& frameBuffer)
+{
+  using LocalType = MessageValue1<UpdateManager, Render::FrameBuffer*>;
+
+  // Reserve some memory inside the render queue
+  uint32_t* slot = manager.ReserveMessageSlot(sizeof(LocalType));
+
+  // Construct message in the render queue memory; note that delete should not be called on the return value
+  new(slot) LocalType(&manager, &UpdateManager::ClearRenderResultToFrameBuffer, &frameBuffer);
+}
+
 inline void SetDepthIndicesMessage(UpdateManager& manager, OwnerPointer<NodeDepths>& nodeDepths)
 {
   using LocalType = MessageValue1<UpdateManager, OwnerPointer<NodeDepths>>;
index a30f971102ba069e4538791e92d735a712a6e0ad..da224365c2a78872e4fb2c58716911d79cba6fb8 100644 (file)
@@ -323,6 +323,21 @@ void RenderTask::RenderUntil(Actor stopperActor)
   return GetImplementation(*this).RenderUntil(actorImpl);
 }
 
+void RenderTask::KeepRenderResult()
+{
+  GetImplementation(*this).KeepRenderResult();
+}
+
+void RenderTask::ClearRenderResult()
+{
+  GetImplementation(*this).ClearRenderResult();
+}
+
+PixelData RenderTask::GetRenderResult()
+{
+  return GetImplementation(*this).GetRenderResult();
+}
+
 RenderTask::RenderTask(Internal::RenderTask* internal)
 : Handle(internal)
 {
index 1835d06231b7f9ae9c6079483009e8076f279916..f9a0a08acecf9eefa0300cfe410836b1acf1d4cf 100644 (file)
@@ -22,6 +22,7 @@
 #include <cstdint> // uint32_t
 
 // INTERNAL INCLUDES
+#include <dali/public-api/images/pixel-data.h>
 #include <dali/public-api/math/viewport.h>
 #include <dali/public-api/object/handle.h>
 #include <dali/public-api/object/property-index-ranges.h>
@@ -594,6 +595,30 @@ public:
    */
   void RenderUntil(Actor stopperActor);
 
+  /**
+   * @brief Requests to Keep rendering result of this frame.
+   * @SINCE_2_3.51
+   * @note This mothod is only available when the refresh rate is REFRESH_ONCE.
+   * @note The result can be used after the FinishedSignal is emitted.
+   */
+  void KeepRenderResult();
+
+  /**
+   * @brief Requests to Clear the rendered result.
+   * @SINCE_2_3.51
+   * @note Since the rendered result is kept in the render thread resource, this method asynchronously clears the result.
+   */
+  void ClearRenderResult();
+
+  /**
+   * @brief Retrieves the rendered result as PixelData.
+   * @SINCE_2_3.51
+   * @return Dali::PixelData that contains rendered result.
+   * @note This method is only available after the FinishedSignal is emitted.
+   * If this method is called before the Finishedsignal is emitted, empty handle is returned.
+   */
+  Dali::PixelData GetRenderResult();
+
 public: // Signals
   /**
    * @brief If the refresh rate is REFRESH_ONCE, connect to this signal to be notified when a RenderTask has finished.