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

528 lines
17 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
/*=============================================================================
D3D11Util.h: D3D RHI utility implementation.
=============================================================================*/
#include "D3D11Util.h"
#include "D3D11RHIPrivate.h"
#include "EngineModule.h"
#include "Engine/GameEngine.h"
#include "RendererInterface.h"
#include "ProfilingDebugging/ScopedDebugInfo.h"
#include "HAL/ExceptionHandling.h"
#define D3DERR(x) case x: ErrorCodeText = TEXT(#x); break;
#define LOCTEXT_NAMESPACE "Developer.MessageLog"
void SetD3D11ObjectName(ID3D11DeviceChild* Object, FAnsiStringView Name)
{
#if RHI_USE_RESOURCE_DEBUG_NAME
if (Object)
{
Object->SetPrivateData(WKPDID_D3DDebugObjectName, Name.Len() + 1, GetData(Name));
}
#endif
}
void SetD3D11ObjectName(ID3D11DeviceChild* Object, FStringView Name)
{
#if RHI_USE_RESOURCE_DEBUG_NAME
if (Object != nullptr)
{
FAnsiString AnsiString(Name);
SetD3D11ObjectName(Object, AnsiString);
}
#endif
}
void SetD3D11ResourceName(FD3D11Texture* Texture, FStringView Name)
{
SetD3D11ObjectName(Texture->GetResource(), Name);
}
void SetD3D11ResourceName(FD3D11Buffer* Buffer, FStringView Name)
{
SetD3D11ObjectName(Buffer->GetResource(), Name);
}
void SetD3D11ResourceName(FD3D11UnorderedAccessView* UAV, FStringView Name)
{
SetD3D11ObjectName(UAV->View, Name);
}
static FString GetD3D11DeviceHungErrorString(HRESULT ErrorCode)
{
FString ErrorCodeText;
switch(ErrorCode)
{
D3DERR(DXGI_ERROR_DEVICE_HUNG)
D3DERR(DXGI_ERROR_DEVICE_REMOVED)
D3DERR(DXGI_ERROR_DEVICE_RESET)
D3DERR(DXGI_ERROR_DRIVER_INTERNAL_ERROR)
D3DERR(DXGI_ERROR_INVALID_CALL)
default: ErrorCodeText = FString::Printf(TEXT("%08X"),(int32)ErrorCode);
}
return ErrorCodeText;
}
FString GetD3D11ErrorString(HRESULT ErrorCode, ID3D11Device* Device)
{
FString ErrorCodeText;
switch(ErrorCode)
{
D3DERR(S_OK);
D3DERR(D3D11_ERROR_FILE_NOT_FOUND)
D3DERR(D3D11_ERROR_TOO_MANY_UNIQUE_STATE_OBJECTS)
D3DERR(E_FAIL)
D3DERR(E_INVALIDARG)
D3DERR(E_OUTOFMEMORY)
D3DERR(DXGI_ERROR_INVALID_CALL)
D3DERR(DXGI_ERROR_WAS_STILL_DRAWING)
D3DERR(E_NOINTERFACE)
D3DERR(DXGI_ERROR_DEVICE_REMOVED)
D3DERR(DXGI_ERROR_NOT_CURRENTLY_AVAILABLE)
default: ErrorCodeText = FString::Printf(TEXT("%08X"),(int32)ErrorCode);
}
if(ErrorCode == DXGI_ERROR_DEVICE_REMOVED && Device)
{
HRESULT hResDeviceRemoved = Device->GetDeviceRemovedReason();
ErrorCodeText += FString(TEXT(" ")) + GetD3D11DeviceHungErrorString(hResDeviceRemoved);
}
return ErrorCodeText;
}
#undef D3DERR
static FString GetD3D11TextureFlagString(uint32 TextureFlags)
{
FString TextureFormatText = TEXT("");
if (TextureFlags & D3D11_BIND_RENDER_TARGET)
{
TextureFormatText += TEXT("D3D11_BIND_RENDER_TARGET ");
}
if (TextureFlags & D3D11_BIND_DEPTH_STENCIL)
{
TextureFormatText += TEXT("D3D11_BIND_DEPTH_STENCIL ");
}
if (TextureFlags & D3D11_BIND_SHADER_RESOURCE)
{
TextureFormatText += TEXT("D3D11_BIND_SHADER_RESOURCE ");
}
if (TextureFlags & D3D11_BIND_UNORDERED_ACCESS)
{
TextureFormatText += TEXT("D3D11_BIND_UNORDERED_ACCESS ");
}
return TextureFormatText;
}
extern CORE_API bool GIsGPUCrashed;
static void TerminateOnDeviceRemoved(HRESULT D3DResult, ID3D11Device* Direct3DDevice)
{
if (D3DResult == DXGI_ERROR_DEVICE_REMOVED)
{
#if NV_AFTERMATH
TArray<UE::RHICore::Nvidia::Aftermath::FCrashResult> AftermathResults;
UE::RHICore::Nvidia::Aftermath::OnGPUCrash(AftermathResults);
#endif
UGameEngine* GameEngine = Cast<UGameEngine>(GEngine);
if (GameEngine)
{
GameEngine->OnGPUCrash();
}
// Report the GPU crash which will raise the exception
ReportGPUCrash(TEXT("GPU Crash dump Triggered"), nullptr);
GIsGPUCrashed = true;
if (Direct3DDevice)
{
HRESULT hRes = Direct3DDevice->GetDeviceRemovedReason();
const TCHAR* Reason = TEXT("?");
switch (hRes)
{
case DXGI_ERROR_DEVICE_HUNG: Reason = TEXT("HUNG"); break;
case DXGI_ERROR_DEVICE_REMOVED: Reason = TEXT("REMOVED"); break;
case DXGI_ERROR_DEVICE_RESET: Reason = TEXT("RESET"); break;
case DXGI_ERROR_DRIVER_INTERNAL_ERROR: Reason = TEXT("INTERNAL_ERROR"); break;
case DXGI_ERROR_INVALID_CALL: Reason = TEXT("INVALID_CALL"); break;
case S_OK: Reason = TEXT("S_OK"); break;
}
// We currently don't support removed devices because FTexture2DResource can't recreate its RHI resources from scratch.
// We would also need to recreate the viewport swap chains from scratch.
UE_LOG(LogD3D11RHI, Fatal, TEXT("Unreal Engine is exiting due to D3D device being lost. (Error: 0x%X - '%s')"), hRes, Reason);
}
else
{
UE_LOG(LogD3D11RHI, Fatal, TEXT("Unreal Engine is exiting due to D3D device being lost. D3D device was not available to determine DXGI cause."));
}
// Workaround for the fact that in non-monolithic builds the exe gets into a weird state and exception handling fails.
// @todo investigate why non-monolithic builds fail to capture the exception when graphics driver crashes.
#if !IS_MONOLITHIC
FPlatformMisc::RequestExit(true, TEXT("TerminateOnDeviceRemoved")); //-V779
#endif
}
}
void GetAndLogMemoryStats(const FD3D11Adapter& InAdapter, uint64& OutVRAMBudgetBytes, uint64& OutVRAMUsageBytes)
{
FD3DMemoryStats MemoryStats;
if (SUCCEEDED(UE::DXGIUtilities::GetD3DMemoryStats(InAdapter.DXGIAdapter, MemoryStats)))
{
UE_LOG(LogD3D11RHI, Error, TEXT("\tLocal Budget:\t%7.2f MB"), MemoryStats.BudgetLocal / (1024.0f * 1024));
UE_LOG(LogD3D11RHI, Error, TEXT("\tLocal Used:\t%7.2f MB"), MemoryStats.UsedLocal / (1024.0f * 1024));
UE_LOG(LogD3D11RHI, Error, TEXT("\tSystem Budget:\t%7.2f MB"), MemoryStats.BudgetSystem / (1024.0f * 1024));
UE_LOG(LogD3D11RHI, Error, TEXT("\tSystem Used:\t%7.2f MB"), MemoryStats.UsedSystem / (1024.0f * 1024));
OutVRAMBudgetBytes = MemoryStats.BudgetLocal;
OutVRAMUsageBytes = MemoryStats.UsedLocal;
}
}
static void TerminateOnOutOfMemory(HRESULT D3DResult, bool bCreatingTextures)
{
if (D3DResult == E_OUTOFMEMORY)
{
uint64 VRAMBudgetBytes = 0, VRAMUsageBytes = 0;
GetAndLogMemoryStats(GD3D11RHI->GetAdapter(), VRAMBudgetBytes, VRAMUsageBytes);
FPlatformMemory::DumpStats(*GLog);
FPlatformMemory::bIsOOM = true;
FCoreDelegates::GetGPUOutOfMemoryDelegate().Broadcast(VRAMBudgetBytes, VRAMUsageBytes);
if (bCreatingTextures)
{
FPlatformMisc::MessageBoxExt(EAppMsgType::Ok, *LOCTEXT("OutOfVideoMemoryTextures", "Out of video memory trying to allocate a texture! Make sure your video card has the minimum required memory, try lowering the resolution and/or closing other applications that are running. Exiting...").ToString(), TEXT("Error"));
}
else
{
FPlatformMisc::MessageBoxExt(EAppMsgType::Ok, *NSLOCTEXT("D3D11RHI", "OutOfMemory", "Out of video memory trying to allocate a rendering resource. Make sure your video card has the minimum required memory, try lowering the resolution and/or closing other applications that are running. Exiting...").ToString(), TEXT("Error"));
}
#if STATS
GetRendererModule().DebugLogOnCrash();
#endif
static IConsoleVariable* GPUCrashOOM = IConsoleManager::Get().FindConsoleVariable(TEXT("r.GPUCrashOnOutOfMemory"));
if (GPUCrashOOM && GPUCrashOOM->GetInt())
{
UE_LOG(LogD3D11RHI, Fatal, TEXT("Out of video memory trying to allocate a rendering resource"));
}
else
{
FPlatformMisc::RequestExit(true, TEXT("TerminateOnOutOfMemory"));
}
}
}
void VerifyD3D11ResultNoExit(HRESULT D3DResult, const ANSICHAR* Code, const ANSICHAR* Filename, uint32 Line, ID3D11Device* Device)
{
check(FAILED(D3DResult));
const FString& ErrorString = GetD3D11ErrorString(D3DResult, Device);
UE_LOG(LogD3D11RHI, Error, TEXT("%s failed with error %s\n at %s:%u\n Error Code List: https://docs.microsoft.com/en-us/windows/desktop/direct3ddxgi/dxgi-error"), ANSI_TO_TCHAR(Code), *ErrorString, ANSI_TO_TCHAR(Filename), Line);
}
void VerifyD3D11Result(HRESULT D3DResult,const ANSICHAR* Code,const ANSICHAR* Filename,uint32 Line, ID3D11Device* Device)
{
check(FAILED(D3DResult));
const FString& ErrorString = GetD3D11ErrorString(D3DResult, Device);
UE_LOG(LogD3D11RHI, Error, TEXT("%s failed with error %s\n at %s:%u"), ANSI_TO_TCHAR(Code), *ErrorString, ANSI_TO_TCHAR(Filename), Line);
TerminateOnDeviceRemoved(D3DResult, Device);
TerminateOnOutOfMemory(D3DResult, false);
UE_LOG(LogD3D11RHI, Fatal,TEXT("%s failed with error %s\n at %s:%u"),ANSI_TO_TCHAR(Code), *ErrorString, ANSI_TO_TCHAR(Filename), Line);
}
void VerifyD3D11ShaderResult(FRHIShader* Shader, HRESULT D3DResult, const ANSICHAR* Code, const ANSICHAR* Filename, uint32 Line, ID3D11Device* Device)
{
check(FAILED(D3DResult));
const FString& ErrorString = GetD3D11ErrorString(D3DResult, Device);
UE_LOG(LogD3D11RHI, Error, TEXT("%s failed trying to create shader '%s' with error %s\n at %s:%u"), ANSI_TO_TCHAR(Code), Shader->GetShaderName(), *ErrorString, ANSI_TO_TCHAR(Filename), Line);
TerminateOnDeviceRemoved(D3DResult, Device);
TerminateOnOutOfMemory(D3DResult, false);
UE_LOG(LogD3D11RHI, Fatal, TEXT("%s failed trying to create shader '%s' with error %s\n at %s:%u"), ANSI_TO_TCHAR(Code), Shader->GetShaderName(), *ErrorString, ANSI_TO_TCHAR(Filename), Line);
}
void VerifyD3D11CreateTextureResult(HRESULT D3DResult, int32 UEFormat,const ANSICHAR* Code,const ANSICHAR* Filename,uint32 Line,uint32 SizeX,uint32 SizeY,uint32 SizeZ,uint8 D3DFormat,uint32 NumMips,uint32 Flags,
D3D11_USAGE Usage, uint32 CPUAccessFlags, uint32 MiscFlags, uint32 SampleCount, uint32 SampleQuality,
const void* SubResPtr, uint32 SubResPitch, uint32 SubResSlicePitch, ID3D11Device* Device, const TCHAR* DebugName)
{
check(FAILED(D3DResult));
const FString ErrorString = GetD3D11ErrorString(D3DResult, 0);
const TCHAR* D3DFormatString = UE::DXGIUtilities::GetFormatString((DXGI_FORMAT)D3DFormat);
FString DebugInfoString;
if (FScopedDebugInfo* DebugInfo = FScopedDebugInfo::GetDebugInfoStack())
{
DebugInfoString = DebugInfo->GetFunctionName();
}
UE_LOG(LogD3D11RHI, Error,
TEXT("%s failed with error %s\n at %s:%u\n Size=%ix%ix%i PF=%d D3DFormat=%s(0x%08X), NumMips=%i, Flags=%s, Usage:0x%x, CPUFlags:0x%x, MiscFlags:0x%x, SampleCount:0x%x, SampleQuality:0x%x, SubresPtr:0x%p, SubresPitch:%i, SubresSlicePitch:%i, Name:'%s', DebugInfo: %s"),
ANSI_TO_TCHAR(Code),
*ErrorString,
ANSI_TO_TCHAR(Filename),
Line,
SizeX,
SizeY,
SizeZ,
UEFormat,
D3DFormatString,
D3DFormat,
NumMips,
*GetD3D11TextureFlagString(Flags),
Usage,
CPUAccessFlags,
MiscFlags,
SampleCount,
SampleQuality,
SubResPtr,
SubResPitch,
SubResSlicePitch,
DebugName ? DebugName : TEXT(""),
*DebugInfoString);
TerminateOnDeviceRemoved(D3DResult, Device);
TerminateOnOutOfMemory(D3DResult, true);
UE_LOG(LogD3D11RHI, Fatal,
TEXT("%s failed with error %s\n at %s:%u\n Size=%ix%ix%i PF=%d Format=%s(0x%08X), NumMips=%i, Flags=%s, Usage:0x%x, CPUFlags:0x%x, MiscFlags:0x%x, SampleCount:0x%x, SampleQuality:0x%x, SubresPtr:0x%p, SubresPitch:%i, SubresSlicePitch:%i"),
ANSI_TO_TCHAR(Code),
*ErrorString,
ANSI_TO_TCHAR(Filename),
Line,
SizeX,
SizeY,
SizeZ,
UEFormat,
D3DFormatString,
D3DFormat,
NumMips,
*GetD3D11TextureFlagString(Flags),
Usage,
CPUAccessFlags,
MiscFlags,
SampleCount,
SampleQuality,
SubResPtr,
SubResPitch,
SubResSlicePitch);
}
void VerifyD3D11ResizeViewportResult(
HRESULT D3DResult,
const ANSICHAR* Code,
const ANSICHAR* Filename,
uint32 Line,
const FD3D11ResizeViewportState& OldState,
const FD3D11ResizeViewportState& NewState,
ID3D11Device* Device)
{
check(FAILED(D3DResult));
const FString ErrorString = GetD3D11ErrorString(D3DResult, 0);
const TCHAR* OldStateFormat = UE::DXGIUtilities::GetFormatString(OldState.Format);
const TCHAR* NewStateFormat = UE::DXGIUtilities::GetFormatString(NewState.Format);
UE_LOG(LogD3D11RHI, Error,
TEXT("%s failed with error %s\n at %s:%u\n (Size=%ix%i Fullscreen=%d Format=%s(0x%08X)) -> (Size=%ix%i Fullscreen=%d Format=%s(0x%08X))"),
ANSI_TO_TCHAR(Code),
*ErrorString,
ANSI_TO_TCHAR(Filename),
Line,
OldState.SizeX,
OldState.SizeY,
OldState.bIsFullscreen ? 1 : 0,
OldStateFormat,
OldState.Format,
NewState.SizeX,
NewState.SizeY,
NewState.bIsFullscreen ? 1 : 0,
NewStateFormat,
NewState.Format);
TerminateOnDeviceRemoved(D3DResult, Device);
TerminateOnOutOfMemory(D3DResult, true);
UE_LOG(LogD3D11RHI, Fatal,
TEXT("%s failed with error %s\n at %s:%u\n (Size=%ix%i Fullscreen=%d Format=%s(0x%08X)) -> (Size=%ix%i Fullscreen=%d Format=%s(0x%08X))"),
ANSI_TO_TCHAR(Code),
*ErrorString,
ANSI_TO_TCHAR(Filename),
Line,
OldState.SizeX,
OldState.SizeY,
OldState.bIsFullscreen ? 1 : 0,
OldStateFormat,
OldState.Format,
NewState.SizeX,
NewState.SizeY,
NewState.bIsFullscreen ? 1 : 0,
NewStateFormat,
NewState.Format);
}
void VerifyD3D11CreateViewResult(HRESULT D3DResult, const ANSICHAR* Code, const ANSICHAR* Filename, uint32 Line, ID3D11Device* Device, const FString& ResourceName, const D3D11_UNORDERED_ACCESS_VIEW_DESC& Desc)
{
check(FAILED(D3DResult));
D3D11_FEATURE_DATA_FORMAT_SUPPORT FormatSupport{};
FormatSupport.InFormat = Desc.Format;
Device->CheckFeatureSupport(D3D11_FEATURE_FORMAT_SUPPORT, &FormatSupport, sizeof(FormatSupport));
D3D11_FEATURE_DATA_FORMAT_SUPPORT2 FormatSupport2{};
FormatSupport2.InFormat = Desc.Format;
Device->CheckFeatureSupport(D3D11_FEATURE_FORMAT_SUPPORT2, &FormatSupport2, sizeof(FormatSupport2));
const TCHAR* ViewFormat = UE::DXGIUtilities::GetFormatString(Desc.Format);
const FString& ErrorString = GetD3D11ErrorString(D3DResult, Device);
UE_LOG(LogD3D11RHI, Error, TEXT("%s failed with error %s (Name='%s', Format='%s' (0x%08X), FormatSupport=0x%08X, FormatSupport2=0x%08X)\n at %s:%u"),
ANSI_TO_TCHAR(Code), *ErrorString,
*ResourceName, ViewFormat, Desc.Format, FormatSupport.OutFormatSupport, FormatSupport2.OutFormatSupport2,
ANSI_TO_TCHAR(Filename), Line);
TerminateOnDeviceRemoved(D3DResult, Device);
TerminateOnOutOfMemory(D3DResult, false);
UE_LOG(LogD3D11RHI, Fatal, TEXT("%s failed with error %s (Name='%s', Format='%s' (0x%08X), FormatSupport=0x%08X, FormatSupport2=0x%08X)\n at %s:%u"),
ANSI_TO_TCHAR(Code), *ErrorString,
*ResourceName, ViewFormat, Desc.Format, FormatSupport.OutFormatSupport, FormatSupport2.OutFormatSupport2,
ANSI_TO_TCHAR(Filename), Line);
}
void VerifyD3D11CreateViewResult(HRESULT D3DResult, const ANSICHAR* Code, const ANSICHAR* Filename, uint32 Line, ID3D11Device* Device, FRHITexture* Texture, const D3D11_UNORDERED_ACCESS_VIEW_DESC& Desc)
{
const FString TextureName = Texture ? Texture->GetName().ToString() : FString(TEXT("<Unknown>"));
VerifyD3D11CreateViewResult(D3DResult, Code, Filename, Line, Device, TextureName, Desc);
}
void VerifyD3D11CreateViewResult(HRESULT D3DResult, const ANSICHAR* Code, const ANSICHAR* Filename, uint32 Line, ID3D11Device* Device, FRHIBuffer* Buffer, const D3D11_UNORDERED_ACCESS_VIEW_DESC& Desc)
{
FString BufferName = FString(TEXT("<Unknown>"));
#if ENABLE_RHI_VALIDATION
if (Buffer)
{
BufferName = Buffer->GetDebugName();
}
#endif
VerifyD3D11CreateViewResult(D3DResult, Code, Filename, Line, Device, BufferName, Desc);
}
void VerifyComRefCount(IUnknown* Object,int32 ExpectedRefs,const TCHAR* Code,const TCHAR* Filename,int32 Line)
{
int32 NumRefs;
if (Object)
{
Object->AddRef();
NumRefs = Object->Release();
checkSlow(NumRefs == ExpectedRefs);
if (NumRefs != ExpectedRefs)
{
UE_LOG(
LogD3D11RHI,
Error,
TEXT("%s:(%d): %s has %d refs, expected %d"),
Filename,
Line,
Code,
NumRefs,
ExpectedRefs
);
}
}
}
FD3D11BoundRenderTargets::FD3D11BoundRenderTargets(ID3D11DeviceContext* InDeviceContext)
{
FMemory::Memzero(RenderTargetViews,sizeof(RenderTargetViews));
DepthStencilView = NULL;
InDeviceContext->OMGetRenderTargets(
MaxSimultaneousRenderTargets,
&RenderTargetViews[0],
&DepthStencilView
);
// Find the last non-null rendertarget to determine the max
// We traverse the array backwards, since they can be sparse
for (NumActiveTargets = MaxSimultaneousRenderTargets; NumActiveTargets > 0; --NumActiveTargets)
{
if (RenderTargetViews[NumActiveTargets-1] != NULL)
{
break;
}
}
}
FD3D11BoundRenderTargets::~FD3D11BoundRenderTargets()
{
// OMGetRenderTargets calls AddRef on each RTV/DSV it returns. We need
// to make a corresponding call to Release.
for (int32 TargetIndex = 0; TargetIndex < NumActiveTargets; ++TargetIndex)
{
if (RenderTargetViews[TargetIndex] != nullptr)
{
RenderTargetViews[TargetIndex]->Release();
}
}
if (DepthStencilView)
{
DepthStencilView->Release();
}
}
//
// Stat declarations.
//
DEFINE_STAT(STAT_D3D11PresentTime);
DEFINE_STAT(STAT_D3D11CustomPresentTime);
DEFINE_STAT(STAT_D3D11TexturesAllocated);
DEFINE_STAT(STAT_D3D11TexturesReleased);
DEFINE_STAT(STAT_D3D11ClearShaderResourceTime);
DEFINE_STAT(STAT_D3D11CreateTextureTime);
DEFINE_STAT(STAT_D3D11LockTextureTime);
DEFINE_STAT(STAT_D3D11UnlockTextureTime);
DEFINE_STAT(STAT_D3D11CopyTextureTime);
DEFINE_STAT(STAT_D3D11NewBoundShaderStateTime);
DEFINE_STAT(STAT_D3D11CreateBoundShaderStateTime);
DEFINE_STAT(STAT_D3D11CleanUniformBufferTime);
DEFINE_STAT(STAT_D3D11UpdateUniformBufferTime);
DEFINE_STAT(STAT_D3D11TexturePoolMemory);
DEFINE_STAT(STAT_D3D11FreeUniformBufferMemory);
DEFINE_STAT(STAT_D3D11NumFreeUniformBuffers);
DEFINE_STAT(STAT_D3D11NumImmutableUniformBuffers);
DEFINE_STAT(STAT_D3D11NumBoundShaderState);
DEFINE_STAT(STAT_D3D11RenderTargetCommits);
DEFINE_STAT(STAT_D3D11RenderTargetCommitsUAV);
#undef LOCTEXT_NAMESPACE