PresentWait: Correctly handle swapchain destruction

Also, reimplmented as WorkQueueThread.
This commit is contained in:
Scott Mansell 2023-01-23 19:51:12 +13:00 committed by nyanpasu64
parent ae837c3493
commit d7e2edeaa2
4 changed files with 47 additions and 39 deletions

View File

@ -7,75 +7,70 @@
#include <thread> #include <thread>
#include <tuple> #include <tuple>
#include "Common/BlockingLoop.h" #include "Common/WorkQueueThread.h"
#include "VideoBackends/Vulkan/VulkanContext.h" #include "VideoBackends/Vulkan/VulkanContext.h"
#include "VideoBackends/Vulkan/VulkanLoader.h" #include "VideoBackends/Vulkan/VulkanLoader.h"
#include "VideoCommon/PerformanceMetrics.h" #include "VideoCommon/PerformanceMetrics.h"
#include <chrono>
namespace Vulkan namespace Vulkan
{ {
static VkDevice s_device;
static std::thread s_present_wait_thread; struct Wait
static Common::BlockingLoop s_present_wait_loop;
static std::deque<std::tuple<u64, VkSwapchainKHR>> s_present_wait_queue;
static void PresentWaitThreadFunc()
{ {
VkDevice device = g_vulkan_context->GetDevice(); u64 present_id;
VkSwapchainKHR swapchain;
};
s_present_wait_loop.Run([device]() { static Common::WorkQueueThread<Wait> s_present_wait_thread;
if (s_present_wait_queue.empty())
{
s_present_wait_loop.AllowSleep();
return;
}
u64 present_id;
VkSwapchainKHR swapchain;
std::tie(present_id, swapchain) = s_present_wait_queue.back();
auto start = std::chrono::high_resolution_clock::now(); void WaitFunction(Wait wait)
{
VkResult res = vkWaitForPresentKHR(device, swapchain, present_id, 100'000'000); // 100ms do
{
// We choose a timeout of 20ms so can poll for IsFlushing
VkResult res = vkWaitForPresentKHR(s_device, wait.swapchain, wait.present_id, 20'000'000);
if (res == VK_TIMEOUT) if (res == VK_TIMEOUT)
{ {
WARN_LOG_FMT(VIDEO, "vkWaitForPresentKHR timed out, retrying {}", present_id); WARN_LOG_FMT(VIDEO, "vkWaitForPresentKHR timed out, retrying {}", wait.present_id);
return; continue;
} }
s_present_wait_queue.pop_back();
if (res != VK_SUCCESS) if (res != VK_SUCCESS)
{ {
LOG_VULKAN_ERROR(res, "vkWaitForPresentKHR failed: "); LOG_VULKAN_ERROR(res, "vkWaitForPresentKHR failed: ");
} }
if (res == VK_SUCCESS) if (res == VK_SUCCESS)
{
auto end = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
fmt::print("vkWaitForPresentKHR took {}us\n", duration.count());
g_perf_metrics.CountPresent(); g_perf_metrics.CountPresent();
}
}); return;
} while (!s_present_wait_thread.IsCancelling());
} }
void StartPresentWaitThread() void StartPresentWaitThread()
{ {
fmt::print("Starting PresentWaitThread"); s_device = g_vulkan_context->GetDevice();
s_present_wait_thread = std::thread(PresentWaitThreadFunc); s_present_wait_thread.Reset("PresentWait", WaitFunction);
}
void StopPresentWaitThread()
{
s_present_wait_thread.Shutdown();
} }
void PresentQueued(u64 present_id, VkSwapchainKHR swapchain) void PresentQueued(u64 present_id, VkSwapchainKHR swapchain)
{ {
s_present_wait_queue.emplace_front(std::make_tuple(present_id, swapchain)); s_present_wait_thread.EmplaceItem(Wait{present_id, swapchain});
s_present_wait_loop.Wakeup(); }
void FlushPresentWaitQueue()
{
s_present_wait_thread.Cancel();
s_present_wait_thread.WaitForCompletion();
} }
} // namespace Vulkan } // namespace Vulkan

View File

@ -10,6 +10,9 @@ namespace Vulkan
{ {
void StartPresentWaitThread(); void StartPresentWaitThread();
void StopPresentWaitThread();
void PresentQueued(u64 present_id, VkSwapchainKHR swapchain); void PresentQueued(u64 present_id, VkSwapchainKHR swapchain);
void FlushPresentWaitQueue();
} // namespace Vulkan } // namespace Vulkan

View File

@ -228,9 +228,7 @@ bool VideoBackend::Initialize(const WindowSystemInfo& wsi)
} }
if (g_vulkan_context->SupportsPresentWait()) if (g_vulkan_context->SupportsPresentWait())
{
StartPresentWaitThread(); StartPresentWaitThread();
}
} }
if (!StateTracker::CreateInstance()) if (!StateTracker::CreateInstance())
@ -251,6 +249,9 @@ bool VideoBackend::Initialize(const WindowSystemInfo& wsi)
void VideoBackend::Shutdown() void VideoBackend::Shutdown()
{ {
if (g_vulkan_context->SupportsPresentWait())
StopPresentWaitThread();
if (g_vulkan_context) if (g_vulkan_context)
vkDeviceWaitIdle(g_vulkan_context->GetDevice()); vkDeviceWaitIdle(g_vulkan_context->GetDevice());

View File

@ -13,6 +13,7 @@
#include "VideoBackends/Vulkan/CommandBufferManager.h" #include "VideoBackends/Vulkan/CommandBufferManager.h"
#include "VideoBackends/Vulkan/ObjectCache.h" #include "VideoBackends/Vulkan/ObjectCache.h"
#include "VideoBackends/Vulkan/PresentWait.h"
#include "VideoBackends/Vulkan/VKTexture.h" #include "VideoBackends/Vulkan/VKTexture.h"
#include "VideoBackends/Vulkan/VulkanContext.h" #include "VideoBackends/Vulkan/VulkanContext.h"
#include "VideoCommon/Present.h" #include "VideoCommon/Present.h"
@ -332,6 +333,11 @@ bool SwapChain::CreateSwapChain()
VkSwapchainKHR old_swap_chain = m_swap_chain; VkSwapchainKHR old_swap_chain = m_swap_chain;
m_swap_chain = VK_NULL_HANDLE; m_swap_chain = VK_NULL_HANDLE;
// vkWaitForPresentKHR uses a copy of swapchain, we need to make sure it's flushed
// before passing it as the old swapchain arg of vkCreateSwapchainKHR
if (old_swap_chain && g_vulkan_context->SupportsPresentWait())
FlushPresentWaitQueue();
// Now we can actually create the swap chain // Now we can actually create the swap chain
VkSwapchainCreateInfoKHR swap_chain_info = {VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, VkSwapchainCreateInfoKHR swap_chain_info = {VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
nullptr, nullptr,
@ -487,6 +493,9 @@ void SwapChain::DestroySwapChain()
if (m_current_fullscreen_state) if (m_current_fullscreen_state)
SetFullscreenState(false); SetFullscreenState(false);
if (g_vulkan_context->SupportsPresentWait())
FlushPresentWaitQueue();
vkDestroySwapchainKHR(g_vulkan_context->GetDevice(), m_swap_chain, nullptr); vkDestroySwapchainKHR(g_vulkan_context->GetDevice(), m_swap_chain, nullptr);
m_swap_chain = VK_NULL_HANDLE; m_swap_chain = VK_NULL_HANDLE;
} }