diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp index d1e607e75f..92dd0f015d 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp +++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp @@ -177,6 +177,10 @@ try RendererVulkan::~RendererVulkan() { scheduler.RegisterOnSubmit([] {}); + scheduler.WaitWorker(); + // vkDeviceWaitIdle MUST be called only after all queue submissions are complete + // to avoid threading errors on VkQueue simultaneous access + std::scoped_lock lock{scheduler.submit_mutex}; void(device.GetLogical().WaitIdle()); } diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.cpp b/src/video_core/renderer_vulkan/vk_blit_screen.cpp index 0f54dd5ade..db6336187c 100644 --- a/src/video_core/renderer_vulkan/vk_blit_screen.cpp +++ b/src/video_core/renderer_vulkan/vk_blit_screen.cpp @@ -30,6 +30,9 @@ BlitScreen::~BlitScreen() = default; void BlitScreen::WaitIdle() { present_manager.WaitPresent(); scheduler.Finish(); + std::scoped_lock lock{scheduler.submit_mutex}; + // vkDeviceWaitIdle MUST be protected by submit_mutex to prevent racing with queue submissions + // from the worker thread. This ensures no simultaneous access to VkQueue. device.GetLogical().WaitIdle(); }