{
}
+ bool DidPresent() override
+ {
+ return true;
+ }
+
void PostRenderDebug() override
{
}
#define DALI_INTERNAL_COMBINED_UPDATE_RENDER_CONTROLLER_DEBUG_H
/*
- * Copyright (c) 2022 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.
#define DEBUG_LEVEL_UPDATE_RENDER Debug::General
#define DEBUG_LEVEL_EVENT Debug::Concise
-Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_THREAD_SYNC");
+Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_THREAD_SYNC");
+Debug::Filter* gLogRenderSceneFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_RENDER_SCENE");
#define LOG_THREAD_SYNC(level, color, format, ...) \
DALI_LOG_INFO(gLogFilter, level, "%s" format "%s\n", color, ##__VA_ARGS__, COLOR_CLEAR)
LOG_THREAD_SYNC(Debug::Concise, color, "%s: " format, __FUNCTION__, ##__VA_ARGS__); \
}
+#define LOG_RENDER_SCENE(format, ...) \
+ DALI_LOG_INFO(gLogRenderSceneFilter, Debug::Verbose, "\033[97m" format "\033[0m", ##__VA_ARGS__)
+
#elif defined(RELEASE_BUILD_LOGGING)
#define ENABLE_LOG_IN_COLOR
#define LOG_THREAD_SYNC_TRACE_FMT(color, format, ...) \
Dali::Integration::Log::LogMessage(Dali::Integration::Log::INFO, "%s%s: " format "%s\n", color, __FUNCTION__, ##__VA_ARGS__, COLOR_CLEAR)
+#define LOG_RENDER_SCENE(format, ...)
+
#else
#define LOG_THREAD_SYNC(level, color, format, ...)
#define LOG_THREAD_SYNC_TRACE(color)
#define LOG_THREAD_SYNC_TRACE_FMT(color, format, ...)
+#define LOG_RENDER_SCENE(format, ...)
#endif // DEBUG_ENABLED
mDamagedRects.clear();
// Collect damage rects
- bool willRender = mCore.PreRender(scene, mDamagedRects);
- bool fullSwap = windowSurface->GetFullSwapNextFrame();
- DALI_LOG_RELEASE_INFO("RenderThread: core.PreRender():%s fullSwap:%s\n", willRender ? "T" : "F", fullSwap ? "T" : "F");
- willRender |= fullSwap;
- if(willRender)
+ bool willRender = mCore.PreRender(scene, mDamagedRects); // willRender is set if there are any render instructions with renderables
+ bool fullSwap = windowSurface->GetFullSwapNextFrame(); // true on Resize|set bg color
+
+ LOG_RENDER_SCENE("RenderThread: core.PreRender():%s fullSwap:%s\n",
+ willRender ? "T" : "F",
+ fullSwap ? "T" : "F");
+
+ if(willRender || fullSwap)
{
graphics.AcquireNextImage(windowSurface);
}
// Render off-screen frame buffers first if any
mCore.RenderScene(windowRenderStatus, scene, true);
+ bool didRender = false;
if(willRender)
{
Rect<int> clippingRect; // Empty for fbo rendering
// Ensure surface can be drawn to; merge damaged areas for previous frames
windowSurface->PreRender(sceneSurfaceResized > 0u, mDamagedRects, clippingRect);
- DALI_LOG_RELEASE_INFO("RenderThread: core.RenderScene() Render the surface\n");
+ LOG_RENDER_SCENE("RenderThread: core.RenderScene() Render the surface\n");
+
// Render the surface (Present & SwapBuffers)
mCore.RenderScene(windowRenderStatus, scene, false, clippingRect);
+ didRender = graphics.DidPresent();
+
+ LOG_RENDER_SCENE("RenderThread: Surface%s presented\n", didRender ? "" : " NOT");
+ }
+
+ // If we weren't going to draw, but need to clear; OR
+ // we were going to draw but didn't, we have acquired the image, and must present.
+ if((!willRender && fullSwap) || (willRender && !didRender))
+ {
+ mCore.ClearScene(scene);
}
// If surface is resized, the surface resized count is decreased.
if(mVsyncRender && 0u == renderToFboInterval)
{
TRACE_UPDATE_RENDER_SCOPE("DALI_UPDATE_RENDER_SLEEP");
- // Sleep until at least the the default frame duration has elapsed. This will return immediately if the specified end-time has already passed.
+ // Sleep until at least the default frame duration has elapsed. This will return immediately if the specified end-time has already passed.
TimeService::SleepUntil(timeToSleepUntil);
}
}
#define DALI_INTERNAL_COMBINED_UPDATE_RENDER_CONTROLLER_H
/*
- * 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.
* @param[in] vertexShader vertexShader need to precompile
* @param[in] fragmentShader fragmentShader need to precompile
* @param[in] shaderName the name of precompile shader (option)
- */
+ */
void PreCompileShader(std::string vertexShader, std::string fragmentShader, std::string shaderName = "");
/**
* Cancel the precompile
- */
+ */
void CancelPreCompile();
/**
*/
void PostRenderWaitForCompletion() override;
-private:
+private: // Attributes
FpsTracker mFpsTracker; ///< Object that tracks the FPS
UpdateStatusLogger mUpdateStatusLogger; ///< Object that logs the update-status as required.
*/
virtual void FrameStart() = 0;
+ /**
+ * @return true if any rendering data was presented
+ */
+ virtual bool DidPresent() = 0;
+
/**
* Log any collected statistics
*/
mGraphicsController.FrameStart();
}
+bool EglGraphics::DidPresent()
+{
+ return true;
+}
+
void EglGraphics::PostRenderDebug()
{
mGLES->PostRender();
*/
void FrameStart() override;
+ /**
+ * @copydoc Dali::Graphics::GraphicsInterface::DidPresent()
+ */
+ bool DidPresent() override;
+
/**
* @copydoc Dali::Graphics::GraphicsInterface::LogMemoryPools()
*/
public:
// Eliminate copy and assigned operations
- EglGraphics(const EglGraphics& rhs) = delete;
+ EglGraphics(const EglGraphics& rhs) = delete;
EglGraphics& operator=(const EglGraphics& rhs) = delete;
private:
if(!mTextureStagingBuffer ||
mTextureStagingBuffer->GetImpl()->GetSize() < size)
{
- auto workerFunc = [&, size](auto workerIndex) {
+ auto workerFunc = [&, size](auto workerIndex)
+ {
Graphics::BufferCreateInfo createInfo{};
createInfo.SetSize(size)
.SetUsage(0u | Dali::Graphics::BufferUsage::TRANSFER_SRC);
}
assert(image);
- auto predicate = [&](auto& item) -> bool {
+ auto predicate = [&](auto& item) -> bool
+ {
return image->GetVkHandle() == item.image.GetVkHandle();
};
auto it = std::find_if(requestMap.begin(), requestMap.end(), predicate);
std::unordered_map<uint32_t, Graphics::UniquePtr<Graphics::Texture>> mExternalTextureResources; ///< Used for ResourceId.
std::queue<const Vulkan::Texture*> mTextureMipmapGenerationRequests; ///< Queue for texture mipmap generation requests
+ bool mDidPresent{false};
std::size_t mCapacity{0u}; ///< Memory Usage (of command buffers)
};
void VulkanGraphicsController::FrameStart()
{
mImpl->mDependencyChecker.Reset(); // Clean down the dependency graph.
- mImpl->mCapacity = 0;
+ mImpl->mCapacity = 0;
+ mImpl->mDidPresent = false;
DALI_LOG_INFO(gVulkanFilter, Debug::Verbose, "FrameStart: bufferIndex:%u\n", mImpl->mGraphicsDevice->GetCurrentBufferIndex());
// Check the size of the discard queues.
mImpl->mDiscardQueues.Resize(bufferCount);
}
+bool VulkanGraphicsController::DidPresent() const
+{
+ return mImpl->mDidPresent;
+}
+
void VulkanGraphicsController::SetResourceBindingHints(const std::vector<SceneResourceBinding>& resourceBindings)
{
// Check if there is some extra information about used resources
{
const auto surfaceId = static_cast<Internal::Adaptor::WindowRenderSurface*>(surface)->GetSurfaceId();
auto swapchain = mImpl->mGraphicsDevice->GetSwapchainForSurfaceId(surfaceId);
- swapchain->Present();
+ mImpl->mDidPresent = swapchain->Present();
surface->PostRender();
}
// else no presentation required for framebuffer render target.
if(destTexture->GetProperties().directWriteAccessEnabled)
{
- auto taskLambda = [pInfo, sourcePtr, sourceInfoPtr, texture](auto workerIndex) {
+ auto taskLambda = [pInfo, sourcePtr, sourceInfoPtr, texture](auto workerIndex)
+ {
const auto& properties = texture->GetProperties();
if(properties.emulated)
// The staging buffer is not allocated yet. The task knows pointer to the pointer which will point
// at staging buffer right before executing tasks. The function will either perform direct copy
// or will do suitable conversion if source format isn't supported and emulation is available.
- auto taskLambda = [ppStagingMemory, currentOffset, pInfo, sourcePtr, texture](auto workerThread) {
+ auto taskLambda = [ppStagingMemory, currentOffset, pInfo, sourcePtr, texture](auto workerThread)
+ {
char* pStagingMemory = reinterpret_cast<char*>(*ppStagingMemory);
// Try to initialise` texture resources explicitly if they are not yet initialised
for(auto& item : updateMap)
{
auto pUpdates = &item.second;
- auto task = [pUpdates](auto workerIndex) {
+ auto task = [pUpdates](auto workerIndex)
+ {
for(auto& update : *pUpdates)
{
update.copyTask(workerIndex);
*/
void RemoveRenderTarget(RenderTarget* renderTarget);
-public: // For debug
void FrameStart();
+ bool DidPresent() const;
+
+public: // For debug
std::size_t GetCapacity() const;
private:
auto presentModes = surface->GetSurfacePresentModes();
auto found = std::find_if(presentModes.begin(),
presentModes.end(),
- [&](vk::PresentModeKHR mode) {
+ [&](vk::PresentModeKHR mode)
+ {
return presentMode == mode;
});
depthAttachment,
mSwapchainCreateInfoKHR.imageExtent.width,
mSwapchainCreateInfoKHR.imageExtent.height),
- [](FramebufferImpl* framebuffer1) {
+ [](FramebufferImpl* framebuffer1)
+ {
framebuffer1->Destroy();
delete framebuffer1;
});
return mSwapchainBuffers.empty() ? 0 : mFrameCounter % mSwapchainBuffers.size();
}
-void Swapchain::Present()
+bool Swapchain::Present()
{
- DALI_LOG_INFO(gVulkanFilter, Debug::Verbose, "Vulkan::Swapchain::Present() valid:%s HaveBuffers:%s\n", mIsValid ? "True" : "False", mSwapchainBuffers.empty() ? "F" : "T");
+ bool presented = false;
+ DALI_LOG_INFO(gVulkanFilter, Debug::Verbose, "Vulkan::Swapchain::Present() valid:%s SwapchainBuffer count:%u\n", mIsValid ? "True" : "False", mSwapchainBuffers.size());
+
// prevent from using invalid swapchain
if(!mIsValid || mSwapchainBuffers.empty())
{
- return;
+ return presented;
}
auto& swapchainBuffer = mSwapchainBuffers[GetCurrentBufferIndex()];
.setWaitSemaphoreCount(1);
vk::Result presentResult = mGraphicsDevice.Present(*mQueue, presentInfo);
+ presented = true;
// handle error
if(presentResult != vk::Result::eSuccess || presentInfo.pResults[0] != vk::Result::eSuccess)
}
swapchainBuffer->submitted = false;
mFrameCounter++;
+ return presented;
}
bool Swapchain::IsValid() const
~Swapchain();
- Swapchain(const Swapchain&) = delete;
+ Swapchain(const Swapchain&) = delete;
Swapchain& operator=(const Swapchain&) = delete;
void Destroy();
/**
* Presents using default present queue, asynchronously
+ * @return true if something was presented to the surface, regardless of error
*/
- void Present();
+ bool Present();
/**
* Returns true when swapchain expired
{
mDepthBufferRequired = static_cast<Integration::DepthBufferAvailable>(depth);
mStencilBufferRequired = static_cast<Integration::StencilBufferAvailable>(stencil);
- mPartialUpdateRequired = Integration::PartialUpdateAvailable::FALSE; //static_cast<Integration::PartialUpdateAvailable>(partialRendering);
+ mPartialUpdateRequired = Integration::PartialUpdateAvailable::FALSE; // static_cast<Integration::PartialUpdateAvailable>(partialRendering);
mMultiSamplingLevel = msaa;
Initialize(displayConnection);
}
mGraphicsController.FrameStart();
}
+bool VulkanGraphics::DidPresent()
+{
+ return mGraphicsController.DidPresent();
+}
+
void VulkanGraphics::PostRenderDebug()
{
// Do nothing for now.
*/
void FrameStart() override;
+ /**
+ * @copydoc GraphicsInterface::DidPresent()
+ */
+ bool DidPresent() override;
+
/**
* Log frame statistics
*/