PresentWait: Correctly handle swapchain destruction
Also, reimplmented as WorkQueueThread.
This commit is contained in:
parent
ae837c3493
commit
d7e2edeaa2
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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());
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue