mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-01-10 16:19:28 +01:00
Merge pull request #6066 from stenzek/vulkan-resize
Vulkan: Fixes for window resizing
This commit is contained in:
commit
90ca2e8042
@ -353,8 +353,13 @@ void CommandBufferManager::SubmitCommandBuffer(size_t index, VkSemaphore wait_se
|
|||||||
nullptr};
|
nullptr};
|
||||||
|
|
||||||
res = vkQueuePresentKHR(g_vulkan_context->GetPresentQueue(), &present_info);
|
res = vkQueuePresentKHR(g_vulkan_context->GetPresentQueue(), &present_info);
|
||||||
if (res != VK_SUCCESS && res != VK_ERROR_OUT_OF_DATE_KHR && res != VK_SUBOPTIMAL_KHR)
|
if (res != VK_SUCCESS)
|
||||||
LOG_VULKAN_ERROR(res, "vkQueuePresentKHR failed: ");
|
{
|
||||||
|
// VK_ERROR_OUT_OF_DATE_KHR is not fatal, just means we need to recreate our swap chain.
|
||||||
|
if (res != VK_ERROR_OUT_OF_DATE_KHR && res != VK_SUBOPTIMAL_KHR)
|
||||||
|
LOG_VULKAN_ERROR(res, "vkQueuePresentKHR failed: ");
|
||||||
|
m_present_failed_flag.Set();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Command buffer has been queued, so permit the next one.
|
// Command buffer has been queued, so permit the next one.
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "Common/BlockingLoop.h"
|
#include "Common/BlockingLoop.h"
|
||||||
|
#include "Common/Flag.h"
|
||||||
#include "Common/Semaphore.h"
|
#include "Common/Semaphore.h"
|
||||||
|
|
||||||
#include "VideoCommon/VideoCommon.h"
|
#include "VideoCommon/VideoCommon.h"
|
||||||
@ -78,6 +79,8 @@ public:
|
|||||||
|
|
||||||
void ExecuteCommandBuffer(bool submit_off_thread, bool wait_for_completion);
|
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 CheckLastPresentFail() { return m_present_failed_flag.TestAndClear(); }
|
||||||
// Schedule a vulkan resource for destruction later on. This will occur when the command buffer
|
// 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.
|
// is next re-used, and the GPU has finished working with the specified resource.
|
||||||
void DeferBufferDestruction(VkBuffer object);
|
void DeferBufferDestruction(VkBuffer object);
|
||||||
@ -144,6 +147,7 @@ private:
|
|||||||
};
|
};
|
||||||
std::deque<PendingCommandBufferSubmit> m_pending_submits;
|
std::deque<PendingCommandBufferSubmit> m_pending_submits;
|
||||||
std::mutex m_pending_submit_lock;
|
std::mutex m_pending_submit_lock;
|
||||||
|
Common::Flag m_present_failed_flag;
|
||||||
bool m_use_threaded_submission = false;
|
bool m_use_threaded_submission = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -325,10 +325,6 @@ void Renderer::BeginFrame()
|
|||||||
// Activate a new command list, and restore state ready for the next draw
|
// Activate a new command list, and restore state ready for the next draw
|
||||||
g_command_buffer_mgr->ActivateCommandBuffer();
|
g_command_buffer_mgr->ActivateCommandBuffer();
|
||||||
|
|
||||||
// Restore the EFB color texture to color attachment ready for rendering the next frame.
|
|
||||||
FramebufferManager::GetInstance()->GetEFBColorTexture()->TransitionToLayout(
|
|
||||||
g_command_buffer_mgr->GetCurrentCommandBuffer(), VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
|
|
||||||
|
|
||||||
// Ensure that the state tracker rebinds everything, and allocates a new set
|
// Ensure that the state tracker rebinds everything, and allocates a new set
|
||||||
// of descriptors out of the next pool.
|
// of descriptors out of the next pool.
|
||||||
StateTracker::GetInstance()->InvalidateDescriptorSets();
|
StateTracker::GetInstance()->InvalidateDescriptorSets();
|
||||||
@ -575,6 +571,10 @@ void Renderer::SwapImpl(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height
|
|||||||
// Prep for the next frame (get command buffer ready) before doing anything else.
|
// Prep for the next frame (get command buffer ready) before doing anything else.
|
||||||
BeginFrame();
|
BeginFrame();
|
||||||
|
|
||||||
|
// Restore the EFB color texture to color attachment ready for rendering the next frame.
|
||||||
|
FramebufferManager::GetInstance()->GetEFBColorTexture()->TransitionToLayout(
|
||||||
|
g_command_buffer_mgr->GetCurrentCommandBuffer(), VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
|
||||||
|
|
||||||
// Determine what (if anything) has changed in the config.
|
// Determine what (if anything) has changed in the config.
|
||||||
CheckForConfigChanges();
|
CheckForConfigChanges();
|
||||||
|
|
||||||
@ -718,8 +718,18 @@ void Renderer::DrawScreen(const TargetRectangle& scaled_efb_rect, u32 xfb_addr,
|
|||||||
const XFBSourceBase* const* xfb_sources, u32 xfb_count, u32 fb_width,
|
const XFBSourceBase* const* xfb_sources, u32 xfb_count, u32 fb_width,
|
||||||
u32 fb_stride, u32 fb_height)
|
u32 fb_stride, u32 fb_height)
|
||||||
{
|
{
|
||||||
// Grab the next image from the swap chain in preparation for drawing the window.
|
VkResult res;
|
||||||
VkResult res = m_swap_chain->AcquireNextImage(m_image_available_semaphore);
|
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);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// If the last present failed, we need to recreate the swap chain.
|
||||||
|
res = VK_ERROR_OUT_OF_DATE_KHR;
|
||||||
|
}
|
||||||
|
|
||||||
if (res == VK_SUBOPTIMAL_KHR || res == VK_ERROR_OUT_OF_DATE_KHR)
|
if (res == VK_SUBOPTIMAL_KHR || res == VK_ERROR_OUT_OF_DATE_KHR)
|
||||||
{
|
{
|
||||||
// There's an issue here. We can't resize the swap chain while the GPU is still busy with it,
|
// There's an issue here. We can't resize the swap chain while the GPU is still busy with it,
|
||||||
@ -729,7 +739,8 @@ void Renderer::DrawScreen(const TargetRectangle& scaled_efb_rect, u32 xfb_addr,
|
|||||||
// command buffer, resize the swap chain (which calls WaitForGPUIdle), and then finally call
|
// command buffer, resize the swap chain (which calls WaitForGPUIdle), and then finally call
|
||||||
// PrepareToSubmitCommandBuffer to return to the state that the caller expects.
|
// PrepareToSubmitCommandBuffer to return to the state that the caller expects.
|
||||||
g_command_buffer_mgr->SubmitCommandBuffer(false);
|
g_command_buffer_mgr->SubmitCommandBuffer(false);
|
||||||
ResizeSwapChain();
|
m_swap_chain->ResizeSwapChain();
|
||||||
|
BeginFrame();
|
||||||
g_command_buffer_mgr->PrepareToSubmitCommandBuffer();
|
g_command_buffer_mgr->PrepareToSubmitCommandBuffer();
|
||||||
res = m_swap_chain->AcquireNextImage(m_image_available_semaphore);
|
res = m_swap_chain->AcquireNextImage(m_image_available_semaphore);
|
||||||
}
|
}
|
||||||
@ -1065,14 +1076,17 @@ void Renderer::CheckForSurfaceChange()
|
|||||||
if (!m_surface_needs_change.IsSet())
|
if (!m_surface_needs_change.IsSet())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
u32 old_width = m_swap_chain ? m_swap_chain->GetWidth() : 0;
|
// Wait for the GPU to catch up since we're going to destroy the swap chain.
|
||||||
u32 old_height = m_swap_chain ? m_swap_chain->GetHeight() : 0;
|
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.
|
// 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())
|
if (m_swap_chain && m_new_surface_handle == m_swap_chain->GetNativeHandle())
|
||||||
{
|
{
|
||||||
INFO_LOG(VIDEO, "Detected window resize.");
|
INFO_LOG(VIDEO, "Detected window resize.");
|
||||||
ResizeSwapChain();
|
m_swap_chain->RecreateSwapChain();
|
||||||
|
|
||||||
// Notify the main thread we are done.
|
// Notify the main thread we are done.
|
||||||
m_surface_needs_change.Clear();
|
m_surface_needs_change.Clear();
|
||||||
@ -1081,9 +1095,6 @@ void Renderer::CheckForSurfaceChange()
|
|||||||
}
|
}
|
||||||
else
|
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?
|
// Did we previously have a swap chain?
|
||||||
if (m_swap_chain)
|
if (m_swap_chain)
|
||||||
{
|
{
|
||||||
@ -1122,12 +1133,8 @@ void Renderer::CheckForSurfaceChange()
|
|||||||
m_surface_changed.Set();
|
m_surface_changed.Set();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_swap_chain)
|
// Handle case where the dimensions are now different.
|
||||||
{
|
OnSwapChainResized();
|
||||||
// Handle case where the dimensions are now different
|
|
||||||
if (old_width != m_swap_chain->GetWidth() || old_height != m_swap_chain->GetHeight())
|
|
||||||
OnSwapChainResized();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Renderer::CheckForConfigChanges()
|
void Renderer::CheckForConfigChanges()
|
||||||
@ -1191,7 +1198,10 @@ void Renderer::CheckForConfigChanges()
|
|||||||
// For quad-buffered stereo we need to change the layer count, so recreate the swap chain.
|
// For quad-buffered stereo we need to change the layer count, so recreate the swap chain.
|
||||||
if (m_swap_chain &&
|
if (m_swap_chain &&
|
||||||
(g_ActiveConfig.iStereoMode == STEREO_QUADBUFFER) != m_swap_chain->IsStereoEnabled())
|
(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.
|
// Wipe sampler cache if force texture filtering or anisotropy changes.
|
||||||
if (anisotropy_changed || force_texture_filtering_changed)
|
if (anisotropy_changed || force_texture_filtering_changed)
|
||||||
@ -1237,18 +1247,6 @@ void Renderer::ResizeEFBTextures()
|
|||||||
BPFunctions::SetScissor();
|
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()
|
void Renderer::ApplyState()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -81,7 +81,6 @@ private:
|
|||||||
void OnSwapChainResized();
|
void OnSwapChainResized();
|
||||||
void BindEFBToStateTracker();
|
void BindEFBToStateTracker();
|
||||||
void ResizeEFBTextures();
|
void ResizeEFBTextures();
|
||||||
void ResizeSwapChain();
|
|
||||||
|
|
||||||
void RecompileShaders();
|
void RecompileShaders();
|
||||||
bool CompileShaders();
|
bool CompileShaders();
|
||||||
|
@ -472,14 +472,27 @@ bool SwapChain::ResizeSwapChain()
|
|||||||
return true;
|
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)
|
bool SwapChain::SetVSync(bool enabled)
|
||||||
{
|
{
|
||||||
if (m_vsync_enabled == enabled)
|
if (m_vsync_enabled == enabled)
|
||||||
return true;
|
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;
|
m_vsync_enabled = enabled;
|
||||||
return ResizeSwapChain();
|
return RecreateSwapChain();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SwapChain::RecreateSurface(void* native_handle)
|
bool SwapChain::RecreateSurface(void* native_handle)
|
||||||
|
@ -55,6 +55,7 @@ public:
|
|||||||
|
|
||||||
bool RecreateSurface(void* native_handle);
|
bool RecreateSurface(void* native_handle);
|
||||||
bool ResizeSwapChain();
|
bool ResizeSwapChain();
|
||||||
|
bool RecreateSwapChain();
|
||||||
|
|
||||||
// Change vsync enabled state. This may fail as it causes a swapchain recreation.
|
// Change vsync enabled state. This may fail as it causes a swapchain recreation.
|
||||||
bool SetVSync(bool enabled);
|
bool SetVSync(bool enabled);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user