2022-03-11 10:44:07 +00:00
/*
* Copyright ( c ) 2014 - 2021 , NVIDIA CORPORATION . All rights reserved .
2023-03-06 19:06:15 +00:00
* Copyright ( C ) 2022 Stephen Pridham ( id Tech 4 x integration )
* Copyright ( C ) 2023 Stephen Saunders ( id Tech 4 x integration )
* Copyright ( C ) 2023 Robert Beckebans ( id Tech 4 x integration )
2022-03-11 10:44:07 +00:00
*
* Permission is hereby granted , free of charge , to any person obtaining a
* copy of this software and associated documentation files ( the " Software " ) ,
* to deal in the Software without restriction , including without limitation
* the rights to use , copy , modify , merge , publish , distribute , sublicense ,
* and / or sell copies of the Software , and to permit persons to whom the
* Software is furnished to do so , subject to the following conditions :
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software .
*
* THE SOFTWARE IS PROVIDED " AS IS " , WITHOUT WARRANTY OF ANY KIND , EXPRESS OR
* IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY ,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT . IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM , DAMAGES OR OTHER
* LIABILITY , WHETHER IN AN ACTION OF CONTRACT , TORT OR OTHERWISE , ARISING
* FROM , OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE .
*/
2022-02-21 17:15:56 +00:00
# include "precompiled.h"
# include "renderer/RenderCommon.h"
# include "renderer/RenderSystem.h"
2023-11-30 17:26:38 +00:00
# include "framework/Common_local.h"
2022-11-10 17:19:54 +00:00
# include <sys/DeviceManager.h>
2022-02-21 17:15:56 +00:00
# include <Windows.h>
# include <dxgi1_5.h>
# include <dxgidebug.h>
# include <nvrhi/d3d12.h>
# include <nvrhi/validation.h>
# include <sstream>
# include <vector>
# pragma comment(lib, "d3d12.lib")
# pragma comment(lib, "dxgi.lib")
using nvrhi : : RefCountPtr ;
# define HR_RETURN(hr) if(FAILED(hr)) return false
2024-07-10 18:33:20 +00:00
idCVar r_graphicsAdapter ( " r_graphicsAdapter " , " " , CVAR_RENDERER | CVAR_INIT | CVAR_ARCHIVE | CVAR_NEW , " Substring in the name the DXGI graphics adapter to select a certain GPU " ) ;
idCVar r_dxMaxFrameLatency ( " r_dxMaxFrameLatency " , " 2 " , CVAR_RENDERER | CVAR_INIT | CVAR_ARCHIVE | CVAR_INTEGER | CVAR_NEW , " Maximum frame latency for DXGI swap chains (DX12 only) " , 0, NUM_FRAME_DATA ) ;
2023-02-28 21:01:26 +00:00
2022-02-21 17:15:56 +00:00
class DeviceManager_DX12 : public DeviceManager
{
RefCountPtr < ID3D12Device > m_Device12 ;
RefCountPtr < ID3D12CommandQueue > m_GraphicsQueue ;
RefCountPtr < ID3D12CommandQueue > m_ComputeQueue ;
RefCountPtr < ID3D12CommandQueue > m_CopyQueue ;
RefCountPtr < IDXGISwapChain3 > m_SwapChain ;
DXGI_SWAP_CHAIN_DESC1 m_SwapChainDesc { } ;
2023-03-06 16:05:43 +00:00
DXGI_SWAP_CHAIN_FULLSCREEN_DESC m_FullScreenDesc { } ;
2023-11-30 17:26:38 +00:00
RefCountPtr < IDXGIAdapter3 > m_DxgiAdapter ;
2023-05-27 05:40:40 +00:00
HANDLE m_frameLatencyWaitableObject = NULL ;
2022-02-21 17:15:56 +00:00
bool m_TearingSupported = false ;
std : : vector < RefCountPtr < ID3D12Resource > > m_SwapChainBuffers ;
std : : vector < nvrhi : : TextureHandle > m_RhiSwapChainBuffers ;
2023-02-28 23:02:45 +00:00
nvrhi : : EventQueryHandle m_FrameWaitQuery ;
2022-02-21 17:15:56 +00:00
2023-03-06 16:05:43 +00:00
nvrhi : : DeviceHandle m_NvrhiDevice ;
2022-02-21 17:15:56 +00:00
2023-03-06 16:05:43 +00:00
std : : string m_RendererString ;
2022-02-21 17:15:56 +00:00
public :
const char * GetRendererString ( ) const override
{
2023-03-06 16:05:43 +00:00
return m_RendererString . c_str ( ) ;
2022-02-21 17:15:56 +00:00
}
nvrhi : : IDevice * GetDevice ( ) const override
{
2023-03-06 16:05:43 +00:00
return m_NvrhiDevice ;
2022-02-21 17:15:56 +00:00
}
void ReportLiveObjects ( ) override ;
nvrhi : : GraphicsAPI GetGraphicsAPI ( ) const override
{
return nvrhi : : GraphicsAPI : : D3D12 ;
}
protected :
bool CreateDeviceAndSwapChain ( ) override ;
void DestroyDeviceAndSwapChain ( ) override ;
void ResizeSwapChain ( ) override ;
nvrhi : : ITexture * GetCurrentBackBuffer ( ) override ;
nvrhi : : ITexture * GetBackBuffer ( uint32_t index ) override ;
uint32_t GetCurrentBackBufferIndex ( ) override ;
uint32_t GetBackBufferCount ( ) override ;
void BeginFrame ( ) override ;
2022-09-22 16:34:07 +00:00
void EndFrame ( ) override ;
2022-02-21 17:15:56 +00:00
void Present ( ) override ;
private :
bool CreateRenderTargets ( ) ;
void ReleaseRenderTargets ( ) ;
} ;
static bool IsNvDeviceID ( UINT id )
{
return id = = 0x10DE ;
}
// Find an adapter whose name contains the given string.
static RefCountPtr < IDXGIAdapter > FindAdapter ( const std : : wstring & targetName )
{
RefCountPtr < IDXGIAdapter > targetAdapter ;
RefCountPtr < IDXGIFactory1 > DXGIFactory ;
HRESULT hres = CreateDXGIFactory1 ( IID_PPV_ARGS ( & DXGIFactory ) ) ;
if ( hres ! = S_OK )
{
common - > FatalError ( " ERROR in CreateDXGIFactory. \n "
" For more info, get log from debug D3D runtime: (1) Install DX SDK, and enable Debug D3D from DX Control Panel Utility. (2) Install and start DbgView. (3) Try running the program again. \n " ) ;
return targetAdapter ;
}
2023-04-02 10:31:22 +00:00
RefCountPtr < IDXGIFactory6 > DXGIFactory6 ;
2022-02-21 17:15:56 +00:00
unsigned int adapterNo = 0 ;
while ( SUCCEEDED ( hres ) )
{
RefCountPtr < IDXGIAdapter > pAdapter ;
2023-04-02 10:31:22 +00:00
// Try to use EnumAdapterByGpuPreference method to get the better performing GPU.
if ( S_OK = = DXGIFactory - > QueryInterface ( IID_PPV_ARGS ( & DXGIFactory6 ) ) )
{
hres = DXGIFactory6 - > EnumAdapterByGpuPreference ( adapterNo , DXGI_GPU_PREFERENCE_HIGH_PERFORMANCE , IID_PPV_ARGS ( & pAdapter ) ) ;
}
else
{
hres = DXGIFactory - > EnumAdapters ( adapterNo , & pAdapter ) ;
}
2022-02-21 17:15:56 +00:00
if ( SUCCEEDED ( hres ) )
{
DXGI_ADAPTER_DESC aDesc ;
pAdapter - > GetDesc ( & aDesc ) ;
// If no name is specified, return the first adapater. This is the same behaviour as the
// default specified for D3D11CreateDevice when no adapter is specified.
if ( targetName . length ( ) = = 0 )
{
targetAdapter = pAdapter ;
break ;
}
std : : wstring aName = aDesc . Description ;
if ( aName . find ( targetName ) ! = std : : string : : npos )
{
targetAdapter = pAdapter ;
break ;
}
}
adapterNo + + ;
}
return targetAdapter ;
}
// Adjust window rect so that it is centred on the given adapter. Clamps to fit if it's too big.
static bool MoveWindowOntoAdapter ( IDXGIAdapter * targetAdapter , RECT & rect )
{
assert ( targetAdapter ! = NULL ) ;
HRESULT hres = S_OK ;
unsigned int outputNo = 0 ;
while ( SUCCEEDED ( hres ) )
{
IDXGIOutput * pOutput = nullptr ;
hres = targetAdapter - > EnumOutputs ( outputNo + + , & pOutput ) ;
if ( SUCCEEDED ( hres ) & & pOutput )
{
DXGI_OUTPUT_DESC OutputDesc ;
pOutput - > GetDesc ( & OutputDesc ) ;
const RECT desktop = OutputDesc . DesktopCoordinates ;
const int centreX = ( int ) desktop . left + ( int ) ( desktop . right - desktop . left ) / 2 ;
const int centreY = ( int ) desktop . top + ( int ) ( desktop . bottom - desktop . top ) / 2 ;
const int winW = rect . right - rect . left ;
const int winH = rect . bottom - rect . top ;
const int left = centreX - winW / 2 ;
const int right = left + winW ;
const int top = centreY - winH / 2 ;
const int bottom = top + winH ;
2022-11-19 15:46:21 +00:00
rect . left = Max ( left , ( int ) desktop . left ) ;
rect . right = Min ( right , ( int ) desktop . right ) ;
rect . bottom = Min ( bottom , ( int ) desktop . bottom ) ;
rect . top = Max ( top , ( int ) desktop . top ) ;
2022-02-21 17:15:56 +00:00
// If there is more than one output, go with the first found. Multi-monitor support could go here.
return true ;
}
}
return false ;
}
void DeviceManager_DX12 : : ReportLiveObjects ( )
{
nvrhi : : RefCountPtr < IDXGIDebug > pDebug ;
DXGIGetDebugInterface1 ( 0 , IID_PPV_ARGS ( & pDebug ) ) ;
if ( pDebug )
{
pDebug - > ReportLiveObjects ( DXGI_DEBUG_ALL , DXGI_DEBUG_RLO_IGNORE_INTERNAL ) ;
}
}
2023-02-28 21:01:26 +00:00
std : : wstring StrToWS ( const std : : string & str )
{
int sizeNeeded = MultiByteToWideChar ( CP_UTF8 , 0 , & str [ 0 ] , ( int ) str . size ( ) , NULL , 0 ) ;
std : : wstring wstrTo ( sizeNeeded , 0 ) ;
MultiByteToWideChar ( CP_UTF8 , 0 , & str [ 0 ] , ( int ) str . size ( ) , & wstrTo [ 0 ] , sizeNeeded ) ;
return wstrTo ;
}
std : : wstring StrToWS ( const idStr & str )
{
int sizeNeeded = MultiByteToWideChar ( CP_UTF8 , 0 , str . c_str ( ) , str . Length ( ) , NULL , 0 ) ;
std : : wstring wstrTo ( sizeNeeded , 0 ) ;
MultiByteToWideChar ( CP_UTF8 , 0 , str . c_str ( ) , str . Length ( ) , & wstrTo [ 0 ] , sizeNeeded ) ;
return wstrTo ;
}
2022-02-21 17:15:56 +00:00
bool DeviceManager_DX12 : : CreateDeviceAndSwapChain ( )
{
RefCountPtr < IDXGIAdapter > targetAdapter ;
2023-03-06 16:05:43 +00:00
if ( m_DeviceParams . adapter )
2022-02-21 17:15:56 +00:00
{
2023-03-06 16:05:43 +00:00
targetAdapter = m_DeviceParams . adapter ;
2022-02-21 17:15:56 +00:00
}
else
{
2023-02-28 21:01:26 +00:00
idStr adapterName ( r_graphicsAdapter . GetString ( ) ) ;
if ( ! adapterName . IsEmpty ( ) )
{
targetAdapter = FindAdapter ( StrToWS ( adapterName ) ) ;
}
else
{
2023-03-06 16:05:43 +00:00
targetAdapter = FindAdapter ( m_DeviceParams . adapterNameSubstring ) ;
2023-02-28 21:01:26 +00:00
}
2022-02-21 17:15:56 +00:00
if ( ! targetAdapter )
{
2023-03-06 16:05:43 +00:00
std : : wstring adapterNameStr ( m_DeviceParams . adapterNameSubstring . begin ( ) , m_DeviceParams . adapterNameSubstring . end ( ) ) ;
2022-02-21 17:15:56 +00:00
common - > FatalError ( " Could not find an adapter matching %s \n " , adapterNameStr . c_str ( ) ) ;
return false ;
}
}
{
DXGI_ADAPTER_DESC aDesc ;
targetAdapter - > GetDesc ( & aDesc ) ;
std : : wstring adapterName = aDesc . Description ;
// A stupid but non-deprecated and portable way of converting a wstring to a string
std : : stringstream ss ;
std : : wstringstream wss ;
for ( auto c : adapterName )
{
ss < < wss . narrow ( c , ' ? ' ) ;
}
2023-03-06 16:05:43 +00:00
m_RendererString = ss . str ( ) ;
2022-02-21 17:15:56 +00:00
isNvidia = IsNvDeviceID ( aDesc . VendorId ) ;
}
2022-11-18 15:47:29 +00:00
/*
// SRS - Don't center window here for DX12 only, instead use portable initialization in CreateWindowDeviceAndSwapChain() within win_glimp.cpp
// - Also, calling SetWindowPos() triggers a window mgr event that overwrites r_windowX / r_windowY, which may be undesirable to the user
2022-11-12 02:51:31 +00:00
2023-03-06 16:05:43 +00:00
UINT windowStyle = m_DeviceParams . startFullscreen
2022-11-18 15:47:29 +00:00
? ( WS_POPUP | WS_SYSMENU | WS_VISIBLE )
2023-03-06 16:05:43 +00:00
: m_DeviceParams . startMaximized
2022-11-18 15:47:29 +00:00
? ( WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_MAXIMIZE )
: ( WS_OVERLAPPEDWINDOW | WS_VISIBLE ) ;
2022-11-12 02:51:31 +00:00
2023-03-06 16:05:43 +00:00
RECT rect = { 0 , 0 , LONG ( m_DeviceParams . backBufferWidth ) , LONG ( m_DeviceParams . backBufferHeight ) } ;
2022-11-18 15:47:29 +00:00
AdjustWindowRect ( & rect , windowStyle , FALSE ) ;
2022-02-21 17:15:56 +00:00
2022-11-18 15:47:29 +00:00
if ( MoveWindowOntoAdapter ( targetAdapter , rect ) )
{
2023-03-06 16:05:43 +00:00
SetWindowPos ( ( HWND ) windowHandle , m_DeviceParams . startFullscreen ? HWND_TOPMOST : HWND_NOTOPMOST ,
2022-11-18 15:47:29 +00:00
rect . left , rect . top , 0 , 0 , SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSIZE ) ;
}
*/
2022-02-21 17:15:56 +00:00
HRESULT hr = E_FAIL ;
RECT clientRect ;
GetClientRect ( ( HWND ) windowHandle , & clientRect ) ;
UINT width = clientRect . right - clientRect . left ;
UINT height = clientRect . bottom - clientRect . top ;
ZeroMemory ( & m_SwapChainDesc , sizeof ( m_SwapChainDesc ) ) ;
m_SwapChainDesc . Width = width ;
m_SwapChainDesc . Height = height ;
2023-03-06 16:05:43 +00:00
m_SwapChainDesc . SampleDesc . Count = m_DeviceParams . swapChainSampleCount ;
2022-02-21 17:15:56 +00:00
m_SwapChainDesc . SampleDesc . Quality = 0 ;
2023-03-06 16:05:43 +00:00
m_SwapChainDesc . BufferUsage = m_DeviceParams . swapChainUsage ;
m_SwapChainDesc . BufferCount = m_DeviceParams . swapChainBufferCount ;
2022-02-21 17:15:56 +00:00
m_SwapChainDesc . SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD ;
2023-05-23 12:48:56 +00:00
m_SwapChainDesc . Flags = ( m_DeviceParams . allowModeSwitch ? DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH : 0 ) |
2024-04-02 15:17:47 +00:00
( r_dxMaxFrameLatency . GetInteger ( ) > 0 ? DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT : 0 ) ;
2022-02-21 17:15:56 +00:00
// 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.
// So we need to use a non-sRGB format here, but store the true sRGB format for later framebuffer creation.
2023-03-06 16:05:43 +00:00
switch ( m_DeviceParams . swapChainFormat ) // NOLINT(clang-diagnostic-switch-enum)
2022-02-21 17:15:56 +00:00
{
case nvrhi : : Format : : SRGBA8_UNORM :
m_SwapChainDesc . Format = DXGI_FORMAT_R8G8B8A8_UNORM ;
break ;
case nvrhi : : Format : : SBGRA8_UNORM :
m_SwapChainDesc . Format = DXGI_FORMAT_B8G8R8A8_UNORM ;
break ;
default :
2023-03-06 16:05:43 +00:00
m_SwapChainDesc . Format = nvrhi : : d3d12 : : convertFormat ( m_DeviceParams . swapChainFormat ) ;
2022-02-21 17:15:56 +00:00
break ;
}
2023-03-17 18:01:41 +00:00
m_DeviceParams . enableDebugRuntime = r_useValidationLayers . GetInteger ( ) > 1 ;
2023-03-06 16:05:43 +00:00
if ( m_DeviceParams . enableDebugRuntime )
2022-02-21 17:15:56 +00:00
{
RefCountPtr < ID3D12Debug > pDebug ;
hr = D3D12GetDebugInterface ( IID_PPV_ARGS ( & pDebug ) ) ;
HR_RETURN ( hr ) ;
pDebug - > EnableDebugLayer ( ) ;
}
RefCountPtr < IDXGIFactory2 > pDxgiFactory ;
2023-03-06 16:05:43 +00:00
UINT dxgiFactoryFlags = m_DeviceParams . enableDebugRuntime ? DXGI_CREATE_FACTORY_DEBUG : 0 ;
2022-02-21 17:15:56 +00:00
hr = CreateDXGIFactory2 ( dxgiFactoryFlags , IID_PPV_ARGS ( & pDxgiFactory ) ) ;
HR_RETURN ( hr ) ;
RefCountPtr < IDXGIFactory5 > pDxgiFactory5 ;
if ( SUCCEEDED ( pDxgiFactory - > QueryInterface ( IID_PPV_ARGS ( & pDxgiFactory5 ) ) ) )
{
BOOL supported = 0 ;
if ( SUCCEEDED ( pDxgiFactory5 - > CheckFeatureSupport ( DXGI_FEATURE_PRESENT_ALLOW_TEARING , & supported , sizeof ( supported ) ) ) )
{
m_TearingSupported = ( supported ! = 0 ) ;
}
}
if ( m_TearingSupported )
{
m_SwapChainDesc . Flags | = DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING ;
}
hr = D3D12CreateDevice (
targetAdapter ,
2023-03-06 16:05:43 +00:00
m_DeviceParams . featureLevel ,
2022-02-21 17:15:56 +00:00
IID_PPV_ARGS ( & m_Device12 ) ) ;
HR_RETURN ( hr ) ;
2023-03-06 16:05:43 +00:00
if ( m_DeviceParams . enableDebugRuntime )
2022-02-21 17:15:56 +00:00
{
RefCountPtr < ID3D12InfoQueue > pInfoQueue ;
m_Device12 - > QueryInterface ( & pInfoQueue ) ;
if ( pInfoQueue )
{
# ifdef _DEBUG
pInfoQueue - > SetBreakOnSeverity ( D3D12_MESSAGE_SEVERITY_CORRUPTION , true ) ;
pInfoQueue - > SetBreakOnSeverity ( D3D12_MESSAGE_SEVERITY_ERROR , true ) ;
# endif
D3D12_MESSAGE_ID disableMessageIDs [ ] =
{
D3D12_MESSAGE_ID_CLEARDEPTHSTENCILVIEW_MISMATCHINGCLEARVALUE ,
2024-05-10 15:55:14 +00:00
D3D12_MESSAGE_ID_CLEARRENDERTARGETVIEW_MISMATCHINGCLEARVALUE ,
2022-02-21 17:15:56 +00:00
D3D12_MESSAGE_ID_COMMAND_LIST_STATIC_DESCRIPTOR_RESOURCE_DIMENSION_MISMATCH , // descriptor validation doesn't understand acceleration structures
2024-05-11 22:32:01 +00:00
D3D12_MESSAGE_ID_CREATEGRAPHICSPIPELINESTATE_RENDERTARGETVIEW_NOT_SET , // disable warning when there is no color attachment (e.g. shadow atlas)
2024-05-10 15:55:14 +00:00
D3D12_MESSAGE_ID_RESOURCE_BARRIER_BEFORE_AFTER_MISMATCH // barrier validation error caused by cinematics - not sure how to fix, suppress for now
2022-02-21 17:15:56 +00:00
} ;
D3D12_INFO_QUEUE_FILTER filter = { } ;
filter . DenyList . pIDList = disableMessageIDs ;
filter . DenyList . NumIDs = sizeof ( disableMessageIDs ) / sizeof ( disableMessageIDs [ 0 ] ) ;
pInfoQueue - > AddStorageFilterEntries ( & filter ) ;
}
}
2023-11-30 17:26:38 +00:00
targetAdapter - > QueryInterface ( IID_PPV_ARGS ( & m_DxgiAdapter ) ) ;
2022-02-21 17:15:56 +00:00
D3D12_COMMAND_QUEUE_DESC queueDesc ;
ZeroMemory ( & queueDesc , sizeof ( queueDesc ) ) ;
queueDesc . Flags = D3D12_COMMAND_QUEUE_FLAG_NONE ;
queueDesc . Type = D3D12_COMMAND_LIST_TYPE_DIRECT ;
queueDesc . NodeMask = 1 ;
hr = m_Device12 - > CreateCommandQueue ( & queueDesc , IID_PPV_ARGS ( & m_GraphicsQueue ) ) ;
HR_RETURN ( hr ) ;
m_GraphicsQueue - > SetName ( L " Graphics Queue " ) ;
2023-03-06 16:05:43 +00:00
if ( m_DeviceParams . enableComputeQueue )
2022-02-21 17:15:56 +00:00
{
queueDesc . Type = D3D12_COMMAND_LIST_TYPE_COMPUTE ;
hr = m_Device12 - > CreateCommandQueue ( & queueDesc , IID_PPV_ARGS ( & m_ComputeQueue ) ) ;
HR_RETURN ( hr ) ;
m_ComputeQueue - > SetName ( L " Compute Queue " ) ;
}
2023-03-06 16:05:43 +00:00
if ( m_DeviceParams . enableCopyQueue )
2022-02-21 17:15:56 +00:00
{
queueDesc . Type = D3D12_COMMAND_LIST_TYPE_COPY ;
hr = m_Device12 - > CreateCommandQueue ( & queueDesc , IID_PPV_ARGS ( & m_CopyQueue ) ) ;
HR_RETURN ( hr ) ;
m_CopyQueue - > SetName ( L " Copy Queue " ) ;
}
2023-03-06 16:05:43 +00:00
m_FullScreenDesc = { } ;
m_FullScreenDesc . RefreshRate . Numerator = m_DeviceParams . refreshRate ;
m_FullScreenDesc . RefreshRate . Denominator = 1 ;
m_FullScreenDesc . ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_PROGRESSIVE ;
m_FullScreenDesc . Scaling = DXGI_MODE_SCALING_UNSPECIFIED ;
m_FullScreenDesc . Windowed = ! m_DeviceParams . startFullscreen ;
2022-02-21 17:15:56 +00:00
RefCountPtr < IDXGISwapChain1 > pSwapChain1 ;
2023-03-06 16:05:43 +00:00
hr = pDxgiFactory - > CreateSwapChainForHwnd ( m_GraphicsQueue , ( HWND ) windowHandle , & m_SwapChainDesc , & m_FullScreenDesc , nullptr , & pSwapChain1 ) ;
2022-02-21 17:15:56 +00:00
HR_RETURN ( hr ) ;
hr = pSwapChain1 - > QueryInterface ( IID_PPV_ARGS ( & m_SwapChain ) ) ;
HR_RETURN ( hr ) ;
2024-04-02 15:17:47 +00:00
if ( r_dxMaxFrameLatency . GetInteger ( ) > 0 )
2023-05-23 12:48:56 +00:00
{
2024-04-02 15:17:47 +00:00
hr = m_SwapChain - > SetMaximumFrameLatency ( r_dxMaxFrameLatency . GetInteger ( ) ) ;
2023-05-23 12:48:56 +00:00
HR_RETURN ( hr ) ;
m_frameLatencyWaitableObject = m_SwapChain - > GetFrameLatencyWaitableObject ( ) ;
}
2022-02-21 17:15:56 +00:00
nvrhi : : d3d12 : : DeviceDesc deviceDesc ;
deviceDesc . errorCB = & DefaultMessageCallback : : GetInstance ( ) ;
deviceDesc . pDevice = m_Device12 ;
deviceDesc . pGraphicsCommandQueue = m_GraphicsQueue ;
deviceDesc . pComputeCommandQueue = m_ComputeQueue ;
deviceDesc . pCopyCommandQueue = m_CopyQueue ;
2023-03-06 16:05:43 +00:00
m_NvrhiDevice = nvrhi : : d3d12 : : createDevice ( deviceDesc ) ;
2022-02-21 17:15:56 +00:00
2023-03-06 16:05:43 +00:00
m_DeviceParams . enableNvrhiValidationLayer = r_useValidationLayers . GetInteger ( ) > 0 ;
if ( m_DeviceParams . enableNvrhiValidationLayer )
2022-02-21 17:15:56 +00:00
{
2023-03-06 16:05:43 +00:00
m_NvrhiDevice = nvrhi : : validation : : createValidationLayer ( m_NvrhiDevice ) ;
2022-02-21 17:15:56 +00:00
}
if ( ! CreateRenderTargets ( ) )
{
return false ;
}
2023-03-06 19:06:15 +00:00
m_FrameWaitQuery = m_NvrhiDevice - > createEventQuery ( ) ;
m_NvrhiDevice - > setEventQuery ( m_FrameWaitQuery , nvrhi : : CommandQueue : : Graphics ) ;
2023-02-28 23:02:45 +00:00
2023-03-07 10:22:46 +00:00
OPTICK_GPU_INIT_D3D12 ( m_Device12 , & m_GraphicsQueue , 1 ) ;
2022-02-21 17:15:56 +00:00
return true ;
}
void DeviceManager_DX12 : : DestroyDeviceAndSwapChain ( )
{
2023-04-08 19:30:04 +00:00
OPTICK_SHUTDOWN ( ) ;
2022-02-21 17:15:56 +00:00
m_RhiSwapChainBuffers . clear ( ) ;
2023-03-06 16:05:43 +00:00
m_RendererString . clear ( ) ;
2022-02-21 17:15:56 +00:00
ReleaseRenderTargets ( ) ;
2023-03-06 16:05:43 +00:00
m_NvrhiDevice = nullptr ;
2022-02-21 17:15:56 +00:00
2023-02-28 23:02:45 +00:00
m_FrameWaitQuery = nullptr ;
2023-05-27 05:40:40 +00:00
if ( m_frameLatencyWaitableObject )
{
CloseHandle ( m_frameLatencyWaitableObject ) ;
m_frameLatencyWaitableObject = NULL ;
}
2022-02-21 17:15:56 +00:00
if ( m_SwapChain )
{
m_SwapChain - > SetFullscreenState ( false , nullptr ) ;
}
m_SwapChainBuffers . clear ( ) ;
m_SwapChain = nullptr ;
m_GraphicsQueue = nullptr ;
m_ComputeQueue = nullptr ;
m_CopyQueue = nullptr ;
m_Device12 = nullptr ;
m_DxgiAdapter = nullptr ;
}
bool DeviceManager_DX12 : : CreateRenderTargets ( )
{
m_SwapChainBuffers . resize ( m_SwapChainDesc . BufferCount ) ;
m_RhiSwapChainBuffers . resize ( m_SwapChainDesc . BufferCount ) ;
for ( UINT n = 0 ; n < m_SwapChainDesc . BufferCount ; n + + )
{
const HRESULT hr = m_SwapChain - > GetBuffer ( n , IID_PPV_ARGS ( & m_SwapChainBuffers [ n ] ) ) ;
HR_RETURN ( hr ) ;
nvrhi : : TextureDesc textureDesc ;
2023-03-06 16:05:43 +00:00
textureDesc . width = m_DeviceParams . backBufferWidth ;
textureDesc . height = m_DeviceParams . backBufferHeight ;
textureDesc . sampleCount = m_DeviceParams . swapChainSampleCount ;
textureDesc . sampleQuality = m_DeviceParams . swapChainSampleQuality ;
textureDesc . format = m_DeviceParams . swapChainFormat ;
2022-02-21 17:15:56 +00:00
textureDesc . debugName = " SwapChainBuffer " ;
textureDesc . isRenderTarget = true ;
textureDesc . isUAV = false ;
textureDesc . initialState = nvrhi : : ResourceStates : : Present ;
textureDesc . keepInitialState = true ;
2023-03-06 16:05:43 +00:00
m_RhiSwapChainBuffers [ n ] = m_NvrhiDevice - > createHandleForNativeTexture ( nvrhi : : ObjectTypes : : D3D12_Resource , nvrhi : : Object ( m_SwapChainBuffers [ n ] ) , textureDesc ) ;
2022-02-21 17:15:56 +00:00
}
return true ;
}
void DeviceManager_DX12 : : ReleaseRenderTargets ( )
{
2023-03-06 16:05:43 +00:00
if ( ! m_NvrhiDevice )
2022-02-21 17:15:56 +00:00
{
return ;
}
// Make sure that all frames have finished rendering
2023-03-06 16:05:43 +00:00
m_NvrhiDevice - > waitForIdle ( ) ;
2022-02-21 17:15:56 +00:00
// Release all in-flight references to the render targets
2023-03-06 16:05:43 +00:00
m_NvrhiDevice - > runGarbageCollection ( ) ;
2022-02-21 17:15:56 +00:00
// Release the old buffers because ResizeBuffers requires that
m_RhiSwapChainBuffers . clear ( ) ;
m_SwapChainBuffers . clear ( ) ;
}
void DeviceManager_DX12 : : ResizeSwapChain ( )
{
ReleaseRenderTargets ( ) ;
2023-03-06 16:05:43 +00:00
if ( ! m_NvrhiDevice )
2022-02-21 17:15:56 +00:00
{
return ;
}
if ( ! m_SwapChain )
{
return ;
}
2023-03-06 16:05:43 +00:00
const HRESULT hr = m_SwapChain - > ResizeBuffers ( m_DeviceParams . swapChainBufferCount ,
m_DeviceParams . backBufferWidth ,
m_DeviceParams . backBufferHeight ,
2022-02-21 17:15:56 +00:00
m_SwapChainDesc . Format ,
m_SwapChainDesc . Flags ) ;
if ( FAILED ( hr ) )
{
common - > FatalError ( " ResizeBuffers failed " ) ;
}
bool ret = CreateRenderTargets ( ) ;
if ( ! ret )
{
common - > FatalError ( " CreateRenderTarget failed " ) ;
}
}
void DeviceManager_DX12 : : BeginFrame ( )
{
2023-03-07 10:22:46 +00:00
OPTICK_CATEGORY ( " DX12_BeginFrame " , Optick : : Category : : Wait ) ;
2023-11-30 17:26:38 +00:00
// SRS - get DXGI GPU memory usage for display in statistics overlay HUD
DXGI_QUERY_VIDEO_MEMORY_INFO memoryInfoLocal = { } , memoryInfoNonLocal = { } ;
m_DxgiAdapter - > QueryVideoMemoryInfo ( 0 , DXGI_MEMORY_SEGMENT_GROUP_LOCAL , & memoryInfoLocal ) ;
m_DxgiAdapter - > QueryVideoMemoryInfo ( 0 , DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL , & memoryInfoNonLocal ) ;
2024-03-07 20:55:00 +00:00
commonLocal . SetRendererGpuMemoryMB ( ( memoryInfoLocal . CurrentUsage + memoryInfoNonLocal . CurrentUsage ) / 1024 / 1024 ) ;
2022-02-21 17:15:56 +00:00
}
nvrhi : : ITexture * DeviceManager_DX12 : : GetCurrentBackBuffer ( )
{
return m_RhiSwapChainBuffers [ m_SwapChain - > GetCurrentBackBufferIndex ( ) ] ;
}
nvrhi : : ITexture * DeviceManager_DX12 : : GetBackBuffer ( uint32_t index )
{
if ( index < m_RhiSwapChainBuffers . size ( ) )
{
return m_RhiSwapChainBuffers [ index ] ;
}
return nullptr ;
}
uint32_t DeviceManager_DX12 : : GetCurrentBackBufferIndex ( )
{
return m_SwapChain - > GetCurrentBackBufferIndex ( ) ;
}
uint32_t DeviceManager_DX12 : : GetBackBufferCount ( )
{
return m_SwapChainDesc . BufferCount ;
}
2022-09-22 16:34:07 +00:00
void DeviceManager_DX12 : : EndFrame ( )
{
2024-02-28 16:59:41 +00:00
OPTICK_CATEGORY ( " DX12_EndFrame " , Optick : : Category : : Wait ) ;
2022-09-22 16:34:07 +00:00
}
2022-02-21 17:15:56 +00:00
void DeviceManager_DX12 : : Present ( )
{
2023-03-06 16:05:43 +00:00
if ( ! m_windowVisible )
2022-02-21 17:15:56 +00:00
{
return ;
}
UINT presentFlags = 0 ;
2022-03-23 19:22:05 +00:00
2022-11-18 04:42:06 +00:00
// SRS - DXGI docs say fullscreen must be disabled for unlocked fps/tear, but this does not seem to be true
2023-03-24 14:04:34 +00:00
if ( m_DeviceParams . vsyncEnabled = = 0 & & m_TearingSupported ) //&& !glConfig.isFullscreen )
2022-02-21 17:15:56 +00:00
{
presentFlags | = DXGI_PRESENT_ALLOW_TEARING ;
}
2023-05-23 12:24:40 +00:00
OPTICK_GPU_FLIP ( m_SwapChain . Get ( ) , idLib : : frameNumber - 1 ) ;
2023-03-24 17:15:00 +00:00
OPTICK_CATEGORY ( " DX12_Present " , Optick : : Category : : Wait ) ;
2024-03-07 20:55:00 +00:00
OPTICK_TAG ( " Frame " , idLib : : frameNumber - 1 ) ;
2023-03-07 10:22:46 +00:00
2023-03-06 16:05:43 +00:00
// SRS - Don't change m_DeviceParams.vsyncEnabled here, simply test for vsync mode 2 to set DXGI SyncInterval
2023-03-24 14:04:34 +00:00
m_SwapChain - > Present ( m_DeviceParams . vsyncEnabled = = 2 ? 1 : 0 , presentFlags ) ;
2022-02-21 17:15:56 +00:00
2023-05-23 12:48:56 +00:00
if ( m_frameLatencyWaitableObject )
{
2023-05-27 05:40:40 +00:00
OPTICK_CATEGORY ( " DX12_Sync1 " , Optick : : Category : : Wait ) ;
2023-05-23 12:48:56 +00:00
// SRS - When m_frameLatencyWaitableObject active, sync first on earlier present
DWORD result = WaitForSingleObjectEx ( m_frameLatencyWaitableObject , INFINITE , true ) ;
2023-05-27 05:40:40 +00:00
assert ( result = = WAIT_OBJECT_0 ) ;
2023-05-23 12:48:56 +00:00
}
2023-03-24 17:15:00 +00:00
if constexpr ( NUM_FRAME_DATA > 2 )
{
OPTICK_CATEGORY ( " DX12_Sync3 " , Optick : : Category : : Wait ) ;
// SRS - For triple buffering, sync on previous frame's command queue completion
m_NvrhiDevice - > waitEventQuery ( m_FrameWaitQuery ) ;
}
2023-03-30 17:30:33 +00:00
2023-03-24 17:15:00 +00:00
m_NvrhiDevice - > resetEventQuery ( m_FrameWaitQuery ) ;
m_NvrhiDevice - > setEventQuery ( m_FrameWaitQuery , nvrhi : : CommandQueue : : Graphics ) ;
if constexpr ( NUM_FRAME_DATA < 3 )
{
OPTICK_CATEGORY ( " DX12_Sync2 " , Optick : : Category : : Wait ) ;
// SRS - For double buffering, sync on current frame's command queue completion
m_NvrhiDevice - > waitEventQuery ( m_FrameWaitQuery ) ;
}
2022-02-21 17:15:56 +00:00
}
DeviceManager * DeviceManager : : CreateD3D12 ( )
{
return new DeviceManager_DX12 ( ) ;
2022-11-10 17:19:54 +00:00
}