Removing DeviceWaitIdle from Vk::SwapBuffers 68/318868/5
authorDavid Steele <david.steele@samsung.com>
Fri, 24 Jan 2025 11:49:08 +0000 (11:49 +0000)
committerDavid Steele <david.steele@samsung.com>
Fri, 7 Feb 2025 12:18:52 +0000 (12:18 +0000)
Rather than a hard wait each frame, should instead
leverage the exising end of frame fence that's owned
by the swapchain buffer.

Moved the AcquireNextImage functions out of BeginRenderPass, and
into the VulkanGraphicsController::FrameStart method; this will
now wait on the current buffer's end of frame fence before trying
to get the next image (for this buffer index).

SwapBuffers() now just increases the buffer index modulo the buffer
count (aka the number of swapchain images that are available for this
device).

Changed CommandBuffer to contain up to BufferCount() Impls; the relevant
impl is selected for each command by getting the current buffer index
from the device.

Changed DescriptorPool implementation to use the current buffer index
rather than trying to manage it's own pool index. This was quite broken!
It now grows the poolList to match the buffer index, which will never
go past the BufferCount. (usually 2 or 3).

Change-Id: I994046805bfb38285063e87f8409e856f14b3459

dali/internal/graphics/vulkan-impl/vulkan-command-buffer.cpp
dali/internal/graphics/vulkan-impl/vulkan-command-buffer.h
dali/internal/graphics/vulkan-impl/vulkan-graphics-controller.cpp
dali/internal/graphics/vulkan-impl/vulkan-program-impl.cpp
dali/internal/graphics/vulkan-impl/vulkan-program-impl.h
dali/internal/graphics/vulkan-impl/vulkan-swapchain-impl.cpp
dali/internal/graphics/vulkan-impl/vulkan-swapchain-impl.h
dali/internal/graphics/vulkan/vulkan-device.cpp
dali/internal/graphics/vulkan/vulkan-device.h

index 4c31dee5a9c90d72e3650f445809b884a16d0460..87acb158586a16d6c633677d932c043888c30695 100644 (file)
@@ -45,8 +45,7 @@ VT* ConstGraphicsCast(const GT* object)
 
 CommandBuffer::CommandBuffer(const Graphics::CommandBufferCreateInfo& createInfo, VulkanGraphicsController& controller)
 : CommandBufferResource(createInfo, controller),
