- Users can use RenderTask API to request and to use the buffer.
Change-Id: Iaf63a60f246df8dc48f152bc8a4b4c0b68cd1e1b
Signed-off-by: Seungho Baek <sbsh.baek@samsung.com>
mCallStack.PushCall("EndRenderPass", namedParams.str(), namedParams);
}
+ void ReadPixels(uint8_t* buffer) override
+ {
+ }
+
void ExecuteCommandBuffers(std::vector<const CommandBuffer*>&& commandBuffers) override
{
mCommands.emplace_back();
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;
+}
*/
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
*
*/
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;
};
/**
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())
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
}
}
+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);
{
DALI_LOG_INFO(gLogRender, Debug::General, "RenderTask::EmitSignalFinish(this:%p)\n", this);
+ mIsRequestedToKeepRenderResult = false;
if(!mSignalFinished.Empty())
{
Dali::RenderTask handle(this);
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),
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
#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>
*/
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.
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.
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()))
*/
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
#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>
sceneContainer.clear();
renderAlgorithms.DestroyCommandBuffer();
+ renderedFrameBufferContainer.clear();
samplerContainer.Clear();
frameBufferContainer.Clear();
vertexBufferContainer.Clear();
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
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)
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)
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)
}
}
-FrameBuffer::~FrameBuffer() = default;
+FrameBuffer::~FrameBuffer()
+{
+ ClearRenderResult();
+}
void FrameBuffer::Destroy()
{
}
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);
}
}
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;
#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
{
*/
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.
// 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;
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
*/
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)
*/
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>>;
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)
{
#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>
*/
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.