Files
UnrealEngine/Engine/Source/Runtime/Windows/D3D11RHI/Private/D3D11Device.cpp
2025-05-18 13:04:45 +08:00

840 lines
30 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
/*=============================================================================
D3D11Device.cpp: D3D device RHI implementation.
=============================================================================*/
#include "D3D11RHIPrivate.h"
#include "D3D11ConstantBuffer.h"
#include "Misc/CommandLine.h"
#include "Misc/ConfigCacheIni.h"
#include "Modules/ModuleManager.h"
#include "Windows/AllowWindowsPlatformTypes.h"
#include <delayimp.h>
#if WITH_AMD_AGS
#include "amd_ags.h"
#endif
#include "Windows/HideWindowsPlatformTypes.h"
bool D3D11RHI_ShouldAllowAsyncResourceCreation()
{
static bool bAllowAsyncResourceCreation = !FParse::Param(FCommandLine::Get(),TEXT("nod3dasync"));
return bAllowAsyncResourceCreation;
}
IMPLEMENT_MODULE(FD3D11DynamicRHIModule, D3D11RHI);
static TAutoConsoleVariable<int32> CVarD3D11UseD24(
TEXT("r.D3D11.Depth24Bit"),
0,
TEXT("0: Use 32-bit float depth buffer\n1: Use 24-bit fixed point depth buffer(default)\n"),
ECVF_ReadOnly
);
TAutoConsoleVariable<int32> CVarD3D11ZeroBufferSizeInMB(
TEXT("d3d11.ZeroBufferSizeInMB"),
4,
TEXT("The D3D11 RHI needs a static allocation of zeroes to use when streaming textures asynchronously. It should be large enough to support the largest mipmap you need to stream. The default is 4MB."),
ECVF_ReadOnly
);
FD3D11DynamicRHI::FD3D11DynamicRHI(IDXGIFactory1* InDXGIFactory1, D3D_FEATURE_LEVEL InFeatureLevel, const FD3D11Adapter& InAdapter)
: DXGIFactory1(InDXGIFactory1)
, FeatureLevel(InFeatureLevel)
, AmdAgsContext(NULL)
#if INTEL_EXTENSIONS
, IntelExtensionContext(nullptr)
, bIntelSupportsUAVOverlap(false)
#endif
, bCurrentDepthStencilStateIsReadOnly(false)
, CurrentDepthTexture(NULL)
, NumSimultaneousRenderTargets(0)
, NumUAVs(0)
, PresentCounter(0)
, CurrentDSVAccessType(FExclusiveDepthStencil::DepthWrite_StencilWrite)
, bDiscardSharedConstants(false)
#if (RHI_NEW_GPU_PROFILER == 0)
, GPUProfilingData(this)
#endif
, Adapter(InAdapter)
{
// This should be called once at the start
check(Adapter.IsValid());
check(IsInGameThread());
check(!GIsThreadedRendering);
// Allocate a buffer of zeroes. This is used when we need to pass D3D memory
// that we don't care about and will overwrite with valid data in the future.
ZeroBufferSize = FMath::Max(CVarD3D11ZeroBufferSizeInMB.GetValueOnAnyThread(), 0) * (1 << 20);
ZeroBuffer = FMemory::Malloc(ZeroBufferSize);
FMemory::Memzero(ZeroBuffer,ZeroBufferSize);
GPoolSizeVRAMPercentage = 0;
GTexturePoolSize = 0;
GConfig->GetInt( TEXT( "TextureStreaming" ), TEXT( "PoolSizeVRAMPercentage" ), GPoolSizeVRAMPercentage, GEngineIni );
// Initialize the RHI capabilities.
check(FeatureLevel >= D3D_FEATURE_LEVEL_11_0);
TRefCountPtr<IDXGIFactory5> Factory5;
HRESULT HResult = DXGIFactory1->QueryInterface(IID_PPV_ARGS(Factory5.GetInitReference()));
if (SUCCEEDED(HResult))
{
bDXGISupportsHDR = true;
}
else
{
bDXGISupportsHDR = false;
}
// Initialize the platform pixel format map.
GPixelFormats[ PF_Unknown ].PlatformFormat = DXGI_FORMAT_UNKNOWN;
GPixelFormats[ PF_A32B32G32R32F ].PlatformFormat = DXGI_FORMAT_R32G32B32A32_FLOAT;
GPixelFormats[ PF_B8G8R8A8 ].PlatformFormat = DXGI_FORMAT_B8G8R8A8_TYPELESS;
GPixelFormats[ PF_G8 ].PlatformFormat = DXGI_FORMAT_R8_UNORM;
GPixelFormats[ PF_G16 ].PlatformFormat = DXGI_FORMAT_R16_UNORM;
GPixelFormats[ PF_DXT1 ].PlatformFormat = DXGI_FORMAT_BC1_TYPELESS;
GPixelFormats[ PF_DXT3 ].PlatformFormat = DXGI_FORMAT_BC2_TYPELESS;
GPixelFormats[ PF_DXT5 ].PlatformFormat = DXGI_FORMAT_BC3_TYPELESS;
GPixelFormats[ PF_BC4 ].PlatformFormat = DXGI_FORMAT_BC4_UNORM;
GPixelFormats[ PF_UYVY ].PlatformFormat = DXGI_FORMAT_UNKNOWN; // TODO: Not supported in D3D11
if (CVarD3D11UseD24.GetValueOnAnyThread())
{
GPixelFormats[PF_DepthStencil].PlatformFormat = DXGI_FORMAT_R24G8_TYPELESS;
GPixelFormats[PF_DepthStencil].BlockBytes = 4;
GPixelFormats[PF_DepthStencil].bIs24BitUnormDepthStencil = true;
GPixelFormats[PF_X24_G8].PlatformFormat = DXGI_FORMAT_X24_TYPELESS_G8_UINT;
GPixelFormats[PF_X24_G8].BlockBytes = 4;
}
else
{
GPixelFormats[PF_DepthStencil].PlatformFormat = DXGI_FORMAT_R32G8X24_TYPELESS;
GPixelFormats[PF_DepthStencil].BlockBytes = 5;
GPixelFormats[PF_DepthStencil].bIs24BitUnormDepthStencil = false;
GPixelFormats[PF_X24_G8].PlatformFormat = DXGI_FORMAT_X32_TYPELESS_G8X24_UINT;
GPixelFormats[PF_X24_G8].BlockBytes = 5;
}
GPixelFormats[ PF_DepthStencil ].Supported = true;
GPixelFormats[ PF_X24_G8 ].Supported = true;
GPixelFormats[ PF_ShadowDepth ].PlatformFormat = DXGI_FORMAT_R16_TYPELESS;
GPixelFormats[ PF_ShadowDepth ].BlockBytes = 2;
GPixelFormats[ PF_ShadowDepth ].Supported = true;
GPixelFormats[ PF_R32_FLOAT ].PlatformFormat = DXGI_FORMAT_R32_FLOAT;
GPixelFormats[ PF_G16R16 ].PlatformFormat = DXGI_FORMAT_R16G16_UNORM;
GPixelFormats[ PF_G16R16F ].PlatformFormat = DXGI_FORMAT_R16G16_FLOAT;
GPixelFormats[ PF_G16R16F_FILTER].PlatformFormat = DXGI_FORMAT_R16G16_FLOAT;
GPixelFormats[ PF_G32R32F ].PlatformFormat = DXGI_FORMAT_R32G32_FLOAT;
GPixelFormats[ PF_A2B10G10R10 ].PlatformFormat = DXGI_FORMAT_R10G10B10A2_UNORM;
GPixelFormats[ PF_A16B16G16R16 ].PlatformFormat = DXGI_FORMAT_R16G16B16A16_UNORM;
GPixelFormats[ PF_D24 ].PlatformFormat = DXGI_FORMAT_R24G8_TYPELESS;
GPixelFormats[ PF_R16F ].PlatformFormat = DXGI_FORMAT_R16_FLOAT;
GPixelFormats[ PF_R16F_FILTER ].PlatformFormat = DXGI_FORMAT_R16_FLOAT;
GPixelFormats[ PF_FloatRGB ].PlatformFormat = DXGI_FORMAT_R11G11B10_FLOAT;
GPixelFormats[ PF_FloatRGB ].BlockBytes = 4;
GPixelFormats[ PF_FloatRGBA ].PlatformFormat = DXGI_FORMAT_R16G16B16A16_FLOAT;
GPixelFormats[ PF_FloatRGBA ].BlockBytes = 8;
GPixelFormats[ PF_FloatR11G11B10].PlatformFormat = DXGI_FORMAT_R11G11B10_FLOAT;
GPixelFormats[ PF_FloatR11G11B10].BlockBytes = 4;
GPixelFormats[ PF_FloatR11G11B10].Supported = true;
GPixelFormats[ PF_V8U8 ].PlatformFormat = DXGI_FORMAT_R8G8_SNORM;
GPixelFormats[ PF_BC5 ].PlatformFormat = DXGI_FORMAT_BC5_UNORM;
GPixelFormats[ PF_A1 ].PlatformFormat = DXGI_FORMAT_R1_UNORM; // Not supported for rendering.
GPixelFormats[ PF_A8 ].PlatformFormat = DXGI_FORMAT_A8_UNORM;
GPixelFormats[ PF_R32_UINT ].PlatformFormat = DXGI_FORMAT_R32_UINT;
GPixelFormats[ PF_R32_SINT ].PlatformFormat = DXGI_FORMAT_R32_SINT;
GPixelFormats[ PF_R16_UINT ].PlatformFormat = DXGI_FORMAT_R16_UINT;
GPixelFormats[ PF_R16_SINT ].PlatformFormat = DXGI_FORMAT_R16_SINT;
GPixelFormats[ PF_R16G16B16A16_UINT].PlatformFormat = DXGI_FORMAT_R16G16B16A16_UINT;
GPixelFormats[ PF_R16G16B16A16_SINT].PlatformFormat = DXGI_FORMAT_R16G16B16A16_SINT;
GPixelFormats[ PF_R5G6B5_UNORM ].PlatformFormat = DXGI_FORMAT_B5G6R5_UNORM;
GPixelFormats[ PF_R5G6B5_UNORM ].Supported = true;
GPixelFormats[ PF_B5G5R5A1_UNORM].PlatformFormat = DXGI_FORMAT_B5G5R5A1_UNORM;
GPixelFormats[ PF_B5G5R5A1_UNORM].Supported = true;
GPixelFormats[ PF_R8G8B8A8 ].PlatformFormat = DXGI_FORMAT_R8G8B8A8_TYPELESS;
GPixelFormats[ PF_R8G8B8A8_UINT ].PlatformFormat = DXGI_FORMAT_R8G8B8A8_UINT;
GPixelFormats[ PF_R8G8B8A8_SNORM].PlatformFormat = DXGI_FORMAT_R8G8B8A8_SNORM;
GPixelFormats[ PF_R8G8 ].PlatformFormat = DXGI_FORMAT_R8G8_UNORM;
GPixelFormats[ PF_R32G32B32A32_UINT].PlatformFormat = DXGI_FORMAT_R32G32B32A32_UINT;
GPixelFormats[ PF_R16G16_UINT ].PlatformFormat = DXGI_FORMAT_R16G16_UINT;
GPixelFormats[ PF_R16G16_SINT ].PlatformFormat = DXGI_FORMAT_R16G16_SINT;
GPixelFormats[ PF_R32G32_UINT ].PlatformFormat = DXGI_FORMAT_R32G32_UINT;
GPixelFormats[ PF_BC6H ].PlatformFormat = DXGI_FORMAT_BC6H_UF16;
GPixelFormats[ PF_BC7 ].PlatformFormat = DXGI_FORMAT_BC7_TYPELESS;
GPixelFormats[ PF_R8_UINT ].PlatformFormat = DXGI_FORMAT_R8_UINT;
GPixelFormats[ PF_R8 ].PlatformFormat = DXGI_FORMAT_R8_UNORM;
GPixelFormats[PF_R16G16B16A16_UNORM].PlatformFormat = DXGI_FORMAT_R16G16B16A16_UNORM;
GPixelFormats[PF_R16G16B16A16_SNORM].PlatformFormat = DXGI_FORMAT_R16G16B16A16_SNORM;
GPixelFormats[PF_NV12 ].PlatformFormat = DXGI_FORMAT_NV12;
GPixelFormats[PF_NV12 ].Supported = true;
GPixelFormats[PF_G16R16_SNORM ].PlatformFormat = DXGI_FORMAT_R16G16_SNORM;
GPixelFormats[PF_R8G8_UINT ].PlatformFormat = DXGI_FORMAT_R8G8_UINT;
GPixelFormats[PF_R32G32B32_UINT ].PlatformFormat = DXGI_FORMAT_R32G32B32_UINT;
GPixelFormats[PF_R32G32B32_SINT ].PlatformFormat = DXGI_FORMAT_R32G32B32_SINT;
GPixelFormats[PF_R32G32B32F ].PlatformFormat = DXGI_FORMAT_R32G32B32_FLOAT;
GPixelFormats[PF_R8_SINT ].PlatformFormat = DXGI_FORMAT_R8_SINT;
GPixelFormats[PF_P010 ].PlatformFormat = DXGI_FORMAT_P010;
GPixelFormats[PF_P010 ].Supported = true;
GSupportsSeparateRenderTargetBlendState = true;
GMaxTextureDimensions = D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION;
GMaxCubeTextureDimensions = D3D11_REQ_TEXTURECUBE_DIMENSION;
GMaxTextureArrayLayers = D3D11_REQ_TEXTURE2D_ARRAY_AXIS_DIMENSION;
GRHIMaxConstantBufferByteSize = MAX_GLOBAL_CONSTANT_BUFFER_BYTE_SIZE;
GRHISupportsMSAADepthSampleAccess = true;
GRHISupportsRHIThread = true;
GMaxTextureMipCount = FMath::CeilLogTwo( GMaxTextureDimensions ) + 1;
GMaxTextureMipCount = FMath::Min<int32>( MAX_TEXTURE_MIP_COUNT, GMaxTextureMipCount );
GMaxShadowDepthBufferSizeX = GMaxTextureDimensions;
GMaxShadowDepthBufferSizeY = GMaxTextureDimensions;
GSupportsTimestampRenderQueries = true;
GRHIMaxDispatchThreadGroupsPerDimension.X = D3D11_CS_DISPATCH_MAX_THREAD_GROUPS_PER_DIMENSION;
GRHIMaxDispatchThreadGroupsPerDimension.Y = D3D11_CS_DISPATCH_MAX_THREAD_GROUPS_PER_DIMENSION;
GRHIMaxDispatchThreadGroupsPerDimension.Z = D3D11_CS_DISPATCH_MAX_THREAD_GROUPS_PER_DIMENSION;
// All D3D11.1 hardware on Windows 8+ supports binding UAVs to Vertex Shaders.
// Enable run-time support if corresponding bit is set in DDSPI.
GRHIGlobals.SupportsVertexShaderUAVs = true;
GRHIGlobals.NeedsShaderUnbinds = true;
GRHITransitionPrivateData_SizeInBytes = sizeof(FD3D11TransitionData);
GRHITransitionPrivateData_AlignInBytes = alignof(FD3D11TransitionData);
GRHIGlobals.MaxViewSizeBytesForNonTypedBuffer = 1 << 27;
GRHIGlobals.MaxViewDimensionForTypedBuffer = 1 << 27;
// Initialize the constant buffers.
InitConstantBuffers();
for (int32 Frequency = 0; Frequency < SF_NumStandardFrequencies; ++Frequency)
{
DirtyUniformBuffers[Frequency] = 0;
for (int32 BindIndex = 0; BindIndex < MAX_UNIFORM_BUFFERS_PER_SHADER_STAGE; ++BindIndex)
{
BoundUniformBuffers[Frequency][BindIndex] = nullptr;
}
}
StaticUniformBuffers.AddZeroed(FUniformBufferStaticSlotRegistry::Get().GetSlotCount());
}
FD3D11DynamicRHI::~FD3D11DynamicRHI()
{
// Removed until shutdown crashes in exception handler are fixed.
#if WITH_DX_PERF
//check(Direct3DDeviceIMAnnotation == nullptr);
#endif
//check(Direct3DDeviceIMContext == nullptr);
//check(Direct3DDevice == nullptr);
}
void FD3D11DynamicRHI::Shutdown()
{
UE_LOG(LogD3D11RHI, Log, TEXT("Shutdown"));
check(IsInGameThread() && IsInRenderingThread()); // require that the render thread has been shut down
// Cleanup the D3D device.
CleanupD3DDevice();
#if (RHI_NEW_GPU_PROFILER == 0)
// Release buffered timestamp queries
GPUProfilingData.FrameTiming.ReleaseResource();
#endif
// Release the buffer of zeroes.
FMemory::Free(ZeroBuffer);
ZeroBuffer = NULL;
ZeroBufferSize = 0;
}
#if WITH_RHI_BREADCRUMBS
void FD3D11DynamicRHI::RHIBeginBreadcrumbGPU(FRHIBreadcrumbNode* Breadcrumb)
{
#if NV_AFTERMATH
UE::RHICore::Nvidia::Aftermath::D3D11::BeginBreadcrumb(AftermathHandle, Breadcrumb);
#endif
// Only emit formatted strings to platform APIs when requested.
if (ShouldEmitBreadcrumbs())
{
const TCHAR* NameStr = nullptr;
FRHIBreadcrumb::FBuffer Buffer;
auto GetNameStr = [&]()
{
if (!NameStr)
{
NameStr = Breadcrumb->GetTCHAR(Buffer);
}
return NameStr;
};
#if WITH_DX_PERF
Direct3DDeviceIMAnnotation->BeginEvent(GetNameStr());
#endif
}
#if RHI_NEW_GPU_PROFILER
FlushProfilerStats();
auto& Event = EmplaceProfilerEvent<UE::RHI::GPUProfiler::FEvent::FBeginBreadcrumb>(Breadcrumb);
InsertProfilerTimestamp(&Event.GPUTimestampTOP);
#else
if (GPUProfilingData.IsProfilingGPU())
{
// @todo dev-pr avoid TCHAR -> ANSI conversion
FRHIBreadcrumb::FBuffer Buffer;
TCHAR const* Name = Breadcrumb->GetTCHAR(Buffer);
GPUProfilingData.PushEvent(Name, FColor::White);
}
#endif
}
void FD3D11DynamicRHI::RHIEndBreadcrumbGPU(FRHIBreadcrumbNode* Breadcrumb)
{
#if RHI_NEW_GPU_PROFILER
FlushProfilerStats();
auto& Event = EmplaceProfilerEvent<UE::RHI::GPUProfiler::FEvent::FEndBreadcrumb>(Breadcrumb);
InsertProfilerTimestamp(&Event.GPUTimestampBOP);
#else
if (GPUProfilingData.IsProfilingGPU())
{
GPUProfilingData.PopEvent();
}
#endif
// Only emit formatted strings to platform APIs when requested.
if (ShouldEmitBreadcrumbs())
{
#if WITH_DX_PERF
Direct3DDeviceIMAnnotation->EndEvent();
#endif
}
#if NV_AFTERMATH
UE::RHICore::Nvidia::Aftermath::D3D11::EndBreadcrumb(AftermathHandle, Breadcrumb);
#endif
}
#endif // WITH_RHI_BREADCRUMBS
/**
* Returns a supported screen resolution that most closely matches the input.
* @param Width - Input: Desired resolution width in pixels. Output: A width that the platform supports.
* @param Height - Input: Desired resolution height in pixels. Output: A height that the platform supports.
*/
void FD3D11DynamicRHI::RHIGetSupportedResolution( uint32 &Width, uint32 &Height )
{
uint32 InitializedMode = false;
DXGI_MODE_DESC BestMode;
BestMode.Width = 0;
BestMode.Height = 0;
{
HRESULT HResult = S_OK;
// Enumerate outputs for this adapter
// TODO: Cap at 1 for default output
for(uint32 o = 0;o < 1; o++)
{
TRefCountPtr<IDXGIOutput> Output;
HResult = Adapter.DXGIAdapter->EnumOutputs(o, Output.GetInitReference());
if(DXGI_ERROR_NOT_FOUND == HResult)
{
break;
}
if(FAILED(HResult))
{
return;
}
// 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,NULL);
if(HResult == DXGI_ERROR_NOT_FOUND)
{
return;
}
else if(HResult == DXGI_ERROR_NOT_CURRENTLY_AVAILABLE)
{
UE_LOG(LogD3D11RHI, Fatal,
TEXT("This application cannot be run over a remote desktop configuration")
);
return;
}
DXGI_MODE_DESC* ModeList = new DXGI_MODE_DESC[ NumModes ];
VERIFYD3D11RESULT(Output->GetDisplayModeList(Format,0,&NumModes,ModeList));
for(uint32 m = 0;m < NumModes;m++)
{
// Search for the best mode
// Suppress static analysis warnings about a potentially out-of-bounds read access to ModeList. This is a false positive - Index is always within range.
CA_SUPPRESS( 6385 );
bool IsEqualOrBetterWidth = FMath::Abs((int32)ModeList[m].Width - (int32)Width) <= FMath::Abs((int32)BestMode.Width - (int32)Width);
bool IsEqualOrBetterHeight = FMath::Abs((int32)ModeList[m].Height - (int32)Height) <= FMath::Abs((int32)BestMode.Height - (int32)Height);
if(!InitializedMode || (IsEqualOrBetterWidth && IsEqualOrBetterHeight))
{
BestMode = ModeList[m];
InitializedMode = true;
}
}
delete[] ModeList;
}
}
check(InitializedMode);
Width = BestMode.Width;
Height = BestMode.Height;
}
void FD3D11DynamicRHI::GetBestSupportedMSAASetting( DXGI_FORMAT PlatformFormat, uint32 MSAACount, uint32& OutBestMSAACount, uint32& OutMSAAQualityLevels )
{
// start counting down from current setting (indicated the current "best" count) and move down looking for support
for(uint32 IndexCount = MSAACount;IndexCount > 0;IndexCount--)
{
uint32 NumMultiSampleQualities = 0;
if( SUCCEEDED(Direct3DDevice->CheckMultisampleQualityLevels(PlatformFormat,IndexCount,&NumMultiSampleQualities)) && NumMultiSampleQualities > 0 )
{
OutBestMSAACount = IndexCount;
OutMSAAQualityLevels = NumMultiSampleQualities;
break;
}
}
}
uint32 FD3D11DynamicRHI::GetMaxMSAAQuality(uint32 SampleCount)
{
if(SampleCount <= DX_MAX_MSAA_COUNT)
{
// 0 has better quality (a more even distribution)
// higher quality levels might be useful for non box filtered AA or when using weighted samples
return 0;
// return AvailableMSAAQualities[SampleCount];
}
// not supported
return 0xffffffff;
}
struct FFormatSupport
{
D3D11_FORMAT_SUPPORT FormatSupport;
D3D11_FORMAT_SUPPORT2 FormatSupport2;
};
static FFormatSupport GetFormatSupport(FD3D11Device* InDevice, DXGI_FORMAT InFormat)
{
FFormatSupport Result{};
{
D3D11_FEATURE_DATA_FORMAT_SUPPORT FormatSupport{};
FormatSupport.InFormat = InFormat;
HRESULT SupportHR = InDevice->CheckFeatureSupport(D3D11_FEATURE_FORMAT_SUPPORT, &FormatSupport, sizeof(FormatSupport));
if (SUCCEEDED(SupportHR))
{
Result.FormatSupport = (D3D11_FORMAT_SUPPORT)FormatSupport.OutFormatSupport;
}
}
{
D3D11_FEATURE_DATA_FORMAT_SUPPORT2 FormatSupport2{};
FormatSupport2.InFormat = InFormat;
HRESULT Support2HR = InDevice->CheckFeatureSupport(D3D11_FEATURE_FORMAT_SUPPORT2, &FormatSupport2, sizeof(FormatSupport2));
if (SUCCEEDED(Support2HR))
{
Result.FormatSupport2 = (D3D11_FORMAT_SUPPORT2)FormatSupport2.OutFormatSupport2;
}
}
return Result;
}
void FD3D11DynamicRHI::SetupAfterDeviceCreation()
{
for (uint32 FormatIndex = PF_Unknown; FormatIndex < PF_MAX; FormatIndex++)
{
FPixelFormatInfo& PixelFormatInfo = GPixelFormats[FormatIndex];
const DXGI_FORMAT PlatformFormat = static_cast<DXGI_FORMAT>(PixelFormatInfo.PlatformFormat);
const DXGI_FORMAT UAVFormat = UE::DXGIUtilities::FindUnorderedAccessFormat(PlatformFormat);
EPixelFormatCapabilities Capabilities = EPixelFormatCapabilities::None;
if (PlatformFormat != DXGI_FORMAT_UNKNOWN)
{
const FFormatSupport FormatSupport = GetFormatSupport(Direct3DDevice, PlatformFormat);
const FFormatSupport SRVFormatSupport = GetFormatSupport(Direct3DDevice, UE::DXGIUtilities::FindShaderResourceFormat(PlatformFormat, false));
const FFormatSupport UAVFormatSupport = GetFormatSupport(Direct3DDevice, UE::DXGIUtilities::FindUnorderedAccessFormat(PlatformFormat));
const FFormatSupport RTVFormatSupport = GetFormatSupport(Direct3DDevice, UE::DXGIUtilities::FindShaderResourceFormat(PlatformFormat, false));
const FFormatSupport DSVFormatSupport = GetFormatSupport(Direct3DDevice, UE::DXGIUtilities::FindDepthStencilFormat(PlatformFormat));
auto ConvertCap1 = [&Capabilities](const FFormatSupport& InSupport, EPixelFormatCapabilities UnrealCap, D3D11_FORMAT_SUPPORT InFlag)
{
if (EnumHasAllFlags(InSupport.FormatSupport, InFlag))
{
EnumAddFlags(Capabilities, UnrealCap);
}
};
auto ConvertCap2 = [&Capabilities](const FFormatSupport& InSupport, EPixelFormatCapabilities UnrealCap, D3D11_FORMAT_SUPPORT2 InFlag)
{
if (EnumHasAllFlags(InSupport.FormatSupport2, InFlag))
{
EnumAddFlags(Capabilities, UnrealCap);
}
};
ConvertCap1(FormatSupport, EPixelFormatCapabilities::Texture1D, D3D11_FORMAT_SUPPORT_TEXTURE1D);
ConvertCap1(FormatSupport, EPixelFormatCapabilities::Texture2D, D3D11_FORMAT_SUPPORT_TEXTURE2D);
ConvertCap1(FormatSupport, EPixelFormatCapabilities::Texture3D, D3D11_FORMAT_SUPPORT_TEXTURE3D);
ConvertCap1(FormatSupport, EPixelFormatCapabilities::TextureCube, D3D11_FORMAT_SUPPORT_TEXTURECUBE);
ConvertCap1(FormatSupport, EPixelFormatCapabilities::Buffer, D3D11_FORMAT_SUPPORT_BUFFER);
ConvertCap1(FormatSupport, EPixelFormatCapabilities::VertexBuffer, D3D11_FORMAT_SUPPORT_IA_VERTEX_BUFFER);
ConvertCap1(FormatSupport, EPixelFormatCapabilities::IndexBuffer, D3D11_FORMAT_SUPPORT_IA_INDEX_BUFFER);
if (EnumHasAnyFlags(Capabilities, EPixelFormatCapabilities::AnyTexture))
{
ConvertCap1(FormatSupport, EPixelFormatCapabilities::RenderTarget, D3D11_FORMAT_SUPPORT_RENDER_TARGET);
ConvertCap1(FormatSupport, EPixelFormatCapabilities::DepthStencil, D3D11_FORMAT_SUPPORT_DEPTH_STENCIL);
ConvertCap1(FormatSupport, EPixelFormatCapabilities::TextureMipmaps, D3D11_FORMAT_SUPPORT_MIP);
ConvertCap1(SRVFormatSupport, EPixelFormatCapabilities::TextureLoad, D3D11_FORMAT_SUPPORT_SHADER_LOAD);
ConvertCap1(SRVFormatSupport, EPixelFormatCapabilities::TextureSample | EPixelFormatCapabilities::TextureFilterable, D3D11_FORMAT_SUPPORT_SHADER_SAMPLE);
ConvertCap1(SRVFormatSupport, EPixelFormatCapabilities::TextureGather, D3D11_FORMAT_SUPPORT_SHADER_GATHER);
ConvertCap2(UAVFormatSupport, EPixelFormatCapabilities::TextureAtomics, D3D11_FORMAT_SUPPORT2_UAV_ATOMIC_EXCHANGE);
ConvertCap1(RTVFormatSupport, EPixelFormatCapabilities::TextureBlendable, D3D11_FORMAT_SUPPORT_BLENDABLE);
ConvertCap2(UAVFormatSupport, EPixelFormatCapabilities::TextureStore, D3D11_FORMAT_SUPPORT2_UAV_TYPED_STORE);
}
if (EnumHasAnyFlags(Capabilities, EPixelFormatCapabilities::Buffer))
{
ConvertCap1(SRVFormatSupport, EPixelFormatCapabilities::BufferLoad, D3D11_FORMAT_SUPPORT_BUFFER);
ConvertCap2(UAVFormatSupport, EPixelFormatCapabilities::BufferStore, D3D11_FORMAT_SUPPORT2_UAV_TYPED_STORE);
ConvertCap2(UAVFormatSupport, EPixelFormatCapabilities::BufferAtomics, D3D11_FORMAT_SUPPORT2_UAV_ATOMIC_EXCHANGE);
}
ConvertCap1(UAVFormatSupport, EPixelFormatCapabilities::UAV, D3D11_FORMAT_SUPPORT_TYPED_UNORDERED_ACCESS_VIEW);
ConvertCap2(UAVFormatSupport, EPixelFormatCapabilities::TypedUAVLoad, D3D11_FORMAT_SUPPORT2_UAV_TYPED_LOAD);
ConvertCap2(UAVFormatSupport, EPixelFormatCapabilities::TypedUAVStore, D3D11_FORMAT_SUPPORT2_UAV_TYPED_STORE);
}
PixelFormatInfo.Capabilities = Capabilities;
}
// without that the first RHIClear would get a scissor rect of (0,0)-(0,0) which means we get a draw call clear
RHISetScissorRect(false, 0, 0, 0, 0);
UpdateMSAASettings();
if (GRHISupportsAsyncTextureCreation)
{
UE_LOG(LogD3D11RHI, Log, TEXT("Async texture creation enabled"));
}
else
{
UE_LOG(LogD3D11RHI, Log, TEXT("Async texture creation disabled: %s"),
D3D11RHI_ShouldAllowAsyncResourceCreation() ? TEXT("no driver support") : TEXT("disabled by user"));
}
{
D3D11_FEATURE_DATA_D3D11_OPTIONS Data;
if (const HRESULT Result = Direct3DDevice->CheckFeatureSupport(D3D11_FEATURE_D3D11_OPTIONS, &Data, sizeof(Data)); SUCCEEDED(Result))
{
GRHISupportsMapWriteNoOverwrite = Data.MapNoOverwriteOnDynamicBufferSRV;
if (GRHISupportsMapWriteNoOverwrite)
{
UE_LOG(LogD3D11RHI, Log, TEXT("D3D11_MAP_WRITE_NO_OVERWRITE for dynamic buffer SRVs is supported"));
}
}
}
{
#if 0
D3D11_FEATURE_DATA_D3D11_OPTIONS2 Data;
HRESULT Result = Direct3DDevice->CheckFeatureSupport(D3D11_FEATURE_D3D11_OPTIONS2, &Data, sizeof(Data));
GRHISupportsStencilRefFromPixelShader = SUCCEEDED(Result) && Data.PSSpecifiedStencilRefSupported;
if (GRHISupportsStencilRefFromPixelShader)
{
UE_LOG(LogD3D11RHI, Log, TEXT("Stencil ref from pixel shader is supported"));
}
#else
GRHISupportsStencilRefFromPixelShader = false; // this boolean is later used to choose a code path that requires DXIL shaders. Cannot set to true without fixing that first.
#endif
}
{
D3D11_FEATURE_DATA_D3D11_OPTIONS3 Data;
HRESULT Result = Direct3DDevice->CheckFeatureSupport(D3D11_FEATURE_D3D11_OPTIONS3, &Data, sizeof(Data));
GRHISupportsArrayIndexFromAnyShader = SUCCEEDED(Result) && Data.VPAndRTArrayIndexFromAnyShaderFeedingRasterizer;
if (GRHISupportsArrayIndexFromAnyShader)
{
UE_LOG(LogD3D11RHI, Log, TEXT("Array index from any shader is supported"));
}
}
TimestampCalibration = CalibrateTimers();
#if RHI_NEW_GPU_PROFILER
// Register the single graphics GPU queue we have access to in D3D11.
UE::RHI::GPUProfiler::FQueue Queue(UE::RHI::GPUProfiler::FQueue::EType::Graphics, 0, 0);
UE::RHI::GPUProfiler::InitializeQueues(MakeConstArrayView(&Queue, 1));
//
// Since we can't tell when the GPU is actually executing engine work in D3D11,
// just mark the GPU as always busy in the frame. We also push begin/end work
// markers in RHIEndFrame either side of the frame boundary.
//
{
auto& Event = EmplaceProfilerEvent<UE::RHI::GPUProfiler::FEvent::FBeginWork>(FPlatformTime::Cycles64());
InsertProfilerTimestamp(&Event.GPUTimestampTOP);
}
#endif
}
void FD3D11DynamicRHI::UpdateMSAASettings()
{
check(DX_MAX_MSAA_COUNT == 8);
// quality levels are only needed for CSAA which we cannot use with custom resolves
// 0xffffffff means not available
AvailableMSAAQualities[0] = 0xffffffff;
AvailableMSAAQualities[1] = 0xffffffff;
AvailableMSAAQualities[2] = 0;
AvailableMSAAQualities[3] = 0xffffffff;
AvailableMSAAQualities[4] = 0;
AvailableMSAAQualities[5] = 0xffffffff;
AvailableMSAAQualities[6] = 0xffffffff;
AvailableMSAAQualities[7] = 0xffffffff;
AvailableMSAAQualities[8] = 0;
}
#if !PLATFORM_SEH_EXCEPTIONS_DISABLED
static int32 ReportDiedDuringDeviceShutdown(LPEXCEPTION_POINTERS ExceptionInfo)
{
UE_LOG(LogD3D11RHI, Error, TEXT("Crashed freeing up the D3D11 device."));
if (GDynamicRHI)
{
GDynamicRHI->FlushPendingLogs();
}
return EXCEPTION_EXECUTE_HANDLER;
}
#endif
void FD3D11DynamicRHI::CleanupD3DDevice()
{
UE_LOG(LogD3D11RHI, Log, TEXT("CleanupD3DDevice"));
if(GIsRHIInitialized)
{
check(Direct3DDevice);
check(Direct3DDeviceIMContext);
#if WITH_DX_PERF
check(Direct3DDeviceIMAnnotation);
#endif
// Reset the RHI initialized flag.
GIsRHIInitialized = false;
check(!GIsCriticalError);
CurrentComputeShader = nullptr;
// Ask all initialized FRenderResources to release their RHI resources.
FRenderResource::ReleaseRHIForAllResources();
extern void EmptyD3DSamplerStateCache();
EmptyD3DSamplerStateCache();
// Release references to bound uniform buffers.
for (int32 Frequency = 0; Frequency < SF_NumStandardFrequencies; ++Frequency)
{
for (int32 BindIndex = 0; BindIndex < MAX_UNIFORM_BUFFERS_PER_SHADER_STAGE; ++BindIndex)
{
BoundUniformBuffers[Frequency][BindIndex] = nullptr;
}
}
// Release the device and its IC
StateCache.SetContext(nullptr);
// Flush all pending deletes before destroying the device.
FRHICommandListExecutor::GetImmediateCommandList().ImmediateFlush(EImmediateFlushType::FlushRHIThreadFlushResources);
ReleasePooledUniformBuffers();
#if WITH_AMD_AGS
// Clean up the AMD extensions and shut down the AMD AGS utility library
if (AmdAgsContext != NULL)
{
check(UE::RHICore::AllowVendorDevice());
// AGS is holding an extra reference to the immediate context. Release it before calling DestroyDevice.
Direct3DDeviceIMContext->Release();
agsDriverExtensionsDX11_DestroyDevice(AmdAgsContext, Direct3DDevice, NULL, Direct3DDeviceIMContext, NULL);
agsDeInitialize(AmdAgsContext);
GRHIDeviceIsAMDPreGCNArchitecture = false;
AmdAgsContext = NULL;
}
#endif // WITH_AMD_AGS
#if INTEL_EXTENSIONS
if (IsRHIDeviceIntel() && UE::RHICore::AllowVendorDevice())
{
StopIntelExtensions();
}
#endif // INTEL_EXTENSIONS
// When running with D3D debug, clear state and flush the device to get rid of spurious live objects in D3D11's report.
if (GRHIGlobals.IsDebugLayerEnabled)
{
Direct3DDeviceIMContext->ClearState();
Direct3DDeviceIMContext->Flush();
// Perform a detailed live object report (with resource types)
ID3D11Debug* D3D11Debug;
Direct3DDevice->QueryInterface(__uuidof(ID3D11Debug), (void**)(&D3D11Debug));
if (D3D11Debug)
{
D3D11Debug->ReportLiveDeviceObjects(D3D11_RLDO_DETAIL);
}
}
if (ExceptionHandlerHandle != INVALID_HANDLE_VALUE)
{
RemoveVectoredExceptionHandler(ExceptionHandlerHandle);
}
// ORION - avoid shutdown crash that is currently present in UE
#if !PLATFORM_SEH_EXCEPTIONS_DISABLED
if (1) // (IsRHIDeviceNVIDIA())
{
#if WITH_DX_PERF
//UE-18906: Workaround to trap crash in NV driver
__try
{
// Perform a detailed live object report (with resource types)
Direct3DDeviceIMAnnotation = nullptr;
}
__except (ReportDiedDuringDeviceShutdown(GetExceptionInformation()))
{
FPlatformMisc::MemoryBarrier();
}
#endif
//UE-18906: Workaround to trap crash in NV driver
__try
{
// Perform a detailed live object report (with resource types)
Direct3DDeviceIMContext = nullptr;
}
__except (ReportDiedDuringDeviceShutdown(GetExceptionInformation()))
{
FPlatformMisc::MemoryBarrier();
}
//UE-18906: Workaround to trap crash in NV driver
__try
{
// Perform a detailed live object report (with resource types)
Direct3DDevice = nullptr;
}
__except (ReportDiedDuringDeviceShutdown(GetExceptionInformation()))
{
FPlatformMisc::MemoryBarrier();
}
}
else
#endif
{
#if WITH_DX_PERF
Direct3DDeviceIMAnnotation = nullptr;
#endif
Direct3DDeviceIMContext = nullptr;
Direct3DDevice = nullptr;
}
}
}
void FD3D11DynamicRHI::RHIFlushResources()
{
// Nothing to do (yet!)
}
void* FD3D11DynamicRHI::RHIGetNativeDevice()
{
return (void*)Direct3DDevice.GetReference();
}
void* FD3D11DynamicRHI::RHIGetNativeInstance()
{
return nullptr;
}
void* FD3D11DynamicRHI::RHIGetNativeCommandBuffer()
{
return (void*)Direct3DDeviceIMContext.GetReference();
}
static bool CanFormatBeDisplayed(const FD3D11DynamicRHI* InD3DRHI, EPixelFormat InPixelFormat)
{
const DXGI_FORMAT DxgiFormat = FD3D11Viewport::GetRenderTargetFormat(InPixelFormat);
UINT FormatSupport = 0;
const HRESULT FormatSupportResult = InD3DRHI->GetDevice()->CheckFormatSupport(DxgiFormat, &FormatSupport);
if (FAILED(FormatSupportResult))
{
const TCHAR* D3DFormatString = UE::DXGIUtilities::GetFormatString(DxgiFormat);
UE_LOG(LogD3D11RHI, Warning, TEXT("CheckFormatSupport(%s) failed: 0x%08x"), D3DFormatString, FormatSupportResult);
return false;
}
const UINT RequiredFlags = D3D11_FORMAT_SUPPORT_DISPLAY | D3D11_FORMAT_SUPPORT_RENDER_TARGET | D3D11_FORMAT_SUPPORT_SHADER_SAMPLE;
return (FormatSupport & RequiredFlags) == RequiredFlags;
}
EPixelFormat FD3D11DynamicRHI::GetDisplayFormat(EPixelFormat InPixelFormat) const
{
EPixelFormat CandidateFormat = InPixelFormat;
// Small list of supported formats and what they should fall back to. This could be expanded more in the future.
struct SDisplayFormat { EPixelFormat PixelFormat; EPixelFormat FallbackPixelFormat; }
static const DisplayFormats[]
{
{ PF_FloatRGBA, PF_A2B10G10R10 },
{ PF_A2B10G10R10, PF_B8G8R8A8 },
};
for (const SDisplayFormat& DisplayFormat : DisplayFormats)
{
if (CandidateFormat == DisplayFormat.PixelFormat && !CanFormatBeDisplayed(this, CandidateFormat))
{
CandidateFormat = DisplayFormat.FallbackPixelFormat;
}
}
check(CanFormatBeDisplayed(this, CandidateFormat));
return CandidateFormat;
}