Merge pull request #13087 from TellowKrinkle/PresentSkip

VideoBackends:Vulkan: Prevent freezes during window resize on Linux
This commit is contained in:
JMC47 2024-10-02 21:09:25 -04:00 committed by GitHub
commit e259831bcf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 76 additions and 34 deletions

View File

@ -157,10 +157,11 @@ void Gfx::DispatchComputeShader(const AbstractShader* shader, u32 groupsize_x, u
D3D::context->Dispatch(groups_x, groups_y, groups_z); D3D::context->Dispatch(groups_x, groups_y, groups_z);
} }
void Gfx::BindBackbuffer(const ClearColor& clear_color) bool Gfx::BindBackbuffer(const ClearColor& clear_color)
{ {
CheckForSwapChainChanges(); CheckForSwapChainChanges();
SetAndClearFramebuffer(m_swap_chain->GetFramebuffer(), clear_color); SetAndClearFramebuffer(m_swap_chain->GetFramebuffer(), clear_color);
return true;
} }
void Gfx::PresentBackbuffer() void Gfx::PresentBackbuffer()

View File

@ -60,7 +60,7 @@ public:
void DrawIndexed(u32 base_index, u32 num_indices, u32 base_vertex) override; void DrawIndexed(u32 base_index, u32 num_indices, u32 base_vertex) override;
void DispatchComputeShader(const AbstractShader* shader, u32 groupsize_x, u32 groupsize_y, void DispatchComputeShader(const AbstractShader* shader, u32 groupsize_x, u32 groupsize_y,
u32 groupsize_z, u32 groups_x, u32 groups_y, u32 groups_z) override; u32 groupsize_z, u32 groups_x, u32 groups_y, u32 groups_z) override;
void BindBackbuffer(const ClearColor& clear_color = {}) override; bool BindBackbuffer(const ClearColor& clear_color = {}) override;
void PresentBackbuffer() override; void PresentBackbuffer() override;
void SetFullscreen(bool enable_fullscreen) override; void SetFullscreen(bool enable_fullscreen) override;
bool IsFullscreen() const override; bool IsFullscreen() const override;

View File

@ -365,10 +365,11 @@ void Gfx::DispatchComputeShader(const AbstractShader* shader, u32 groupsize_x, u
m_dirty_bits |= DirtyState_Pipeline; m_dirty_bits |= DirtyState_Pipeline;
} }
void Gfx::BindBackbuffer(const ClearColor& clear_color) bool Gfx::BindBackbuffer(const ClearColor& clear_color)
{ {
CheckForSwapChainChanges(); CheckForSwapChainChanges();
SetAndClearFramebuffer(m_swap_chain->GetCurrentFramebuffer(), clear_color); SetAndClearFramebuffer(m_swap_chain->GetCurrentFramebuffer(), clear_color);
return true;
} }
void Gfx::CheckForSwapChainChanges() void Gfx::CheckForSwapChainChanges()

View File

@ -68,7 +68,7 @@ public:
void DrawIndexed(u32 base_index, u32 num_indices, u32 base_vertex) override; void DrawIndexed(u32 base_index, u32 num_indices, u32 base_vertex) override;
void DispatchComputeShader(const AbstractShader* shader, u32 groupsize_x, u32 groupsize_y, void DispatchComputeShader(const AbstractShader* shader, u32 groupsize_x, u32 groupsize_y,
u32 groupsize_z, u32 groups_x, u32 groups_y, u32 groups_z) override; u32 groupsize_z, u32 groups_x, u32 groups_y, u32 groups_z) override;
void BindBackbuffer(const ClearColor& clear_color = {}) override; bool BindBackbuffer(const ClearColor& clear_color = {}) override;
void PresentBackbuffer() override; void PresentBackbuffer() override;
SurfaceInfo GetSurfaceInfo() const override; SurfaceInfo GetSurfaceInfo() const override;

View File

@ -67,7 +67,7 @@ public:
void DrawIndexed(u32 base_index, u32 num_indices, u32 base_vertex) override; void DrawIndexed(u32 base_index, u32 num_indices, u32 base_vertex) override;
void DispatchComputeShader(const AbstractShader* shader, u32 groupsize_x, u32 groupsize_y, void DispatchComputeShader(const AbstractShader* shader, u32 groupsize_x, u32 groupsize_y,
u32 groupsize_z, u32 groups_x, u32 groups_y, u32 groups_z) override; u32 groupsize_z, u32 groups_x, u32 groups_y, u32 groups_z) override;
void BindBackbuffer(const ClearColor& clear_color = {}) override; bool BindBackbuffer(const ClearColor& clear_color = {}) override;
void PresentBackbuffer() override; void PresentBackbuffer() override;
SurfaceInfo GetSurfaceInfo() const override; SurfaceInfo GetSurfaceInfo() const override;

