From 4d68446f1497c350f5ca9d3d079937f9813ab304 Mon Sep 17 00:00:00 2001 From: goeiecool9999 <7033575+goeiecool9999@users.noreply.github.com> Date: Fri, 11 Nov 2022 08:14:38 +0100 Subject: [PATCH] Vulkan: Further swapchain code improvements. (#473) --- .../Latte/Renderer/Vulkan/SwapchainInfoVk.cpp | 14 ++-- .../Latte/Renderer/Vulkan/SwapchainInfoVk.h | 22 ++--- .../Latte/Renderer/Vulkan/VulkanRenderer.cpp | 83 +++++++++---------- .../HW/Latte/Renderer/Vulkan/VulkanRenderer.h | 3 +- src/gui/canvas/VulkanCanvas.cpp | 8 +- src/imgui/imgui_impl_vulkan.cpp | 4 +- src/imgui/imgui_impl_vulkan.h | 2 +- 7 files changed, 58 insertions(+), 78 deletions(-) diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/SwapchainInfoVk.cpp b/src/Cafe/HW/Latte/Renderer/Vulkan/SwapchainInfoVk.cpp index fd227106..8d96acd0 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/SwapchainInfoVk.cpp +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/SwapchainInfoVk.cpp @@ -11,14 +11,14 @@ void SwapchainInfoVk::Create(VkPhysicalDevice physicalDevice, VkDevice logicalDe m_logicalDevice = logicalDevice; const auto details = QuerySwapchainSupport(surface, physicalDevice); m_surfaceFormat = ChooseSurfaceFormat(details.formats); - swapchainExtent = ChooseSwapExtent(details.capabilities, getSize()); + m_actualExtent = ChooseSwapExtent(details.capabilities); // calculate number of swapchain presentation images uint32_t image_count = details.capabilities.minImageCount + 1; if (details.capabilities.maxImageCount > 0 && image_count > details.capabilities.maxImageCount) image_count = details.capabilities.maxImageCount; - VkSwapchainCreateInfoKHR create_info = CreateSwapchainCreateInfo(surface, details, m_surfaceFormat, image_count, swapchainExtent); + VkSwapchainCreateInfoKHR create_info = CreateSwapchainCreateInfo(surface, details, m_surfaceFormat, image_count, m_actualExtent); create_info.oldSwapchain = nullptr; create_info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; @@ -26,8 +26,6 @@ void SwapchainInfoVk::Create(VkPhysicalDevice physicalDevice, VkDevice logicalDe if (result != VK_SUCCESS) UnrecoverableError("Error attempting to create a swapchain"); - sizeOutOfDate = false; - result = vkGetSwapchainImagesKHR(logicalDevice, swapchain, &image_count, nullptr); if (result != VK_SUCCESS) UnrecoverableError("Error attempting to retrieve the count of swapchain images"); @@ -101,8 +99,8 @@ void SwapchainInfoVk::Create(VkPhysicalDevice physicalDevice, VkDevice logicalDe framebufferInfo.renderPass = m_swapchainRenderPass; framebufferInfo.attachmentCount = 1; framebufferInfo.pAttachments = attachments; - framebufferInfo.width = swapchainExtent.width; - framebufferInfo.height = swapchainExtent.height; + framebufferInfo.width = m_actualExtent.width; + framebufferInfo.height = m_actualExtent.height; framebufferInfo.layers = 1; result = vkCreateFramebuffer(logicalDevice, &framebufferInfo, nullptr, &m_swapchainFramebuffers[i]); if (result != VK_SUCCESS) @@ -293,12 +291,12 @@ VkSurfaceFormatKHR SwapchainInfoVk::ChooseSurfaceFormat(const std::vector::max()) return capabilities.currentExtent; - VkExtent2D actualExtent = { (uint32)size.x, (uint32)size.y }; + VkExtent2D actualExtent = { (uint32)m_desiredExtent.x, (uint32)m_desiredExtent.y }; actualExtent.width = std::max(capabilities.minImageExtent.width, std::min(capabilities.maxImageExtent.width, actualExtent.width)); actualExtent.height = std::max(capabilities.minImageExtent.height, std::min(capabilities.maxImageExtent.height, actualExtent.height)); return actualExtent; diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/SwapchainInfoVk.h b/src/Cafe/HW/Latte/Renderer/Vulkan/SwapchainInfoVk.h index 9201c30a..06add21a 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/SwapchainInfoVk.h +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/SwapchainInfoVk.h @@ -42,20 +42,14 @@ struct SwapchainInfoVk VkPresentModeKHR ChoosePresentMode(const std::vector& modes); VkSurfaceFormatKHR ChooseSurfaceFormat(const std::vector& formats) const; - VkExtent2D ChooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities, const Vector2i& size) const; + VkExtent2D ChooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities) const; VkSwapchainCreateInfoKHR CreateSwapchainCreateInfo(VkSurfaceKHR surface, const SwapchainSupportDetails& swapchainSupport, const VkSurfaceFormatKHR& surfaceFormat, uint32 imageCount, const VkExtent2D& extent); - void setSize(const Vector2i& newSize) + VkExtent2D getExtent() const { - desiredExtent = newSize; - sizeOutOfDate = true; - } - - const Vector2i& getSize() const - { - return desiredExtent; + return m_actualExtent; } SwapchainInfoVk(VkSurfaceKHR surface, bool mainWindow) @@ -69,8 +63,9 @@ struct SwapchainInfoVk bool mainWindow{}; - bool sizeOutOfDate{}; + bool m_shouldRecreate = false; bool m_usesSRGB = false; + VSync m_vsyncState = VSync::Immediate; bool hasDefinedSwapchainImage{}; // indicates if the swapchain image is in a defined state VkPhysicalDevice m_physicalDevice{}; @@ -78,11 +73,10 @@ struct SwapchainInfoVk VkSurfaceKHR surface{}; VkSurfaceFormatKHR m_surfaceFormat{}; VkSwapchainKHR swapchain{}; - VkExtent2D swapchainExtent{}; + Vector2i m_desiredExtent{}; VkFence m_imageAvailableFence{}; uint32 swapchainImageIndex = (uint32)-1; uint32 m_acquireIndex = 0; // increases with every successful vkAcquireNextImageKHR - VSync m_vsyncState = VSync::Immediate; // swapchain image ringbuffer (indexed by swapchainImageIndex) @@ -94,8 +88,6 @@ struct SwapchainInfoVk VkRenderPass m_swapchainRenderPass = nullptr; - - private: - Vector2i desiredExtent; + VkExtent2D m_actualExtent{}; }; diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp index 3a219b7b..6b76ac7e 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp @@ -651,7 +651,7 @@ void VulkanRenderer::Initialize(const Vector2i& size, bool mainWindow) if (mainWindow) { m_mainSwapchainInfo = std::make_unique(surface, mainWindow); - SetSwapchainTargetSize(size, mainWindow); + m_mainSwapchainInfo->m_desiredExtent = size; m_mainSwapchainInfo->Create(m_physicalDevice, m_logicalDevice); // aquire first command buffer @@ -660,7 +660,7 @@ void VulkanRenderer::Initialize(const Vector2i& size, bool mainWindow) else { m_padSwapchainInfo = std::make_unique(surface, mainWindow); - SetSwapchainTargetSize(size, mainWindow); + m_padSwapchainInfo->m_desiredExtent = size; // todo: figure out a way to exclusively create swapchain on main LatteThread m_padSwapchainInfo->Create(m_physicalDevice, m_logicalDevice); } @@ -977,11 +977,6 @@ void VulkanRenderer::HandleScreenshotRequest(LatteTextureView* texView, bool pad SaveScreenshot(rgb_data, width, height, !padView); } -void VulkanRenderer::SetSwapchainTargetSize(const Vector2i& size, bool mainWindow) -{ - GetChainInfo(mainWindow).setSize(size); -} - static const float kQueuePriority = 1.0f; std::vector VulkanRenderer::CreateQueueCreateInfos(const std::set& uniqueQueueFamilies) const @@ -1671,7 +1666,7 @@ bool VulkanRenderer::ImguiBegin(bool mainWindow) m_state.currentPipeline = VK_NULL_HANDLE; ImGui_ImplVulkan_CreateFontsTexture(m_state.currentCommandBuffer); - ImGui_ImplVulkan_NewFrame(m_state.currentCommandBuffer, chainInfo.m_swapchainFramebuffers[chainInfo.swapchainImageIndex], chainInfo.swapchainExtent); + ImGui_ImplVulkan_NewFrame(m_state.currentCommandBuffer, chainInfo.m_swapchainFramebuffers[chainInfo.swapchainImageIndex], chainInfo.getExtent()); ImGui_UpdateWindowInformation(mainWindow); ImGui::NewFrame(); return true; @@ -2543,18 +2538,8 @@ bool VulkanRenderer::AcquireNextSwapchainImage(bool mainWindow) auto& chainInfo = GetChainInfo(mainWindow); - UpdateVSyncState(mainWindow); - - const bool latteBufferUsesSRGB = mainWindow ? LatteGPUState.tvBufferUsesSRGB : LatteGPUState.drcBufferUsesSRGB; - if (chainInfo.sizeOutOfDate || chainInfo.m_usesSRGB != latteBufferUsesSRGB) - { - try - { - RecreateSwapchain(mainWindow); - chainInfo.m_usesSRGB = latteBufferUsesSRGB; - } - catch (std::exception&) { cemu_assert_debug(false); } - } + if (!UpdateSwapchainProperties(mainWindow)) + return false; if (chainInfo.swapchainImageIndex != -1) return true; // image already reserved @@ -2567,24 +2552,14 @@ bool VulkanRenderer::AcquireNextSwapchainImage(bool mainWindow) VkResult result = vkAcquireNextImageKHR(m_logicalDevice, chainInfo.swapchain, std::numeric_limits::max(), acquireSemaphore, chainInfo.m_imageAvailableFence, &chainInfo.swapchainImageIndex); if (result != VK_SUCCESS) { - while (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) // todo: handle error state correctly. Looping doesnt always make sense? - { - try - { - RecreateSwapchain(mainWindow); - if (vkWaitForFences(m_logicalDevice, 1, &chainInfo.m_imageAvailableFence, VK_TRUE, 0) == VK_SUCCESS) - vkResetFences(m_logicalDevice, 1, &chainInfo.m_imageAvailableFence); - result = vkAcquireNextImageKHR(m_logicalDevice, chainInfo.swapchain, std::numeric_limits::max(), acquireSemaphore, chainInfo.m_imageAvailableFence, &chainInfo.swapchainImageIndex); - if (result == VK_SUCCESS) - return true; - } - catch (std::exception&) {} + if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) + chainInfo.m_shouldRecreate = true; - std::this_thread::yield(); - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - } + if (result == VK_ERROR_OUT_OF_DATE_KHR) + return false; - throw std::runtime_error(fmt::format("Failed to acquire next image: {}", result)); + if (result != VK_ERROR_OUT_OF_DATE_KHR && result != VK_SUBOPTIMAL_KHR) + throw std::runtime_error(fmt::format("Failed to acquire next image: {}", result)); } chainInfo.m_acquireIndex = (chainInfo.m_acquireIndex + 1) % chainInfo.m_acquireSemaphores.size(); @@ -2613,7 +2588,7 @@ void VulkanRenderer::RecreateSwapchain(bool mainWindow, bool skipCreate) } chainInfo.Cleanup(); - chainInfo.setSize(size); + chainInfo.m_desiredExtent = size; if(!skipCreate) { chainInfo.Create(m_physicalDevice, m_logicalDevice); @@ -2624,14 +2599,36 @@ void VulkanRenderer::RecreateSwapchain(bool mainWindow, bool skipCreate) ImguiInit(); } -void VulkanRenderer::UpdateVSyncState(bool mainWindow) +bool VulkanRenderer::UpdateSwapchainProperties(bool mainWindow) { auto& chainInfo = GetChainInfo(mainWindow); + bool stateChanged = chainInfo.m_shouldRecreate; + const auto configValue = (VSync)GetConfig().vsync.GetValue(); - if(chainInfo.m_vsyncState != configValue){ - RecreateSwapchain(mainWindow); - chainInfo.m_vsyncState = configValue; + if(chainInfo.m_vsyncState != configValue) + stateChanged = true; + + const bool latteBufferUsesSRGB = mainWindow ? LatteGPUState.tvBufferUsesSRGB : LatteGPUState.drcBufferUsesSRGB; + if (chainInfo.m_usesSRGB != latteBufferUsesSRGB) + stateChanged = true; + + if(stateChanged) + { + try + { + RecreateSwapchain(mainWindow); + } + catch (std::exception&) + { + cemu_assert_debug(false); + return false; + } } + + chainInfo.m_shouldRecreate = false; + chainInfo.m_vsyncState = configValue; + chainInfo.m_usesSRGB = latteBufferUsesSRGB; + return true; } void VulkanRenderer::SwapBuffer(bool mainWindow) @@ -2857,7 +2854,7 @@ void VulkanRenderer::DrawBackbufferQuad(LatteTextureView* texView, RendererOutpu renderPassInfo.renderPass = chainInfo.m_swapchainRenderPass; renderPassInfo.framebuffer = chainInfo.m_swapchainFramebuffers[chainInfo.swapchainImageIndex]; renderPassInfo.renderArea.offset = { 0, 0 }; - renderPassInfo.renderArea.extent = chainInfo.swapchainExtent; + renderPassInfo.renderArea.extent = chainInfo.getExtent(); renderPassInfo.clearValueCount = 0; VkViewport viewport{}; @@ -2870,7 +2867,7 @@ void VulkanRenderer::DrawBackbufferQuad(LatteTextureView* texView, RendererOutpu vkCmdSetViewport(m_state.currentCommandBuffer, 0, 1, &viewport); VkRect2D scissor{}; - scissor.extent = chainInfo.swapchainExtent; + scissor.extent = chainInfo.getExtent(); vkCmdSetScissor(m_state.currentCommandBuffer, 0, 1, &scissor); auto descriptSet = backbufferBlit_createDescriptorSet(m_swapchainDescriptorSetLayout, texViewVk, useLinearTexFilter); diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h index 941aaca8..27fb57fb 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h @@ -191,7 +191,6 @@ public: bool IsPadWindowActive() override; void HandleScreenshotRequest(LatteTextureView* texView, bool padView) override; - void SetSwapchainTargetSize(const Vector2i& size, bool mainWindow); void QueryMemoryInfo(); void QueryAvailableFormats(); @@ -485,7 +484,7 @@ private: static bool CheckDeviceExtensionSupport(const VkPhysicalDevice device, FeatureControl& info); static std::vector CheckInstanceExtensionSupport(FeatureControl& info); - void UpdateVSyncState(bool mainWindow); + bool UpdateSwapchainProperties(bool mainWindow); void SwapBuffer(bool mainWindow); VkDescriptorSetLayout m_swapchainDescriptorSetLayout; diff --git a/src/gui/canvas/VulkanCanvas.cpp b/src/gui/canvas/VulkanCanvas.cpp index c16f3288..ed7bf8ae 100644 --- a/src/gui/canvas/VulkanCanvas.cpp +++ b/src/gui/canvas/VulkanCanvas.cpp @@ -56,15 +56,9 @@ void VulkanCanvas::OnPaint(wxPaintEvent& event) void VulkanCanvas::OnResize(wxSizeEvent& event) { const wxSize size = GetSize(); - if (size.GetWidth() == 0 || size.GetHeight() == 0) + if (size.GetWidth() == 0 || size.GetHeight() == 0) return; const wxRect refreshRect(size); RefreshRect(refreshRect, false); - - if (g_renderer == nullptr) - return; - - auto vulkan_renderer = VulkanRenderer::GetInstance(); - vulkan_renderer->SetSwapchainTargetSize({size.x, size.y}, m_is_main_window); } diff --git a/src/imgui/imgui_impl_vulkan.cpp b/src/imgui/imgui_impl_vulkan.cpp index 4c8695f4..88679378 100644 --- a/src/imgui/imgui_impl_vulkan.cpp +++ b/src/imgui/imgui_impl_vulkan.cpp @@ -912,7 +912,7 @@ void ImGui_ImplVulkan_Shutdown() ImGui_ImplVulkan_DestroyDeviceObjects(); } -void ImGui_ImplVulkan_NewFrame(VkCommandBuffer command_buffer, VkFramebuffer framebuffer, VkExtent2D& extend) +void ImGui_ImplVulkan_NewFrame(VkCommandBuffer command_buffer, VkFramebuffer framebuffer, VkExtent2D extent) { auto& io = ImGui::GetIO(); @@ -921,7 +921,7 @@ void ImGui_ImplVulkan_NewFrame(VkCommandBuffer command_buffer, VkFramebuffer fra renderPassInfo.renderPass = g_RenderPass; renderPassInfo.framebuffer = framebuffer; renderPassInfo.renderArea.offset = {0, 0}; - renderPassInfo.renderArea.extent = extend; + renderPassInfo.renderArea.extent = extent; renderPassInfo.clearValueCount = 0; vkCmdBeginRenderPass(command_buffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE); } diff --git a/src/imgui/imgui_impl_vulkan.h b/src/imgui/imgui_impl_vulkan.h index c4af49c5..44bcdd50 100644 --- a/src/imgui/imgui_impl_vulkan.h +++ b/src/imgui/imgui_impl_vulkan.h @@ -46,7 +46,7 @@ struct ImGui_ImplVulkan_InitInfo // Called by user code IMGUI_IMPL_API bool ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo* info, VkRenderPass render_pass); IMGUI_IMPL_API void ImGui_ImplVulkan_Shutdown(); -IMGUI_IMPL_API void ImGui_ImplVulkan_NewFrame(VkCommandBuffer command_buffer, VkFramebuffer framebuffer, VkExtent2D& extend); +IMGUI_IMPL_API void ImGui_ImplVulkan_NewFrame(VkCommandBuffer command_buffer, VkFramebuffer framebuffer, VkExtent2D extent); IMGUI_IMPL_API void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer command_buffer); IMGUI_IMPL_API bool ImGui_ImplVulkan_CreateFontsTexture(VkCommandBuffer command_buffer); IMGUI_IMPL_API void ImGui_ImplVulkan_DestroyFontsTexture();