Reworking pre-render/post-render 41/321241/4
authorDavid Steele <david.steele@samsung.com>
Thu, 13 Mar 2025 17:22:45 +0000 (17:22 +0000)
committerDavid Steele <david.steele@samsung.com>
Fri, 21 Mar 2025 11:01:35 +0000 (11:01 +0000)
Changed main render loop to check if there is a full-swap
without any rendering (usually caused by having no render-task
yet, but resizing or setting the background color of the window).

If so, we need to ensure that an empty scene can be correctly
rendered. Added a ClearScene to Core API.

This is more pertinent to Vulkan renderer, which has to ensure
that the window's render surface is properly acquired _before_
drawing to it, and presenting that surface needs the right
sync primitives.

Modified VulkanGraphicsController::PresentRenderTarget() to also call
Surface->PostRender(), in the same way that EglGraphicsController does.
(Prelude to other re-work for PartialRendering).

Change-Id: Ic3fb46faa17b3ec67053cfc4fbd48f6a31cc414c

13 files changed:
automated-tests/src/dali-adaptor/dali-test-suite-utils/test-graphics-egl-application.h
dali/internal/adaptor/common/combined-update-render-controller-debug.h
dali/internal/adaptor/common/combined-update-render-controller.cpp
dali/internal/adaptor/common/combined-update-render-controller.h
dali/internal/graphics/common/graphics-interface.h
dali/internal/graphics/gles/egl-graphics.cpp
dali/internal/graphics/gles/egl-graphics.h
dali/internal/graphics/vulkan-impl/vulkan-graphics-controller.cpp
dali/internal/graphics/vulkan-impl/vulkan-graphics-controller.h
dali/internal/graphics/vulkan-impl/vulkan-swapchain-impl.cpp
dali/internal/graphics/vulkan-impl/vulkan-swapchain-impl.h
dali/internal/graphics/vulkan/vulkan-graphics-impl.cpp
dali/internal/graphics/vulkan/vulkan-graphics-impl.h

index 4626f3d8ba7436aa57e774167c443f2c1f192c37..14bb24d62aa958a0e98fdbd333b80e467139d2da 100644 (file)
@@ -292,6 +292,11 @@ public:
   {
   }
 
+  bool DidPresent() override
+  {
+    return true;
+  }
+
   void PostRenderDebug() override
   {
   }
index 193085ef6aac370c07d43c05b3a1df8789c13513..9caa4a8b319763fd1b537791fbeebafe54b5a6a0 100644 (file)
@@ -2,7 +2,7 @@
 #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.
@@ -44,7 +44,8 @@ namespace
 #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)
@@ -63,6 +64,9 @@ Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_THR
     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
@@ -83,11 +87,14 @@ Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_THR
 #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
 
index 4e1fe2d20b2d6628c3e018120ffeef9e1d90dda4..1fdfa0d671b64b01314af26d6c081b7b90942391 100644 (file)
@@ -862,11 +862,14 @@ void CombinedUpdateRenderController::UpdateRenderThread()
           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);
           }
@@ -874,6 +877,7 @@ void CombinedUpdateRenderController::UpdateRenderThread()
           // 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
@@ -881,9 +885,20 @@ void CombinedUpdateRenderController::UpdateRenderThread()
             // 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.
@@ -986,7 +1001,7 @@ void CombinedUpdateRenderController::UpdateRenderThread()
     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);
     }
   }
index 13e647304ea66693a1cfd6be639947bb0523049a..e4a2bb6df8a6c016ffcf7d90a990bc4f5d9ae57d 100644 (file)
@@ -2,7 +2,7 @@
 #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.
@@ -280,12 +280,12 @@ private:
    * @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();
 
   /**
@@ -347,7 +347,7 @@ private:
    */
   void PostRenderWaitForCompletion() override;
 
-private:
+private:                                  // Attributes
   FpsTracker         mFpsTracker;         ///< Object that tracks the FPS
   UpdateStatusLogger mUpdateStatusLogger; ///< Object that logs the update-status as required.
 
index 8966f3733f0a52dfa41edd7a11ef7416dff7704e..df329d5c927b476ef0f4ecd4cd8866ca52a17453 100644 (file)
@@ -356,6 +356,11 @@ public:
    */
   virtual void FrameStart() = 0;
 
+  /**
+   * @return true if any rendering data was presented
+   */
+  virtual bool DidPresent() = 0;
+
   /**
    * Log any collected statistics
    */
index a5f093531f756b1da9a4899090160382f5b9aac1..34a6c71e61870b702965af8eb451ff749096eb59 100644 (file)
@@ -398,6 +398,11 @@ void EglGraphics::FrameStart()
   mGraphicsController.FrameStart();
 }
 
+bool EglGraphics::DidPresent()
+{
+  return true;
+}
+
 void EglGraphics::PostRenderDebug()
 {
   mGLES->PostRender();
index bd9ec864b9584a17d81038663edec6979497a5b7..eff22c9ee560e85d1053f9aab15ad72890ad28ec 100644 (file)
@@ -305,6 +305,11 @@ public:
    */
   void FrameStart() override;
 
+  /**
+   * @copydoc Dali::Graphics::GraphicsInterface::DidPresent()
+   */
+  bool DidPresent() override;
+
   /**
    * @copydoc Dali::Graphics::GraphicsInterface::LogMemoryPools()
    */
@@ -312,7 +317,7 @@ public:
 
 public:
   // Eliminate copy and assigned operations
-  EglGraphics(const EglGraphics& rhs) = delete;
+  EglGraphics(const EglGraphics& rhs)            = delete;
   EglGraphics& operator=(const EglGraphics& rhs) = delete;
 
 private:
index 0bf729fde605dfa461530f2db4f7411ea7cdb971..ce72b1d9a5d819108d2b7a1ccac2e4cab37272b7 100644 (file)
@@ -220,7 +220,8 @@ struct VulkanGraphicsController::Impl
     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);
@@ -310,7 +311,8 @@ struct VulkanGraphicsController::Impl
         }
         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);
@@ -501,6 +503,7 @@ struct VulkanGraphicsController::Impl
 
   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)
 };
@@ -529,7 +532,8 @@ Integration::GraphicsConfig& VulkanGraphicsController::GetGraphicsConfig()
 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.
@@ -537,6 +541,11 @@ void VulkanGraphicsController::FrameStart()
   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
@@ -611,7 +620,7 @@ void VulkanGraphicsController::PresentRenderTarget(Graphics::RenderTarget* rende
   {
     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.
@@ -722,7 +731,8 @@ void VulkanGraphicsController::UpdateTextures(
 
         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)
@@ -757,7 +767,8 @@ void VulkanGraphicsController::UpdateTextures(
           // 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
@@ -795,7 +806,8 @@ void VulkanGraphicsController::UpdateTextures(
   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);
index eeb0e868ac021e74c674ad81b3fdc52f172666c9..7292bfc7faa3b503fa3a7de7fbb2b19f9d103e59 100644 (file)
@@ -451,9 +451,11 @@ public: // Other API
    */
   void RemoveRenderTarget(RenderTarget* renderTarget);
 
-public: // For debug
   void FrameStart();
 
+  bool DidPresent() const;
+
+public: // For debug
   std::size_t GetCapacity() const;
 
 private:
index 60b85b56fe383617ec4a9f80ce4318ff2f6d9fdd..2a4c60c2b3e5b371d6e00b3d41b033924bc1f4a3 100644 (file)
@@ -161,7 +161,8 @@ void Swapchain::CreateVkSwapchain(
   auto presentModes = surface->GetSurfacePresentModes();
   auto found        = std::find_if(presentModes.begin(),
                             presentModes.end(),
-                            [&](vk::PresentModeKHR mode) {
+                            [&](vk::PresentModeKHR mode)
+                            {
                               return presentMode == mode;
                             });
 
@@ -268,7 +269,8 @@ void Swapchain::CreateFramebuffers(FramebufferAttachmentHandle depthAttachment)
                            depthAttachment,
                            mSwapchainCreateInfoKHR.imageExtent.width,
                            mSwapchainCreateInfoKHR.imageExtent.height),
-      [](FramebufferImpl* framebuffer1) {
+      [](FramebufferImpl* framebuffer1)
+      {
         framebuffer1->Destroy();
         delete framebuffer1;
       });
@@ -405,13 +407,15 @@ uint32_t Swapchain::GetCurrentBufferIndex() const
   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()];
@@ -432,6 +436,7 @@ void Swapchain::Present()
       .setWaitSemaphoreCount(1);
 
     vk::Result presentResult = mGraphicsDevice.Present(*mQueue, presentInfo);
+    presented                = true;
 
     // handle error
     if(presentResult != vk::Result::eSuccess || presentInfo.pResults[0] != vk::Result::eSuccess)
@@ -451,6 +456,7 @@ void Swapchain::Present()
   }
   swapchainBuffer->submitted = false;
   mFrameCounter++;
+  return presented;
 }
 
 bool Swapchain::IsValid() const
index c334833840cc07778b0bd6b5b5fdc88c4baf7c89..bee9ee9da4bb482bb75a3b2df2fcd072b567c812 100644 (file)
@@ -63,7 +63,7 @@ public:
 
   ~Swapchain();
 
-  Swapchain(const Swapchain&) = delete;
+  Swapchain(const Swapchain&)            = delete;
   Swapchain& operator=(const Swapchain&) = delete;
 
   void Destroy();
@@ -119,8 +119,9 @@ public:
 
   /**
    * 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
index d4db74f585038e78a217b272f691f8501fc23e45..913ac2d94eed513fce22f5aaead13d7060df6af1 100644 (file)
@@ -56,7 +56,7 @@ void VulkanGraphics::Initialize(const Dali::DisplayConnection& displayConnection
 {
   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);
 }
@@ -247,6 +247,11 @@ void VulkanGraphics::FrameStart()
   mGraphicsController.FrameStart();
 }
 
+bool VulkanGraphics::DidPresent()
+{
+  return mGraphicsController.DidPresent();
+}
+
 void VulkanGraphics::PostRenderDebug()
 {
   // Do nothing for now.
index 3bd7a7cf5fd1aa22ffca4e471b6926cbbaa5904c..b766927c2ee86c83823ad66d2bf594a0d5007f4f 100644 (file)
@@ -188,6 +188,11 @@ public: // Debug APIs
    */
   void FrameStart() override;
 
+  /**
+   * @copydoc GraphicsInterface::DidPresent()
+   */
+  bool DidPresent() override;
+
   /**
    * Log frame statistics
    */