From 49a18f68e14a00e8ed6b787d564244431c99e38a Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Tue, 17 Feb 2026 17:43:28 +0800 Subject: [PATCH] [Fix] (MG_Backend/DirectVulkan): fixing inter-frame swapchain sync? --- .../DirectVulkan/Renderer/FrameContext.cpp | 60 +++++++++++++------ .../DirectVulkan/Renderer/FrameContext.h | 7 ++- .../DirectVulkan/Renderer/VulkanRenderer.cpp | 8 ++- 3 files changed, 54 insertions(+), 21 deletions(-) diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/FrameContext.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/FrameContext.cpp index 3311f529..0a7c9211 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/FrameContext.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/FrameContext.cpp @@ -53,6 +53,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { for (Uint32 i = 0; i < frameCount; ++i) { DestroySyncObjectsForFrame(device, i); } + DestroySwapchainSemaphores(device); if (device != VK_NULL_HANDLE && commandPool != VK_NULL_HANDLE && !m_frames.empty()) { vkFreeCommandBuffers(device, commandPool, frameCount, commandBuffers.data()); } @@ -107,6 +108,36 @@ namespace MobileGL::MG_Backend::DirectVulkan { frame.hasCommandBufferRecorded = true; } + VkResult FrameContext::InitializeSwapchainSemaphores(VkDevice device, Uint32 swapchainImageCount) { + DestroySwapchainSemaphores(device); + if (swapchainImageCount == 0) { + return VK_SUCCESS; + } + + m_swapchainImageRenderFinishedSemaphores.assign(swapchainImageCount, VK_NULL_HANDLE); + VkSemaphoreCreateInfo semaphoreInfo{VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO}; + for (Uint32 imageIndex = 0; imageIndex < swapchainImageCount; ++imageIndex) { + VkResult result = + vkCreateSemaphore(device, &semaphoreInfo, nullptr, &m_swapchainImageRenderFinishedSemaphores[imageIndex]); + if (result != VK_SUCCESS) { + DestroySwapchainSemaphores(device); + return result; + } + } + return VK_SUCCESS; + } + + void FrameContext::DestroySwapchainSemaphores(VkDevice device) { + if (device != VK_NULL_HANDLE) { + for (auto semaphore : m_swapchainImageRenderFinishedSemaphores) { + if (semaphore != VK_NULL_HANDLE) { + vkDestroySemaphore(device, semaphore, nullptr); + } + } + } + m_swapchainImageRenderFinishedSemaphores.clear(); + } + Bool FrameContext::TransitionToPresent(VkImage image, VkImageLayout oldLayout, VkImageLayout presentLayout) { auto& frame = GetCurrent(); if (frame.hasCommandBufferRecorded || frame.isCommandRecording || oldLayout == presentLayout || @@ -137,12 +168,14 @@ namespace MobileGL::MG_Backend::DirectVulkan { return true; } - FrameContext::SubmitInfoPacket FrameContext::GetSubmitInfo(Bool shouldSubmitCommandBuffer) const { + FrameContext::SubmitInfoPacket FrameContext::GetSubmitInfo(Bool shouldSubmitCommandBuffer, + Uint32 swapchainImageIndex) const { const auto& frame = GetCurrent(); MOBILEGL_ASSERT(!frame.isCommandRecording, "GetSubmitInfo called while command buffer recording is still active"); + AssertValidSwapchainImageIndex(swapchainImageIndex); SubmitInfoPacket packet{}; packet.waitSemaphore = frame.imageAvailableSemaphore; - packet.signalSemaphore = frame.renderFinishedSemaphore; + packet.signalSemaphore = m_swapchainImageRenderFinishedSemaphores[swapchainImageIndex]; packet.commandBuffer = frame.commandBuffer; packet.submitInfo.waitSemaphoreCount = 1; @@ -156,9 +189,9 @@ namespace MobileGL::MG_Backend::DirectVulkan { } FrameContext::PresentInfoPacket FrameContext::GetPresentInfo(VkSwapchainKHR swapchain, const Uint32& imageIndex) const { - const auto& frame = GetCurrent(); + AssertValidSwapchainImageIndex(imageIndex); PresentInfoPacket packet{}; - packet.waitSemaphore = frame.renderFinishedSemaphore; + packet.waitSemaphore = m_swapchainImageRenderFinishedSemaphores[imageIndex]; packet.swapchain = swapchain; packet.imageIndex = &imageIndex; @@ -200,6 +233,11 @@ namespace MobileGL::MG_Backend::DirectVulkan { MOBILEGL_ASSERT(frameIndex < m_frames.size(), "FrameContext index out of range"); } + void FrameContext::AssertValidSwapchainImageIndex(Uint32 imageIndex) const { + MOBILEGL_ASSERT(imageIndex < m_swapchainImageRenderFinishedSemaphores.size(), + "FrameContext swapchain image index out of range"); + } + VkResult FrameContext::CreateSyncObjectsForFrame(VkDevice device, Uint32 frameIndex, const VkSemaphoreCreateInfo& semaphoreInfo, const VkFenceCreateInfo& fenceInfo) { @@ -213,18 +251,9 @@ namespace MobileGL::MG_Backend::DirectVulkan { return result; } - result = vkCreateSemaphore(device, &semaphoreInfo, nullptr, &frame.renderFinishedSemaphore); - if (result != VK_SUCCESS) { - vkDestroySemaphore(device, frame.imageAvailableSemaphore, nullptr); - frame.imageAvailableSemaphore = VK_NULL_HANDLE; - return result; - } - result = vkCreateFence(device, &fenceInfo, nullptr, &frame.imageInFlightFence); if (result != VK_SUCCESS) { - vkDestroySemaphore(device, frame.renderFinishedSemaphore, nullptr); vkDestroySemaphore(device, frame.imageAvailableSemaphore, nullptr); - frame.renderFinishedSemaphore = VK_NULL_HANDLE; frame.imageAvailableSemaphore = VK_NULL_HANDLE; return result; } @@ -242,11 +271,6 @@ namespace MobileGL::MG_Backend::DirectVulkan { } frame.imageInFlightFence = VK_NULL_HANDLE; - if (device != VK_NULL_HANDLE && frame.renderFinishedSemaphore != VK_NULL_HANDLE) { - vkDestroySemaphore(device, frame.renderFinishedSemaphore, nullptr); - } - frame.renderFinishedSemaphore = VK_NULL_HANDLE; - if (device != VK_NULL_HANDLE && frame.imageAvailableSemaphore != VK_NULL_HANDLE) { vkDestroySemaphore(device, frame.imageAvailableSemaphore, nullptr); } diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/FrameContext.h b/MobileGL/MG_Backend/DirectVulkan/Renderer/FrameContext.h index 5924dd26..83815445 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/FrameContext.h +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/FrameContext.h @@ -32,7 +32,6 @@ namespace MobileGL::MG_Backend::DirectVulkan { struct FrameData { VkCommandBuffer commandBuffer = VK_NULL_HANDLE; VkSemaphore imageAvailableSemaphore = VK_NULL_HANDLE; - VkSemaphore renderFinishedSemaphore = VK_NULL_HANDLE; VkFence imageInFlightFence = VK_NULL_HANDLE; Bool isCommandRecording = false; Bool hasCommandBufferRecorded = false; @@ -49,9 +48,11 @@ namespace MobileGL::MG_Backend::DirectVulkan { VkCommandBuffer& BeginCommandRecording(VkCommandBufferUsageFlags flags = 0, const VkCommandBufferInheritanceInfo* pInheritanceInfo = nullptr); void EndCommandRecording(); + VkResult InitializeSwapchainSemaphores(VkDevice device, Uint32 swapchainImageCount); + void DestroySwapchainSemaphores(VkDevice device); Bool TransitionToPresent(VkImage image, VkImageLayout oldLayout, VkImageLayout presentLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR); - SubmitInfoPacket GetSubmitInfo(Bool shouldSubmitCommandBuffer) const; + SubmitInfoPacket GetSubmitInfo(Bool shouldSubmitCommandBuffer, Uint32 swapchainImageIndex) const; PresentInfoPacket GetPresentInfo(VkSwapchainKHR swapchain, const Uint32& imageIndex) const; VkResult WaitAndAcquireNextImage(VkDevice device, VkSwapchainKHR swapchain, Uint32& outImageIndex, Uint64 timeout = UINT64_MAX, VkFence acquireFence = VK_NULL_HANDLE); @@ -61,6 +62,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { private: void AssertValidFrameIndex(Uint32 frameIndex) const; + void AssertValidSwapchainImageIndex(Uint32 imageIndex) const; VkResult CreateSyncObjectsForFrame(VkDevice device, Uint32 frameIndex, const VkSemaphoreCreateInfo& semaphoreInfo, @@ -68,6 +70,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { void DestroySyncObjectsForFrame(VkDevice device, Uint32 frameIndex); Vector m_frames; + Vector m_swapchainImageRenderFinishedSemaphores; Uint32 currentFrameIndex = 0; }; } // namespace MobileGL::MG_Backend::DirectVulkan diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp index a61a9223..ef4241da 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp @@ -103,6 +103,9 @@ namespace MobileGL::MG_Backend::DirectVulkan { void VulkanRenderer::CreateFrameContexts() { VK_VERIFY(m_frameContext.Initialize(m_device, m_commandPool, m_config.MaxFramesInFlight), "CreateFrameContexts"); + VK_VERIFY(m_frameContext.InitializeSwapchainSemaphores( + m_device, static_cast(m_swapchainObject.GetImageCount())), + "CreateFrameContexts, InitializeSwapchainSemaphores"); MGLOG_I("CreateFrameContexts completed"); } @@ -688,7 +691,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { const Bool shouldSubmitCommandBuffer = frame.hasCommandBufferRecorded || needsLayoutTransitionForPresent; // 1) Submit current frame work. - auto submitPacket = m_frameContext.GetSubmitInfo(shouldSubmitCommandBuffer); + auto submitPacket = m_frameContext.GetSubmitInfo(shouldSubmitCommandBuffer, m_imageIndexAcquired); VK_VERIFY(vkQueueSubmit(m_graphicsQueue, 1, &submitPacket.submitInfo, frame.imageInFlightFence)); frame.isCommandRecording = false; frame.hasCommandBufferRecorded = false; @@ -1399,6 +1402,9 @@ namespace MobileGL::MG_Backend::DirectVulkan { ShutdownSwapchain(); CreateSwapchain(); + VK_VERIFY(m_frameContext.InitializeSwapchainSemaphores( + m_device, static_cast(m_swapchainObject.GetImageCount())), + "RecreateSwapchain, InitializeSwapchainSemaphores"); CreateDepthStencilResources(); CreateDefaultRenderPass(); CreateDefaultFramebuffers();