View File

@ -447,7 +447,7 @@ void Metal::Gfx::DispatchComputeShader(const AbstractShader* shader, //
} }
} }
void Metal::Gfx::BindBackbuffer(const ClearColor& clear_color) bool Metal::Gfx::BindBackbuffer(const ClearColor& clear_color)
{ {
@autoreleasepool @autoreleasepool
{ {
@ -456,6 +456,7 @@ void Metal::Gfx::BindBackbuffer(const ClearColor& clear_color)
m_drawable = MRCRetain([m_layer nextDrawable]); m_drawable = MRCRetain([m_layer nextDrawable]);
m_backbuffer->UpdateBackbufferTexture([m_drawable texture]); m_backbuffer->UpdateBackbufferTexture([m_drawable texture]);
SetAndClearFramebuffer(m_backbuffer.get(), clear_color); SetAndClearFramebuffer(m_backbuffer.get(), clear_color);
return m_drawable != nullptr;
} }
} }

View File

@ -407,11 +407,12 @@ void OGLGfx::ClearRegion(const MathUtil::Rectangle<int>& target_rc, bool colorEn
glDepthMask(m_current_depth_state.updateenable); glDepthMask(m_current_depth_state.updateenable);
} }
void OGLGfx::BindBackbuffer(const ClearColor& clear_color) bool OGLGfx::BindBackbuffer(const ClearColor& clear_color)
{ {
CheckForSurfaceChange(); CheckForSurfaceChange();
CheckForSurfaceResize(); CheckForSurfaceResize();
SetAndClearFramebuffer(m_system_framebuffer.get(), clear_color); SetAndClearFramebuffer(m_system_framebuffer.get(), clear_color);
return true;
} }
void OGLGfx::PresentBackbuffer() void OGLGfx::PresentBackbuffer()

View File

@ -57,7 +57,7 @@ public:
void DrawIndexed(u32 base_index, u32 num_indices, u32 base_vertex) override; void DrawIndexed(u32 base_index, u32 num_indices, u32 base_vertex) override;
void DispatchComputeShader(const AbstractShader* shader, u32 groupsize_x, u32 groupsize_y, void DispatchComputeShader(const AbstractShader* shader, u32 groupsize_x, u32 groupsize_y,
u32 groupsize_z, u32 groups_x, u32 groups_y, u32 groups_z) override; u32 groupsize_z, u32 groups_x, u32 groups_y, u32 groups_z) override;
void BindBackbuffer(const ClearColor& clear_color = {}) override; bool BindBackbuffer(const ClearColor& clear_color = {}) override;
void PresentBackbuffer() override; void PresentBackbuffer() override;
void BeginUtilityDrawing() override; void BeginUtilityDrawing() override;

View File

@ -55,15 +55,16 @@ SWGfx::CreateFramebuffer(AbstractTexture* color_attachment, AbstractTexture* dep
std::move(additional_color_attachments)); std::move(additional_color_attachments));
} }
void SWGfx::BindBackbuffer(const ClearColor& clear_color) bool SWGfx::BindBackbuffer(const ClearColor& clear_color)
{ {
// Look for framebuffer resizes // Look for framebuffer resizes
if (!g_presenter->SurfaceResizedTestAndClear()) if (!g_presenter->SurfaceResizedTestAndClear())
return; return true;
GLContext* context = m_window->GetContext(); GLContext* context = m_window->GetContext();
context->Update(); context->Update();
g_presenter->SetBackbuffer(context->GetBackBufferWidth(), context->GetBackBufferHeight()); g_presenter->SetBackbuffer(context->GetBackBufferWidth(), context->GetBackBufferHeight());
return true;
} }
class SWShader final : public AbstractShader class SWShader final : public AbstractShader

View File

@ -26,7 +26,7 @@ public:
CreateFramebuffer(AbstractTexture* color_attachment, AbstractTexture* depth_attachment, CreateFramebuffer(AbstractTexture* color_attachment, AbstractTexture* depth_attachment,
std::vector<AbstractTexture*> additional_color_attachments) override; std::vector<AbstractTexture*> additional_color_attachments) override;
void BindBackbuffer(const ClearColor& clear_color = {}) override; bool BindBackbuffer(const ClearColor& clear_color = {}) override;
std::unique_ptr<AbstractShader> CreateShaderFromSource(ShaderStage stage, std::string_view source, std::unique_ptr<AbstractShader> CreateShaderFromSource(ShaderStage stage, std::string_view source,
std::string_view name) override; std::string_view name) override;

