diff --git a/Source/Core/VideoBackends/Vulkan/CommandBufferManager.h b/Source/Core/VideoBackends/Vulkan/CommandBufferManager.h index 908abe97d1..9cefe100d7 100644 --- a/Source/Core/VideoBackends/Vulkan/CommandBufferManager.h +++ b/Source/Core/VideoBackends/Vulkan/CommandBufferManager.h @@ -80,7 +80,7 @@ public: void ExecuteCommandBuffer(bool submit_off_thread, bool wait_for_completion); // Was the last present submitted to the queue a failure? If so, we must recreate our swapchain. - bool DidLastPresentFail() { return m_present_failed_flag.TestAndClear(); } + bool CheckLastPresentFail() { return m_present_failed_flag.TestAndClear(); } // Schedule a vulkan resource for destruction later on. This will occur when the command buffer // is next re-used, and the GPU has finished working with the specified resource. void DeferBufferDestruction(VkBuffer object); diff --git a/Source/Core/VideoBackends/Vulkan/Renderer.cpp b/Source/Core/VideoBackends/Vulkan/Renderer.cpp index 018df1dbf0..3a4907b88c 100644 --- a/Source/Core/VideoBackends/Vulkan/Renderer.cpp +++ b/Source/Core/VideoBackends/Vulkan/Renderer.cpp @@ -719,7 +719,7 @@ void Renderer::DrawScreen(const TargetRectangle& scaled_efb_rect, u32 xfb_addr, u32 fb_stride, u32 fb_height) { VkResult res; - if (!g_command_buffer_mgr->DidLastPresentFail()) + if (!g_command_buffer_mgr->CheckLastPresentFail()) { // Grab the next image from the swap chain in preparation for drawing the window. res = m_swap_chain->AcquireNextImage(m_image_available_semaphore); @@ -739,7 +739,7 @@ void Renderer::DrawScreen(const TargetRectangle& scaled_efb_rect, u32 xfb_addr, // command buffer, resize the swap chain (which calls WaitForGPUIdle), and then finally call // PrepareToSubmitCommandBuffer to return to the state that the caller expects. g_command_buffer_mgr->SubmitCommandBuffer(false); - ResizeSwapChain(); + m_swap_chain->ResizeSwapChain(); BeginFrame(); g_command_buffer_mgr->PrepareToSubmitCommandBuffer(); res = m_swap_chain->AcquireNextImage(m_image_available_semaphore); @@ -1076,14 +1076,17 @@ void Renderer::CheckForSurfaceChange() if (!m_surface_needs_change.IsSet()) return; - u32 old_width = m_swap_chain ? m_swap_chain->GetWidth() : 0; - u32 old_height = m_swap_chain ? m_swap_chain->GetHeight() : 0; + // Wait for the GPU to catch up since we're going to destroy the swap chain. + g_command_buffer_mgr->WaitForGPUIdle(); + + // Clear the present failed flag, since we don't want to resize after recreating. + g_command_buffer_mgr->CheckLastPresentFail(); // Fast path, if the surface handle is the same, the window has just been resized. if (m_swap_chain && m_new_surface_handle == m_swap_chain->GetNativeHandle()) { INFO_LOG(VIDEO, "Detected window resize."); - ResizeSwapChain(); + m_swap_chain->RecreateSwapChain(); // Notify the main thread we are done. m_surface_needs_change.Clear(); @@ -1092,9 +1095,6 @@ void Renderer::CheckForSurfaceChange() } else { - // Wait for the GPU to catch up since we're going to destroy the swap chain. - g_command_buffer_mgr->WaitForGPUIdle(); - // Did we previously have a swap chain? if (m_swap_chain) { @@ -1133,12 +1133,8 @@ void Renderer::CheckForSurfaceChange() m_surface_changed.Set(); } - if (m_swap_chain) - { - // Handle case where the dimensions are now different - if (old_width != m_swap_chain->GetWidth() || old_height != m_swap_chain->GetHeight()) - OnSwapChainResized(); - } + // Handle case where the dimensions are now different. + OnSwapChainResized(); } void Renderer::CheckForConfigChanges() @@ -1202,7 +1198,10 @@ void Renderer::CheckForConfigChanges() // For quad-buffered stereo we need to change the layer count, so recreate the swap chain. if (m_swap_chain && (g_ActiveConfig.iStereoMode == STEREO_QUADBUFFER) != m_swap_chain->IsStereoEnabled()) - ResizeSwapChain(); + { + g_command_buffer_mgr->WaitForGPUIdle(); + m_swap_chain->RecreateSwapChain(); + } // Wipe sampler cache if force texture filtering or anisotropy changes. if (anisotropy_changed || force_texture_filtering_changed) @@ -1248,18 +1247,6 @@ void Renderer::ResizeEFBTextures() BPFunctions::SetScissor(); } -void Renderer::ResizeSwapChain() -{ - // The worker thread may still be submitting a present on this swap chain. - g_command_buffer_mgr->WaitForGPUIdle(); - - // It's now safe to resize the swap chain. - if (!m_swap_chain->ResizeSwapChain()) - PanicAlert("Failed to resize swap chain."); - - OnSwapChainResized(); -} - void Renderer::ApplyState() { } diff --git a/Source/Core/VideoBackends/Vulkan/Renderer.h b/Source/Core/VideoBackends/Vulkan/Renderer.h index 593c2cd887..5a367f2bcd 100644 --- a/Source/Core/VideoBackends/Vulkan/Renderer.h +++ b/Source/Core/VideoBackends/Vulkan/Renderer.h @@ -81,7 +81,6 @@ private: void OnSwapChainResized(); void BindEFBToStateTracker(); void ResizeEFBTextures(); - void ResizeSwapChain(); void RecompileShaders(); bool CompileShaders(); diff --git a/Source/Core/VideoBackends/Vulkan/SwapChain.cpp b/Source/Core/VideoBackends/Vulkan/SwapChain.cpp index c5b3791637..abcd38c94a 100644 --- a/Source/Core/VideoBackends/Vulkan/SwapChain.cpp +++ b/Source/Core/VideoBackends/Vulkan/SwapChain.cpp @@ -472,14 +472,27 @@ bool SwapChain::ResizeSwapChain() return true; } +bool SwapChain::RecreateSwapChain() +{ + DestroySwapChainImages(); + DestroySwapChain(); + if (!CreateSwapChain() || !SetupSwapChainImages()) + { + PanicAlert("Failed to re-configure swap chain images, this is fatal (for now)"); + return false; + } + + return true; +} + bool SwapChain::SetVSync(bool enabled) { if (m_vsync_enabled == enabled) return true; - // Resizing recreates the swap chain with the new present mode. + // Recreate the swap chain with the new present mode. m_vsync_enabled = enabled; - return ResizeSwapChain(); + return RecreateSwapChain(); } bool SwapChain::RecreateSurface(void* native_handle) diff --git a/Source/Core/VideoBackends/Vulkan/SwapChain.h b/Source/Core/VideoBackends/Vulkan/SwapChain.h index 37c3460796..69ccb6dff1 100644 --- a/Source/Core/VideoBackends/Vulkan/SwapChain.h +++ b/Source/Core/VideoBackends/Vulkan/SwapChain.h @@ -55,6 +55,7 @@ public: bool RecreateSurface(void* native_handle); bool ResizeSwapChain(); + bool RecreateSwapChain(); // Change vsync enabled state. This may fail as it causes a swapchain recreation. bool SetVSync(bool enabled);