From 7607d6bdd8f83aef2ed686f771da0b427f24f959 Mon Sep 17 00:00:00 2001 From: Vladislav Belov Date: Mon, 8 Jun 2026 18:27:10 +0200 Subject: [PATCH] Decouples acquire/present from SubmitScheduler SubmitScheduler doesn't have to know anything about Acquire/Present as its responsibility to schedule and submit command buffers to a queue. --- source/renderer/backend/vulkan/Device.cpp | 7 +- .../backend/vulkan/SubmitScheduler.cpp | 90 ++++++------------- .../renderer/backend/vulkan/SubmitScheduler.h | 26 +++--- source/renderer/backend/vulkan/SwapChain.cpp | 48 +++++++++- source/renderer/backend/vulkan/SwapChain.h | 9 ++ 5 files changed, 95 insertions(+), 85 deletions(-) diff --git a/source/renderer/backend/vulkan/Device.cpp b/source/renderer/backend/vulkan/Device.cpp index 200dea1446..00e3ee21f2 100644 --- a/source/renderer/backend/vulkan/Device.cpp +++ b/source/renderer/backend/vulkan/Device.cpp @@ -626,7 +626,7 @@ std::unique_ptr CDevice::Create(SDL_Window* window) device->m_SamplerManager = std::make_unique(device.get()); device->m_SubmitScheduler = CSubmitScheduler::Create( - device.get(), device->m_GraphicsQueueFamilyIndex, device->m_GraphicsQueue); + device.get(), device->m_GraphicsQueue); if (!device->m_SubmitScheduler) return nullptr; @@ -742,7 +742,10 @@ std::unique_ptr CDevice::CreateSwapChain( if (window) SDL_Vulkan_GetDrawableSize(window, &surfaceDrawableWidth, &surfaceDrawableHeight); return CSwapChain::Create( - this, m_SubmitScheduler.get(), name, m_Surface, surfaceDrawableWidth, surfaceDrawableHeight, + this, m_SubmitScheduler.get(), + m_GraphicsQueueFamilyIndex, m_GraphicsQueue, + name, m_Surface, + surfaceDrawableWidth, surfaceDrawableHeight, vsync, std::move(oldSwapChain)); } diff --git a/source/renderer/backend/vulkan/SubmitScheduler.cpp b/source/renderer/backend/vulkan/SubmitScheduler.cpp index 4cc8fb1ca0..0007c52ed8 100644 --- a/source/renderer/backend/vulkan/SubmitScheduler.cpp +++ b/source/renderer/backend/vulkan/SubmitScheduler.cpp @@ -24,7 +24,6 @@ #include "ps/ConfigDB.h" #include "renderer/backend/vulkan/Device.h" #include "renderer/backend/vulkan/RingCommandContext.h" -#include "renderer/backend/vulkan/SwapChain.h" #include "renderer/backend/vulkan/Utilities.h" #include @@ -40,7 +39,7 @@ namespace Vulkan { std::unique_ptr CSubmitScheduler::Create( - CDevice* device, const uint32_t queueFamilyIndex, VkQueue queue) + CDevice* device, VkQueue queue) { std::unique_ptr submitScheduler{new CSubmitScheduler{device, queue}}; @@ -57,26 +56,6 @@ std::unique_ptr CSubmitScheduler::Create( device->GetVkDevice(), &fenceCreateInfo, nullptr, &fence)); submitScheduler->m_Fences.push_back({fence, INVALID_SUBMIT_HANDLE}); } - - submitScheduler->m_AcquireCommandContext = CRingCommandContext::Create( - device, NUMBER_OF_FRAMES_IN_FLIGHT, queueFamilyIndex, *submitScheduler); - if (!submitScheduler->m_AcquireCommandContext) - return nullptr; - submitScheduler->m_PresentCommandContext = CRingCommandContext::Create( - device, NUMBER_OF_FRAMES_IN_FLIGHT, queueFamilyIndex, *submitScheduler); - if (!submitScheduler->m_PresentCommandContext) - return nullptr; - - submitScheduler->m_DebugWaitIdleBeforeAcquire = g_ConfigDB.Get( - "renderer.backend.vulkan.debugwaitidlebeforeacquire", - submitScheduler->m_DebugWaitIdleBeforeAcquire); - submitScheduler->m_DebugWaitIdleBeforePresent = g_ConfigDB.Get( - "renderer.backend.vulkan.debugwaitidlebeforepresent", - submitScheduler->m_DebugWaitIdleBeforePresent); - submitScheduler->m_DebugWaitIdleAfterPresent = g_ConfigDB.Get( - "renderer.backend.vulkan.debugwaitidleafterpresent", - submitScheduler->m_DebugWaitIdleAfterPresent); - return submitScheduler; } @@ -94,39 +73,6 @@ CSubmitScheduler::~CSubmitScheduler() vkDestroyFence(device, fence.value, nullptr); } -bool CSubmitScheduler::AcquireNextImage(CSwapChain& swapChain, VkSemaphore acquireImageSemaphore) -{ - if (m_DebugWaitIdleBeforeAcquire) - vkDeviceWaitIdle(m_Device->GetVkDevice()); - - if (!swapChain.AcquireNextImage()) - return false; - swapChain.SubmitCommandsAfterAcquireNextImage(*m_AcquireCommandContext); - - m_NextWaitSemaphore = acquireImageSemaphore; - m_NextWaitDstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - m_AcquireCommandContext->Flush(); - return true; -} - -CSubmitScheduler::SubmitHandle CSubmitScheduler::Present(CSwapChain& swapChain, VkSemaphore submitDone) -{ - swapChain.SubmitCommandsBeforePresent(*m_PresentCommandContext); - m_NextSubmitSignalSemaphore = submitDone; - m_PresentCommandContext->Flush(); - const SubmitHandle submitHandle{Flush()}; - - if (m_DebugWaitIdleBeforePresent) - vkDeviceWaitIdle(m_Device->GetVkDevice()); - - swapChain.Present(submitDone, m_Queue); - - if (m_DebugWaitIdleAfterPresent) - vkDeviceWaitIdle(m_Device->GetVkDevice()); - - return submitHandle; -} - CSubmitScheduler::SubmitHandle CSubmitScheduler::Submit(VkCommandBuffer commandBuffer) { m_SubmittedCommandBuffers.emplace_back(commandBuffer); @@ -167,31 +113,45 @@ CSubmitScheduler::SubmitHandle CSubmitScheduler::Flush() VkSubmitInfo submitInfo{}; submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; - if (m_NextWaitSemaphore != VK_NULL_HANDLE) + if (!m_NextWaitSemaphores.empty()) { - submitInfo.waitSemaphoreCount = 1; - submitInfo.pWaitSemaphores = &m_NextWaitSemaphore; - submitInfo.pWaitDstStageMask = &m_NextWaitDstStageMask; + ENSURE(m_NextWaitSemaphores.size() == m_NextWaitDstStageMasks.size()); + submitInfo.waitSemaphoreCount = m_NextWaitSemaphores.size(); + submitInfo.pWaitSemaphores = m_NextWaitSemaphores.data(); + submitInfo.pWaitDstStageMask = m_NextWaitDstStageMasks.data(); } - if (m_NextSubmitSignalSemaphore != VK_NULL_HANDLE) + if (!m_NextSubmitSignalSemaphores.empty()) { - submitInfo.signalSemaphoreCount = 1; - submitInfo.pSignalSemaphores = &m_NextSubmitSignalSemaphore; + submitInfo.signalSemaphoreCount = m_NextSubmitSignalSemaphores.size(); + submitInfo.pSignalSemaphores = m_NextSubmitSignalSemaphores.data(); } submitInfo.commandBufferCount = m_SubmittedCommandBuffers.size(); submitInfo.pCommandBuffers = m_SubmittedCommandBuffers.data(); ENSURE_VK_SUCCESS(vkQueueSubmit(m_Queue, 1, &submitInfo, fence.value)); - m_NextWaitSemaphore = VK_NULL_HANDLE; - m_NextWaitDstStageMask = 0; - m_NextSubmitSignalSemaphore = VK_NULL_HANDLE; + m_NextWaitSemaphores.clear(); + m_NextWaitDstStageMasks.clear(); + m_NextSubmitSignalSemaphores.clear(); m_SubmittedCommandBuffers.clear(); return fence.lastUsedHandle; } +void CSubmitScheduler::EnqueueWaitOnNextSubmit( + VkSemaphore semaphore, const VkPipelineStageFlags stageMask) +{ + m_NextWaitSemaphores.emplace_back(semaphore); + m_NextWaitDstStageMasks.emplace_back(stageMask); +} + +void CSubmitScheduler::EnqueueSignalOnNextSubmit( + VkSemaphore semaphore) +{ + m_NextSubmitSignalSemaphores.emplace_back(semaphore); +} + } // namespace Vulkan } // namespace Backend diff --git a/source/renderer/backend/vulkan/SubmitScheduler.h b/source/renderer/backend/vulkan/SubmitScheduler.h index be9df34f50..b82aebce61 100644 --- a/source/renderer/backend/vulkan/SubmitScheduler.h +++ b/source/renderer/backend/vulkan/SubmitScheduler.h @@ -28,7 +28,6 @@ #include namespace Renderer::Backend::Vulkan { class CRingCommandContext; } -namespace Renderer::Backend::Vulkan { class CSwapChain; } namespace Renderer { @@ -50,13 +49,9 @@ public: static constexpr SubmitHandle INVALID_SUBMIT_HANDLE = 0; static std::unique_ptr Create( - CDevice* device, const uint32_t queueFamilyIndex, VkQueue queue); + CDevice* device, VkQueue queue); ~CSubmitScheduler(); - bool AcquireNextImage(CSwapChain& swapChain, VkSemaphore acquireImageSemaphore); - - SubmitHandle Present(CSwapChain& swapChain, VkSemaphore submitDone); - SubmitHandle Submit(VkCommandBuffer commandBuffer); void WaitUntilFree(const SubmitHandle handle); @@ -65,6 +60,12 @@ public: SubmitHandle Flush(); + /** + * It's a caller responsibility to guarantee a semaphore lifespan. + */ + void EnqueueWaitOnNextSubmit(VkSemaphore semaphore, const VkPipelineStageFlags stageMask); + void EnqueueSignalOnNextSubmit(VkSemaphore semaphore); + private: CSubmitScheduler(CDevice* device, VkQueue queue); @@ -90,18 +91,11 @@ private: }; std::queue m_SubmittedHandles; - VkSemaphore m_NextWaitSemaphore = VK_NULL_HANDLE; - VkPipelineStageFlags m_NextWaitDstStageMask = 0; - VkSemaphore m_NextSubmitSignalSemaphore = VK_NULL_HANDLE; + std::vector m_NextWaitSemaphores; + std::vector m_NextWaitDstStageMasks; + std::vector m_NextSubmitSignalSemaphores; std::vector m_SubmittedCommandBuffers; - - std::unique_ptr m_AcquireCommandContext; - std::unique_ptr m_PresentCommandContext; - - bool m_DebugWaitIdleBeforeAcquire = false; - bool m_DebugWaitIdleBeforePresent = false; - bool m_DebugWaitIdleAfterPresent = false; }; } // namespace Vulkan diff --git a/source/renderer/backend/vulkan/SwapChain.cpp b/source/renderer/backend/vulkan/SwapChain.cpp index 403f2e1589..579519ea98 100644 --- a/source/renderer/backend/vulkan/SwapChain.cpp +++ b/source/renderer/backend/vulkan/SwapChain.cpp @@ -54,6 +54,7 @@ namespace Vulkan // static std::unique_ptr CSwapChain::Create( CDevice* device, CSubmitScheduler* submitScheduler, + const uint32_t queueFamilyIndex, VkQueue queue, const char* name, VkSurfaceKHR surface, int surfaceDrawableWidth, int surfaceDrawableHeight, const bool vsync, std::unique_ptr oldSwapChain) @@ -200,6 +201,7 @@ std::unique_ptr CSwapChain::Create( std::unique_ptr swapChain(new CSwapChain()); swapChain->m_Device = device; swapChain->m_SubmitScheduler = submitScheduler; + swapChain->m_Queue = queue; RETURN_NULLPTR_IF_NOT_VK_SUCCESS(vkCreateSwapchainKHR( device->GetVkDevice(), &swapChainCreateInfo, nullptr, &swapChain->m_SwapChain)); @@ -279,6 +281,25 @@ std::unique_ptr CSwapChain::Create( swapChain->m_SubmitSemaphores.emplace_back(semaphore); } + swapChain->m_AcquireCommandContext = CRingCommandContext::Create( + device, NUMBER_OF_FRAMES_IN_FLIGHT, queueFamilyIndex, *submitScheduler); + if (!swapChain->m_AcquireCommandContext) + return nullptr; + swapChain->m_PresentCommandContext = CRingCommandContext::Create( + device, NUMBER_OF_FRAMES_IN_FLIGHT, queueFamilyIndex, *submitScheduler); + if (!swapChain->m_PresentCommandContext) + return nullptr; + + swapChain->m_DebugWaitIdleBeforeAcquire = g_ConfigDB.Get( + "renderer.backend.vulkan.debugwaitidlebeforeacquire", + swapChain->m_DebugWaitIdleBeforeAcquire); + swapChain->m_DebugWaitIdleBeforePresent = g_ConfigDB.Get( + "renderer.backend.vulkan.debugwaitidlebeforepresent", + swapChain->m_DebugWaitIdleBeforePresent); + swapChain->m_DebugWaitIdleAfterPresent = g_ConfigDB.Get( + "renderer.backend.vulkan.debugwaitidleafterpresent", + swapChain->m_DebugWaitIdleAfterPresent); + swapChain->m_IsValid = true; return swapChain; @@ -331,7 +352,17 @@ bool CSwapChain::AcquireNextBackbuffer() m_SubmitScheduler->WaitUntilFree(frameObject.submitHandle); } - return m_SubmitScheduler->AcquireNextImage(*this, frameObject.acquireImageSemaphore); + if (m_DebugWaitIdleBeforeAcquire) + vkDeviceWaitIdle(m_Device->GetVkDevice()); + + if (!AcquireNextImage()) + return false; + SubmitCommandsAfterAcquireNextImage(*m_AcquireCommandContext); + + m_SubmitScheduler->EnqueueWaitOnNextSubmit( + frameObject.acquireImageSemaphore, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT); + m_AcquireCommandContext->Flush(); + return true; } void CSwapChain::Present() @@ -342,7 +373,20 @@ void CSwapChain::Present() PROFILE3("Present"); FrameObject& frameObject{m_FrameObjects[m_FrameID % m_FrameObjects.size()]}; - frameObject.submitHandle = m_SubmitScheduler->Present(*this, m_SubmitSemaphores[m_CurrentImageIndex]); + + SubmitCommandsBeforePresent(*m_PresentCommandContext); + m_SubmitScheduler->EnqueueSignalOnNextSubmit(m_SubmitSemaphores[m_CurrentImageIndex]); + m_PresentCommandContext->Flush(); + frameObject.submitHandle = m_SubmitScheduler->Flush(); + + if (m_DebugWaitIdleBeforePresent) + vkDeviceWaitIdle(m_Device->GetVkDevice()); + + Present(m_SubmitSemaphores[m_CurrentImageIndex], m_Queue); + + if (m_DebugWaitIdleAfterPresent) + vkDeviceWaitIdle(m_Device->GetVkDevice()); + m_Device->OnPresent(); ++m_FrameID; } diff --git a/source/renderer/backend/vulkan/SwapChain.h b/source/renderer/backend/vulkan/SwapChain.h index 17d234beaa..f9e1a87614 100644 --- a/source/renderer/backend/vulkan/SwapChain.h +++ b/source/renderer/backend/vulkan/SwapChain.h @@ -84,6 +84,7 @@ private: static std::unique_ptr Create( CDevice* device, CSubmitScheduler* submitScheduler, + const uint32_t queueFamilyIndex, VkQueue queue, const char* name, VkSurfaceKHR surface, int surfaceDrawableWidth, int surfaceDrawableHeight, const bool vsync, std::unique_ptr oldSwapChain); @@ -92,6 +93,7 @@ private: CDevice* m_Device = nullptr; CSubmitScheduler* m_SubmitScheduler{nullptr}; + VkQueue m_Queue{VK_NULL_HANDLE}; bool m_IsValid = false; VkSwapchainKHR m_SwapChain = VK_NULL_HANDLE; @@ -156,6 +158,13 @@ private: std::vector m_Backbuffers; std::unique_ptr m_BackbufferReadbackTexture; + + std::unique_ptr m_AcquireCommandContext; + std::unique_ptr m_PresentCommandContext; + + bool m_DebugWaitIdleBeforeAcquire = false; + bool m_DebugWaitIdleBeforePresent = false; + bool m_DebugWaitIdleAfterPresent = false; }; } // namespace Vulkan