View File

@ -300,7 +300,7 @@ void CommandBufferManager::WaitForCommandBufferCompletion(u32 index)
} }
void CommandBufferManager::SubmitCommandBuffer(bool submit_on_worker_thread, void CommandBufferManager::SubmitCommandBuffer(bool submit_on_worker_thread,
bool wait_for_completion, bool wait_for_completion, bool advance_to_next_frame,
VkSwapchainKHR present_swap_chain, VkSwapchainKHR present_swap_chain,
uint32_t present_image_index) uint32_t present_image_index)
{ {
@ -334,7 +334,7 @@ void CommandBufferManager::SubmitCommandBuffer(bool submit_on_worker_thread,
WaitForCommandBufferCompletion(m_current_cmd_buffer); WaitForCommandBufferCompletion(m_current_cmd_buffer);
} }
if (present_swap_chain != VK_NULL_HANDLE) if (advance_to_next_frame)
{ {
m_current_frame = (m_current_frame + 1) % NUM_FRAMES_IN_FLIGHT; m_current_frame = (m_current_frame + 1) % NUM_FRAMES_IN_FLIGHT;

View File

@ -63,11 +63,18 @@ public:
// Returns the semaphore for the current command buffer, which can be used to ensure the // Returns the semaphore for the current command buffer, which can be used to ensure the
// swap chain image is ready before the command buffer executes. // swap chain image is ready before the command buffer executes.
// Once you've confirmed that the semaphore will be signalled this frame, call
// `MarkCurrentCommandBufferSemaphoreUsed`.
VkSemaphore GetCurrentCommandBufferSemaphore() VkSemaphore GetCurrentCommandBufferSemaphore()
{ {
auto& resources = m_command_buffers[m_current_cmd_buffer]; return m_command_buffers[m_current_cmd_buffer].semaphore;
resources.semaphore_used = true; }
return resources.semaphore;
// Marks the current command buffer's semaphore as used, so we'll wait on it in the next
// command buffer submission.
void MarkCurrentCommandBufferSemaphoreUsed()
{
m_command_buffers[m_current_cmd_buffer].semaphore_used = true;
} }
// Ensure that the worker thread has submitted any previous command buffers and is idle. // Ensure that the worker thread has submitted any previous command buffers and is idle.
@ -78,6 +85,7 @@ public:
void WaitForFenceCounter(u64 fence_counter); void WaitForFenceCounter(u64 fence_counter);
void SubmitCommandBuffer(bool submit_on_worker_thread, bool wait_for_completion, void SubmitCommandBuffer(bool submit_on_worker_thread, bool wait_for_completion,
bool advance_to_next_frame = false,
VkSwapchainKHR present_swap_chain = VK_NULL_HANDLE, VkSwapchainKHR present_swap_chain = VK_NULL_HANDLE,
uint32_t present_image_index = 0xFFFFFFFF); uint32_t present_image_index = 0xFFFFFFFF);

View File

@ -221,7 +221,7 @@ void VKGfx::WaitForGPUIdle()
ExecuteCommandBuffer(false, true); ExecuteCommandBuffer(false, true);
} }
void VKGfx::BindBackbuffer(const ClearColor& clear_color) bool VKGfx::BindBackbuffer(const ClearColor& clear_color)
{ {
StateTracker::GetInstance()->EndRenderPass(); StateTracker::GetInstance()->EndRenderPass();
@ -284,10 +284,22 @@ void VKGfx::BindBackbuffer(const ClearColor& clear_color)
res = m_swap_chain->AcquireNextImage(); res = m_swap_chain->AcquireNextImage();
if (res != VK_SUCCESS && res != VK_SUBOPTIMAL_KHR) if (res != VK_SUCCESS && res != VK_SUBOPTIMAL_KHR)
PanicAlertFmt("Failed to grab image from swap chain: {:#010X} {}", Common::ToUnderlying(res), {
VkResultToString(res)); if (res == VK_ERROR_OUT_OF_DATE_KHR)
{
INFO_LOG_FMT(VIDEO, "Swapchain still out of date, will try again next frame...");
}
else
{
PanicAlertFmt("Failed to grab image from swap chain: {:#010X} {}",
Common::ToUnderlying(res), VkResultToString(res));
}
}
} }
if (!m_swap_chain->IsCurrentImageValid())
return false;
// Transition from undefined (or present src, but it can be substituted) to // Transition from undefined (or present src, but it can be substituted) to
// color attachment ready for writing. These transitions must occur outside // color attachment ready for writing. These transitions must occur outside
// a render pass, unless the render pass declares a self-dependency. // a render pass, unless the render pass declares a self-dependency.
@ -296,6 +308,7 @@ void VKGfx::BindBackbuffer(const ClearColor& clear_color)
g_command_buffer_mgr->GetCurrentCommandBuffer(), VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); g_command_buffer_mgr->GetCurrentCommandBuffer(), VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
SetAndClearFramebuffer(m_swap_chain->GetCurrentFramebuffer(), SetAndClearFramebuffer(m_swap_chain->GetCurrentFramebuffer(),
ClearColor{{0.0f, 0.0f, 0.0f, 1.0f}}); ClearColor{{0.0f, 0.0f, 0.0f, 1.0f}});
return true;
} }
void VKGfx::PresentBackbuffer() void VKGfx::PresentBackbuffer()
@ -303,17 +316,24 @@ void VKGfx::PresentBackbuffer()
// End drawing to backbuffer // End drawing to backbuffer
StateTracker::GetInstance()->EndRenderPass(); StateTracker::GetInstance()->EndRenderPass();
// Transition the backbuffer to PRESENT_SRC to ensure all commands drawing if (m_swap_chain->IsCurrentImageValid())
// to it have finished before present. {
m_swap_chain->GetCurrentTexture()->TransitionToLayout( // Transition the backbuffer to PRESENT_SRC to ensure all commands drawing
g_command_buffer_mgr->GetCurrentCommandBuffer(), VK_IMAGE_LAYOUT_PRESENT_SRC_KHR); // to it have finished before present.
m_swap_chain->GetCurrentTexture()->TransitionToLayout(
g_command_buffer_mgr->GetCurrentCommandBuffer(), VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
// Submit the current command buffer, signaling rendering finished semaphore when it's done // Submit the current command buffer, signaling rendering finished semaphore when it's done
// Because this final command buffer is rendering to the swap chain, we need to wait for // Because this final command buffer is rendering to the swap chain, we need to wait for
// the available semaphore to be signaled before executing the buffer. This final submission // the available semaphore to be signaled before executing the buffer. This final submission
// can happen off-thread in the background while we're preparing the next frame. // can happen off-thread in the background while we're preparing the next frame.
g_command_buffer_mgr->SubmitCommandBuffer(true, false, m_swap_chain->GetSwapChain(), g_command_buffer_mgr->SubmitCommandBuffer(true, false, true, m_swap_chain->GetSwapChain(),
m_swap_chain->GetCurrentImageIndex()); m_swap_chain->GetCurrentImageIndex());
}
else
{
g_command_buffer_mgr->SubmitCommandBuffer(true, false, true);
}
// New cmdbuffer, so invalidate state. // New cmdbuffer, so invalidate state.
StateTracker::GetInstance()->InvalidateCachedState(); StateTracker::GetInstance()->InvalidateCachedState();

