Files
UnrealEngineUWP/Engine/Source/Runtime/RHI/Private/RHI.cpp
tiago costa 30c8264346 Implemented ShouldCompileRayTracingCallableShadersForProject(ShaderPlatform)
- added bSupportsRayTracingCallableShaders to DDPI.
- only Windows D3D12 RHI supports callable shaders at the moment.

#rb yuriy.odonnell
#preflight 628bb493693c5e1de282983f

[CL 20331097 by tiago costa in ue5-main branch]
2022-05-23 12:52:55 -04:00

3046 lines
111 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
/*=============================================================================
RHI.cpp: Render Hardware Interface implementation.
=============================================================================*/
#include "RHI.h"
#include "RHITransientResourceAllocator.h"
#include "Modules/ModuleManager.h"
#include "Misc/ConfigCacheIni.h"
#include "Misc/MessageDialog.h"
#include "RHIShaderFormatDefinitions.inl"
#include "ProfilingDebugging/CsvProfiler.h"
#include "String/Find.h"
#include "String/LexFromString.h"
#include "String/ParseTokens.h"
#include "Misc/BufferedOutputDevice.h"
#include "Misc/OutputDeviceFile.h"
#if RHI_ENABLE_RESOURCE_INFO
#include "HAL/FileManager.h"
#endif
IMPLEMENT_MODULE(FDefaultModuleImpl, RHI);
/** RHI Logging. */
DEFINE_LOG_CATEGORY(LogRHI);
CSV_DEFINE_CATEGORY(RHI, true);
#if UE_BUILD_SHIPPING
CSV_DEFINE_CATEGORY(DrawCall, false);
#else
CSV_DEFINE_CATEGORY(DrawCall, true);
#endif
// Define counter stats.
DEFINE_STAT(STAT_RHIDrawPrimitiveCalls);
DEFINE_STAT(STAT_RHITriangles);
DEFINE_STAT(STAT_RHILines);
// Define memory stats.
DEFINE_STAT(STAT_RenderTargetMemory2D);
DEFINE_STAT(STAT_RenderTargetMemory3D);
DEFINE_STAT(STAT_RenderTargetMemoryCube);
DEFINE_STAT(STAT_TextureMemory2D);
DEFINE_STAT(STAT_TextureMemory3D);
DEFINE_STAT(STAT_TextureMemoryCube);
DEFINE_STAT(STAT_UniformBufferMemory);
DEFINE_STAT(STAT_IndexBufferMemory);
DEFINE_STAT(STAT_VertexBufferMemory);
DEFINE_STAT(STAT_RTAccelerationStructureMemory);
DEFINE_STAT(STAT_StructuredBufferMemory);
DEFINE_STAT(STAT_PixelBufferMemory);
IMPLEMENT_TYPE_LAYOUT(FRHIUniformBufferLayoutInitializer);
IMPLEMENT_TYPE_LAYOUT(FRHIUniformBufferResource);
#if !defined(RHIRESOURCE_NUM_FRAMES_TO_EXPIRE)
#define RHIRESOURCE_NUM_FRAMES_TO_EXPIRE 3
#endif
static TAutoConsoleVariable<int32> CVarDisableEngineAndAppRegistration(
TEXT("r.DisableEngineAndAppRegistration"),
0,
TEXT("If true, disables engine and app registration, to disable GPU driver optimizations during debugging and development\n")
TEXT("Changes will only take effect in new game/editor instances - can't be changed at runtime.\n"),
ECVF_Default);
static TAutoConsoleVariable<int32> CVarGraphicsAdapter(
TEXT("r.GraphicsAdapter"),
-1,
TEXT("User request to pick a specific graphics adapter (e.g. when using a integrated graphics card with a discrete one)\n")
TEXT("For Windows D3D, unless a specific adapter is chosen we reject Microsoft adapters because we don't want the software emulation.\n")
TEXT("This takes precedence over -prefer{AMD|NVidia|Intel} when the value is >= 0.\n")
TEXT(" -2: Take the first one that fulfills the criteria\n")
TEXT(" -1: Favour non integrated because there are usually faster (default)\n")
TEXT(" 0: Adapter #0\n")
TEXT(" 1: Adapter #1, ..."),
ECVF_ReadOnly | ECVF_RenderThreadSafe);
template<typename EnumType>
inline FString BuildEnumNameBitList(EnumType Value, const TCHAR*(*GetEnumName)(EnumType))
{
if (Value == EnumType(0))
{
return GetEnumName(Value);
}
using T = __underlying_type(EnumType);
T StateValue = (T)Value;
FString Name;
int32 BitIndex = 0;
while (StateValue)
{
if (StateValue & 1)
{
if (Name.Len() > 0 && StateValue > 0)
{
Name += TEXT("|");
}
Name += GetEnumName(EnumType(T(1) << BitIndex));
}
BitIndex++;
StateValue >>= 1;
}
return MoveTemp(Name);
}
FString GetRHIAccessName(ERHIAccess Access)
{
return BuildEnumNameBitList<ERHIAccess>(Access, [](ERHIAccess AccessBit)
{
switch (AccessBit)
{
default: checkNoEntry(); // fall through
case ERHIAccess::Unknown: return TEXT("Unknown");
case ERHIAccess::CPURead: return TEXT("CPURead");
case ERHIAccess::Present: return TEXT("Present");
case ERHIAccess::IndirectArgs: return TEXT("IndirectArgs");
case ERHIAccess::VertexOrIndexBuffer: return TEXT("VertexOrIndexBuffer");
case ERHIAccess::SRVCompute: return TEXT("SRVCompute");
case ERHIAccess::SRVGraphics: return TEXT("SRVGraphics");
case ERHIAccess::CopySrc: return TEXT("CopySrc");
case ERHIAccess::ResolveSrc: return TEXT("ResolveSrc");
case ERHIAccess::DSVRead: return TEXT("DSVRead");
case ERHIAccess::UAVCompute: return TEXT("UAVCompute");
case ERHIAccess::UAVGraphics: return TEXT("UAVGraphics");
case ERHIAccess::RTV: return TEXT("RTV");
case ERHIAccess::CopyDest: return TEXT("CopyDest");
case ERHIAccess::ResolveDst: return TEXT("ResolveDst");
case ERHIAccess::DSVWrite: return TEXT("DSVWrite");
case ERHIAccess::ShadingRateSource: return TEXT("ShadingRateSource");
case ERHIAccess::BVHRead: return TEXT("BVHRead");
case ERHIAccess::BVHWrite: return TEXT("BVHWrite");
case ERHIAccess::Discard: return TEXT("Discard");
}
});
}
FString GetResourceTransitionFlagsName(EResourceTransitionFlags Flags)
{
return BuildEnumNameBitList<EResourceTransitionFlags>(Flags, [](EResourceTransitionFlags Value)
{
switch (Value)
{
default: checkNoEntry(); // fall through
case EResourceTransitionFlags::None: return TEXT("None");
case EResourceTransitionFlags::MaintainCompression: return TEXT("MaintainCompression");
case EResourceTransitionFlags::Discard: return TEXT("Discard");
case EResourceTransitionFlags::Clear: return TEXT("Clear");
}
});
}
FString GetRHIPipelineName(ERHIPipeline Pipeline)
{
return BuildEnumNameBitList<ERHIPipeline>(Pipeline, [](ERHIPipeline Value)
{
if (Value == ERHIPipeline(0)) { return TEXT("None"); }
switch (Value)
{
default: checkNoEntry(); // fall through
case ERHIPipeline::Graphics: return TEXT("Graphics");
case ERHIPipeline::AsyncCompute: return TEXT("AsyncCompute");
}
});
}
FString GetBufferUsageFlagsName(EBufferUsageFlags BufferUsage)
{
return BuildEnumNameBitList<EBufferUsageFlags>(BufferUsage, [](EBufferUsageFlags BufferUsage)
{
switch (BufferUsage)
{
default: checkNoEntry(); // fall through
case BUF_None: return TEXT("BUF_None");
case BUF_Static: return TEXT("BUF_Static");
case BUF_Dynamic: return TEXT("BUF_Dynamic");
case BUF_Volatile: return TEXT("BUF_Volitile");
case BUF_UnorderedAccess: return TEXT("BUF_UnorderedAccess");
case BUF_ByteAddressBuffer: return TEXT("BUF_ByteAddressBuffer");
case BUF_SourceCopy: return TEXT("BUF_SourceCopy");
case BUF_StreamOutput: return TEXT("BUF_StreamOutput");
case BUF_DrawIndirect: return TEXT("BUF_DrawIndirect");
case BUF_ShaderResource: return TEXT("BUF_ShaderResource");
case BUF_KeepCPUAccessible: return TEXT("BUF_KeepCPUAccessible");
case BUF_FastVRAM: return TEXT("BUF_FastVRAM");
case BUF_Shared: return TEXT("BUF_Shared");
case BUF_AccelerationStructure: return TEXT("BUF_AccelerationStructure");
case BUF_RayTracingScratch: return TEXT("BUF_RayTracingScratch");
case BUF_VertexBuffer: return TEXT("BUF_VertexBuffer");
case BUF_IndexBuffer: return TEXT("BUF_IndexBuffer");
case BUF_StructuredBuffer: return TEXT("BUF_StructuredBuffer");
}
});
}
FString GetTextureCreateFlagsName(ETextureCreateFlags TextureCreateFlags)
{
return BuildEnumNameBitList<ETextureCreateFlags>(TextureCreateFlags, [](ETextureCreateFlags TextureCreateFlags)
{
switch (TextureCreateFlags)
{
default: checkNoEntry(); // fall through
case TexCreate_None: return TEXT("TexCreate_None");
case TexCreate_RenderTargetable: return TEXT("TexCreate_RenderTargetable");
case TexCreate_ResolveTargetable: return TEXT("TexCreate_ResolveTargetable");
case TexCreate_DepthStencilTargetable: return TEXT("TexCreate_DepthStencilTargetable");
case TexCreate_ShaderResource: return TEXT("TexCreate_ShaderResource");
case TexCreate_SRGB: return TEXT("TexCreate_SRGB");
case TexCreate_CPUWritable: return TEXT("TexCreate_CPUWritable");
case TexCreate_NoTiling: return TEXT("TexCreate_NoTiling");
case TexCreate_VideoDecode: return TEXT("TexCreate_VideoDecode");
case TexCreate_Dynamic: return TEXT("TexCreate_Dynamic");
case TexCreate_InputAttachmentRead: return TEXT("TexCreate_InputAttachmentRead");
case TexCreate_Foveation: return TEXT("TexCreate_Foveation");
case TexCreate_3DTiling: return TEXT("TexCreate_3DTiling");
case TexCreate_Memoryless: return TEXT("TexCreate_Memoryless");
case TexCreate_GenerateMipCapable: return TEXT("TexCreate_GenerateMipCapable");
case TexCreate_FastVRAMPartialAlloc: return TEXT("TexCreate_FastVRAMPartialAlloc");
case TexCreate_DisableSRVCreation: return TEXT("TexCreate_DisableSRVCreation");
case TexCreate_DisableDCC: return TEXT("TexCreate_DisableDCC");
case TexCreate_UAV: return TEXT("TexCreate_UAV");
case TexCreate_Presentable: return TEXT("TexCreate_Presentable");
case TexCreate_CPUReadback: return TEXT("TexCreate_CPUReadback");
case TexCreate_OfflineProcessed: return TEXT("TexCreate_OfflineProcessed");
case TexCreate_FastVRAM: return TEXT("TexCreate_FastVRAM");
case TexCreate_HideInVisualizeTexture: return TEXT("TexCreate_HideInVisualizeTexture");
case TexCreate_Virtual: return TEXT("TexCreate_Virtual");
case TexCreate_TargetArraySlicesIndependently: return TEXT("TexCreate_TargetArraySlicesIndependently");
case TexCreate_Shared: return TEXT("TexCreate_Shared");
case TexCreate_NoFastClear: return TEXT("TexCreate_NoFastClear");
case TexCreate_DepthStencilResolveTarget: return TEXT("TexCreate_DepthStencilResolveTarget");
case TexCreate_Streamable: return TEXT("TexCreate_Streamable");
case TexCreate_NoFastClearFinalize: return TEXT("TexCreate_NoFastClearFinalize");
case TexCreate_AFRManual: return TEXT("TexCreate_AFRManual");
case TexCreate_ReduceMemoryWithTilingMode: return TEXT("TexCreate_ReduceMemoryWithTilingMode");
case TexCreate_AtomicCompatible: return TEXT("TexCreate_AtomicCompatible");
case TexCreate_External: return TEXT("TexCreate_External");
}
});
}
#if STATS
#include "Stats/StatsData.h"
static void DumpRHIMemory(FOutputDevice& OutputDevice)
{
TArray<FStatMessage> Stats;
GetPermanentStats(Stats);
FName NAME_STATGROUP_RHI(FStatGroup_STATGROUP_RHI::GetGroupName());
OutputDevice.Logf(TEXT("RHI resource memory (not tracked by our allocator)"));
int64 TotalMemory = 0;
for (int32 Index = 0; Index < Stats.Num(); Index++)
{
FStatMessage const& Meta = Stats[Index];
FName LastGroup = Meta.NameAndInfo.GetGroupName();
if (LastGroup == NAME_STATGROUP_RHI && Meta.NameAndInfo.GetFlag(EStatMetaFlags::IsMemory))
{
OutputDevice.Logf(TEXT("%s"), *FStatsUtils::DebugPrint(Meta));
TotalMemory += Meta.GetValue_int64();
}
}
OutputDevice.Logf(TEXT("%.3fMB total"), TotalMemory / 1024.f / 1024.f);
}
static FAutoConsoleCommandWithOutputDevice GDumpRHIMemoryCmd(
TEXT("rhi.DumpMemory"),
TEXT("Dumps RHI memory stats to the log"),
FConsoleCommandWithOutputDeviceDelegate::CreateStatic(DumpRHIMemory)
);
#endif
//DO NOT USE THE STATIC FLINEARCOLORS TO INITIALIZE THIS STUFF.
//Static init order is undefined and you will likely end up with bad values on some platforms.
const FClearValueBinding FClearValueBinding::None(EClearBinding::ENoneBound);
const FClearValueBinding FClearValueBinding::Black(FLinearColor(0.0f, 0.0f, 0.0f, 1.0f));
const FClearValueBinding FClearValueBinding::BlackMaxAlpha(FLinearColor(0.0f, 0.0f, 0.0f, FLT_MAX));
const FClearValueBinding FClearValueBinding::White(FLinearColor(1.0f, 1.0f, 1.0f, 1.0f));
const FClearValueBinding FClearValueBinding::Transparent(FLinearColor(0.0f, 0.0f, 0.0f, 0.0f));
const FClearValueBinding FClearValueBinding::DepthOne(1.0f, 0);
const FClearValueBinding FClearValueBinding::DepthZero(0.0f, 0);
const FClearValueBinding FClearValueBinding::DepthNear((float)ERHIZBuffer::NearPlane, 0);
const FClearValueBinding FClearValueBinding::DepthFar((float)ERHIZBuffer::FarPlane, 0);
const FClearValueBinding FClearValueBinding::Green(FLinearColor(0.0f, 1.0f, 0.0f, 1.0f));
// Note: this is used as the default normal for DBuffer decals. It must decode to a value of 0 in DecodeDBufferData.
const FClearValueBinding FClearValueBinding::DefaultNormal8Bit(FLinearColor(128.0f / 255.0f, 128.0f / 255.0f, 128.0f / 255.0f, 1.0f));
std::atomic<TClosableMpscQueue<FRHIResource*>*> FRHIResource::PendingDeletes { new TClosableMpscQueue<FRHIResource*>() };
FHazardPointerCollection FRHIResource::PendingDeletesHPC;
FRHIResource* FRHIResource::CurrentlyDeleting = nullptr;
RHI_API FDrawCallCategoryName* FDrawCallCategoryName::Array[FDrawCallCategoryName::MAX_DRAWCALL_CATEGORY];
RHI_API int32 FDrawCallCategoryName::DisplayCounts[FDrawCallCategoryName::MAX_DRAWCALL_CATEGORY][MAX_NUM_GPUS];
RHI_API int32 FDrawCallCategoryName::NumCategory = 0;
TRefCountPtr<FRHITexture> FRHITextureReference::DefaultTexture;
FString FVertexElement::ToString() const
{
return FString::Printf(TEXT("<%u %u %u %u %u %u>")
, uint32(StreamIndex)
, uint32(Offset)
, uint32(Type)
, uint32(AttributeIndex)
, uint32(Stride)
, uint32(bUseInstanceIndex)
);
}
void FVertexElement::FromString(const FString& InSrc)
{
FromString(FStringView(InSrc));
}
void FVertexElement::FromString(const FStringView& InSrc)
{
constexpr int32 PartCount = 6;
TArray<FStringView, TInlineAllocator<PartCount>> Parts;
UE::String::ParseTokensMultiple(InSrc.TrimStartAndEnd(), {TEXT('\r'), TEXT('\n'), TEXT('\t'), TEXT('<'), TEXT('>'), TEXT(' ')},
[&Parts](FStringView Part) { if (!Part.IsEmpty()) { Parts.Add(Part); } });
check(Parts.Num() == PartCount && sizeof(Type) == 1); //not a very robust parser
const FStringView* PartIt = Parts.GetData();
LexFromString(StreamIndex, *PartIt++);
LexFromString(Offset, *PartIt++);
LexFromString((uint8&)Type, *PartIt++);
LexFromString(AttributeIndex, *PartIt++);
LexFromString(Stride, *PartIt++);
LexFromString(bUseInstanceIndex, *PartIt++);
check(Parts.GetData() + PartCount == PartIt);
}
uint32 GetTypeHash(const FSamplerStateInitializerRHI& Initializer)
{
uint32 Hash = GetTypeHash(Initializer.Filter);
Hash = HashCombine(Hash, GetTypeHash(Initializer.AddressU));
Hash = HashCombine(Hash, GetTypeHash(Initializer.AddressV));
Hash = HashCombine(Hash, GetTypeHash(Initializer.AddressW));
Hash = HashCombine(Hash, GetTypeHash(Initializer.MipBias));
Hash = HashCombine(Hash, GetTypeHash(Initializer.MinMipLevel));
Hash = HashCombine(Hash, GetTypeHash(Initializer.MaxMipLevel));
Hash = HashCombine(Hash, GetTypeHash(Initializer.MaxAnisotropy));
Hash = HashCombine(Hash, GetTypeHash(Initializer.BorderColor));
Hash = HashCombine(Hash, GetTypeHash(Initializer.SamplerComparisonFunction));
return Hash;
}
bool operator== (const FSamplerStateInitializerRHI& A, const FSamplerStateInitializerRHI& B)
{
bool bSame =
A.Filter == B.Filter &&
A.AddressU == B.AddressU &&
A.AddressV == B.AddressV &&
A.AddressW == B.AddressW &&
A.MipBias == B.MipBias &&
A.MinMipLevel == B.MinMipLevel &&
A.MaxMipLevel == B.MaxMipLevel &&
A.MaxAnisotropy == B.MaxAnisotropy &&
A.BorderColor == B.BorderColor &&
A.SamplerComparisonFunction == B.SamplerComparisonFunction;
return bSame;
}
uint32 GetTypeHash(const FRasterizerStateInitializerRHI& Initializer)
{
uint32 Hash = GetTypeHash(Initializer.FillMode);
Hash = HashCombine(Hash, GetTypeHash(Initializer.CullMode));
Hash = HashCombine(Hash, GetTypeHash(Initializer.DepthBias));
Hash = HashCombine(Hash, GetTypeHash(Initializer.SlopeScaleDepthBias));
Hash = HashCombine(Hash, GetTypeHash(Initializer.DepthClipMode));
Hash = HashCombine(Hash, GetTypeHash(Initializer.bAllowMSAA));
Hash = HashCombine(Hash, GetTypeHash(Initializer.bEnableLineAA));
return Hash;
}
bool operator== (const FRasterizerStateInitializerRHI& A, const FRasterizerStateInitializerRHI& B)
{
bool bSame =
A.FillMode == B.FillMode &&
A.CullMode == B.CullMode &&
A.DepthBias == B.DepthBias &&
A.SlopeScaleDepthBias == B.SlopeScaleDepthBias &&
A.DepthClipMode == B.DepthClipMode &&
A.bAllowMSAA == B.bAllowMSAA &&
A.bEnableLineAA == B.bEnableLineAA;
return bSame;
}
uint32 GetTypeHash(const FDepthStencilStateInitializerRHI& Initializer)
{
uint32 Hash = GetTypeHash(Initializer.bEnableDepthWrite);
Hash = HashCombine(Hash, GetTypeHash(Initializer.DepthTest));
Hash = HashCombine(Hash, GetTypeHash(Initializer.bEnableFrontFaceStencil));
Hash = HashCombine(Hash, GetTypeHash(Initializer.FrontFaceStencilTest));
Hash = HashCombine(Hash, GetTypeHash(Initializer.FrontFaceStencilFailStencilOp));
Hash = HashCombine(Hash, GetTypeHash(Initializer.FrontFaceDepthFailStencilOp));
Hash = HashCombine(Hash, GetTypeHash(Initializer.FrontFacePassStencilOp));
Hash = HashCombine(Hash, GetTypeHash(Initializer.bEnableBackFaceStencil));
Hash = HashCombine(Hash, GetTypeHash(Initializer.BackFaceStencilTest));
Hash = HashCombine(Hash, GetTypeHash(Initializer.BackFaceStencilFailStencilOp));
Hash = HashCombine(Hash, GetTypeHash(Initializer.BackFaceDepthFailStencilOp));
Hash = HashCombine(Hash, GetTypeHash(Initializer.BackFacePassStencilOp));
Hash = HashCombine(Hash, GetTypeHash(Initializer.StencilReadMask));
Hash = HashCombine(Hash, GetTypeHash(Initializer.StencilWriteMask));
return Hash;
}
bool operator== (const FDepthStencilStateInitializerRHI& A, const FDepthStencilStateInitializerRHI& B)
{
bool bSame =
A.bEnableDepthWrite == B.bEnableDepthWrite &&
A.DepthTest == B.DepthTest &&
A.bEnableFrontFaceStencil == B.bEnableFrontFaceStencil &&
A.FrontFaceStencilTest == B.FrontFaceStencilTest &&
A.FrontFaceStencilFailStencilOp == B.FrontFaceStencilFailStencilOp &&
A.FrontFaceDepthFailStencilOp == B.FrontFaceDepthFailStencilOp &&
A.FrontFacePassStencilOp == B.FrontFacePassStencilOp &&
A.bEnableBackFaceStencil == B.bEnableBackFaceStencil &&
A.BackFaceStencilTest == B.BackFaceStencilTest &&
A.BackFaceStencilFailStencilOp == B.BackFaceStencilFailStencilOp &&
A.BackFaceDepthFailStencilOp == B.BackFaceDepthFailStencilOp &&
A.BackFacePassStencilOp == B.BackFacePassStencilOp &&
A.StencilReadMask == B.StencilReadMask &&
A.StencilWriteMask == B.StencilWriteMask;
return bSame;
}
FString FDepthStencilStateInitializerRHI::ToString() const
{
return
FString::Printf(TEXT("<%u %u ")
, uint32(!!bEnableDepthWrite)
, uint32(DepthTest)
)
+ FString::Printf(TEXT("%u %u %u %u %u ")
, uint32(!!bEnableFrontFaceStencil)
, uint32(FrontFaceStencilTest)
, uint32(FrontFaceStencilFailStencilOp)
, uint32(FrontFaceDepthFailStencilOp)
, uint32(FrontFacePassStencilOp)
)
+ FString::Printf(TEXT("%u %u %u %u %u ")
, uint32(!!bEnableBackFaceStencil)
, uint32(BackFaceStencilTest)
, uint32(BackFaceStencilFailStencilOp)
, uint32(BackFaceDepthFailStencilOp)
, uint32(BackFacePassStencilOp)
)
+ FString::Printf(TEXT("%u %u>")
, uint32(StencilReadMask)
, uint32(StencilWriteMask)
);
}
void FDepthStencilStateInitializerRHI::FromString(const FString& InSrc)
{
FromString(FStringView(InSrc));
}
void FDepthStencilStateInitializerRHI::FromString(const FStringView& InSrc)
{
constexpr int32 PartCount = 14;
TArray<FStringView, TInlineAllocator<PartCount>> Parts;
UE::String::ParseTokensMultiple(InSrc.TrimStartAndEnd(), {TEXT('\r'), TEXT('\n'), TEXT('\t'), TEXT('<'), TEXT('>'), TEXT(' ')},
[&Parts](FStringView Part) { if (!Part.IsEmpty()) { Parts.Add(Part); } });
check(Parts.Num() == PartCount && sizeof(bool) == 1 && sizeof(FrontFaceStencilFailStencilOp) == 1 && sizeof(BackFaceStencilTest) == 1 && sizeof(BackFaceDepthFailStencilOp) == 1); //not a very robust parser
const FStringView* PartIt = Parts.GetData();
LexFromString((uint8&)bEnableDepthWrite, *PartIt++);
LexFromString((uint8&)DepthTest, *PartIt++);
LexFromString((uint8&)bEnableFrontFaceStencil, *PartIt++);
LexFromString((uint8&)FrontFaceStencilTest, *PartIt++);
LexFromString((uint8&)FrontFaceStencilFailStencilOp, *PartIt++);
LexFromString((uint8&)FrontFaceDepthFailStencilOp, *PartIt++);
LexFromString((uint8&)FrontFacePassStencilOp, *PartIt++);
LexFromString((uint8&)bEnableBackFaceStencil, *PartIt++);
LexFromString((uint8&)BackFaceStencilTest, *PartIt++);
LexFromString((uint8&)BackFaceStencilFailStencilOp, *PartIt++);
LexFromString((uint8&)BackFaceDepthFailStencilOp, *PartIt++);
LexFromString((uint8&)BackFacePassStencilOp, *PartIt++);
LexFromString(StencilReadMask, *PartIt++);
LexFromString(StencilWriteMask, *PartIt++);
check(Parts.GetData() + PartCount == PartIt);
}
FString FBlendStateInitializerRHI::ToString() const
{
FString Result = TEXT("<");
for (int32 Index = 0; Index < MaxSimultaneousRenderTargets; Index++)
{
Result += RenderTargets[Index].ToString();
}
Result += FString::Printf(TEXT("%d %d>"), uint32(!!bUseIndependentRenderTargetBlendStates), uint32(!!bUseAlphaToCoverage));
return Result;
}
void FBlendStateInitializerRHI::FromString(const FString& InSrc)
{
FromString(FStringView(InSrc));
}
void FBlendStateInitializerRHI::FromString(const FStringView& InSrc)
{
// files written before bUseAlphaToCoverage change (added in CL 13846572) have one less part
constexpr int32 BackwardCompatiblePartCount = MaxSimultaneousRenderTargets * FRenderTarget::NUM_STRING_FIELDS + 1;
constexpr int32 PartCount = BackwardCompatiblePartCount + 1;
TArray<FStringView, TInlineAllocator<PartCount>> Parts;
UE::String::ParseTokensMultiple(InSrc.TrimStartAndEnd(), {TEXT('\r'), TEXT('\n'), TEXT('\t'), TEXT('<'), TEXT('>'), TEXT(' ')},
[&Parts](FStringView Part) { if (!Part.IsEmpty()) { Parts.Add(Part); } });
checkf((Parts.Num() == PartCount || Parts.Num() == BackwardCompatiblePartCount) && sizeof(bool) == 1,
TEXT("Expecting %d (or %d, for an older format) parts in the blendstate string, got %d"), PartCount, BackwardCompatiblePartCount, Parts.Num()); //not a very robust parser
bool bHasAlphaToCoverageField = Parts.Num() == PartCount;
const FStringView* PartIt = Parts.GetData();
for (int32 Index = 0; Index < MaxSimultaneousRenderTargets; Index++)
{
RenderTargets[Index].FromString(MakeArrayView(PartIt, FRenderTarget::NUM_STRING_FIELDS));
PartIt += FRenderTarget::NUM_STRING_FIELDS;
}
LexFromString((int8&)bUseIndependentRenderTargetBlendStates, *PartIt++);
if (bHasAlphaToCoverageField)
{
LexFromString((int8&)bUseAlphaToCoverage, *PartIt++);
check(Parts.GetData() + PartCount == PartIt);
}
else
{
bUseAlphaToCoverage = false;
check(Parts.GetData() + BackwardCompatiblePartCount == PartIt);
}
}
uint32 GetTypeHash(const FBlendStateInitializerRHI& Initializer)
{
uint32 Hash = GetTypeHash(Initializer.bUseIndependentRenderTargetBlendStates);
Hash = HashCombine(Hash, Initializer.bUseAlphaToCoverage);
for (int32 i = 0; i < MaxSimultaneousRenderTargets; ++i)
{
Hash = HashCombine(Hash, GetTypeHash(Initializer.RenderTargets[i]));
}
return Hash;
}
bool operator== (const FBlendStateInitializerRHI& A, const FBlendStateInitializerRHI& B)
{
bool bSame = A.bUseIndependentRenderTargetBlendStates == B.bUseIndependentRenderTargetBlendStates;
bSame = bSame && A.bUseAlphaToCoverage == B.bUseAlphaToCoverage;
for (int32 i = 0; i < MaxSimultaneousRenderTargets && bSame; ++i)
{
bSame = bSame && A.RenderTargets[i] == B.RenderTargets[i];
}
return bSame;
}
FString FBlendStateInitializerRHI::FRenderTarget::ToString() const
{
return FString::Printf(TEXT("%u %u %u %u %u %u %u ")
, uint32(ColorBlendOp)
, uint32(ColorSrcBlend)
, uint32(ColorDestBlend)
, uint32(AlphaBlendOp)
, uint32(AlphaSrcBlend)
, uint32(AlphaDestBlend)
, uint32(ColorWriteMask)
);
}
void FBlendStateInitializerRHI::FRenderTarget::FromString(const TArray<FString>& Parts, int32 Index)
{
check(Index + NUM_STRING_FIELDS <= Parts.Num());
LexFromString((uint8&)ColorBlendOp, *Parts[Index++]);
LexFromString((uint8&)ColorSrcBlend, *Parts[Index++]);
LexFromString((uint8&)ColorDestBlend, *Parts[Index++]);
LexFromString((uint8&)AlphaBlendOp, *Parts[Index++]);
LexFromString((uint8&)AlphaSrcBlend, *Parts[Index++]);
LexFromString((uint8&)AlphaDestBlend, *Parts[Index++]);
LexFromString((uint8&)ColorWriteMask, *Parts[Index++]);
}
void FBlendStateInitializerRHI::FRenderTarget::FromString(TArrayView<const FStringView> Parts)
{
check(Parts.Num() == NUM_STRING_FIELDS);
const FStringView* PartIt = Parts.GetData();
LexFromString((uint8&)ColorBlendOp, *PartIt++);
LexFromString((uint8&)ColorSrcBlend, *PartIt++);
LexFromString((uint8&)ColorDestBlend, *PartIt++);
LexFromString((uint8&)AlphaBlendOp, *PartIt++);
LexFromString((uint8&)AlphaSrcBlend, *PartIt++);
LexFromString((uint8&)AlphaDestBlend, *PartIt++);
LexFromString((uint8&)ColorWriteMask, *PartIt++);
}
uint32 GetTypeHash(const FBlendStateInitializerRHI::FRenderTarget& Initializer)
{
uint32 Hash = GetTypeHash(Initializer.ColorBlendOp);
Hash = HashCombine(Hash, GetTypeHash(Initializer.ColorDestBlend));
Hash = HashCombine(Hash, GetTypeHash(Initializer.ColorSrcBlend));
Hash = HashCombine(Hash, GetTypeHash(Initializer.AlphaBlendOp));
Hash = HashCombine(Hash, GetTypeHash(Initializer.AlphaDestBlend));
Hash = HashCombine(Hash, GetTypeHash(Initializer.AlphaSrcBlend));
Hash = HashCombine(Hash, GetTypeHash(Initializer.ColorWriteMask));
return Hash;
}
bool operator==(const FBlendStateInitializerRHI::FRenderTarget& A, const FBlendStateInitializerRHI::FRenderTarget& B)
{
bool bSame =
A.ColorBlendOp == B.ColorBlendOp &&
A.ColorDestBlend == B.ColorDestBlend &&
A.ColorSrcBlend == B.ColorSrcBlend &&
A.AlphaBlendOp == B.AlphaBlendOp &&
A.AlphaDestBlend == B.AlphaDestBlend &&
A.AlphaSrcBlend == B.AlphaSrcBlend &&
A.ColorWriteMask == B.ColorWriteMask;
return bSame;
}
#if RHI_ENABLE_RESOURCE_INFO
static FCriticalSection GRHIResourceTrackingCriticalSection;
static TSet<FRHIResource*> GRHITrackedResources;
static bool GRHITrackingResources = false;
void FRHIResource::BeginTrackingResource(FRHIResource* InResource)
{
if (GRHITrackingResources)
{
LLM_SCOPE_BYNAME(TEXT("RHIMisc/ResourceTracking"));
FScopeLock Lock(&GRHIResourceTrackingCriticalSection);
InResource->bBeingTracked = true;
GRHITrackedResources.Add(InResource);
}
}
void FRHIResource::EndTrackingResource(FRHIResource* InResource)
{
if (InResource->bBeingTracked)
{
FScopeLock Lock(&GRHIResourceTrackingCriticalSection);
GRHITrackedResources.Remove(InResource);
InResource->bBeingTracked = false;
}
}
void FRHIResource::StartTrackingAllResources()
{
GRHITrackingResources = true;
}
void FRHIResource::StopTrackingAllResources()
{
FScopeLock Lock(&GRHIResourceTrackingCriticalSection);
for (FRHIResource* Resource : GRHITrackedResources)
{
if (Resource)
{
Resource->bBeingTracked = false;
}
}
GRHITrackedResources.Empty();
GRHITrackingResources = false;
}
struct FRHIResourceTypeName
{
ERHIResourceType Type;
const TCHAR* Name;
};
static const FRHIResourceTypeName GRHIResourceTypeNames[] =
{
#define RHI_RESOURCE_TYPE_DEF(Name) { RRT_##Name, TEXT(#Name) }
RHI_RESOURCE_TYPE_DEF(None),
RHI_RESOURCE_TYPE_DEF(SamplerState),
RHI_RESOURCE_TYPE_DEF(RasterizerState),
RHI_RESOURCE_TYPE_DEF(DepthStencilState),
RHI_RESOURCE_TYPE_DEF(BlendState),
RHI_RESOURCE_TYPE_DEF(VertexDeclaration),
RHI_RESOURCE_TYPE_DEF(VertexShader),
RHI_RESOURCE_TYPE_DEF(MeshShader),
RHI_RESOURCE_TYPE_DEF(AmplificationShader),
RHI_RESOURCE_TYPE_DEF(PixelShader),
RHI_RESOURCE_TYPE_DEF(GeometryShader),
RHI_RESOURCE_TYPE_DEF(RayTracingShader),
RHI_RESOURCE_TYPE_DEF(ComputeShader),
RHI_RESOURCE_TYPE_DEF(GraphicsPipelineState),
RHI_RESOURCE_TYPE_DEF(ComputePipelineState),
RHI_RESOURCE_TYPE_DEF(RayTracingPipelineState),
RHI_RESOURCE_TYPE_DEF(BoundShaderState),
RHI_RESOURCE_TYPE_DEF(UniformBufferLayout),
RHI_RESOURCE_TYPE_DEF(UniformBuffer),
RHI_RESOURCE_TYPE_DEF(Buffer),
RHI_RESOURCE_TYPE_DEF(Texture),
RHI_RESOURCE_TYPE_DEF(Texture2D),
RHI_RESOURCE_TYPE_DEF(Texture2DArray),
RHI_RESOURCE_TYPE_DEF(Texture3D),
RHI_RESOURCE_TYPE_DEF(TextureCube),
RHI_RESOURCE_TYPE_DEF(TextureReference),
RHI_RESOURCE_TYPE_DEF(TimestampCalibrationQuery),
RHI_RESOURCE_TYPE_DEF(GPUFence),
RHI_RESOURCE_TYPE_DEF(RenderQuery),
RHI_RESOURCE_TYPE_DEF(RenderQueryPool),
RHI_RESOURCE_TYPE_DEF(ComputeFence),
RHI_RESOURCE_TYPE_DEF(Viewport),
RHI_RESOURCE_TYPE_DEF(UnorderedAccessView),
RHI_RESOURCE_TYPE_DEF(ShaderResourceView),
RHI_RESOURCE_TYPE_DEF(RayTracingAccelerationStructure),
RHI_RESOURCE_TYPE_DEF(StagingBuffer),
RHI_RESOURCE_TYPE_DEF(CustomPresent),
RHI_RESOURCE_TYPE_DEF(ShaderLibrary),
RHI_RESOURCE_TYPE_DEF(PipelineBinaryLibrary),
};
static ERHIResourceType RHIResourceTypeFromString(const FString& InString)
{
for (const auto& TypeName : GRHIResourceTypeNames)
{
if (InString.Equals(TypeName.Name, ESearchCase::IgnoreCase))
{
return TypeName.Type;
}
}
return RRT_None;
}
static const TCHAR* StringFromRHIResourceType(ERHIResourceType ResourceType)
{
for (const auto& TypeName : GRHIResourceTypeNames)
{
if (TypeName.Type == ResourceType)
{
return TypeName.Name;
}
}
return TEXT("<unknown>");
}
enum class EBooleanFilter
{
No,
Yes,
All
};
static EBooleanFilter ParseBooleanFilter(const FString& InText)
{
if (InText.Equals(TEXT("No"), ESearchCase::IgnoreCase))
{
return EBooleanFilter::No;
}
if (InText.Equals(TEXT("Yes"), ESearchCase::IgnoreCase))
{
return EBooleanFilter::Yes;
}
if (InText.Equals(TEXT("All"), ESearchCase::IgnoreCase))
{
return EBooleanFilter::All;
}
return EBooleanFilter::No;
}
static FAutoConsoleCommandWithWorldArgsAndOutputDevice GDumpRHIResourceCountsCmd(
TEXT("rhi.DumpResourceCounts"),
TEXT("Dumps RHI resource counts to the log"),
FConsoleCommandWithWorldArgsAndOutputDeviceDelegate::CreateStatic([](const TArray<FString>& Args, UWorld*, FOutputDevice& OutputDevice)
{
int32 ResourceCounts[RRT_Num]{};
int32 TotalResources{};
FRHIResourceInfo ResourceInfo;
{
FScopeLock Lock(&GRHIResourceTrackingCriticalSection);
TotalResources = GRHITrackedResources.Num();
for (const FRHIResource* Resource : GRHITrackedResources)
{
if (Resource)
{
ERHIResourceType ResourceType = Resource->GetType();
if (ResourceType > 0 && ResourceType < RRT_Num)
{
ResourceCounts[ResourceType]++;
}
}
}
}
FBufferedOutputDevice BufferedOutput;
FName CategoryName(TEXT("RHIResources"));
BufferedOutput.CategorizedLogf(CategoryName, ELogVerbosity::Log, TEXT("RHIResource Counts"));
for (int32 Index = 0; Index < RRT_Num; Index++)
{
const int32 CurrentCount = ResourceCounts[Index];
if (CurrentCount > 0)
{
BufferedOutput.CategorizedLogf(CategoryName, ELogVerbosity::Log, TEXT("%s: %d"),
StringFromRHIResourceType((ERHIResourceType)Index),
CurrentCount);
}
}
BufferedOutput.CategorizedLogf(CategoryName, ELogVerbosity::Log, TEXT("Total: %d"), TotalResources);
BufferedOutput.RedirectTo(OutputDevice);
}));
static FAutoConsoleCommandWithWorldArgsAndOutputDevice GDumpRHIResourceMemoryCmd(
TEXT("rhi.DumpResourceMemory"),
TEXT("Dumps RHI resource memory stats to the log\n")
TEXT("Usage: rhi.DumpResourceMemory [<Number To Show>] [all] [summary] [Name=<Filter Text>] [Type=<RHI Resource Type>] [Transient=<no, yes, or all> [csv]"),
FConsoleCommandWithWorldArgsAndOutputDeviceDelegate::CreateStatic([](const TArray<FString>& Args, UWorld*, FOutputDevice& OutputDevice)
{
FString NameFilter;
ERHIResourceType TypeFilter = RRT_None;
EBooleanFilter TransientFilter = EBooleanFilter::No;
int32 NumberOfResourcesToShow = 50;
bool bUseCSVOutput = false;
bool bSummaryOutput = false;
for (const FString& Argument : Args)
{
if (Argument.Equals(TEXT("all"), ESearchCase::IgnoreCase))
{
NumberOfResourcesToShow = -1;
}
else if (Argument.Equals(TEXT("-csv"), ESearchCase::IgnoreCase))
{
bUseCSVOutput = true;
}
else if (Argument.StartsWith(TEXT("Name="), ESearchCase::IgnoreCase))
{
NameFilter = Argument.RightChop(5);
}
else if (Argument.StartsWith(TEXT("Type="), ESearchCase::IgnoreCase))
{
TypeFilter = RHIResourceTypeFromString(Argument.RightChop(5));
}
else if (Argument.StartsWith(TEXT("Transient="), ESearchCase::IgnoreCase))
{
TransientFilter = ParseBooleanFilter(Argument.RightChop(10));
}
else if (FCString::IsNumeric(*Argument))
{
LexFromString(NumberOfResourcesToShow, *Argument);
}
else if (Argument.Equals(TEXT("summary"), ESearchCase::IgnoreCase))
{
// Respects name, type and transient filters but only reports total sizes.
// Does not report a list of individual resources.
bSummaryOutput = true;
NumberOfResourcesToShow = -1;
}
else
{
NameFilter = Argument;
}
}
// Summary output and csv are mutually exclusive. So if both are on, summary takes precedence
if (bSummaryOutput && bUseCSVOutput)
{
bUseCSVOutput = false;
}
TCHAR ResourceNameBuffer[FName::StringBufferSize];
auto ShouldIncludeResource = [&](const FRHIResource* Resource, const FRHIResourceInfo& ResourceInfo) -> bool
{
if (!NameFilter.IsEmpty())
{
if (ResourceInfo.Name.ToString(ResourceNameBuffer) == 0 || UE::String::FindFirst(ResourceNameBuffer, *NameFilter, ESearchCase::IgnoreCase) == INDEX_NONE)
{
return false;
}
}
if (TypeFilter != RRT_None)
{
if (TypeFilter == RRT_Texture)
{
if (ResourceInfo.Type != RRT_Texture2D &&
ResourceInfo.Type != RRT_Texture2DArray &&
ResourceInfo.Type != RRT_Texture3D &&
ResourceInfo.Type != RRT_TextureCube)
{
return false;
}
}
else if (TypeFilter != ResourceInfo.Type)
{
return false;
}
}
if (TransientFilter != EBooleanFilter::All)
{
const bool bAllowedFlag = TransientFilter == EBooleanFilter::Yes ? true : false;
if (ResourceInfo.IsTransient != bAllowedFlag)
{
return false;
}
}
return true;
};
struct FLocalResourceEntry
{
const FRHIResource* Resource;
FRHIResourceInfo ResourceInfo;
};
TArray<FLocalResourceEntry> Resources;
FScopeLock Lock(&GRHIResourceTrackingCriticalSection);
int32 TotalResourcesWithInfo = 0;
int32 TotalTrackedResources = 0;
int64 TotalTrackedResourceSize = 0;
int64 TotalTrackedTransientResourceSize = 0;
{
FRHIResourceInfo ResourceInfo;
TotalTrackedResources = GRHITrackedResources.Num();
for (const FRHIResource* Resource : GRHITrackedResources)
{
if (Resource && Resource->GetResourceInfo(ResourceInfo))
{
if (ShouldIncludeResource(Resource, ResourceInfo))
{
Resources.Emplace(FLocalResourceEntry{ Resource, ResourceInfo });
}
TotalResourcesWithInfo++;
if (ResourceInfo.IsTransient)
{
TotalTrackedTransientResourceSize += ResourceInfo.VRamAllocation.AllocationSize;
}
else
{
TotalTrackedResourceSize += ResourceInfo.VRamAllocation.AllocationSize;
}
}
}
}
const int32 NumberOfResourcesBeforeNumberFilter = Resources.Num();
if (NumberOfResourcesToShow < 0 || NumberOfResourcesToShow > Resources.Num())
{
NumberOfResourcesToShow = Resources.Num();
}
Resources.Sort([](const FLocalResourceEntry& EntryA, const FLocalResourceEntry& EntryB)
{
return EntryA.ResourceInfo.VRamAllocation.AllocationSize > EntryB.ResourceInfo.VRamAllocation.AllocationSize;
});
FBufferedOutputDevice BufferedOutput;
FName CategoryName(TEXT("RHIResources"));
if (bUseCSVOutput)
{
BufferedOutput.CategorizedLogf(CategoryName, ELogVerbosity::Log, TEXT("Name,Type,Size,Transient,Streaming,RenderTarget,UAV,\"Raytracing Acceleration Structure\""));
}
else
{
if (bSummaryOutput == false)
{
BufferedOutput.CategorizedLogf(CategoryName, ELogVerbosity::Log, TEXT("Tracked RHIResources (%d total with info, %d total tracked)"), TotalResourcesWithInfo, TotalTrackedResources);
}
if (NumberOfResourcesToShow != NumberOfResourcesBeforeNumberFilter)
{
BufferedOutput.CategorizedLogf(CategoryName, ELogVerbosity::Log, TEXT("Showing %d of %d matched resources"), NumberOfResourcesToShow, NumberOfResourcesBeforeNumberFilter);
}
}
int64 TotalShownResourceSize = 0;
for (int32 Index = 0; Index < Resources.Num(); Index++)
{
if (Index < NumberOfResourcesToShow)
{
const FRHIResourceInfo& ResourceInfo = Resources[Index].ResourceInfo;
ResourceInfo.Name.ToString(ResourceNameBuffer);
const TCHAR* ResourceType = StringFromRHIResourceType(ResourceInfo.Type);
const int64 SizeInBytes = ResourceInfo.VRamAllocation.AllocationSize;
bool bTransient = ResourceInfo.IsTransient;
bool bStreaming = false;
bool bRT = false;
bool bDS = false;
bool bUAV = false;
bool bRTAS = false;
bool bIsTexture = ResourceInfo.Type == RRT_Texture2D ||
ResourceInfo.Type == RRT_Texture2DArray ||
ResourceInfo.Type == RRT_Texture3D ||
ResourceInfo.Type == RRT_TextureCube;
if (bIsTexture)
{
FRHITexture* Texture = (FRHITexture*)Resources[Index].Resource;
bRT = EnumHasAnyFlags(Texture->GetFlags(), TexCreate_RenderTargetable);
bDS = EnumHasAnyFlags(Texture->GetFlags(), TexCreate_DepthStencilTargetable);
bUAV = EnumHasAnyFlags(Texture->GetFlags(), TexCreate_UAV);
bStreaming = EnumHasAnyFlags(Texture->GetFlags(), TexCreate_Streamable);
}
else if (ResourceInfo.Type == RRT_Buffer)
{
FRHIBuffer* Buffer = (FRHIBuffer*)Resources[Index].Resource;
bUAV = EnumHasAnyFlags((EBufferUsageFlags)Buffer->GetUsage(), BUF_UnorderedAccess);
bRTAS = EnumHasAnyFlags((EBufferUsageFlags)Buffer->GetUsage(), BUF_AccelerationStructure);
}
if (bSummaryOutput == false)
{
if (bUseCSVOutput)
{
BufferedOutput.CategorizedLogf(CategoryName, ELogVerbosity::Log, TEXT("%s,%s,%.9f,%s,%s,%s,%s,%s"),
ResourceNameBuffer,
ResourceType,
SizeInBytes / double(1 << 20),
bTransient ? TEXT("Yes") : TEXT("No"),
bStreaming ? TEXT("Yes") : TEXT("No"),
(bRT || bDS) ? TEXT("Yes") : TEXT("No"),
bUAV ? TEXT("Yes") : TEXT("No"),
bRTAS ? TEXT("Yes") : TEXT("No"));
}
else
{
FString ResoureFlags;
bool bHasFlag = false;
if (bTransient)
{
ResoureFlags += "Transient";
bHasFlag = true;
}
if (bStreaming)
{
ResoureFlags += bHasFlag ? "|Streaming" : "Streaming";
bHasFlag = true;
}
if (bRT)
{
ResoureFlags += bHasFlag ? "|RT" : "RT";
bHasFlag = true;
}
else if (bDS)
{
ResoureFlags += bHasFlag ? "|DS" : "DS";
bHasFlag = true;
}
if (bUAV)
{
ResoureFlags += bHasFlag ? "|UAV" : "UAV";
bHasFlag = true;
}
if (bRTAS)
{
ResoureFlags += bHasFlag ? "|RTAS" : "RTAS";
bHasFlag = true;
}
BufferedOutput.CategorizedLogf(CategoryName, ELogVerbosity::Log, TEXT("Name: %s - Type: %s - Size: %.9f MB - Flags: %s"),
ResourceNameBuffer,
ResourceType,
SizeInBytes / double(1 << 20),
bHasFlag ? *ResoureFlags : TEXT("None"));
}
}
TotalShownResourceSize += SizeInBytes;
}
}
if (!bUseCSVOutput)
{
const double TotalNonTransientSizeF = TotalTrackedResourceSize / double(1 << 20);
const double TotalTransientSizeF = TotalTrackedTransientResourceSize / double(1 << 20);
const double TotalSizeF = (TotalTrackedResourceSize + TotalTrackedTransientResourceSize) / double(1 << 20);
const double ShownSizeF = TotalShownResourceSize / double(1 << 20);
if (NumberOfResourcesToShow != TotalResourcesWithInfo)
{
double TotalSizeToUse = 0.0;
const TCHAR* ExtraText = TEXT("");
if (TransientFilter == EBooleanFilter::No)
{
TotalSizeToUse = TotalNonTransientSizeF;
ExtraText = TEXT(" non-transient");
}
else if (TransientFilter == EBooleanFilter::Yes)
{
TotalSizeToUse = TotalTransientSizeF;
ExtraText = TEXT(" transient");
}
else
{
TotalSizeToUse = TotalSizeF;
ExtraText = TEXT("");
}
if (TotalSizeToUse == 0.0)
{
TotalSizeToUse = TotalSizeF;
}
BufferedOutput.CategorizedLogf(CategoryName, ELogVerbosity::Log, TEXT("Shown %d entries%s. Size: %.2f/%.2f MB (%.2f%% of total%s)"),
NumberOfResourcesToShow, !NameFilter.IsEmpty() ? *FString::Printf(TEXT(" with name %s"), *NameFilter) : TEXT(""), ShownSizeF, TotalSizeToUse, 100.0 * ShownSizeF / TotalSizeToUse, ExtraText);
}
if (bSummaryOutput == false)
{
BufferedOutput.CategorizedLogf(CategoryName, ELogVerbosity::Log, TEXT("Total tracked resource size: %.9f MB"), TotalSizeF);
if (TotalTrackedTransientResourceSize > 0)
{
BufferedOutput.CategorizedLogf(CategoryName, ELogVerbosity::Log, TEXT(" Non-Transient: %.9f MB"), TotalNonTransientSizeF);
BufferedOutput.CategorizedLogf(CategoryName, ELogVerbosity::Log, TEXT(" Transient: %.9f MB"), TotalTransientSizeF);
}
}
}
BufferedOutput.RedirectTo(OutputDevice);
}));
#endif // RHI_ENABLE_RESOURCE_INFO
bool FRHIResource::Bypass()
{
return GRHICommandList.Bypass();
}
DECLARE_CYCLE_STAT(TEXT("Delete Resources"), STAT_DeleteResources, STATGROUP_RHICMDLIST);
int32 FRHIResource::FlushPendingDeletes(FRHICommandListImmediate& RHICmdList)
{
SCOPE_CYCLE_COUNTER(STAT_DeleteResources);
check(IsInRenderingThread());
#if ENABLE_RHI_VALIDATION
if (GDynamicRHI)
{
// Submit all remaining work to the GPU. This also ensures that validation RHI barrier tracking
// operations have been flushed before we delete any resources they could be referring to.
RHICmdList.SubmitCommandsHint();
}
#endif
TArray<FRHIResource*> DeletedResources;
TClosableMpscQueue<FRHIResource*>* PendingDeletesPtr = PendingDeletes.exchange(new TClosableMpscQueue<FRHIResource*>());
PendingDeletesPtr->Close([&DeletedResources](FRHIResource* Resource)
{
DeletedResources.Push(Resource);
});
PendingDeletesHPC.Delete(PendingDeletesPtr);
const int32 NumDeletes = DeletedResources.Num();
RHICmdList.EnqueueLambda([DeletedResources = MoveTemp(DeletedResources)](FRHICommandListImmediate& RHICmdList) mutable
{
if (GDynamicRHI)
{
GDynamicRHI->RHIPerFrameRHIFlushComplete();
}
for (FRHIResource* Resource : DeletedResources)
{
if (Resource->AtomicFlags.Deleteing())
{
FRHIResource::CurrentlyDeleting = Resource;
delete Resource;
}
}
});
return NumDeletes;
}
static_assert(ERHIZBuffer::FarPlane != ERHIZBuffer::NearPlane, "Near and Far planes must be different!");
static_assert((int32)ERHIZBuffer::NearPlane == 0 || (int32)ERHIZBuffer::NearPlane == 1, "Invalid Values for Near Plane, can only be 0 or 1!");
static_assert((int32)ERHIZBuffer::FarPlane == 0 || (int32)ERHIZBuffer::FarPlane == 1, "Invalid Values for Far Plane, can only be 0 or 1");
/**
* RHI configuration settings.
*/
static TAutoConsoleVariable<int32> ResourceTableCachingCvar(
TEXT("rhi.ResourceTableCaching"),
1,
TEXT("If 1, the RHI will cache resource table contents within a frame. Otherwise resource tables are rebuilt for every draw call.")
);
static TAutoConsoleVariable<int32> GSaveScreenshotAfterProfilingGPUCVar(
TEXT("r.ProfileGPU.Screenshot"),
1,
TEXT("Whether a screenshot should be taken when profiling the GPU. 0:off, 1:on (default)"),
ECVF_RenderThreadSafe);
static TAutoConsoleVariable<int32> GShowProfilerAfterProfilingGPUCVar(
TEXT("r.ProfileGPU.ShowUI"),
1,
TEXT("Whether the user interface profiler should be displayed after profiling the GPU.\n")
TEXT("The results will always go to the log/console\n")
TEXT("0:off, 1:on (default)"),
ECVF_RenderThreadSafe);
static TAutoConsoleVariable<float> GGPUHitchThresholdCVar(
TEXT("RHI.GPUHitchThreshold"),
100.0f,
TEXT("Threshold for detecting hitches on the GPU (in milliseconds).")
);
static TAutoConsoleVariable<int32> GCVarRHIRenderPass(
TEXT("r.RHIRenderPasses"),
0,
TEXT(""),
ECVF_Default);
static TAutoConsoleVariable<int32> CVarGPUCrashDebugging(
TEXT("r.GPUCrashDebugging"),
0,
TEXT("Enable vendor specific GPU crash analysis tools"),
ECVF_ReadOnly
);
static TAutoConsoleVariable<int32> CVarGPUCrashDump(
TEXT("r.GPUCrashDump"),
0,
TEXT("Enable vendor specific GPU crash dumps"),
ECVF_ReadOnly
);
static TAutoConsoleVariable<int32> CVarGPUCrashOnOutOfMemory(
TEXT("r.GPUCrashOnOutOfMemory"),
0,
TEXT("Enable crash reporting on GPU OOM"),
ECVF_ReadOnly
);
static TAutoConsoleVariable<int32> CVarGPUCrashDebuggingAftermathMarkers(
TEXT("r.GPUCrashDebugging.Aftermath.Markers"),
0,
TEXT("Enable draw event markers in Aftermath dumps"),
ECVF_ReadOnly
);
static TAutoConsoleVariable<int32> CVarGPUCrashDebuggingAftermathCallstack(
TEXT("r.GPUCrashDebugging.Aftermath.Callstack"),
0,
TEXT("Enable callstack capture in Aftermath dumps"),
ECVF_ReadOnly
);
static TAutoConsoleVariable<int32> CVarGPUCrashDebuggingAftermathResourceTracking(
TEXT("r.GPUCrashDebugging.Aftermath.ResourceTracking"),
0,
TEXT("Enable resource tracking for Aftermath dumps"),
ECVF_ReadOnly
);
static TAutoConsoleVariable<int32> CVarGPUCrashDebuggingAftermathTrackAll(
TEXT("r.GPUCrashDebugging.Aftermath.TrackAll"),
1,
TEXT("Enable maximum tracking for Aftermath dumps"),
ECVF_ReadOnly
);
static FAutoConsoleVariableRef CVarEnableVariableRateShading(
TEXT("r.VRS.Enable"),
GRHIVariableRateShadingEnabled,
TEXT("Toggle to enable Variable Rate Shading."),
ECVF_RenderThreadSafe);
static FAutoConsoleVariableRef CVarEnableAttachmentVariableRateShading(
TEXT("r.VRS.EnableImage"),
GRHIAttachmentVariableRateShadingEnabled,
TEXT("Toggle to enable image-based Variable Rate Shading."),
ECVF_RenderThreadSafe);
namespace RHIConfig
{
bool ShouldSaveScreenshotAfterProfilingGPU()
{
return GSaveScreenshotAfterProfilingGPUCVar.GetValueOnAnyThread() != 0;
}
bool ShouldShowProfilerAfterProfilingGPU()
{
return GShowProfilerAfterProfilingGPUCVar.GetValueOnAnyThread() != 0;
}
float GetGPUHitchThreshold()
{
return GGPUHitchThresholdCVar.GetValueOnAnyThread() * 0.001f;
}
}
/**
* RHI globals.
*/
bool GIsRHIInitialized = false;
int32 GRHIPersistentThreadGroupCount = 0;
int32 GMaxTextureMipCount = MAX_TEXTURE_MIP_COUNT;
bool GSupportsQuadBufferStereo = false;
FString GRHIAdapterName;
FString GRHIAdapterInternalDriverVersion;
FString GRHIAdapterUserDriverVersion;
FString GRHIAdapterDriverDate;
bool GRHIAdapterDriverOnDenyList = false;
uint32 GRHIVendorId = 0;
uint32 GRHIDeviceId = 0;
uint32 GRHIDeviceRevision = 0;
bool GRHIDeviceIsAMDPreGCNArchitecture = false;
bool GSupportsRenderDepthTargetableShaderResources = true;
TRHIGlobal<bool> GSupportsRenderTargetFormat_PF_G8(true);
TRHIGlobal<bool> GSupportsRenderTargetFormat_PF_FloatRGBA(true);
bool GSupportsShaderFramebufferFetch = false;
bool GSupportsShaderDepthStencilFetch = false;
bool GSupportsShaderMRTFramebufferFetch = false;
bool GSupportsPixelLocalStorage = false;
bool GSupportsTimestampRenderQueries = false;
bool GRHISupportsGPUTimestampBubblesRemoval = false;
bool GRHISupportsFrameCyclesBubblesRemoval = false;
bool GHardwareHiddenSurfaceRemoval = false;
bool GRHISupportsAsyncTextureCreation = false;
bool GRHISupportsQuadTopology = false;
bool GRHISupportsRectTopology = false;
bool GRHISupportsPrimitiveShaders = false;
bool GRHISupportsAtomicUInt64 = false;
bool GRHISupportsDX12AtomicUInt64 = false;
bool GRHISupportsPipelineStateSortKey = false;
bool GRHISupportsResummarizeHTile = false;
bool GRHISupportsExplicitHTile = false;
bool GRHISupportsExplicitFMask = false;
bool GRHISupportsDepthUAV = false;
bool GSupportsParallelRenderingTasksWithSeparateRHIThread = true;
bool GRHIThreadNeedsKicking = false;
int32 GRHIMaximumReccommendedOustandingOcclusionQueries = MAX_int32;
bool GRHISupportsExactOcclusionQueries = true;
bool GSupportsVolumeTextureRendering = true;
bool GSupportsSeparateRenderTargetBlendState = false;
bool GRHINeedsUnatlasedCSMDepthsWorkaround = false;
bool GSupportsTexture3D = true;
bool GSupportsMobileMultiView = false;
bool GSupportsImageExternal = false;
bool GRHISupportsDrawIndirect = true;
bool GRHISupportsMultithreading = false;
bool GRHISupportsUpdateFromBufferTexture = false;
bool GSupportsWideMRT = true;
bool GRHINeedsExtraDeletionLatency = false;
bool GRHIForceNoDeletionLatencyForStreamingTextures = false;
TRHIGlobal<int32> GMaxComputeDispatchDimension((1 << 16) - 1);
bool GRHILazyShaderCodeLoading = false;
bool GRHISupportsLazyShaderCodeLoading = false;
TRHIGlobal<int32> GMaxShadowDepthBufferSizeX(2048);
TRHIGlobal<int32> GMaxShadowDepthBufferSizeY(2048);
TRHIGlobal<int32> GMaxTextureDimensions(2048);
TRHIGlobal<int64> GMaxBufferDimensions(1<<27);
TRHIGlobal<int64> GMaxComputeSharedMemory(1<<15);
TRHIGlobal<int32> GMaxVolumeTextureDimensions(2048);
TRHIGlobal<int32> GMaxCubeTextureDimensions(2048);
TRHIGlobal<int32> GMaxWorkGroupInvocations(1024);
bool GRHISupportsRawViewsForAnyBuffer = false;
bool GRHISupportsRWTextureBuffers = true;
bool GRHISupportsVRS = false;
bool GRHISupportsLateVRSUpdate = false;
int32 GMaxTextureArrayLayers = 256;
int32 GMaxTextureSamplers = 16;
bool GUsingNullRHI = false;
int32 GDrawUPVertexCheckCount = MAX_int32;
int32 GDrawUPIndexCheckCount = MAX_int32;
bool GTriggerGPUProfile = false;
FString GGPUTraceFileName;
bool GRHISupportsTextureStreaming = false;
bool GSupportsDepthBoundsTest = false;
bool GSupportsEfficientAsyncCompute = false;
bool GRHISupportsBaseVertexIndex = true;
bool GRHISupportsFirstInstance = false;
bool GRHISupportsDynamicResolution = false;
bool GRHISupportsRayTracing = false;
bool GRHISupportsRayTracingPSOAdditions = false;
bool GRHISupportsRayTracingDispatchIndirect = false;
bool GRHISupportsRayTracingAsyncBuildAccelerationStructure = false;
bool GRHISupportsRayTracingAMDHitToken = false;
bool GRHISupportsInlineRayTracing = false;
bool GRHISupportsRayTracingShaders = false;
uint32 GRHIRayTracingAccelerationStructureAlignment = 0;
uint32 GRHIRayTracingScratchBufferAlignment = 0;
uint32 GRHIRayTracingShaderTableAlignment = 0;
uint32 GRHIRayTracingInstanceDescriptorSize = 0;
bool GRHISupportsWaveOperations = false;
int32 GRHIMinimumWaveSize = 0;
int32 GRHIMaximumWaveSize = 0;
bool GRHISupportsRHIThread = false;
bool GRHISupportsRHIOnTaskThread = false;
bool GRHISupportsParallelRHIExecute = false;
bool GSupportsParallelOcclusionQueries = false;
bool GSupportsTransientResourceAliasing = false;
bool GRHIRequiresRenderTargetForPixelShaderUAVs = false;
bool GRHISupportsUAVFormatAliasing = false;
bool GRHISupportsDirectGPUMemoryLock = false;
bool GRHISupportsMultithreadedShaderCreation = true;
bool GRHISupportsMSAADepthSampleAccess = false;
bool GRHISupportsBackBufferWithCustomDepthStencil = true;
bool GRHIIsHDREnabled = false;
bool GRHISupportsHDROutput = false;
bool GRHIVariableRateShadingEnabled = true;
bool GRHIAttachmentVariableRateShadingEnabled = true;
bool GRHISupportsPipelineVariableRateShading = false;
bool GRHISupportsAttachmentVariableRateShading = false;
bool GRHISupportsComplexVariableRateShadingCombinerOps = false;
bool GRHISupportsVariableRateShadingAttachmentArrayTextures = false;
int32 GRHIVariableRateShadingImageTileMaxWidth = 0;
int32 GRHIVariableRateShadingImageTileMaxHeight = 0;
int32 GRHIVariableRateShadingImageTileMinWidth = 0;
int32 GRHIVariableRateShadingImageTileMinHeight = 0;
EVRSImageDataType GRHIVariableRateShadingImageDataType = VRSImage_NotSupported;
EPixelFormat GRHIVariableRateShadingImageFormat = PF_Unknown;
bool GRHISupportsLateVariableRateShadingUpdate = false;
EPixelFormat GRHIHDRDisplayOutputFormat = PF_FloatRGBA;
FIntVector GRHIMaxDispatchThreadGroupsPerDimension(0, 0, 0);
uint64 GRHIPresentCounter = 1;
bool GRHISupportsArrayIndexFromAnyShader = false;
bool GRHISupportsStencilRefFromPixelShader = false;
bool GRHISupportsConservativeRasterization = false;
bool GRHISupportsPipelineFileCache = false;
bool GRHIDeviceIsIntegrated = false;
/** Whether we are profiling GPU hitches. */
bool GTriggerGPUHitchProfile = false;
bool GRHISupportsPixelShaderUAVs = true;
bool GRHISupportsMeshShadersTier0 = false;
bool GRHISupportsMeshShadersTier1 = false;
bool GRHISupportsShaderTimestamp = false;
bool GRHISupportsEfficientUploadOnResourceCreation = false;
bool GRHISupportsAsyncPipelinePrecompile = true;
bool GRHISupportsMapWriteNoOverwrite = false;
bool GRHISupportsSeparateDepthStencilCopyAccess = true;
bool GRHISupportsBindless = false;
FVertexElementTypeSupportInfo GVertexElementTypeSupport;
RHI_API int32 volatile GCurrentTextureMemorySize = 0;
RHI_API int32 volatile GCurrentRendertargetMemorySize = 0;
RHI_API int64 GTexturePoolSize = 0 * 1024 * 1024;
RHI_API int32 GPoolSizeVRAMPercentage = 0;
RHI_API uint64 GDemotedLocalMemorySize = 0;
RHI_API EShaderPlatform GShaderPlatformForFeatureLevel[ERHIFeatureLevel::Num] = {SP_NumPlatforms,SP_NumPlatforms,SP_NumPlatforms,SP_NumPlatforms,SP_NumPlatforms};
// simple stats about draw calls. GNum is the previous frame and
// GCurrent is the current frame.
// GCurrentNumDrawCallsRHIPtr points to the drawcall counter to increment
RHI_API int32 GCurrentNumDrawCallsRHI[MAX_NUM_GPUS] = {};
RHI_API int32 GNumDrawCallsRHI[MAX_NUM_GPUS] = {};
thread_local FRHIDrawCallsStatPtr GCurrentNumDrawCallsRHIPtr = &GCurrentNumDrawCallsRHI;
RHI_API int32 GCurrentNumPrimitivesDrawnRHI[MAX_NUM_GPUS] = {};
RHI_API int32 GNumPrimitivesDrawnRHI[MAX_NUM_GPUS] = {};
RHI_API uint64 GRHITransitionPrivateData_SizeInBytes = 0;
RHI_API uint64 GRHITransitionPrivateData_AlignInBytes = 0;
// By default, read only states and UAV states are allowed to participate in state merging.
ERHIAccess GRHIMergeableAccessMask = ERHIAccess::ReadOnlyMask | ERHIAccess::UAVMask;
// By default, only exclusively read only accesses are allowed.
ERHIAccess GRHIMultiPipelineMergeableAccessMask = ERHIAccess::ReadOnlyExclusiveMask;
/** Called once per frame only from within an RHI. */
void RHIPrivateBeginFrame()
{
for (int32 GPUIndex = 0; GPUIndex < MAX_NUM_GPUS; GPUIndex++)
{
GNumDrawCallsRHI[GPUIndex] = GCurrentNumDrawCallsRHI[GPUIndex];
}
#if CSV_PROFILER
// Only copy the display counters every so many frames to keep things more stable.
const int32 FramesUntilDisplayCopy = 30;
static int32 FrameCount = 0;
bool bCopyDisplayFrames = false;
++FrameCount;
if (FrameCount >= FramesUntilDisplayCopy)
{
bCopyDisplayFrames = true;
FrameCount = 0;
}
for (int32 Index=0; Index<FDrawCallCategoryName::NumCategory; ++Index)
{
FDrawCallCategoryName* CategoryName = FDrawCallCategoryName::Array[Index];
for (int32 GPUIndex = 0; GPUIndex < MAX_NUM_GPUS; GPUIndex++)
{
if (bCopyDisplayFrames)
{
FDrawCallCategoryName::DisplayCounts[Index][GPUIndex] = CategoryName->Counters[GPUIndex];
}
GNumDrawCallsRHI[GPUIndex] += CategoryName->Counters[GPUIndex];
}
// Multi-GPU support : CSV stats do not support MGPU yet
FCsvProfiler::RecordCustomStat(CategoryName->Name, CSV_CATEGORY_INDEX(DrawCall), CategoryName->Counters[0], ECsvCustomStatOp::Set);
for (int32 GPUIndex = 0; GPUIndex < MAX_NUM_GPUS; GPUIndex++)
{
CategoryName->Counters[GPUIndex] = 0;
}
}
#endif
for (int32 GPUIndex = 0; GPUIndex < MAX_NUM_GPUS; GPUIndex++)
{
GNumPrimitivesDrawnRHI[GPUIndex] = GCurrentNumPrimitivesDrawnRHI[GPUIndex];
}
// Multi-GPU support : CSV stats do not support MGPU yet
CSV_CUSTOM_STAT(RHI, DrawCalls, GNumDrawCallsRHI[0], ECsvCustomStatOp::Set);
CSV_CUSTOM_STAT(RHI, PrimitivesDrawn, GNumPrimitivesDrawnRHI[0], ECsvCustomStatOp::Set);
for (int32 GPUIndex = 0; GPUIndex < MAX_NUM_GPUS; GPUIndex++)
{
GCurrentNumDrawCallsRHI[GPUIndex] = GCurrentNumPrimitivesDrawnRHI[GPUIndex] = 0;
}
}
/** Whether to initialize 3D textures using a bulk data (or through a mip update if false). */
RHI_API bool GUseTexture3DBulkDataRHI = false;
//
// The current shader platform.
//
RHI_API EShaderPlatform GMaxRHIShaderPlatform = SP_PCD3D_SM5;
/** The maximum feature level supported on this machine */
RHI_API ERHIFeatureLevel::Type GMaxRHIFeatureLevel = ERHIFeatureLevel::SM5;
FName FeatureLevelNames[] =
{
FName(TEXT("ES2")),
FName(TEXT("ES3_1")),
FName(TEXT("SM4_REMOVED")),
FName(TEXT("SM5")),
FName(TEXT("SM6")),
};
static_assert(UE_ARRAY_COUNT(FeatureLevelNames) == ERHIFeatureLevel::Num, "Missing entry from feature level names.");
RHI_API bool GetFeatureLevelFromName(FName Name, ERHIFeatureLevel::Type& OutFeatureLevel)
{
for (int32 NameIndex = 0; NameIndex < UE_ARRAY_COUNT(FeatureLevelNames); NameIndex++)
{
if (FeatureLevelNames[NameIndex] == Name)
{
OutFeatureLevel = (ERHIFeatureLevel::Type)NameIndex;
return true;
}
}
OutFeatureLevel = ERHIFeatureLevel::Num;
return false;
}
RHI_API void GetFeatureLevelName(ERHIFeatureLevel::Type InFeatureLevel, FString& OutName)
{
check(InFeatureLevel < UE_ARRAY_COUNT(FeatureLevelNames));
if (InFeatureLevel < UE_ARRAY_COUNT(FeatureLevelNames))
{
FeatureLevelNames[(int32)InFeatureLevel].ToString(OutName);
}
else
{
OutName = TEXT("InvalidFeatureLevel");
}
}
static FName InvalidFeatureLevelName(TEXT("InvalidFeatureLevel"));
RHI_API void GetFeatureLevelName(ERHIFeatureLevel::Type InFeatureLevel, FName& OutName)
{
check(InFeatureLevel < UE_ARRAY_COUNT(FeatureLevelNames));
if (InFeatureLevel < UE_ARRAY_COUNT(FeatureLevelNames))
{
OutName = FeatureLevelNames[(int32)InFeatureLevel];
}
else
{
OutName = InvalidFeatureLevelName;
}
}
FName ShadingPathNames[] =
{
FName(TEXT("Deferred")),
FName(TEXT("Forward")),
FName(TEXT("Mobile")),
};
static_assert(UE_ARRAY_COUNT(ShadingPathNames) == ERHIShadingPath::Num, "Missing entry from shading path names.");
RHI_API bool GetShadingPathFromName(FName Name, ERHIShadingPath::Type& OutShadingPath)
{
for (int32 NameIndex = 0; NameIndex < UE_ARRAY_COUNT(ShadingPathNames); NameIndex++)
{
if (ShadingPathNames[NameIndex] == Name)
{
OutShadingPath = (ERHIShadingPath::Type)NameIndex;
return true;
}
}
OutShadingPath = ERHIShadingPath::Num;
return false;
}
RHI_API void GetShadingPathName(ERHIShadingPath::Type InShadingPath, FString& OutName)
{
check(InShadingPath < UE_ARRAY_COUNT(ShadingPathNames));
if (InShadingPath < UE_ARRAY_COUNT(ShadingPathNames))
{
ShadingPathNames[(int32)InShadingPath].ToString(OutName);
}
else
{
OutName = TEXT("InvalidShadingPath");
}
}
static FName InvalidShadingPathName(TEXT("InvalidShadingPath"));
RHI_API void GetShadingPathName(ERHIShadingPath::Type InShadingPath, FName& OutName)
{
check(InShadingPath < UE_ARRAY_COUNT(ShadingPathNames));
if (InShadingPath < UE_ARRAY_COUNT(ShadingPathNames))
{
OutName = ShadingPathNames[(int32)InShadingPath];
}
else
{
OutName = InvalidShadingPathName;
}
}
static FName NAME_PLATFORM_WINDOWS(TEXT("Windows"));
static FName NAME_PLATFORM_XBOXONE(TEXT("XboxOne"));
static FName NAME_PLATFORM_ANDROID(TEXT("Android"));
static FName NAME_PLATFORM_IOS(TEXT("IOS"));
static FName NAME_PLATFORM_MAC(TEXT("Mac"));
static FName NAME_PLATFORM_TVOS(TEXT("TVOS"));
// @todo platplug: This is still here, only being used now by UMaterialShaderQualitySettings::GetOrCreatePlatformSettings
// since I have moved the other uses to FindTargetPlatformWithSupport
// But I'd like to delete it anyway!
FName ShaderPlatformToPlatformName(EShaderPlatform Platform)
{
switch (Platform)
{
case SP_PCD3D_SM6:
case SP_PCD3D_SM5:
case SP_PCD3D_ES3_1:
case SP_OPENGL_PCES3_1:
case SP_VULKAN_PCES3_1:
case SP_VULKAN_SM5:
case SP_D3D_ES3_1_HOLOLENS:
return NAME_PLATFORM_WINDOWS;
case SP_VULKAN_ES3_1_ANDROID:
case SP_VULKAN_SM5_ANDROID:
case SP_OPENGL_ES3_1_ANDROID:
return NAME_PLATFORM_ANDROID;
case SP_METAL:
case SP_METAL_MRT:
return NAME_PLATFORM_IOS;
case SP_METAL_SM5:
case SP_METAL_MACES3_1:
case SP_METAL_MRT_MAC:
return NAME_PLATFORM_MAC;
case SP_METAL_TVOS:
case SP_METAL_MRT_TVOS:
return NAME_PLATFORM_TVOS;
default:
if (FStaticShaderPlatformNames::IsStaticPlatform(Platform))
{
return FStaticShaderPlatformNames::Get().GetPlatformName(Platform);
}
else
{
return NAME_None;
}
}
}
FName LegacyShaderPlatformToShaderFormat(EShaderPlatform Platform)
{
return ShaderPlatformToShaderFormatName(Platform);
}
EShaderPlatform ShaderFormatToLegacyShaderPlatform(FName ShaderFormat)
{
return ShaderFormatNameToShaderPlatform(ShaderFormat);
}
RHI_API bool IsRHIDeviceAMD()
{
check(GRHIVendorId != 0);
// AMD's drivers tested on July 11 2013 have hitching problems with async resource streaming, setting single threaded for now until fixed.
return GRHIVendorId == 0x1002;
}
RHI_API bool IsRHIDeviceIntel()
{
check(GRHIVendorId != 0);
return GRHIVendorId == 0x8086;
}
RHI_API bool IsRHIDeviceNVIDIA()
{
check(GRHIVendorId != 0);
// NVIDIA GPUs are discrete and use DedicatedVideoMemory only.
return GRHIVendorId == 0x10DE;
}
RHI_API const TCHAR* RHIVendorIdToString()
{
return RHIVendorIdToString((EGpuVendorId)GRHIVendorId);
}
RHI_API const TCHAR* RHIVendorIdToString(EGpuVendorId VendorId)
{
switch (VendorId)
{
case EGpuVendorId::Amd: return TEXT("AMD");
case EGpuVendorId::ImgTec: return TEXT("ImgTec");
case EGpuVendorId::Nvidia: return TEXT("NVIDIA");
case EGpuVendorId::Arm: return TEXT("ARM");
case EGpuVendorId::Broadcom: return TEXT("Broadcom");
case EGpuVendorId::Qualcomm: return TEXT("Qualcomm");
case EGpuVendorId::Apple: return TEXT("Apple");
case EGpuVendorId::Intel: return TEXT("Intel");
case EGpuVendorId::Vivante: return TEXT("Vivante");
case EGpuVendorId::VeriSilicon: return TEXT("VeriSilicon");
case EGpuVendorId::Kazan: return TEXT("Kazan");
case EGpuVendorId::Codeplay: return TEXT("Codeplay");
case EGpuVendorId::Mesa: return TEXT("Mesa");
case EGpuVendorId::NotQueried: return TEXT("Not Queried");
default:
break;
}
return TEXT("Unknown");
}
RHI_API uint32 RHIGetMetalShaderLanguageVersion(const FStaticShaderPlatform Platform)
{
if (IsMetalPlatform(Platform))
{
if (IsPCPlatform(Platform))
{
static int32 MacMetalShaderLanguageVersion = -1;
if (MacMetalShaderLanguageVersion == -1)
{
if (!GConfig->GetInt(TEXT("/Script/MacTargetPlatform.MacTargetSettings"), TEXT("MetalLanguageVersion"), MacMetalShaderLanguageVersion, GEngineIni))
{
MacMetalShaderLanguageVersion = 0; // 0 means default EMacMetalShaderStandard::MacMetalSLStandard_Minimum
}
}
return MacMetalShaderLanguageVersion;
}
else
{
static int32 IOSMetalShaderLanguageVersion = -1;
if (IOSMetalShaderLanguageVersion == -1)
{
if (!GConfig->GetInt(TEXT("/Script/IOSRuntimeSettings.IOSRuntimeSettings"), TEXT("MetalLanguageVersion"), IOSMetalShaderLanguageVersion, GEngineIni))
{
IOSMetalShaderLanguageVersion = 0; // 0 means default EIOSMetalShaderStandard::IOSMetalSLStandard_Minimum
}
}
return IOSMetalShaderLanguageVersion;
}
}
return 0;
}
static ERHIFeatureLevel::Type GRHIMobilePreviewFeatureLevel = ERHIFeatureLevel::Num;
RHI_API void RHISetMobilePreviewFeatureLevel(ERHIFeatureLevel::Type MobilePreviewFeatureLevel)
{
check(GRHIMobilePreviewFeatureLevel == ERHIFeatureLevel::Num);
check(!GIsEditor);
GRHIMobilePreviewFeatureLevel = MobilePreviewFeatureLevel;
}
bool RHIGetPreviewFeatureLevel(ERHIFeatureLevel::Type& PreviewFeatureLevelOUT)
{
static bool bForceFeatureLevelES3_1 = !GIsEditor && (FParse::Param(FCommandLine::Get(), TEXT("FeatureLevelES31")) || FParse::Param(FCommandLine::Get(), TEXT("FeatureLevelES3_1")));
if (bForceFeatureLevelES3_1)
{
PreviewFeatureLevelOUT = ERHIFeatureLevel::ES3_1;
}
else if (!GIsEditor && GRHIMobilePreviewFeatureLevel != ERHIFeatureLevel::Num)
{
PreviewFeatureLevelOUT = GRHIMobilePreviewFeatureLevel;
}
else
{
return false;
}
return true;
}
RHI_API EPixelFormat RHIPreferredPixelFormatHint(EPixelFormat PreferredPixelFormat)
{
if (GDynamicRHI)
{
return GDynamicRHI->RHIPreferredPixelFormatHint(PreferredPixelFormat);
}
return PreferredPixelFormat;
}
RHI_API int32 RHIGetPreferredClearUAVRectPSResourceType(const FStaticShaderPlatform Platform)
{
if (IsMetalPlatform(Platform))
{
static constexpr uint32 METAL_TEXTUREBUFFER_SHADER_LANGUAGE_VERSION = 4;
if (METAL_TEXTUREBUFFER_SHADER_LANGUAGE_VERSION <= RHIGetMetalShaderLanguageVersion(Platform))
{
return 0; // BUFFER
}
}
return 1; // TEXTURE_2D
}
void FRHIRenderPassInfo::ConvertToRenderTargetsInfo(FRHISetRenderTargetsInfo& OutRTInfo) const
{
for (int32 Index = 0; Index < MaxSimultaneousRenderTargets; ++Index)
{
if (!ColorRenderTargets[Index].RenderTarget)
{
break;
}
OutRTInfo.ColorRenderTarget[Index].Texture = ColorRenderTargets[Index].RenderTarget;
ERenderTargetLoadAction LoadAction = GetLoadAction(ColorRenderTargets[Index].Action);
OutRTInfo.ColorRenderTarget[Index].LoadAction = LoadAction;
OutRTInfo.ColorRenderTarget[Index].StoreAction = GetStoreAction(ColorRenderTargets[Index].Action);
OutRTInfo.ColorRenderTarget[Index].ArraySliceIndex = ColorRenderTargets[Index].ArraySlice;
OutRTInfo.ColorRenderTarget[Index].MipIndex = ColorRenderTargets[Index].MipIndex;
++OutRTInfo.NumColorRenderTargets;
OutRTInfo.bClearColor |= (LoadAction == ERenderTargetLoadAction::EClear);
ensure(!OutRTInfo.bHasResolveAttachments || ColorRenderTargets[Index].ResolveTarget);
if (ColorRenderTargets[Index].ResolveTarget)
{
OutRTInfo.bHasResolveAttachments = true;
OutRTInfo.ColorResolveRenderTarget[Index] = OutRTInfo.ColorRenderTarget[Index];
OutRTInfo.ColorResolveRenderTarget[Index].Texture = ColorRenderTargets[Index].ResolveTarget;
}
}
ERenderTargetActions DepthActions = GetDepthActions(DepthStencilRenderTarget.Action);
ERenderTargetActions StencilActions = GetStencilActions(DepthStencilRenderTarget.Action);
ERenderTargetLoadAction DepthLoadAction = GetLoadAction(DepthActions);
ERenderTargetStoreAction DepthStoreAction = GetStoreAction(DepthActions);
ERenderTargetLoadAction StencilLoadAction = GetLoadAction(StencilActions);
ERenderTargetStoreAction StencilStoreAction = GetStoreAction(StencilActions);
OutRTInfo.DepthStencilRenderTarget = FRHIDepthRenderTargetView(DepthStencilRenderTarget.DepthStencilTarget,
DepthLoadAction,
GetStoreAction(DepthActions),
StencilLoadAction,
GetStoreAction(StencilActions),
DepthStencilRenderTarget.ExclusiveDepthStencil);
OutRTInfo.bClearDepth = (DepthLoadAction == ERenderTargetLoadAction::EClear);
OutRTInfo.bClearStencil = (StencilLoadAction == ERenderTargetLoadAction::EClear);
OutRTInfo.ShadingRateTexture = ShadingRateTexture;
OutRTInfo.ShadingRateTextureCombiner = ShadingRateTextureCombiner;
OutRTInfo.MultiViewCount = MultiViewCount;
}
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
void FRHIRenderPassInfo::Validate() const
{
int32 NumSamples = -1; // -1 means nothing found yet
int32 ColorIndex = 0;
for (; ColorIndex < MaxSimultaneousRenderTargets; ++ColorIndex)
{
const FColorEntry& Entry = ColorRenderTargets[ColorIndex];
if (Entry.RenderTarget)
{
// Ensure NumSamples matches amongst all color RTs
if (NumSamples == -1)
{
NumSamples = Entry.RenderTarget->GetNumSamples();
}
else
{
ensure(Entry.RenderTarget->GetNumSamples() == NumSamples);
}
ERenderTargetStoreAction Store = GetStoreAction(Entry.Action);
// Don't try to resolve a non-msaa
ensure(Store != ERenderTargetStoreAction::EMultisampleResolve || Entry.RenderTarget->GetNumSamples() > 1);
// Don't resolve to null
ensure(Store != ERenderTargetStoreAction::EMultisampleResolve || Entry.ResolveTarget);
if (Entry.ResolveTarget)
{
//ensure(Store == ERenderTargetStoreAction::EMultisampleResolve);
}
}
else
{
break;
}
}
int32 NumColorRenderTargets = ColorIndex;
for (; ColorIndex < MaxSimultaneousRenderTargets; ++ColorIndex)
{
// Gap in the sequence of valid render targets (ie RT0, null, RT2, ...)
ensureMsgf(!ColorRenderTargets[ColorIndex].RenderTarget, TEXT("Missing color render target on slot %d"), ColorIndex - 1);
}
if (DepthStencilRenderTarget.DepthStencilTarget)
{
// Ensure NumSamples matches with color RT
if (NumSamples != -1)
{
ensure(DepthStencilRenderTarget.DepthStencilTarget->GetNumSamples() == NumSamples);
}
ERenderTargetStoreAction DepthStore = GetStoreAction(GetDepthActions(DepthStencilRenderTarget.Action));
ERenderTargetStoreAction StencilStore = GetStoreAction(GetStencilActions(DepthStencilRenderTarget.Action));
bool bIsMSAAResolve = (DepthStore == ERenderTargetStoreAction::EMultisampleResolve) || (StencilStore == ERenderTargetStoreAction::EMultisampleResolve);
// Don't try to resolve a non-msaa
ensure(!bIsMSAAResolve || DepthStencilRenderTarget.DepthStencilTarget->GetNumSamples() > 1);
// Don't resolve to null
//ensure(DepthStencilRenderTarget.ResolveTarget || DepthStore != ERenderTargetStoreAction::EStore);
// Don't write to depth if read-only
//ensure(DepthStencilRenderTarget.ExclusiveDepthStencil.IsDepthWrite() || DepthStore != ERenderTargetStoreAction::EStore);
// This is not true for stencil. VK and Metal specify that the DontCare store action MAY leave the attachment in an undefined state.
/*ensure(DepthStencilRenderTarget.ExclusiveDepthStencil.IsStencilWrite() || StencilStore != ERenderTargetStoreAction::EStore);*/
// If we have a depthstencil target we MUST Store it or it will be undefined after rendering.
if (DepthStencilRenderTarget.DepthStencilTarget->GetFormat() != PF_D24)
{
// If this is DepthStencil we must store it out unless we are absolutely sure it will never be used again.
// it is valid to use a depthbuffer for performance and not need the results later.
//ensure(StencilStore == ERenderTargetStoreAction::EStore);
}
if (DepthStencilRenderTarget.ExclusiveDepthStencil.IsDepthWrite())
{
// this check is incorrect for mobile, depth/stencil is intermediate and we don't want to store it to main memory
//ensure(DepthStore == ERenderTargetStoreAction::EStore);
}
if (DepthStencilRenderTarget.ExclusiveDepthStencil.IsStencilWrite())
{
// this check is incorrect for mobile, depth/stencil is intermediate and we don't want to store it to main memory
//ensure(StencilStore == ERenderTargetStoreAction::EStore);
}
if (SubpassHint == ESubpassHint::DepthReadSubpass)
{
// for depth read sub-pass
// 1. render pass must have depth target
// 2. depth target must support InputAttachement
ensure(EnumHasAnyFlags(DepthStencilRenderTarget.DepthStencilTarget->GetFlags(), TexCreate_InputAttachmentRead));
}
}
else
{
ensure(DepthStencilRenderTarget.Action == EDepthStencilTargetActions::DontLoad_DontStore);
ensure(DepthStencilRenderTarget.ExclusiveDepthStencil == FExclusiveDepthStencil::DepthNop_StencilNop);
ensure(SubpassHint != ESubpassHint::DepthReadSubpass);
}
}
#endif
#define ValidateResourceDesc(expr, format, ...) \
if (bFatal) \
{ \
checkf(expr, format, ##__VA_ARGS__); \
} \
else if (!(expr)) \
{ \
return false;\
} \
// static
bool FRHITextureDesc::Validate(const FRHITextureCreateInfo& Desc, const TCHAR* Name, bool bFatal)
{
// Validate texture's pixel format.
{
ValidateResourceDesc(Desc.Format != PF_Unknown, TEXT("Illegal to create texture %s with an invalid pixel format."), Name);
ValidateResourceDesc(Desc.Format < PF_MAX, TEXT("Illegal to create texture %s with an invalid pixel format."), Name);
ValidateResourceDesc(GPixelFormats[Desc.Format].Supported,
TEXT("Failed to create texture %s with pixel format %s because it is not supported."),
Name,
GPixelFormats[Desc.Format].Name);
}
// Validate texture's extent.
{
int32 MaxDimension = (Desc.Dimension == ETextureDimension::TextureCube || Desc.Dimension == ETextureDimension::TextureCubeArray) ? GMaxCubeTextureDimensions : GMaxTextureDimensions;
ValidateResourceDesc(Desc.Extent.X > 0, TEXT("Texture %s's Extent.X=%d is invalid."), Name, Desc.Extent.X);
ValidateResourceDesc(Desc.Extent.X <= MaxDimension, TEXT("Texture %s's Extent.X=%d is too large."), Name, Desc.Extent.X);
ValidateResourceDesc(Desc.Extent.Y > 0, TEXT("Texture %s's Extent.Y=%d is invalid."), Name, Desc.Extent.Y);
ValidateResourceDesc(Desc.Extent.Y <= MaxDimension, TEXT("Texture %s's Extent.Y=%d is too large."), Name, Desc.Extent.Y);
}
// Validate texture's depth
if (Desc.Dimension == ETextureDimension::Texture3D)
{
ValidateResourceDesc(Desc.Depth > 0, TEXT("Texture %s's Depth=%d is invalid."), Name, int32(Desc.Depth));
ValidateResourceDesc(Desc.Depth <= GMaxTextureDimensions, TEXT("Texture %s's Extent.Depth=%d is too large."), Name, Desc.Depth);
}
else
{
ValidateResourceDesc(Desc.Depth == 1, TEXT("Texture %s's Depth=%d is invalid for Dimension=%s."), Name, int32(Desc.Depth), GetTextureDimensionString(Desc.Dimension));
}
// Validate texture's array size
if (Desc.Dimension == ETextureDimension::Texture2DArray || Desc.Dimension == ETextureDimension::TextureCubeArray)
{
ValidateResourceDesc(Desc.ArraySize > 0, TEXT("Texture %s's ArraySize=%d is invalid."), Name, Desc.ArraySize);
ValidateResourceDesc(Desc.ArraySize <= GMaxTextureArrayLayers, TEXT("Texture %s's Extent.ArraySize=%d is too large."), Name, int32(Desc.ArraySize));
}
else
{
ValidateResourceDesc(Desc.ArraySize == 1, TEXT("Texture %s's ArraySize=%d is invalid for Dimension=%s."), Name, Desc.ArraySize, GetTextureDimensionString(Desc.Dimension));
}
// Validate texture's samples count.
if (Desc.Dimension == ETextureDimension::Texture2D || Desc.Dimension == ETextureDimension::Texture2DArray)
{
ValidateResourceDesc(Desc.NumSamples > 0, TEXT("Texture %s's NumSamples=%d is invalid."), Name, Desc.NumSamples);
}
else
{
ValidateResourceDesc(Desc.NumSamples == 1, TEXT("Texture %s's NumSamples=%d is invalid for Dimension=%s."), Name, Desc.NumSamples, GetTextureDimensionString(Desc.Dimension));
}
// Validate texture's mips.
if (Desc.IsMultisample())
{
ValidateResourceDesc(Desc.NumMips == 1, TEXT("MSAA Texture %s's can only have one mip."), Name);
}
else
{
ValidateResourceDesc(Desc.NumMips > 0, TEXT("Texture %s's NumMips=%d is invalid."), Name, Desc.NumMips);
ValidateResourceDesc(Desc.NumMips <= GMaxTextureMipCount, TEXT("Texture %s's NumMips=%d is too large."), Name, Desc.NumMips);
}
return true;
}
// static
bool FRHITextureSRVCreateInfo::Validate(const FRHITextureDesc& TextureDesc, const FRHITextureSRVCreateInfo& TextureSRVDesc, const TCHAR* TextureName, bool bFatal)
{
if (TextureName == nullptr)
{
TextureName = TEXT("UnnamedTexture");
}
ValidateResourceDesc(TextureDesc.Flags & TexCreate_ShaderResource,
TEXT("Attempted to create SRV from texture %s which was not created with TexCreate_ShaderResource"),
TextureName);
// Validate the pixel format if overridden by the SRV's descriptor.
if (TextureSRVDesc.Format == PF_X24_G8)
{
// PF_X24_G8 is a bit of mess in the RHI, used to read the stencil, but have varying BlockBytes.
ValidateResourceDesc(TextureDesc.Format == PF_DepthStencil,
TEXT("PF_X24_G8 is only to read stencil from a PF_DepthStencil texture"));
}
else if (TextureSRVDesc.Format != PF_Unknown)
{
ValidateResourceDesc(TextureSRVDesc.Format < PF_MAX,
TEXT("Illegal to create SRV for texture %s with invalid FPooledRenderTargetDesc::Format."),
TextureName);
ValidateResourceDesc(GPixelFormats[TextureSRVDesc.Format].Supported,
TEXT("Failed to create SRV for texture %s with pixel format %s because it is not supported."),
TextureName, GPixelFormats[TextureSRVDesc.Format].Name);
EPixelFormat ResourcePixelFormat = TextureDesc.Format;
ValidateResourceDesc(
GPixelFormats[TextureSRVDesc.Format].BlockBytes == GPixelFormats[ResourcePixelFormat].BlockBytes &&
GPixelFormats[TextureSRVDesc.Format].BlockSizeX == GPixelFormats[ResourcePixelFormat].BlockSizeX &&
GPixelFormats[TextureSRVDesc.Format].BlockSizeY == GPixelFormats[ResourcePixelFormat].BlockSizeY &&
GPixelFormats[TextureSRVDesc.Format].BlockSizeZ == GPixelFormats[ResourcePixelFormat].BlockSizeZ,
TEXT("Failed to create SRV for texture %s with pixel format %s because it does not match the byte size of the texture's pixel format %s."),
TextureName, GPixelFormats[TextureSRVDesc.Format].Name, GPixelFormats[ResourcePixelFormat].Name);
}
ValidateResourceDesc((TextureSRVDesc.MipLevel + TextureSRVDesc.NumMipLevels) <= TextureDesc.NumMips,
TEXT("Failed to create SRV at mips %d-%d: the texture %s has only %d mip levels."),
TextureSRVDesc.MipLevel, (TextureSRVDesc.MipLevel + TextureSRVDesc.NumMipLevels), TextureName, TextureDesc.NumMips);
// Validate the array sloces
if (TextureDesc.IsTextureArray())
{
ValidateResourceDesc((TextureSRVDesc.FirstArraySlice + TextureSRVDesc.NumArraySlices) <= TextureDesc.ArraySize,
TEXT("Failed to create SRV at array slices %d-%d: the texture array %s has only %d slices."),
TextureSRVDesc.FirstArraySlice,
(TextureSRVDesc.FirstArraySlice + TextureSRVDesc.NumArraySlices),
TextureName,
TextureDesc.ArraySize);
}
else
{
ValidateResourceDesc(TextureSRVDesc.FirstArraySlice == 0,
TEXT("Failed to create SRV with FirstArraySlice=%d: the texture %s is not a texture array."),
TextureSRVDesc.FirstArraySlice, TextureName);
ValidateResourceDesc(TextureSRVDesc.NumArraySlices == 0,
TEXT("Failed to create SRV with NumArraySlices=%d: the texture %s is not a texture array."),
TextureSRVDesc.NumArraySlices, TextureName);
}
ValidateResourceDesc(TextureSRVDesc.MetaData != ERHITextureMetaDataAccess::FMask || GRHISupportsExplicitFMask,
TEXT("Failed to create FMask SRV for texture %s because the current RHI doesn't support it. Be sure to gate the call with GRHISupportsExplicitFMask."),
TextureName);
ValidateResourceDesc(TextureSRVDesc.MetaData != ERHITextureMetaDataAccess::HTile || GRHISupportsExplicitHTile,
TEXT("Failed to create HTile SRV for texture %s because the current RHI doesn't support it. Be sure to gate the call with GRHISupportsExplicitHTile."),
TextureName);
return true;
}
#undef ValidateResourceDesc
uint64 FRHITextureDesc::CalcMemorySizeEstimate(uint32 FirstMipIndex, uint32 LastMipIndex) const
{
#if DO_CHECK
Validate(*this, TEXT("CalcMemorySizeEstimate"), /* bFatal = */true);
#endif
check(FirstMipIndex < NumMips && FirstMipIndex <= LastMipIndex && LastMipIndex < NumMips);
uint64 MemorySize = 0;
for (uint32 MipIndex = FirstMipIndex; MipIndex <= LastMipIndex; ++MipIndex)
{
FIntVector MipSizeInBlocks = FIntVector(
FMath::DivideAndRoundUp(FMath::Max(Extent.X >> MipIndex, 1), GPixelFormats[Format].BlockSizeX),
FMath::DivideAndRoundUp(FMath::Max(Extent.Y >> MipIndex, 1), GPixelFormats[Format].BlockSizeY),
FMath::DivideAndRoundUp(FMath::Max(Depth >> MipIndex, 1), GPixelFormats[Format].BlockSizeZ)
);
uint32 NumBlocksInMip = MipSizeInBlocks.X * MipSizeInBlocks.Y * MipSizeInBlocks.Z;
MemorySize += NumBlocksInMip * GPixelFormats[Format].BlockBytes;
}
MemorySize *= ArraySize;
MemorySize *= NumSamples;
if (IsTextureCube())
{
MemorySize *= 6;
}
return MemorySize;
}
static FRHIPanicEvent RHIPanicEvent;
FRHIPanicEvent& RHIGetPanicDelegate()
{
return RHIPanicEvent;
}
#include "Misc/DataDrivenPlatformInfoRegistry.h"
FString LexToString(EShaderPlatform Platform, bool bError)
{
switch (Platform)
{
case SP_PCD3D_SM6: return TEXT("PCD3D_SM6");
case SP_PCD3D_SM5: return TEXT("PCD3D_SM5");
case SP_PCD3D_ES3_1: return TEXT("PCD3D_ES3_1");
case SP_OPENGL_PCES3_1: return TEXT("OPENGL_PCES3_1");
case SP_OPENGL_ES3_1_ANDROID: return TEXT("OPENGL_ES3_1_ANDROID");
case SP_METAL: return TEXT("METAL");
case SP_METAL_MRT: return TEXT("METAL_MRT");
case SP_METAL_TVOS: return TEXT("METAL_TVOS");
case SP_METAL_MRT_TVOS: return TEXT("METAL_MRT_TVOS");
case SP_METAL_MRT_MAC: return TEXT("METAL_MRT_MAC");
case SP_METAL_SM5: return TEXT("METAL_SM5");
case SP_METAL_MACES3_1: return TEXT("METAL_MACES3_1");
case SP_VULKAN_ES3_1_ANDROID: return TEXT("VULKAN_ES3_1_ANDROID");
case SP_VULKAN_PCES3_1: return TEXT("VULKAN_PCES3_1");
case SP_VULKAN_SM5: return TEXT("VULKAN_SM5");
case SP_VULKAN_SM5_ANDROID: return TEXT("VULKAN_SM5_ANDROID");
case SP_D3D_ES3_1_HOLOLENS: return TEXT("D3D_ES3_1_HOLOLENS");
default:
if (FStaticShaderPlatformNames::IsStaticPlatform(Platform))
{
return FStaticShaderPlatformNames::Get().GetShaderPlatform(Platform).ToString();
}
else
{
checkf(!bError, TEXT("Unknown or removed EShaderPlatform %d!"), (int32)Platform);
return TEXT("");
}
}
}
FString LexToString(EShaderPlatform Platform)
{
bool bError = true;
return LexToString(Platform, bError);
}
void LexFromString(EShaderPlatform& Value, const TCHAR* String)
{
Value = EShaderPlatform::SP_NumPlatforms;
for (uint8 i = 0; i < (uint8)EShaderPlatform::SP_NumPlatforms; ++i)
{
if (LexToString((EShaderPlatform)i, false).Equals(String, ESearchCase::IgnoreCase))
{
Value = (EShaderPlatform)i;
return;
}
}
}
FString LexToString(ERHIFeatureLevel::Type Level)
{
switch (Level)
{
case ERHIFeatureLevel::ES2_REMOVED:
return TEXT("ES2_REMOVED");
case ERHIFeatureLevel::ES3_1:
return TEXT("ES3_1");
case ERHIFeatureLevel::SM4_REMOVED:
return TEXT("SM4_REMOVED");
case ERHIFeatureLevel::SM5:
return TEXT("SM5");
case ERHIFeatureLevel::SM6:
return TEXT("SM6");
default:
break;
}
return TEXT("UnknownFeatureLevel");
}
const TCHAR* LexToString(ERHIDescriptorHeapType InHeapType)
{
switch (InHeapType)
{
default: checkNoEntry(); return TEXT("UnknownDescriptorHeapType");
case ERHIDescriptorHeapType::Standard: return TEXT("Standard");
case ERHIDescriptorHeapType::RenderTarget: return TEXT("RenderTarget");
case ERHIDescriptorHeapType::DepthStencil: return TEXT("DepthStencil");
case ERHIDescriptorHeapType::Sampler: return TEXT("Sampler");
}
}
const FName LANGUAGE_D3D("D3D");
const FName LANGUAGE_Metal("Metal");
const FName LANGUAGE_OpenGL("OpenGL");
const FName LANGUAGE_Vulkan("Vulkan");
const FName LANGUAGE_Sony("Sony");
const FName LANGUAGE_Nintendo("Nintendo");
RHI_API FGenericDataDrivenShaderPlatformInfo FGenericDataDrivenShaderPlatformInfo::Infos[SP_NumPlatforms];
// Gets a string from a section, or empty string if it didn't exist
static inline FString GetSectionString(const FConfigSection& Section, FName Key)
{
return Section.FindRef(Key).GetValue();
}
// Gets a bool from a section. It returns the original value if the setting does not exist
static inline bool GetSectionBool(const FConfigSection& Section, FName Key, bool OriginalValue)
{
const FConfigValue* ConfigValue = Section.Find(Key);
if (ConfigValue != nullptr)
{
return FCString::ToBool(*ConfigValue->GetValue());
}
else
{
return OriginalValue;
}
}
// Gets an integer from a section. It returns the original value if the setting does not exist
static inline uint32 GetSectionUint(const FConfigSection& Section, FName Key, uint32 OriginalValue)
{
const FConfigValue* ConfigValue = Section.Find(Key);
if (ConfigValue != nullptr)
{
return (uint32)FCString::Atoi(*ConfigValue->GetValue());
}
else
{
return OriginalValue;
}
}
// Gets an integer from a section. It returns the original value if the setting does not exist
static inline uint32 GetSectionFeatureSupport(const FConfigSection& Section, FName Key, uint32 OriginalValue)
{
const FConfigValue* ConfigValue = Section.Find(Key);
if (ConfigValue != nullptr)
{
FString Value = ConfigValue->GetValue();
if (Value == TEXT("Unsupported"))
{
return uint32(ERHIFeatureSupport::Unsupported);
}
if (Value == TEXT("RuntimeDependent"))
{
return uint32(ERHIFeatureSupport::RuntimeDependent);
}
else if (Value == TEXT("RuntimeGuaranteed"))
{
return uint32(ERHIFeatureSupport::RuntimeGuaranteed);
}
else
{
checkf(false, TEXT("Unknown ERHIFeatureSupport value \"%s\" for %s"), *Value, *Key.ToString());
}
}
return OriginalValue;
}
FRHIViewableResource* GetViewableResource(const FRHITransitionInfo& Info)
{
switch (Info.Type)
{
case FRHITransitionInfo::EType::Buffer:
case FRHITransitionInfo::EType::Texture:
return Info.ViewableResource;
case FRHITransitionInfo::EType::UAV:
return Info.UAV ? Info.UAV->GetParentResource() : nullptr;
}
return nullptr;
}
void FGenericDataDrivenShaderPlatformInfo::SetDefaultValues()
{
MaxFeatureLevel = ERHIFeatureLevel::Num;
bSupportsMSAA = true;
bNeedsToSwitchVerticalAxisOnMobileOpenGL = true;
bSupportsDOFHybridScattering = true;
bSupportsHZBOcclusion = true;
bSupportsWaterIndirectDraw = true;
bSupportsAsyncPipelineCompilation = true;
bSupportsManualVertexFetch = true;
bSupportsVolumeTextureAtomics = true;
}
void FGenericDataDrivenShaderPlatformInfo::ParseDataDrivenShaderInfo(const FConfigSection& Section, FGenericDataDrivenShaderPlatformInfo& Info)
{
Info.Language = *GetSectionString(Section, "Language");
GetFeatureLevelFromName(*GetSectionString(Section, "MaxFeatureLevel"), Info.MaxFeatureLevel);
#define GET_SECTION_BOOL_HELPER(SettingName) \
Info.SettingName = GetSectionBool(Section, #SettingName, Info.SettingName)
#define GET_SECTION_INT_HELPER(SettingName) \
Info.SettingName = GetSectionUint(Section, #SettingName, Info.SettingName)
#define GET_SECTION_SUPPORT_HELPER(SettingName) \
Info.SettingName = GetSectionFeatureSupport(Section, #SettingName, Info.SettingName)
GET_SECTION_BOOL_HELPER(bIsMobile);
GET_SECTION_BOOL_HELPER(bIsMetalMRT);
GET_SECTION_BOOL_HELPER(bIsPC);
GET_SECTION_BOOL_HELPER(bIsConsole);
GET_SECTION_BOOL_HELPER(bIsAndroidOpenGLES);
GET_SECTION_BOOL_HELPER(bSupportsDebugViewShaders);
GET_SECTION_BOOL_HELPER(bSupportsMobileMultiView);
GET_SECTION_BOOL_HELPER(bSupportsArrayTextureCompression);
GET_SECTION_BOOL_HELPER(bSupportsDistanceFields);
GET_SECTION_BOOL_HELPER(bSupportsDiaphragmDOF);
GET_SECTION_BOOL_HELPER(bSupportsRGBColorBuffer);
GET_SECTION_BOOL_HELPER(bSupportsCapsuleShadows);
GET_SECTION_BOOL_HELPER(bSupportsPercentageCloserShadows);
GET_SECTION_BOOL_HELPER(bSupportsVolumetricFog);
GET_SECTION_BOOL_HELPER(bSupportsIndexBufferUAVs);
GET_SECTION_BOOL_HELPER(bSupportsInstancedStereo);
GET_SECTION_BOOL_HELPER(bSupportsMultiView);
GET_SECTION_BOOL_HELPER(bSupportsMSAA);
GET_SECTION_BOOL_HELPER(bSupports4ComponentUAVReadWrite);
GET_SECTION_BOOL_HELPER(bSupportsRenderTargetWriteMask);
GET_SECTION_BOOL_HELPER(bSupportsRayTracing);
GET_SECTION_BOOL_HELPER(bSupportsRayTracingShaders);
GET_SECTION_BOOL_HELPER(bSupportsInlineRayTracing);
GET_SECTION_BOOL_HELPER(bSupportsRayTracingCallableShaders);
GET_SECTION_BOOL_HELPER(bSupportsRayTracingProceduralPrimitive);
GET_SECTION_BOOL_HELPER(bSupportsRayTracingTraversalStatistics);
GET_SECTION_BOOL_HELPER(bSupportsRayTracingIndirectInstanceData);
GET_SECTION_BOOL_HELPER(bSupportsPathTracing);
GET_SECTION_BOOL_HELPER(bSupportsHighEndRayTracingReflections);
GET_SECTION_BOOL_HELPER(bSupportsByteBufferComputeShaders);
GET_SECTION_BOOL_HELPER(bSupportsGPUScene);
GET_SECTION_BOOL_HELPER(bSupportsPrimitiveShaders);
GET_SECTION_BOOL_HELPER(bSupportsUInt64ImageAtomics);
GET_SECTION_BOOL_HELPER(bRequiresVendorExtensionsForAtomics);
GET_SECTION_BOOL_HELPER(bSupportsNanite);
GET_SECTION_BOOL_HELPER(bSupportsLumenGI);
GET_SECTION_BOOL_HELPER(bSupportsSSDIndirect);
GET_SECTION_BOOL_HELPER(bSupportsTemporalHistoryUpscale);
GET_SECTION_BOOL_HELPER(bSupportsRTIndexFromVS);
GET_SECTION_BOOL_HELPER(bSupportsIntrinsicWaveOnce);
GET_SECTION_BOOL_HELPER(bSupportsConservativeRasterization);
GET_SECTION_BOOL_HELPER(bSupportsWaveOperations);
GET_SECTION_BOOL_HELPER(bRequiresExplicit128bitRT);
GET_SECTION_BOOL_HELPER(bSupportsGen5TemporalAA);
GET_SECTION_BOOL_HELPER(bTargetsTiledGPU);
GET_SECTION_BOOL_HELPER(bNeedsOfflineCompiler);
GET_SECTION_BOOL_HELPER(bSupportsComputeFramework);
GET_SECTION_BOOL_HELPER(bSupportsAnisotropicMaterials);
GET_SECTION_BOOL_HELPER(bSupportsDualSourceBlending);
GET_SECTION_BOOL_HELPER(bRequiresGeneratePrevTransformBuffer);
GET_SECTION_BOOL_HELPER(bRequiresRenderTargetDuringRaster);
GET_SECTION_BOOL_HELPER(bRequiresDisableForwardLocalLights);
GET_SECTION_BOOL_HELPER(bCompileSignalProcessingPipeline);
GET_SECTION_BOOL_HELPER(bSupportsMeshShadersTier0);
GET_SECTION_BOOL_HELPER(bSupportsMeshShadersTier1);
GET_SECTION_INT_HELPER(MaxMeshShaderThreadGroupSize);
GET_SECTION_BOOL_HELPER(bSupportsPerPixelDBufferMask);
GET_SECTION_BOOL_HELPER(bIsHlslcc);
GET_SECTION_BOOL_HELPER(bSupportsDxc);
GET_SECTION_BOOL_HELPER(bSupportsVariableRateShading);
GET_SECTION_INT_HELPER(NumberOfComputeThreads);
GET_SECTION_BOOL_HELPER(bWaterUsesSimpleForwardShading);
GET_SECTION_BOOL_HELPER(bNeedsToSwitchVerticalAxisOnMobileOpenGL);
GET_SECTION_BOOL_HELPER(bSupportsHairStrandGeometry);
GET_SECTION_BOOL_HELPER(bSupportsDOFHybridScattering);
GET_SECTION_BOOL_HELPER(bNeedsExtraMobileFrames);
GET_SECTION_BOOL_HELPER(bSupportsHZBOcclusion);
GET_SECTION_BOOL_HELPER(bSupportsWaterIndirectDraw);
GET_SECTION_BOOL_HELPER(bSupportsAsyncPipelineCompilation);
GET_SECTION_BOOL_HELPER(bSupportsManualVertexFetch);
GET_SECTION_BOOL_HELPER(bRequiresReverseCullingOnMobile);
GET_SECTION_BOOL_HELPER(bOverrideFMaterial_NeedsGBufferEnabled);
GET_SECTION_BOOL_HELPER(bSupportsMobileDistanceField);
GET_SECTION_BOOL_HELPER(bSupportsFFTBloom);
GET_SECTION_BOOL_HELPER(bSupportsVertexShaderLayer);
GET_SECTION_BOOL_HELPER(bSupportsBindless);
GET_SECTION_BOOL_HELPER(bSupportsVolumeTextureAtomics);
GET_SECTION_BOOL_HELPER(bSupportsROV);
GET_SECTION_BOOL_HELPER(bSupportsOIT);
GET_SECTION_SUPPORT_HELPER(bSupportsRealTypes);
GET_SECTION_INT_HELPER(EnablesHLSL2021ByDefault);
#undef GET_SECTION_BOOL_HELPER
#undef GET_SECTION_INT_HELPER
#undef GET_SECTION_SUPPORT_HELPER
#if WITH_EDITOR
FTextStringHelper::ReadFromBuffer(*GetSectionString(Section, FName("FriendlyName")), Info.FriendlyName);
#endif
}
void FGenericDataDrivenShaderPlatformInfo::Initialize()
{
static bool bInitialized = false;
if (bInitialized)
{
return;
}
// look for the standard DataDriven ini files
int32 NumDDInfoFiles = FDataDrivenPlatformInfoRegistry::GetNumDataDrivenIniFiles();
for (int32 Index = 0; Index < NumDDInfoFiles; Index++)
{
FConfigFile IniFile;
FString PlatformName;
FDataDrivenPlatformInfoRegistry::LoadDataDrivenIniFile(Index, IniFile, PlatformName);
// now walk over the file, looking for ShaderPlatformInfo sections
for (auto Section : IniFile)
{
if (Section.Key.StartsWith(TEXT("ShaderPlatform ")))
{
const FString& SectionName = Section.Key;
EShaderPlatform ShaderPlatform;
// get enum value for the string name
LexFromString(ShaderPlatform, *SectionName.Mid(15));
if (ShaderPlatform == EShaderPlatform::SP_NumPlatforms)
{
#if DDPI_HAS_EXTENDED_PLATFORMINFO_DATA
const bool bIsEnabled = FDataDrivenPlatformInfoRegistry::GetPlatformInfo(PlatformName).bEnabledForUse;
#else
const bool bIsEnabled = true;
#endif
UE_CLOG(bIsEnabled, LogRHI, Warning, TEXT("Found an unknown shader platform %s in a DataDriven ini file"), *SectionName.Mid(15));
continue;
}
// at this point, we can start pulling information out
ParseDataDrivenShaderInfo(Section.Value, Infos[ShaderPlatform]);
Infos[ShaderPlatform].bContainsValidPlatformInfo = true;
}
}
}
bInitialized = true;
}
//
// MSAA sample offsets.
//
// https://docs.microsoft.com/en-us/windows/win32/api/d3d11/ne-d3d11-d3d11_standard_multisample_quality_levels
FVector2f GRHIDefaultMSAASampleOffsets[1 + 2 + 4 + 8 + 16] = {
// MSAA x1
FVector2f(+0.0f / 8.0f, +0.0f / 8.0f),
// MSAA x2
FVector2f(+4.0f / 8.0f, +4.0f / 8.0f),
FVector2f(-4.0f / 8.0f, -4.0f / 8.0f),
// MSAA x4
FVector2f(-2.0f / 8.0f, -6.0f / 8.0f),
FVector2f(+6.0f / 8.0f, -2.0f / 8.0f),
FVector2f(-6.0f / 8.0f, +2.0f / 8.0f),
FVector2f(+2.0f / 8.0f, +6.0f / 8.0f),
// MSAA x8
FVector2f(+1.0f / 8.0f, -3.0f / 8.0f),
FVector2f(-1.0f / 8.0f, +3.0f / 8.0f),
FVector2f(+5.0f / 8.0f, +1.0f / 8.0f),
FVector2f(-3.0f / 8.0f, -5.0f / 8.0f),
FVector2f(-5.0f / 8.0f, +5.0f / 8.0f),
FVector2f(-7.0f / 8.0f, -1.0f / 8.0f),
FVector2f(+3.0f / 8.0f, +7.0f / 8.0f),
FVector2f(+7.0f / 8.0f, -7.0f / 8.0f),
// MSAA x16
FVector2f(+1.0f / 8.0f, +1.0f / 8.0f),
FVector2f(-1.0f / 8.0f, -3.0f / 8.0f),
FVector2f(-3.0f / 8.0f, +2.0f / 8.0f),
FVector2f(+4.0f / 8.0f, -1.0f / 8.0f),
FVector2f(-5.0f / 8.0f, -2.0f / 8.0f),
FVector2f(+2.0f / 8.0f, +5.0f / 8.0f),
FVector2f(+5.0f / 8.0f, +3.0f / 8.0f),
FVector2f(+3.0f / 8.0f, -5.0f / 8.0f),
FVector2f(-2.0f / 8.0f, +6.0f / 8.0f),
FVector2f(+0.0f / 8.0f, -7.0f / 8.0f),
FVector2f(-4.0f / 8.0f, -6.0f / 8.0f),
FVector2f(-6.0f / 8.0f, +4.0f / 8.0f),
FVector2f(-8.0f / 8.0f, +0.0f / 8.0f),
FVector2f(+7.0f / 8.0f, -4.0f / 8.0f),
FVector2f(+6.0f / 8.0f, +7.0f / 8.0f),
FVector2f(-7.0f / 8.0f, -8.0f / 8.0f),
};
int32 CalculateMSAASampleArrayIndex(int32 NumSamples, int32 SampleIndex)
{
check(NumSamples > 0 && NumSamples <= 16);
check(FMath::IsPowerOfTwo(NumSamples));
check(SampleIndex < NumSamples);
return NumSamples - 1 + SampleIndex;
}
//
// Pixel format information.
//
FPixelFormatInfo::FPixelFormatInfo(
EPixelFormat InUnrealFormat,
const TCHAR* InName,
int32 InBlockSizeX,
int32 InBlockSizeY,
int32 InBlockSizeZ,
int32 InBlockBytes,
int32 InNumComponents,
bool InSupported)
: Name(InName)
, UnrealFormat(InUnrealFormat)
, BlockSizeX(InBlockSizeX)
, BlockSizeY(InBlockSizeY)
, BlockSizeZ(InBlockSizeZ)
, BlockBytes(InBlockBytes)
, NumComponents(InNumComponents)
, Supported(InSupported)
, bIs24BitUnormDepthStencil(true)
{
}
FPixelFormatInfo GPixelFormats[PF_MAX] =
{
// UnrealFormat Name BlockSizeX BlockSizeY BlockSizeZ BlockBytes NumComponents Supported
FPixelFormatInfo(PF_Unknown, TEXT("unknown"), 0, 0, 0, 0, 0, 0),
FPixelFormatInfo(PF_A32B32G32R32F, TEXT("A32B32G32R32F"), 1, 1, 1, 16, 4, 1),
FPixelFormatInfo(PF_B8G8R8A8, TEXT("B8G8R8A8"), 1, 1, 1, 4, 4, 1),
FPixelFormatInfo(PF_G8, TEXT("G8"), 1, 1, 1, 1, 1, 1),
FPixelFormatInfo(PF_G16, TEXT("G16"), 1, 1, 1, 2, 1, 1),
FPixelFormatInfo(PF_DXT1, TEXT("DXT1"), 4, 4, 1, 8, 3, 1),
FPixelFormatInfo(PF_DXT3, TEXT("DXT3"), 4, 4, 1, 16, 4, 1),
FPixelFormatInfo(PF_DXT5, TEXT("DXT5"), 4, 4, 1, 16, 4, 1),
FPixelFormatInfo(PF_UYVY, TEXT("UYVY"), 2, 1, 1, 4, 4, 0),
FPixelFormatInfo(PF_FloatRGB, TEXT("FloatRGB"), 1, 1, 1, 4, 3, 1),
FPixelFormatInfo(PF_FloatRGBA, TEXT("FloatRGBA"), 1, 1, 1, 8, 4, 1),
FPixelFormatInfo(PF_DepthStencil, TEXT("DepthStencil"), 1, 1, 1, 4, 1, 0),
FPixelFormatInfo(PF_ShadowDepth, TEXT("ShadowDepth"), 1, 1, 1, 4, 1, 0),
FPixelFormatInfo(PF_R32_FLOAT, TEXT("R32_FLOAT"), 1, 1, 1, 4, 1, 1),
FPixelFormatInfo(PF_G16R16, TEXT("G16R16"), 1, 1, 1, 4, 2, 1),
FPixelFormatInfo(PF_G16R16F, TEXT("G16R16F"), 1, 1, 1, 4, 2, 1),
FPixelFormatInfo(PF_G16R16F_FILTER, TEXT("G16R16F_FILTER"), 1, 1, 1, 4, 2, 1),
FPixelFormatInfo(PF_G32R32F, TEXT("G32R32F"), 1, 1, 1, 8, 2, 1),
FPixelFormatInfo(PF_A2B10G10R10, TEXT("A2B10G10R10"), 1, 1, 1, 4, 4, 1),
FPixelFormatInfo(PF_A16B16G16R16, TEXT("A16B16G16R16"), 1, 1, 1, 8, 4, 1),
FPixelFormatInfo(PF_D24, TEXT("D24"), 1, 1, 1, 4, 1, 1),
FPixelFormatInfo(PF_R16F, TEXT("PF_R16F"), 1, 1, 1, 2, 1, 1),
FPixelFormatInfo(PF_R16F_FILTER, TEXT("PF_R16F_FILTER"), 1, 1, 1, 2, 1, 1),
FPixelFormatInfo(PF_BC5, TEXT("BC5"), 4, 4, 1, 16, 2, 1),
FPixelFormatInfo(PF_V8U8, TEXT("V8U8"), 1, 1, 1, 2, 2, 1),
FPixelFormatInfo(PF_A1, TEXT("A1"), 1, 1, 1, 1, 1, 0),
FPixelFormatInfo(PF_FloatR11G11B10, TEXT("FloatR11G11B10"), 1, 1, 1, 4, 3, 0),
FPixelFormatInfo(PF_A8, TEXT("A8"), 1, 1, 1, 1, 1, 1),
FPixelFormatInfo(PF_R32_UINT, TEXT("R32_UINT"), 1, 1, 1, 4, 1, 1),
FPixelFormatInfo(PF_R32_SINT, TEXT("R32_SINT"), 1, 1, 1, 4, 1, 1),
// IOS Support
FPixelFormatInfo(PF_PVRTC2, TEXT("PVRTC2"), 8, 4, 1, 8, 4, 0),
FPixelFormatInfo(PF_PVRTC4, TEXT("PVRTC4"), 4, 4, 1, 8, 4, 0),
FPixelFormatInfo(PF_R16_UINT, TEXT("R16_UINT"), 1, 1, 1, 2, 1, 1),
FPixelFormatInfo(PF_R16_SINT, TEXT("R16_SINT"), 1, 1, 1, 2, 1, 1),
FPixelFormatInfo(PF_R16G16B16A16_UINT, TEXT("R16G16B16A16_UINT"), 1, 1, 1, 8, 4, 1),
FPixelFormatInfo(PF_R16G16B16A16_SINT, TEXT("R16G16B16A16_SINT"), 1, 1, 1, 8, 4, 1),
FPixelFormatInfo(PF_R5G6B5_UNORM, TEXT("R5G6B5_UNORM"), 1, 1, 1, 2, 3, 0),
FPixelFormatInfo(PF_R8G8B8A8, TEXT("R8G8B8A8"), 1, 1, 1, 4, 4, 1),
FPixelFormatInfo(PF_A8R8G8B8, TEXT("A8R8G8B8"), 1, 1, 1, 4, 4, 1),
FPixelFormatInfo(PF_BC4, TEXT("BC4"), 4, 4, 1, 8, 1, 1),
FPixelFormatInfo(PF_R8G8, TEXT("R8G8"), 1, 1, 1, 2, 2, 1),
FPixelFormatInfo(PF_ATC_RGB, TEXT("ATC_RGB"), 4, 4, 1, 8, 3, 0),
FPixelFormatInfo(PF_ATC_RGBA_E, TEXT("ATC_RGBA_E"), 4, 4, 1, 16, 4, 0),
FPixelFormatInfo(PF_ATC_RGBA_I, TEXT("ATC_RGBA_I"), 4, 4, 1, 16, 4, 0),
FPixelFormatInfo(PF_X24_G8, TEXT("X24_G8"), 1, 1, 1, 1, 1, 0),
FPixelFormatInfo(PF_ETC1, TEXT("ETC1"), 4, 4, 1, 8, 3, 0),
FPixelFormatInfo(PF_ETC2_RGB, TEXT("ETC2_RGB"), 4, 4, 1, 8, 3, 0),
FPixelFormatInfo(PF_ETC2_RGBA, TEXT("ETC2_RGBA"), 4, 4, 1, 16, 4, 0),
FPixelFormatInfo(PF_R32G32B32A32_UINT, TEXT("PF_R32G32B32A32_UINT"), 1, 1, 1, 16, 4, 1),
FPixelFormatInfo(PF_R16G16_UINT, TEXT("PF_R16G16_UINT"), 1, 1, 1, 4, 4, 1),
// ASTC support
FPixelFormatInfo(PF_ASTC_4x4, TEXT("ASTC_4x4"), 4, 4, 1, 16, 4, 0),
FPixelFormatInfo(PF_ASTC_6x6, TEXT("ASTC_6x6"), 6, 6, 1, 16, 4, 0),
FPixelFormatInfo(PF_ASTC_8x8, TEXT("ASTC_8x8"), 8, 8, 1, 16, 4, 0),
FPixelFormatInfo(PF_ASTC_10x10, TEXT("ASTC_10x10"), 10, 10, 1, 16, 4, 0),
FPixelFormatInfo(PF_ASTC_12x12, TEXT("ASTC_12x12"), 12, 12, 1, 16, 4, 0),
FPixelFormatInfo(PF_BC6H, TEXT("BC6H"), 4, 4, 1, 16, 3, 1),
FPixelFormatInfo(PF_BC7, TEXT("BC7"), 4, 4, 1, 16, 4, 1),
FPixelFormatInfo(PF_R8_UINT, TEXT("R8_UINT"), 1, 1, 1, 1, 1, 1),
FPixelFormatInfo(PF_L8, TEXT("L8"), 1, 1, 1, 1, 1, 0),
FPixelFormatInfo(PF_XGXR8, TEXT("XGXR8"), 1, 1, 1, 4, 4, 1),
FPixelFormatInfo(PF_R8G8B8A8_UINT, TEXT("R8G8B8A8_UINT"), 1, 1, 1, 4, 4, 1),
FPixelFormatInfo(PF_R8G8B8A8_SNORM, TEXT("R8G8B8A8_SNORM"), 1, 1, 1, 4, 4, 1),
FPixelFormatInfo(PF_R16G16B16A16_UNORM, TEXT("R16G16B16A16_UINT"), 1, 1, 1, 8, 4, 1),
FPixelFormatInfo(PF_R16G16B16A16_SNORM, TEXT("R16G16B16A16_SINT"), 1, 1, 1, 8, 4, 1),
FPixelFormatInfo(PF_PLATFORM_HDR_0, TEXT("PLATFORM_HDR_0"), 0, 0, 0, 0, 0, 0),
FPixelFormatInfo(PF_PLATFORM_HDR_1, TEXT("PLATFORM_HDR_1"), 0, 0, 0, 0, 0, 0),
FPixelFormatInfo(PF_PLATFORM_HDR_2, TEXT("PLATFORM_HDR_2"), 0, 0, 0, 0, 0, 0),
// NV12 contains 2 textures: R8 luminance plane followed by R8G8 1/4 size chrominance plane.
// BlockSize/BlockBytes/NumComponents values don't make much sense for this format, so set them all to one.
FPixelFormatInfo(PF_NV12, TEXT("NV12"), 1, 1, 1, 1, 1, 0),
FPixelFormatInfo(PF_R32G32_UINT, TEXT("PF_R32G32_UINT"), 1, 1, 1, 8, 2, 1),
FPixelFormatInfo(PF_ETC2_R11_EAC, TEXT("PF_ETC2_R11_EAC"), 4, 4, 1, 8, 1, 0),
FPixelFormatInfo(PF_ETC2_RG11_EAC, TEXT("PF_ETC2_RG11_EAC"), 4, 4, 1, 16, 2, 0),
FPixelFormatInfo(PF_R8, TEXT("R8"), 1, 1, 1, 1, 1, 1),
FPixelFormatInfo(PF_B5G5R5A1_UNORM, TEXT("B5G5R5A1_UNORM"), 1, 1, 1, 2, 4, 0),
// ASTC HDR support
FPixelFormatInfo(PF_ASTC_4x4_HDR, TEXT("ASTC_4x4_HDR"), 4, 4, 1, 16, 4, 0),
FPixelFormatInfo(PF_ASTC_6x6_HDR, TEXT("ASTC_6x6_HDR"), 6, 6, 1, 16, 4, 0),
FPixelFormatInfo(PF_ASTC_8x8_HDR, TEXT("ASTC_8x8_HDR"), 8, 8, 1, 16, 4, 0),
FPixelFormatInfo(PF_ASTC_10x10_HDR, TEXT("ASTC_10x10_HDR"), 10, 10, 1, 16, 4, 0),
FPixelFormatInfo(PF_ASTC_12x12_HDR, TEXT("ASTC_12x12_HDR"), 12, 12, 1, 16, 4, 0),
FPixelFormatInfo(PF_G16R16_SNORM, TEXT("G16R16_SNORM"), 1, 1, 1, 4, 2, 1),
FPixelFormatInfo(PF_R8G8_UINT, TEXT("R8G8_UINT"), 1, 1, 1, 2, 2, 1),
FPixelFormatInfo(PF_R32G32B32_UINT, TEXT("R32G32B32_UINT"), 1, 1, 1, 12, 3, 1),
FPixelFormatInfo(PF_R32G32B32_SINT, TEXT("R32G32B32_SINT"), 1, 1, 1, 12, 3, 1),
FPixelFormatInfo(PF_R32G32B32F, TEXT("R32G32B32F"), 1, 1, 1, 12, 3, 1),
FPixelFormatInfo(PF_R8_SINT, TEXT("R8_SINT"), 1, 1, 1, 1, 1, 1),
FPixelFormatInfo(PF_R64_UINT, TEXT("R64_UINT"), 1, 1, 1, 8, 1, 0),
};
void RHIInitDefaultPixelFormatCapabilities()
{
for (FPixelFormatInfo& Info : GPixelFormats)
{
if (Info.Supported)
{
const EPixelFormat PixelFormat = Info.UnrealFormat;
if (IsBlockCompressedFormat(PixelFormat))
{
// Block compressed formats should have limited capabilities
EnumAddFlags(Info.Capabilities, EPixelFormatCapabilities::AnyTexture | EPixelFormatCapabilities::TextureMipmaps | EPixelFormatCapabilities::TextureLoad | EPixelFormatCapabilities::TextureSample | EPixelFormatCapabilities::TextureGather);
}
else
{
EnumAddFlags(Info.Capabilities, EPixelFormatCapabilities::AllTextureFlags | EPixelFormatCapabilities::AllBufferFlags | EPixelFormatCapabilities::UAV);
if (!IsDepthOrStencilFormat(PixelFormat))
{
EnumRemoveFlags(Info.Capabilities, EPixelFormatCapabilities::DepthStencil);
}
}
}
}
}
static struct FValidatePixelFormats
{
FValidatePixelFormats()
{
for (int32 Index = 0; Index < UE_ARRAY_COUNT(GPixelFormats); ++Index)
{
// Make sure GPixelFormats has an entry for every unreal format
checkf((EPixelFormat)Index == GPixelFormats[Index].UnrealFormat, TEXT("Missing entry for EPixelFormat %d"), (int32)Index);
}
}
} ValidatePixelFormats;
//
// CalculateImageBytes
//
SIZE_T CalculateImageBytes(uint32 SizeX,uint32 SizeY,uint32 SizeZ,uint8 Format)
{
if ( Format == PF_A1 )
{
// The number of bytes needed to store all 1 bit pixels in a line is the width of the image divided by the number of bits in a byte
uint32 BytesPerLine = SizeX / 8;
// The number of actual bytes in a 1 bit image is the bytes per line of pixels times the number of lines
return sizeof(uint8) * BytesPerLine * SizeY;
}
else if( SizeZ > 0 )
{
return static_cast<SIZE_T>(SizeX / GPixelFormats[Format].BlockSizeX) * (SizeY / GPixelFormats[Format].BlockSizeY) * (SizeZ / GPixelFormats[Format].BlockSizeZ) * GPixelFormats[Format].BlockBytes;
}
else
{
return static_cast<SIZE_T>(SizeX / GPixelFormats[Format].BlockSizeX) * (SizeY / GPixelFormats[Format].BlockSizeY) * GPixelFormats[Format].BlockBytes;
}
}
FRHIShaderResourceView* FRHITextureViewCache::GetOrCreateSRV(FRHITexture* Texture, const FRHITextureSRVCreateInfo& SRVCreateInfo)
{
for (const auto& KeyValue : SRVs)
{
if (KeyValue.Key == SRVCreateInfo)
{
return KeyValue.Value.GetReference();
}
}
FShaderResourceViewRHIRef RHIShaderResourceView;
if (SRVCreateInfo.MetaData != ERHITextureMetaDataAccess::None)
{
FRHITexture2D* Texture2D = Texture->GetTexture2D();
check(Texture2D);
switch (SRVCreateInfo.MetaData)
{
case ERHITextureMetaDataAccess::HTile:
check(GRHISupportsExplicitHTile);
RHIShaderResourceView = RHICreateShaderResourceViewHTile(Texture2D);
break;
case ERHITextureMetaDataAccess::FMask:
RHIShaderResourceView = RHICreateShaderResourceViewFMask(Texture2D);
break;
case ERHITextureMetaDataAccess::CMask:
RHIShaderResourceView = RHICreateShaderResourceViewWriteMask(Texture2D);
break;
}
}
if (!RHIShaderResourceView)
{
RHIShaderResourceView = RHICreateShaderResourceView(Texture, SRVCreateInfo);
}
check(RHIShaderResourceView);
FRHIShaderResourceView* View = RHIShaderResourceView.GetReference();
SRVs.Emplace(SRVCreateInfo, MoveTemp(RHIShaderResourceView));
return View;
}
FRHIUnorderedAccessView* FRHITextureViewCache::GetOrCreateUAV(FRHITexture* Texture, const FRHITextureUAVCreateInfo& UAVCreateInfo)
{
for (const auto& KeyValue : UAVs)
{
if (KeyValue.Key == UAVCreateInfo)
{
return KeyValue.Value.GetReference();
}
}
FUnorderedAccessViewRHIRef RHIUnorderedAccessView;
if (UAVCreateInfo.MetaData != ERHITextureMetaDataAccess::None)
{
FRHITexture2D* Texture2D = Texture->GetTexture2D();
check(Texture2D);
switch (UAVCreateInfo.MetaData)
{
case ERHITextureMetaDataAccess::HTile:
check(GRHISupportsExplicitHTile);
RHIUnorderedAccessView = RHICreateUnorderedAccessViewHTile(Texture2D);
break;
case ERHITextureMetaDataAccess::Stencil:
RHIUnorderedAccessView = RHICreateUnorderedAccessViewStencil(Texture2D, UAVCreateInfo.MipLevel);
break;
}
}
if (!RHIUnorderedAccessView)
{
if (UAVCreateInfo.Format != PF_Unknown)
{
RHIUnorderedAccessView = RHICreateUnorderedAccessView(Texture, UAVCreateInfo.MipLevel, UAVCreateInfo.Format, UAVCreateInfo.FirstArraySlice, UAVCreateInfo.NumArraySlices);
}
else
{
RHIUnorderedAccessView = RHICreateUnorderedAccessView(Texture, UAVCreateInfo.MipLevel, UAVCreateInfo.FirstArraySlice, UAVCreateInfo.NumArraySlices);
}
}
check(RHIUnorderedAccessView);
FRHIUnorderedAccessView* View = RHIUnorderedAccessView.GetReference();
UAVs.Emplace(UAVCreateInfo, MoveTemp(RHIUnorderedAccessView));
return View;
}
FRHIShaderResourceView* FRHIBufferViewCache::GetOrCreateSRV(FRHIBuffer* Buffer, const FRHIBufferSRVCreateInfo& SRVCreateInfo)
{
for (const auto& KeyValue : SRVs)
{
if (KeyValue.Key == SRVCreateInfo)
{
return KeyValue.Value.GetReference();
}
}
FShaderResourceViewRHIRef RHIShaderResourceView;
if (SRVCreateInfo.Format != PF_Unknown)
{
RHIShaderResourceView = RHICreateShaderResourceView(Buffer, SRVCreateInfo.BytesPerElement, SRVCreateInfo.Format);
}
else
{
RHIShaderResourceView = RHICreateShaderResourceView(Buffer);
}
FRHIShaderResourceView* View = RHIShaderResourceView.GetReference();
SRVs.Emplace(SRVCreateInfo, MoveTemp(RHIShaderResourceView));
return View;
}
FRHIUnorderedAccessView* FRHIBufferViewCache::GetOrCreateUAV(FRHIBuffer* Buffer, const FRHIBufferUAVCreateInfo& UAVCreateInfo)
{
for (const auto& KeyValue : UAVs)
{
if (KeyValue.Key == UAVCreateInfo)
{
return KeyValue.Value.GetReference();
}
}
FUnorderedAccessViewRHIRef RHIUnorderedAccessView;
if (UAVCreateInfo.Format != PF_Unknown)
{
RHIUnorderedAccessView = RHICreateUnorderedAccessView(Buffer, UAVCreateInfo.Format);
}
else
{
RHIUnorderedAccessView = RHICreateUnorderedAccessView(Buffer, UAVCreateInfo.bSupportsAtomicCounter, UAVCreateInfo.bSupportsAppendBuffer);
}
FRHIUnorderedAccessView* View = RHIUnorderedAccessView.GetReference();
UAVs.Emplace(UAVCreateInfo, MoveTemp(RHIUnorderedAccessView));
return View;
}
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
void FRHITextureViewCache::SetDebugName(const TCHAR* DebugName)
{
for (const auto& KeyValue : UAVs)
{
RHIBindDebugLabelName(KeyValue.Value, DebugName);
}
}
void FRHIBufferViewCache::SetDebugName(const TCHAR* DebugName)
{
for (const auto& KeyValue : UAVs)
{
RHIBindDebugLabelName(KeyValue.Value, DebugName);
}
}
#endif
void FRHITransientTexture::Acquire(const TCHAR* InName, uint32 InPassIndex, uint64 InAcquireCycle)
{
FRHITransientResource::Acquire(InName, InPassIndex, InAcquireCycle);
ViewCache.SetDebugName(InName);
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
RHIBindDebugLabelName(GetRHI(), InName);
#endif
}
void FRHITransientBuffer::Acquire(const TCHAR* InName, uint32 InPassIndex, uint64 InAcquireCycle)
{
FRHITransientResource::Acquire(InName, InPassIndex, InAcquireCycle);
ViewCache.SetDebugName(InName);
// TODO: Add method to rename a buffer.
}
FDebugName::FDebugName()
: Name()
, Number(NAME_NO_NUMBER_INTERNAL)
{
}
FDebugName::FDebugName(FName InName)
: Name(InName)
, Number(NAME_NO_NUMBER_INTERNAL)
{
}
FDebugName::FDebugName(FName InName, int32 InNumber)
: Name(InName)
, Number(InNumber)
{
}
FDebugName::FDebugName(FMemoryImageName InName, int32 InNumber)
: Name(InName)
, Number(InNumber)
{
}
FDebugName& FDebugName::operator=(FName Other)
{
Name = FMemoryImageName(Other);
Number = NAME_NO_NUMBER_INTERNAL;
return *this;
}
FString FDebugName::ToString() const
{
FString Out;
FName(Name).AppendString(Out);
if (Number != NAME_NO_NUMBER_INTERNAL)
{
Out.Appendf(TEXT("_%u"), Number);
}
return Out;
}
void FDebugName::AppendString(FStringBuilderBase& Builder) const
{
FName(Name).AppendString(Builder);
if (Number != NAME_NO_NUMBER_INTERNAL)
{
Builder << '_' << Number;
}
}
RHI_API void RHISetCurrentNumDrawCallPtr(FRHIDrawCallsStatPtr InNumDrawCallsRHIPtr)
{
GCurrentNumDrawCallsRHIPtr = InNumDrawCallsRHIPtr;
}
RHI_API void RHIIncCurrentNumDrawCallPtr(uint32 GPUIndex)
{
FPlatformAtomics::InterlockedIncrement(&(*GCurrentNumDrawCallsRHIPtr)[GPUIndex]);
}