2937 lines
92 KiB
C++
2937 lines
92 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
/*=============================================================================
|
|
Shader.cpp: Shader implementation.
|
|
=============================================================================*/
|
|
|
|
#include "Shader.h"
|
|
#include "Misc/CoreMisc.h"
|
|
#include "Misc/StringBuilder.h"
|
|
#include "VertexFactory.h"
|
|
#include "ProfilingDebugging/DiagnosticTable.h"
|
|
#include "Interfaces/ITargetPlatform.h"
|
|
#include "Interfaces/ITargetPlatformManagerModule.h"
|
|
#include "Interfaces/IShaderFormat.h"
|
|
#include "Internationalization/Regex.h"
|
|
#include "Serialization/ShaderKeyGenerator.h"
|
|
#include "ShaderCodeLibrary.h"
|
|
#include "ShaderCore.h"
|
|
#include "ShaderCompilerCore.h"
|
|
#include "RenderUtils.h"
|
|
#include "StereoRenderUtils.h"
|
|
#include "Misc/ConfigCacheIni.h"
|
|
#include "Misc/ScopeLock.h"
|
|
#include "UObject/RenderingObjectVersion.h"
|
|
#include "ProfilingDebugging/LoadTimeTracker.h"
|
|
#include "Misc/LargeWorldRenderPosition.h"
|
|
#include "DataDrivenShaderPlatformInfo.h"
|
|
#include "ShaderPlatformCachedIniValue.h"
|
|
#include "ColorManagement/ColorSpace.h"
|
|
|
|
#if WITH_EDITORONLY_DATA
|
|
#include "Interfaces/IShaderFormat.h"
|
|
#endif
|
|
|
|
#if WITH_EDITOR
|
|
#include "Serialization/CompactBinary.h"
|
|
#include "Serialization/CompactBinaryWriter.h"
|
|
#endif
|
|
|
|
#if RHI_RAYTRACING
|
|
#include "RayTracingPayloadType.h"
|
|
#endif
|
|
|
|
DEFINE_LOG_CATEGORY(LogShaders);
|
|
|
|
IMPLEMENT_TYPE_LAYOUT(FShader);
|
|
IMPLEMENT_TYPE_LAYOUT(FShaderParameterBindings);
|
|
IMPLEMENT_TYPE_LAYOUT(FShaderMapContent);
|
|
IMPLEMENT_TYPE_LAYOUT(FShaderTypeDependency);
|
|
IMPLEMENT_TYPE_LAYOUT(FShaderPipeline);
|
|
IMPLEMENT_TYPE_LAYOUT(FShaderUniformBufferParameterInfo);
|
|
IMPLEMENT_TYPE_LAYOUT(FShaderResourceParameterInfo);
|
|
IMPLEMENT_TYPE_LAYOUT(FShaderLooseParameterInfo);
|
|
IMPLEMENT_TYPE_LAYOUT(FShaderLooseParameterBufferInfo);
|
|
IMPLEMENT_TYPE_LAYOUT(FShaderParameterMapInfo);
|
|
|
|
void Freeze::IntrinsicToString(const TIndexedPtr<FShaderType>& Object, const FTypeLayoutDesc& TypeDesc, const FPlatformTypeLayoutParameters& LayoutParams, FMemoryToStringContext& OutContext)
|
|
{
|
|
const FShaderType* Type = Object.Get(OutContext.TryGetPrevPointerTable());
|
|
if (Type)
|
|
{
|
|
OutContext.String->Appendf(TEXT("%s\n"), Type->GetName());
|
|
}
|
|
else
|
|
{
|
|
OutContext.AppendNullptr();
|
|
}
|
|
}
|
|
|
|
void Freeze::IntrinsicToString(const TIndexedPtr<FVertexFactoryType>& Object, const FTypeLayoutDesc& TypeDesc, const FPlatformTypeLayoutParameters& LayoutParams, FMemoryToStringContext& OutContext)
|
|
{
|
|
const FVertexFactoryType* Type = Object.Get(OutContext.TryGetPrevPointerTable());
|
|
if (Type)
|
|
{
|
|
OutContext.String->Appendf(TEXT("%s\n"), Type->GetName());
|
|
}
|
|
else
|
|
{
|
|
OutContext.AppendNullptr();
|
|
}
|
|
}
|
|
|
|
IMPLEMENT_EXPORTED_INTRINSIC_TYPE_LAYOUT(TIndexedPtr<FShaderType>);
|
|
IMPLEMENT_EXPORTED_INTRINSIC_TYPE_LAYOUT(TIndexedPtr<FVertexFactoryType>);
|
|
|
|
static TAutoConsoleVariable<int32> CVarUsePipelines(
|
|
TEXT("r.ShaderPipelines"),
|
|
1,
|
|
TEXT("Enable using Shader pipelines."));
|
|
|
|
static TAutoConsoleVariable<int32> CVarRemoveUnusedInterpolators(
|
|
TEXT("r.Shaders.RemoveUnusedInterpolators"),
|
|
0,
|
|
TEXT("Enables removing unused interpolators mode when compiling shader pipelines.\n")
|
|
TEXT(" 0: Disable (default)\n")
|
|
TEXT(" 1: Enable removing unused"),
|
|
ECVF_ReadOnly
|
|
);
|
|
|
|
static TAutoConsoleVariable<int32> CVarSkipShaderCompression(
|
|
TEXT("r.Shaders.SkipCompression"),
|
|
0,
|
|
TEXT("Skips shader compression after compiling. Shader compression time can be quite significant when using debug shaders. This CVar is only valid in non-shipping/test builds."),
|
|
ECVF_ReadOnly | ECVF_Cheat
|
|
);
|
|
|
|
static TAutoConsoleVariable<int32> CVarAllowCompilingThroughWorkers(
|
|
TEXT("r.Shaders.AllowCompilingThroughWorkers"),
|
|
1,
|
|
TEXT("Allows shader compilation through external ShaderCompileWorker processes.\n")
|
|
TEXT("1 - (Default) Allows external shader compiler workers\n")
|
|
TEXT("0 - Disallows external shader compiler workers. Will run shader compilation in proc of UE process."),
|
|
ECVF_ReadOnly
|
|
);
|
|
|
|
static TAutoConsoleVariable<int32> CVarShadersForceDXC(
|
|
TEXT("r.Shaders.ForceDXC"),
|
|
1,
|
|
TEXT("Forces DirectX Shader Compiler (DXC) to be used for all shaders instead of HLSLcc if supported.\n")
|
|
TEXT(" 1: Force new compiler for all shaders (default)\n")
|
|
TEXT(" 0: Disable"),
|
|
ECVF_ReadOnly);
|
|
|
|
static TLinkedList<FShaderType*>* GShaderTypeList = nullptr;
|
|
static TLinkedList<FShaderPipelineType*>* GShaderPipelineList = nullptr;
|
|
|
|
static FSHAHash ShaderSourceDefaultHash; //will only be read (never written) for the cooking case
|
|
|
|
/**
|
|
* Find the shader pipeline type with the given name.
|
|
* @return NULL if no type matched.
|
|
*/
|
|
inline const FShaderPipelineType* FindShaderPipelineType(FName TypeName)
|
|
{
|
|
for (TLinkedList<FShaderPipelineType*>::TIterator ShaderPipelineTypeIt(FShaderPipelineType::GetTypeList()); ShaderPipelineTypeIt; ShaderPipelineTypeIt.Next())
|
|
{
|
|
if (ShaderPipelineTypeIt->GetFName() == TypeName)
|
|
{
|
|
return *ShaderPipelineTypeIt;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
|
|
/**
|
|
* Serializes a reference to a shader pipeline type.
|
|
*/
|
|
FArchive& operator<<(FArchive& Ar, const FShaderPipelineType*& TypeRef)
|
|
{
|
|
if (Ar.IsSaving())
|
|
{
|
|
FName TypeName = TypeRef ? FName(TypeRef->Name) : NAME_None;
|
|
Ar << TypeName;
|
|
}
|
|
else if (Ar.IsLoading())
|
|
{
|
|
FName TypeName = NAME_None;
|
|
Ar << TypeName;
|
|
TypeRef = FindShaderPipelineType(TypeName);
|
|
}
|
|
return Ar;
|
|
}
|
|
|
|
|
|
void FShaderParameterMap::VerifyBindingsAreComplete(const TCHAR* ShaderTypeName, FShaderTarget Target, const FVertexFactoryType* InVertexFactoryType) const
|
|
{
|
|
#if WITH_EDITORONLY_DATA
|
|
// Only people working on shaders (and therefore have LogShaders unsuppressed) will want to see these errors
|
|
if (UE_LOG_ACTIVE(LogShaders, Warning))
|
|
{
|
|
const TCHAR* VertexFactoryName = InVertexFactoryType ? InVertexFactoryType->GetName() : TEXT("?");
|
|
|
|
bool bBindingsComplete = true;
|
|
FString UnBoundParameters = TEXT("");
|
|
for (TMap<FString,FParameterAllocation>::TConstIterator ParameterIt(ParameterMap);ParameterIt;++ParameterIt)
|
|
{
|
|
const FString& ParamName = ParameterIt.Key();
|
|
const FParameterAllocation& ParamValue = ParameterIt.Value();
|
|
if(!ParamValue.bBound)
|
|
{
|
|
// Only valid parameters should be in the shader map
|
|
checkSlow(ParamValue.Size > 0);
|
|
bBindingsComplete = bBindingsComplete && ParamValue.bBound;
|
|
UnBoundParameters += FString(TEXT(" Parameter ")) + ParamName + TEXT(" not bound!\n");
|
|
}
|
|
}
|
|
|
|
if (!bBindingsComplete)
|
|
{
|
|
FString ErrorMessage = FString(TEXT("Found unbound parameters being used in shadertype ")) + ShaderTypeName + TEXT(" (VertexFactory: ") + VertexFactoryName + TEXT(")\n") + UnBoundParameters;
|
|
|
|
// We use a non-Slate message box to avoid problem where we haven't compiled the shaders for Slate.
|
|
FPlatformMisc::MessageBoxExt(EAppMsgType::Ok, *ErrorMessage, TEXT("Error"));
|
|
}
|
|
}
|
|
#endif // WITH_EDITORONLY_DATA
|
|
}
|
|
|
|
|
|
void FShaderParameterMap::UpdateHash(FSHA1& HashState) const
|
|
{
|
|
for(TMap<FString,FParameterAllocation>::TConstIterator ParameterIt(ParameterMap);ParameterIt;++ParameterIt)
|
|
{
|
|
const FString& ParamName = ParameterIt.Key();
|
|
const FParameterAllocation& ParamValue = ParameterIt.Value();
|
|
HashState.Update((const uint8*)*ParamName, ParamName.Len() * sizeof(TCHAR));
|
|
HashState.Update((const uint8*)&ParamValue.BufferIndex, sizeof(ParamValue.BufferIndex));
|
|
HashState.Update((const uint8*)&ParamValue.BaseIndex, sizeof(ParamValue.BaseIndex));
|
|
HashState.Update((const uint8*)&ParamValue.Size, sizeof(ParamValue.Size));
|
|
}
|
|
}
|
|
|
|
|
|
static TArray<FShaderType*>& GetSortedShaderTypes(FShaderType::EShaderTypeForDynamicCast Type)
|
|
{
|
|
static TArray<FShaderType*>* SortedTypesArray = new TArray<FShaderType*>[(uint32)FShaderType::EShaderTypeForDynamicCast::NumShaderTypes];
|
|
return SortedTypesArray[(uint32)Type];
|
|
}
|
|
|
|
|
|
namespace {
|
|
|
|
uint32 RegisteredRayTracingPayloads = 0;
|
|
uint32 RayTracingPayloadSizes[32] = {};
|
|
TRaytracingPayloadSizeFunction RayTracingPayloadSizeFunctions[32] = {};
|
|
|
|
bool IsRayTracingPayloadRegistered(ERayTracingPayloadType PayloadType)
|
|
{
|
|
// make sure all bits are on in the registered bitmask
|
|
return (static_cast<uint32>(PayloadType) & RegisteredRayTracingPayloads) == static_cast<uint32>(PayloadType);
|
|
}
|
|
|
|
} // anonymous namespace
|
|
|
|
|
|
FShaderType::FShaderType(
|
|
EShaderTypeForDynamicCast InShaderTypeForDynamicCast,
|
|
FTypeLayoutDesc& InTypeLayout,
|
|
const TCHAR* InName,
|
|
const TCHAR* InSourceFilename,
|
|
const TCHAR* InFunctionName,
|
|
uint32 InFrequency,
|
|
int32 InTotalPermutationCount,
|
|
ConstructSerializedType InConstructSerializedRef,
|
|
ConstructCompiledType InConstructCompiledRef,
|
|
ShouldCompilePermutationType InShouldCompilePermutationRef,
|
|
ShouldPrecachePermutationType InShouldPrecachePermutationRef,
|
|
GetRayTracingPayloadTypeType InGetRayTracingPayloadTypeRef,
|
|
GetShaderBindingLayoutType InGetShaderBindingLayoutTypeRef,
|
|
#if WITH_EDITOR
|
|
ModifyCompilationEnvironmentType InModifyCompilationEnvironmentRef,
|
|
ValidateCompiledResultType InValidateCompiledResultRef,
|
|
GetOverrideJobPriorityType InGetOverrideJobPriorityRef,
|
|
#endif // WITH_EDITOR
|
|
uint32 InTypeSize,
|
|
const FShaderParametersMetadata* InRootParametersMetadata
|
|
#if WITH_EDITOR
|
|
, GetPermutationIdStringType InGetPermutationIdStringRef
|
|
#endif
|
|
)
|
|
: ShaderTypeForDynamicCast(InShaderTypeForDynamicCast)
|
|
, TypeLayout(&InTypeLayout)
|
|
, Name(InName)
|
|
, TypeName(InName)
|
|
, HashedName(TypeName)
|
|
, HashedSourceFilename(InSourceFilename)
|
|
, SourceFilename(InSourceFilename)
|
|
, FunctionName(InFunctionName)
|
|
, Frequency(InFrequency)
|
|
, TypeSize(InTypeSize)
|
|
, TotalPermutationCount(InTotalPermutationCount)
|
|
, ConstructSerializedRef(InConstructSerializedRef)
|
|
, ConstructCompiledRef(InConstructCompiledRef)
|
|
, ShouldCompilePermutationRef(InShouldCompilePermutationRef)
|
|
, ShouldPrecachePermutationRef(InShouldPrecachePermutationRef)
|
|
, GetRayTracingPayloadTypeRef(InGetRayTracingPayloadTypeRef)
|
|
, GetShaderBindingLayoutTypeRef(InGetShaderBindingLayoutTypeRef)
|
|
#if WITH_EDITOR
|
|
, ModifyCompilationEnvironmentRef(InModifyCompilationEnvironmentRef)
|
|
, ValidateCompiledResultRef(InValidateCompiledResultRef)
|
|
, GetOverrideJobPriorityRef(InGetOverrideJobPriorityRef)
|
|
, GetPermutationIdStringRef(InGetPermutationIdStringRef)
|
|
#endif // WITH_EDITOR
|
|
, RootParametersMetadata(InRootParametersMetadata)
|
|
, GlobalListLink(this)
|
|
{
|
|
FTypeLayoutDesc::Register(InTypeLayout);
|
|
|
|
//make sure the name is shorter than the maximum serializable length
|
|
check(FCString::Strlen(InName) < NAME_SIZE);
|
|
|
|
// Make sure the format of the source file path is right.
|
|
check(CheckVirtualShaderFilePath(InSourceFilename));
|
|
|
|
// register this shader type
|
|
GlobalListLink.LinkHead(GetTypeList());
|
|
GetNameToTypeMap().Add(HashedName, this);
|
|
|
|
TArray<FShaderType*>& SortedTypes = GetSortedShaderTypes(InShaderTypeForDynamicCast);
|
|
const int32 SortedIndex = Algo::LowerBoundBy(SortedTypes, HashedName, [](const FShaderType* InType) { return InType->GetHashedName(); });
|
|
SortedTypes.Insert(this, SortedIndex);
|
|
}
|
|
|
|
FShaderType::~FShaderType()
|
|
{
|
|
GlobalListLink.Unlink();
|
|
GetNameToTypeMap().Remove(HashedName);
|
|
|
|
TArray<FShaderType*>& SortedTypes = GetSortedShaderTypes(ShaderTypeForDynamicCast);
|
|
const int32 SortedIndex = Algo::BinarySearchBy(SortedTypes, HashedName, [](const FShaderType* InType) { return InType->GetHashedName(); });
|
|
check(SortedIndex != INDEX_NONE);
|
|
SortedTypes.RemoveAt(SortedIndex);
|
|
}
|
|
|
|
bool FShaderTypeRegistration::bShaderTypesInitialized = false;
|
|
static TArray<const FShaderTypeRegistration*>* GShaderTypeRegistrationInstances = nullptr;
|
|
TArray<const FShaderTypeRegistration*>& FShaderTypeRegistration::GetInstances()
|
|
{
|
|
if (GShaderTypeRegistrationInstances == nullptr)
|
|
{
|
|
GShaderTypeRegistrationInstances = new TArray<const FShaderTypeRegistration*>();
|
|
}
|
|
return *GShaderTypeRegistrationInstances;
|
|
}
|
|
|
|
void FShaderTypeRegistration::CommitAll()
|
|
{
|
|
for (const auto& Instance : GetInstances())
|
|
{
|
|
FShaderType& ShaderType = Instance->LazyShaderTypeAccessor(); // constructs and registers type
|
|
}
|
|
GetInstances().Empty();
|
|
bShaderTypesInitialized = true;
|
|
}
|
|
|
|
bool FShaderTypeRegistration::AreShaderTypesInitialized()
|
|
{
|
|
return bShaderTypesInitialized;
|
|
}
|
|
|
|
TLinkedList<FShaderType*>*& FShaderType::GetTypeList()
|
|
{
|
|
return GShaderTypeList;
|
|
}
|
|
|
|
FShaderType* FShaderType::GetShaderTypeByName(const TCHAR* Name)
|
|
{
|
|
for(TLinkedList<FShaderType*>::TIterator It(GetTypeList()); It; It.Next())
|
|
{
|
|
FShaderType* Type = *It;
|
|
|
|
if (FPlatformString::Strcmp(Name, Type->GetName()) == 0)
|
|
{
|
|
return Type;
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
TArray<const FShaderType*> FShaderType::GetShaderTypesByFilename(const TCHAR* InFilename, bool bSearchAsRegexFilter)
|
|
{
|
|
if (bSearchAsRegexFilter)
|
|
{
|
|
const FRegexPattern RegexSearch(FString(InFilename).Replace(TEXT("*"), TEXT("(.)*")), ERegexPatternFlags::CaseInsensitive);
|
|
return FShaderType::GetShaderTypesByFilenameFilter(
|
|
[&RegexSearch](const TCHAR* ShaderTypeFilename) -> bool
|
|
{
|
|
return FRegexMatcher(RegexSearch, ShaderTypeFilename).FindNext();
|
|
}
|
|
);
|
|
}
|
|
else
|
|
{
|
|
return FShaderType::GetShaderTypesByFilenameFilter(
|
|
[InFilename](const TCHAR* ShaderTypeFilename) -> bool
|
|
{
|
|
return FPlatformString::Strcmp(InFilename, ShaderTypeFilename) == 0;
|
|
}
|
|
);
|
|
}
|
|
}
|
|
|
|
TArray<const FShaderType*> FShaderType::GetShaderTypesByFilenameFilter(const TFunction<bool(const TCHAR*)>& InFilenameFilter)
|
|
{
|
|
TArray<const FShaderType*> OutShaders;
|
|
for (TLinkedList<FShaderType*>::TIterator It(GetTypeList()); It; It.Next())
|
|
{
|
|
const FShaderType* Type = *It;
|
|
if (InFilenameFilter(Type->GetShaderFilename()))
|
|
{
|
|
OutShaders.Add(Type);
|
|
}
|
|
}
|
|
return OutShaders;
|
|
}
|
|
|
|
TMap<FHashedName, FShaderType*>& FShaderType::GetNameToTypeMap()
|
|
{
|
|
static TMap<FHashedName, FShaderType*> ShaderNameToTypeMap;
|
|
return ShaderNameToTypeMap;
|
|
}
|
|
|
|
const TArray<FShaderType*>& FShaderType::GetSortedTypes(EShaderTypeForDynamicCast Type)
|
|
{
|
|
return GetSortedShaderTypes(Type);
|
|
}
|
|
|
|
FArchive& operator<<(FArchive& Ar,FShaderType*& Ref)
|
|
{
|
|
if(Ar.IsSaving())
|
|
{
|
|
FName ShaderTypeName = Ref ? FName(Ref->Name) : NAME_None;
|
|
Ar << ShaderTypeName;
|
|
}
|
|
else if(Ar.IsLoading())
|
|
{
|
|
FName ShaderTypeName = NAME_None;
|
|
Ar << ShaderTypeName;
|
|
|
|
Ref = NULL;
|
|
|
|
if(ShaderTypeName != NAME_None)
|
|
{
|
|
// look for the shader type in the global name to type map
|
|
FShaderType** ShaderType = FShaderType::GetNameToTypeMap().Find(ShaderTypeName);
|
|
if (ShaderType)
|
|
{
|
|
// if we found it, use it
|
|
Ref = *ShaderType;
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogShaders, Verbose, TEXT("ShaderType '%s' dependency was not found."), *ShaderTypeName.ToString());
|
|
}
|
|
}
|
|
}
|
|
return Ar;
|
|
}
|
|
|
|
FShader* FShaderType::ConstructForDeserialization() const
|
|
{
|
|
return (*ConstructSerializedRef)();
|
|
}
|
|
|
|
FShader* FShaderType::ConstructCompiled(const FShader::CompiledShaderInitializerType& Initializer) const
|
|
{
|
|
return (*ConstructCompiledRef)(Initializer);
|
|
}
|
|
|
|
static bool ShouldCompileShaderFrequency(EShaderFrequency Frequency, EShaderPlatform ShaderPlatform)
|
|
{
|
|
if (IsMobilePlatform(ShaderPlatform))
|
|
{
|
|
return Frequency == SF_Vertex || Frequency == SF_Pixel || Frequency == SF_Compute;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool FShaderType::ShouldCompilePermutation(const FShaderPermutationParameters& Parameters) const
|
|
{
|
|
return ShouldCompileShaderFrequency((EShaderFrequency)Frequency, Parameters.Platform) && (*ShouldCompilePermutationRef)(Parameters);
|
|
}
|
|
|
|
EShaderPermutationPrecacheRequest FShaderType::ShouldPrecachePermutation(const FShaderPermutationParameters& Parameters) const
|
|
{
|
|
return ShouldCompileShaderFrequency((EShaderFrequency)Frequency, Parameters.Platform) ? (*ShouldPrecachePermutationRef)(Parameters) : EShaderPermutationPrecacheRequest::NotUsed;
|
|
}
|
|
|
|
const FShaderBindingLayout* FShaderType::GetShaderBindingLayout(const FShaderPermutationParameters& Parameters) const
|
|
{
|
|
return (*GetShaderBindingLayoutTypeRef)(Parameters);
|
|
}
|
|
|
|
|
|
#if WITH_EDITOR
|
|
|
|
void FShaderType::ModifyCompilationEnvironment(const FShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) const
|
|
{
|
|
(*ModifyCompilationEnvironmentRef)(Parameters, OutEnvironment);
|
|
|
|
OutEnvironment.ShaderBindingLayout = GetShaderBindingLayout(Parameters);
|
|
if (OutEnvironment.ShaderBindingLayout)
|
|
{
|
|
// Store copy of RHI version of the shader binding layout in the environment so it can be serialized for the shader compiler workers
|
|
OutEnvironment.RHIShaderBindingLayout = OutEnvironment.ShaderBindingLayout->RHILayout;
|
|
}
|
|
|
|
if (Frequency == SF_RayHitGroup)
|
|
{
|
|
// TODO: add a define for each of the 3 possible entry points?
|
|
// See UE::ShaderCompilerCommon::ParseRayTracingEntryPoint for how to parse them
|
|
}
|
|
else
|
|
{
|
|
// define the function name as itself so one can use #ifdef to isolate the shader being compiled within a larger .usf file
|
|
OutEnvironment.SetDefine(FunctionName, FunctionName);
|
|
}
|
|
|
|
#if RHI_RAYTRACING
|
|
ERayTracingPayloadType RayTracingPayloadType = GetRayTracingPayloadType(Parameters.PermutationId);
|
|
switch (Frequency)
|
|
{
|
|
case SF_RayGen:
|
|
{
|
|
// Raygen shader can use any number of payloads, but must use at least one
|
|
checkf(RayTracingPayloadType != ERayTracingPayloadType::None, TEXT("Raygen shader %s did not declare which payload type(s) it uses. Make sure you override GetRayTracingPayloadType()"), Name);
|
|
break;
|
|
}
|
|
case SF_RayHitGroup:
|
|
case SF_RayMiss:
|
|
case SF_RayCallable:
|
|
{
|
|
// these shader types must know which payload type they are using
|
|
checkf(RayTracingPayloadType != ERayTracingPayloadType::None, TEXT("Raytracing shader %s did not declare which payload type(s) it uses. Make sure you override GetRayTracingPayloadType()"), Name);
|
|
checkf(FMath::CountBits(static_cast<uint32>(RayTracingPayloadType)) == 1, TEXT("Raytracing shader %s did not declare a unique payload type. Only one payload type is supported for this shader frequency."), Name);
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
// not a raytracing shader, specifying a payload type would suggest some confusion has occured
|
|
checkf(RayTracingPayloadType == ERayTracingPayloadType::None, TEXT("Non-Raytracing shader %s declared a payload type!"), Name);
|
|
break;
|
|
}
|
|
}
|
|
if (RayTracingPayloadType != ERayTracingPayloadType::None)
|
|
{
|
|
checkf(IsRayTracingPayloadRegistered(RayTracingPayloadType), TEXT("Raytracing shader %s is using a payload type (%u) which was never registered"), Name, RayTracingPayloadType);
|
|
|
|
OutEnvironment.SetDefineAndCompileArgument(TEXT("RT_PAYLOAD_TYPE"), static_cast<uint32>(RayTracingPayloadType));
|
|
OutEnvironment.SetDefineAndCompileArgument(TEXT("RT_PAYLOAD_MAX_SIZE"), GetRayTracingPayloadTypeMaxSize(RayTracingPayloadType));
|
|
|
|
if ( (uint32(RayTracingPayloadType) & uint32(ERayTracingPayloadType::RayTracingMaterial))
|
|
|| (uint32(RayTracingPayloadType) & uint32(ERayTracingPayloadType::GPULightmass))
|
|
)
|
|
{
|
|
// If any payload requires a fully simplified material, we force fully simplified material all the way.
|
|
// That is used to have material ray tracing shaders compressed to single slab.
|
|
// Smaller payload means faster performance and for some tracing this will be enough, e.g. reflected materials, lightmass diffuse interactions.
|
|
OutEnvironment.SetDefine(TEXT("SUBSTRATE_USE_FULLYSIMPLIFIED_MATERIAL"), 1);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
bool FShaderType::ValidateCompiledResult(EShaderPlatform Platform, const FShaderParameterMap& ParameterMap, TArray<FString>& OutError) const
|
|
{
|
|
return (*ValidateCompiledResultRef)(Platform, ParameterMap, OutError);
|
|
}
|
|
|
|
EShaderCompileJobPriority FShaderType::GetOverrideJobPriority() const
|
|
{
|
|
return (*GetOverrideJobPriorityRef)();
|
|
}
|
|
|
|
void FShaderType::UpdateReferencedUniformBufferNames(const TMap<FString, TArray<const TCHAR*>>& ShaderFileToUniformBufferVariables)
|
|
{
|
|
ReferencedUniformBuffers.Empty();
|
|
GenerateReferencedUniformBuffers(SourceFilename, Name, ShaderFileToUniformBufferVariables, ReferencedUniformBuffers);
|
|
}
|
|
|
|
FString FShaderType::GetPermutationIdString(int32 PermutationId, bool bFullNames) const
|
|
{
|
|
// This function is for diagnostics only, so ignore it if the shader type did not initialize its format string
|
|
if (GetPermutationIdStringRef)
|
|
{
|
|
FString PermutationIdentifier;
|
|
(*GetPermutationIdStringRef)(PermutationId, PermutationIdentifier, bFullNames);
|
|
return PermutationIdentifier;
|
|
}
|
|
return FString();
|
|
}
|
|
#endif // WITH_EDITOR
|
|
|
|
ERayTracingPayloadType FShaderType::GetRayTracingPayloadType(const int32 PermutationId) const
|
|
{
|
|
#if RHI_RAYTRACING
|
|
return (*GetRayTracingPayloadTypeRef)(PermutationId);
|
|
#else
|
|
return static_cast<ERayTracingPayloadType>(0);
|
|
#endif
|
|
}
|
|
|
|
const FSHAHash& FShaderType::GetSourceHash(EShaderPlatform ShaderPlatform) const
|
|
{
|
|
return GetShaderFileHash(GetShaderFilename(), ShaderPlatform);
|
|
}
|
|
|
|
void FShaderType::Initialize(const TMap<FString, TArray<const TCHAR*> >& ShaderFileToUniformBufferVariables)
|
|
{
|
|
#if WITH_EDITOR
|
|
//#todo-rco: Need to call this only when Initializing from a Pipeline once it's removed from the global linked list
|
|
if (!FPlatformProperties::RequiresCookedData())
|
|
{
|
|
#if UE_BUILD_DEBUG
|
|
TArray<FShaderType*> UniqueShaderTypes;
|
|
#endif
|
|
TArray<UE::Tasks::TTask<void>> Tasks;
|
|
Tasks.Reserve(FShaderType::GetNameToTypeMap().Num());
|
|
|
|
for(TLinkedList<FShaderType*>::TIterator It(FShaderType::GetTypeList()); It; It.Next())
|
|
{
|
|
FShaderType* Type = *It;
|
|
#if UE_BUILD_DEBUG
|
|
UniqueShaderTypes.Add(Type);
|
|
#endif
|
|
Tasks.Emplace(
|
|
UE::Tasks::Launch(
|
|
TEXT("UpdateReferencedUniformBufferNames"),
|
|
[Type, &ShaderFileToUniformBufferVariables]() mutable
|
|
{
|
|
Type->UpdateReferencedUniformBufferNames(ShaderFileToUniformBufferVariables);
|
|
}
|
|
)
|
|
);
|
|
}
|
|
UE::Tasks::Wait(Tasks);
|
|
|
|
#if UE_BUILD_DEBUG
|
|
// Check for duplicated shader type names
|
|
UniqueShaderTypes.Sort([](const FShaderType& A, const FShaderType& B) { return (SIZE_T)&A < (SIZE_T)&B; });
|
|
for (int32 Index = 1; Index < UniqueShaderTypes.Num(); ++Index)
|
|
{
|
|
checkf(UniqueShaderTypes[Index - 1] != UniqueShaderTypes[Index], TEXT("Duplicated FShader type name %s found, please rename one of them!"), UniqueShaderTypes[Index]->GetName());
|
|
}
|
|
#endif
|
|
}
|
|
#endif // WITH_EDITOR
|
|
}
|
|
|
|
int32 FShaderMapPointerTable::AddIndexedPointer(const FTypeLayoutDesc& TypeDesc, void* Ptr)
|
|
{
|
|
int32 Index = INDEX_NONE;
|
|
if (ShaderTypes.TryAddIndexedPtr(TypeDesc, Ptr, Index)) return Index;
|
|
if (VFTypes.TryAddIndexedPtr(TypeDesc, Ptr, Index)) return Index;
|
|
return Index;
|
|
}
|
|
|
|
void* FShaderMapPointerTable::GetIndexedPointer(const FTypeLayoutDesc& TypeDesc, uint32 i) const
|
|
{
|
|
void* Ptr = nullptr;
|
|
if (ShaderTypes.TryGetIndexedPtr(TypeDesc, i, Ptr)) return Ptr;
|
|
if (VFTypes.TryGetIndexedPtr(TypeDesc, i, Ptr)) return Ptr;
|
|
return Ptr;
|
|
}
|
|
|
|
void FShaderMapPointerTable::SaveToArchive(FArchive& Ar, const FPlatformTypeLayoutParameters& LayoutParams, const void* FrozenObject) const
|
|
{
|
|
FPointerTableBase::SaveToArchive(Ar, LayoutParams, FrozenObject);
|
|
|
|
int32 NumTypes = ShaderTypes.Num();
|
|
int32 NumVFTypes = VFTypes.Num();
|
|
|
|
Ar << NumTypes;
|
|
Ar << NumVFTypes;
|
|
|
|
for (int32 TypeIndex = 0; TypeIndex < NumTypes; ++TypeIndex)
|
|
{
|
|
const FShaderType* Type = ShaderTypes.GetIndexedPointer(TypeIndex);
|
|
FHashedName TypeName = Type->GetHashedName();
|
|
Ar << TypeName;
|
|
}
|
|
|
|
for (int32 VFTypeIndex = 0; VFTypeIndex < NumVFTypes; ++VFTypeIndex)
|
|
{
|
|
const FVertexFactoryType* VFType = VFTypes.GetIndexedPointer(VFTypeIndex);
|
|
FHashedName TypeName = VFType->GetHashedName();
|
|
Ar << TypeName;
|
|
}
|
|
}
|
|
|
|
bool FShaderMapPointerTable::LoadFromArchive(FArchive& Ar, const FPlatformTypeLayoutParameters& LayoutParams, void* FrozenObject)
|
|
{
|
|
SCOPED_LOADTIMER(FShaderMapPointerTable_LoadFromArchive);
|
|
|
|
const bool bResult = FPointerTableBase::LoadFromArchive(Ar, LayoutParams, FrozenObject);
|
|
|
|
int32 NumTypes = 0;
|
|
int32 NumVFTypes = 0;
|
|
|
|
Ar << NumTypes;
|
|
Ar << NumVFTypes;
|
|
|
|
ShaderTypes.Empty(NumTypes);
|
|
for (int32 TypeIndex = 0; TypeIndex < NumTypes; ++TypeIndex)
|
|
{
|
|
FHashedName TypeName;
|
|
Ar << TypeName;
|
|
FShaderType* Type = FindShaderTypeByName(TypeName);
|
|
ShaderTypes.LoadIndexedPointer(Type);
|
|
}
|
|
|
|
VFTypes.Empty(NumVFTypes);
|
|
for (int32 VFTypeIndex = 0; VFTypeIndex < NumVFTypes; ++VFTypeIndex)
|
|
{
|
|
FHashedName TypeName;
|
|
Ar << TypeName;
|
|
FVertexFactoryType* VFType = FVertexFactoryType::GetVFByName(TypeName);
|
|
VFTypes.LoadIndexedPointer(VFType);
|
|
}
|
|
|
|
return bResult;
|
|
}
|
|
|
|
FShaderCompiledShaderInitializerType::FShaderCompiledShaderInitializerType(
|
|
const FShaderType* InType,
|
|
const FShaderType::FParameters* InParameters,
|
|
int32 InPermutationId,
|
|
const FShaderCompilerOutput& CompilerOutput,
|
|
const FSHAHash& InMaterialShaderMapHash,
|
|
const FShaderPipelineType* InShaderPipeline,
|
|
const FVertexFactoryType* InVertexFactoryType
|
|
)
|
|
: Type(InType)
|
|
, Parameters(InParameters)
|
|
, Target(CompilerOutput.Target)
|
|
, Code(CompilerOutput.ShaderCode.GetReadView())
|
|
, ParameterMap(CompilerOutput.ParameterMap)
|
|
, OutputHash(CompilerOutput.OutputHash)
|
|
, MaterialShaderMapHash(InMaterialShaderMapHash)
|
|
, ShaderPipeline(InShaderPipeline)
|
|
, VertexFactoryType(InVertexFactoryType)
|
|
, NumInstructions(CompilerOutput.NumInstructions)
|
|
, NumTextureSamplers(CompilerOutput.NumTextureSamplers)
|
|
, CodeSize(CompilerOutput.ShaderCode.GetShaderCodeSize())
|
|
, PermutationId(InPermutationId)
|
|
, ShaderStatistics(CompilerOutput.ShaderStatistics)
|
|
{
|
|
}
|
|
|
|
/**
|
|
* Used to construct a shader for deserialization.
|
|
* This still needs to initialize members to safe values since FShaderType::GenerateSerializationHistory uses this constructor.
|
|
*/
|
|
FShader::FShader()
|
|
// set to undefined (currently shared with SF_Vertex)
|
|
: Target((EShaderFrequency)0, GShaderPlatformForFeatureLevel[GMaxRHIFeatureLevel])
|
|
, ResourceIndex(INDEX_NONE)
|
|
#if WITH_EDITORONLY_DATA
|
|
, NumInstructions(0u)
|
|
, NumTextureSamplers(0u)
|
|
, CodeSize(0u)
|
|
#endif // WITH_EDITORONLY_DATA
|
|
{
|
|
}
|
|
|
|
/**
|
|
* Construct a shader from shader compiler output.
|
|
*/
|
|
FShader::FShader(const CompiledShaderInitializerType& Initializer)
|
|
: Type(const_cast<FShaderType*>(Initializer.Type)) // TODO - remove const_cast, make TIndexedPtr work with 'const'
|
|
, VFType(const_cast<FVertexFactoryType*>(Initializer.VertexFactoryType))
|
|
, Target(Initializer.Target)
|
|
, ResourceIndex(INDEX_NONE)
|
|
#if WITH_EDITORONLY_DATA
|
|
, NumInstructions(Initializer.NumInstructions)
|
|
, NumTextureSamplers(Initializer.NumTextureSamplers)
|
|
, CodeSize(Initializer.CodeSize)
|
|
#endif // WITH_EDITORONLY_DATA
|
|
{
|
|
checkSlow(Initializer.OutputHash != FSHAHash());
|
|
|
|
// Only store a truncated hash to minimize memory overhead
|
|
static_assert(sizeof(SortKey) <= sizeof(Initializer.OutputHash.Hash));
|
|
FMemory::Memcpy(&SortKey, Initializer.OutputHash.Hash, sizeof(SortKey));
|
|
|
|
#if WITH_EDITORONLY_DATA
|
|
OutputHash = Initializer.OutputHash;
|
|
|
|
// Store off the source hash that this shader was compiled with
|
|
// This will be used as part of the shader key in order to identify when shader files have been changed and a recompile is needed
|
|
SourceHash = Initializer.Type->GetSourceHash(Initializer.Target.GetPlatform());
|
|
|
|
if (Initializer.VertexFactoryType)
|
|
{
|
|
// Store off the VF source hash that this shader was compiled with
|
|
VFSourceHash = Initializer.VertexFactoryType->GetSourceHash(Initializer.Target.GetPlatform());
|
|
}
|
|
#endif // WITH_EDITORONLY_DATA
|
|
|
|
BuildParameterMapInfo(Initializer.ParameterMap.GetParameterMap());
|
|
|
|
// Bind uniform buffer parameters automatically
|
|
for (TLinkedList<FShaderParametersMetadata*>::TIterator StructIt(FShaderParametersMetadata::GetStructList()); StructIt; StructIt.Next())
|
|
{
|
|
if (Initializer.ParameterMap.ContainsParameterAllocation(StructIt->GetShaderVariableName()))
|
|
{
|
|
UniformBufferParameterStructs.Add(StructIt->GetShaderVariableHashedName());
|
|
FShaderUniformBufferParameter& Parameter = UniformBufferParameters.AddDefaulted_GetRef();
|
|
Parameter.Bind(Initializer.ParameterMap, StructIt->GetShaderVariableName(), SPF_Mandatory);
|
|
}
|
|
}
|
|
|
|
// Register the shader now that it is valid, so that it can be reused
|
|
//Register(false);
|
|
}
|
|
|
|
FShader::~FShader()
|
|
{
|
|
}
|
|
|
|
void FShader::Finalize(const FShaderMapResourceCode* Code)
|
|
{
|
|
const FSHAHash& Hash = GetOutputHash();
|
|
const int32 NewResourceIndex = Code->FindShaderIndex(Hash);
|
|
checkf(NewResourceIndex != INDEX_NONE, TEXT("Missing shader code %s"), *Hash.ToString());
|
|
ResourceIndex = NewResourceIndex;
|
|
}
|
|
|
|
template<class TType>
|
|
static void CityHashArray(uint64& Hash, const TMemoryImageArray<TType>& Array)
|
|
{
|
|
const int32 ArrayNum = Array.Num();
|
|
CityHash64WithSeed((const char*)&ArrayNum, sizeof(ArrayNum), Hash);
|
|
CityHash64WithSeed((const char*)Array.GetData(), Array.Num() * sizeof(TType), Hash);
|
|
}
|
|
|
|
void FShader::BuildParameterMapInfo(const TMap<FString, FParameterAllocation>& ParameterMap)
|
|
{
|
|
uint32 UniformCount = 0;
|
|
uint32 SamplerCount = 0;
|
|
uint32 SRVCount = 0;
|
|
|
|
for (TMap<FString, FParameterAllocation>::TConstIterator ParameterIt(ParameterMap); ParameterIt; ++ParameterIt)
|
|
{
|
|
const FParameterAllocation& ParamValue = ParameterIt.Value();
|
|
|
|
switch (ParamValue.Type)
|
|
{
|
|
case EShaderParameterType::UniformBuffer:
|
|
UniformCount++;
|
|
break;
|
|
case EShaderParameterType::BindlessSampler:
|
|
case EShaderParameterType::Sampler:
|
|
SamplerCount++;
|
|
break;
|
|
case EShaderParameterType::BindlessSRV:
|
|
case EShaderParameterType::SRV:
|
|
SRVCount++;
|
|
break;
|
|
}
|
|
}
|
|
|
|
ParameterMapInfo.UniformBuffers.Empty(UniformCount);
|
|
ParameterMapInfo.TextureSamplers.Empty(SamplerCount);
|
|
ParameterMapInfo.SRVs.Empty(SRVCount);
|
|
|
|
auto GetResourceParameterMap = [this](EShaderParameterType ParameterType) -> TMemoryImageArray<FShaderResourceParameterInfo>*
|
|
{
|
|
switch (ParameterType)
|
|
{
|
|
case EShaderParameterType::Sampler:
|
|
return &ParameterMapInfo.TextureSamplers;
|
|
case EShaderParameterType::SRV:
|
|
return &ParameterMapInfo.SRVs;
|
|
case EShaderParameterType::BindlessSRV:
|
|
return &ParameterMapInfo.SRVs;
|
|
case EShaderParameterType::BindlessSampler:
|
|
return &ParameterMapInfo.TextureSamplers;
|
|
default:
|
|
return nullptr;
|
|
}
|
|
};
|
|
|
|
for (TMap<FString, FParameterAllocation>::TConstIterator ParameterIt(ParameterMap); ParameterIt; ++ParameterIt)
|
|
{
|
|
const FParameterAllocation& ParamValue = ParameterIt.Value();
|
|
|
|
if (ParamValue.Type == EShaderParameterType::LooseData)
|
|
{
|
|
bool bAddedToExistingBuffer = false;
|
|
|
|
for (int32 LooseParameterBufferIndex = 0; LooseParameterBufferIndex < ParameterMapInfo.LooseParameterBuffers.Num(); LooseParameterBufferIndex++)
|
|
{
|
|
FShaderLooseParameterBufferInfo& LooseParameterBufferInfo = ParameterMapInfo.LooseParameterBuffers[LooseParameterBufferIndex];
|
|
|
|
if (LooseParameterBufferInfo.BaseIndex == ParamValue.BufferIndex)
|
|
{
|
|
LooseParameterBufferInfo.Parameters.Emplace(ParamValue.BaseIndex, ParamValue.Size);
|
|
LooseParameterBufferInfo.Size += ParamValue.Size;
|
|
bAddedToExistingBuffer = true;
|
|
}
|
|
}
|
|
|
|
if (!bAddedToExistingBuffer)
|
|
{
|
|
FShaderLooseParameterBufferInfo NewParameterBufferInfo(ParamValue.BufferIndex, ParamValue.Size);
|
|
|
|
NewParameterBufferInfo.Parameters.Emplace(ParamValue.BaseIndex, ParamValue.Size);
|
|
|
|
ParameterMapInfo.LooseParameterBuffers.Add(NewParameterBufferInfo);
|
|
}
|
|
}
|
|
else if (ParamValue.Type == EShaderParameterType::UniformBuffer)
|
|
{
|
|
ParameterMapInfo.UniformBuffers.Emplace(ParamValue.BufferIndex);
|
|
}
|
|
else if (TMemoryImageArray<FShaderResourceParameterInfo>* ParameterInfoArray = GetResourceParameterMap(ParamValue.Type))
|
|
{
|
|
ParameterInfoArray->Emplace(ParamValue.BaseIndex, ParamValue.BufferIndex, ParamValue.Type);
|
|
}
|
|
}
|
|
|
|
for (FShaderLooseParameterBufferInfo& Info : ParameterMapInfo.LooseParameterBuffers)
|
|
{
|
|
Info.Parameters.Sort();
|
|
}
|
|
ParameterMapInfo.LooseParameterBuffers.Sort();
|
|
ParameterMapInfo.UniformBuffers.Sort();
|
|
ParameterMapInfo.TextureSamplers.Sort();
|
|
ParameterMapInfo.SRVs.Sort();
|
|
|
|
uint64 Hash = 0;
|
|
|
|
{
|
|
const auto CityHashValue = [&](auto Value)
|
|
{
|
|
CityHash64WithSeed((const char*)&Value, sizeof(Value), Hash);
|
|
};
|
|
|
|
for (FShaderLooseParameterBufferInfo& Info : ParameterMapInfo.LooseParameterBuffers)
|
|
{
|
|
CityHashValue(Info.BaseIndex);
|
|
CityHashValue(Info.Size);
|
|
CityHashArray(Hash, Info.Parameters);
|
|
}
|
|
CityHashArray(Hash, ParameterMapInfo.UniformBuffers);
|
|
CityHashArray(Hash, ParameterMapInfo.TextureSamplers);
|
|
CityHashArray(Hash, ParameterMapInfo.SRVs);
|
|
}
|
|
|
|
ParameterMapInfo.Hash = Hash;
|
|
}
|
|
|
|
const FSHAHash& FShader::GetOutputHash() const
|
|
{
|
|
#if WITH_EDITORONLY_DATA
|
|
return OutputHash;
|
|
#else
|
|
return ShaderSourceDefaultHash;
|
|
#endif
|
|
}
|
|
|
|
const FSHAHash& FShader::GetHash() const
|
|
{
|
|
#if WITH_EDITORONLY_DATA
|
|
return SourceHash;
|
|
#else
|
|
return ShaderSourceDefaultHash;
|
|
#endif
|
|
}
|
|
|
|
const FSHAHash& FShader::GetVertexFactoryHash() const
|
|
{
|
|
#if WITH_EDITORONLY_DATA
|
|
return VFSourceHash;
|
|
#else
|
|
return ShaderSourceDefaultHash;
|
|
#endif
|
|
}
|
|
|
|
const FTypeLayoutDesc& GetTypeLayoutDesc(const FPointerTableBase* PtrTable, const FShader& Shader)
|
|
{
|
|
const FShaderType* Type = Shader.GetType(PtrTable);
|
|
checkf(Type, TEXT("FShaderType is missing"));
|
|
return Type->GetLayout();
|
|
}
|
|
|
|
const FShaderParametersMetadata* FShader::FindAutomaticallyBoundUniformBufferStruct(int32 BaseIndex) const
|
|
{
|
|
for (int32 i = 0; i < UniformBufferParameters.Num(); i++)
|
|
{
|
|
if (UniformBufferParameters[i].GetBaseIndex() == BaseIndex)
|
|
{
|
|
FShaderParametersMetadata** Parameters = FShaderParametersMetadata::GetNameStructMap().Find(UniformBufferParameterStructs[i]);
|
|
return Parameters ? *Parameters : nullptr;
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
void FShader::DumpDebugInfo(const FShaderMapPointerTable& InPtrTable)
|
|
{
|
|
FVertexFactoryType* VertexFactoryType = GetVertexFactoryType(InPtrTable);
|
|
|
|
UE_LOG(LogConsoleResponse, Display, TEXT(" FShader :Frequency %s"), GetShaderFrequencyString(GetFrequency()));
|
|
UE_LOG(LogConsoleResponse, Display, TEXT(" :Target %s"), *LegacyShaderPlatformToShaderFormat(GetShaderPlatform()).ToString());
|
|
UE_LOG(LogConsoleResponse, Display, TEXT(" :VFType %s"), VertexFactoryType ? VertexFactoryType->GetName() : TEXT("null"));
|
|
UE_LOG(LogConsoleResponse, Display, TEXT(" :Type %s"), GetType(InPtrTable)->GetName());
|
|
UE_LOG(LogConsoleResponse, Display, TEXT(" :SourceHash %s"), *GetHash().ToString());
|
|
UE_LOG(LogConsoleResponse, Display, TEXT(" :VFSourceHash %s"), *GetVertexFactoryHash().ToString());
|
|
UE_LOG(LogConsoleResponse, Display, TEXT(" :OutputHash %s"), *GetOutputHash().ToString());
|
|
}
|
|
|
|
#if WITH_EDITOR
|
|
void FShader::SaveShaderStableKeys(const FShaderMapPointerTable& InPtrTable, EShaderPlatform TargetShaderPlatform, int32 PermutationId, const FStableShaderKeyAndValue& InSaveKeyVal)
|
|
{
|
|
if ((TargetShaderPlatform == EShaderPlatform::SP_NumPlatforms || GetShaderPlatform() == TargetShaderPlatform)
|
|
&& FShaderLibraryCooker::NeedsShaderStableKeys(TargetShaderPlatform))
|
|
{
|
|
FShaderType* ShaderType = GetType(InPtrTable);
|
|
FVertexFactoryType* VertexFactoryType = GetVertexFactoryType(InPtrTable);
|
|
|
|
FStableShaderKeyAndValue SaveKeyVal(InSaveKeyVal);
|
|
SaveKeyVal.TargetFrequency = FName(GetShaderFrequencyString(GetFrequency()));
|
|
SaveKeyVal.TargetPlatform = LegacyShaderPlatformToShaderFormat(GetShaderPlatform());
|
|
SaveKeyVal.VFType = FName(VertexFactoryType ? VertexFactoryType->GetName() : TEXT("null"));
|
|
SaveKeyVal.PermutationId = FName(*FString::Printf(TEXT("Perm_%d"), PermutationId));
|
|
SaveKeyVal.OutputHash = GetOutputHash();
|
|
if (ShaderType)
|
|
{
|
|
ShaderType->GetShaderStableKeyParts(SaveKeyVal);
|
|
}
|
|
FShaderLibraryCooker::AddShaderStableKeyValue(GetShaderPlatform(), SaveKeyVal);
|
|
}
|
|
}
|
|
#endif // WITH_EDITOR
|
|
|
|
bool FShaderPipelineType::bInitialized = false;
|
|
|
|
static TArray<FShaderPipelineType*>& GetSortedShaderPipelineTypes(FShaderType::EShaderTypeForDynamicCast Type)
|
|
{
|
|
static TArray<FShaderPipelineType*> SortedTypes[(uint32)FShaderType::EShaderTypeForDynamicCast::NumShaderTypes];
|
|
return SortedTypes[(uint32)Type];
|
|
}
|
|
|
|
FShaderPipelineType::FShaderPipelineType(
|
|
const TCHAR* InName,
|
|
const FShaderType* InVertexOrMeshShader,
|
|
const FShaderType* InGeometryOrAmplificationShader,
|
|
const FShaderType* InPixelShader,
|
|
bool bInIsMeshPipeline,
|
|
bool bInShouldOptimizeUnusedOutputs) :
|
|
Name(InName),
|
|
TypeName(Name),
|
|
HashedName(TypeName),
|
|
HashedPrimaryShaderFilename(InVertexOrMeshShader->GetShaderFilename()),
|
|
GlobalListLink(this),
|
|
bShouldOptimizeUnusedOutputs(bInShouldOptimizeUnusedOutputs)
|
|
{
|
|
checkf(Name && *Name, TEXT("Shader Pipeline Type requires a valid Name!"));
|
|
|
|
checkf(InVertexOrMeshShader, TEXT("A Shader Pipeline always requires a Vertex or Mesh Shader"));
|
|
|
|
//make sure the name is shorter than the maximum serializable length
|
|
check(FCString::Strlen(InName) < NAME_SIZE);
|
|
|
|
FMemory::Memzero(AllStages);
|
|
|
|
if (InPixelShader)
|
|
{
|
|
check(InPixelShader->GetTypeForDynamicCast() == InVertexOrMeshShader->GetTypeForDynamicCast());
|
|
Stages.Add(InPixelShader);
|
|
AllStages[SF_Pixel] = InPixelShader;
|
|
}
|
|
|
|
if (InGeometryOrAmplificationShader)
|
|
{
|
|
check(InGeometryOrAmplificationShader->GetTypeForDynamicCast() == InVertexOrMeshShader->GetTypeForDynamicCast());
|
|
Stages.Add(InGeometryOrAmplificationShader);
|
|
AllStages[bInIsMeshPipeline ? SF_Amplification : SF_Geometry] = InGeometryOrAmplificationShader;
|
|
}
|
|
Stages.Add(InVertexOrMeshShader);
|
|
AllStages[bInIsMeshPipeline ? SF_Mesh : SF_Vertex] = InVertexOrMeshShader;
|
|
|
|
for (uint32 FrequencyIndex = 0; FrequencyIndex < SF_NumStandardFrequencies; ++FrequencyIndex)
|
|
{
|
|
if (const FShaderType* ShaderType = AllStages[FrequencyIndex])
|
|
{
|
|
checkf(ShaderType->GetPermutationCount() == 1, TEXT("Shader '%s' has multiple shader permutations. Shader pipelines only support a single permutation."), ShaderType->GetName())
|
|
}
|
|
}
|
|
|
|
static uint32 TypeHashCounter = 0;
|
|
++TypeHashCounter;
|
|
HashIndex = TypeHashCounter;
|
|
|
|
GlobalListLink.LinkHead(GetTypeList());
|
|
GetNameToTypeMap().Add(HashedName, this);
|
|
|
|
TArray<FShaderPipelineType*>& SortedTypes = GetSortedShaderPipelineTypes(InVertexOrMeshShader->GetTypeForDynamicCast());
|
|
const int32 SortedIndex = Algo::LowerBoundBy(SortedTypes, HashedName, [](const FShaderPipelineType* InType) { return InType->GetHashedName(); });
|
|
SortedTypes.Insert(this, SortedIndex);
|
|
|
|
// This will trigger if an IMPLEMENT_SHADER_TYPE was in a module not loaded before InitializeShaderTypes
|
|
// Shader types need to be implemented in modules that are loaded before that
|
|
checkf(!bInitialized, TEXT("Shader Pipeline was loaded after Engine init, use ELoadingPhase::PostConfigInit on your module to cause it to load earlier."));
|
|
}
|
|
|
|
FShaderPipelineType::~FShaderPipelineType()
|
|
{
|
|
GetNameToTypeMap().Remove(HashedName);
|
|
GlobalListLink.Unlink();
|
|
|
|
TArray<FShaderPipelineType*>& SortedTypes = GetSortedShaderPipelineTypes(AllStages[HasMeshShader() ? SF_Mesh : SF_Vertex]->GetTypeForDynamicCast());
|
|
const int32 SortedIndex = Algo::BinarySearchBy(SortedTypes, HashedName, [](const FShaderPipelineType* InType) { return InType->GetHashedName(); });
|
|
check(SortedIndex != INDEX_NONE);
|
|
SortedTypes.RemoveAt(SortedIndex);
|
|
}
|
|
|
|
TMap<FHashedName, FShaderPipelineType*>& FShaderPipelineType::GetNameToTypeMap()
|
|
{
|
|
static TMap<FHashedName, FShaderPipelineType*> GShaderPipelineNameToTypeMap;
|
|
return GShaderPipelineNameToTypeMap;
|
|
}
|
|
|
|
TLinkedList<FShaderPipelineType*>*& FShaderPipelineType::GetTypeList()
|
|
{
|
|
return GShaderPipelineList;
|
|
}
|
|
|
|
const TArray<FShaderPipelineType*>& FShaderPipelineType::GetSortedTypes(FShaderType::EShaderTypeForDynamicCast Type)
|
|
{
|
|
return GetSortedShaderPipelineTypes(Type);
|
|
}
|
|
|
|
TArray<const FShaderPipelineType*> FShaderPipelineType::GetShaderPipelineTypesByFilename(const TCHAR* InFilename, bool bSearchAsRegexFilter)
|
|
{
|
|
if (bSearchAsRegexFilter)
|
|
{
|
|
const FRegexPattern RegexSearch(FString(InFilename).Replace(TEXT("*"), TEXT("(.)*")), ERegexPatternFlags::CaseInsensitive);
|
|
return FShaderPipelineType::GetShaderPipelineTypesByFilenameFilter(
|
|
[&RegexSearch](const TCHAR* ShaderTypeFilename) -> bool
|
|
{
|
|
return FRegexMatcher(RegexSearch, ShaderTypeFilename).FindNext();
|
|
}
|
|
);
|
|
}
|
|
else
|
|
{
|
|
return FShaderPipelineType::GetShaderPipelineTypesByFilenameFilter(
|
|
[InFilename](const TCHAR* ShaderTypeFilename) -> bool
|
|
{
|
|
return FPlatformString::Strcmp(InFilename, ShaderTypeFilename) == 0;
|
|
}
|
|
);
|
|
}
|
|
}
|
|
|
|
TArray<const FShaderPipelineType*> FShaderPipelineType::GetShaderPipelineTypesByFilenameFilter(const TFunction<bool(const TCHAR*)>& InFilenameFilter)
|
|
{
|
|
TArray<const FShaderPipelineType*> PipelineTypes;
|
|
for (TLinkedList<FShaderPipelineType*>::TIterator It(FShaderPipelineType::GetTypeList()); It; It.Next())
|
|
{
|
|
auto* PipelineType = *It;
|
|
for (auto* ShaderType : PipelineType->Stages)
|
|
{
|
|
if (InFilenameFilter(ShaderType->GetShaderFilename()))
|
|
{
|
|
PipelineTypes.AddUnique(PipelineType);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return PipelineTypes;
|
|
}
|
|
|
|
void FShaderPipelineType::Initialize()
|
|
{
|
|
check(!bInitialized);
|
|
|
|
TSet<FName> UsedNames;
|
|
|
|
#if UE_BUILD_DEBUG
|
|
TArray<const FShaderPipelineType*> UniqueShaderPipelineTypes;
|
|
#endif
|
|
for (TLinkedList<FShaderPipelineType*>::TIterator It(FShaderPipelineType::GetTypeList()); It; It.Next())
|
|
{
|
|
const auto* PipelineType = *It;
|
|
|
|
#if UE_BUILD_DEBUG
|
|
UniqueShaderPipelineTypes.Add(PipelineType);
|
|
#endif
|
|
|
|
// Validate stages
|
|
for (int32 Index = 0; Index < SF_NumFrequencies; ++Index)
|
|
{
|
|
check(!PipelineType->AllStages[Index] || PipelineType->AllStages[Index]->GetFrequency() == (EShaderFrequency)Index);
|
|
}
|
|
|
|
auto& Stages = PipelineType->GetStages();
|
|
|
|
// #todo-rco: Do we allow mix/match of global/mesh/material stages?
|
|
// Check all shaders are the same type, start from the top-most stage
|
|
const FGlobalShaderType* GlobalType = Stages[0]->GetGlobalShaderType();
|
|
const FMeshMaterialShaderType* MeshType = Stages[0]->GetMeshMaterialShaderType();
|
|
const FMaterialShaderType* MateriallType = Stages[0]->GetMaterialShaderType();
|
|
for (int32 Index = 1; Index < Stages.Num(); ++Index)
|
|
{
|
|
if (GlobalType)
|
|
{
|
|
checkf(Stages[Index]->GetGlobalShaderType(), TEXT("Invalid combination of Shader types on Pipeline %s"), PipelineType->Name);
|
|
}
|
|
else if (MeshType)
|
|
{
|
|
checkf(Stages[Index]->GetMeshMaterialShaderType(), TEXT("Invalid combination of Shader types on Pipeline %s"), PipelineType->Name);
|
|
}
|
|
else if (MateriallType)
|
|
{
|
|
checkf(Stages[Index]->GetMaterialShaderType(), TEXT("Invalid combination of Shader types on Pipeline %s"), PipelineType->Name);
|
|
}
|
|
}
|
|
|
|
FName PipelineName = PipelineType->GetFName();
|
|
checkf(!UsedNames.Contains(PipelineName), TEXT("Two Pipelines with the same name %s found!"), PipelineType->Name);
|
|
UsedNames.Add(PipelineName);
|
|
}
|
|
|
|
#if UE_BUILD_DEBUG
|
|
// Check for duplicated shader pipeline type names
|
|
UniqueShaderPipelineTypes.Sort([](const FShaderPipelineType& A, const FShaderPipelineType& B) { return (SIZE_T)&A < (SIZE_T)&B; });
|
|
for (int32 Index = 1; Index < UniqueShaderPipelineTypes.Num(); ++Index)
|
|
{
|
|
checkf(UniqueShaderPipelineTypes[Index - 1] != UniqueShaderPipelineTypes[Index], TEXT("Duplicated FShaderPipeline type name %s found, please rename one of them!"), UniqueShaderPipelineTypes[Index]->GetName());
|
|
}
|
|
#endif
|
|
|
|
bInitialized = true;
|
|
}
|
|
|
|
const FShaderPipelineType* FShaderPipelineType::GetShaderPipelineTypeByName(const FHashedName& Name)
|
|
{
|
|
FShaderPipelineType** FoundType = GetNameToTypeMap().Find(Name);
|
|
return FoundType ? *FoundType : nullptr;
|
|
}
|
|
|
|
bool FShaderPipelineType::ShouldOptimizeUnusedOutputs(EShaderPlatform Platform) const
|
|
{
|
|
return bShouldOptimizeUnusedOutputs && RHISupportsShaderPipelines(Platform);
|
|
}
|
|
|
|
const FSHAHash& FShaderPipelineType::GetSourceHash(EShaderPlatform ShaderPlatform) const
|
|
{
|
|
TArray<FString> Filenames;
|
|
for (const FShaderType* ShaderType : Stages)
|
|
{
|
|
Filenames.Add(ShaderType->GetShaderFilename());
|
|
}
|
|
return GetShaderFilesHash(Filenames, ShaderPlatform);
|
|
}
|
|
|
|
bool FShaderPipelineType::ShouldCompilePermutation(const FShaderPermutationParameters& Parameters) const
|
|
{
|
|
for (const FShaderType* ShaderType : Stages)
|
|
{
|
|
if (!ShaderType->ShouldCompilePermutation(Parameters))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
EShaderPermutationPrecacheRequest FShaderPipelineType::ShouldPrecachePermutation(const FShaderPermutationParameters& Parameters) const
|
|
{
|
|
EShaderPermutationPrecacheRequest Result = EShaderPermutationPrecacheRequest::NotUsed;
|
|
for (const FShaderType* ShaderType : Stages)
|
|
{
|
|
EShaderPermutationPrecacheRequest ShaderTypeRequest = ShaderType->ShouldPrecachePermutation(Parameters);
|
|
if (ShaderTypeRequest == EShaderPermutationPrecacheRequest::Precached)
|
|
{
|
|
return ShaderTypeRequest;
|
|
}
|
|
else if (ShaderTypeRequest == EShaderPermutationPrecacheRequest::NotPrecached)
|
|
{
|
|
Result = ShaderTypeRequest;
|
|
}
|
|
}
|
|
return Result;
|
|
}
|
|
|
|
void FShaderTypeDependency::RefreshCachedSourceHash(EShaderPlatform ShaderPlatform)
|
|
{
|
|
const FShaderType*const* ShaderTypePtr = FShaderType::GetNameToTypeMap().Find(ShaderTypeName);
|
|
const FShaderType* ShaderType = ShaderTypePtr ? *ShaderTypePtr : nullptr;
|
|
if (!ShaderType)
|
|
{
|
|
SourceHash = FSHAHash();
|
|
return;
|
|
}
|
|
SourceHash = ShaderType->GetSourceHash(ShaderPlatform);
|
|
}
|
|
|
|
void FShaderPipelineTypeDependency::RefreshCachedSourceHash(EShaderPlatform ShaderPlatform)
|
|
{
|
|
const FShaderPipelineType* ShaderPipelineType =
|
|
FShaderPipelineType::GetShaderPipelineTypeByName(ShaderPipelineTypeName);
|
|
if (!ShaderPipelineType)
|
|
{
|
|
StagesSourceHash = FSHAHash();
|
|
return;
|
|
}
|
|
StagesSourceHash = ShaderPipelineType->GetSourceHash(ShaderPlatform);
|
|
}
|
|
|
|
#if WITH_EDITOR
|
|
|
|
void FShaderTypeDependency::Save(FCbWriter& Writer) const
|
|
{
|
|
Writer.BeginArray();
|
|
Writer << ShaderTypeName;
|
|
Writer << SourceHash;
|
|
Writer << PermutationId;
|
|
Writer.EndArray();
|
|
}
|
|
|
|
bool FShaderTypeDependency::TryLoad(FCbFieldView Field)
|
|
{
|
|
*this = FShaderTypeDependency();
|
|
FCbFieldViewIterator ElementField(Field.CreateViewIterator());
|
|
if (!LoadFromCompactBinary(ElementField++, ShaderTypeName))
|
|
{
|
|
return false;
|
|
}
|
|
if (!LoadFromCompactBinary(ElementField++, SourceHash))
|
|
{
|
|
return false;
|
|
}
|
|
PermutationId = ElementField.AsInt32();
|
|
if ((ElementField++).HasError())
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool LoadFromCompactBinary(FCbFieldView Field, FShaderTypeDependency& OutValue)
|
|
{
|
|
return OutValue.TryLoad(Field);
|
|
}
|
|
|
|
void FShaderPipelineTypeDependency::Save(FCbWriter& Writer) const
|
|
{
|
|
Writer.BeginArray();
|
|
Writer << ShaderPipelineTypeName;
|
|
Writer << StagesSourceHash;
|
|
Writer.EndArray();
|
|
}
|
|
|
|
bool FShaderPipelineTypeDependency::TryLoad(FCbFieldView Field)
|
|
{
|
|
*this = FShaderPipelineTypeDependency();
|
|
FCbFieldViewIterator ElementField(Field.CreateViewIterator());
|
|
if (!LoadFromCompactBinary(ElementField++, ShaderPipelineTypeName))
|
|
{
|
|
return false;
|
|
}
|
|
if (!LoadFromCompactBinary(ElementField++, StagesSourceHash))
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool LoadFromCompactBinary(FCbFieldView Field, FShaderPipelineTypeDependency& OutValue)
|
|
{
|
|
return OutValue.TryLoad(Field);
|
|
}
|
|
#endif // WITH_EDITOR
|
|
|
|
void FShaderPipeline::AddShader(FShader* Shader, int32 PermutationId)
|
|
{
|
|
const EShaderFrequency Frequency = Shader->GetFrequency();
|
|
check(Shaders[Frequency].IsNull());
|
|
Shaders[Frequency] = Shader;
|
|
PermutationIds[Frequency] = PermutationId;
|
|
}
|
|
|
|
FShader* FShaderPipeline::FindOrAddShader(FShader* Shader, int32 PermutationId)
|
|
{
|
|
const EShaderFrequency Frequency = Shader->GetFrequency();
|
|
FShader* PrevShader = Shaders[Frequency];
|
|
if (PrevShader && PermutationIds[Frequency] == PermutationId)
|
|
{
|
|
DeleteObjectFromLayout(Shader);
|
|
return PrevShader;
|
|
}
|
|
|
|
Shaders[Frequency].SafeDelete();
|
|
Shaders[Frequency] = Shader;
|
|
PermutationIds[Frequency] = PermutationId;
|
|
return Shader;
|
|
}
|
|
|
|
FShaderPipeline::~FShaderPipeline()
|
|
{
|
|
// Manually set references to nullptr, helps debugging
|
|
for (uint32 i = 0u; i < SF_NumGraphicsFrequencies; ++i)
|
|
{
|
|
Shaders[i] = nullptr;
|
|
}
|
|
}
|
|
|
|
void FShaderPipeline::Validate(const FShaderPipelineType* InPipelineType) const
|
|
{
|
|
check(InPipelineType->GetHashedName() == TypeName);
|
|
for (const FShaderType* Stage : InPipelineType->GetStages())
|
|
{
|
|
const FShader* Shader = GetShader(Stage->GetFrequency());
|
|
check(Shader);
|
|
check(Shader->GetTypeUnfrozen() == Stage);
|
|
}
|
|
}
|
|
|
|
void FShaderPipeline::Finalize(const FShaderMapResourceCode* Code)
|
|
{
|
|
for (uint32 i = 0u; i < SF_NumGraphicsFrequencies; ++i)
|
|
{
|
|
if (Shaders[i])
|
|
{
|
|
Shaders[i]->Finalize(Code);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
#if WITH_EDITOR
|
|
void FShaderPipeline::SaveShaderStableKeys(const FShaderMapPointerTable& InPtrTable, EShaderPlatform TargetShaderPlatform, const struct FStableShaderKeyAndValue& InSaveKeyVal) const
|
|
{
|
|
// the higher level code can pass SP_NumPlatforms, in which case play it safe and use a platform that we know can remove inteprolators
|
|
const EShaderPlatform ShaderPlatformThatSupportsRemovingInterpolators = SP_PCD3D_SM5;
|
|
checkf(RHISupportsShaderPipelines(ShaderPlatformThatSupportsRemovingInterpolators), TEXT("We assumed that shader platform %d supports shaderpipelines while it doesn't"), static_cast<int32>(ShaderPlatformThatSupportsRemovingInterpolators));
|
|
|
|
FShaderPipelineType** FoundPipelineType = FShaderPipelineType::GetNameToTypeMap().Find(TypeName);
|
|
check(FoundPipelineType);
|
|
FShaderPipelineType* PipelineType = *FoundPipelineType;
|
|
|
|
bool bCanHaveUniqueShaders = (TargetShaderPlatform != SP_NumPlatforms) ? PipelineType->ShouldOptimizeUnusedOutputs(TargetShaderPlatform) : PipelineType->ShouldOptimizeUnusedOutputs(ShaderPlatformThatSupportsRemovingInterpolators);
|
|
if (bCanHaveUniqueShaders)
|
|
{
|
|
FStableShaderKeyAndValue SaveKeyVal(InSaveKeyVal);
|
|
SaveKeyVal.SetPipelineHash(this); // could use PipelineType->GetSourceHash(), but each pipeline instance even of the same type can have unique shaders
|
|
|
|
for (uint32 Frequency = 0u; Frequency < SF_NumGraphicsFrequencies; ++Frequency)
|
|
{
|
|
FShader* Shader = Shaders[Frequency];
|
|
if (Shader)
|
|
{
|
|
Shader->SaveShaderStableKeys(InPtrTable, TargetShaderPlatform, PermutationIds[Frequency], SaveKeyVal);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif // WITH_EDITOR
|
|
|
|
void DumpShaderStats(EShaderPlatform Platform, EShaderFrequency Frequency)
|
|
{
|
|
#if ALLOW_DEBUG_FILES
|
|
FDiagnosticTableViewer ShaderTypeViewer(*FDiagnosticTableViewer::GetUniqueTemporaryFilePath(TEXT("ShaderStats")));
|
|
|
|
// Iterate over all shader types and log stats.
|
|
int32 TotalShaderCount = 0;
|
|
int32 TotalTypeCount = 0;
|
|
int32 TotalInstructionCount = 0;
|
|
int32 TotalSize = 0;
|
|
int32 TotalPipelineCount = 0;
|
|
float TotalSizePerType = 0;
|
|
|
|
// Write a row of headings for the table's columns.
|
|
ShaderTypeViewer.AddColumn(TEXT("Type"));
|
|
ShaderTypeViewer.AddColumn(TEXT("Instances"));
|
|
ShaderTypeViewer.AddColumn(TEXT("Average instructions"));
|
|
ShaderTypeViewer.AddColumn(TEXT("Size"));
|
|
ShaderTypeViewer.AddColumn(TEXT("AvgSizePerInstance"));
|
|
ShaderTypeViewer.AddColumn(TEXT("Pipelines"));
|
|
ShaderTypeViewer.AddColumn(TEXT("Shared Pipelines"));
|
|
ShaderTypeViewer.CycleRow();
|
|
|
|
for( TLinkedList<FShaderType*>::TIterator It(FShaderType::GetTypeList()); It; It.Next() )
|
|
{
|
|
const FShaderType* Type = *It;
|
|
if (Type->GetNumShaders())
|
|
{
|
|
// Calculate the average instruction count and total size of instances of this shader type.
|
|
float AverageNumInstructions = 0.0f;
|
|
int32 NumInitializedInstructions = 0;
|
|
int32 Size = 0;
|
|
int32 NumShaders = 0;
|
|
int32 NumPipelines = 0;
|
|
int32 NumSharedPipelines = 0;
|
|
#if 0
|
|
for (TMap<FShaderId,FShader*>::TConstIterator ShaderIt(Type->ShaderIdMap);ShaderIt;++ShaderIt)
|
|
{
|
|
const FShader* Shader = ShaderIt.Value();
|
|
// Skip shaders that don't match frequency.
|
|
if( Shader->GetTarget().Frequency != Frequency && Frequency != SF_NumFrequencies )
|
|
{
|
|
continue;
|
|
}
|
|
// Skip shaders that don't match platform.
|
|
if( Shader->GetTarget().Platform != Platform && Platform != SP_NumPlatforms )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
NumInitializedInstructions += Shader->GetNumInstructions();
|
|
Size += Shader->GetCode().Num();
|
|
NumShaders++;
|
|
}
|
|
AverageNumInstructions = (float)NumInitializedInstructions / (float)Type->GetNumShaders();
|
|
#endif
|
|
|
|
for (TLinkedList<FShaderPipelineType*>::TConstIterator PipelineIt(FShaderPipelineType::GetTypeList()); PipelineIt; PipelineIt.Next())
|
|
{
|
|
const FShaderPipelineType* PipelineType = *PipelineIt;
|
|
bool bFound = false;
|
|
if (Frequency == SF_NumFrequencies)
|
|
{
|
|
if (PipelineType->GetShader(Type->GetFrequency()) == Type)
|
|
{
|
|
++NumPipelines;
|
|
bFound = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (PipelineType->GetShader(Frequency) == Type)
|
|
{
|
|
++NumPipelines;
|
|
bFound = true;
|
|
}
|
|
}
|
|
|
|
if (!PipelineType->ShouldOptimizeUnusedOutputs(Platform) && bFound)
|
|
{
|
|
++NumSharedPipelines;
|
|
}
|
|
}
|
|
|
|
// Only add rows if there is a matching shader.
|
|
if( NumShaders )
|
|
{
|
|
// Write a row for the shader type.
|
|
ShaderTypeViewer.AddColumn(Type->GetName());
|
|
ShaderTypeViewer.AddColumn(TEXT("%u"),NumShaders);
|
|
ShaderTypeViewer.AddColumn(TEXT("%.1f"),AverageNumInstructions);
|
|
ShaderTypeViewer.AddColumn(TEXT("%u"),Size);
|
|
ShaderTypeViewer.AddColumn(TEXT("%.1f"),Size / (float)NumShaders);
|
|
ShaderTypeViewer.AddColumn(TEXT("%d"), NumPipelines);
|
|
ShaderTypeViewer.AddColumn(TEXT("%d"), NumSharedPipelines);
|
|
ShaderTypeViewer.CycleRow();
|
|
|
|
TotalShaderCount += NumShaders;
|
|
TotalPipelineCount += NumPipelines;
|
|
TotalInstructionCount += NumInitializedInstructions;
|
|
TotalTypeCount++;
|
|
TotalSize += Size;
|
|
TotalSizePerType += Size / (float)NumShaders;
|
|
}
|
|
}
|
|
}
|
|
|
|
// go through non shared pipelines
|
|
|
|
// Write a total row.
|
|
ShaderTypeViewer.AddColumn(TEXT("Total"));
|
|
ShaderTypeViewer.AddColumn(TEXT("%u"),TotalShaderCount);
|
|
ShaderTypeViewer.AddColumn(TEXT("%u"),TotalInstructionCount);
|
|
ShaderTypeViewer.AddColumn(TEXT("%u"),TotalSize);
|
|
ShaderTypeViewer.AddColumn(TEXT("0"));
|
|
ShaderTypeViewer.AddColumn(TEXT("%u"), TotalPipelineCount);
|
|
ShaderTypeViewer.AddColumn(TEXT("-"));
|
|
ShaderTypeViewer.CycleRow();
|
|
|
|
// Write an average row.
|
|
ShaderTypeViewer.AddColumn(TEXT("Average"));
|
|
ShaderTypeViewer.AddColumn(TEXT("%.1f"),TotalTypeCount ? (TotalShaderCount / (float)TotalTypeCount) : 0.0f);
|
|
ShaderTypeViewer.AddColumn(TEXT("%.1f"),TotalShaderCount ? ((float)TotalInstructionCount / TotalShaderCount) : 0.0f);
|
|
ShaderTypeViewer.AddColumn(TEXT("%.1f"),TotalShaderCount ? (TotalSize / (float)TotalShaderCount) : 0.0f);
|
|
ShaderTypeViewer.AddColumn(TEXT("%.1f"),TotalTypeCount ? (TotalSizePerType / TotalTypeCount) : 0.0f);
|
|
ShaderTypeViewer.AddColumn(TEXT("-"));
|
|
ShaderTypeViewer.AddColumn(TEXT("-"));
|
|
ShaderTypeViewer.CycleRow();
|
|
#endif
|
|
}
|
|
|
|
void DumpShaderPipelineStats(EShaderPlatform Platform)
|
|
{
|
|
#if ALLOW_DEBUG_FILES
|
|
FDiagnosticTableViewer ShaderTypeViewer(*FDiagnosticTableViewer::GetUniqueTemporaryFilePath(TEXT("ShaderPipelineStats")));
|
|
|
|
int32 TotalNumPipelines = 0;
|
|
int32 TotalSize = 0;
|
|
float TotalSizePerType = 0;
|
|
|
|
// Write a row of headings for the table's columns.
|
|
ShaderTypeViewer.AddColumn(TEXT("Type"));
|
|
ShaderTypeViewer.AddColumn(TEXT("Shared/Unique"));
|
|
|
|
// Exclude compute
|
|
for (int32 Index = 0; Index < SF_NumFrequencies - 1; ++Index)
|
|
{
|
|
ShaderTypeViewer.AddColumn(GetShaderFrequencyString((EShaderFrequency)Index));
|
|
}
|
|
ShaderTypeViewer.CycleRow();
|
|
|
|
int32 TotalTypeCount = 0;
|
|
for (TLinkedList<FShaderPipelineType*>::TIterator It(FShaderPipelineType::GetTypeList()); It; It.Next())
|
|
{
|
|
const FShaderPipelineType* Type = *It;
|
|
|
|
// Write a row for the shader type.
|
|
ShaderTypeViewer.AddColumn(Type->GetName());
|
|
ShaderTypeViewer.AddColumn(Type->ShouldOptimizeUnusedOutputs(Platform) ? TEXT("U") : TEXT("S"));
|
|
|
|
for (int32 Index = 0; Index < SF_NumFrequencies - 1; ++Index)
|
|
{
|
|
const FShaderType* ShaderType = Type->GetShader((EShaderFrequency)Index);
|
|
ShaderTypeViewer.AddColumn(ShaderType ? ShaderType->GetName() : TEXT(""));
|
|
}
|
|
|
|
ShaderTypeViewer.CycleRow();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
FShaderType* FindShaderTypeByName(const FHashedName& ShaderTypeName)
|
|
{
|
|
FShaderType** FoundShader = FShaderType::GetNameToTypeMap().Find(ShaderTypeName);
|
|
if (FoundShader)
|
|
{
|
|
return *FoundShader;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
void DispatchComputeShader(
|
|
FRHIComputeCommandList& RHICmdList,
|
|
FShader* Shader,
|
|
uint32 ThreadGroupCountX,
|
|
uint32 ThreadGroupCountY,
|
|
uint32 ThreadGroupCountZ)
|
|
{
|
|
RHICmdList.DispatchComputeShader(ThreadGroupCountX, ThreadGroupCountY, ThreadGroupCountZ);
|
|
}
|
|
|
|
void DispatchIndirectComputeShader(
|
|
FRHIComputeCommandList& RHICmdList,
|
|
FShader* Shader,
|
|
FRHIBuffer* ArgumentBuffer,
|
|
uint32 ArgumentOffset)
|
|
{
|
|
RHICmdList.DispatchIndirectComputeShader(ArgumentBuffer, ArgumentOffset);
|
|
}
|
|
|
|
bool IsDxcEnabledForPlatform(EShaderPlatform Platform, bool bHlslVersion2021)
|
|
{
|
|
// Check the generic console variable first (if DXC is supported)
|
|
if (FDataDrivenShaderPlatformInfo::GetSupportsDxc(Platform))
|
|
{
|
|
static FShaderPlatformCachedIniValue<bool> ShaderForceDXC(TEXT("r.Shaders.ForceDXC"));
|
|
if (bHlslVersion2021 || (ShaderForceDXC.Get(Platform) != 0))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
// Check backend specific console variables next
|
|
if (IsD3DPlatform(Platform) && IsPCPlatform(Platform))
|
|
{
|
|
// D3D backend supports a precompile step for HLSL2021 which is separate from ForceDXC option
|
|
static const IConsoleVariable* CVar = IConsoleManager::Get().FindConsoleVariable(TEXT("r.D3D.ForceDXC"));
|
|
return ((CVar && CVar->GetInt() != 0));
|
|
}
|
|
// Hlslcc has been removed for Metal, Vulkan, and OpenGL backends. There is only DXC now.
|
|
if (IsMetalPlatform(Platform) || IsVulkanPlatform(Platform) || IsOpenGLPlatform(Platform))
|
|
{
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool IsUsingEmulatedUniformBuffers(EShaderPlatform Platform)
|
|
{
|
|
if (IsOpenGLPlatform(Platform))
|
|
{
|
|
// DXC only supports emulated uniform buffers on GLES
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void ShaderMapAppendKeyString(EShaderPlatform Platform, FString& KeyString)
|
|
{
|
|
FShaderKeyGenerator KeyGen(KeyString);
|
|
ShaderMapAppendKey(Platform, KeyGen);
|
|
}
|
|
|
|
void ShaderMapAppendKey(EShaderPlatform Platform, FShaderKeyGenerator& KeyGen)
|
|
{
|
|
const FName ShaderFormatName = LegacyShaderPlatformToShaderFormat(Platform);
|
|
|
|
for (const FAutoConsoleObject* ConsoleObject : FAutoConsoleObject::AccessGeneralShaderChangeCvars())
|
|
{
|
|
FString ConsoleObjectName = IConsoleManager::Get().FindConsoleObjectName(ConsoleObject->AsVariable());
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(ConsoleObjectName);
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(ConsoleObject->AsVariable()->GetString());
|
|
}
|
|
if (IsMobilePlatform(Platform))
|
|
{
|
|
for (const FAutoConsoleObject* ConsoleObject : FAutoConsoleObject::AccessMobileShaderChangeCvars())
|
|
{
|
|
FString ConsoleObjectName = IConsoleManager::Get().FindConsoleObjectName(ConsoleObject->AsVariable());
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(ConsoleObjectName);
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(ConsoleObject->AsVariable()->GetString());
|
|
}
|
|
}
|
|
else if (IsConsolePlatform(Platform))
|
|
{
|
|
for (const FAutoConsoleObject* ConsoleObject : FAutoConsoleObject::AccessDesktopShaderChangeCvars())
|
|
{
|
|
FString ConsoleObjectName = IConsoleManager::Get().FindConsoleObjectName(ConsoleObject->AsVariable());
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(ConsoleObjectName);
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(ConsoleObject->AsVariable()->GetString());
|
|
}
|
|
}
|
|
|
|
// Globals that should cause all shaders to recompile when changed must be appended to the key here
|
|
// Key should be kept as short as possible while being somewhat human readable for debugging
|
|
|
|
{
|
|
static const auto CVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("Compat.UseDXT5NormalMaps"));
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append((CVar && CVar->GetValueOnAnyThread() != 0) ? TEXT("DXTN") : TEXT("BC5N"));
|
|
}
|
|
|
|
{
|
|
static const auto CVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.ClearCoatNormal"));
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append((CVar && CVar->GetValueOnAnyThread() != 0) ? TEXT("CCBN") : TEXT("NoCCBN"));
|
|
}
|
|
|
|
{
|
|
static const auto CVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.IrisNormal"));
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append((CVar && CVar->GetValueOnAnyThread() != 0) ? TEXT("Iris") : TEXT("NoIris"));
|
|
}
|
|
|
|
{
|
|
static const auto CVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.CompileShadersForDevelopment"));
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append((CVar && CVar->GetValueOnAnyThread() != 0) ? TEXT("DEV") : TEXT("NoDEV"));
|
|
}
|
|
|
|
{
|
|
const bool bValue = IsStaticLightingAllowed();
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(bValue ? TEXT("SL") : TEXT("NoSL"));
|
|
}
|
|
|
|
{
|
|
static const auto CVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.MaterialEditor.LWCTruncateMode"));
|
|
const int32 LWCTruncateValue = CVar ? CVar->GetValueOnAnyThread() : 0;
|
|
if (LWCTruncateValue == 1)
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("LWC1"));
|
|
}
|
|
else if (LWCTruncateValue == 2)
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("LWC2"));
|
|
}
|
|
}
|
|
|
|
{
|
|
static const auto CVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.VelocityOutputPass"));
|
|
const int32 VelocityOutputPassValue = CVar ? CVar->GetValueOnAnyThread() : 0;
|
|
if (VelocityOutputPassValue == 1)
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("GV"));
|
|
}
|
|
else
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("VOP"));
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(VelocityOutputPassValue);
|
|
}
|
|
}
|
|
|
|
{
|
|
const UE::StereoRenderUtils::FStereoShaderAspects Aspects(Platform);
|
|
|
|
if (Aspects.IsInstancedStereoEnabled())
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("VRIS"));
|
|
|
|
if (Aspects.IsInstancedMultiViewportEnabled())
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("MVIEW"));
|
|
}
|
|
}
|
|
|
|
if (Aspects.IsMobileMultiViewEnabled())
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("MMVIEW"));
|
|
}
|
|
}
|
|
|
|
{
|
|
if (IsUsingSelectiveBasePassOutputs(Platform))
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("SO"));
|
|
}
|
|
}
|
|
|
|
{
|
|
// PreExposure is always used
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("PreExp"));
|
|
}
|
|
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(IsUsingDBuffers(Platform) ? TEXT("DBuf") : TEXT("NoDBuf"));
|
|
}
|
|
|
|
{
|
|
static IConsoleVariable* CVar = IConsoleManager::Get().FindConsoleVariable(TEXT("r.AllowGlobalClipPlane"));
|
|
if (CVar && CVar->GetInt() != 0)
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("ClipP"));
|
|
}
|
|
}
|
|
|
|
{
|
|
// Extra data (names, etc)
|
|
if (ShouldEnableExtraShaderData(ShaderFormatName))
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("ExtraData"));
|
|
}
|
|
// Symbols and/or SymbolsInfo and version if symbols serialization changes
|
|
if (ShouldGenerateShaderSymbols(ShaderFormatName))
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("Symbols"));
|
|
}
|
|
if (ShouldGenerateShaderSymbolsInfo(ShaderFormatName))
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("SymbolsInfo"));
|
|
}
|
|
// Are symbols based on source or results
|
|
if (ShouldAllowUniqueShaderSymbols(ShaderFormatName))
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("FullDbg"));
|
|
}
|
|
}
|
|
|
|
if (!ShouldOptimizeShaders(ShaderFormatName))
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("NoOpt"));
|
|
}
|
|
|
|
{
|
|
// Always default to fast math unless specified
|
|
static const auto CVar = IConsoleManager::Get().FindConsoleVariable(TEXT("r.Shaders.FastMath"));
|
|
if ((CVar && CVar->GetInt() == 0))
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("NoFastMath"));
|
|
}
|
|
}
|
|
|
|
{
|
|
static FShaderPlatformCachedIniValue<int32> CVarWarningsAsErrorsPerPlatform(TEXT("r.Shaders.WarningsAsErrors"));
|
|
if (const int32 Level = CVarWarningsAsErrorsPerPlatform.Get(Platform); Level != 0)
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("WX"));
|
|
KeyGen.Append(Level);
|
|
}
|
|
}
|
|
|
|
{
|
|
static const auto CVar = IConsoleManager::Get().FindConsoleVariable(TEXT("r.Shaders.CheckLevel"));
|
|
// Note: Since 1 is the default, we don't modify the hash for this case, so as to not force a rebuild, and to keep the hash shorter.
|
|
if (CVar && (CVar->GetInt() == 0 || CVar->GetInt() == 2))
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("C"));
|
|
KeyGen.Append(CVar->GetInt());
|
|
}
|
|
}
|
|
|
|
{
|
|
static const auto CVar = IConsoleManager::Get().FindConsoleVariable(TEXT("r.Shaders.FlowControlMode"));
|
|
if (CVar)
|
|
{
|
|
switch(CVar->GetInt())
|
|
{
|
|
case 2:
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("AvoidFlow"));
|
|
break;
|
|
case 1:
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("PreferFlow"));
|
|
break;
|
|
case 0:
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!AllowPixelDepthOffset(Platform))
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("NoPDO"));
|
|
}
|
|
|
|
if (!AllowPerPixelShadingModels(Platform))
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("NoPPSM"));
|
|
}
|
|
|
|
if (UseRemoveUnsedInterpolators(Platform) && !IsOpenGLPlatform(Platform))
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("UnInt"));
|
|
}
|
|
|
|
if (ForwardShadingForcesSkyLightCubemapBlending(Platform))
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("FwdSkyBlnd"));
|
|
}
|
|
|
|
if (IsMobilePlatform(Platform))
|
|
{
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(IsMobileHDR() ? TEXT("HDR") : TEXT("LDR"));
|
|
}
|
|
|
|
{
|
|
static IConsoleVariable* CVar = IConsoleManager::Get().FindConsoleVariable(TEXT("r.Mobile.DisableVertexFog"));
|
|
if (CVar && CVar->GetInt() != 0)
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("NoVFog"));
|
|
}
|
|
}
|
|
|
|
{
|
|
static IConsoleVariable* CVar = IConsoleManager::Get().FindConsoleVariable(TEXT("r.Mobile.FloatPrecisionMode"));
|
|
if (CVar && CVar->GetInt() > 0)
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("highp"));
|
|
KeyGen.Append(CVar->GetInt());
|
|
}
|
|
}
|
|
|
|
{
|
|
static IConsoleVariable* CVar = IConsoleManager::Get().FindConsoleVariable(TEXT("r.Mobile.AllowDitheredLODTransition"));
|
|
if (CVar && CVar->GetInt() != 0)
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("DLODT"));
|
|
}
|
|
}
|
|
|
|
if (IsUsingEmulatedUniformBuffers(Platform))
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("NoUB"));
|
|
}
|
|
|
|
{
|
|
const bool bMobileMovableSpotlightShadowsEnabled = IsMobileMovableSpotlightShadowsEnabled(Platform);
|
|
if (bMobileMovableSpotlightShadowsEnabled)
|
|
{
|
|
KeyGen.Append(TEXT("S"));
|
|
}
|
|
}
|
|
|
|
{
|
|
static IConsoleVariable* CVar = IConsoleManager::Get().FindConsoleVariable(TEXT("r.Mobile.UseHWsRGBEncoding"));
|
|
if (CVar && CVar->GetInt() != 0)
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("HWsRGB"));
|
|
}
|
|
}
|
|
|
|
{
|
|
// make it per shader platform ?
|
|
static IConsoleVariable* CVar = IConsoleManager::Get().FindConsoleVariable(TEXT("r.Mobile.SupportGPUScene"));
|
|
if (CVar && CVar->GetInt() != 0)
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("MobGPUSc"));
|
|
}
|
|
}
|
|
|
|
{
|
|
bool bIsMobileDeferredShading = IsMobileDeferredShadingEnabled(Platform);
|
|
|
|
if (bIsMobileDeferredShading)
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(MobileUsesExtenedGBuffer(Platform) ? TEXT("MobDShEx") : TEXT("MobDSh"));
|
|
}
|
|
else
|
|
{
|
|
static IConsoleVariable* MobileForwardEnableClusteredReflectionsCVAR = IConsoleManager::Get().FindConsoleVariable(TEXT("r.Mobile.Forward.EnableClusteredReflections"));
|
|
if (MobileForwardEnableClusteredReflectionsCVAR && MobileForwardEnableClusteredReflectionsCVAR->GetInt() != 0)
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("MobFCR"));
|
|
}
|
|
}
|
|
}
|
|
|
|
{
|
|
static IConsoleVariable* MobileGTAOPreIntegratedTextureTypeCVar = IConsoleManager::Get().FindConsoleVariable(TEXT("r.Mobile.GTAOPreIntegratedTextureType"));
|
|
static IConsoleVariable* MobileAmbientOcclusionCVar = IConsoleManager::Get().FindConsoleVariable(TEXT("r.Mobile.AmbientOcclusion"));
|
|
int32 GTAOPreIntegratedTextureType = MobileGTAOPreIntegratedTextureTypeCVar ? MobileGTAOPreIntegratedTextureTypeCVar->GetInt() : 0;
|
|
if (MobileAmbientOcclusionCVar && MobileAmbientOcclusionCVar->GetInt() != 0 && IsMobileHDR())
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("MobileAO"));
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(GTAOPreIntegratedTextureType);
|
|
}
|
|
}
|
|
|
|
if (IsMobileDistanceFieldEnabled(Platform))
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("MobSDF"));
|
|
}
|
|
|
|
{
|
|
static FShaderPlatformCachedIniValue<bool> EnableCullBeforeFetchIniValue(TEXT("r.CullBeforeFetch"));
|
|
if (EnableCullBeforeFetchIniValue.Get(Platform) == 1)
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("CBF"));
|
|
}
|
|
static FShaderPlatformCachedIniValue<bool> EnableWarpCullingIniValue(TEXT("r.WarpCulling"));
|
|
if (EnableWarpCullingIniValue.Get(Platform) == 1)
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("WC"));
|
|
}
|
|
}
|
|
|
|
if (MobileUsesFullDepthPrepass(Platform))
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("MobFDP"));
|
|
}
|
|
|
|
if (AreMobileScreenSpaceReflectionsEnabled(Platform))
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("MobSSR"));
|
|
}
|
|
|
|
if (MobileAllowFramebufferFetch(Platform) == false)
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("NoFBF"));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (IsUsingEmulatedUniformBuffers(Platform))
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("NoUB"));
|
|
}
|
|
}
|
|
|
|
if (RenderRectLightsAsSpotLights(GetMaxSupportedFeatureLevel(Platform)))
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("R2S"));
|
|
}
|
|
|
|
uint32 PlatformShadingModelsMask = GetPlatformShadingModelsMask(Platform);
|
|
if (PlatformShadingModelsMask != 0xFFFFFFFF)
|
|
{
|
|
KeyGen.Append(TEXT("SMM"));
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.AppendHex(PlatformShadingModelsMask);
|
|
}
|
|
|
|
const IShaderFormat* ShaderFormat = GetTargetPlatformManagerRef().FindShaderFormat(ShaderFormatName);
|
|
if (ShaderFormat)
|
|
{
|
|
FString ShaderFormatExtraData;
|
|
ShaderFormat->AppendToKeyString(ShaderFormatExtraData);
|
|
KeyGen.Append(ShaderFormatExtraData);
|
|
}
|
|
|
|
ITargetPlatform* TargetPlatform = GetTargetPlatformManagerRef().FindTargetPlatformWithSupport(TEXT("ShaderFormat"), ShaderFormatName);
|
|
|
|
uint32 SupportedHardwareMask = TargetPlatform ? TargetPlatform->GetSupportedHardwareMask() : 0;
|
|
|
|
if (SupportedHardwareMask != 0)
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("SHM"));
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.AppendHex(SupportedHardwareMask);
|
|
}
|
|
|
|
// Encode the Metal standard into the shader compile options so that they recompile if the settings change.
|
|
if (IsMetalPlatform(Platform))
|
|
{
|
|
{
|
|
static const auto CVar = IConsoleManager::Get().FindConsoleVariable(TEXT("r.Shaders.ZeroInitialise"));
|
|
if (CVar && CVar->GetInt() != 0)
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("ZeroInit"));
|
|
}
|
|
}
|
|
{
|
|
static const auto CVar = IConsoleManager::Get().FindConsoleVariable(TEXT("r.Shaders.BoundsChecking"));
|
|
if (CVar && CVar->GetInt() != 0)
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("BoundsChecking"));
|
|
}
|
|
}
|
|
{
|
|
if (RHISupportsManualVertexFetch(Platform))
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("MVF"));
|
|
KeyGen.AppendSeparator();
|
|
}
|
|
}
|
|
|
|
uint32 ShaderVersion = RHIGetMetalShaderLanguageVersion(Platform);
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("MTLSTD"));
|
|
KeyGen.Append(ShaderVersion);
|
|
KeyGen.AppendSeparator();
|
|
|
|
bool bAllowFastIntrinsics = false;
|
|
bool bEnableMathOptimisations = true;
|
|
bool bForceFloats = false;
|
|
bool bSupportAppleA8 = false;
|
|
int32 IndirectArgumentTier = 0;
|
|
bool bMetalOptimizeBySize = false;
|
|
|
|
if (IsPCPlatform(Platform))
|
|
{
|
|
GConfig->GetBool(TEXT("/Script/MacTargetPlatform.MacTargetSettings"), TEXT("UseFastIntrinsics"), bAllowFastIntrinsics, GEngineIni);
|
|
GConfig->GetBool(TEXT("/Script/MacTargetPlatform.MacTargetSettings"), TEXT("EnableMathOptimisations"), bEnableMathOptimisations, GEngineIni);
|
|
GConfig->GetInt(TEXT("/Script/MacTargetPlatform.MacTargetSettings"), TEXT("IndirectArgumentTier"), IndirectArgumentTier, GEngineIni);
|
|
GConfig->GetBool(TEXT("/Script/MacTargetPlatform.MacTargetSettings"), TEXT("MetalOptimizeBySize"), bMetalOptimizeBySize, GEngineIni);
|
|
}
|
|
else
|
|
{
|
|
GConfig->GetBool(TEXT("/Script/IOSRuntimeSettings.IOSRuntimeSettings"), TEXT("UseFastIntrinsics"), bAllowFastIntrinsics, GEngineIni);
|
|
GConfig->GetBool(TEXT("/Script/IOSRuntimeSettings.IOSRuntimeSettings"), TEXT("EnableMathOptimisations"), bEnableMathOptimisations, GEngineIni);
|
|
GConfig->GetBool(TEXT("/Script/IOSRuntimeSettings.IOSRuntimeSettings"), TEXT("ForceFloats"), bForceFloats, GEngineIni);
|
|
GConfig->GetInt(TEXT("/Script/IOSRuntimeSettings.IOSRuntimeSettings"), TEXT("IndirectArgumentTier"), IndirectArgumentTier, GEngineIni);
|
|
GConfig->GetBool(TEXT("/Script/IOSRuntimeSettings.IOSRuntimeSettings"), TEXT("bSupportAppleA8"), bSupportAppleA8, GEngineIni);
|
|
GConfig->GetBool(TEXT("/Script/IOSRuntimeSettings.IOSRuntimeSettings"), TEXT("MetalOptimizeBySize"), bMetalOptimizeBySize, GEngineIni);
|
|
}
|
|
|
|
if (bAllowFastIntrinsics)
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("MTLSL_FastIntrin"));
|
|
}
|
|
|
|
// Same as console-variable above, but that's global and this is per-platform, per-project
|
|
if (!bEnableMathOptimisations)
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("NoFastMath"));
|
|
}
|
|
|
|
if (bForceFloats)
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("FP32"));
|
|
}
|
|
|
|
if(bSupportAppleA8)
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("A8GPU"));
|
|
}
|
|
|
|
if(bMetalOptimizeBySize)
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("Os"));
|
|
}
|
|
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("IAB"));
|
|
KeyGen.Append(IndirectArgumentTier);
|
|
|
|
// Shaders built for archiving - for Metal that requires compiling the code in a different way so that we can strip it later
|
|
bool bArchive = false;
|
|
GConfig->GetBool(TEXT("/Script/UnrealEd.ProjectPackagingSettings"), TEXT("bSharedMaterialNativeLibraries"), bArchive, GGameIni);
|
|
if (bArchive)
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("ARCHIVE"));
|
|
}
|
|
}
|
|
|
|
if (Platform == SP_VULKAN_ES3_1_ANDROID || Platform == SP_VULKAN_SM5_ANDROID)
|
|
{
|
|
bool bStripReflect = true;
|
|
GConfig->GetBool(TEXT("/Script/AndroidRuntimeSettings.AndroidRuntimeSettings"), TEXT("bStripShaderReflection"), bStripReflect, GEngineIni);
|
|
if (!bStripReflect)
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("NoStripReflect"));
|
|
}
|
|
}
|
|
|
|
// Is DXC shader compiler enabled for this platform?
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(IsDxcEnabledForPlatform(Platform) ? TEXT("DXC1") : TEXT("DXC0"));
|
|
|
|
if (IsFeatureLevelSupported(Platform, ERHIFeatureLevel::SM5))
|
|
{
|
|
static const auto CVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.StencilForLODDither"));
|
|
if (CVar && CVar->GetValueOnAnyThread() > 0)
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("SD"));
|
|
}
|
|
}
|
|
|
|
{
|
|
bool bForwardShading = false;
|
|
if (TargetPlatform)
|
|
{
|
|
// if there is a specific target platform that matches our shader platform, use that to drive forward shading
|
|
bForwardShading = TargetPlatform->UsesForwardShading();
|
|
}
|
|
else
|
|
{
|
|
// shader platform doesn't match a specific target platform, use cvar setting for forward shading
|
|
static IConsoleVariable* CVarForwardShadingLocal = IConsoleManager::Get().FindConsoleVariable(TEXT("r.ForwardShading"));
|
|
bForwardShading = CVarForwardShadingLocal ? (CVarForwardShadingLocal->GetInt() != 0) : false;
|
|
}
|
|
|
|
if (bForwardShading)
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("FS"));
|
|
}
|
|
}
|
|
|
|
{
|
|
static const auto CVar = IConsoleManager::Get().FindConsoleVariable(TEXT("r.Deferred.SupportPrimitiveAlphaHoldout"));
|
|
const bool bDeferredSupportPrimitiveAlphaHoldout = CVar ? CVar->GetBool() : false;
|
|
|
|
if (bDeferredSupportPrimitiveAlphaHoldout)
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("PAH"));
|
|
}
|
|
}
|
|
|
|
if (TargetPlatform &&
|
|
TargetPlatform->SupportsFeature(ETargetPlatformFeatures::NormalmapLAEncodingMode))
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("NLA"));
|
|
}
|
|
|
|
{
|
|
static const auto CVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.VertexFoggingForOpaque"));
|
|
bool bVertexFoggingForOpaque = CVar && CVar->GetValueOnAnyThread() > 0;
|
|
if (TargetPlatform)
|
|
{
|
|
const int32 PlatformHeightFogMode = TargetPlatform->GetHeightFogModeForOpaque();
|
|
if (PlatformHeightFogMode == 1)
|
|
{
|
|
bVertexFoggingForOpaque = false;
|
|
}
|
|
else if (PlatformHeightFogMode == 2)
|
|
{
|
|
bVertexFoggingForOpaque = true;
|
|
}
|
|
}
|
|
if (bVertexFoggingForOpaque)
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("VFO"));
|
|
}
|
|
}
|
|
|
|
bool bSupportLocalFogVolumes = false;
|
|
{
|
|
static IConsoleVariable* CVar = IConsoleManager::Get().FindConsoleVariable(TEXT("r.SupportLocalFogVolumes"));
|
|
bSupportLocalFogVolumes = CVar && CVar->GetInt() > 0;
|
|
if (bSupportLocalFogVolumes)
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("LFV"));
|
|
}
|
|
}
|
|
|
|
{
|
|
if (DoesProjectSupportLumenRayTracedTranslucentRefraction())
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("LTRRT"));
|
|
}
|
|
}
|
|
|
|
{
|
|
static IConsoleVariable* CVar = IConsoleManager::Get().FindConsoleVariable(TEXT("r.LocalFogVolume.ApplyOnTranslucent"));
|
|
const bool bLocalFogVolumesApplyOnTranclucent = CVar && CVar->GetInt() > 0;
|
|
if (bSupportLocalFogVolumes && bLocalFogVolumesApplyOnTranclucent)
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("LFVTRA"));
|
|
}
|
|
}
|
|
|
|
{
|
|
static const auto CVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.SupportSkyAtmosphere"));
|
|
if (CVar && CVar->GetValueOnAnyThread() > 0)
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("SKYATM"));
|
|
|
|
static const auto CVarHeightFog = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.SupportSkyAtmosphereAffectsHeightFog"));
|
|
if (CVarHeightFog && CVarHeightFog->GetValueOnAnyThread() > 0)
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("SKYHF"));
|
|
}
|
|
}
|
|
}
|
|
|
|
{
|
|
static const auto CVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.VolumetricCloud.Support"));
|
|
if (CVar && CVar->GetValueOnAnyThread() > 0)
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("VCLOUD"));
|
|
}
|
|
}
|
|
|
|
{
|
|
if (DoesProjectSupportExpFogMatchesVolumetricFog())
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("EXPVFOG"));
|
|
}
|
|
}
|
|
|
|
const bool bNeedsSeparateMainDirLightTexture = IsWaterSeparateMainDirLightEnabled(Platform);
|
|
if (bNeedsSeparateMainDirLightTexture)
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("SLWSMDLT"));
|
|
}
|
|
|
|
{
|
|
static const auto CVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.SupportCloudShadowOnForwardLitTranslucent"));
|
|
if (CVar && CVar->GetValueOnAnyThread() > 0)
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("CLDTRANS"));
|
|
}
|
|
}
|
|
|
|
{
|
|
const bool bTranslucentUsesLightRectLights = GetTranslucentUsesLightRectLights();
|
|
if (bTranslucentUsesLightRectLights)
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("RECTTRANS"));
|
|
}
|
|
}
|
|
|
|
{
|
|
const bool bTranslucentUsesShadowedLocalLights = GetTranslucentUsesShadowedLocalLights();
|
|
if (bTranslucentUsesShadowedLocalLights)
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("SHALOLIT"));
|
|
}
|
|
}
|
|
|
|
{
|
|
const bool bTranslucentUsesLightIESProfiles = GetTranslucentUsesLightIESProfiles();
|
|
if (bTranslucentUsesLightIESProfiles)
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("IESTRANS"));
|
|
}
|
|
}
|
|
|
|
{
|
|
static const auto CVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.Shadow.Virtual.TranslucentQuality"));
|
|
if (CVar && CVar->GetValueOnAnyThread() > 0)
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("VSMTRANSQUALITY"));
|
|
}
|
|
}
|
|
|
|
if (GetHairStrandsUsesTriangleStrips())
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("STRDSTRIP"));
|
|
}
|
|
|
|
if (Substrate::IsSubstrateEnabled())
|
|
{
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("SUBSTRATE"));
|
|
}
|
|
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("SUBGBFMT"));
|
|
KeyGen.Append(Substrate::IsSubstrateBlendableGBufferEnabled(Platform));
|
|
}
|
|
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("BUDGET"));
|
|
KeyGen.Append(Substrate::GetBytePerPixel(Platform));
|
|
}
|
|
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("CLOSURE"));
|
|
KeyGen.Append(Substrate::GetClosurePerPixel(Platform));
|
|
}
|
|
|
|
if (Substrate::IsDBufferPassEnabled(Platform))
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("DBUFFERPASS"));
|
|
}
|
|
|
|
if (Substrate::IsHiddenMaterialAssetConversionEnabled())
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("HIDDENCONV"));
|
|
}
|
|
|
|
if (Substrate::IsBackCompatibilityEnabled())
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("BACKCOMPAT"));
|
|
}
|
|
|
|
if (Substrate::IsOpaqueRoughRefractionEnabled(Platform))
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("ROUGHDIFF"));
|
|
}
|
|
|
|
if (Substrate::GetNormalQuality() > 0)
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("STRTNRMQ"));
|
|
}
|
|
|
|
if (Substrate::IsAdvancedVisualizationEnabled())
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("ADVDEBUG"));
|
|
}
|
|
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("STSHQL"));
|
|
KeyGen.Append(Substrate::GetShadingQuality(Platform));
|
|
}
|
|
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("SSHEEN"));
|
|
KeyGen.Append(Substrate::GetSheenQuality(Platform));
|
|
}
|
|
|
|
if (Substrate::IsGlintEnabled(Platform))
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("STRTGLT"));
|
|
}
|
|
|
|
if (Substrate::IsSpecularProfileEnabled(Platform))
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("STRTSP"));
|
|
}
|
|
}
|
|
|
|
{
|
|
static const auto CVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.Material.RoughDiffuse"));
|
|
if (CVar && CVar->GetValueOnAnyThread() > 0)
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("MATRDIFF"));
|
|
}
|
|
}
|
|
|
|
{
|
|
int32 LightFunctionAtlasFormat = GetLightFunctionAtlasFormat();
|
|
if (LightFunctionAtlasFormat > 0)
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("LFAC"));
|
|
KeyGen.Append(static_cast<uint32>(LightFunctionAtlasFormat));
|
|
}
|
|
|
|
bool bSingleLayerWaterUsesLightFunctionAtlas = GetSingleLayerWaterUsesLightFunctionAtlas();
|
|
if (bSingleLayerWaterUsesLightFunctionAtlas)
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("SLWLFA"));
|
|
}
|
|
|
|
bool bTranslucentUsesLightFunctionAtlas = GetTranslucentUsesLightFunctionAtlas();
|
|
if (bTranslucentUsesLightFunctionAtlas)
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("FWDLFA"));
|
|
}
|
|
|
|
}
|
|
|
|
{
|
|
static const auto CVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.Material.EnergyConservation"));
|
|
if (CVar && CVar->GetValueOnAnyThread() > 0)
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("MATENERGY"));
|
|
}
|
|
}
|
|
|
|
{
|
|
if (MaskedInEarlyPass(Platform))
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("EZPMM"));
|
|
}
|
|
}
|
|
|
|
{
|
|
static const auto CVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.GPUSkin.Limit2BoneInfluences"));
|
|
if (CVar && CVar->GetValueOnAnyThread() != 0)
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("2bi"));
|
|
}
|
|
}
|
|
{
|
|
if(UseGPUScene(Platform, GetMaxSupportedFeatureLevel(Platform)))
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("gs1"));
|
|
}
|
|
else
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("gs0"));
|
|
}
|
|
}
|
|
|
|
if (FDataDrivenShaderPlatformInfo::GetSupportSceneDataCompressedTransforms(Platform))
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("sdct"));
|
|
}
|
|
|
|
{
|
|
static const auto CVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.GBufferDiffuseSampleOcclusion"));
|
|
if (CVar && CVar->GetValueOnAnyThread() != 0)
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("GDSO"));
|
|
}
|
|
}
|
|
|
|
{
|
|
static const auto CVarVirtualTextureLightmaps = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.VirtualTexturedLightmaps"));
|
|
const bool VTLightmaps = CVarVirtualTextureLightmaps && CVarVirtualTextureLightmaps->GetValueOnAnyThread() != 0;
|
|
|
|
static const auto CVarVirtualTexture = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.VirtualTextures"));
|
|
bool VTTextures = CVarVirtualTexture && CVarVirtualTexture->GetValueOnAnyThread() != 0;
|
|
|
|
static const auto CVarVTAnisotropic = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.VT.AnisotropicFiltering"));
|
|
int32 VTFiltering = CVarVTAnisotropic && CVarVTAnisotropic->GetValueOnAnyThread() != 0 ? 1 : 0;
|
|
|
|
if (IsMobilePlatform(Platform) && VTTextures)
|
|
{
|
|
static FShaderPlatformCachedIniValue<bool> MobileVirtualTexturesIniValue(TEXT("r.Mobile.VirtualTextures"));
|
|
VTTextures = (MobileVirtualTexturesIniValue.Get(Platform) != false);
|
|
|
|
if (VTTextures)
|
|
{
|
|
static FShaderPlatformCachedIniValue<bool> CVarVTMobileManualTrilinearFiltering(TEXT("r.VT.Mobile.ManualTrilinearFiltering"));
|
|
VTFiltering += (CVarVTMobileManualTrilinearFiltering.Get(Platform) ? 2 : 0);
|
|
}
|
|
}
|
|
|
|
const bool VTSupported = UseVirtualTexturing(Platform, TargetPlatform);
|
|
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("VT"));
|
|
KeyGen.AppendDebugText(TEXT("-"));
|
|
KeyGen.Append(VTLightmaps);
|
|
KeyGen.AppendDebugText(TEXT("-"));
|
|
KeyGen.Append(VTTextures);
|
|
KeyGen.AppendDebugText(TEXT("-"));
|
|
KeyGen.Append(VTSupported);
|
|
KeyGen.AppendDebugText(TEXT("-"));
|
|
KeyGen.Append(VTFiltering);
|
|
}
|
|
|
|
{
|
|
const UE::Color::FColorSpace& WCS = UE::Color::FColorSpace::GetWorking();
|
|
if (!WCS.IsSRGB())
|
|
{
|
|
// The working color space is uniquely defined by its chromaticities (as loaded from renderer settings).
|
|
uint32 WCSHash = 0;
|
|
WCSHash ^= GetTypeHash(WCS.GetRedChromaticity());
|
|
WCSHash ^= GetTypeHash(WCS.GetGreenChromaticity());
|
|
WCSHash ^= GetTypeHash(WCS.GetBlueChromaticity());
|
|
WCSHash ^= GetTypeHash(WCS.GetWhiteChromaticity());
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("WCS"));
|
|
KeyGen.AppendDebugText(TEXT("-"));
|
|
KeyGen.Append(WCSHash);
|
|
}
|
|
|
|
static const auto CVar = IConsoleManager::Get().FindConsoleVariable(TEXT("r.LegacyLuminanceFactors"));
|
|
if (CVar && CVar->GetInt() != 0)
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("LLF"));
|
|
}
|
|
}
|
|
|
|
{
|
|
static const auto CVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.Shaders.RemoveDeadCode"));
|
|
if (CVar && CVar->GetValueOnAnyThread() != 0)
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("MIN"));
|
|
}
|
|
}
|
|
|
|
{
|
|
static const auto CVar = IConsoleManager::Get().FindTConsoleVariableDataBool(TEXT("r.ShaderCompiler.PreprocessedJobCache"));
|
|
if (CVar && CVar->GetValueOnAnyThread())
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("PJC"));
|
|
}
|
|
}
|
|
|
|
if (RHISupportsShaderRootConstants(Platform))
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("SHRC"));
|
|
}
|
|
|
|
if (RHISupportsShaderBundleDispatch(Platform))
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("SHBD"));
|
|
}
|
|
|
|
if (RHISupportsRenderTargetWriteMask(Platform))
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("RTWM"));
|
|
}
|
|
|
|
if (FDataDrivenShaderPlatformInfo::GetSupportsPerPixelDBufferMask(Platform))
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("PPDBM"));
|
|
}
|
|
|
|
if (FDataDrivenShaderPlatformInfo::GetSupportsDistanceFields(Platform))
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("DF"));
|
|
}
|
|
|
|
if (RHISupportsMeshShadersTier0(Platform))
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("MS_T0"));
|
|
}
|
|
|
|
if (RHISupportsMeshShadersTier1(Platform))
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("MS_T1"));
|
|
}
|
|
|
|
if (FDataDrivenShaderPlatformInfo::GetSupportsBindless(Platform))
|
|
{
|
|
const ERHIBindlessConfiguration BindlessConfig = UE::ShaderCompiler::GetBindlessConfiguration(Platform);
|
|
if (BindlessConfig != ERHIBindlessConfiguration::Disabled)
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append("BNDLS");
|
|
|
|
switch (BindlessConfig)
|
|
{
|
|
case ERHIBindlessConfiguration::RayTracing:
|
|
KeyGen.Append("RT");
|
|
break;
|
|
case ERHIBindlessConfiguration::Minimal:
|
|
KeyGen.Append("MIN");
|
|
break;
|
|
case ERHIBindlessConfiguration::All:
|
|
KeyGen.Append("ALL");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
const ERHIStaticShaderBindingLayoutSupport StaticShaderBindingLayoutSupport = FDataDrivenShaderPlatformInfo::GetStaticShaderBindingLayoutSupport(Platform);
|
|
if (StaticShaderBindingLayoutSupport != ERHIStaticShaderBindingLayoutSupport::Unsupported)
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(StaticShaderBindingLayoutSupport == ERHIStaticShaderBindingLayoutSupport::RayTracingOnly ?
|
|
TEXT("SSBL-RT") : TEXT("SSBL"));
|
|
}
|
|
|
|
if (ShouldCompileRayTracingShadersForProject(Platform))
|
|
{
|
|
static FShaderPlatformCachedIniValue<int32> CVarCompileRayTracingMaterialCHS(TEXT("r.RayTracing.CompileMaterialCHS"));
|
|
static FShaderPlatformCachedIniValue<int32> CVarCompileRayTracingMaterialAHS(TEXT("r.RayTracing.CompileMaterialAHS"));
|
|
|
|
static const auto CVarTextureLod = IConsoleManager::Get().FindConsoleVariable(TEXT("r.RayTracing.UseTextureLod"));
|
|
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("RAY"));
|
|
KeyGen.AppendDebugText(TEXT("-CHS"));
|
|
KeyGen.AppendBoolInt(CVarCompileRayTracingMaterialCHS.Get(Platform) != 0);
|
|
KeyGen.AppendDebugText(TEXT("AHS"));
|
|
KeyGen.AppendBoolInt(CVarCompileRayTracingMaterialAHS.Get(Platform) != 0);
|
|
KeyGen.AppendDebugText(TEXT("LOD"));
|
|
KeyGen.AppendBoolInt(CVarTextureLod && CVarTextureLod->GetBool());
|
|
}
|
|
|
|
if (DoesPlatformSupportHeterogeneousVolumes(Platform))
|
|
{
|
|
static const auto ShadowCVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.HeterogeneousVolumes.Shadows"));
|
|
if (ShadowCVar && ShadowCVar->GetValueOnAnyThread() != 0)
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("HVSHADOW"));
|
|
}
|
|
|
|
static const auto CompTranslucencyCVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.Translucency.HeterogeneousVolumes"));
|
|
if (CompTranslucencyCVar && CompTranslucencyCVar->GetValueOnAnyThread() != 0)
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("HVCOMPTRANSL"));
|
|
}
|
|
}
|
|
|
|
if (ForceSimpleSkyDiffuse(Platform))
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("SSD"));
|
|
}
|
|
|
|
if (VelocityEncodeDepth(Platform))
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("VED"));
|
|
}
|
|
|
|
{
|
|
const bool bSupportsAnisotropicMaterials = FDataDrivenShaderPlatformInfo::GetSupportsAnisotropicMaterials(Platform);
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("Aniso"));
|
|
KeyGen.AppendDebugText(TEXT("-"));
|
|
KeyGen.AppendBoolInt(bSupportsAnisotropicMaterials);
|
|
}
|
|
|
|
{
|
|
// add shader compression format
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("Compr"));
|
|
FName CompressionFormat = GetShaderCompressionFormat();
|
|
KeyGen.Append(CompressionFormat);
|
|
if (CompressionFormat == NAME_Oodle)
|
|
{
|
|
FOodleDataCompression::ECompressor OodleCompressor;
|
|
FOodleDataCompression::ECompressionLevel OodleLevel;
|
|
GetShaderCompressionOodleSettings(OodleCompressor, OodleLevel);
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("Compr"));
|
|
KeyGen.Append(static_cast<int32>(OodleCompressor));
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.AppendDebugText(TEXT("Lev"));
|
|
KeyGen.Append(static_cast<int32>(OodleLevel));
|
|
}
|
|
}
|
|
|
|
{
|
|
// add whether or not non-pipelined shader types are included
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("ExclNonPipSh-"));
|
|
KeyGen.AppendBoolInt(ExcludeNonPipelinedShaderTypes(Platform));
|
|
}
|
|
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("LWC-"));
|
|
KeyGen.Append(FMath::FloorToInt(FLargeWorldRenderScalar::GetTileSize()));
|
|
|
|
uint64 ShaderPlatformPropertiesHash = FDataDrivenShaderPlatformInfo::GetShaderPlatformPropertiesHash(Platform);
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(ShaderPlatformPropertiesHash);
|
|
|
|
if (IsSingleLayerWaterDepthPrepassEnabled(Platform, GetMaxSupportedFeatureLevel(Platform)))
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("SLWDP"));
|
|
}
|
|
|
|
if (IsGPUSkinPassThroughSupported(Platform))
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("SKPassThrough1"));
|
|
}
|
|
else
|
|
{
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("SKPassThrough0"));
|
|
}
|
|
|
|
if (DoesRuntimeSupportNanite(Platform, false, true))
|
|
{
|
|
static const auto CVarAllowSpline = IConsoleManager::Get().FindConsoleVariable(TEXT("r.Nanite.AllowSplineMeshes"));
|
|
static const auto CVarAllowSkinned = IConsoleManager::Get().FindConsoleVariable(TEXT("r.Nanite.AllowSkinnedMeshes"));
|
|
|
|
KeyGen.AppendSeparator();
|
|
KeyGen.Append(TEXT("Nanite-"));
|
|
KeyGen.AppendDebugText(TEXT("Spline"));
|
|
KeyGen.Append(CVarAllowSpline ? CVarAllowSpline->GetInt() : 0);
|
|
KeyGen.AppendDebugText(TEXT("Skinned"));
|
|
KeyGen.Append(CVarAllowSkinned ? CVarAllowSkinned->GetInt() : 0);
|
|
}
|
|
}
|
|
|
|
static EShaderPermutationFlags GAdditionalShaderPermutationFlags = EShaderPermutationFlags::None;
|
|
|
|
void SetAdditionalShaderPermutationFlags(EShaderPermutationFlags AdditionalFlags)
|
|
{
|
|
check(GAdditionalShaderPermutationFlags == EShaderPermutationFlags::None);
|
|
GAdditionalShaderPermutationFlags = AdditionalFlags;
|
|
}
|
|
|
|
EShaderPermutationFlags GetShaderPermutationFlags(const FPlatformTypeLayoutParameters& LayoutParams)
|
|
{
|
|
EShaderPermutationFlags Result = GAdditionalShaderPermutationFlags;
|
|
if (LayoutParams.WithEditorOnly())
|
|
{
|
|
Result |= EShaderPermutationFlags::HasEditorOnlyData;
|
|
}
|
|
return Result;
|
|
}
|
|
|
|
void RegisterRayTracingPayloadType(ERayTracingPayloadType PayloadType, uint32 PayloadSize, TRaytracingPayloadSizeFunction PayloadSizeFunction)
|
|
{
|
|
// Make sure we haven't registered this payload type yet
|
|
uint32 PayloadTypeInt = static_cast<uint32>(PayloadType);
|
|
checkf(FMath::CountBits(PayloadTypeInt) == 1, TEXT("PayloadType should have only 1 bit set -- got %u"), PayloadTypeInt);
|
|
checkf(!IsRayTracingPayloadRegistered(PayloadType), TEXT("Payload type %u has already been registered"), PayloadTypeInt);
|
|
int32 PayloadIndex = FPlatformMath::CountTrailingZeros(PayloadTypeInt);
|
|
RayTracingPayloadSizeFunctions[PayloadIndex] = PayloadSizeFunction;
|
|
RayTracingPayloadSizes[PayloadIndex] = PayloadSizeFunction ? 0u : PayloadSize;
|
|
RegisteredRayTracingPayloads |= PayloadTypeInt;
|
|
}
|
|
|
|
uint32 GetRayTracingPayloadTypeMaxSize(ERayTracingPayloadType PayloadType)
|
|
{
|
|
// Compute the largest payload size among all set bits
|
|
uint32 Result = 0;
|
|
checkf(IsRayTracingPayloadRegistered(PayloadType), TEXT("Payload type %u has not been registered"), PayloadType);
|
|
for (uint32 PayloadTypeInt = static_cast<uint32>(PayloadType); PayloadTypeInt;)
|
|
{
|
|
const int32 PayloadIndex = FPlatformMath::CountTrailingZeros(PayloadTypeInt);
|
|
if (RayTracingPayloadSizeFunctions[PayloadIndex] != nullptr)
|
|
{
|
|
Result = FMath::Max(Result, RayTracingPayloadSizeFunctions[PayloadIndex]());
|
|
}
|
|
else
|
|
{
|
|
Result = FMath::Max(Result, RayTracingPayloadSizes[PayloadIndex]);
|
|
}
|
|
|
|
// remove bit we just processed
|
|
PayloadTypeInt &= ~(1u << PayloadIndex);
|
|
}
|
|
return Result;
|
|
}
|