View File

@ -74,7 +74,7 @@ public:
void DrawIndexed(u32 base_index, u32 num_indices, u32 base_vertex) override; void DrawIndexed(u32 base_index, u32 num_indices, u32 base_vertex) override;
void DispatchComputeShader(const AbstractShader* shader, u32 groupsize_x, u32 groupsize_y, void DispatchComputeShader(const AbstractShader* shader, u32 groupsize_x, u32 groupsize_y,
u32 groupsize_z, u32 groups_x, u32 groups_y, u32 groups_z) override; u32 groupsize_z, u32 groups_x, u32 groups_y, u32 groups_z) override;
void BindBackbuffer(const ClearColor& clear_color = {}) override; bool BindBackbuffer(const ClearColor& clear_color = {}) override;
void PresentBackbuffer() override; void PresentBackbuffer() override;
void SetFullscreen(bool enable_fullscreen) override; void SetFullscreen(bool enable_fullscreen) override;
bool IsFullscreen() const override; bool IsFullscreen() const override;

View File

@ -497,6 +497,9 @@ VkResult SwapChain::AcquireNextImage()
VkResult res = vkAcquireNextImageKHR(g_vulkan_context->GetDevice(), m_swap_chain, UINT64_MAX, VkResult res = vkAcquireNextImageKHR(g_vulkan_context->GetDevice(), m_swap_chain, UINT64_MAX,
g_command_buffer_mgr->GetCurrentCommandBufferSemaphore(), g_command_buffer_mgr->GetCurrentCommandBufferSemaphore(),
VK_NULL_HANDLE, &m_current_swap_chain_image_index); VK_NULL_HANDLE, &m_current_swap_chain_image_index);
m_current_swap_chain_image_is_valid = res >= 0;
if (IsCurrentImageValid())
g_command_buffer_mgr->MarkCurrentCommandBufferSemaphoreUsed();
if (res != VK_SUCCESS && res != VK_ERROR_OUT_OF_DATE_KHR && res != VK_SUBOPTIMAL_KHR) if (res != VK_SUCCESS && res != VK_ERROR_OUT_OF_DATE_KHR && res != VK_SUBOPTIMAL_KHR)
LOG_VULKAN_ERROR(res, "vkAcquireNextImageKHR failed: "); LOG_VULKAN_ERROR(res, "vkAcquireNextImageKHR failed: ");

