Files
Brandyn / Techy fcc1b09210 init
2026-04-04 15:40:51 -05:00

2332 lines
81 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
/*=============================================================================
WindowsD3D12Device.cpp: Windows D3D device RHI implementation.
=============================================================================*/
#include "D3D12RHIPrivate.h"
#include "D3D12AmdExtensions.h"
#include "D3D12IntelExtensions.h"
#include "D3D12NvidiaExtensions.h"
#include "WindowsD3D12Adapter.h"
#include "Modules/ModuleManager.h"
#include "Windows/AllowWindowsPlatformTypes.h"
#include "Windows/WindowsPlatformCrashContext.h"
#include "HAL/FileManager.h"
#include "ProfilingDebugging/AssetMetadataTrace.h"
#include <delayimp.h>
#include "Windows/HideWindowsPlatformTypes.h"
#include "HardwareInfo.h"
#include "IHeadMountedDisplayModule.h"
#include "GenericPlatform/GenericPlatformDriver.h" // FGPUDriverInfo
#include "RHIValidation.h"
#include "RHIUtilities.h"
#include "ShaderCompiler.h"
#include "Misc/EngineVersion.h"
#pragma comment(lib, "d3d12.lib")
IMPLEMENT_MODULE(FD3D12DynamicRHIModule, D3D12RHI);
extern bool D3D12RHI_ShouldCreateWithWarp();
extern bool D3D12RHI_AllowSoftwareFallback();
extern bool D3D12RHI_ShouldAllowAsyncResourceCreation();
extern bool D3D12RHI_ShouldForceCompatibility();
#if INTEL_EXTENSIONS
bool GDX12INTCAtomicUInt64Emulation = false;
#endif
FD3D12DynamicRHI* GD3D12RHI = nullptr;
int32 GMinimumWindowsBuildVersionForRayTracing = 0;
static FAutoConsoleVariableRef CVarMinBuildVersionForRayTracing(
TEXT("r.D3D12.DXR.MinimumWindowsBuildVersion"),
GMinimumWindowsBuildVersionForRayTracing,
TEXT("Sets the minimum Windows build version required to enable ray tracing."),
ECVF_ReadOnly | ECVF_RenderThreadSafe
);
int32 GMinimumDriverVersionForRayTracingNVIDIA = 0;
static FAutoConsoleVariableRef CVarMinDriverVersionForRayTracingNVIDIA(
TEXT("r.D3D12.DXR.MinimumDriverVersionNVIDIA"),
GMinimumDriverVersionForRayTracingNVIDIA,
TEXT("Sets the minimum driver version required to enable ray tracing on NVIDIA GPUs."),
ECVF_ReadOnly | ECVF_RenderThreadSafe
);
// Use AGS_MAKE_VERSION() macro to define the version.
// i.e. AGS_MAKE_VERSION(major, minor, patch) ((major << 22) | (minor << 12) | patch)
int32 GMinimumDriverVersionForRayTracingAMD = 0;
static FAutoConsoleVariableRef CVarMinDriverVersionForRayTracingAMD(
TEXT("r.D3D12.DXR.MinimumDriverVersionAMD"),
GMinimumDriverVersionForRayTracingAMD,
TEXT("Sets the minimum driver version required to enable ray tracing on AMD GPUs."),
ECVF_ReadOnly | ECVF_RenderThreadSafe
);
#if !UE_BUILD_SHIPPING
static TAutoConsoleVariable<int32> CVarExperimentalShaderModels(
TEXT("r.D3D12.ExperimentalShaderModels"),
0,
TEXT("Controls whether D3D12 experimental shader models should be allowed. Not available in shipping builds. (default = 0)."),
ECVF_ReadOnly
);
#endif // !UE_BUILD_SHIPPING
// See https://microsoft.github.io/DirectX-Specs/d3d/BackgroundProcessing.html.
int32 GDevDisableD3DRuntimeBackgroundThreads = 0;
static FAutoConsoleVariableRef CVarDevDisableD3DRuntimeBackgroundThreads(
TEXT("r.D3D12.DevDisableD3DRuntimeBackgroundThreads"),
GDevDisableD3DRuntimeBackgroundThreads,
TEXT("If > 0, disables the background threads created by the D3D runtime for background shader optimization. Only available when Windows developer mode is enabled. (default = 0)."),
ECVF_ReadOnly
);
#if D3D12RHI_SUPPORTS_WIN_PIX
int32 GAutoAttachPIX = 0;
static FAutoConsoleVariableRef CVarAutoAttachPIX(
TEXT("r.D3D12.AutoAttachPIX"),
GAutoAttachPIX,
TEXT("Automatically attach PIX on startup"),
ECVF_ReadOnly | ECVF_RenderThreadSafe
);
#endif // D3D12RHI_SUPPORTS_WIN_PIX
using namespace D3D12RHI;
static bool bIsQuadBufferStereoEnabled = false;
/** This function is used as a SEH filter to catch only delay load exceptions. */
static bool IsDelayLoadException(PEXCEPTION_POINTERS ExceptionPointers)
{
switch (ExceptionPointers->ExceptionRecord->ExceptionCode)
{
case VcppException(ERROR_SEVERITY_ERROR, ERROR_MOD_NOT_FOUND):
case VcppException(ERROR_SEVERITY_ERROR, ERROR_PROC_NOT_FOUND):
return EXCEPTION_EXECUTE_HANDLER;
default:
return EXCEPTION_CONTINUE_SEARCH;
}
}
/**
* Since CreateDXGIFactory is a delay loaded import from the DXGI DLL, if the user
* doesn't have Vista/DX10, calling CreateDXGIFactory will throw an exception.
* We use SEH to detect that case and fail gracefully.
*/
static void SafeCreateDXGIFactory(IDXGIFactory4** DXGIFactory)
{
#if !defined(D3D12_CUSTOM_VIEWPORT_CONSTRUCTOR) || !D3D12_CUSTOM_VIEWPORT_CONSTRUCTOR
__try
{
bIsQuadBufferStereoEnabled = FParse::Param(FCommandLine::Get(), TEXT("quad_buffer_stereo"));
CreateDXGIFactory(__uuidof(IDXGIFactory4), (void**)DXGIFactory);
}
__except (IsDelayLoadException(GetExceptionInformation()))
{
// We suppress warning C6322: Empty _except block. Appropriate checks are made upon returning.
CA_SUPPRESS(6322);
}
#endif //!D3D12_CUSTOM_VIEWPORT_CONSTRUCTOR
}
/**
* Returns the minimum D3D feature level required to create based on
* command line parameters.
*/
static D3D_FEATURE_LEVEL GetRequiredD3DFeatureLevel()
{
return D3D_FEATURE_LEVEL_11_0;
}
static D3D_FEATURE_LEVEL FindHighestFeatureLevel(ID3D12Device* Device, D3D_FEATURE_LEVEL MinFeatureLevel)
{
const D3D_FEATURE_LEVEL FeatureLevels[] =
{
// Add new feature levels that the app supports here.
#if D3D12_CORE_ENABLED
D3D_FEATURE_LEVEL_12_2,
#endif
D3D_FEATURE_LEVEL_12_1,
D3D_FEATURE_LEVEL_12_0,
D3D_FEATURE_LEVEL_11_1,
D3D_FEATURE_LEVEL_11_0
};
// Determine the max feature level supported by the driver and hardware.
D3D12_FEATURE_DATA_FEATURE_LEVELS FeatureLevelCaps{};
FeatureLevelCaps.pFeatureLevelsRequested = FeatureLevels;
FeatureLevelCaps.NumFeatureLevels = UE_ARRAY_COUNT(FeatureLevels);
if (SUCCEEDED(Device->CheckFeatureSupport(D3D12_FEATURE_FEATURE_LEVELS, &FeatureLevelCaps, sizeof(FeatureLevelCaps))))
{
return FeatureLevelCaps.MaxSupportedFeatureLevel;
}
return MinFeatureLevel;
}
static D3D_SHADER_MODEL FindHighestShaderModel(ID3D12Device* Device)
{
// Because we can't guarantee older Windows versions will know about newer shader models, we need to check them all
// in descending order and return the first result that succeeds.
const D3D_SHADER_MODEL ShaderModelsToCheck[] =
{
#if D3D12_CORE_ENABLED
D3D_SHADER_MODEL_6_7,
D3D_SHADER_MODEL_6_6,
#endif
D3D_SHADER_MODEL_6_5,
D3D_SHADER_MODEL_6_4,
D3D_SHADER_MODEL_6_3,
D3D_SHADER_MODEL_6_2,
D3D_SHADER_MODEL_6_1,
D3D_SHADER_MODEL_6_0,
};
D3D12_FEATURE_DATA_SHADER_MODEL FeatureShaderModel{};
for (const D3D_SHADER_MODEL ShaderModelToCheck : ShaderModelsToCheck)
{
FeatureShaderModel.HighestShaderModel = ShaderModelToCheck;
if (SUCCEEDED(Device->CheckFeatureSupport(D3D12_FEATURE_SHADER_MODEL, &FeatureShaderModel, sizeof(FeatureShaderModel))))
{
return FeatureShaderModel.HighestShaderModel;
}
}
// Last ditch effort, the minimum requirement for DX12 is 5.1
return D3D_SHADER_MODEL_5_1;
}
#if INTEL_EXTENSIONS
static INTCExtensionAppInfo1 GetIntelApplicationInfo()
{
// CVar set to disable workload registration
static TConsoleVariableData<int32>* CVarDisableEngineAndAppRegistration = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.DisableEngineAndAppRegistration"));
INTCExtensionAppInfo1 AppInfo{};
if (!(CVarDisableEngineAndAppRegistration && CVarDisableEngineAndAppRegistration->GetValueOnAnyThread() != 0))
{
AppInfo.pApplicationName = FApp::HasProjectName() ? FApp::GetProjectName() : TEXT("");
//AppInfo.ApplicationVersion = FApp::GetBuildVersion(); // Currently no support for version
AppInfo.pEngineName = TEXT("Unreal Engine");
AppInfo.EngineVersion.major = FEngineVersion::Current().GetMajor();
AppInfo.EngineVersion.minor = FEngineVersion::Current().GetMinor();
AppInfo.EngineVersion.patch = FEngineVersion::Current().GetPatch();
}
return AppInfo;
}
INTCExtensionContext* CreateIntelExtensionsContext(ID3D12Device* Device, INTCExtensionInfo& INTCExtensionInfo, uint32 DeviceId)
{
if (FAILED(INTC_LoadExtensionsLibrary(false, (uint32)EGpuVendorId::Intel, DeviceId)))
{
UE_LOG(LogD3D12RHI, Log, TEXT("Failed to load Intel Extensions Library!"));
return nullptr;
}
INTCExtensionVersion* SupportedExtensionsVersions = nullptr;
uint32_t SupportedExtensionsVersionCount = 0;
if( SUCCEEDED( INTC_D3D12_GetSupportedVersions( Device, nullptr, &SupportedExtensionsVersionCount ) ) )
{
SupportedExtensionsVersions = new INTCExtensionVersion[ SupportedExtensionsVersionCount ]{};
if( SUCCEEDED( INTC_D3D12_GetSupportedVersions( Device, SupportedExtensionsVersions, &SupportedExtensionsVersionCount ) ) && SupportedExtensionsVersions != nullptr )
{
// We have the list of supported versions, now find the lowest common version that supports the required features
for( uint32_t i = 0; i < SupportedExtensionsVersionCount; i++ )
{
CA_SUPPRESS( 6385 );
if( MatchIntelExtensionVersion( SupportedExtensionsVersions[ i ] ) )
{
INTCExtensionInfo.RequestedExtensionVersion = SupportedExtensionsVersions[ i ];
}
}
}
}
// No need for this table anymore
if (SupportedExtensionsVersions != nullptr)
{
delete[] SupportedExtensionsVersions;
}
// No version matched means we cannot use the Intel Extensions
if (INTCExtensionInfo.RequestedExtensionVersion.HWFeatureLevel == 0 &&
INTCExtensionInfo.RequestedExtensionVersion.APIVersion == 0 )
{
UE_LOG(LogD3D12RHI, Log, TEXT("Intel Extensions Framework not supported by driver. Please check if a driver update is available."));
return nullptr;
}
INTCExtensionContext* IntelExtensionContext = nullptr;
// Fill in registration information for this workload (App name and Engine name)
INTCExtensionAppInfo1 AppInfo = GetIntelApplicationInfo();
const HRESULT hr = INTC_D3D12_CreateDeviceExtensionContext1(Device, &IntelExtensionContext, &INTCExtensionInfo, &AppInfo);
if (SUCCEEDED(hr))
{
UE_LOG( LogD3D12RHI, Log, TEXT( "Intel Extensions Framework enabled (version: %u.%u.%u)." ),
INTCExtensionInfo.RequestedExtensionVersion.HWFeatureLevel,
INTCExtensionInfo.RequestedExtensionVersion.APIVersion,
INTCExtensionInfo.RequestedExtensionVersion.Revision );
}
else
{
if (hr == E_NOINTERFACE)
{
UE_LOG(LogD3D12RHI, Log, TEXT("Intel Extensions Framework not supported by driver. Please check if a driver update is available."));
}
else if (hr == E_INVALIDARG)
{
UE_LOG(LogD3D12RHI, Log, TEXT("Intel Extensions Framework passed invalid creation arguments."));
}
else
{
UE_LOG(LogD3D12RHI, Log, TEXT("Intel Extensions Framework failed to initialize. Error code: 0x%08x. Please check if a driver update is available."), hr);
}
if (IntelExtensionContext)
{
DestroyIntelExtensionsContext(IntelExtensionContext);
IntelExtensionContext = nullptr;
}
}
return IntelExtensionContext;
}
void DestroyIntelExtensionsContext(INTCExtensionContext* IntelExtensionContext)
{
if (IntelExtensionContext)
{
const HRESULT hr = INTC_DestroyDeviceExtensionContext(&IntelExtensionContext);
if (SUCCEEDED(hr))
{
UE_LOG(LogD3D12RHI, Log, TEXT("Intel Extensions Framework unloaded"));
}
else
{
UE_LOG(LogD3D12RHI, Log, TEXT("Intel Extensions Framework error when unloading: 0x%08x"), hr);
}
}
}
bool EnableIntelAtomic64Support(INTCExtensionContext* IntelExtensionContext, INTCExtensionInfo& INTCExtensionInfo)
{
if (IntelExtensionContext)
{
INTC_D3D12_FEATURE_DATA_D3D12_OPTIONS1 INTCFeatureSupportData;;
const HRESULT hrCheck = INTC_D3D12_CheckFeatureSupport(IntelExtensionContext, INTC_D3D12_FEATURE_D3D12_OPTIONS1, &INTCFeatureSupportData, sizeof(INTCFeatureSupportData));
if (SUCCEEDED(hrCheck))
{
if (INTCFeatureSupportData.EmulatedTyped64bitAtomics)
{
INTC_D3D12_FEATURE INTCFeature;
INTCFeature.EmulatedTyped64bitAtomics = true;
const HRESULT hrSet = INTC_D3D12_SetFeatureSupport(IntelExtensionContext, &INTCFeature);
if (SUCCEEDED(hrSet))
{
GDX12INTCAtomicUInt64Emulation = true;
UE_LOG(LogD3D12RHI, Log, TEXT("Intel Extensions 64-bit Typed Atomics emulation enabled."));
}
else
{
UE_LOG(LogD3D12RHI, Log, TEXT("Failed to enable Intel Extensions 64-bit Typed Atomics emulation, error code: 0x%08x."), hrSet);
}
}
else
{
UE_LOG(LogD3D12RHI, Log, TEXT("Intel Extensions 64-bit Typed Atomics emulation not needed."));
}
}
else
{
UE_LOG(LogD3D12RHI, Log, TEXT("Failed to check for Intel Extensions 64-bit Typed Atomics emulation, error code: 0x%08x."), hrCheck);
}
}
return GDX12INTCAtomicUInt64Emulation;
}
void EnableIntelAppDiscovery(uint32 DeviceId)
{
if (FAILED(INTC_LoadExtensionsLibrary(false, (uint32)EGpuVendorId::Intel, DeviceId)))
{
UE_LOG(LogD3D12RHI, Log, TEXT("Failed to load Intel Extensions Library (App Discovery)"));
return;
}
// Fill in registration information for this workload (App name and Engine name)
INTCExtensionAppInfo1 AppInfo = GetIntelApplicationInfo();
// Intel Application Discovery - registering UE5 application info in the driver
INTC_D3D12_SetApplicationInfo(&AppInfo);
}
#endif // INTEL_EXTENSIONS
static bool CheckDeviceForEmulatedAtomic64Support(IDXGIAdapter* Adapter, ID3D12Device* Device)
{
bool bEmulatedAtomic64Support = false;
#if INTEL_EXTENSIONS
DXGI_ADAPTER_DESC AdapterDesc{};
Adapter->GetDesc(&AdapterDesc);
if ((RHIConvertToGpuVendorId(AdapterDesc.VendorId) == EGpuVendorId::Intel) && UE::RHICore::AllowVendorDevice())
{
INTCExtensionInfo INTCExtensionInfo{};
if (INTCExtensionContext* IntelExtensionContext = CreateIntelExtensionsContext(Device, INTCExtensionInfo, AdapterDesc.DeviceId))
{
INTC_D3D12_FEATURE_DATA_D3D12_OPTIONS1 INTCFeatureSupportData;;
const HRESULT hr = INTC_D3D12_CheckFeatureSupport(IntelExtensionContext, INTC_D3D12_FEATURE_D3D12_OPTIONS1, &INTCFeatureSupportData, sizeof(INTCFeatureSupportData));
if (SUCCEEDED(hr))
{
bEmulatedAtomic64Support = INTCFeatureSupportData.EmulatedTyped64bitAtomics;
}
else
{
UE_LOG(LogD3D12RHI, Log, TEXT("Failed to check for Intel Extensions 64-bit Typed Atomics emulation."));
}
DestroyIntelExtensionsContext(IntelExtensionContext);
}
}
#endif
return bEmulatedAtomic64Support;
}
inline bool ShouldCheckBindlessSupport(EShaderPlatform ShaderPlatform)
{
// Note: only checking against All allows the RayTracingOnly configuration to not raise the requirements for projects that aren't using RayTracing.
return RHIGetRuntimeBindlessConfiguration(ShaderPlatform) > ERHIBindlessConfiguration::RayTracing;
}
inline ERHIFeatureLevel::Type FindMaxRHIFeatureLevel(D3D_FEATURE_LEVEL InMaxFeatureLevel, D3D_SHADER_MODEL InMaxShaderModel, D3D12_RESOURCE_BINDING_TIER ResourceBindingTier, bool bSupportsWaveOps, bool bSupportsAtomic64)
{
ERHIFeatureLevel::Type MaxRHIFeatureLevel = ERHIFeatureLevel::Num;
if (InMaxFeatureLevel >= D3D_FEATURE_LEVEL_12_0 && InMaxShaderModel >= D3D_SHADER_MODEL_6_6)
{
bool bHighEnoughBindingTier = true;
if (ShouldCheckBindlessSupport(SP_PCD3D_SM6))
{
bHighEnoughBindingTier = ResourceBindingTier >= D3D12_RESOURCE_BINDING_TIER_3;
}
else
{
bHighEnoughBindingTier = ResourceBindingTier >= D3D12_RESOURCE_BINDING_TIER_2;
}
if (bSupportsWaveOps && bHighEnoughBindingTier && bSupportsAtomic64)
{
if (FParse::Param(FCommandLine::Get(), TEXT("ForceDisableSM6")))
{
UE_LOG(LogD3D12RHI, Log, TEXT("ERHIFeatureLevel::SM6 disabled via -ForceDisableSM6"));
}
else
{
MaxRHIFeatureLevel = ERHIFeatureLevel::SM6;
}
}
}
if (MaxRHIFeatureLevel == ERHIFeatureLevel::Num && InMaxFeatureLevel >= D3D_FEATURE_LEVEL_11_0)
{
MaxRHIFeatureLevel = ERHIFeatureLevel::SM5;
}
return MaxRHIFeatureLevel;
}
inline void GetResourceTiers(ID3D12Device* Device, D3D12_RESOURCE_BINDING_TIER& OutResourceBindingTier, D3D12_RESOURCE_HEAP_TIER& OutResourceHeapTier)
{
D3D12_FEATURE_DATA_D3D12_OPTIONS D3D12Caps{};
Device->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS, &D3D12Caps, sizeof(D3D12Caps));
OutResourceBindingTier = D3D12Caps.ResourceBindingTier;
OutResourceHeapTier = D3D12Caps.ResourceHeapTier;
}
inline bool GetSupportsWaveOps(ID3D12Device* Device)
{
D3D12_FEATURE_DATA_D3D12_OPTIONS1 D3D12Caps1{};
Device->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS1, &D3D12Caps1, sizeof(D3D12Caps1));
return D3D12Caps1.WaveOps;
}
inline bool GetSupportsAtomic64(IDXGIAdapter* Adapter, ID3D12Device* Device)
{
D3D12_FEATURE_DATA_D3D12_OPTIONS9 D3D12Caps9{};
Device->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS9, &D3D12Caps9, sizeof(D3D12Caps9));
return (D3D12Caps9.AtomicInt64OnTypedResourceSupported || CheckDeviceForEmulatedAtomic64Support(Adapter, Device));
}
inline bool IsDeviceUMA(ID3D12Device* device)
{
D3D12_FEATURE_DATA_ARCHITECTURE data = { 0 };
if (SUCCEEDED(device->CheckFeatureSupport(D3D12_FEATURE_ARCHITECTURE, &data, sizeof(D3D12_FEATURE_DATA_ARCHITECTURE))))
{
return (bool)(data.UMA);
}
return false;
}
/**
* Attempts to create a D3D12 device for the adapter using at minimum MinFeatureLevel.
* If creation is successful, true is returned and the max supported feature level is set in OutMaxFeatureLevel.
*/
static bool SafeTestD3D12CreateDevice(IDXGIAdapter* Adapter, D3D_FEATURE_LEVEL MinFeatureLevel, FD3D12DeviceBasicInfo& OutInfo)
{
__try
{
ID3D12Device* Device = nullptr;
const HRESULT D3D12CreateDeviceResult = D3D12CreateDevice(Adapter, MinFeatureLevel, IID_PPV_ARGS(&Device));
if (SUCCEEDED(D3D12CreateDeviceResult))
{
OutInfo.MaxFeatureLevel = FindHighestFeatureLevel(Device, MinFeatureLevel);
OutInfo.MaxShaderModel = FindHighestShaderModel(Device);
GetResourceTiers(Device, OutInfo.ResourceBindingTier, OutInfo.ResourceHeapTier);
OutInfo.NumDeviceNodes = Device->GetNodeCount();
OutInfo.bSupportsWaveOps = GetSupportsWaveOps(Device);
OutInfo.bSupportsAtomic64 = GetSupportsAtomic64(Adapter, Device);
OutInfo.MaxRHIFeatureLevel = FindMaxRHIFeatureLevel(OutInfo.MaxFeatureLevel, OutInfo.MaxShaderModel, OutInfo.ResourceBindingTier, OutInfo.bSupportsWaveOps, OutInfo.bSupportsAtomic64);
OutInfo.bUMA = IsDeviceUMA(Device);
Device->Release();
return true;
}
else
{
UE_LOG(LogD3D12RHI, Log, TEXT("D3D12CreateDevice failed with code 0x%08X"), static_cast<int32>(D3D12CreateDeviceResult));
}
}
__except (IsDelayLoadException(GetExceptionInformation()))
{
// We suppress warning C6322: Empty _except block. Appropriate checks are made upon returning.
CA_SUPPRESS(6322);
}
return false;
}
static bool SupportsDepthBoundsTest(FD3D12DynamicRHI* D3DRHI)
{
// Determines if the primary adapter supports depth bounds test
check(D3DRHI && D3DRHI->GetNumAdapters() >= 1);
return D3DRHI->GetAdapter().IsDepthBoundsTestSupported();
}
bool FD3D12DynamicRHI::SetupDisplayHDRMetaData()
{
// Determines if any displays support HDR
check(GetNumAdapters() >= 1);
TArray<TRefCountPtr<IDXGIAdapter> > DXGIAdapters;
bool bStaleAdapters = false;
const int32 NumAdapters = GetNumAdapters();
for (int32 AdapterIndex = 0; AdapterIndex < NumAdapters; ++AdapterIndex)
{
FD3D12Adapter& CurrentAdapter = GetAdapter(AdapterIndex);
DXGIAdapters.Add(GetAdapter(AdapterIndex).GetAdapter());
if (CurrentAdapter.GetDXGIFactory2() != nullptr && !CurrentAdapter.GetDXGIFactory2()->IsCurrent())
{
bStaleAdapters = true;
}
}
#if PLATFORM_WINDOWS
// if we found that the list of adapters is stale (changed windows HDR setting), try to update it with the new list
if (bStaleAdapters)
{
if (!DXGIFactoryForDisplayList.IsValid() || !DXGIFactoryForDisplayList->IsCurrent())
{
FD3D12Adapter::CreateDXGIFactory(DXGIFactoryForDisplayList, false, GetAdapter(0).GetDxgiDllHandle());
}
if (DXGIFactoryForDisplayList.IsValid() && DXGIFactoryForDisplayList->IsCurrent())
{
DXGIAdapters.Empty();
TRefCountPtr<IDXGIAdapter> TempAdapter;
for (uint32 AdapterIndex = 0; DXGIFactoryForDisplayList->EnumAdapters(AdapterIndex, TempAdapter.GetInitReference()) != DXGI_ERROR_NOT_FOUND; ++AdapterIndex)
{
DXGIAdapters.Add(TempAdapter);
}
}
}
#endif
DisplayList.Empty();
bool bSupportsHDROutput = false;
const int32 NumDXGIAdapters = DXGIAdapters.Num();
for (int32 AdapterIndex = 0; AdapterIndex < NumDXGIAdapters; ++AdapterIndex)
{
IDXGIAdapter* DXGIAdapter = DXGIAdapters[AdapterIndex];
for (uint32 DisplayIndex = 0; true; ++DisplayIndex)
{
TRefCountPtr<IDXGIOutput> DXGIOutput;
if (S_OK != DXGIAdapter->EnumOutputs(DisplayIndex, DXGIOutput.GetInitReference()))
{
break;
}
TRefCountPtr<IDXGIOutput6> Output6;
if (SUCCEEDED(DXGIOutput->QueryInterface(IID_PPV_ARGS(Output6.GetInitReference()))))
{
DXGI_OUTPUT_DESC1 OutputDesc;
VERIFYD3D12RESULT(Output6->GetDesc1(&OutputDesc));
// Check for HDR support on the display.
const bool bDisplaySupportsHDROutput = (OutputDesc.ColorSpace == DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020);
if (bDisplaySupportsHDROutput)
{
UE_LOG(LogD3D12RHI, Log, TEXT("HDR output is supported on adapter %i, display %u:"), AdapterIndex, DisplayIndex);
UE_LOG(LogD3D12RHI, Log, TEXT("\t\tMinLuminance = %f"), OutputDesc.MinLuminance);
UE_LOG(LogD3D12RHI, Log, TEXT("\t\tMaxLuminance = %f"), OutputDesc.MaxLuminance);
UE_LOG(LogD3D12RHI, Log, TEXT("\t\tMaxFullFrameLuminance = %f"), OutputDesc.MaxFullFrameLuminance);
bSupportsHDROutput = true;
}
FDisplayInformation DisplayInformation{};
DisplayInformation.bHDRSupported = bDisplaySupportsHDROutput;
const RECT& DisplayCoords = OutputDesc.DesktopCoordinates;
DisplayInformation.DesktopCoordinates = FIntRect(DisplayCoords.left, DisplayCoords.top, DisplayCoords.right, DisplayCoords.bottom);
DisplayList.Add(DisplayInformation);
}
}
}
return bSupportsHDROutput;
}
#if PLATFORM_WINDOWS
extern void HDRSettingChangedSinkCallback();
void FD3D12DynamicRHI::RHIHandleDisplayChange()
{
RHIBlockUntilGPUIdle();
GRHISupportsHDROutput = SetupDisplayHDRMetaData();
// make sure CVars are being updated properly
HDRSettingChangedSinkCallback();
}
#endif
static bool IsAdapterBlocked(const FD3D12Adapter* InAdapter)
{
#if !UE_BUILD_SHIPPING
if (InAdapter)
{
FString BlockedIHVString;
if (GConfig->GetString(TEXT("SystemSettings"), TEXT("RHI.BlockIHVD3D12"), BlockedIHVString, GEngineIni))
{
TArray<FString> BlockedIHVs;
BlockedIHVString.ParseIntoArray(BlockedIHVs, TEXT(","));
const TCHAR* VendorId = RHIVendorIdToString(EGpuVendorId(InAdapter->GetD3DAdapterDesc().VendorId));
for (const FString& BlockedVendor : BlockedIHVs)
{
if (BlockedVendor.Equals(VendorId, ESearchCase::IgnoreCase))
{
return true;
}
}
}
}
#endif
return false;
}
inline ERHIFeatureLevel::Type GetAdapterMaxFeatureLevel(const FD3D12Adapter* InAdapter)
{
if (InAdapter)
{
if (const FD3D12AdapterDesc& Desc = InAdapter->GetDesc(); Desc.IsValid())
{
return Desc.MaxRHIFeatureLevel;
}
}
return ERHIFeatureLevel::Num;
}
static bool IsAdapterSupported(const FD3D12Adapter* InAdapter, ERHIFeatureLevel::Type InRequestedFeatureLevel)
{
const ERHIFeatureLevel::Type AdapterMaxFeatureLevel = GetAdapterMaxFeatureLevel(InAdapter);
return AdapterMaxFeatureLevel != ERHIFeatureLevel::Num && AdapterMaxFeatureLevel >= InRequestedFeatureLevel;
}
#if D3D12_CORE_ENABLED
static bool CheckIfAgilitySDKLoaded()
{
const TCHAR* AgilitySDKDllName = TEXT("D3D12Core.dll");
HMODULE AgilitySDKDllHandle = ::GetModuleHandle(AgilitySDKDllName);
return AgilitySDKDllHandle != NULL;
}
#endif
bool FD3D12DynamicRHIModule::IsSupported(ERHIFeatureLevel::Type RequestedFeatureLevel)
{
// Windows version 15063 is Windows 1703 aka "Windows Creator Update"
// This is the first version that supports ID3D12Device2 which is our minimum runtime device version.
if (!FPlatformMisc::VerifyWindowsVersion(10, 0, 15063))
{
UE_LOG(LogD3D12RHI, Warning, TEXT("Missing full support for Direct3D 12. Update to Windows 1703 or newer for D3D12 support."));
return false;
}
// If not computed yet
if (ChosenAdapters.Num() == 0)
{
FindAdapter();
}
if (ChosenAdapters.Num() == 0)
{
UE_LOG(LogD3D12RHI, Log, TEXT("No adapters were found."));
return false;
}
const FD3D12Adapter* Adapter = ChosenAdapters[0].Get();
if (!Adapter || !Adapter->GetDesc().IsValid())
{
UE_LOG(LogD3D12RHI, Log, TEXT("Adapter was not found"));
return false;
}
if (IsAdapterBlocked(Adapter))
{
UE_LOG(LogD3D12RHI, Log, TEXT("Adapter was blocked by RHI.BlockIHVD3D12"));
return false;
}
if (!IsAdapterSupported(Adapter, RequestedFeatureLevel))
{
auto GetFeatureLevelNameInline = [](ERHIFeatureLevel::Type InFeatureLevel)
{
FString FeatureLevelName;
::GetFeatureLevelName(InFeatureLevel, FeatureLevelName);
return FeatureLevelName;
};
const FString SupportedFeatureLevelName = GetFeatureLevelNameInline(GetAdapterMaxFeatureLevel(Adapter));
const FString RequestedFeatureLevelName = GetFeatureLevelNameInline(RequestedFeatureLevel);
UE_LOG(LogD3D12RHI, Log,
TEXT("Adapter only supports up to Feature Level '%s', requested Feature Level was '%s'"),
*SupportedFeatureLevelName,
*RequestedFeatureLevelName
);
return false;
}
return true;
}
namespace D3D12RHI
{
const TCHAR* GetFeatureLevelString(D3D_FEATURE_LEVEL FeatureLevel)
{
switch (FeatureLevel)
{
case D3D_FEATURE_LEVEL_9_1: return TEXT("9_1");
case D3D_FEATURE_LEVEL_9_2: return TEXT("9_2");
case D3D_FEATURE_LEVEL_9_3: return TEXT("9_3");
case D3D_FEATURE_LEVEL_10_0: return TEXT("10_0");
case D3D_FEATURE_LEVEL_10_1: return TEXT("10_1");
case D3D_FEATURE_LEVEL_11_0: return TEXT("11_0");
case D3D_FEATURE_LEVEL_11_1: return TEXT("11_1");
case D3D_FEATURE_LEVEL_12_0: return TEXT("12_0");
case D3D_FEATURE_LEVEL_12_1: return TEXT("12_1");
#if D3D12_CORE_ENABLED
case D3D_FEATURE_LEVEL_12_2: return TEXT("12_2");
#endif
}
return TEXT("X_X");
}
}
static uint32 CountAdapterOutputs(TRefCountPtr<IDXGIAdapter>& Adapter)
{
uint32 OutputCount = 0;
for (;;)
{
TRefCountPtr<IDXGIOutput> Output;
HRESULT hr = Adapter->EnumOutputs(OutputCount, Output.GetInitReference());
if (FAILED(hr))
{
break;
}
++OutputCount;
}
return OutputCount;
}
void FD3D12DynamicRHIModule::FindAdapter()
{
// Once we've chosen one we don't need to do it again.
check(ChosenAdapters.Num() == 0);
#if !UE_BUILD_SHIPPING
if (CVarExperimentalShaderModels.GetValueOnAnyThread() == 1)
{
// Experimental features must be enabled before doing anything else with D3D.
UUID ExperimentalFeatures[] =
{
D3D12ExperimentalShaderModels
};
HRESULT hr = D3D12EnableExperimentalFeatures(UE_ARRAY_COUNT(ExperimentalFeatures), ExperimentalFeatures, nullptr, nullptr);
if (SUCCEEDED(hr))
{
UE_LOG(LogD3D12RHI, Log, TEXT("D3D12 experimental shader models enabled"));
}
}
#endif
// Try to create the DXGIFactory. This will fail if we're not running Vista.
TRefCountPtr<IDXGIFactory4> DXGIFactory4;
SafeCreateDXGIFactory(DXGIFactory4.GetInitReference());
if (!DXGIFactory4)
{
return;
}
TRefCountPtr<IDXGIFactory6> DXGIFactory6;
DXGIFactory4->QueryInterface(__uuidof(IDXGIFactory6), (void**)DXGIFactory6.GetInitReference());
bool bAllowPerfHUD = true;
#if UE_BUILD_SHIPPING || UE_BUILD_TEST
bAllowPerfHUD = false;
#endif
// Allow HMD to override which graphics adapter is chosen, so we pick the adapter where the HMD is connected
uint64 HmdGraphicsAdapterLuid = IHeadMountedDisplayModule::IsAvailable() ? IHeadMountedDisplayModule::Get().GetGraphicsAdapterLuid() : 0;
// Non-static as it is used only a few times
auto* CVarGraphicsAdapter = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.GraphicsAdapter"));
int32 CVarExplicitAdapterValue = HmdGraphicsAdapterLuid == 0 ? (CVarGraphicsAdapter ? CVarGraphicsAdapter->GetValueOnGameThread() : -1) : -2;
FParse::Value(FCommandLine::Get(), TEXT("graphicsadapter="), CVarExplicitAdapterValue);
const bool bFavorDiscreteAdapter = CVarExplicitAdapterValue == -1;
TRefCountPtr<IDXGIAdapter> TempAdapter;
const D3D_FEATURE_LEVEL MinRequiredFeatureLevel = GetRequiredD3DFeatureLevel();
FD3D12AdapterDesc PreferredAdapter;
FD3D12AdapterDesc FirstDiscreteAdapter;
FD3D12AdapterDesc BestMemoryAdapter;
FD3D12AdapterDesc FirstAdapter;
bool bRequestedWARP = D3D12RHI_ShouldCreateWithWarp();
bool bAllowSoftwareRendering = D3D12RHI_AllowSoftwareFallback();
int GpuPreferenceInt = DXGI_GPU_PREFERENCE_HIGH_PERFORMANCE;
FParse::Value(FCommandLine::Get(), TEXT("-gpupreference="), GpuPreferenceInt);
DXGI_GPU_PREFERENCE GpuPreference;
switch(GpuPreferenceInt)
{
case 1: GpuPreference = DXGI_GPU_PREFERENCE_MINIMUM_POWER; break;
case 2: GpuPreference = DXGI_GPU_PREFERENCE_HIGH_PERFORMANCE; break;
default: GpuPreference = DXGI_GPU_PREFERENCE_UNSPECIFIED; break;
}
const EGpuVendorId PreferredVendor = RHIGetPreferredAdapterVendor();
const bool bAllowVendorDevice = !FParse::Param(FCommandLine::Get(), TEXT("novendordevice"));
// Enumerate the DXGIFactory's adapters.
for (uint32 AdapterIndex = 0; FD3D12AdapterDesc::EnumAdapters(AdapterIndex, GpuPreference, DXGIFactory4, DXGIFactory6, TempAdapter.GetInitReference()) != DXGI_ERROR_NOT_FOUND; ++AdapterIndex)
{
// Check that if adapter supports D3D12.
if (TempAdapter)
{
FD3D12DeviceBasicInfo DeviceInfo;
// Log some information about the available D3D12 adapters.
DXGI_ADAPTER_DESC AdapterDesc{};
VERIFYD3D12RESULT(TempAdapter->GetDesc(&AdapterDesc));
#if INTEL_EXTENSIONS
// Enable Intel App Discovery
if (AdapterDesc.VendorId == (uint32)EGpuVendorId::Intel && bAllowVendorDevice)
{
// Intel's App information needs to be registered *before* device creation, so we have to do it here at the last second.
// Even though it takes the device ID as an argument, we've been told by Intel that this isn't going to cause problems if multiple Intel devices are detected.
EnableIntelAppDiscovery(AdapterDesc.DeviceId);
}
#endif
if (SafeTestD3D12CreateDevice(TempAdapter, MinRequiredFeatureLevel, DeviceInfo))
{
check(DeviceInfo.NumDeviceNodes > 0);
const uint32 OutputCount = CountAdapterOutputs(TempAdapter);
UE_LOG(LogD3D12RHI, Log,
TEXT("Found D3D12 adapter %u: %s (VendorId: %04x, DeviceId: %04x, SubSysId: %04x, Revision: %04x"),
AdapterIndex,
AdapterDesc.Description,
AdapterDesc.VendorId, AdapterDesc.DeviceId, AdapterDesc.SubSysId, AdapterDesc.Revision
);
UE_LOG(LogD3D12RHI, Log,
TEXT(" Max supported Feature Level %s, shader model %d.%d, binding tier %d, wave ops %s, atomic64 %s"),
GetFeatureLevelString(DeviceInfo.MaxFeatureLevel),
(DeviceInfo.MaxShaderModel >> 4), (DeviceInfo.MaxShaderModel & 0xF),
DeviceInfo.ResourceBindingTier,
DeviceInfo.bSupportsWaveOps ? TEXT("supported") : TEXT("unsupported"),
DeviceInfo.bSupportsAtomic64 ? TEXT("supported") : TEXT("unsupported")
);
UE_LOG(LogD3D12RHI, Log,
TEXT(" Adapter has %uMB of dedicated video memory, %uMB of dedicated system memory, and %uMB of shared system memory, %d output[s], UMA:%s"),
(uint32)(AdapterDesc.DedicatedVideoMemory / (1024 * 1024)),
(uint32)(AdapterDesc.DedicatedSystemMemory / (1024 * 1024)),
(uint32)(AdapterDesc.SharedSystemMemory / (1024 * 1024)),
OutputCount,
DeviceInfo.bUMA ? TEXT("true") : TEXT("false")
);
const bool bIsWARP = (RHIConvertToGpuVendorId(AdapterDesc.VendorId) == EGpuVendorId::Microsoft);
if (!bIsWARP)
{
const FGPUDriverInfo GPUDriverInfo = FPlatformMisc::GetGPUDriverInfo(AdapterDesc.Description, false);
UE_LOG(LogD3D12RHI, Log, TEXT(" Driver Version: %s (internal:%s, unified:%s)"), *GPUDriverInfo.UserDriverVersion, *GPUDriverInfo.InternalDriverVersion, *GPUDriverInfo.GetUnifiedDriverVersion());
UE_LOG(LogD3D12RHI, Log, TEXT(" Driver Date: %s"), *GPUDriverInfo.DriverDate);
}
else
{
const TCHAR* WarpDllName = TEXT("d3d10warp.dll");
void* WarpDllHandle = FPlatformProcess::GetDllHandle(WarpDllName);
if(WarpDllHandle)
{
const uint64 WarpVersion = FPlatformMisc::GetFileVersion(WarpDllName);
const uint64 Major = ((WarpVersion >> 48) & 0xffff);
const uint64 Minor = ((WarpVersion >> 32) & 0xffff);
const uint64 Build = ((WarpVersion >> 16) & 0xffff);
const uint64 Revision = (WarpVersion & 0xffff);
UE_LOG(LogD3D12RHI, Log, TEXT(" D3D10Warp Version: %d.%d.%d.%d"), Major, Minor, Build, Revision);
}
}
FD3D12AdapterDesc CurrentAdapter(AdapterDesc, AdapterIndex, DeviceInfo);
CurrentAdapter.NumDeviceNodes = DeviceInfo.NumDeviceNodes;
CurrentAdapter.GpuPreference = GpuPreference;
// If requested WARP, then reject all other adapters. If WARP not requested, then reject the WARP device if software rendering support is disallowed
const bool bSkipWARP = (bRequestedWARP && !bIsWARP) || (!bRequestedWARP && bIsWARP && !bAllowSoftwareRendering);
// PerfHUD is for performance profiling
const bool bIsPerfHUD = !FCString::Stricmp(AdapterDesc.Description, TEXT("NVIDIA PerfHUD"));
// we don't allow the PerfHUD adapter
const bool bSkipPerfHUDAdapter = bIsPerfHUD && !bAllowPerfHUD;
// the HMD wants a specific adapter, not this one
const bool bSkipHmdGraphicsAdapter = HmdGraphicsAdapterLuid != 0 && FMemory::Memcmp(&HmdGraphicsAdapterLuid, &AdapterDesc.AdapterLuid, sizeof(LUID)) != 0;
// the user wants a specific adapter, not this one
const bool bSkipExplicitAdapter = CVarExplicitAdapterValue >= 0 && AdapterIndex != CVarExplicitAdapterValue;
const bool bSkipAdapter = bSkipWARP || bSkipPerfHUDAdapter || bSkipHmdGraphicsAdapter || bSkipExplicitAdapter || FParse::Param(FCommandLine::Get(), TEXT("ForceZeroAdapters"));
const bool bIsIntegrated = DeviceInfo.bUMA;
if (!bSkipAdapter && CurrentAdapter.IsValid())
{
if (PreferredVendor != EGpuVendorId::Unknown && PreferredVendor == RHIConvertToGpuVendorId(AdapterDesc.VendorId) && !PreferredAdapter.IsValid())
{
PreferredAdapter = CurrentAdapter;
}
if (!bIsWARP && !bIsIntegrated)
{
if (!FirstDiscreteAdapter.IsValid())
{
FirstDiscreteAdapter = CurrentAdapter;
}
if (CurrentAdapter.Desc.DedicatedVideoMemory > BestMemoryAdapter.Desc.DedicatedVideoMemory)
{
BestMemoryAdapter = CurrentAdapter;
if (PreferredVendor != EGpuVendorId::Unknown && PreferredVendor == RHIConvertToGpuVendorId(AdapterDesc.VendorId))
{
// Choose the best option of the preferred IHV devices
PreferredAdapter = BestMemoryAdapter;
}
}
}
if (!FirstAdapter.IsValid())
{
FirstAdapter = CurrentAdapter;
}
}
}
}
}
TSharedPtr<FD3D12Adapter> NewAdapter;
if (bFavorDiscreteAdapter)
{
// We assume Intel is integrated graphics (slower than discrete) than NVIDIA or AMD cards and rather take a different one
if (PreferredAdapter.IsValid())
{
NewAdapter = TSharedPtr<FD3D12Adapter>(new FWindowsD3D12Adapter(PreferredAdapter));
ChosenAdapters.Add(NewAdapter);
}
else if (BestMemoryAdapter.IsValid())
{
NewAdapter = TSharedPtr<FD3D12Adapter>(new FWindowsD3D12Adapter(BestMemoryAdapter));
ChosenAdapters.Add(NewAdapter);
}
else if (FirstDiscreteAdapter.IsValid())
{
NewAdapter = TSharedPtr<FD3D12Adapter>(new FWindowsD3D12Adapter(FirstDiscreteAdapter));
ChosenAdapters.Add(NewAdapter);
}
else
{
NewAdapter = TSharedPtr<FD3D12Adapter>(new FWindowsD3D12Adapter(FirstAdapter));
ChosenAdapters.Add(NewAdapter);
}
}
else
{
NewAdapter = TSharedPtr<FD3D12Adapter>(new FWindowsD3D12Adapter(FirstAdapter));
ChosenAdapters.Add(NewAdapter);
}
#if D3D12_CORE_ENABLED
UE_LOG(LogD3D12RHI, Log, TEXT("DirectX Agility SDK runtime %s."), CheckIfAgilitySDKLoaded() ? TEXT("found") : TEXT("not found"));
#endif
if (ChosenAdapters.Num() > 0 && ChosenAdapters[0]->GetDesc().IsValid())
{
UE_LOG(LogD3D12RHI, Log, TEXT("Chosen D3D12 Adapter Id = %u"), ChosenAdapters[0]->GetAdapterIndex());
const DXGI_ADAPTER_DESC& AdapterDesc = ChosenAdapters[0]->GetD3DAdapterDesc();
GRHIVendorId = AdapterDesc.VendorId;
GRHIAdapterName = AdapterDesc.Description;
GRHIDeviceId = AdapterDesc.DeviceId;
GRHIDeviceRevision = AdapterDesc.Revision;
}
else
{
UE_LOG(LogD3D12RHI, Error, TEXT("Failed to choose a D3D12 Adapter."));
}
}
static bool DoesAnyAdapterSupportSM6(const TArray<TSharedPtr<FD3D12Adapter>>& Adapters)
{
for (const TSharedPtr<FD3D12Adapter>& Adapter : Adapters)
{
if (IsAdapterSupported(Adapter.Get(), ERHIFeatureLevel::SM6))
{
return true;
}
}
return false;
}
FDynamicRHI* FD3D12DynamicRHIModule::CreateRHI(ERHIFeatureLevel::Type RequestedFeatureLevel)
{
GShaderPlatformForFeatureLevel[ERHIFeatureLevel::ES2_REMOVED] = SP_NumPlatforms;
GShaderPlatformForFeatureLevel[ERHIFeatureLevel::ES3_1] = SP_PCD3D_ES3_1;
GShaderPlatformForFeatureLevel[ERHIFeatureLevel::SM4_REMOVED] = SP_NumPlatforms;
GShaderPlatformForFeatureLevel[ERHIFeatureLevel::SM5] = SP_PCD3D_SM5;
GShaderPlatformForFeatureLevel[ERHIFeatureLevel::SM6] = SP_NumPlatforms;
if (DoesAnyAdapterSupportSM6(ChosenAdapters))
{
GShaderPlatformForFeatureLevel[ERHIFeatureLevel::SM6] = SP_PCD3D_SM6;
}
ERHIFeatureLevel::Type PreviewFeatureLevel;
if (!GIsEditor && RHIGetPreviewFeatureLevel(PreviewFeatureLevel))
{
check(PreviewFeatureLevel == ERHIFeatureLevel::ES3_1);
// ES3.1 feature level emulation in D3D
GMaxRHIFeatureLevel = PreviewFeatureLevel;
}
else
{
GMaxRHIFeatureLevel = RequestedFeatureLevel;
}
if (!ensure(GMaxRHIFeatureLevel < ERHIFeatureLevel::Num))
{
GMaxRHIFeatureLevel = ERHIFeatureLevel::SM5;
}
GMaxRHIShaderPlatform = GShaderPlatformForFeatureLevel[GMaxRHIFeatureLevel];
check(GMaxRHIShaderPlatform != SP_NumPlatforms);
#if D3D12RHI_SUPPORTS_WIN_PIX
bool bPixEventEnabled = (WindowsPixDllHandle != nullptr);
#else
bool bPixEventEnabled = false;
#endif // USE_PIX
if (ChosenAdapters.Num() > 0 && ChosenAdapters[0].IsValid())
{
const bool bIsIntegrated = ChosenAdapters[0].Get()->GetDesc().bUMA;
FGenericCrashContext::SetEngineData(TEXT("RHI.IntegratedGPU"), bIsIntegrated ? TEXT("true") : TEXT("false"));
GRHIDeviceIsIntegrated = bIsIntegrated;
UE_LOG(LogD3D12RHI, Log, TEXT("Integrated GPU (iGPU): %s"), GRHIDeviceIsIntegrated ? TEXT("true") : TEXT("false"));
}
const FString FeatureLevelString = LexToString(GMaxRHIFeatureLevel);
UE_LOG(LogD3D12RHI, Display, TEXT("Creating D3D12 RHI with Max Feature Level %s"), *FeatureLevelString);
GD3D12RHI = new FD3D12DynamicRHI(ChosenAdapters, bPixEventEnabled);
#if ENABLE_RHI_VALIDATION
if (FParse::Param(FCommandLine::Get(), TEXT("RHIValidation")))
{
return new FValidationRHI(GD3D12RHI);
}
#endif
for (int32 Index = 0; Index < ERHIFeatureLevel::Num; ++Index)
{
if (GShaderPlatformForFeatureLevel[Index] != SP_NumPlatforms)
{
check(GMaxTextureSamplers >= (int32)FDataDrivenShaderPlatformInfo::GetMaxSamplers(GShaderPlatformForFeatureLevel[Index]));
if (GMaxTextureSamplers < (int32)FDataDrivenShaderPlatformInfo::GetMaxSamplers(GShaderPlatformForFeatureLevel[Index]))
{
UE_LOG(LogD3D12RHI, Error, TEXT("Shader platform requires at least: %d samplers, device supports: %d."), FDataDrivenShaderPlatformInfo::GetMaxSamplers(GShaderPlatformForFeatureLevel[Index]), GMaxTextureSamplers);
}
}
}
return GD3D12RHI;
}
void FD3D12DynamicRHIModule::StartupModule()
{
#if D3D12RHI_SUPPORTS_WIN_PIX
#if PLATFORM_CPU_ARM_FAMILY && !PLATFORM_WINDOWS_ARM64EC
static FString WindowsPixDllRelativePath = FPaths::Combine(FPaths::EngineDir(), TEXT("Binaries/ThirdParty/Windows/WinPixEventRuntime/arm64"));
static const TCHAR* WindowsPixDll = TEXT("WinPixEventRuntime.dll");
#else
static FString WindowsPixDllRelativePath = FPaths::Combine(FPaths::EngineDir(), TEXT("Binaries/ThirdParty/Windows/WinPixEventRuntime/x64"));
static const TCHAR* WindowsPixDll = TEXT("WinPixEventRuntime.dll");
#endif
UE_LOG(LogD3D12RHI, Log, TEXT("Loading %s for PIX profiling (from %s)."), WindowsPixDll, *WindowsPixDllRelativePath);
WindowsPixDllHandle = FPlatformProcess::GetDllHandle(*FPaths::Combine(WindowsPixDllRelativePath, WindowsPixDll));
if (WindowsPixDllHandle == nullptr)
{
const int32 ErrorNum = FPlatformMisc::GetLastError();
TCHAR ErrorMsg[1024];
FPlatformMisc::GetSystemErrorMessage(ErrorMsg, 1024, ErrorNum);
UE_LOG(LogD3D12RHI, Error, TEXT("Failed to get %s handle: %s (%d)"), WindowsPixDll, ErrorMsg, ErrorNum);
}
#endif
}
void FD3D12DynamicRHIModule::ShutdownModule()
{
#if D3D12RHI_SUPPORTS_WIN_PIX
if (WindowsPixDllHandle)
{
FPlatformProcess::FreeDllHandle(WindowsPixDllHandle);
WindowsPixDllHandle = nullptr;
}
#endif
}
static bool IsRayTracingEmulated(uint32 DeviceId)
{
uint32 EmulatedRayTracingIds[] =
{
0x1B80, // "NVIDIA GeForce GTX 1080"
0x1B81, // "NVIDIA GeForce GTX 1070"
0x1B82, // "NVIDIA GeForce GTX 1070 Ti"
0x1B83, // "NVIDIA GeForce GTX 1060 6GB"
0x1B84, // "NVIDIA GeForce GTX 1060 3GB"
0x1C01, // "NVIDIA GeForce GTX 1050 Ti"
0x1C02, // "NVIDIA GeForce GTX 1060 3GB"
0x1C03, // "NVIDIA GeForce GTX 1060 6GB"
0x1C04, // "NVIDIA GeForce GTX 1060 5GB"
0x1C06, // "NVIDIA GeForce GTX 1060 6GB"
0x1C08, // "NVIDIA GeForce GTX 1050"
0x1C81, // "NVIDIA GeForce GTX 1050"
0x1C82, // "NVIDIA GeForce GTX 1050 Ti"
0x1C83, // "NVIDIA GeForce GTX 1050"
0x1B06, // "NVIDIA GeForce GTX 1080 Ti"
0x1B00, // "NVIDIA TITAN X (Pascal)"
0x1B02, // "NVIDIA TITAN Xp"
0x1D81, // "NVIDIA TITAN V"
};
for (int Index = 0; Index < UE_ARRAY_COUNT(EmulatedRayTracingIds); ++Index)
{
if (DeviceId == EmulatedRayTracingIds[Index])
{
return true;
}
}
return false;
}
static bool IsNvidiaAmpereGPU(uint32 DeviceId)
{
uint32 DeviceList[] =
{
0x2200, // GA102
0x2204, // GA102 - GeForce RTX 3090
0x2205, // GA102 - GeForce RTX 3080 Ti 20GB
0x2206, // GA102 - GeForce RTX 3080
0x2208, // GA102 - GeForce RTX 3080 Ti
0x220a, // GA102 - GeForce RTX 3080 12GB
0x220d, // GA102 - CMP 90HX
0x2216, // GA102 - GeForce RTX 3080 Lite Hash Rate
0x2230, // GA102GL - RTX A6000
0x2231, // GA102GL - RTX A5000
0x2232, // GA102GL - RTX A4500
0x2233, // GA102GL - RTX A5500
0x2235, // GA102GL - A40
0x2236, // GA102GL - A10
0x2237, // GA102GL - A10G
0x2238, // GA102GL - A10M
0x223f, // GA102G
0x2302, // GA103
0x2321, // GA103
0x2414, // GA103 - GeForce RTX 3060 Ti
0x2420, // GA103M - GeForce RTX 3080 Ti Mobile
0x2460, // GA103M - GeForce RTX 3080 Ti Laptop GPU
0x2482, // GA104 - GeForce RTX 3070 Ti
0x2483, // GA104
0x2484, // GA104 - GeForce RTX 3070
0x2486, // GA104 - GeForce RTX 3060 Ti
0x2487, // GA104 - GeForce RTX 3060
0x2488, // GA104 - GeForce RTX 3070 Lite Hash Rate
0x2489, // GA104 - GeForce RTX 3060 Ti Lite Hash Rate
0x248a, // GA104 - CMP 70HX
0x249c, // GA104M - GeForce RTX 3080 Mobile / Max-Q 8GB/16GB
0x249f, // GA104M
0x24a0, // GA104 - Geforce RTX 3070 Ti Laptop GPU
0x24b0, // GA104GL - RTX A4000
0x24b6, // GA104GLM - RTX A5000 Mobile
0x24b7, // GA104GLM - RTX A4000 Mobile
0x24b8, // GA104GLM - RTX A3000 Mobile
0x24dc, // GA104M - GeForce RTX 3080 Mobile / Max-Q 8GB/16GB
0x24dd, // GA104M - GeForce RTX 3070 Mobile / Max-Q
0x24e0, // GA104M - Geforce RTX 3070 Ti Laptop GPU
0x24fa, // GA104 - RTX A4500 Embedded GPU
0x2501, // GA106 - GeForce RTX 3060
0x2503, // GA106 - GeForce RTX 3060
0x2504, // GA106 - GeForce RTX 3060 Lite Hash Rate
0x2505, // GA106
0x2507, // GA106 - Geforce RTX 3050
0x2520, // GA106M - GeForce RTX 3060 Mobile / Max-Q
0x2523, // GA106M - GeForce RTX 3050 Ti Mobile / Max-Q
0x2531, // GA106 - RTX A2000
0x2560, // GA106M - GeForce RTX 3060 Mobile / Max-Q
0x2563, // GA106M - GeForce RTX 3050 Ti Mobile / Max-Q
0x2571, // GA106 - RTX A2000 12GB
0x2583, // GA107 - GeForce RTX 3050
0x25a0, // GA107M - GeForce RTX 3050 Ti Mobile
0x25a2, // GA107M - GeForce RTX 3050 Mobile
0x25a4, // GA107
0x25a5, // GA107M - GeForce RTX 3050 Mobile
0x25a6, // GA107M - GeForce MX570
0x25a7, // GA107M - GeForce MX570
0x25a9, // GA107M - GeForce RTX 2050
0x25b5, // GA107GLM - RTX A4 Mobile
0x25b8, // GA107GLM - RTX A2000 Mobile
0x25b9, // GA107GLM - RTX A1000 Laptop GPU
0x25e0, // GA107BM - GeForce RTX 3050 Ti Mobile
0x25e2, // GA107BM - GeForce RTX 3050 Mobile
0x25e5, // GA107BM - GeForce RTX 3050 Mobile
0x25f9, // GA107 - RTX A1000 Embedded GPU
0x25fa, // GA107 - RTX A2000 Embedded GPU
};
for (uint32 KnownDeviceId : DeviceList)
{
if (DeviceId == KnownDeviceId)
{
return true;
}
}
return false;
}
static void DisableRayTracingSupport()
{
GRHISupportsRayTracing = false;
GRHISupportsRayTracingPSOAdditions = false;
GRHISupportsRayTracingDispatchIndirect = false;
GRHISupportsRayTracingAsyncBuildAccelerationStructure = false;
GRHISupportsRayTracingAMDHitToken = false;
GRHISupportsInlineRayTracing = false;
GRHISupportsRayTracingShaders = false;
GRHIGlobals.RayTracing.SupportsAsyncRayTraceDispatch = false;
}
static void ClearPSODriverCache()
{
FString LocalAppDataFolder = FPlatformMisc::GetEnvironmentVariable(TEXT("LOCALAPPDATA"));
if (!LocalAppDataFolder.IsEmpty())
{
auto DeleteFolder = [](const FString& PSOFolderPath) {
IFileManager& FileManager = IFileManager::Get();
FileManager.DeleteDirectory(*PSOFolderPath, /*RequireExists*/ false, /*Tree*/ true);
};
auto ClearFolder = [](const FString& PSOPath, const TCHAR* Extension)
{
TArray<FString> Files;
IFileManager& FileManager = IFileManager::Get();
FileManager.FindFiles(Files, *PSOPath, Extension);
for (FString& File : Files)
{
FString FilePath = FString::Printf(L"%s\\%s", *PSOPath, *File);
FileManager.Delete(*FilePath, /*RequireExists*/ false, /*EvenReadOnly*/ true, /*Quiet*/ true);
}
};
if (IsRHIDeviceNVIDIA())
{
// NVIDIA used to have a global cache, but now also has a per-driver cache in a different folder in LocalLow.
FString GlobalPSOPath = FPaths::Combine(*LocalAppDataFolder, TEXT("NVIDIA"), TEXT("DXCache"));
ClearFolder(GlobalPSOPath, nullptr);
FString PerDriverPSOPath = FPaths::Combine(*LocalAppDataFolder, TEXT(".."), TEXT("LocalLow"), TEXT("NVIDIA"), TEXT("PerDriverVersion"), TEXT("DXCache"));
ClearFolder(PerDriverPSOPath, nullptr);
}
else if (IsRHIDeviceAMD())
{
FString PSOPath = FPaths::Combine(*LocalAppDataFolder, TEXT("AMD"), TEXT("DxCache"));
ClearFolder(PSOPath, nullptr);
PSOPath = FPaths::Combine(*LocalAppDataFolder, TEXT("AMD"), TEXT("DxcCache"));
ClearFolder(PSOPath, nullptr);
PSOPath = FPaths::Combine(*LocalAppDataFolder, TEXT(".."), TEXT("LocalLow"), TEXT("AMD"), TEXT("DxCache"));
ClearFolder(PSOPath, nullptr);
PSOPath = FPaths::Combine(*LocalAppDataFolder, TEXT(".."), TEXT("LocalLow"), TEXT("AMD"), TEXT("DxcCache"));
ClearFolder(PSOPath, nullptr);
}
else if (IsRHIDeviceIntel())
{
// Intel stores the cache in LocalLow.
FString PSOPath = FPaths::Combine(*LocalAppDataFolder, TEXT(".."), TEXT("LocalLow"), TEXT("Intel"), TEXT("ShaderCache"));
ClearFolder(PSOPath, nullptr);
}
else if (IsRHIDeviceQualcomm())
{
// Qualcomm uses D3DSCache. The folder contains sub-folders for each application's cache entry so it needs recursive deletion.
FString PSOPath = FPaths::Combine(*LocalAppDataFolder, TEXT("D3DSCache"));
DeleteFolder(PSOPath);
}
}
else
{
UE_LOG(LogD3D12RHI, Error, TEXT("clearPSODriverCache failed: please ensure that LOCALAPPDATA points to C:\\Users\\<username>\\AppData\\Local"));
}
}
void FD3D12DynamicRHI::Init()
{
#if D3D12RHI_SUPPORTS_WIN_PIX
// PIX GPU capture dll: makes PIX be able to attach to our process. !GetModuleHandle() is required because PIX may already be attached.
if (GAutoAttachPIX || FParse::Param(FCommandLine::Get(), TEXT("attachPIX")))
{
// If PIX is not already attached, load its dll to auto attach ourselves
if (!FPlatformProcess::GetDllHandle(L"WinPixGpuCapturer.dll"))
{
// This should always be loaded from the installed PIX directory.
// This function does assume it's installed under Program Files so we may have to revisit for custom install locations.
WinPixGpuCapturerHandle = PIXLoadLatestWinPixGpuCapturerLibrary();
}
}
#endif
SetupD3D12Debug();
check(!GIsRHIInitialized);
if (FParse::Param(FCommandLine::Get(), TEXT("clearPSODriverCache")))
{
ClearPSODriverCache();
}
const DXGI_ADAPTER_DESC& AdapterDesc = GetAdapter().GetD3DAdapterDesc();
UE_LOG(LogD3D12RHI, Log, TEXT(" GPU DeviceId: 0x%x (for the marketing name, search the web for \"GPU Device Id\")"), AdapterDesc.DeviceId);
#if WITH_AMD_AGS
// Initialize the AMD AGS utility library, when running on an AMD device
AGSGPUInfo AmdAgsGpuInfo = {};
if (IsRHIDeviceAMD() && UE::RHICore::AllowVendorDevice())
{
check(AmdAgsContext == nullptr);
check(AmdSupportedExtensionFlags == 0);
// agsInit should be called before D3D device creation
agsInitialize(AGS_MAKE_VERSION(AMD_AGS_VERSION_MAJOR, AMD_AGS_VERSION_MINOR, AMD_AGS_VERSION_PATCH), nullptr, &AmdAgsContext, &AmdAgsGpuInfo);
}
#endif
// Create a device chain for each of the adapters we have chosen. This could be a single discrete card,
// a set discrete cards linked together (i.e. SLI/Crossfire) an Integrated device or any combination of the above
for (TSharedPtr<FD3D12Adapter>& Adapter : ChosenAdapters)
{
check(Adapter->GetDesc().IsValid());
Adapter->InitializeDevices();
}
if (GDevDisableD3DRuntimeBackgroundThreads)
{
#if D3D12_MAX_DEVICE_INTERFACE >= 6
ID3D12Device6* Device6 = GetAdapter().GetD3DDevice6();
if (Device6)
{
HRESULT Res = Device6->SetBackgroundProcessingMode(
D3D12_BACKGROUND_PROCESSING_MODE_DISABLE_PROFILING_BY_SYSTEM,
D3D12_MEASUREMENTS_ACTION_KEEP_ALL,
nullptr, nullptr);
if (SUCCEEDED(Res))
{
UE_LOG(LogD3D12RHI, Log, TEXT("Disabled D3D runtime's background threads"));
}
else
{
UE_LOG(LogD3D12RHI, Error, TEXT("Could not disable D3D runtime's background threads: SetBackgroundProcessingMode returned error 0x%08X"), Res);
}
}
else
#endif
{
UE_LOG(LogD3D12RHI, Warning, TEXT("Could not disable D3D runtime's background threads because the ID3D12Device6 interface is not available"));
}
}
bool bHasVendorSupportForAtomic64 = false;
#if WITH_AMD_AGS
// Check if the AMD device is pre-RDNA, and ensure it doesn't misreport wave32 support
if (IsRHIDeviceAMD() && UE::RHICore::AllowVendorDevice() && AmdAgsContext)
{
for (int32 DeviceIndex = 0; DeviceIndex < AmdAgsGpuInfo.numDevices; DeviceIndex++)
{
const AGSDeviceInfo& DeviceInfo = AmdAgsGpuInfo.devices[DeviceIndex];
if (DeviceInfo.deviceId == AdapterDesc.DeviceId && DeviceInfo.vendorId == AdapterDesc.VendorId)
{
if (DeviceInfo.asicFamily != AGSDeviceInfo::AsicFamily_Unknown && DeviceInfo.asicFamily < AGSDeviceInfo::AsicFamily_RDNA)
{
GRHIMinimumWaveSize = 64;
GRHIMaximumWaveSize = 64;
}
break;
}
}
}
// Warn if we are trying to use RGP frame markers but are either running on a non-AMD device
// or using an older AMD driver without RGP marker support
if (IsRHIDeviceAMD())
{
if (UE::RHICore::AllowVendorDevice())
{
static_assert(sizeof(AGSDX12ReturnedParams::ExtensionsSupported) == sizeof(uint32));
AGSDX12ReturnedParams::ExtensionsSupported AMDSupportedExtensions;
FMemory::Memcpy(&AMDSupportedExtensions, &AmdSupportedExtensionFlags, sizeof(uint32));
if (GEmitRgpFrameMarkers && AMDSupportedExtensions.userMarkers == 0)
{
UE_LOG(LogD3D12RHI, Warning, TEXT("Attempting to use RGP frame markers without driver support. Update AMD driver."));
}
bHasVendorSupportForAtomic64 = AMDSupportedExtensions.intrinsics19 != 0; // "intrinsics19" includes AtomicU64
bHasVendorSupportForAtomic64 = bHasVendorSupportForAtomic64 && AMDSupportedExtensions.UAVBindSlot != 0;
}
}
else if (GEmitRgpFrameMarkers)
{
UE_LOG(LogD3D12RHI, Warning, TEXT("Attempting to use RGP frame markers on a non-AMD device."));
}
#endif
// Disable ray tracing for Windows build versions
if (GRHISupportsRayTracing
&& GMinimumWindowsBuildVersionForRayTracing > 0
&& !FPlatformMisc::VerifyWindowsVersion(10, 0, GMinimumWindowsBuildVersionForRayTracing))
{
DisableRayTracingSupport();
UE_LOG(LogD3D12RHI, Warning, TEXT("Ray tracing is disabled because it requires Windows 10 version %u"), (uint32)GMinimumWindowsBuildVersionForRayTracing);
}
#if WITH_NVAPI
if (IsRHIDeviceNVIDIA() && UE::RHICore::AllowVendorDevice())
{
const NvAPI_Status NvStatus = NvAPI_Initialize();
NvU32 PipelineFlags = NVAPI_D3D12_PIPELINE_CREATION_STATE_FLAGS_NONE;
// Helper to allow all pipeline state capabilities to test and accumulate their support, so all can be applied atomically
auto CheckPipelineStateCap = [&](NVAPI_D3D12_PIPELINE_CREATION_STATE_FLAGS Flag) -> bool
{
NVAPI_D3D12_SET_CREATE_PIPELINE_STATE_OPTIONS_PARAMS PipelineStateOptions;
PipelineStateOptions.version = NVAPI_D3D12_SET_CREATE_PIPELINE_STATE_OPTIONS_PARAMS_VER;
PipelineStateOptions.flags = Flag;
if (NvAPI_D3D12_SetCreatePipelineStateOptions(GetAdapter().GetD3DDevice5(), &PipelineStateOptions) == NVAPI_OK)
{
PipelineFlags |= Flag;
return true;
}
return false;
};
if (NvStatus == NVAPI_OK)
{
NvAPI_Status NvStatusAtomicU64 = NvAPI_D3D12_IsNvShaderExtnOpCodeSupported(GetAdapter().GetD3DDevice(), NV_EXTN_OP_UINT64_ATOMIC, &bHasVendorSupportForAtomic64);
if (NvStatusAtomicU64 != NVAPI_OK)
{
UE_LOG(LogD3D12RHI, Warning, TEXT("Failed to query support for 64 bit atomics"));
}
NvAPI_Status NvStatusSER = NvAPI_D3D12_IsNvShaderExtnOpCodeSupported(GetAdapter().GetD3DDevice(), NV_EXTN_OP_HIT_OBJECT_REORDER_THREAD, &GRHIGlobals.SupportsShaderExecutionReordering);
if (NvStatusSER == NVAPI_OK && GRHIGlobals.SupportsShaderExecutionReordering)
{
NVAPI_D3D12_RAYTRACING_THREAD_REORDERING_CAPS ReorderCaps = NVAPI_D3D12_RAYTRACING_THREAD_REORDERING_CAP_NONE;
NvStatusSER = NvAPI_D3D12_GetRaytracingCaps(
GetAdapter().GetD3DDevice(),
NVAPI_D3D12_RAYTRACING_CAPS_TYPE_THREAD_REORDERING,
&ReorderCaps,
sizeof(ReorderCaps));
if (NvStatusSER != NVAPI_OK || ReorderCaps == NVAPI_D3D12_RAYTRACING_THREAD_REORDERING_CAP_NONE)
{
GRHIGlobals.SupportsShaderExecutionReordering = false;
}
}
if (GRHIGlobals.SupportsShaderExecutionReordering)
{
UE_LOG(LogD3D12RHI, Log, TEXT("NVIDIA Shader Execution Reordering interface supported!"));
}
else
{
UE_LOG(LogD3D12RHI, Log, TEXT("NVIDIA Shader Execution Reordering NOT supported!"));
}
// Ray Tracing Cluster Ops
{
GRHIGlobals.RayTracing.SupportsClusterOps = false;
NVAPI_D3D12_RAYTRACING_CLUSTER_OPERATIONS_CAPS ClusterOpsCaps = {};
NvAPI_Status NvStatusRayTracingClusterOps = NvAPI_D3D12_GetRaytracingCaps(GetAdapter().GetD3DDevice5(), NVAPI_D3D12_RAYTRACING_CAPS_TYPE_CLUSTER_OPERATIONS, &ClusterOpsCaps, sizeof(NVAPI_D3D12_RAYTRACING_CLUSTER_OPERATIONS_CAPS));
if (NvStatusRayTracingClusterOps == NVAPI_OK && ClusterOpsCaps == NVAPI_D3D12_RAYTRACING_CLUSTER_OPERATIONS_CAP_STANDARD)
{
GRHIGlobals.RayTracing.SupportsClusterOps = CheckPipelineStateCap(NVAPI_D3D12_PIPELINE_CREATION_STATE_FLAGS_ENABLE_CLUSTER_SUPPORT);
}
if (GRHIGlobals.RayTracing.SupportsClusterOps)
{
GRHIGlobals.RayTracing.ClusterAccelerationStructureAlignment = NVAPI_D3D12_RAYTRACING_CLAS_BYTE_ALIGNMENT;
GRHIGlobals.RayTracing.ClusterAccelerationStructureTemplateAlignment = NVAPI_D3D12_RAYTRACING_CLUSTER_TEMPLATE_BYTE_ALIGNMENT;
UE_LOG(LogD3D12RHI, Log, TEXT("NVIDIA Ray Tracing Cluster Operations supported and enabled"));
}
else
{
UE_LOG(LogD3D12RHI, Log, TEXT("NVIDIA Ray Tracing Cluster Operations not supported on this device"));
}
}
}
else
{
UE_LOG(LogD3D12RHI, Warning, TEXT("Failed to initialize NVAPI"));
}
NvU32 DriverVersion = UINT32_MAX;
if (NvStatus == NVAPI_OK)
{
NvAPI_ShortString BranchString("");
if (NvAPI_SYS_GetDriverAndBranchVersion(&DriverVersion, BranchString) != NVAPI_OK)
{
UE_LOG(LogD3D12RHI, Warning, TEXT("Failed to query NVIDIA driver version"));
}
}
// Disable ray tracing for old Nvidia drivers
if (GRHISupportsRayTracing
&& GMinimumDriverVersionForRayTracingNVIDIA > 0
&& DriverVersion < (uint32)GMinimumDriverVersionForRayTracingNVIDIA)
{
DisableRayTracingSupport();
UE_LOG(LogD3D12RHI, Warning, TEXT("Ray tracing is disabled because the driver is too old"));
}
// Disable indirect ray tracing dispatch on drivers that have a known bug.
if (GRHISupportsRayTracingDispatchIndirect
&& DriverVersion < (uint32)46611)
{
GRHISupportsRayTracingDispatchIndirect = false;
UE_LOG(LogD3D12RHI, Warning,
TEXT("Indirect ray tracing dispatch is disabled because of known bugs in the current driver. ")
TEXT("Please update to NVIDIA driver version 466.11 or newer."));
}
if (GRHISupportsRayTracing
&& IsRayTracingEmulated(AdapterDesc.DeviceId))
{
DisableRayTracingSupport();
UE_LOG(LogD3D12RHI, Warning, TEXT("Ray tracing is disabled for NVIDIA cards with the Pascal architecture."));
}
if (GRHISupportsRayTracing
&& PipelineFlags != NVAPI_D3D12_PIPELINE_CREATION_STATE_FLAGS_NONE)
{
// Set the accumulated feature flags for pipeline creation
NVAPI_D3D12_SET_CREATE_PIPELINE_STATE_OPTIONS_PARAMS PipelineStateOptions;
PipelineStateOptions.version = NVAPI_D3D12_SET_CREATE_PIPELINE_STATE_OPTIONS_PARAMS_VER;
PipelineStateOptions.flags = PipelineFlags;
NvAPI_Status Status = NvAPI_D3D12_SetCreatePipelineStateOptions(GetAdapter().GetD3DDevice5(), &PipelineStateOptions);
if (Status == NVAPI_OK)
{
UE_LOG(LogD3D12RHI, Log, TEXT("NVIDIA Extended ray tracing pipeline state applied."));
}
else
{
checkf(0, TEXT("NvAPI SetCreatePipelineStateOptions failed, incompatible feature flags are enabled"));
}
}
} // if NVIDIA
#endif // NVAPI
if (IsRHIDeviceNVIDIA())
{
//
// See Jira UE-233616
//
// The NVIDIA driver drops barriers at the beginning of command lists on the async queue because
// it assumes each list is executed within its own ECL scope and therefore the GPU is idle'd between
// each. That's not the case if we execute more than one list at a time, so break it up into individual
// executions to make it true.
//
// @TODO - Add driver version check and message text once a fix is released. Also raise message priority to warning.
//
static constexpr auto D3D12SubmissionExecuteBatchSizeAsyncCVarName = TEXT("r.D3D12.Submission.MaxExecuteBatchSize.Async");
const auto CVarD3D12SubmissionAsyncMaxExecuteBatchSize = IConsoleManager::Get().FindConsoleVariable(D3D12SubmissionExecuteBatchSizeAsyncCVarName);
if (CVarD3D12SubmissionAsyncMaxExecuteBatchSize)
{
CVarD3D12SubmissionAsyncMaxExecuteBatchSize->Set(1);
UE_LOG(LogD3D12RHI, Display,
TEXT("Batched command list execution is disabled for async queues due to known bugs in the current driver."));
}
else
{
// If we're here, someone moved our cheese and this work-around logic needs to be updated
UE_LOG(LogD3D12RHI, Error,
TEXT("Couldn't find CVar %s to work around a known bug in the current driver."), D3D12SubmissionExecuteBatchSizeAsyncCVarName);
checkNoEntry();
}
} // if NVIDIA
#if WITH_AMD_AGS
if (GRHISupportsRayTracing
&& IsRHIDeviceAMD()
&& AmdAgsContext
&& AmdAgsGpuInfo.radeonSoftwareVersion)
{
if (GMinimumDriverVersionForRayTracingAMD > 0
&& agsCheckDriverVersion(AmdAgsGpuInfo.radeonSoftwareVersion, GMinimumDriverVersionForRayTracingAMD) == AGS_SOFTWAREVERSIONCHECK_OLDER)
{
DisableRayTracingSupport();
UE_LOG(LogD3D12RHI, Warning, TEXT("Ray tracing is disabled because the driver is too old"));
}
if (GRHISupportsRayTracing)
{
static_assert(sizeof(AGSDX12ReturnedParams::ExtensionsSupported) == sizeof(uint32));
AGSDX12ReturnedParams::ExtensionsSupported AMDSupportedExtensions;
FMemory::Memcpy(&AMDSupportedExtensions, &AmdSupportedExtensionFlags, sizeof(uint32));
GRHISupportsRayTracingAMDHitToken = AMDSupportedExtensions.rayHitToken;
UE_LOG(LogD3D12RHI, Log, TEXT("AMD hit token extension is %s"), GRHISupportsRayTracingAMDHitToken ? TEXT("supported") : TEXT("not supported"));
}
}
#endif // WITH_AMD_AGS
#if INTEL_EXTENSIONS
if (IsRHIDeviceIntel() && UE::RHICore::AllowVendorDevice())
{
if( GDX12INTCAtomicUInt64Emulation )
{
bHasVendorSupportForAtomic64 = GDX12INTCAtomicUInt64Emulation;
}
// Create a new Intel Extensions Device Extension Context to support DX12 extension calls
INTCExtensionInfo INTCExtensionInfo{};
IntelExtensionContext = CreateIntelExtensionsContext(GetAdapter().GetD3DDevice(), INTCExtensionInfo);
}
#endif // INTEL_EXTENSIONS
GRHIPersistentThreadGroupCount = 1440; // TODO: Revisit based on vendor/adapter/perf query
GTexturePoolSize = 0;
// Issue: 32bit windows doesn't report 64bit value, we take what we get.
FD3D12GlobalStats::GDedicatedVideoMemory = int64(AdapterDesc.DedicatedVideoMemory);
FD3D12GlobalStats::GDedicatedSystemMemory = int64(AdapterDesc.DedicatedSystemMemory);
FD3D12GlobalStats::GSharedSystemMemory = int64(AdapterDesc.SharedSystemMemory);
// Total amount of system memory, clamped to 8 GB
int64 TotalPhysicalMemory = FMath::Min(int64(FPlatformMemory::GetConstants().TotalPhysicalGB), 8ll) * (1024ll * 1024ll * 1024ll);
// Consider 50% of the shared memory but max 25% of total system memory.
int64 ConsideredSharedSystemMemory = FMath::Min(FD3D12GlobalStats::GSharedSystemMemory / 2ll, TotalPhysicalMemory / 4ll);
TRefCountPtr<IDXGIAdapter3> DxgiAdapter3;
VERIFYD3D12RESULT(GetAdapter().GetAdapter()->QueryInterface(IID_PPV_ARGS(DxgiAdapter3.GetInitReference())));
DXGI_QUERY_VIDEO_MEMORY_INFO LocalVideoMemoryInfo;
VERIFYD3D12RESULT(DxgiAdapter3->QueryVideoMemoryInfo(0, DXGI_MEMORY_SEGMENT_GROUP_LOCAL, &LocalVideoMemoryInfo));
const int64 TargetBudget = LocalVideoMemoryInfo.Budget * 0.90f; // Target using 90% of our budget to account for some fragmentation.
FD3D12GlobalStats::GTotalGraphicsMemory = TargetBudget;
if (sizeof(SIZE_T) < 8)
{
// Clamp to 1 GB if we're less than 64-bit
FD3D12GlobalStats::GTotalGraphicsMemory = FMath::Min(FD3D12GlobalStats::GTotalGraphicsMemory, 1024ll * 1024ll * 1024ll);
}
if (GPoolSizeVRAMPercentage > 0)
{
float PoolSize = float(GPoolSizeVRAMPercentage) * 0.01f * float(FD3D12GlobalStats::GTotalGraphicsMemory);
// Truncate GTexturePoolSize to MB (but still counted in bytes)
GTexturePoolSize = int64(FGenericPlatformMath::TruncToFloat(PoolSize / 1024.0f / 1024.0f)) * 1024 * 1024;
UE_LOG(LogRHI, Log, TEXT("Texture pool is %llu MB (%d%% of %llu MB)"),
GTexturePoolSize / 1024 / 1024,
GPoolSizeVRAMPercentage,
FD3D12GlobalStats::GTotalGraphicsMemory / 1024 / 1024);
}
RequestedTexturePoolSize = GTexturePoolSize;
VERIFYD3D12RESULT(DxgiAdapter3->SetVideoMemoryReservation(0, DXGI_MEMORY_SEGMENT_GROUP_LOCAL, FMath::Min((int64)LocalVideoMemoryInfo.AvailableForReservation, FD3D12GlobalStats::GTotalGraphicsMemory)));
#if (UE_BUILD_SHIPPING && WITH_EDITOR) && PLATFORM_WINDOWS && !PLATFORM_64BITS
// Disable PIX for windows in the shipping editor builds
D3DPERF_SetOptions(1);
#endif
// Multi-threaded resource creation is always supported in DX12, but allow users to disable it.
GRHISupportsAsyncTextureCreation = D3D12RHI_ShouldAllowAsyncResourceCreation();
if (GRHISupportsAsyncTextureCreation)
{
GRHISupportAsyncTextureStreamOut = true;
UE_LOG(LogD3D12RHI, Log, TEXT("Async texture creation enabled"));
}
else
{
UE_LOG(LogD3D12RHI, Log, TEXT("Async texture creation disabled: %s"),
D3D12RHI_ShouldAllowAsyncResourceCreation() ? TEXT("no driver support") : TEXT("disabled by user"));
}
if (GRHISupportsAtomicUInt64)
{
UE_LOG(LogD3D12RHI, Log, TEXT("RHI has support for 64 bit atomics"));
}
else if (bHasVendorSupportForAtomic64)
{
GRHISupportsAtomicUInt64 = true;
UE_LOG(LogD3D12RHI, Log, TEXT("RHI has vendor support for 64 bit atomics"));
}
else
{
UE_LOG(LogD3D12RHI, Log, TEXT("RHI does not have support for 64 bit atomics"));
}
// Detect reserved resource support
{
D3D12_FEATURE_DATA_D3D12_OPTIONS Options = {};
if (SUCCEEDED(GetAdapter().GetD3DDevice()->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS, &Options, sizeof(Options))))
{
// Tier 2 is guaranteed for all adapters with feature level 12_0.
GRHIGlobals.ReservedResources.Supported = Options.TiledResourcesTier >= D3D12_TILED_RESOURCES_TIER_2;
// Tier 3 is required to create volume textures. Some hardware may support it.
GRHIGlobals.ReservedResources.SupportsVolumeTextures = Options.TiledResourcesTier >= D3D12_TILED_RESOURCES_TIER_3;
}
}
D3D12_FEATURE_DATA_D3D12_OPTIONS6 options = {};
HRESULT Options6HR = GetAdapter().GetD3DDevice()->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS6, &options, sizeof(options));
#if WITH_MGPU
// Disallow async compute in mGPU mode due to submission order bugs (UE-193929).
if (GNumExplicitGPUsForRendering > 1)
{
GSupportsEfficientAsyncCompute = false;
}
#endif
GSupportsDepthBoundsTest = SupportsDepthBoundsTest(this);
{
GRHISupportsHDROutput = SetupDisplayHDRMetaData();
// Specify the desired HDR pixel format.
// Possible values are:
// 1) PF_FloatRGBA - FP16 format that allows for linear gamma. This is the current engine default.
// r.HDR.Display.ColorGamut = 0 (sRGB which is the same gamut as ScRGB)
// r.HDR.Display.OutputDevice = 5 or 6 (ScRGB)
// 2) PF_A2B10G10R10 - Save memory vs FP16 as well as allow for possible performance improvements
// in fullscreen by avoiding format conversions.
// r.HDR.Display.ColorGamut = 2 (Rec2020 / BT2020)
// r.HDR.Display.OutputDevice = 3 or 4 (ST-2084)
#if WITH_EDITOR
GRHIHDRDisplayOutputFormat = PF_FloatRGBA;
#else
GRHIHDRDisplayOutputFormat = PF_A2B10G10R10;
#endif
}
FHardwareInfo::RegisterHardwareInfo(NAME_RHI, TEXT("D3D12"));
GRHISupportsTextureStreaming = true;
GRHISupportsFirstInstance = true;
GRHISupportsShaderRootConstants = true;
GRHIGlobals.ShaderBundles.SupportsDispatch = false; // Shader Bundles only implemented with Work Graphs
GRHIGlobals.ShaderBundles.SupportsWorkGraphDispatch = GRHIGlobals.SupportsShaderWorkGraphsTier1;
GRHIGlobals.ShaderBundles.SupportsWorkGraphGraphicsDispatch = GRHIGlobals.SupportsShaderWorkGraphsTier1_1;
GRHIGlobals.ShaderBundles.SupportsParallel = false; // TODO: FD3D12ExplicitDescriptorHeap::UpdateSyncPoint() is not safe for parallel translate due to frame fencing
// PC D3D12 support async dispatch rays calls
if (GRHIGlobals.RayTracing.Supported)
{
GRHIGlobals.RayTracing.SupportsAsyncRayTraceDispatch = true;
GRHIGlobals.RayTracing.SupportsPersistentSBTs = true;
}
// Indicate that the RHI needs to use the engine's deferred deletion queue.
GRHINeedsExtraDeletionLatency = true;
// There is no need to defer deletion of streaming textures
// - Suballocated ones are defer-deleted by their allocators
// - Standalones are added to the deferred deletion queue of its parent FD3D12Adapter
GRHIForceNoDeletionLatencyForStreamingTextures = !!PLATFORM_WINDOWS;
if(Options6HR == S_OK && options.VariableShadingRateTier != D3D12_VARIABLE_SHADING_RATE_TIER_NOT_SUPPORTED && HardwareVariableRateShadingSupportedByPlatform(GMaxRHIShaderPlatform))
{
GRHISupportsPipelineVariableRateShading = true; // We have at least tier 1.
GRHISupportsLargerVariableRateShadingSizes = (options.AdditionalShadingRatesSupported != 0);
if (options.VariableShadingRateTier == D3D12_VARIABLE_SHADING_RATE_TIER_2)
{
GRHISupportsAttachmentVariableRateShading = true;
GRHISupportsComplexVariableRateShadingCombinerOps = true;
GRHIVariableRateShadingImageTileMinWidth = options.ShadingRateImageTileSize;
GRHIVariableRateShadingImageTileMinHeight = options.ShadingRateImageTileSize;
GRHIVariableRateShadingImageTileMaxWidth = options.ShadingRateImageTileSize;
GRHIVariableRateShadingImageTileMaxHeight = options.ShadingRateImageTileSize;
GRHIVariableRateShadingImageDataType = VRSImage_Palette;
GRHIVariableRateShadingImageFormat = PF_R8_UINT;
}
}
else
{
GRHISupportsAttachmentVariableRateShading = GRHISupportsPipelineVariableRateShading = false;
GRHIVariableRateShadingImageTileMinWidth = 1;
GRHIVariableRateShadingImageTileMinHeight = 1;
GRHIVariableRateShadingImageTileMaxWidth = 1;
GRHIVariableRateShadingImageTileMaxHeight = 1;
}
#if D3D12RHI_SUPPORTS_UNCOMPRESSED_UAV
D3D12_FEATURE_DATA_D3D12_OPTIONS12 Options12{};
if (SUCCEEDED(GetAdapter().GetD3DDevice()->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS12, &Options12, sizeof(Options12))))
{
GRHIGlobals.SupportsUAVFormatAliasing =
(Options12.RelaxedFormatCastingSupported != 0)
// Our BCn casting requires enhanced barrier support
&& (Options12.EnhancedBarriersSupported != 0)
// We require ID3D12Device12 for GetResourceAllocationInfo3
&& GetAdapter().GetD3DDevice12() != nullptr
#if PLATFORM_WINDOWS
// Make sure RenderDoc supports the new interfaces
&& (D3D12RHI_IsRenderDocPresent(GetAdapter().GetD3DDevice()) == D3D12RHI_IsRenderDocPresent(GetAdapter().GetD3DDevice12()))
#endif
;
}
#else
GRHIGlobals.SupportsUAVFormatAliasing = (GetAdapter().GetResourceHeapTier() > D3D12_RESOURCE_HEAP_TIER_1 && IsRHIDeviceNVIDIA());
#endif
InitializeSubmissionPipe();
#if (RHI_NEW_GPU_PROFILER == 0)
for (TSharedPtr<FD3D12Adapter>& Adapter : ChosenAdapters)
{
FD3D12BufferedGPUTiming::Initialize(Adapter.Get());
}
#endif
FRenderResource::InitPreRHIResources();
GIsRHIInitialized = true;
}
bool FD3D12DynamicRHI::IsQuadBufferStereoEnabled() const
{
return bIsQuadBufferStereoEnabled;
}
void FD3D12DynamicRHI::DisableQuadBufferStereo()
{
bIsQuadBufferStereoEnabled = false;
}
void FD3D12Device::CreateSamplerInternal(const D3D12_SAMPLER_DESC& Desc, D3D12_CPU_DESCRIPTOR_HANDLE Descriptor)
{
GetDevice()->CreateSampler(&Desc, Descriptor);
}
void FD3D12Device::CopyDescriptors(ID3D12Device* D3DDevice, D3D12_CPU_DESCRIPTOR_HANDLE Destination, const D3D12_CPU_DESCRIPTOR_HANDLE* Source, uint32 NumSourceDescriptors, D3D12_DESCRIPTOR_HEAP_TYPE Type)
{
D3DDevice->CopyDescriptors(1, &Destination, &NumSourceDescriptors, NumSourceDescriptors, Source, nullptr, Type);
}
#if D3D12_RHI_RAYTRACING
void FD3D12Device::GetRaytracingAccelerationStructurePrebuildInfo(
const D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_INPUTS* pDesc,
D3D12_RAYTRACING_ACCELERATION_STRUCTURE_PREBUILD_INFO* pInfo)
{
ID3D12Device5* RayTracingDevice = GetDevice5();
RayTracingDevice->GetRaytracingAccelerationStructurePrebuildInfo(pDesc, pInfo);
}
TRefCountPtr<ID3D12StateObject> FD3D12Device::DeserializeRayTracingStateObject(D3D12_SHADER_BYTECODE Bytecode, ID3D12RootSignature* RootSignature)
{
checkNoEntry();
TRefCountPtr<ID3D12StateObject> Result;
return Result;
}
bool FD3D12Device::GetRayTracingPipelineInfo(ID3D12StateObject* Pipeline, FD3D12RayTracingPipelineInfo* OutInfo)
{
// Return a safe default result on Windows, as there is no API to query interesting pipeline metrics.
FD3D12RayTracingPipelineInfo Result = {};
*OutInfo = Result;
return false;
}
#endif // D3D12_RHI_RAYTRACING
/**
* Retrieve available screen resolutions.
*
* @param Resolutions TArray<FScreenResolutionRHI> parameter that will be filled in.
* @param bIgnoreRefreshRate If true, ignore refresh rates.
*
* @return bool true if successfully filled the array
*/
bool FD3D12DynamicRHI::RHIGetAvailableResolutions(FScreenResolutionArray& Resolutions, bool bIgnoreRefreshRate)
{
int32 MinAllowableResolutionX = 0;
int32 MinAllowableResolutionY = 0;
int32 MaxAllowableResolutionX = 10480;
int32 MaxAllowableResolutionY = 10480;
int32 MinAllowableRefreshRate = 0;
int32 MaxAllowableRefreshRate = 10480;
if (MaxAllowableResolutionX == 0) //-V547
{
MaxAllowableResolutionX = 10480;
}
if (MaxAllowableResolutionY == 0) //-V547
{
MaxAllowableResolutionY = 10480;
}
if (MaxAllowableRefreshRate == 0) //-V547
{
MaxAllowableRefreshRate = 10480;
}
FD3D12Adapter& ChosenAdapter = GetAdapter();
HRESULT HResult = S_OK;
TRefCountPtr<IDXGIAdapter> Adapter;
//TODO: should only be called on display out device
HResult = ChosenAdapter.EnumAdapters(Adapter.GetInitReference());
if (DXGI_ERROR_NOT_FOUND == HResult)
{
return false;
}
if (FAILED(HResult))
{
return false;
}
// get the description of the adapter
DXGI_ADAPTER_DESC AdapterDesc;
if (FAILED(Adapter->GetDesc(&AdapterDesc)))
{
return false;
}
int32 CurrentOutput = 0;
do
{
TRefCountPtr<IDXGIOutput> Output;
HResult = Adapter->EnumOutputs(CurrentOutput, Output.GetInitReference());
if (DXGI_ERROR_NOT_FOUND == HResult)
{
break;
}
if (FAILED(HResult))
{
return false;
}
// TODO: GetDisplayModeList is a terribly SLOW call. It can take up to a second per invocation.
// We might want to work around some DXGI badness here.
DXGI_FORMAT Format = DXGI_FORMAT_R8G8B8A8_UNORM;
uint32 NumModes = 0;
HResult = Output->GetDisplayModeList(Format, 0, &NumModes, nullptr);
if (HResult == DXGI_ERROR_NOT_FOUND)
{
++CurrentOutput;
continue;
}
else if (HResult == DXGI_ERROR_NOT_CURRENTLY_AVAILABLE)
{
UE_LOG(LogD3D12RHI, Warning,
TEXT("RHIGetAvailableResolutions() can not be used over a remote desktop configuration")
);
return false;
}
if (NumModes > 0)
{
DXGI_MODE_DESC* ModeList = new DXGI_MODE_DESC[NumModes];
VERIFYD3D12RESULT(Output->GetDisplayModeList(Format, 0, &NumModes, ModeList));
for (uint32 m = 0; m < NumModes; m++)
{
CA_SUPPRESS(6385);
if (((int32)ModeList[m].Width >= MinAllowableResolutionX) &&
((int32)ModeList[m].Width <= MaxAllowableResolutionX) &&
((int32)ModeList[m].Height >= MinAllowableResolutionY) &&
((int32)ModeList[m].Height <= MaxAllowableResolutionY)
)
{
bool bAddIt = true;
if (bIgnoreRefreshRate == false)
{
if (((int32)ModeList[m].RefreshRate.Numerator < MinAllowableRefreshRate * ModeList[m].RefreshRate.Denominator) ||
((int32)ModeList[m].RefreshRate.Numerator > MaxAllowableRefreshRate * ModeList[m].RefreshRate.Denominator)
)
{
continue;
}
}
else
{
// See if it is in the list already
for (int32 CheckIndex = 0; CheckIndex < Resolutions.Num(); CheckIndex++)
{
FScreenResolutionRHI& CheckResolution = Resolutions[CheckIndex];
if ((CheckResolution.Width == ModeList[m].Width) &&
(CheckResolution.Height == ModeList[m].Height))
{
// Already in the list...
bAddIt = false;
break;
}
}
}
if (bAddIt)
{
// Add the mode to the list
int32 Temp2Index = Resolutions.AddZeroed();
FScreenResolutionRHI& ScreenResolution = Resolutions[Temp2Index];
ScreenResolution.Width = ModeList[m].Width;
ScreenResolution.Height = ModeList[m].Height;
ScreenResolution.RefreshRate = ModeList[m].RefreshRate.Numerator / ModeList[m].RefreshRate.Denominator;
}
}
}
delete[] ModeList;
}
else
{
UE_LOG(LogD3D12RHI, Warning, TEXT("No display modes found for the standard format DXGI_FORMAT_R8G8B8A8_UNORM!"));
}
++CurrentOutput;
// TODO: Cap at 1 for default output
} while (CurrentOutput < 1);
return Resolutions.Num() > 0;
}
void FWindowsD3D12Adapter::CreateCommandSignatures()
{
ID3D12Device* Device = GetD3DDevice();
#if D3D12_RHI_RAYTRACING
if (GRHISupportsRayTracing)
{
D3D12_FEATURE_DATA_D3D12_OPTIONS5 D3D12Caps5 = {};
if (SUCCEEDED(Device->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS5, &D3D12Caps5, sizeof(D3D12Caps5))))
{
if (D3D12Caps5.RaytracingTier >= D3D12_RAYTRACING_TIER_1_1)
{
GRHISupportsRayTracingDispatchIndirect = true;
}
}
}
if (GRHISupportsRayTracingDispatchIndirect)
{
D3D12_COMMAND_SIGNATURE_DESC SignatureDesc = {};
SignatureDesc.NumArgumentDescs = 1;
SignatureDesc.ByteStride = sizeof(D3D12_DISPATCH_RAYS_DESC);
SignatureDesc.NodeMask = FRHIGPUMask::All().GetNative();
D3D12_INDIRECT_ARGUMENT_DESC ArgumentDesc[1] = {};
ArgumentDesc[0].Type = D3D12_INDIRECT_ARGUMENT_TYPE_DISPATCH_RAYS;
SignatureDesc.pArgumentDescs = ArgumentDesc;
checkf(DispatchRaysIndirectCommandSignature == nullptr, TEXT("Indirect ray tracing dispatch command signature is expected to be initialized by FWindowsD3D12Adapter."));
VERIFYD3D12RESULT(Device->CreateCommandSignature(&SignatureDesc, nullptr, IID_PPV_ARGS(DispatchRaysIndirectCommandSignature.GetInitReference())));
}
#endif // D3D12_RHI_RAYTRACING
// Create windows-specific indirect dispatch command signatures
{
D3D12_COMMAND_SIGNATURE_DESC CommandSignatureDesc = {};
CommandSignatureDesc.NumArgumentDescs = 1;
CommandSignatureDesc.ByteStride = sizeof(D3D12_DISPATCH_ARGUMENTS);
CommandSignatureDesc.NodeMask = FRHIGPUMask::All().GetNative();
D3D12_INDIRECT_ARGUMENT_DESC IndirectParameterDesc[1] = {};
IndirectParameterDesc[0].Type = D3D12_INDIRECT_ARGUMENT_TYPE_DISPATCH;
CommandSignatureDesc.pArgumentDescs = IndirectParameterDesc;
checkf(DispatchIndirectGraphicsCommandSignature == nullptr, TEXT("Indirect graphics dispatch command signature is expected to be initialized by FWindowsD3D12Adapter."));
VERIFYD3D12RESULT(Device->CreateCommandSignature(&CommandSignatureDesc, nullptr, IID_PPV_ARGS(DispatchIndirectGraphicsCommandSignature.GetInitReference())));
checkf(DispatchIndirectComputeCommandSignature == nullptr, TEXT("Indirect compute dispatch command signature is expected to be initialized by FWindowsD3D12Adapter."));
VERIFYD3D12RESULT(Device->CreateCommandSignature(&CommandSignatureDesc, nullptr, IID_PPV_ARGS(DispatchIndirectComputeCommandSignature.GetInitReference())));
#if PLATFORM_SUPPORTS_MESH_SHADERS
if (GRHISupportsMeshShadersTier0)
{
IndirectParameterDesc[0].Type = D3D12_INDIRECT_ARGUMENT_TYPE_DISPATCH_MESH;
CommandSignatureDesc.ByteStride = sizeof(D3D12_DISPATCH_ARGUMENTS);
VERIFYD3D12RESULT(Device->CreateCommandSignature(&CommandSignatureDesc, nullptr, IID_PPV_ARGS(DispatchIndirectMeshCommandSignature.GetInitReference())));
}
#endif
}
// Create all the generic / cross-platform command signatures
FD3D12Adapter::CreateCommandSignatures();
}
FD3D12DiagnosticBuffer::FD3D12DiagnosticBuffer(FD3D12Queue& Queue)
{
const static FLazyName D3D12DiagnosticBufferName(TEXT("FD3D12DiagnosticBuffer"));
const static FLazyName CreateDiagnosticBufferName(TEXT("FD3D12Device::CreateDiagnosticBuffer"));
UE_TRACE_METADATA_SCOPE_ASSET_FNAME(D3D12DiagnosticBufferName, CreateDiagnosticBufferName, NAME_None);
// Create the platform-specific diagnostic buffer
FString Name = FString::Printf(TEXT("DiagnosticBuffer (Queue: 0x%p)"), &Queue);
extern TAutoConsoleVariable<int32> CVarD3D12ExtraDiagnosticBufferMemory;
const D3D12_RESOURCE_DESC Desc = CD3DX12_RESOURCE_DESC::Buffer(SizeInBytes + FMath::Max(0, CVarD3D12ExtraDiagnosticBufferMemory.GetValueOnAnyThread()), D3D12_RESOURCE_FLAG_ALLOW_CROSS_ADAPTER);
ID3D12Device3* D3D12Device3 = Queue.Device->GetParentAdapter()->GetD3DDevice3();
if (!D3D12Device3)
{
UE_LOG(LogD3D12RHI, Warning, TEXT("[GPUBreadCrumb] ID3D12Device3 not available (only available on Windows 10 1709+)"));
return;
}
D3D12_FEATURE_DATA_EXISTING_HEAPS ExistingHeapSupport{};
if (FAILED(D3D12Device3->CheckFeatureSupport(D3D12_FEATURE_EXISTING_HEAPS, &ExistingHeapSupport, sizeof(ExistingHeapSupport))) || !ExistingHeapSupport.Supported)
{
UE_LOG(LogD3D12RHI, Warning, TEXT("[GPUBreadCrumb] D3D12_FEATURE_EXISTING_HEAPS is not supported."));
return;
}
// Allocate persistent CPU readable memory which will still be valid after a device lost and wrap this data in a placed resource so the GPU command list can write to it
Data = static_cast<FQueue*>(VirtualAlloc(nullptr, Desc.Width, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE));
if (!Data)
{
UE_LOG(LogD3D12RHI, Warning, TEXT("[GPUBreadCrumb] Failed to VirtualAlloc resource memory"));
return;
}
FMemory::Memzero(Data, Desc.Width);
ID3D12Heap* D3D12Heap = nullptr;
HRESULT hr = D3D12Device3->OpenExistingHeapFromAddress(Data, IID_PPV_ARGS(&D3D12Heap));
if (FAILED(hr))
{
VirtualFree(Data, 0, MEM_RELEASE);
Data = nullptr;
UE_LOG(LogD3D12RHI, Warning, TEXT("[GPUBreadCrumb] Failed to OpenExistingHeapFromAddress, error: %x"), hr);
return;
}
Heap = new FD3D12Heap(Queue.Device, Queue.Device->GetGPUMask());
Heap->SetHeap(D3D12Heap, TEXT("DiagnosticBuffer"));
hr = Queue.Device->GetParentAdapter()->CreatePlacedResource(
Desc,
Heap.GetReference(),
0,
ED3D12Access::CopyDest,
nullptr,
Resource.GetInitReference(),
*Name,
false);
if (FAILED(hr))
{
Heap.SafeRelease();
VirtualFree(Data, 0, MEM_RELEASE);
Data = nullptr;
UE_LOG(LogD3D12RHI, Warning, TEXT("[GPUBreadCrumb] Failed to CreatePlacedResource, error: %x"), hr);
return;
}
UE_LOG(LogD3D12RHI, Log, TEXT("[GPUBreadCrumb] Successfully setup breadcrumb resource for %s"), *Name);
GpuAddress = Resource->GetGPUVirtualAddress();
#if WITH_RHI_BREADCRUMBS
Data->MarkerIn = 0;
Data->MarkerOut = 0;
#endif
}
FD3D12DiagnosticBuffer::~FD3D12DiagnosticBuffer()
{
Resource.SafeRelease();
Heap.SafeRelease();
if (Data)
{
VirtualFree(Data, 0, MEM_RELEASE);
Data = nullptr;
}
GpuAddress = 0;
}
void FD3D12DynamicRHI::ProcessDeferredDeletionQueue_Platform()
{
// Nothing Windows-specific here.
}
HRESULT FD3D12Device::CreateCommandList(
UINT nodeMask,
D3D12_COMMAND_LIST_TYPE type,
ID3D12CommandAllocator* pCommandAllocator,
ID3D12PipelineState* pInitialState,
REFIID riid,
void** ppCommandList
)
{
return GetDevice()->CreateCommandList(
nodeMask,
type,
pCommandAllocator,
pInitialState,
riid,
ppCommandList
);
}
void FD3D12Queue::ExecuteCommandLists(TArrayView<ID3D12CommandList*> D3DCommandLists
#if ENABLE_RESIDENCY_MANAGEMENT
, TArrayView<FD3D12ResidencySet*> ResidencySets
#endif
)
{
TRACE_CPUPROFILER_EVENT_SCOPE(FD3D12Queue::ExecuteCommandLists);
#if ENABLE_RESIDENCY_MANAGEMENT
check(D3DCommandLists.Num() == ResidencySets.Num());
if (GEnableResidencyManagement)
{
VERIFYD3D12RESULT(Device->GetResidencyManager().ExecuteCommandLists(
D3DCommandQueue,
D3DCommandLists.GetData(),
ResidencySets.GetData(),
D3DCommandLists.Num()
));
}
else
#endif
{
D3DCommandQueue->ExecuteCommandLists(
D3DCommandLists.Num(),
D3DCommandLists.GetData()
);
}
}