/*
- * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2025 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.
END_TEST;
}
+
+int UtcDaliCoreClearScene(void)
+{
+ TestApplication application;
+ tet_infoline("Testing Dali::Integration::Core::ClearScene");
+
+ application.GetScene().SetBackgroundColor(Color::MAGENTA);
+
+ TestGraphicsController& controller = application.GetGraphicsController();
+ auto& contTrace = controller.mCallStack;
+ auto& cmdTrace = controller.mCommandBufferCallStack;
+ contTrace.Enable(true);
+ contTrace.EnableLogging(true);
+ cmdTrace.Enable(true);
+ cmdTrace.EnableLogging(true);
+
+ application.SendNotification();
+ application.Render();
+
+ auto& core = application.GetCore();
+ core.ClearScene(application.GetScene());
+
+ DALI_TEST_CHECK(cmdTrace.FindMethod("BeginRenderPass"));
+ DALI_TEST_CHECK(contTrace.FindMethod("SubmitCommandBuffers"));
+ DALI_TEST_CHECK(contTrace.FindMethod("PresentRenderTarget"));
+ END_TEST;
+}
mImpl->RenderScene(status, scene, renderToFbo, clippingRect);
}
+void Core::ClearScene(Integration::Scene scene)
+{
+ mImpl->ClearScene(scene);
+}
+
void Core::PostRender()
{
mImpl->PostRender();
*/
void RenderScene(RenderStatus& status, Integration::Scene& scene, bool renderToFbo, Rect<int>& clippingRect);
+ /**
+ * Clear the scene's surface if there is nothing else to draw.
+ *
+ * Don't need to call this if there is something to draw;
+ * @param[in] scene The scene to be cleared.
+ */
+ void ClearScene(Integration::Scene scene);
+
/**
* This is called after rendering all the scenes in the next frame. This method should be
* followed by a call to RenderScene.
mRenderManager->RenderScene(status, scene, renderToFbo, clippingRect);
}
+void Core::ClearScene(Integration::Scene scene)
+{
+ mRenderManager->ClearScene(scene);
+}
+
void Core::PostRender()
{
mUpdateManager->PostRender();
*/
void RenderScene(Integration::RenderStatus& status, Integration::Scene& scene, bool renderToFbo, Rect<int>& clippingRect);
+ /**
+ * @copydoc Dali::Integration::Core::ClearScene()
+ */
+ void ClearScene(Integration::Scene scene);
+
/**
* @copydoc Dali::Integration::Core::Render()
*/
/*
- * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2025 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.
{
mBackgroundColor = color;
+ // Normally, clear color is taken from render instruction, so set the
+ // background color to the render task (which generates render instructions).
mRenderTaskList->GetTask(0u)->SetClearColor(color);
mRenderTaskList->GetTask(0u)->SetClearEnabled(true);
+
+ // But, if nothing is drawn, and a clear is required, then there are no
+ // render instructions, so we need to get it from the scene object instead.
+ ThreadLocalStorage* tls = ThreadLocalStorage::GetInternal();
+ SetClearColorMessage(tls->GetEventThreadServices(), *mSceneObject, color);
}
Vector4 Scene::GetBackgroundColor() const
}
}
+void RenderManager::ClearScene(Integration::Scene scene)
+{
+ Internal::Scene& sceneInternal = GetImplementation(scene);
+ SceneGraph::Scene* sceneObject = sceneInternal.GetSceneObject();
+ if(!sceneObject)
+ {
+ return;
+ }
+
+ auto& currentClearValues = sceneObject->GetGraphicsRenderPassClearValues();
+ DALI_ASSERT_DEBUG(!currentClearValues.empty());
+
+ Graphics::RenderTarget* currentRenderTarget = sceneObject->GetSurfaceRenderTarget();
+ Graphics::RenderPass* currentRenderPass = sceneObject->GetGraphicsRenderPass(
+ Graphics::AttachmentLoadOp::CLEAR, Graphics::AttachmentStoreOp::STORE);
+
+ Rect<int32_t> surfaceRect = sceneObject->GetSurfaceRect();
+ Graphics::Rect2D scissorArea{0, 0, uint32_t(surfaceRect.width), uint32_t(surfaceRect.height)};
+ int32_t surfaceOrientation = sceneObject->GetSurfaceOrientation() + sceneObject->GetScreenOrientation();
+ if(surfaceOrientation >= 360)
+ {
+ surfaceOrientation -= 360;
+ }
+ scissorArea = RecalculateScissorArea(scissorArea, surfaceOrientation, surfaceRect);
+
+ auto commandBuffer = mImpl->graphicsController.CreateCommandBuffer(Graphics::CommandBufferCreateInfo().SetLevel(Graphics::CommandBufferLevel::PRIMARY), nullptr);
+ commandBuffer->Begin(Graphics::CommandBufferBeginInfo()
+ .SetUsage(0 | Graphics::CommandBufferUsageFlagBits::ONE_TIME_SUBMIT)
+ .SetRenderTarget(*currentRenderTarget));
+
+ commandBuffer->BeginRenderPass(currentRenderPass, currentRenderTarget, scissorArea, currentClearValues);
+ commandBuffer->EndRenderPass(nullptr);
+ commandBuffer->End();
+
+ Graphics::SubmitInfo submitInfo;
+ submitInfo.flags = 0 | Graphics::SubmitFlagBits::FLUSH;
+ submitInfo.cmdBuffer.push_back(commandBuffer.get());
+ mImpl->graphicsController.SubmitCommandBuffers(submitInfo);
+ mImpl->graphicsController.PresentRenderTarget(currentRenderTarget);
+}
+
void RenderManager::PostRender()
{
if(!mImpl->commandBufferSubmitted)
*/
void RenderScene(Integration::RenderStatus& status, Integration::Scene& scene, bool renderToFbo, Rect<int>& clippingRect);
+ /**
+ * Clear the scene's surface.
+ *
+ * Note, this does not need to be called if there is something to render.
+ *
+ * Multi-threading note: this method should be called from a dedicated rendering thread.
+ * @param[in] scene The scene to be rendered.
+ */
+ void ClearScene(Integration::Scene scene);
+
// This method should be called from Core::PostRender()
/**
// INTERNAL INCLUDES
#include <dali/integration-api/core-enumerations.h>
+#include <dali/internal/update/controllers/render-message-dispatcher.h>
#include <dali/internal/update/render-tasks/scene-graph-render-task-list.h>
namespace Dali
namespace SceneGraph
{
Scene::Scene()
-: mFrameRenderedCallbacks(),
+: mRenderMessageDispatcher(nullptr),
+ mFrameRenderedCallbacks(),
mFramePresentedCallbacks(),
mSurfaceRect(),
mSurfaceOrientation(0),
mFramePresentedCallbacks.clear();
}
+void Scene::SetRenderMessageDispatcher(RenderMessageDispatcher* messageDispatcher)
+{
+ mRenderMessageDispatcher = messageDispatcher;
+}
+
+void Scene::SetSurfaceRenderTargetCreateInfo(const Graphics::RenderTargetCreateInfo& renderTargetCreateInfo)
+{
+ if(mRenderTarget != nullptr &&
+ mRenderTargetCreateInfo.surface != renderTargetCreateInfo.surface)
+ {
+ // Only recreate if the surface has changed.
+ mRenderTargetCreateInfo = renderTargetCreateInfo;
+ if(mGraphicsController) // shouldn't be null, as we can't have already set mRenderTarget unless graphics controller exists.
+ {
+ mRenderTarget = mGraphicsController->CreateRenderTarget(renderTargetCreateInfo, std::move(mRenderTarget));
+ }
+ }
+ else
+ {
+ // 2nd Stage initialization happens in RenderManager, not UpdateManager, so is delayed.
+ mRenderTargetCreateInfo = renderTargetCreateInfo;
+ }
+}
+
void Scene::Initialize(Graphics::Controller& graphicsController, Integration::DepthBufferAvailable depthBufferAvailable, Integration::StencilBufferAvailable stencilBufferAvailable)
{
mGraphicsController = &graphicsController;
rpInfo.SetAttachments(attachmentDescriptions);
// Add default render pass (loadOp = clear)
- mRenderPass = graphicsController.CreateRenderPass(rpInfo, nullptr); // Warning: Shallow ptr
+ mRenderPass = graphicsController.CreateRenderPass(rpInfo, nullptr);
desc.SetLoadOp(Graphics::AttachmentLoadOp::LOAD);
attachmentDescriptions[0] = desc;
return mItemsDirtyRects;
}
-void Scene::SetSurfaceRenderTargetCreateInfo(const Graphics::RenderTargetCreateInfo& renderTargetCreateInfo)
+void Scene::SetClearColor(const Vector4& color)
{
- if(mRenderTarget != nullptr &&
- mRenderTargetCreateInfo.surface != renderTargetCreateInfo.surface)
- {
- // Only recreate if the surface has changed.
- mRenderTargetCreateInfo = renderTargetCreateInfo;
- if(mGraphicsController) // shouldn't be null, as we can't have already set mRenderTarget unless graphics controller exists.
- {
- mRenderTarget = mGraphicsController->CreateRenderTarget(renderTargetCreateInfo, std::move(mRenderTarget));
- }
- }
- else
- {
- // 2nd Stage initialization happens in RenderManager, not UpdateManager, so is delayed.
- mRenderTargetCreateInfo = renderTargetCreateInfo;
- }
+ DALI_ASSERT_DEBUG(!mClearValues.empty());
+ mClearValues[0].color = {color.r, color.g, color.b, color.a};
+}
+
+void Scene::SetClearColorInRenderQ(const Vector4& color)
+{
+ // Tramp the color through the render manager's queue to ensure it happens after Initialize().
+ using DerivedType = MessageValue1<Scene, Vector4>;
+ uint32_t* slot = mRenderMessageDispatcher->ReserveMessageSlot(sizeof(DerivedType));
+ new(slot) DerivedType(this, &Scene::SetClearColor, color);
}
void Scene::KeepRendering(float durationSeconds)
{
namespace SceneGraph
{
+class RenderMessageDispatcher;
class RenderInstructionContainer;
class Node;
bool visited{true};
};
+/**
+ * SceneGraph::Scene is a core object representing the window and it's scene-graph. It's used in both
+ * UpdateManager and RenderManager, so has to live in both worlds, and has to know how to send
+ * messages to itself to be run in RenderManager's Queue.
+ *
+ * Initialization is tricky - the Scene's render target has to be setup in RenderManager, so needs
+ * mRenderTargetCreateInfo to be defined before Initialize is called in RenderManage's Queue.
+ * Also, a scene's background color may also get set before Initialize is called.
+ */
class Scene
{
public:
/**
* Constructor
- * @param[in] surface The render surface
*/
Scene();
virtual ~Scene();
/**
- * Creates a scene object in the GPU.
+ * Set the render message dispatcher.
+ * Called from UpdateManagerQueue, before Initialize.
+ */
+ void SetRenderMessageDispatcher(RenderMessageDispatcher* dispatcher);
+
+ /**
+ * Set the render target of the surface
+ * Called from UpdateManager Queue, before Initialize
+ *
+ * @param[in] renderTarget The render target.
+ */
+ void SetSurfaceRenderTargetCreateInfo(const Graphics::RenderTargetCreateInfo& renderTargetCreateInfo);
+
+ /**
+ * Creates a scene object
+ * Note, this is run inside RenderManager Queue, not UpdateManagerQueue.
+ * So, other APIs must also run inside RenderManager Queue if they need
+ * to run afterwards.
+ *
* @param[in] graphicsController The graphics controller
* @param[in] depthBufferAvailable True if there is a depth buffer
* @param[in] stencilBufferAvailable True if there is a stencil buffer
bool IsRotationCompletedAcknowledgementSet();
/**
- * Set the render target of the surface
+ * @brief Set the clear color for the scene's render passes
*
- * @param[in] renderTarget The render target.
+ * @note Normally, the render instruction's clear color is used (from the render task).
+ *
+ * @param[in] color The color to clear for the render passes
*/
- void SetSurfaceRenderTargetCreateInfo(const Graphics::RenderTargetCreateInfo& renderTargetCreateInfo);
+ void SetClearColor(const Vector4& color);
+
+ /**
+ * @brief Set the clear color for the scene's ClearOp render pass
+ *
+ * @note Needs to run after 2nd stage Initialize in RenderManagerQ, so
+ * has to re-send.
+ *
+ * @param[in] color The color to clear for the render pass
+ */
+ void SetClearColorInRenderQ(const Vector4& color);
/**
* @brief Keep rendering for at least the given amount of time.
RenderInstructionContainer mInstructions; ///< Render instructions for the scene
- Graphics::Controller* mGraphicsController{nullptr}; ///< Graphics controller
+ RenderMessageDispatcher* mRenderMessageDispatcher{nullptr}; ///< RenderManager message dispatcher
+ Graphics::Controller* mGraphicsController{nullptr}; ///< Graphics controller
Dali::Integration::Scene::FrameCallbackContainer mFrameRenderedCallbacks; ///< Frame rendered callbacks
Dali::Integration::Scene::FrameCallbackContainer mFramePresentedCallbacks; ///< Frame presented callbacks
new(slot) LocalType(&scene, &Scene::SetPartialUpdateEnabled, enabled);
}
+inline void SetClearColorMessage(EventThreadServices& eventThreadServices, const Scene& scene, const Vector4& color)
+{
+ using LocalType = MessageValue1<Scene, Vector4>;
+
+ // Reserve some memory inside the message queue
+ uint32_t* slot = eventThreadServices.ReserveMessageSlot(sizeof(LocalType));
+
+ // Construct message in the message queue memory; note that delete should not be called on the return value
+ new(slot) LocalType(&scene, &Scene::SetClearColorInRenderQ, color);
+}
+
} // namespace SceneGraph
} // namespace Internal
/*
- * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2025 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.
{
}
- ~SceneInfo() = default; ///< Default non-virtual destructor
- SceneInfo(SceneInfo&& rhs) = default; ///< Move constructor
- SceneInfo& operator=(SceneInfo&& rhs) = default; ///< Move assignment operator
- SceneInfo& operator=(const SceneInfo& rhs) = delete; ///< Assignment operator
- SceneInfo(const SceneInfo& rhs) = delete; ///< Copy constructor
+ ~SceneInfo() = default; ///< Default non-virtual destructor
+ SceneInfo(SceneInfo&& rhs) = default; ///< Move constructor
+ SceneInfo& operator=(SceneInfo&& rhs) = default; ///< Move assignment operator
+ SceneInfo& operator=(const SceneInfo& rhs) = delete; ///< Assignment operator
+ SceneInfo(const SceneInfo& rhs) = delete; ///< Copy constructor
Layer* root{nullptr}; ///< Root node (root is a layer). The layer is not stored in the node memory pool.
OwnerPointer<RenderTaskList> taskList; ///< Scene graph render task list
Layer* rootLayer = layer.Release();
- DALI_ASSERT_DEBUG(std::find_if(mImpl->scenes.begin(), mImpl->scenes.end(), [rootLayer](Impl::SceneInfoPtr& scene) { return scene && scene->root == rootLayer; }) == mImpl->scenes.end() &&
+ DALI_ASSERT_DEBUG(std::find_if(mImpl->scenes.begin(), mImpl->scenes.end(), [rootLayer](Impl::SceneInfoPtr& scene)
+ { return scene && scene->root == rootLayer; }) == mImpl->scenes.end() &&
"Root Node already installed");
rootLayer->CreateTransform(&mImpl->transformManager);
// Set root to the Scene
sceneInfo->scene->SetRoot(sceneInfo->root);
+ sceneInfo->scene->SetRenderMessageDispatcher(&mImpl->renderMessageDispatcher);
// Initialize the context from render manager
typedef MessageValue1<RenderManager, SceneGraph::Scene*> DerivedType;
auto&& iter = mImpl->animations.Begin();
- DALI_TRACE_BEGIN_WITH_MESSAGE_GENERATOR(gTraceFilter, "DALI_ANIMATION_ANIMATE", [&](std::ostringstream& oss) {
- oss << "[" << mImpl->animations.Count() << "]";
- });
+ DALI_TRACE_BEGIN_WITH_MESSAGE_GENERATOR(gTraceFilter, "DALI_ANIMATION_ANIMATE", [&](std::ostringstream& oss)
+ { oss << "[" << mImpl->animations.Count() << "]"; });
while(iter != mImpl->animations.End())
{
mImpl->notificationManager.QueueNotification(&mImpl->animationPlaylist, std::move(mImpl->notifyRequiredAnimations));
}
- DALI_TRACE_END_WITH_MESSAGE_GENERATOR(gTraceFilter, "DALI_ANIMATION_ANIMATE", [&](std::ostringstream& oss) {
- oss << "[" << mImpl->animations.Count() << "]";
- });
+ DALI_TRACE_END_WITH_MESSAGE_GENERATOR(gTraceFilter, "DALI_ANIMATION_ANIMATE", [&](std::ostringstream& oss)
+ { oss << "[" << mImpl->animations.Count() << "]"; });
return animationActive;
}
return;
}
- DALI_TRACE_BEGIN_WITH_MESSAGE_GENERATOR(gTraceFilter, "DALI_UPDATE_RENDERERS", [&](std::ostringstream& oss) {
- oss << "[" << mImpl->renderers.Count() << "]";
- });
+ DALI_TRACE_BEGIN_WITH_MESSAGE_GENERATOR(gTraceFilter, "DALI_UPDATE_RENDERERS", [&](std::ostringstream& oss)
+ { oss << "[" << mImpl->renderers.Count() << "]"; });
for(const auto& rendererKey : mImpl->renderers)
{
// We should not start skipping update steps or reusing lists until there has been two frames where nothing changes
if(updateScene || mImpl->previousUpdateScene)
{
- DALI_TRACE_BEGIN_WITH_MESSAGE_GENERATOR(gTraceFilter, "DALI_UPDATE_INTERNAL", [&](std::ostringstream& oss) {
+ DALI_TRACE_BEGIN_WITH_MESSAGE_GENERATOR(gTraceFilter, "DALI_UPDATE_INTERNAL", [&](std::ostringstream& oss)
+ {
oss << "[n:" << mImpl->nodes.Size() << ",";
oss << "c:" << mImpl->customObjects.Size() << ",";
oss << "a:" << mImpl->animations.Size() << ",";
oss << "r:" << mImpl->renderers.Size() << ",";
oss << "t:" << mImpl->textureSets.Size() << ",";
- oss << "s:" << mImpl->shaders.Size() << "]";
- });
+ oss << "s:" << mImpl->shaders.Size() << "]"; });
// Animate
bool animationActive = Animate(bufferIndex, elapsedSeconds);
{
// Reorder children container only if sibiling order changed.
NodeContainer& container = node->GetChildren();
- std::sort(container.Begin(), container.End(), [](Node* a, Node* b) { return a->GetDepthIndex() < b->GetDepthIndex(); });
+ std::sort(container.Begin(), container.End(), [](Node* a, Node* b)
+ { return a->GetDepthIndex() < b->GetDepthIndex(); });
}
}
}