View File

@ -38,6 +38,7 @@ public:
u32 GetWidth() const { return m_width; } u32 GetWidth() const { return m_width; }
u32 GetHeight() const { return m_height; } u32 GetHeight() const { return m_height; }
u32 GetCurrentImageIndex() const { return m_current_swap_chain_image_index; } u32 GetCurrentImageIndex() const { return m_current_swap_chain_image_index; }
bool IsCurrentImageValid() const { return m_current_swap_chain_image_is_valid; }
VkImage GetCurrentImage() const VkImage GetCurrentImage() const
{ {
return m_swap_chain_images[m_current_swap_chain_image_index].image; return m_swap_chain_images[m_current_swap_chain_image_index].image;
@ -98,6 +99,7 @@ private:
bool m_fullscreen_supported = false; bool m_fullscreen_supported = false;
bool m_current_fullscreen_state = false; bool m_current_fullscreen_state = false;
bool m_next_fullscreen_state = false; bool m_next_fullscreen_state = false;
bool m_current_swap_chain_image_is_valid = false;
VkSwapchainKHR m_swap_chain = VK_NULL_HANDLE; VkSwapchainKHR m_swap_chain = VK_NULL_HANDLE;
std::vector<SwapChainImage> m_swap_chain_images; std::vector<SwapChainImage> m_swap_chain_images;

View File

@ -101,7 +101,10 @@ public:
// Binds the backbuffer for rendering. The buffer will be cleared immediately after binding. // Binds the backbuffer for rendering. The buffer will be cleared immediately after binding.
// This is where any window size changes are detected, therefore m_backbuffer_width and/or // This is where any window size changes are detected, therefore m_backbuffer_width and/or
// m_backbuffer_height may change after this function returns. // m_backbuffer_height may change after this function returns.
virtual void BindBackbuffer(const ClearColor& clear_color = {}) {} // If this returns false, a problem occurred binding the backbuffer.
// Don't render anything to it, but still call `PresentBackbuffer`, which will reset any
// per-frame resources and prepare for the next frame.
virtual bool BindBackbuffer(const ClearColor& clear_color = {}) { return true; }
// Presents the backbuffer to the window system, or "swaps buffers". // Presents the backbuffer to the window system, or "swaps buffers".
virtual void PresentBackbuffer() {} virtual void PresentBackbuffer() {}

View File

@ -844,10 +844,10 @@ void Presenter::Present()
UpdateDrawRectangle(); UpdateDrawRectangle();
g_gfx->BeginUtilityDrawing(); g_gfx->BeginUtilityDrawing();
g_gfx->BindBackbuffer({{0.0f, 0.0f, 0.0f, 1.0f}}); const bool backbuffer_bound = g_gfx->BindBackbuffer({{0.0f, 0.0f, 0.0f, 1.0f}});
// Render the XFB to the screen. // Render the XFB to the screen.
if (m_xfb_entry) if (backbuffer_bound && m_xfb_entry)
{ {
// Adjust the source rectangle instead of using an oversized viewport to render the XFB. // Adjust the source rectangle instead of using an oversized viewport to render the XFB.
auto render_target_rc = GetTargetRectangle(); auto render_target_rc = GetTargetRectangle();
@ -860,7 +860,8 @@ void Presenter::Present()
if (m_onscreen_ui) if (m_onscreen_ui)
{ {
m_onscreen_ui->Finalize(); m_onscreen_ui->Finalize();
m_onscreen_ui->DrawImGui(); if (backbuffer_bound)
m_onscreen_ui->DrawImGui();
} }
// Present to the window system. // Present to the window system.