mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-07-22 22:00:39 -06:00
Renderer: Handle resize events on-demand instead of polling
We now differentiate between a resize event and surface change/destroyed event, reducing the overhead for resizes in the Vulkan backend. It is also now now safe to change the surface multiple times if the video thread is lagging behind.
This commit is contained in:
@ -500,6 +500,10 @@ void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region
|
||||
StateTracker::GetInstance()->EndRenderPass();
|
||||
StateTracker::GetInstance()->OnEndFrame();
|
||||
|
||||
// Handle host window resizes.
|
||||
CheckForSurfaceChange();
|
||||
CheckForSurfaceResize();
|
||||
|
||||
// There are a few variables which can alter the final window draw rectangle, and some of them
|
||||
// are determined by guest state. Currently, the only way to catch these is to update every frame.
|
||||
UpdateDrawRectangle();
|
||||
@ -543,9 +547,6 @@ void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region
|
||||
// Determine what (if anything) has changed in the config.
|
||||
CheckForConfigChanges();
|
||||
|
||||
// Handle host window resizes.
|
||||
CheckForSurfaceChange();
|
||||
|
||||
// Clean up stale textures.
|
||||
TextureCache::GetInstance()->Cleanup(frameCount);
|
||||
|
||||
@ -650,71 +651,83 @@ void Renderer::BlitScreen(VkRenderPass render_pass, const TargetRectangle& dst_r
|
||||
|
||||
void Renderer::CheckForSurfaceChange()
|
||||
{
|
||||
if (!m_surface_needs_change.IsSet())
|
||||
if (!m_surface_changed.TestAndClear())
|
||||
return;
|
||||
|
||||
// Wait for the GPU to catch up since we're going to destroy the swap chain.
|
||||
m_surface_handle = m_new_surface_handle;
|
||||
m_new_surface_handle = nullptr;
|
||||
|
||||
// Submit the current draws up until rendering the XFB.
|
||||
g_command_buffer_mgr->ExecuteCommandBuffer(false, false);
|
||||
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())
|
||||
// Did we previously have a swap chain?
|
||||
if (m_swap_chain)
|
||||
{
|
||||
INFO_LOG(VIDEO, "Detected window resize.");
|
||||
m_swap_chain->RecreateSwapChain();
|
||||
|
||||
// Notify the main thread we are done.
|
||||
m_surface_needs_change.Clear();
|
||||
m_new_surface_handle = nullptr;
|
||||
m_surface_changed.Set();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Did we previously have a swap chain?
|
||||
if (m_swap_chain)
|
||||
if (!m_surface_handle)
|
||||
{
|
||||
if (!m_new_surface_handle)
|
||||
{
|
||||
// If there is no surface now, destroy the swap chain.
|
||||
m_swap_chain.reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Recreate the surface. If this fails we're in trouble.
|
||||
if (!m_swap_chain->RecreateSurface(m_new_surface_handle))
|
||||
PanicAlert("Failed to recreate Vulkan surface. Cannot continue.");
|
||||
}
|
||||
// If there is no surface now, destroy the swap chain.
|
||||
m_swap_chain.reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Previously had no swap chain. So create one.
|
||||
VkSurfaceKHR surface = SwapChain::CreateVulkanSurface(g_vulkan_context->GetVulkanInstance(),
|
||||
m_new_surface_handle);
|
||||
if (surface != VK_NULL_HANDLE)
|
||||
{
|
||||
m_swap_chain = SwapChain::Create(m_new_surface_handle, surface, g_ActiveConfig.IsVSync());
|
||||
if (!m_swap_chain)
|
||||
PanicAlert("Failed to create swap chain.");
|
||||
}
|
||||
else
|
||||
{
|
||||
PanicAlert("Failed to create surface.");
|
||||
}
|
||||
// Recreate the surface. If this fails we're in trouble.
|
||||
if (!m_swap_chain->RecreateSurface(m_surface_handle))
|
||||
PanicAlert("Failed to recreate Vulkan surface. Cannot continue.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Previously had no swap chain. So create one.
|
||||
VkSurfaceKHR surface =
|
||||
SwapChain::CreateVulkanSurface(g_vulkan_context->GetVulkanInstance(), m_surface_handle);
|
||||
if (surface != VK_NULL_HANDLE)
|
||||
{
|
||||
m_swap_chain = SwapChain::Create(m_surface_handle, surface, g_ActiveConfig.IsVSync());
|
||||
if (!m_swap_chain)
|
||||
PanicAlert("Failed to create swap chain.");
|
||||
}
|
||||
else
|
||||
{
|
||||
PanicAlert("Failed to create surface.");
|
||||
}
|
||||
|
||||
// Notify calling thread.
|
||||
m_surface_needs_change.Clear();
|
||||
m_surface_handle = m_new_surface_handle;
|
||||
m_new_surface_handle = nullptr;
|
||||
m_surface_changed.Set();
|
||||
}
|
||||
|
||||
// Handle case where the dimensions are now different.
|
||||
OnSwapChainResized();
|
||||
}
|
||||
|
||||
void Renderer::CheckForSurfaceResize()
|
||||
{
|
||||
if (!m_surface_resized.TestAndClear())
|
||||
return;
|
||||
|
||||
m_backbuffer_width = m_new_backbuffer_width;
|
||||
m_backbuffer_height = m_new_backbuffer_height;
|
||||
|
||||
// If we don't have a surface, how can we resize the swap chain?
|
||||
// CheckForSurfaceChange should handle this case.
|
||||
if (!m_swap_chain)
|
||||
{
|
||||
WARN_LOG(VIDEO, "Surface resize event received without active surface, ignoring");
|
||||
return;
|
||||
}
|
||||
|
||||
// Wait for the GPU to catch up since we're going to destroy the swap chain.
|
||||
g_command_buffer_mgr->ExecuteCommandBuffer(false, false);
|
||||
g_command_buffer_mgr->WaitForGPUIdle();
|
||||
|
||||
// Clear the present failed flag, since we don't want to resize after recreating.
|
||||
g_command_buffer_mgr->CheckLastPresentFail();
|
||||
|
||||
// Resize the swap chain.
|
||||
m_swap_chain->RecreateSwapChain();
|
||||
OnSwapChainResized();
|
||||
}
|
||||
|
||||
void Renderer::CheckForConfigChanges()
|
||||
{
|
||||
// Save the video config so we can compare against to determine which settings have changed.
|
||||
@ -782,9 +795,6 @@ void Renderer::OnSwapChainResized()
|
||||
{
|
||||
m_backbuffer_width = m_swap_chain->GetWidth();
|
||||
m_backbuffer_height = m_swap_chain->GetHeight();
|
||||
UpdateDrawRectangle();
|
||||
if (CalculateTargetSize())
|
||||
RecreateEFBFramebuffer();
|
||||
}
|
||||
|
||||
void Renderer::BindEFBToStateTracker()
|
||||
@ -914,14 +924,6 @@ void Renderer::SetViewport(float x, float y, float width, float height, float ne
|
||||
StateTracker::GetInstance()->SetViewport(viewport);
|
||||
}
|
||||
|
||||
void Renderer::ChangeSurface(void* new_surface_handle)
|
||||
{
|
||||
// Called by the main thread when the window is resized.
|
||||
m_new_surface_handle = new_surface_handle;
|
||||
m_surface_needs_change.Set();
|
||||
m_surface_changed.Set();
|
||||
}
|
||||
|
||||
void Renderer::RecompileShaders()
|
||||
{
|
||||
DestroyShaders();
|
||||
|
Reference in New Issue
Block a user