From 34332dbeebe04b933e0cf26764356dc5048dfac2 Mon Sep 17 00:00:00 2001 From: Stephen Saunders Date: Tue, 23 May 2023 08:48:56 -0400 Subject: [PATCH 1/3] Implement m_frameLatencyWaitableObject sync for reduced DX12 frame latency --- neo/sys/DeviceManager_DX12.cpp | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/neo/sys/DeviceManager_DX12.cpp b/neo/sys/DeviceManager_DX12.cpp index f6c228f8..34d13702 100644 --- a/neo/sys/DeviceManager_DX12.cpp +++ b/neo/sys/DeviceManager_DX12.cpp @@ -47,6 +47,7 @@ using nvrhi::RefCountPtr; #define HR_RETURN(hr) if(FAILED(hr)) return false idCVar r_graphicsAdapter( "r_graphicsAdapter", "", CVAR_RENDERER | CVAR_INIT | CVAR_ARCHIVE, "Substring in the name the DXGI graphics adapter to select a certain GPU" ); +idCVar r_dx12FrameLatency( "r_dx12FrameLatency", "0", CVAR_INTEGER | CVAR_RENDERER | CVAR_INIT, "DX12 maximum frame latency using DXGI swap chain waitable object" ); class DeviceManager_DX12 : public DeviceManager { @@ -58,6 +59,7 @@ class DeviceManager_DX12 : public DeviceManager DXGI_SWAP_CHAIN_DESC1 m_SwapChainDesc{}; DXGI_SWAP_CHAIN_FULLSCREEN_DESC m_FullScreenDesc{}; RefCountPtr m_DxgiAdapter; + HANDLE m_frameLatencyWaitableObject = nullptr; bool m_TearingSupported = false; std::vector> m_SwapChainBuffers; @@ -310,7 +312,8 @@ bool DeviceManager_DX12::CreateDeviceAndSwapChain() m_SwapChainDesc.BufferUsage = m_DeviceParams.swapChainUsage; m_SwapChainDesc.BufferCount = m_DeviceParams.swapChainBufferCount; m_SwapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; - m_SwapChainDesc.Flags = m_DeviceParams.allowModeSwitch ? DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH : 0; + m_SwapChainDesc.Flags = ( m_DeviceParams.allowModeSwitch ? DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH : 0 ) | + ( r_dx12FrameLatency.GetInteger() > 0 ? DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT : 0 ); // Special processing for sRGB swap chain formats. // DXGI will not create a swap chain with an sRGB format, but its contents will be interpreted as sRGB. @@ -429,6 +432,14 @@ bool DeviceManager_DX12::CreateDeviceAndSwapChain() hr = pSwapChain1->QueryInterface( IID_PPV_ARGS( &m_SwapChain ) ); HR_RETURN( hr ); + if( r_dx12FrameLatency.GetInteger() > 0 ) + { + hr = m_SwapChain->SetMaximumFrameLatency( r_dx12FrameLatency.GetInteger() ); + HR_RETURN( hr ); + + m_frameLatencyWaitableObject = m_SwapChain->GetFrameLatencyWaitableObject(); + } + nvrhi::d3d12::DeviceDesc deviceDesc; deviceDesc.errorCB = &DefaultMessageCallback::GetInstance(); deviceDesc.pDevice = m_Device12; @@ -616,6 +627,14 @@ void DeviceManager_DX12::Present() // SRS - Don't change m_DeviceParams.vsyncEnabled here, simply test for vsync mode 2 to set DXGI SyncInterval m_SwapChain->Present( m_DeviceParams.vsyncEnabled == 2 ? 1 : 0, presentFlags ); + if( m_frameLatencyWaitableObject ) + { + OPTICK_CATEGORY("DX12_Sync1", Optick::Category::Wait); + + // SRS - When m_frameLatencyWaitableObject active, sync first on earlier present + DWORD result = WaitForSingleObjectEx( m_frameLatencyWaitableObject, INFINITE, true ); + } + if constexpr( NUM_FRAME_DATA > 2 ) { OPTICK_CATEGORY( "DX12_Sync3", Optick::Category::Wait ); From 85e980421af857c2f67a8d0e403313d80a42a327 Mon Sep 17 00:00:00 2001 From: Stephen Saunders Date: Sat, 27 May 2023 01:40:40 -0400 Subject: [PATCH 2/3] Change r_maxFrameLatency cvar name and set to default value of 2 frames --- neo/sys/DeviceManager_DX12.cpp | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/neo/sys/DeviceManager_DX12.cpp b/neo/sys/DeviceManager_DX12.cpp index 34d13702..021c7131 100644 --- a/neo/sys/DeviceManager_DX12.cpp +++ b/neo/sys/DeviceManager_DX12.cpp @@ -47,7 +47,7 @@ using nvrhi::RefCountPtr; #define HR_RETURN(hr) if(FAILED(hr)) return false idCVar r_graphicsAdapter( "r_graphicsAdapter", "", CVAR_RENDERER | CVAR_INIT | CVAR_ARCHIVE, "Substring in the name the DXGI graphics adapter to select a certain GPU" ); -idCVar r_dx12FrameLatency( "r_dx12FrameLatency", "0", CVAR_INTEGER | CVAR_RENDERER | CVAR_INIT, "DX12 maximum frame latency using DXGI swap chain waitable object" ); +idCVar r_maxFrameLatency( "r_maxFrameLatency", "2", CVAR_RENDERER | CVAR_INIT | CVAR_ARCHIVE | CVAR_INTEGER, "Maximum frame latency for DXGI swap chains (DX12 only)", 0, 3 ); class DeviceManager_DX12 : public DeviceManager { @@ -59,7 +59,7 @@ class DeviceManager_DX12 : public DeviceManager DXGI_SWAP_CHAIN_DESC1 m_SwapChainDesc{}; DXGI_SWAP_CHAIN_FULLSCREEN_DESC m_FullScreenDesc{}; RefCountPtr m_DxgiAdapter; - HANDLE m_frameLatencyWaitableObject = nullptr; + HANDLE m_frameLatencyWaitableObject = NULL; bool m_TearingSupported = false; std::vector> m_SwapChainBuffers; @@ -313,7 +313,7 @@ bool DeviceManager_DX12::CreateDeviceAndSwapChain() m_SwapChainDesc.BufferCount = m_DeviceParams.swapChainBufferCount; m_SwapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; m_SwapChainDesc.Flags = ( m_DeviceParams.allowModeSwitch ? DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH : 0 ) | - ( r_dx12FrameLatency.GetInteger() > 0 ? DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT : 0 ); + ( r_maxFrameLatency.GetInteger() > 0 ? DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT : 0 ); // Special processing for sRGB swap chain formats. // DXGI will not create a swap chain with an sRGB format, but its contents will be interpreted as sRGB. @@ -432,9 +432,9 @@ bool DeviceManager_DX12::CreateDeviceAndSwapChain() hr = pSwapChain1->QueryInterface( IID_PPV_ARGS( &m_SwapChain ) ); HR_RETURN( hr ); - if( r_dx12FrameLatency.GetInteger() > 0 ) + if( r_maxFrameLatency.GetInteger() > 0 ) { - hr = m_SwapChain->SetMaximumFrameLatency( r_dx12FrameLatency.GetInteger() ); + hr = m_SwapChain->SetMaximumFrameLatency( r_maxFrameLatency.GetInteger() ); HR_RETURN( hr ); m_frameLatencyWaitableObject = m_SwapChain->GetFrameLatencyWaitableObject(); @@ -479,6 +479,12 @@ void DeviceManager_DX12::DestroyDeviceAndSwapChain() m_FrameWaitQuery = nullptr; + if( m_frameLatencyWaitableObject ) + { + CloseHandle( m_frameLatencyWaitableObject ); + m_frameLatencyWaitableObject = NULL; + } + if( m_SwapChain ) { m_SwapChain->SetFullscreenState( false, nullptr ); @@ -629,10 +635,11 @@ void DeviceManager_DX12::Present() if( m_frameLatencyWaitableObject ) { - OPTICK_CATEGORY("DX12_Sync1", Optick::Category::Wait); + OPTICK_CATEGORY( "DX12_Sync1", Optick::Category::Wait ); // SRS - When m_frameLatencyWaitableObject active, sync first on earlier present DWORD result = WaitForSingleObjectEx( m_frameLatencyWaitableObject, INFINITE, true ); + assert( result == WAIT_OBJECT_0 ); } if constexpr( NUM_FRAME_DATA > 2 ) From 89ad088ef36c53dac1f1f5c61342e65a577d166d Mon Sep 17 00:00:00 2001 From: Stephen Saunders Date: Thu, 1 Jun 2023 01:01:53 -0400 Subject: [PATCH 3/3] Set r_maxFrameLatency max value constraint to NUM_FRAME_DATA --- neo/sys/DeviceManager_DX12.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neo/sys/DeviceManager_DX12.cpp b/neo/sys/DeviceManager_DX12.cpp index 021c7131..fd558b16 100644 --- a/neo/sys/DeviceManager_DX12.cpp +++ b/neo/sys/DeviceManager_DX12.cpp @@ -47,7 +47,7 @@ using nvrhi::RefCountPtr; #define HR_RETURN(hr) if(FAILED(hr)) return false idCVar r_graphicsAdapter( "r_graphicsAdapter", "", CVAR_RENDERER | CVAR_INIT | CVAR_ARCHIVE, "Substring in the name the DXGI graphics adapter to select a certain GPU" ); -idCVar r_maxFrameLatency( "r_maxFrameLatency", "2", CVAR_RENDERER | CVAR_INIT | CVAR_ARCHIVE | CVAR_INTEGER, "Maximum frame latency for DXGI swap chains (DX12 only)", 0, 3 ); +idCVar r_maxFrameLatency( "r_maxFrameLatency", "2", CVAR_RENDERER | CVAR_INIT | CVAR_ARCHIVE | CVAR_INTEGER, "Maximum frame latency for DXGI swap chains (DX12 only)", 0, NUM_FRAME_DATA ); class DeviceManager_DX12 : public DeviceManager {