-  mDynamicStateMask(CommandBuffer::INITIAL_DYNAMIC_MASK_VALUE),
-  mCommandBufferImpl(nullptr)
+  mDynamicStateMask(CommandBuffer::INITIAL_DYNAMIC_MASK_VALUE)
 {
   auto& device    = controller.GetGraphicsDevice();
   bool  isPrimary = true;
@@ -54,8 +53,11 @@ CommandBuffer::CommandBuffer(const Graphics::CommandBufferCreateInfo& createInfo
   {
     isPrimary = false;
   }
-  auto commandPool   = device.GetCommandPool(std::this_thread::get_id());
-  mCommandBufferImpl = commandPool->NewCommandBuffer(isPrimary);
+  auto commandPool = device.GetCommandPool(std::this_thread::get_id());
+  for(uint32_t i = 0; i < device.GetBufferCount(); ++i)
+  {
+    mCommandBufferImpl.emplace_back(commandPool->NewCommandBuffer(isPrimary));
+  }
 }
 
 CommandBuffer::~CommandBuffer() = default;
@@ -64,7 +66,7 @@ void CommandBuffer::DestroyResource()
 {
   // Don't delete the impl, it's pool allocated and should have been
   // returned to the command pool for re-use.
-  mCommandBufferImpl = nullptr;
+  mCommandBufferImpl.clear();
 }
 
 bool CommandBuffer::InitializeResource()
@@ -79,11 +81,13 @@ void CommandBuffer::DiscardResource()
 
 void CommandBuffer::Begin(const Graphics::CommandBufferBeginInfo& info)
 {
-  mDynamicStateMask = CommandBuffer::INITIAL_DYNAMIC_MASK_VALUE;
-  mRenderTarget     = ConstGraphicsCast<Vulkan::RenderTarget, Graphics::RenderTarget>(info.renderTarget);
+  mDynamicStateMask    = CommandBuffer::INITIAL_DYNAMIC_MASK_VALUE;
+  mRenderTarget        = ConstGraphicsCast<Vulkan::RenderTarget, Graphics::RenderTarget>(info.renderTarget);
+  uint32_t bufferIndex = mController.GetGraphicsDevice().GetCurrentBufferIndex();
 
-  if(mCommandBufferImpl)
+  if(mCommandBufferImpl[bufferIndex])
   {
+    DALI_LOG_INFO(gLogCmdBufferFilter, Debug::Verbose, "CommandBuffer::Begin: ptr:%p bufferIndex=%d", this, bufferIndex);
     vk::CommandBufferInheritanceInfo inheritanceInfo{};
     if(info.renderPass)
     {
@@ -93,7 +97,7 @@ void CommandBuffer::Begin(const Graphics::CommandBufferBeginInfo& info)
       inheritanceInfo.queryFlags         = static_cast<vk::QueryControlFlags>(0);
       inheritanceInfo.pipelineStatistics = static_cast<vk::QueryPipelineStatisticFlags>(0);
     }
-    mCommandBufferImpl->Begin(static_cast<vk::CommandBufferUsageFlags>(info.usage), &inheritanceInfo);
+    mCommandBufferImpl[bufferIndex]->Begin(static_cast<vk::CommandBufferUsageFlags>(info.usage), &inheritanceInfo);
 
     // Default depth/stencil should be off:
     SetDepthTestEnable(false);
@@ -105,17 +109,21 @@ void CommandBuffer::Begin(const Graphics::CommandBufferBeginInfo& info)
 
 void CommandBuffer::End()
 {
-  if(mCommandBufferImpl)
+  uint32_t bufferIndex = mController.GetGraphicsDevice().GetCurrentBufferIndex();
+  if(bufferIndex < mCommandBufferImpl.size() && mCommandBufferImpl[bufferIndex])
   {
-    mCommandBufferImpl->End();
+    mCommandBufferImpl[bufferIndex]->End();
   }
 }
 
 void CommandBuffer::Reset()
 {
-  if(mCommandBufferImpl)
+  uint32_t bufferIndex = mController.GetGraphicsDevice().GetCurrentBufferIndex();
+  DALI_LOG_INFO(gLogCmdBufferFilter, Debug::Verbose, "CommandBuffer::Reset: ptr:%p bufferIndex=%d", this, bufferIndex);
+
+  if(bufferIndex < mCommandBufferImpl.size() && mCommandBufferImpl[bufferIndex])
   {
-    mCommandBufferImpl->Reset();
+    mCommandBufferImpl[bufferIndex]->Reset();
   }
   mDynamicStateMask = CommandBuffer::INITIAL_DYNAMIC_MASK_VALUE;
   mRenderTarget     = nullptr;
@@ -125,43 +133,69 @@ void CommandBuffer::BindVertexBuffers(uint32_t
                                       const std::vector<const Graphics::Buffer*>& gfxBuffers,
                                       const std::vector<uint32_t>&                offsets)
 {
+  uint32_t                 bufferIndex = mController.GetGraphicsDevice().GetCurrentBufferIndex();
   std::vector<BufferImpl*> buffers;
   buffers.reserve(gfxBuffers.size());
   for(auto& gfxBuffer : gfxBuffers)
   {
     buffers.push_back(ConstGraphicsCast<Buffer, Graphics::Buffer>(gfxBuffer)->GetImpl());
   }
-  mCommandBufferImpl->BindVertexBuffers(firstBinding, buffers, offsets);
+  if(bufferIndex < mCommandBufferImpl.size() && mCommandBufferImpl[bufferIndex])
+  {
+    mCommandBufferImpl[bufferIndex]->BindVertexBuffers(firstBinding, buffers, offsets);
+  }
 }
 
 void CommandBuffer::BindIndexBuffer(const Graphics::Buffer& gfxBuffer,
                                     uint32_t                offset,
                                     Format                  format)
 {
-  auto indexBuffer = ConstGraphicsCast<Buffer, Graphics::Buffer>(&gfxBuffer);
-  DALI_ASSERT_DEBUG(indexBuffer && indexBuffer->GetImpl());
-  mCommandBufferImpl->BindIndexBuffer(*indexBuffer->GetImpl(), offset, format);
+  const uint32_t bufferIndex = mController.GetGraphicsDevice().GetCurrentBufferIndex();
+  if(bufferIndex < mCommandBufferImpl.size() && mCommandBufferImpl[bufferIndex])
+  {
+    auto indexBuffer = ConstGraphicsCast<Buffer, Graphics::Buffer>(&gfxBuffer);
+    DALI_ASSERT_DEBUG(indexBuffer && indexBuffer->GetImpl());
+
+    mCommandBufferImpl[bufferIndex]->BindIndexBuffer(*indexBuffer->GetImpl(), offset, format);
+  }
 }
 
 void CommandBuffer::BindUniformBuffers(const std::vector<UniformBufferBinding>& bindings)
 {
-  mCommandBufferImpl->BindUniformBuffers(bindings);
+  const uint32_t bufferIndex = mController.GetGraphicsDevice().GetCurrentBufferIndex();
+
+  if(bufferIndex < mCommandBufferImpl.size() && mCommandBufferImpl[bufferIndex])
+  {
+    mCommandBufferImpl[bufferIndex]->BindUniformBuffers(bindings);
+  }
 }
 
 void CommandBuffer::BindPipeline(const Graphics::Pipeline& pipeline)
 {
-  mCommandBufferImpl->BindPipeline(&pipeline);
+  const uint32_t bufferIndex = mController.GetGraphicsDevice().GetCurrentBufferIndex();
+  if(bufferIndex < mCommandBufferImpl.size() && mCommandBufferImpl[bufferIndex])
+  {
+    mCommandBufferImpl[bufferIndex]->BindPipeline(&pipeline);
+  }
 }
 
 void CommandBuffer::BindTextures(const std::vector<TextureBinding>& textureBindings)
 {
-  mCommandBufferImpl->BindTextures(textureBindings);
+  const uint32_t bufferIndex = mController.GetGraphicsDevice().GetCurrentBufferIndex();
+  if(bufferIndex < mCommandBufferImpl.size() && mCommandBufferImpl[bufferIndex])
+  {
+    mCommandBufferImpl[bufferIndex]->BindTextures(textureBindings);
+  }
   mController.CheckTextureDependencies(textureBindings, mRenderTarget);
 }
 
 void CommandBuffer::BindSamplers(const std::vector<SamplerBinding>& samplerBindings)
 {
-  mCommandBufferImpl->BindSamplers(samplerBindings);
+  const uint32_t bufferIndex = mController.GetGraphicsDevice().GetCurrentBufferIndex();
+  if(bufferIndex < mCommandBufferImpl.size() && mCommandBufferImpl[bufferIndex])
+  {
+    mCommandBufferImpl[bufferIndex]->BindSamplers(samplerBindings);
+  }
 }
 
 void CommandBuffer::BindPushConstants(void*    data,
@@ -175,110 +209,92 @@ void CommandBuffer::BeginRenderPass(Graphics::RenderPass*          gfxRenderPass
                                     Rect2D                         renderArea,
                                     const std::vector<ClearValue>& clearValues)
 {
-  auto renderTarget = static_cast<Vulkan::RenderTarget*>(gfxRenderTarget);
-  DALI_ASSERT_DEBUG(mRenderTarget == renderTarget && "RenderPass has different render target to cmd buffer Begin");
+  const uint32_t bufferIndex = mController.GetGraphicsDevice().GetCurrentBufferIndex();
 
-  auto             renderPass  = static_cast<Vulkan::RenderPass*>(gfxRenderPass);
-  auto             surface     = renderTarget->GetSurface();
-  auto&            device      = mController.GetGraphicsDevice();
-  FramebufferImpl* framebuffer = nullptr;
-  RenderPassHandle renderPassImpl;
-  if(surface)
+  if(bufferIndex < mCommandBufferImpl.size() && mCommandBufferImpl[bufferIndex])
   {
-    auto window    = static_cast<Internal::Adaptor::WindowRenderSurface*>(surface);
-    auto surfaceId = window->GetSurfaceId();
-    auto swapchain = device.GetSwapchainForSurfaceId(surfaceId);
-
-    // If we have swapchain then we need to acquire image
-    // This is a special case:
-    // We assume that:
-    // - only one BeginRenderPass() happens per surface so we can acquire image here
-    // - swapchain shouldn't change but in case it does hence the condition below (?)
-    if(mLastSwapchain != swapchain)
+    auto renderTarget = static_cast<Vulkan::RenderTarget*>(gfxRenderTarget);
+    DALI_ASSERT_DEBUG(mRenderTarget == renderTarget && "RenderPass has different render target to cmd buffer Begin");
+
+    auto             renderPass  = static_cast<Vulkan::RenderPass*>(gfxRenderPass);
+    auto             surface     = mRenderTarget->GetSurface();
+    auto&            device      = mController.GetGraphicsDevice();
+    FramebufferImpl* framebuffer = nullptr;
+    RenderPassHandle renderPassImpl;
+    if(surface)
     {
-      mLastSwapchain = swapchain;
+      auto window    = static_cast<Internal::Adaptor::WindowRenderSurface*>(surface);
+      auto surfaceId = window->GetSurfaceId();
+      auto swapchain = device.GetSwapchainForSurfaceId(surfaceId);
+      framebuffer    = swapchain->GetCurrentFramebuffer();
+      renderPassImpl = framebuffer->GetImplFromRenderPass(renderPass);
     }
-
-    if(mLastSwapchain)
+    else
     {
-      framebuffer = mLastSwapchain->AcquireNextFramebuffer(true);
+      auto framebufferHandle = mRenderTarget->GetFramebuffer();
+      framebuffer            = framebufferHandle->GetImpl();
+      renderPassImpl         = framebuffer->GetImplFromRenderPass(renderPass);
+      mController.AddTextureDependencies(renderTarget);
     }
 
-    // In case something went wrong we will try to replace swapchain once
-    // before calling it a day.
-    if(!framebuffer || !swapchain->IsValid())
-    {
-      // make sure device doesn't do any work before replacing swapchain
-      device.DeviceWaitIdle();
-
-      // replace swapchain (only once)
-      swapchain = device.ReplaceSwapchainForSurface(swapchain->GetSurface(), std::move(swapchain));
+    std::vector<vk::ClearValue> vkClearValues;
 
-      mLastSwapchain = swapchain;
-
-      // get new valid framebuffer
-      if(mLastSwapchain)
+    auto attachments = renderPass->GetCreateInfo().attachments;
+    if(attachments != nullptr &&
+       !attachments->empty()) // Can specify clear color even if load op is not clear.
+    {
+      for(auto clearValue : clearValues)
       {
-        framebuffer = swapchain->AcquireNextFramebuffer(true);
+        vk::ClearColorValue color;
+        color.float32[0] = clearValue.color.r;
+        color.float32[1] = clearValue.color.g;
+        color.float32[2] = clearValue.color.b;
+        color.float32[3] = clearValue.color.a;
+        vkClearValues.emplace_back(color);
       }
-      DALI_ASSERT_ALWAYS(framebuffer && "Replacing invalid swapchain unsuccessful! Goodbye!");
     }
 
-    renderPassImpl = framebuffer->GetImplFromRenderPass(renderPass);
-  }
-  else
-  {
-    auto coreFramebuffer = renderTarget->GetFramebuffer();
-    framebuffer          = coreFramebuffer->GetImpl();
-    renderPassImpl       = framebuffer->GetImplFromRenderPass(renderPass);
-    mController.AddTextureDependencies(renderTarget);
-  }
-
-  std::vector<vk::ClearValue> vkClearValues;
-
-  auto attachments = renderPass->GetCreateInfo().attachments;
-  if(attachments != nullptr &&
-     !attachments->empty()) // Can specify clear color even if load op is not clear.
-  {
-    for(auto clearValue : clearValues)
-    {
-      vk::ClearColorValue color;
-      color.float32[0] = clearValue.color.r;
-      color.float32[1] = clearValue.color.g;
-      color.float32[2] = clearValue.color.b;
-      color.float32[3] = clearValue.color.a;
-      vkClearValues.emplace_back(color);
-    }
+    mCommandBufferImpl[bufferIndex]->BeginRenderPass(vk::RenderPassBeginInfo{}
+                                                       .setFramebuffer(framebuffer->GetVkHandle())
+                                                       .setRenderPass(renderPassImpl->GetVkHandle())
+                                                       .setRenderArea({{0, 0}, {renderArea.width, renderArea.height}})
+                                                       .setPClearValues(vkClearValues.data())
+                                                       .setClearValueCount(uint32_t(vkClearValues.size())),
+                                                     vk::SubpassContents::eSecondaryCommandBuffers);
   }
-
-  mCommandBufferImpl->BeginRenderPass(vk::RenderPassBeginInfo{}
-                                        .setFramebuffer(framebuffer->GetVkHandle())
-                                        .setRenderPass(renderPassImpl->GetVkHandle())
-                                        .setRenderArea({{0, 0}, {renderArea.width, renderArea.height}})
-                                        .setPClearValues(vkClearValues.data())
-                                        .setClearValueCount(uint32_t(vkClearValues.size())),
-                                      vk::SubpassContents::eSecondaryCommandBuffers);
 }
 
 void CommandBuffer::EndRenderPass(Graphics::SyncObject* syncObject)
 {
-  mCommandBufferImpl->EndRenderPass();
+  const uint32_t bufferIndex = mController.GetGraphicsDevice().GetCurrentBufferIndex();
+  if(bufferIndex < mCommandBufferImpl.size() && mCommandBufferImpl[bufferIndex])
+  {
+    mCommandBufferImpl[bufferIndex]->EndRenderPass();
+  }
 }
 
 void CommandBuffer::ReadPixels(uint8_t* buffer)
 {
-  mCommandBufferImpl->ReadPixels(buffer);
+  const uint32_t bufferIndex = mController.GetGraphicsDevice().GetCurrentBufferIndex();
+  if(bufferIndex < mCommandBufferImpl.size() && mCommandBufferImpl[bufferIndex])
+  {
+    mCommandBufferImpl[bufferIndex]->ReadPixels(buffer);
+  }
 }
 
 void CommandBuffer::ExecuteCommandBuffers(std::vector<const Graphics::CommandBuffer*>&& gfxCommandBuffers)
 {
-  std::vector<vk::CommandBuffer> vkCommandBuffers;
-  vkCommandBuffers.reserve(gfxCommandBuffers.size());
-  for(auto& gfxCmdBuf : gfxCommandBuffers)
+  uint32_t bufferIndex = mController.GetGraphicsDevice().GetCurrentBufferIndex();
+  if(bufferIndex < mCommandBufferImpl.size() && mCommandBufferImpl[bufferIndex])
   {
-    vkCommandBuffers.push_back(ConstGraphicsCast<CommandBuffer, Graphics::CommandBuffer>(gfxCmdBuf)->GetImpl()->GetVkHandle());
+    std::vector<vk::CommandBuffer> vkCommandBuffers;
+    vkCommandBuffers.reserve(gfxCommandBuffers.size());
+    for(auto& gfxCmdBuf : gfxCommandBuffers)
+    {
+      vkCommandBuffers.push_back(ConstGraphicsCast<CommandBuffer, Graphics::CommandBuffer>(gfxCmdBuf)->GetImpl()->GetVkHandle());
+    }
+    mCommandBufferImpl[bufferIndex]->ExecuteCommandBuffers(vkCommandBuffers);
   }
-  mCommandBufferImpl->ExecuteCommandBuffers(vkCommandBuffers);
 }
 
 void CommandBuffer::Draw(uint32_t vertexCount,
@@ -286,7 +302,11 @@ void CommandBuffer::Draw(uint32_t vertexCount,
                          uint32_t firstVertex,
                          uint32_t firstInstance)
 {
-  mCommandBufferImpl->Draw(vertexCount, instanceCount, firstVertex, firstInstance);
+  const uint32_t bufferIndex = mController.GetGraphicsDevice().GetCurrentBufferIndex();
+  if(bufferIndex < mCommandBufferImpl.size() && mCommandBufferImpl[bufferIndex])
+  {
+    mCommandBufferImpl[bufferIndex]->Draw(vertexCount, instanceCount, firstVertex, firstInstance);
+  }
 }
 
 void CommandBuffer::DrawIndexed(uint32_t indexCount,
@@ -295,7 +315,11 @@ void CommandBuffer::DrawIndexed(uint32_t indexCount,
                                 int32_t  vertexOffset,
                                 uint32_t firstInstance)
 {
-  mCommandBufferImpl->DrawIndexed(indexCount, instanceCount, firstIndex, vertexOffset, firstInstance);
+  const uint32_t bufferIndex = mController.GetGraphicsDevice().GetCurrentBufferIndex();
+  if(bufferIndex < mCommandBufferImpl.size() && mCommandBufferImpl[bufferIndex])
+  {
+    mCommandBufferImpl[bufferIndex]->DrawIndexed(indexCount, instanceCount, firstIndex, vertexOffset, firstInstance);
+  }
 }
 
 void CommandBuffer::DrawIndexedIndirect(Graphics::Buffer& gfxBuffer,
@@ -303,9 +327,12 @@ void CommandBuffer::DrawIndexedIndirect(Graphics::Buffer& gfxBuffer,
                                         uint32_t          drawCount,
                                         uint32_t          stride)
 {
-  auto buffer = ConstGraphicsCast<Buffer, Graphics::Buffer>(&gfxBuffer)->GetImpl();
-
-  mCommandBufferImpl->DrawIndexedIndirect(*buffer, offset, drawCount, stride);
+  const uint32_t bufferIndex = mController.GetGraphicsDevice().GetCurrentBufferIndex();
+  if(bufferIndex < mCommandBufferImpl.size() && mCommandBufferImpl[bufferIndex])
+  {
+    auto buffer = ConstGraphicsCast<Buffer, Graphics::Buffer>(&gfxBuffer)->GetImpl();
+    mCommandBufferImpl[bufferIndex]->DrawIndexedIndirect(*buffer, offset, drawCount, stride);
+  }
 }
 
 void CommandBuffer::DrawNative(const DrawNativeInfo* drawInfo)
@@ -314,11 +341,15 @@ void CommandBuffer::DrawNative(const DrawNativeInfo* drawInfo)
 
 void CommandBuffer::SetScissor(Rect2D value)
 {
-  // @todo Vulkan accepts array of scissors... add to API
-
-  if(SetDynamicState(mDynamicState.scissor, value, DynamicStateMaskBits::SCISSOR))
+  const uint32_t bufferIndex = mController.GetGraphicsDevice().GetCurrentBufferIndex();
+  if(bufferIndex < mCommandBufferImpl.size() && mCommandBufferImpl[bufferIndex])
   {
-    mCommandBufferImpl->SetScissor(value);
+    // @todo Vulkan accepts array of scissors... add to API
+
+    if(SetDynamicState(mDynamicState.scissor, value, DynamicStateMaskBits::SCISSOR))
+    {
+      mCommandBufferImpl[bufferIndex]->SetScissor(value);
+    }
   }
 }
 
@@ -338,9 +369,13 @@ void CommandBuffer::SetViewport(Viewport value)
     correctedValue.y      = value.height;
   }
 
-  if(SetDynamicState(mDynamicState.viewport, correctedValue, DynamicStateMaskBits::VIEWPORT))
+  uint32_t bufferIndex = mController.GetGraphicsDevice().GetCurrentBufferIndex();
+  if(bufferIndex < mCommandBufferImpl.size() && mCommandBufferImpl[bufferIndex])
   {
-    mCommandBufferImpl->SetViewport(correctedValue);
+    if(SetDynamicState(mDynamicState.viewport, correctedValue, DynamicStateMaskBits::VIEWPORT))
+    {
+      mCommandBufferImpl[bufferIndex]->SetViewport(correctedValue);
+    }
   }
 }
 
@@ -366,17 +401,25 @@ void CommandBuffer::ClearDepthBuffer()
 
 void CommandBuffer::SetStencilTestEnable(bool stencilEnable)
 {
-  if(SetDynamicState(mDynamicState.stencilTest, stencilEnable, DynamicStateMaskBits::STENCIL_TEST))
+  const uint32_t bufferIndex = mController.GetGraphicsDevice().GetCurrentBufferIndex();
+  if(bufferIndex < mCommandBufferImpl.size() && mCommandBufferImpl[bufferIndex])
   {
-    mCommandBufferImpl->SetStencilTestEnable(stencilEnable);
+    if(SetDynamicState(mDynamicState.stencilTest, stencilEnable, DynamicStateMaskBits::STENCIL_TEST))
+    {
+      mCommandBufferImpl[bufferIndex]->SetStencilTestEnable(stencilEnable);
+    }
   }
 }
 
 void CommandBuffer::SetStencilWriteMask(uint32_t writeMask)
 {
-  if(SetDynamicState(mDynamicState.stencilWriteMask, writeMask, DynamicStateMaskBits::STENCIL_WRITE_MASK))
+  const uint32_t bufferIndex = mController.GetGraphicsDevice().GetCurrentBufferIndex();
+  if(bufferIndex < mCommandBufferImpl.size() && mCommandBufferImpl[bufferIndex])
   {
-    mCommandBufferImpl->SetStencilWriteMask(vk::StencilFaceFlagBits::eFrontAndBack, writeMask);
+    if(SetDynamicState(mDynamicState.stencilWriteMask, writeMask, DynamicStateMaskBits::STENCIL_WRITE_MASK))
+    {
+      mCommandBufferImpl[bufferIndex]->SetStencilWriteMask(vk::StencilFaceFlagBits::eFrontAndBack, writeMask);
+    }
   }
 }
 
@@ -387,13 +430,14 @@ void CommandBuffer::SetStencilState(Graphics::CompareOp compareOp,
                                     Graphics::StencilOp passOp,
                                     Graphics::StencilOp depthFailOp)
 {
+  uint32_t bufferIndex = mController.GetGraphicsDevice().GetCurrentBufferIndex();
   if(SetDynamicState(mDynamicState.stencilCompareMask, compareMask, DynamicStateMaskBits::STENCIL_COMP_MASK))
   {
-    mCommandBufferImpl->SetStencilCompareMask(vk::StencilFaceFlagBits::eFrontAndBack, compareMask);
+    mCommandBufferImpl[bufferIndex]->SetStencilCompareMask(vk::StencilFaceFlagBits::eFrontAndBack, compareMask);
   }
   if(SetDynamicState(mDynamicState.stencilReference, reference, DynamicStateMaskBits::STENCIL_REF))
   {
-    mCommandBufferImpl->SetStencilReference(vk::StencilFaceFlagBits::eFrontAndBack, reference);
+    mCommandBufferImpl[bufferIndex]->SetStencilReference(vk::StencilFaceFlagBits::eFrontAndBack, reference);
   }
 
   if(SetDynamicState(mDynamicState.stencilFailOp, failOp, DynamicStateMaskBits::STENCIL_OP_FAIL) ||
@@ -401,35 +445,47 @@ void CommandBuffer::SetStencilState(Graphics::CompareOp compareOp,
      SetDynamicState(mDynamicState.stencilDepthFailOp, depthFailOp, DynamicStateMaskBits::STENCIL_OP_DEPTH_FAIL) ||
      SetDynamicState(mDynamicState.stencilCompareOp, compareOp, DynamicStateMaskBits::STENCIL_OP_COMP))
   {
-    mCommandBufferImpl->SetStencilOp(vk::StencilFaceFlagBits::eFrontAndBack,
-                                     VkStencilOpType(failOp).op,
-                                     VkStencilOpType(passOp).op,
-                                     VkStencilOpType(depthFailOp).op,
-                                     VkCompareOpType(compareOp).op);
+    mCommandBufferImpl[bufferIndex]->SetStencilOp(vk::StencilFaceFlagBits::eFrontAndBack,
+                                                  VkStencilOpType(failOp).op,
+                                                  VkStencilOpType(passOp).op,
+                                                  VkStencilOpType(depthFailOp).op,
+                                                  VkCompareOpType(compareOp).op);
   }
 }
 
 void CommandBuffer::SetDepthCompareOp(Graphics::CompareOp compareOp)
 {
-  if(SetDynamicState(mDynamicState.depthCompareOp, compareOp, DynamicStateMaskBits::DEPTH_OP_COMP))
+  const uint32_t bufferIndex = mController.GetGraphicsDevice().GetCurrentBufferIndex();
+  if(bufferIndex < mCommandBufferImpl.size() && mCommandBufferImpl[bufferIndex])
   {
-    mCommandBufferImpl->SetDepthCompareOp(VkCompareOpType(compareOp).op);
+    if(SetDynamicState(mDynamicState.depthCompareOp, compareOp, DynamicStateMaskBits::DEPTH_OP_COMP))
+    {
+      mCommandBufferImpl[bufferIndex]->SetDepthCompareOp(VkCompareOpType(compareOp).op);
+    }
   }
 }
 
 void CommandBuffer::SetDepthTestEnable(bool depthTestEnable)
 {
-  if(SetDynamicState(mDynamicState.depthTest, depthTestEnable, DynamicStateMaskBits::DEPTH_TEST))
+  const uint32_t bufferIndex = mController.GetGraphicsDevice().GetCurrentBufferIndex();
+  if(bufferIndex < mCommandBufferImpl.size() && mCommandBufferImpl[bufferIndex])
   {
-    mCommandBufferImpl->SetDepthTestEnable(depthTestEnable);
+    if(SetDynamicState(mDynamicState.depthTest, depthTestEnable, DynamicStateMaskBits::DEPTH_TEST))
+    {
+      mCommandBufferImpl[bufferIndex]->SetDepthTestEnable(depthTestEnable);
+    }
   }
 }
 
 void CommandBuffer::SetDepthWriteEnable(bool depthWriteEnable)
 {
-  if(SetDynamicState(mDynamicState.depthWrite, depthWriteEnable, DynamicStateMaskBits::DEPTH_WRITE))
+  const uint32_t bufferIndex = mController.GetGraphicsDevice().GetCurrentBufferIndex();
+  if(bufferIndex < mCommandBufferImpl.size() && mCommandBufferImpl[bufferIndex])
   {
-    mCommandBufferImpl->SetDepthWriteEnable(depthWriteEnable);
+    if(SetDynamicState(mDynamicState.depthWrite, depthWriteEnable, DynamicStateMaskBits::DEPTH_WRITE))
+    {
+      mCommandBufferImpl[bufferIndex]->SetDepthWriteEnable(depthWriteEnable);
+    }
   }
 }
 
@@ -439,4 +495,14 @@ Vulkan::RenderTarget* CommandBuffer::GetRenderTarget() const
   return mRenderTarget;
 }
 
+[[nodiscard]] Vulkan::CommandBufferImpl* CommandBuffer::GetImpl() const
+{
+  const uint32_t bufferIndex = mController.GetGraphicsDevice().GetCurrentBufferIndex();
+  if(bufferIndex < mCommandBufferImpl.size() && mCommandBufferImpl[bufferIndex])
+  {
+    return mCommandBufferImpl[bufferIndex];
+  }
+  return nullptr;
+}
+
 } // namespace Dali::Graphics::Vulkan
index f50cdd41bb1358b4824307a6861164f9acbc4460..9d8916fa87cc486bc32e7e785e03c028fdeb9c00 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_GRAPHICS_VULKAN_COMMAND_BUFFER_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.
@@ -384,10 +384,7 @@ public: // API
    */
   RenderTarget* GetRenderTarget() const;
 
-  [[nodiscard]] CommandBufferImpl* GetImpl() const
-  {
-    return mCommandBufferImpl;
-  }
+  [[nodiscard]] CommandBufferImpl* GetImpl() const;
 
 private:
   static const DynamicStateMask INITIAL_DYNAMIC_MASK_VALUE{0xFFFFFFFF};
@@ -423,9 +420,9 @@ private:
     return false;
   }
 
-  CommandBufferImpl* mCommandBufferImpl;
-  RenderTarget*      mRenderTarget{nullptr};
-  Swapchain*         mLastSwapchain{nullptr};
+  std::vector<CommandBufferImpl*> mCommandBufferImpl; ///< There are as many elements as there are swapchain images
+  RenderTarget*                   mRenderTarget{nullptr};
+  Swapchain*                      mLastSwapchain{nullptr};
 };
 
 } // namespace Dali::Graphics::Vulkan
index 9d4d8a4bbb05a2e807e85bd2d7494a3e801d7bc1..e468d48ad874d444e520383f37a466f382cf1a84 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * 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.
@@ -219,7 +219,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);
@@ -309,7 +310,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);
@@ -564,7 +566,8 @@ Integration::GraphicsConfig& VulkanGraphicsController::GetGraphicsConfig()
 void VulkanGraphicsController::FrameStart()
 {
   mImpl->mDependencyChecker.Reset(); // Clean down the dependency graph.
-
+  // Wait for end of frame sync for this buffer, and get next swapchain image for each window.
+  mImpl->mGraphicsDevice->AcquireNextImage();
   mImpl->mCapacity = 0;
 }
 
@@ -611,11 +614,10 @@ void VulkanGraphicsController::SubmitCommandBuffers(const SubmitInfo& submitInfo
 
 void VulkanGraphicsController::PresentRenderTarget(Graphics::RenderTarget* renderTarget)
 {
-  auto surface = static_cast<Vulkan::RenderTarget*>(renderTarget)->GetSurface();
-  if(surface)
+  if(const auto surface = static_cast<Vulkan::RenderTarget*>(renderTarget)->GetSurface())
   {
-    auto surfaceId = static_cast<Internal::Adaptor::WindowRenderSurface*>(surface)->GetSurfaceId();
-    auto swapchain = mImpl->mGraphicsDevice->GetSwapchainForSurfaceId(surfaceId);
+    const auto surfaceId = static_cast<Internal::Adaptor::WindowRenderSurface*>(surface)->GetSurfaceId();
+    auto       swapchain = mImpl->mGraphicsDevice->GetSwapchainForSurfaceId(surfaceId);
     swapchain->Present();
   }
   // else no presentation required for framebuffer render target.
@@ -725,7 +727,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)
@@ -760,7 +763,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
@@ -798,7 +802,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);
@@ -946,7 +951,8 @@ UniquePtr<Graphics::RenderTarget> VulkanGraphicsController::CreateRenderTarget(c
 
 UniquePtr<Graphics::CommandBuffer> VulkanGraphicsController::CreateCommandBuffer(const Graphics::CommandBufferCreateInfo& commandBufferCreateInfo, UniquePtr<Graphics::CommandBuffer>&& oldCommandBuffer)
 {
-  return NewGraphicsObject<Vulkan::CommandBuffer>(commandBufferCreateInfo, *this, std::move(oldCommandBuffer));
+  auto commandBuffer = NewGraphicsObject<Vulkan::CommandBuffer>(commandBufferCreateInfo, *this, std::move(oldCommandBuffer));
+  return commandBuffer;
 }
 
 UniquePtr<Graphics::RenderPass> VulkanGraphicsController::CreateRenderPass(const Graphics::RenderPassCreateInfo& renderPassCreateInfo, UniquePtr<Graphics::RenderPass>&& oldRenderPass)
index add5e8a1223b16adb78fd469f4858fe0f395a307..af01d72f2ecef45be385cedd9d5a2122e6733b09 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * 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.
@@ -71,8 +71,8 @@ struct ProgramImpl::Impl
     vk::DescriptorPool           vkPool;
   };
 
-  std::vector<DescriptorPool> poolList;
-  int32_t                     currentPoolIndex{-1};
+  std::vector<DescriptorPool> poolList;            ///< List of descriptor pools. Each element corresponds to overall bufferIndex.
+  uint32_t                    currentPoolIndex{0}; ///< Current pool index matches bufferIndex
 };
 
 ProgramImpl::ProgramImpl(const Graphics::ProgramCreateInfo& createInfo, VulkanGraphicsController& controller)
