From bc104859f3173f29d5cbbea004afc33c164296be Mon Sep 17 00:00:00 2001 From: goeiecool9999 <7033575+goeiecool9999@users.noreply.github.com> Date: Fri, 25 Nov 2022 09:51:47 +0100 Subject: [PATCH] Vulkan: return to more conventional swapchain sync method, encapsulate more code (#525) --- .../Latte/Renderer/Vulkan/SwapchainInfoVk.cpp | 81 ++++++++++++++++--- .../Latte/Renderer/Vulkan/SwapchainInfoVk.h | 20 ++++- .../Latte/Renderer/Vulkan/VulkanRenderer.cpp | 38 +++------ .../HW/Latte/Renderer/Vulkan/VulkanRenderer.h | 2 +- 4 files changed, 102 insertions(+), 39 deletions(-) diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/SwapchainInfoVk.cpp b/src/Cafe/HW/Latte/Renderer/Vulkan/SwapchainInfoVk.cpp index d6d0d1a6..ad3b71d4 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/SwapchainInfoVk.cpp +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/SwapchainInfoVk.cpp @@ -13,7 +13,12 @@ void SwapchainInfoVk::Create(VkPhysicalDevice physicalDevice, VkDevice logicalDe m_surfaceFormat = ChooseSurfaceFormat(details.formats); m_actualExtent = ChooseSwapExtent(details.capabilities); - uint32_t image_count = details.capabilities.minImageCount; + // use at least two swapchain images. fewer than that causes problems on some drivers + uint32_t image_count = std::max(2u, details.capabilities.minImageCount); + if(details.capabilities.maxImageCount > 0) + image_count = std::min(image_count, details.capabilities.maxImageCount); + if(image_count < 2) + cemuLog_force("Vulkan: Swapchain image count less than 2 may cause problems"); VkSwapchainCreateInfoKHR create_info = CreateSwapchainCreateInfo(surface, details, m_surfaceFormat, image_count, m_actualExtent); create_info.oldSwapchain = nullptr; @@ -103,15 +108,25 @@ void SwapchainInfoVk::Create(VkPhysicalDevice physicalDevice, VkDevice logicalDe if (result != VK_SUCCESS) UnrecoverableError("Failed to create framebuffer for swapchain"); } - m_swapchainPresentSemaphores.resize(m_swapchainImages.size()); - // create present semaphore + + m_presentSemaphores.resize(m_swapchainImages.size()); + // create present semaphores VkSemaphoreCreateInfo info = {}; info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; - for (auto& semaphore : m_swapchainPresentSemaphores){ + for (auto& semaphore : m_presentSemaphores){ if (vkCreateSemaphore(logicalDevice, &info, nullptr, &semaphore) != VK_SUCCESS) UnrecoverableError("Failed to create semaphore for swapchain present"); } + m_acquireSemaphores.resize(m_swapchainImages.size()); + // create acquire semaphores + info = {}; + info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + for (auto& semaphore : m_acquireSemaphores){ + if (vkCreateSemaphore(logicalDevice, &info, nullptr, &semaphore) != VK_SUCCESS) + UnrecoverableError("Failed to create semaphore for swapchain acquire"); + } + VkFenceCreateInfo fenceInfo = {}; fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT; @@ -119,6 +134,7 @@ void SwapchainInfoVk::Create(VkPhysicalDevice physicalDevice, VkDevice logicalDe if (result != VK_SUCCESS) UnrecoverableError("Failed to create fence for swapchain"); + m_acquireIndex = 0; hasDefinedSwapchainImage = false; } @@ -126,9 +142,13 @@ void SwapchainInfoVk::Cleanup() { m_swapchainImages.clear(); - for (auto& sem: m_swapchainPresentSemaphores) + for (auto& sem: m_acquireSemaphores) vkDestroySemaphore(m_logicalDevice, sem, nullptr); - m_swapchainPresentSemaphores.clear(); + m_acquireSemaphores.clear(); + + for (auto& sem: m_presentSemaphores) + vkDestroySemaphore(m_logicalDevice, sem, nullptr); + m_presentSemaphores.clear(); if (m_swapchainRenderPass) { @@ -159,12 +179,55 @@ void SwapchainInfoVk::Cleanup() bool SwapchainInfoVk::IsValid() const { - return swapchain && m_imageAvailableFence; + return swapchain && !m_acquireSemaphores.empty(); } -void SwapchainInfoVk::WaitAvailableFence() const +void SwapchainInfoVk::WaitAvailableFence() { - vkWaitForFences(m_logicalDevice, 1, &m_imageAvailableFence, VK_TRUE, UINT64_MAX); + if(m_awaitableFence != VK_NULL_HANDLE) + vkWaitForFences(m_logicalDevice, 1, &m_awaitableFence, VK_TRUE, UINT64_MAX); + m_awaitableFence = VK_NULL_HANDLE; +} + +void SwapchainInfoVk::ResetAvailableFence() const +{ + vkResetFences(m_logicalDevice, 1, &m_imageAvailableFence); +} + +VkSemaphore SwapchainInfoVk::ConsumeAcquireSemaphore() +{ + VkSemaphore ret = m_currentSemaphore; + m_currentSemaphore = VK_NULL_HANDLE; + return ret; +} + +bool SwapchainInfoVk::AcquireImage(uint64 timeout) +{ + WaitAvailableFence(); + ResetAvailableFence(); + + VkSemaphore acquireSemaphore = m_acquireSemaphores[m_acquireIndex]; + VkResult result = vkAcquireNextImageKHR(m_logicalDevice, swapchain, timeout, acquireSemaphore, m_imageAvailableFence, &swapchainImageIndex); + if (result == VK_TIMEOUT) + { + return false; + } + else if (result != VK_SUCCESS) + { + if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) + m_shouldRecreate = true; + + if (result == VK_ERROR_OUT_OF_DATE_KHR) + return false; + + if (result != VK_ERROR_OUT_OF_DATE_KHR && result != VK_SUBOPTIMAL_KHR) + throw std::runtime_error(fmt::format("Failed to acquire next image: {}", result)); + } + m_currentSemaphore = acquireSemaphore; + m_awaitableFence = m_imageAvailableFence; + m_acquireIndex = (m_acquireIndex + 1) % m_swapchainImages.size(); + + return true; } void SwapchainInfoVk::UnrecoverableError(const char* errMsg) diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/SwapchainInfoVk.h b/src/Cafe/HW/Latte/Renderer/Vulkan/SwapchainInfoVk.h index d2d5d23e..26dbc7d1 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/SwapchainInfoVk.h +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/SwapchainInfoVk.h @@ -34,7 +34,14 @@ struct SwapchainInfoVk bool IsValid() const; - void WaitAvailableFence() const; + void WaitAvailableFence(); + void ResetAvailableFence() const; + + bool AcquireImage(uint64 timeout); + // retrieve semaphore of last acquire for submitting a wait operation + // only one wait operation must be submitted per acquire (which submits a single signal operation) + // therefore subsequent calls will return a NULL handle + VkSemaphore ConsumeAcquireSemaphore(); static void UnrecoverableError(const char* errMsg); @@ -76,7 +83,6 @@ struct SwapchainInfoVk VkSurfaceFormatKHR m_surfaceFormat{}; VkSwapchainKHR swapchain{}; Vector2i m_desiredExtent{}; - VkFence m_imageAvailableFence{}; uint32 swapchainImageIndex = (uint32)-1; @@ -84,11 +90,17 @@ struct SwapchainInfoVk std::vector m_swapchainImages; std::vector m_swapchainImageViews; std::vector m_swapchainFramebuffers; - std::vector m_swapchainPresentSemaphores; - std::array m_swapchainQueueFamilyIndices; + std::vector m_presentSemaphores; // indexed by swapchainImageIndex VkRenderPass m_swapchainRenderPass = nullptr; private: + uint32 m_acquireIndex = 0; + std::vector m_acquireSemaphores; // indexed by m_acquireIndex + VkFence m_imageAvailableFence{}; + VkSemaphore m_currentSemaphore = VK_NULL_HANDLE; + VkFence m_awaitableFence = VK_NULL_HANDLE; + + std::array m_swapchainQueueFamilyIndices; VkExtent2D m_actualExtent{}; }; diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp index 6f2d5130..f1f5afdd 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp @@ -1665,7 +1665,6 @@ bool VulkanRenderer::ImguiBegin(bool mainWindow) draw_endRenderPass(); m_state.currentPipeline = VK_NULL_HANDLE; - chainInfo.WaitAvailableFence(); ImGui_ImplVulkan_CreateFontsTexture(m_state.currentCommandBuffer); ImGui_ImplVulkan_NewFrame(m_state.currentCommandBuffer, chainInfo.m_swapchainFramebuffers[chainInfo.swapchainImageIndex], chainInfo.getExtent()); ImGui_UpdateWindowInformation(mainWindow); @@ -1722,7 +1721,6 @@ bool VulkanRenderer::BeginFrame(bool mainWindow) auto& chainInfo = GetChainInfo(mainWindow); - chainInfo.WaitAvailableFence(); VkClearColorValue clearColor{ 0, 0, 0, 0 }; ClearColorImageRaw(chainInfo.m_swapchainImages[chainInfo.swapchainImageIndex], 0, 0, clearColor, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR); @@ -1848,7 +1846,7 @@ void VulkanRenderer::WaitForNextFinishedCommandBuffer() ProcessFinishedCommandBuffers(); } -void VulkanRenderer::SubmitCommandBuffer(VkSemaphore* signalSemaphore, VkSemaphore* waitSemaphore) +void VulkanRenderer::SubmitCommandBuffer(VkSemaphore signalSemaphore, VkSemaphore waitSemaphore) { draw_endRenderPass(); @@ -1863,11 +1861,11 @@ void VulkanRenderer::SubmitCommandBuffer(VkSemaphore* signalSemaphore, VkSemapho // signal current command buffer semaphore VkSemaphore signalSemArray[2]; - if (signalSemaphore) + if (signalSemaphore != VK_NULL_HANDLE) { submitInfo.signalSemaphoreCount = 2; signalSemArray[0] = m_commandBufferSemaphores[m_commandBufferIndex]; // signal current - signalSemArray[1] = *signalSemaphore; // signal current + signalSemArray[1] = signalSemaphore; // signal current submitInfo.pSignalSemaphores = signalSemArray; } else @@ -1883,8 +1881,8 @@ void VulkanRenderer::SubmitCommandBuffer(VkSemaphore* signalSemaphore, VkSemapho submitInfo.waitSemaphoreCount = 0; if (m_numSubmittedCmdBuffers > 0) waitSemArray[submitInfo.waitSemaphoreCount++] = prevSem; // wait on semaphore from previous submit - if (waitSemaphore) - waitSemArray[submitInfo.waitSemaphoreCount++] = *waitSemaphore; + if (waitSemaphore != VK_NULL_HANDLE) + waitSemArray[submitInfo.waitSemaphoreCount++] = waitSemaphore; submitInfo.pWaitDstStageMask = semWaitStageMask; submitInfo.pWaitSemaphores = waitSemArray; @@ -2546,20 +2544,11 @@ bool VulkanRenderer::AcquireNextSwapchainImage(bool mainWindow) if (!UpdateSwapchainProperties(mainWindow)) return false; - vkResetFences(m_logicalDevice, 1, &chainInfo.m_imageAvailableFence); - VkResult result = vkAcquireNextImageKHR(m_logicalDevice, chainInfo.swapchain, std::numeric_limits::max(), VK_NULL_HANDLE, chainInfo.m_imageAvailableFence, &chainInfo.swapchainImageIndex); - if (result != VK_SUCCESS) - { - if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) - chainInfo.m_shouldRecreate = true; - - if (result == VK_ERROR_OUT_OF_DATE_KHR) - return false; - - if (result != VK_ERROR_OUT_OF_DATE_KHR && result != VK_SUBOPTIMAL_KHR) - throw std::runtime_error(fmt::format("Failed to acquire next image: {}", result)); - } + bool result = chainInfo.AcquireImage(UINT64_MAX); + if (!result) + return false; + SubmitCommandBuffer(VK_NULL_HANDLE, chainInfo.ConsumeAcquireSemaphore()); return true; } @@ -2568,6 +2557,8 @@ void VulkanRenderer::RecreateSwapchain(bool mainWindow, bool skipCreate) SubmitCommandBuffer(); WaitDeviceIdle(); auto& chainInfo = GetChainInfo(mainWindow); + // make sure fence has no signal operation submitted + chainInfo.WaitAvailableFence(); Vector2i size; if (mainWindow) @@ -2633,14 +2624,13 @@ void VulkanRenderer::SwapBuffer(bool mainWindow) if (!chainInfo.hasDefinedSwapchainImage) { - chainInfo.WaitAvailableFence(); // set the swapchain image to a defined state VkClearColorValue clearColor{ 0, 0, 0, 0 }; ClearColorImageRaw(chainInfo.m_swapchainImages[chainInfo.swapchainImageIndex], 0, 0, clearColor, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR); } - VkSemaphore presentSemaphore = chainInfo.m_swapchainPresentSemaphores[chainInfo.swapchainImageIndex]; - SubmitCommandBuffer(&presentSemaphore); // submit all command and signal semaphore + VkSemaphore presentSemaphore = chainInfo.m_presentSemaphores[chainInfo.swapchainImageIndex]; + SubmitCommandBuffer(presentSemaphore); // submit all command and signal semaphore cemu_assert_debug(m_numSubmittedCmdBuffers > 0); @@ -2701,7 +2691,6 @@ void VulkanRenderer::ClearColorbuffer(bool padView) if (chainInfo.swapchainImageIndex == -1) return; - chainInfo.WaitAvailableFence(); VkClearColorValue clearColor{ 0, 0, 0, 0 }; ClearColorImageRaw(chainInfo.m_swapchainImages[chainInfo.swapchainImageIndex], 0, 0, clearColor, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL); } @@ -2792,7 +2781,6 @@ void VulkanRenderer::DrawBackbufferQuad(LatteTextureView* texView, RendererOutpu LatteTextureViewVk* texViewVk = (LatteTextureViewVk*)texView; draw_endRenderPass(); - chainInfo.WaitAvailableFence(); if (clearBackground) ClearColorbuffer(padView); diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h index 27fb57fb..2dd8735a 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h @@ -233,7 +233,7 @@ public: void InitFirstCommandBuffer(); void ProcessFinishedCommandBuffers(); void WaitForNextFinishedCommandBuffer(); - void SubmitCommandBuffer(VkSemaphore* signalSemaphore = nullptr, VkSemaphore* waitSemaphore = nullptr); + void SubmitCommandBuffer(VkSemaphore signalSemaphore = VK_NULL_HANDLE, VkSemaphore waitSemaphore = VK_NULL_HANDLE); void RequestSubmitSoon(); void RequestSubmitOnIdle();