From 56395c9bc86557720074073427d0ceaee44559cc Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Mon, 2 Aug 2021 19:11:35 +0200 Subject: [PATCH] [d3d11] Re-implement frame latency events using Win32 semaphore Matches native DXGI behaviour more accurately, and fixes a hang in Shin Megami Tensei 3. --- src/d3d11/d3d11_swapchain.cpp | 20 ++++++++++++++------ src/d3d11/d3d11_swapchain.h | 4 ++-- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/d3d11/d3d11_swapchain.cpp b/src/d3d11/d3d11_swapchain.cpp index c250bf77e..b803d37c0 100644 --- a/src/d3d11/d3d11_swapchain.cpp +++ b/src/d3d11/d3d11_swapchain.cpp @@ -177,9 +177,16 @@ namespace dxvk { if (MaxLatency == 0 || MaxLatency > DXGI_MAX_SWAP_CHAIN_BUFFERS) return DXGI_ERROR_INVALID_CALL; - m_frameLatency = MaxLatency; + if (m_frameLatencyEvent) { + // Windows DXGI does not seem to handle the case where the new maximum + // latency is less than the current value, and some games relying on + // this behaviour will hang if we attempt to decrement the semaphore. + // Thus, only increment the semaphore as necessary. + if (MaxLatency > m_frameLatency) + ReleaseSemaphore(m_frameLatencyEvent, MaxLatency - m_frameLatency, nullptr); + } - SyncFrameLatency(); + m_frameLatency = MaxLatency; return S_OK; } @@ -367,10 +374,10 @@ namespace dxvk { void D3D11SwapChain::CreateFrameLatencyEvent() { - m_frameLatencySignal = new sync::Win32Fence(m_frameId); + m_frameLatencySignal = new sync::CallbackFence(m_frameId); if (m_desc.Flags & DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT) - m_frameLatencyEvent = CreateEvent(nullptr, false, true, nullptr); + m_frameLatencyEvent = CreateSemaphore(nullptr, m_frameLatency, DXGI_MAX_SWAP_CHAIN_BUFFERS, nullptr); } @@ -558,8 +565,9 @@ namespace dxvk { m_frameLatencySignal->wait(m_frameId - GetActualFrameLatency()); if (m_frameLatencyEvent) { - // Signal event with the same value that we'd wait for during the next present. - m_frameLatencySignal->setEvent(m_frameLatencyEvent, m_frameId - GetActualFrameLatency() + 1); + m_frameLatencySignal->setCallback(m_frameId, [cFrameLatencyEvent = m_frameLatencyEvent] () { + ReleaseSemaphore(cFrameLatencyEvent, 1, nullptr); + }); } } diff --git a/src/d3d11/d3d11_swapchain.h b/src/d3d11/d3d11_swapchain.h index 530fdca3e..8fc9fe10c 100644 --- a/src/d3d11/d3d11_swapchain.h +++ b/src/d3d11/d3d11_swapchain.h @@ -6,7 +6,7 @@ #include "../dxvk/dxvk_swapchain_blitter.h" -#include "../util/sync/sync_signal_win32.h" +#include "../util/sync/sync_signal.h" namespace dxvk { @@ -107,7 +107,7 @@ namespace dxvk { uint32_t m_frameLatency = DefaultFrameLatency; uint32_t m_frameLatencyCap = 0; HANDLE m_frameLatencyEvent = nullptr; - Rc m_frameLatencySignal; + Rc m_frameLatencySignal; bool m_dirty = true; bool m_vsync = true;