@@ -262,22 +262,22 @@ const ProgramCreateInfo& ProgramImpl::GetCreateInfo() const
 
 [[nodiscard]] int ProgramImpl::AddDescriptorPool(uint32_t poolCapacity, uint32_t maxPoolCounts)
 {
-  auto& poolList  = mImpl->poolList;
-  auto& poolIndex = mImpl->currentPoolIndex;
-  poolIndex %= maxPoolCounts;
+  auto& poolList = mImpl->poolList;
 
   auto& gfxDevice = mImpl->controller.GetGraphicsDevice();
   auto& allocator = gfxDevice.GetAllocator();
   auto  vkDevice  = gfxDevice.GetLogicalDevice();
 
-  if(poolCapacity != poolList.size())
+  uint32_t bufferIndex    = gfxDevice.GetCurrentBufferIndex();
+  mImpl->currentPoolIndex = bufferIndex % maxPoolCounts;
+
+  if(mImpl->currentPoolIndex >= poolList.size())
   {
-    poolList.resize(poolCapacity);
-    // should error if pool index exceeds pool capacity
+    poolList.resize(mImpl->currentPoolIndex + 1);
   }
 
   // round-robin the pool index
-  Impl::DescriptorPool& descriptorPool = mImpl->poolList[poolIndex];
+  Impl::DescriptorPool& descriptorPool = mImpl->poolList[mImpl->currentPoolIndex];
 
   // if pool exists at index...
   if(descriptorPool.vkPool)
@@ -286,13 +286,14 @@ const ProgramCreateInfo& ProgramImpl::GetCreateInfo() const
     if(descriptorPool.createInfo.maxSets >= poolCapacity)
     {
       vkDevice.resetDescriptorPool(descriptorPool.vkPool, vk::DescriptorPoolResetFlags{});
-      return poolIndex;
+      return mImpl->currentPoolIndex;
     }
 
-    // ... else, destroy vulkan object
+    // ... else, destroy vulkan object, and re-create it below
     vkDevice.destroyDescriptorPool(descriptorPool.vkPool, &allocator);
   }
 
