// 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 #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 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 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 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( 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(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(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 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(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(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; }