+  // Create new descriptor pool for the required capacity
   descriptorPool.createInfo.setMaxSets(poolCapacity);
   std::vector<vk::DescriptorPoolSize> poolSizes;
 
@@ -321,7 +322,7 @@ const ProgramCreateInfo& ProgramImpl::GetCreateInfo() const
   // create pool
   VkAssert(vkDevice.createDescriptorPool(&descriptorPool.createInfo, &allocator, &descriptorPool.vkPool));
 
-  return poolIndex;
+  return mImpl->currentPoolIndex;
 }
 
 [[nodiscard]] vk::DescriptorSet ProgramImpl::AllocateDescriptorSet(int poolIndex)
index be7b09c8d5d2fa301688f09ffbc988d8b5ec8b1b..376d33538f7ed1efc53272a9d73c88f48ad9dd50 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_GRAPHICS_VULKAN_PROGRAM_IMPL_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.
@@ -30,7 +30,7 @@ namespace Dali::Graphics::Vulkan
 class Reflection;
 
 /**
- * @brief Program implementation
+ * @brief Program implementation.
  *
  * Program implementation is owned only by the PipelineCache
  *
@@ -161,4 +161,4 @@ private:
 
 } // namespace Dali::Graphics::Vulkan
 
-#endif //DALI_GRAPHICS_VULKAN_PROGRAM_IMPL_H
+#endif // DALI_GRAPHICS_VULKAN_PROGRAM_IMPL_H
index 6df00820dac1cdf3f4d7f78dc7f30ef29249cb9b..6fc871745de72d9960e6f32e4af87c692225a146 100644 (file)
@@ -86,7 +86,7 @@ Swapchain* Swapchain::NewSwapchain(
   SurfaceImpl*       surface,
   vk::Format         requestedFormat,
   vk::PresentModeKHR presentMode,
-  uint32_t           bufferCount)
+  uint32_t&          bufferCount)
 {
   auto swapchain = new Swapchain(device, presentationQueue);
   swapchain->CreateVkSwapchain(oldSwapchain, surface, requestedFormat, presentMode, bufferCount);
@@ -110,7 +110,7 @@ void Swapchain::CreateVkSwapchain(
   SurfaceImpl*       surface,
   vk::Format         requestedFormat,
   vk::PresentModeKHR presentMode,
-  uint32_t           bufferCount)
+  uint32_t&          bufferCount)
 {
   mSurface = surface;
   vk::Format        swapchainImageFormat{};
@@ -164,7 +164,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;
       });
@@ -325,6 +327,19 @@ FramebufferImpl* Swapchain::AcquireNextFramebuffer(bool shouldCollectGarbageNow)
 
   auto& swapchainBuffer = mSwapchainBuffers[mGraphicsDevice.GetCurrentBufferIndex()];
 
+  // First frames don't need waiting as they haven't been submitted
+  // yet. Note, that waiting on the fence without resetting it may
+  // cause a stall ( nvidia, ubuntu )
+  if(mFrameCounter >= mSwapchainBuffers.size())
+  {
+    vk::Result status = swapchainBuffer->endOfFrameFence->GetStatus();
+    if(status == vk::Result::eNotReady)
+    {
+      swapchainBuffer->endOfFrameFence->Wait();
+      swapchainBuffer->endOfFrameFence->Reset();
+    }
+  }
+
   auto result = device.acquireNextImageKHR(mSwapchainKHR,
                                            TIMEOUT,
                                            swapchainBuffer->acquireNextImageSemaphore,
@@ -355,24 +370,6 @@ FramebufferImpl* Swapchain::AcquireNextFramebuffer(bool shouldCollectGarbageNow)
     }
   }
 
-  // First frames don't need waiting as they haven't been submitted
-  // yet. Note, that waiting on the fence without resetting it may
-  // cause a stall ( nvidia, ubuntu )
-  if(mFrameCounter >= mSwapchainBuffers.size())
-  {
-    vk::Result status = swapchainBuffer->endOfFrameFence->GetStatus();
-    if(status == vk::Result::eNotReady)
-    {
-      swapchainBuffer->endOfFrameFence->Wait();
-      swapchainBuffer->endOfFrameFence->Reset();
-    }
-  }
-  else
-  {
-    mGraphicsDevice.DeviceWaitIdle();
-  }
-  // mGraphicsDevice.CollectGarbage();
-
   return mFramebuffers[mSwapchainImageIndex].get();
 }
 
index 41c60e5051dfce5ee8d4b584ed3d9764dba0d349..04a8d60ccad0f958e7b12be57f9595ffe371e455 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_INTERNAL_GRAPHICS_VULKAN_SWAPCHAIN_IMPL_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.
@@ -37,6 +37,18 @@ struct SwapchainBuffer;
 class Swapchain
 {
 public:
+  /**
+   * @brief Create a new swapchain for the given surface.
+   *
+   * @param device The vulkan device
+   * @param presentationQueue The queue to use for presenting the swapchain
+   * @param oldSwapchain Any old swapchain we're recyclying
+   * @param surface The surface to create the swapchain images for
+   * @param requestedFormat The desired image format
+   * @param presentMode Usually eFifo or eMailbox
+   * @param[out] bufferCount Number of available swapchain buffers
+   * @return A new swapchain
+   */
   static Swapchain* NewSwapchain(
     Device&            device,
     Queue&             presentationQueue,
@@ -44,13 +56,13 @@ public:
     SurfaceImpl*       surface,
     vk::Format         requestedFormat,
     vk::PresentModeKHR presentMode,
-    uint32_t           bufferCount);
+    uint32_t&          bufferCount);
 
   Swapchain(Device& graphicsDevice, Queue& presentationQueue);
 
   ~Swapchain();
 
-  Swapchain(const Swapchain&) = delete;
+  Swapchain(const Swapchain&)            = delete;
   Swapchain& operator=(const Swapchain&) = delete;
 
   void Destroy();
@@ -140,7 +152,7 @@ private:
     SurfaceImpl*       surface,
     vk::Format         requestedFormat,
     vk::PresentModeKHR presentMode,
-    uint32_t           bufferCount);
+    uint32_t&          bufferCount);
 
 private:
   Device&      mGraphicsDevice;
index ccdd2db0d676b52e0a287e0014f5d320d4251ba9..243e8f7ded7ce5aab094d14b4e260897c806b5ef 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * 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.
@@ -360,10 +360,10 @@ Swapchain* Device::ReplaceSwapchainForSurface(SurfaceImpl* surface, Swapchain*&&
 Swapchain* Device::CreateSwapchain(SurfaceImpl*       surface,
                                    vk::Format         requestedFormat,
                                    vk::PresentModeKHR presentMode,
-                                   uint32_t           bufferCount,
+                                   uint32_t&          bufferCount,
                                    Swapchain*&&       oldSwapchain)
 {
-  auto newSwapchain = Swapchain::NewSwapchain(*this, GetPresentQueue(), oldSwapchain ? oldSwapchain->GetVkHandle() : nullptr, surface, requestedFormat, presentMode, bufferCount);
+  auto newSwapchain = Swapchain::NewSwapchain(*this, GetPresentQueue(), oldSwapchain ? oldSwapchain->GetVkHandle() : nullptr, surface, requestedFormat, presentMode, mBufferCount);
 
   if(oldSwapchain)
   {
@@ -395,6 +395,36 @@ Swapchain* Device::CreateSwapchain(SurfaceImpl*       surface,
   return newSwapchain;
 }
 
+void Device::AcquireNextImage()
+{
+  for(auto& s : mSurfaceMap)
+  {
+    auto swapchain = s.second.swapchain;
+    if(swapchain != nullptr)
+    {
+      FramebufferImpl* framebuffer = swapchain->AcquireNextFramebuffer(true);
+
+      // In case something went wrong we will try to replace swapchain once
+      // before calling it a day.
+      if(!framebuffer || !swapchain->IsValid())
+      {
+        // make sure device doesn't do any work before replacing swapchain
+        DeviceWaitIdle();
+
+        // replace swapchain (only once)
+        swapchain = ReplaceSwapchainForSurface(swapchain->GetSurface(), std::move(swapchain));
+
+        // get new valid framebuffer
+        if(swapchain)
+        {
+          framebuffer = swapchain->AcquireNextFramebuffer(true);
+        }
+        DALI_ASSERT_ALWAYS(framebuffer && "Replacing invalid swapchain unsuccessful! Goodbye!");
+      }
+    }
+  }
+}
+
 vk::Result Device::Present(Queue& queue, vk::PresentInfoKHR presentInfo)
 {
   auto lock(queue.Lock());
@@ -577,15 +607,20 @@ void Device::SurfaceResized(unsigned int width, unsigned int height)
   }
 }
 
-uint32_t Device::SwapBuffers()
+uint32_t Device::GetCurrentBufferIndex() const
 {
-  DeviceWaitIdle();
-  mCurrentBufferIndex = (mCurrentBufferIndex + 1) & 1;
   return mCurrentBufferIndex;
 }
 
-uint32_t Device::GetCurrentBufferIndex() const
+uint32_t Device::GetBufferCount() const
+{
+  return mBufferCount;
+}
+
+uint32_t Device::SwapBuffers()
 {
+  // Increase the current buffer index. This should match the number of swapchain images in the main window.
+  mCurrentBufferIndex = (mCurrentBufferIndex + 1) % mBufferCount;
   return mCurrentBufferIndex;
 }
 
index 843ff2fd792b3035ae12ff816a1039b16f638b6c..9be0e250979c2bccd0dd4299f2fe0341d5c17b66 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_GRAPHICS_VULKAN_DEVICE_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.
@@ -73,7 +73,9 @@ public: // Create methods
 
   Swapchain* ReplaceSwapchainForSurface(SurfaceImpl* surface, Swapchain*&& oldSwapchain);
 
-  Swapchain* CreateSwapchain(SurfaceImpl* surface, vk::Format requestedFormat, vk::PresentModeKHR presentMode, uint32_t bufferCount, Swapchain*&& oldSwapchain);
+  Swapchain* CreateSwapchain(SurfaceImpl* surface, vk::Format requestedFormat, vk::PresentModeKHR presentMode, uint32_t& bufferCount, Swapchain*&& oldSwapchain);
+
+  void AcquireNextImage();
 
   vk::Result Present(Queue& queue, vk::PresentInfoKHR presentInfo);
   vk::Result QueueWaitIdle(Queue& queue);
@@ -118,6 +120,8 @@ public: // Getters
 
   uint32_t GetCurrentBufferIndex() const;
 
+  uint32_t GetBufferCount() const;
+
   uint32_t SwapBuffers();
 
   const vk::PhysicalDeviceMemoryProperties& GetMemoryProperties() const
@@ -176,6 +180,7 @@ private: // Members
 
   Platform   mPlatform{Platform::UNDEFINED};
   uint32_t   mCurrentBufferIndex{0u};
+  uint32_t   mBufferCount{2};
   std::mutex mMutex;
 
   bool mHasDepth{false};