You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
For Forward ES31 Default SceneColor RG11B10 + R16F\32F Depth texture With PropagateAlpha on RGBA16F + R16F\32F PostProcess we sample SceneDepthAux for Depth For Deferred ES31 SceneDepthAux only for Metal PropagateAlpha not working yet PostProcess we sample SceneDepthTexture for Depth cvar to change Depth texture from 16 to 32Fr.Mobile.SceneDepthAux cvar for AlphaPropagate r.Mobile.PropagateAlpha #jira UE-98033 #rb Dmitriy.Dyomin, Carl.Lloyd, Jack.Porter [CL 16644095 by Florin Pascu in ue5-main branch]
1854 lines
63 KiB
C++
1854 lines
63 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 "Stats/StatsMisc.h"
|
|
#include "Serialization/MemoryWriter.h"
|
|
#include "VertexFactory.h"
|
|
#include "ProfilingDebugging/DiagnosticTable.h"
|
|
#include "Interfaces/ITargetPlatform.h"
|
|
#include "Interfaces/ITargetPlatformManagerModule.h"
|
|
#include "Interfaces/IShaderFormat.h"
|
|
#include "ShaderCodeLibrary.h"
|
|
#include "ShaderCore.h"
|
|
#include "ShaderCompilerCore.h"
|
|
#include "RenderUtils.h"
|
|
#include "Misc/ConfigCacheIni.h"
|
|
#include "Misc/ScopeLock.h"
|
|
#include "UObject/RenderingObjectVersion.h"
|
|
#include "UObject/FortniteMainBranchObjectVersion.h"
|
|
#include "Misc/ScopeRWLock.h"
|
|
#include "ProfilingDebugging/LoadTimeTracker.h"
|
|
|
|
#if WITH_EDITORONLY_DATA
|
|
#include "Interfaces/IShaderFormat.h"
|
|
#endif
|
|
|
|
DEFINE_LOG_CATEGORY(LogShaders);
|
|
DECLARE_LOG_CATEGORY_CLASS(LogShaderWarnings, Log, Log);
|
|
|
|
IMPLEMENT_TYPE_LAYOUT(FShader);
|
|
IMPLEMENT_TYPE_LAYOUT(FShaderParameterBindings);
|
|
IMPLEMENT_TYPE_LAYOUT(FShaderMapContent);
|
|
IMPLEMENT_TYPE_LAYOUT(FShaderTypeDependency);
|
|
IMPLEMENT_TYPE_LAYOUT(FShaderPipeline);
|
|
IMPLEMENT_TYPE_LAYOUT(FShaderParameterInfo);
|
|
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> 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> CVarShaderCompilerEmitWarningsOnLoad(
|
|
TEXT("r.ShaderCompiler.EmitWarningsOnLoad"),
|
|
0,
|
|
TEXT("When 1, shader compiler warnings are emitted to the log for all shaders as they are loaded."),
|
|
ECVF_Default
|
|
);
|
|
|
|
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));
|
|
}
|
|
}
|
|
|
|
bool FShaderType::bInitializedSerializationHistory = false;
|
|
|
|
static TArray<FShaderType*>& GetSortedShaderTypes(FShaderType::EShaderTypeForDynamicCast Type)
|
|
{
|
|
static TArray<FShaderType*> SortedTypes[(uint32)FShaderType::EShaderTypeForDynamicCast::NumShaderTypes];
|
|
return SortedTypes[(uint32)Type];
|
|
}
|
|
|
|
FShaderType::FShaderType(
|
|
EShaderTypeForDynamicCast InShaderTypeForDynamicCast,
|
|
FTypeLayoutDesc& InTypeLayout,
|
|
const TCHAR* InName,
|
|
const TCHAR* InSourceFilename,
|
|
const TCHAR* InFunctionName,
|
|
uint32 InFrequency,
|
|
int32 InTotalPermutationCount,
|
|
ConstructSerializedType InConstructSerializedRef,
|
|
ConstructCompiledType InConstructCompiledRef,
|
|
ModifyCompilationEnvironmentType InModifyCompilationEnvironmentRef,
|
|
ShouldCompilePermutationType InShouldCompilePermutationRef,
|
|
ValidateCompiledResultType InValidateCompiledResultRef,
|
|
uint32 InTypeSize,
|
|
const FShaderParametersMetadata* InRootParametersMetadata
|
|
):
|
|
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),
|
|
ModifyCompilationEnvironmentRef(InModifyCompilationEnvironmentRef),
|
|
ShouldCompilePermutationRef(InShouldCompilePermutationRef),
|
|
ValidateCompiledResultRef(InValidateCompiledResultRef),
|
|
RootParametersMetadata(InRootParametersMetadata),
|
|
GlobalListLink(this)
|
|
{
|
|
FTypeLayoutDesc::Register(InTypeLayout);
|
|
|
|
bCachedUniformBufferStructDeclarations = false;
|
|
|
|
// 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(!bInitializedSerializationHistory, TEXT("Shader type was loaded after engine init, use ELoadingPhase::PostConfigInit on your module to cause it to load earlier."));
|
|
|
|
//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);
|
|
}
|
|
|
|
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* Filename)
|
|
{
|
|
TArray<const FShaderType*> OutShaders;
|
|
for(TLinkedList<FShaderType*>::TIterator It(GetTypeList()); It; It.Next())
|
|
{
|
|
const FShaderType* Type = *It;
|
|
if (FPlatformString::Strcmp(Filename, Type->GetShaderFilename()) == 0)
|
|
{
|
|
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);
|
|
}
|
|
|
|
bool FShaderType::ShouldCompilePermutation(const FShaderPermutationParameters& Parameters) const
|
|
{
|
|
return (*ShouldCompilePermutationRef)(Parameters);
|
|
}
|
|
|
|
void FShaderType::ModifyCompilationEnvironment(const FShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) const
|
|
{
|
|
(*ModifyCompilationEnvironmentRef)(Parameters, OutEnvironment);
|
|
}
|
|
|
|
bool FShaderType::ValidateCompiledResult(EShaderPlatform Platform, const FShaderParameterMap& ParameterMap, TArray<FString>& OutError) const
|
|
{
|
|
return (*ValidateCompiledResultRef)(Platform, ParameterMap, OutError);
|
|
}
|
|
|
|
const FSHAHash& FShaderType::GetSourceHash(EShaderPlatform ShaderPlatform) const
|
|
{
|
|
return GetShaderFileHash(GetShaderFilename(), ShaderPlatform);
|
|
}
|
|
|
|
void FShaderType::Initialize(const TMap<FString, TArray<const TCHAR*> >& ShaderFileToUniformBufferVariables)
|
|
{
|
|
//#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
|
|
for(TLinkedList<FShaderType*>::TIterator It(FShaderType::GetTypeList()); It; It.Next())
|
|
{
|
|
FShaderType* Type = *It;
|
|
#if UE_BUILD_DEBUG
|
|
UniqueShaderTypes.Add(Type);
|
|
#endif
|
|
GenerateReferencedUniformBuffers(Type->SourceFilename, Type->Name, ShaderFileToUniformBufferVariables, Type->ReferencedUniformBufferStructsCache);
|
|
}
|
|
|
|
#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
|
|
}
|
|
|
|
bInitializedSerializationHistory = true;
|
|
}
|
|
|
|
void FShaderType::Uninitialize()
|
|
{
|
|
bInitializedSerializationHistory = false;
|
|
}
|
|
|
|
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.GetReadAccess())
|
|
, ParameterMap(CompilerOutput.ParameterMap)
|
|
, OutputHash(CompilerOutput.OutputHash)
|
|
, MaterialShaderMapHash(InMaterialShaderMapHash)
|
|
, ShaderPipeline(InShaderPipeline)
|
|
, VertexFactoryType(InVertexFactoryType)
|
|
, NumInstructions(CompilerOutput.NumInstructions)
|
|
, NumTextureSamplers(CompilerOutput.NumTextureSamplers)
|
|
, CodeSize(CompilerOutput.ShaderCode.GetShaderCodeSize())
|
|
, PermutationId(InPermutationId)
|
|
{
|
|
}
|
|
|
|
/**
|
|
* 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
|
|
{
|
|
#if WITH_EDITORONLY_DATA
|
|
checkSlow(Initializer.OutputHash != FSHAHash());
|
|
|
|
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;
|
|
}
|
|
|
|
void FShader::BuildParameterMapInfo(const TMap<FString, FParameterAllocation>& ParameterMap)
|
|
{
|
|
for (int32 ParameterTypeIndex = 0; ParameterTypeIndex < (int32)EShaderParameterType::Num; ParameterTypeIndex++)
|
|
{
|
|
EShaderParameterType CurrentParameterType = (EShaderParameterType)ParameterTypeIndex;
|
|
|
|
if (CurrentParameterType == EShaderParameterType::LooseData)
|
|
{
|
|
for (TMap<FString, FParameterAllocation>::TConstIterator ParameterIt(ParameterMap); ParameterIt; ++ParameterIt)
|
|
{
|
|
const FParameterAllocation& ParamValue = ParameterIt.Value();
|
|
|
|
if (ParamValue.Type == CurrentParameterType)
|
|
{
|
|
bool bAddedToExistingBuffer = false;
|
|
|
|
for (int32 LooseParameterBufferIndex = 0; LooseParameterBufferIndex < ParameterMapInfo.LooseParameterBuffers.Num(); LooseParameterBufferIndex++)
|
|
{
|
|
FShaderLooseParameterBufferInfo& LooseParameterBufferInfo = ParameterMapInfo.LooseParameterBuffers[LooseParameterBufferIndex];
|
|
|
|
if (LooseParameterBufferInfo.BaseIndex == ParamValue.BufferIndex)
|
|
{
|
|
FShaderParameterInfo ParameterInfo(ParamValue.BaseIndex, ParamValue.Size);
|
|
LooseParameterBufferInfo.Parameters.Add(ParameterInfo);
|
|
LooseParameterBufferInfo.Size += ParamValue.Size;
|
|
bAddedToExistingBuffer = true;
|
|
}
|
|
}
|
|
|
|
if (!bAddedToExistingBuffer)
|
|
{
|
|
FShaderLooseParameterBufferInfo NewParameterBufferInfo(ParamValue.BufferIndex, ParamValue.Size);
|
|
|
|
FShaderParameterInfo ParameterInfo(ParamValue.BaseIndex, ParamValue.Size);
|
|
NewParameterBufferInfo.Parameters.Add(ParameterInfo);
|
|
|
|
ParameterMapInfo.LooseParameterBuffers.Add(NewParameterBufferInfo);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (CurrentParameterType != EShaderParameterType::UAV)
|
|
{
|
|
int32 NumParameters = 0;
|
|
|
|
for (TMap<FString, FParameterAllocation>::TConstIterator ParameterIt(ParameterMap); ParameterIt; ++ParameterIt)
|
|
{
|
|
const FParameterAllocation& ParamValue = ParameterIt.Value();
|
|
|
|
if (ParamValue.Type == CurrentParameterType)
|
|
{
|
|
NumParameters++;
|
|
}
|
|
}
|
|
|
|
TMemoryImageArray<FShaderParameterInfo>* ParameterInfoArray = &ParameterMapInfo.UniformBuffers;
|
|
|
|
if (CurrentParameterType == EShaderParameterType::Sampler)
|
|
{
|
|
ParameterInfoArray = &ParameterMapInfo.TextureSamplers;
|
|
}
|
|
else if (CurrentParameterType == EShaderParameterType::SRV)
|
|
{
|
|
ParameterInfoArray = &ParameterMapInfo.SRVs;
|
|
}
|
|
else
|
|
{
|
|
check(CurrentParameterType == EShaderParameterType::UniformBuffer);
|
|
}
|
|
|
|
ParameterInfoArray->Empty(NumParameters);
|
|
|
|
for (TMap<FString, FParameterAllocation>::TConstIterator ParameterIt(ParameterMap); ParameterIt; ++ParameterIt)
|
|
{
|
|
const FParameterAllocation& ParamValue = ParameterIt.Value();
|
|
|
|
if (ParamValue.Type == CurrentParameterType)
|
|
{
|
|
const uint16 BaseIndex = CurrentParameterType == EShaderParameterType::UniformBuffer ? ParamValue.BufferIndex : ParamValue.BaseIndex;
|
|
FShaderParameterInfo ParameterInfo(BaseIndex, ParamValue.Size);
|
|
ParameterInfoArray->Add(ParameterInfo);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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);
|
|
};
|
|
|
|
const auto CityHashArray = [&](const TMemoryImageArray<FShaderParameterInfo>& Array)
|
|
{
|
|
CityHashValue(Array.Num());
|
|
CityHash64WithSeed((const char*)Array.GetData(), Array.Num() * sizeof(FShaderParameterInfo), Hash);
|
|
};
|
|
|
|
for (FShaderLooseParameterBufferInfo& Info : ParameterMapInfo.LooseParameterBuffers)
|
|
{
|
|
CityHashValue(Info.BaseIndex);
|
|
CityHashValue(Info.Size);
|
|
CityHashArray(Info.Parameters);
|
|
}
|
|
CityHashArray(ParameterMapInfo.UniformBuffers);
|
|
CityHashArray(ParameterMapInfo.TextureSamplers);
|
|
CityHashArray(ParameterMapInfo.SRVs);
|
|
}
|
|
|
|
ParameterMapInfo.Hash = Hash;
|
|
}
|
|
|
|
const FSHAHash& FShader::GetOutputHash() const
|
|
{
|
|
#if WITH_EDITORONLY_DATA
|
|
return OutputHash;
|
|
#endif
|
|
return ShaderSourceDefaultHash;
|
|
}
|
|
|
|
const FSHAHash& FShader::GetHash() const
|
|
{
|
|
#if WITH_EDITORONLY_DATA
|
|
return SourceHash;
|
|
#endif
|
|
return ShaderSourceDefaultHash;
|
|
}
|
|
|
|
const FSHAHash& FShader::GetVertexFactoryHash() const
|
|
{
|
|
#if WITH_EDITORONLY_DATA
|
|
return VFSourceHash;
|
|
#endif
|
|
return ShaderSourceDefaultHash;
|
|
}
|
|
|
|
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* Filename)
|
|
{
|
|
TArray<const FShaderPipelineType*> PipelineTypes;
|
|
for (TLinkedList<FShaderPipelineType*>::TIterator It(FShaderPipelineType::GetTypeList()); It; It.Next())
|
|
{
|
|
auto* PipelineType = *It;
|
|
for (auto* ShaderType : PipelineType->Stages)
|
|
{
|
|
if (FPlatformString::Strcmp(Filename, ShaderType->GetShaderFilename()) == 0)
|
|
{
|
|
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;
|
|
}
|
|
|
|
void FShaderPipelineType::Uninitialize()
|
|
{
|
|
check(bInitialized);
|
|
|
|
bInitialized = false;
|
|
}
|
|
|
|
const FShaderPipelineType* FShaderPipelineType::GetShaderPipelineTypeByName(const FHashedName& Name)
|
|
{
|
|
FShaderPipelineType** FoundType = GetNameToTypeMap().Find(Name);
|
|
return FoundType ? *FoundType : nullptr;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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)
|
|
{
|
|
delete 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)
|
|
{
|
|
if (IsD3DPlatform(Platform) && IsPCPlatform(Platform))
|
|
{
|
|
static const auto CVar = IConsoleManager::Get().FindConsoleVariable(TEXT("r.D3D.ForceDXC"));
|
|
return (CVar && CVar->GetInt() != 0);
|
|
}
|
|
if (IsOpenGLPlatform(Platform))
|
|
{
|
|
static const auto CVar = IConsoleManager::Get().FindConsoleVariable(TEXT("r.OpenGL.ForceDXC"));
|
|
return (CVar && CVar->GetInt() != 0);
|
|
}
|
|
if (IsMetalPlatform(Platform))
|
|
{
|
|
// Hlslcc has been removed for Metal. There is only DXC now.
|
|
return true;
|
|
}
|
|
if (IsVulkanPlatform(Platform))
|
|
{
|
|
static const auto CVar = IConsoleManager::Get().FindConsoleVariable(TEXT("r.Vulkan.ForceDXC"));
|
|
int32 VulkanForceDxc = (CVar ? CVar->GetInt() : 0);
|
|
const bool bIsVulkanMobile = IsVulkanMobilePlatform((EShaderPlatform)Platform) || IsVulkanMobileSM5Platform((EShaderPlatform)Platform);
|
|
const bool bIsDxcEnabledForDesktop = (VulkanForceDxc == 1 && !bIsVulkanMobile);
|
|
const bool bIsDxcEnabledForMobile = (VulkanForceDxc == 2 && bIsVulkanMobile);
|
|
const bool bIsDxcEnableForAll = (VulkanForceDxc == 3);
|
|
return (bIsDxcEnabledForDesktop || bIsDxcEnabledForMobile || bIsDxcEnableForAll);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void ShaderMapAppendKeyString(EShaderPlatform Platform, FString& KeyString)
|
|
{
|
|
const FName ShaderFormatName = LegacyShaderPlatformToShaderFormat(Platform);
|
|
|
|
// 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"));
|
|
KeyString += (CVar && CVar->GetValueOnAnyThread() != 0) ? TEXT("_DXTN") : TEXT("_BC5N");
|
|
}
|
|
|
|
{
|
|
static const auto CVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.ClearCoatNormal"));
|
|
KeyString += (CVar && CVar->GetValueOnAnyThread() != 0) ? TEXT("_CCBN") : TEXT("_NoCCBN");
|
|
}
|
|
|
|
{
|
|
static const auto CVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.IrisNormal"));
|
|
KeyString += (CVar && CVar->GetValueOnAnyThread() != 0) ? TEXT("_Iris") : TEXT("_NoIris");
|
|
}
|
|
|
|
{
|
|
static const auto CVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.CompileShadersForDevelopment"));
|
|
KeyString += (CVar && CVar->GetValueOnAnyThread() != 0) ? TEXT("_DEV") : TEXT("_NoDEV");
|
|
}
|
|
|
|
{
|
|
static const auto CVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.AllowStaticLighting"));
|
|
const bool bValue = CVar ? CVar->GetValueOnAnyThread() != 0 : true;
|
|
KeyString += bValue ? TEXT("_SL") : TEXT("_NoSL");
|
|
}
|
|
|
|
{
|
|
KeyString += IsUsingBasePassVelocity(Platform) ? TEXT("_GV") : TEXT("");
|
|
}
|
|
|
|
{
|
|
static const auto CVarInstancedStereo = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("vr.InstancedStereo"));
|
|
static const auto CVarMobileMultiView = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("vr.MobileMultiView"));
|
|
static const auto CVarODSCapture = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("vr.ODSCapture"));
|
|
|
|
bool bIsInstancedStereo = (RHISupportsInstancedStereo(Platform) && (CVarInstancedStereo && CVarInstancedStereo->GetValueOnGameThread() != 0));
|
|
const bool bIsMultiView = (RHISupportsMultiView(Platform) && bIsInstancedStereo);
|
|
|
|
bool bIsMobileMultiView = (CVarMobileMultiView && CVarMobileMultiView->GetValueOnGameThread() != 0);
|
|
if (bIsMobileMultiView && !RHISupportsMobileMultiView(Platform))
|
|
{
|
|
// Native mobile multi-view is not supported, fall back to instancing if available
|
|
bIsMobileMultiView = bIsInstancedStereo = RHISupportsInstancedStereo(Platform);
|
|
}
|
|
|
|
const bool bIsODSCapture = CVarODSCapture && (CVarODSCapture->GetValueOnGameThread() != 0);
|
|
|
|
if (bIsInstancedStereo)
|
|
{
|
|
KeyString += TEXT("_VRIS");
|
|
|
|
if (bIsMultiView)
|
|
{
|
|
KeyString += TEXT("_MVIEW");
|
|
}
|
|
}
|
|
|
|
if (bIsMobileMultiView)
|
|
{
|
|
KeyString += TEXT("_MMVIEW");
|
|
}
|
|
|
|
if (bIsODSCapture)
|
|
{
|
|
KeyString += TEXT("_ODSC");
|
|
}
|
|
}
|
|
|
|
{
|
|
KeyString += IsUsingSelectiveBasePassOutputs(Platform) ? TEXT("_SO") : TEXT("");
|
|
}
|
|
|
|
{
|
|
// PreExposure is always used
|
|
KeyString += TEXT("_PreExp");
|
|
}
|
|
|
|
{
|
|
KeyString += IsUsingDBuffers(Platform) ? TEXT("_DBuf") : TEXT("_NoDBuf");
|
|
}
|
|
|
|
{
|
|
static IConsoleVariable* CVar = IConsoleManager::Get().FindConsoleVariable(TEXT("r.AllowGlobalClipPlane"));
|
|
KeyString += (CVar && CVar->GetInt() != 0) ? TEXT("_ClipP") : TEXT("");
|
|
}
|
|
|
|
{
|
|
KeyString += ShouldKeepShaderDebugInfo(ShaderFormatName) ? TEXT("_NoStrip") : TEXT("");
|
|
KeyString += ShouldAllowUniqueDebugInfo(ShaderFormatName) ? TEXT("_FullDbg") : TEXT("");
|
|
}
|
|
|
|
{
|
|
static const auto CVar = IConsoleManager::Get().FindConsoleVariable(TEXT("r.Shaders.Optimize"));
|
|
KeyString += (CVar && CVar->GetInt() != 0) ? TEXT("") : TEXT("_NoOpt");
|
|
}
|
|
|
|
{
|
|
// Always default to fast math unless specified
|
|
static const auto CVar = IConsoleManager::Get().FindConsoleVariable(TEXT("r.Shaders.FastMath"));
|
|
KeyString += (CVar && CVar->GetInt() == 0) ? TEXT("_NoFastMath") : TEXT("");
|
|
}
|
|
|
|
{
|
|
static const auto CVar = IConsoleManager::Get().FindConsoleVariable(TEXT("r.Shaders.WarningsAsErrors"));
|
|
KeyString += (CVar && CVar->GetInt() == 1) ? TEXT("_WX") : TEXT("");
|
|
}
|
|
|
|
{
|
|
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))
|
|
{
|
|
KeyString.Appendf(TEXT("_C%d"), CVar->GetInt());
|
|
}
|
|
}
|
|
|
|
{
|
|
static const auto CVar = IConsoleManager::Get().FindConsoleVariable(TEXT("r.Shaders.FlowControlMode"));
|
|
if (CVar)
|
|
{
|
|
switch(CVar->GetInt())
|
|
{
|
|
case 2:
|
|
KeyString += TEXT("_AvoidFlow");
|
|
break;
|
|
case 1:
|
|
KeyString += TEXT("_PreferFlow");
|
|
break;
|
|
case 0:
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!AllowPixelDepthOffset(Platform))
|
|
{
|
|
KeyString += TEXT("_NoPDO");
|
|
}
|
|
|
|
if (IsD3DPlatform(Platform) && IsPCPlatform(Platform))
|
|
{
|
|
{
|
|
static const auto CVar = IConsoleManager::Get().FindConsoleVariable(TEXT("r.D3D.RemoveUnusedInterpolators"));
|
|
if (CVar && CVar->GetInt() != 0)
|
|
{
|
|
KeyString += TEXT("_UnInt");
|
|
}
|
|
}
|
|
}
|
|
|
|
if (IsMobilePlatform(Platform))
|
|
{
|
|
{
|
|
static IConsoleVariable* CVar = IConsoleManager::Get().FindConsoleVariable(TEXT("r.Mobile.DisableVertexFog"));
|
|
KeyString += (CVar && CVar->GetInt() != 0) ? TEXT("_NoVFog") : TEXT("");
|
|
}
|
|
|
|
{
|
|
static const auto* CVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.Shadow.CSM.MaxMobileCascades"));
|
|
KeyString += (CVar) ? FString::Printf(TEXT("MMC%d"), CVar->GetValueOnAnyThread()) : TEXT("");
|
|
}
|
|
|
|
{
|
|
static IConsoleVariable* CVar = IConsoleManager::Get().FindConsoleVariable(TEXT("r.Mobile.ForceFullPrecisionInPS"));
|
|
KeyString += (CVar && CVar->GetInt() != 0) ? TEXT("_highp") : TEXT("");
|
|
}
|
|
|
|
{
|
|
static IConsoleVariable* CVar = IConsoleManager::Get().FindConsoleVariable(TEXT("r.Mobile.AllowDitheredLODTransition"));
|
|
KeyString += (CVar && CVar->GetInt() != 0) ? TEXT("_DLODT") : TEXT("");
|
|
}
|
|
|
|
if (IsOpenGLPlatform(Platform))
|
|
{
|
|
static IConsoleVariable* CVar = IConsoleManager::Get().FindConsoleVariable(TEXT("OpenGL.UseEmulatedUBs"));
|
|
KeyString += (CVar && CVar->GetInt() != 0) ? TEXT("_NoUB") : TEXT("");
|
|
}
|
|
|
|
{
|
|
static FShaderPlatformCachedIniValue<bool> MobileEnableMovableSpotlightsIniValue(TEXT("/Script/Engine.RendererSettings"), TEXT("r.Mobile.EnableMovableSpotlights"));
|
|
static FShaderPlatformCachedIniValue<bool> MobileEnableMovableSpotlightsShadowIniValue(TEXT("/Script/Engine.RendererSettings"), TEXT("r.Mobile.EnableMovableSpotlightsShadow"));
|
|
|
|
bool bMobileEnableMovableSpotlights = (MobileEnableMovableSpotlightsIniValue.Get(Platform) != 0);
|
|
KeyString += (bMobileEnableMovableSpotlights) ? TEXT("_MSPTL") : TEXT("");
|
|
|
|
bool bMobileEnableMovableSpotlightsShadow = (MobileEnableMovableSpotlightsShadowIniValue.Get(Platform) != 0);
|
|
KeyString += (bMobileEnableMovableSpotlights && bMobileEnableMovableSpotlightsShadow) ? TEXT("S") : TEXT("");
|
|
}
|
|
|
|
{
|
|
static IConsoleVariable* CVar = IConsoleManager::Get().FindConsoleVariable(TEXT("r.Mobile.UseHWsRGBEncoding"));
|
|
KeyString += (CVar && CVar->GetInt() != 0) ? TEXT("_HWsRGB") : TEXT("");
|
|
}
|
|
|
|
{
|
|
// make it per shader platform ?
|
|
static IConsoleVariable* CVar = IConsoleManager::Get().FindConsoleVariable(TEXT("r.Mobile.SupportGPUScene"));
|
|
bool bMobileGpuScene = (CVar && CVar->GetInt() != 0);
|
|
KeyString += bMobileGpuScene ? TEXT("_MobGPUSc") : TEXT("");
|
|
}
|
|
|
|
{
|
|
static IConsoleVariable* CVar = IConsoleManager::Get().FindConsoleVariable(TEXT("r.MobileHDR"));
|
|
KeyString += (CVar && CVar->GetInt() != 0) ? TEXT("_MobileHDR") : TEXT("");
|
|
}
|
|
|
|
{
|
|
static FShaderPlatformCachedIniValue<int32> MobilePropagateAlphaIniValue(TEXT("/Script/Engine.RendererSettings"), TEXT("r.Mobile.PropagateAlpha"));
|
|
int MobilePropagateAlphaIniValueInt = MobilePropagateAlphaIniValue.Get((EShaderPlatform)Platform);
|
|
|
|
if (MobilePropagateAlphaIniValueInt == 1)
|
|
{
|
|
KeyString += TEXT("_MbPropA");
|
|
}
|
|
else if (MobilePropagateAlphaIniValueInt == 2)
|
|
{
|
|
KeyString += TEXT("_MbPrePropA");
|
|
}
|
|
}
|
|
|
|
{
|
|
static IConsoleVariable* CVar = IConsoleManager::Get().FindConsoleVariable(TEXT("r.Mobile.ShadingPath"));
|
|
KeyString += (CVar && CVar->GetInt() != 0) ? TEXT("_MobDSh") : TEXT("");
|
|
}
|
|
|
|
{
|
|
static IConsoleVariable* MobileGTAOPreIntegratedTextureTypeCVar = IConsoleManager::Get().FindConsoleVariable(TEXT("r.Mobile.GTAOPreIntegratedTextureType"));
|
|
static IConsoleVariable* MobileAmbientOcclusionCVar = IConsoleManager::Get().FindConsoleVariable(TEXT("r.Mobile.AmbientOcclusion"));
|
|
static IConsoleVariable* MobileHDRCVar = IConsoleManager::Get().FindConsoleVariable(TEXT("r.MobileHDR"));
|
|
int32 GTAOPreIntegratedTextureType = MobileGTAOPreIntegratedTextureTypeCVar ? MobileGTAOPreIntegratedTextureTypeCVar->GetInt() : 0;
|
|
KeyString += ((MobileAmbientOcclusionCVar && MobileAmbientOcclusionCVar->GetInt() != 0) && (MobileHDRCVar && MobileHDRCVar->GetInt() !=0)) ? FString::Printf(TEXT("_MobileAO_%d"), GTAOPreIntegratedTextureType) : TEXT("");
|
|
}
|
|
|
|
{
|
|
KeyString += IsMobileDistanceFieldEnabled(Platform) ? TEXT("_MobSDF") : TEXT("");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (IsOpenGLPlatform(Platform))
|
|
{
|
|
static IConsoleVariable* CVar = IConsoleManager::Get().FindConsoleVariable(TEXT("OpenGL.UseEmulatedUBs"));
|
|
KeyString += (CVar && CVar->GetInt() != 0) ? TEXT("_NoUB") : TEXT("");
|
|
}
|
|
}
|
|
|
|
const IShaderFormat* ShaderFormat = GetTargetPlatformManagerRef().FindShaderFormat(ShaderFormatName);
|
|
if (ShaderFormat)
|
|
{
|
|
ShaderFormat->AppendToKeyString(KeyString);
|
|
}
|
|
|
|
// 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"));
|
|
KeyString += (CVar && CVar->GetInt() != 0) ? TEXT("_ZeroInit") : TEXT("");
|
|
}
|
|
{
|
|
static const auto CVar = IConsoleManager::Get().FindConsoleVariable(TEXT("r.Shaders.BoundsChecking"));
|
|
KeyString += (CVar && CVar->GetInt() != 0) ? TEXT("_BoundsChecking") : TEXT("");
|
|
}
|
|
{
|
|
KeyString += RHISupportsManualVertexFetch(Platform) ? TEXT("_MVF_") : TEXT("");
|
|
}
|
|
|
|
uint32 ShaderVersion = RHIGetShaderLanguageVersion(Platform);
|
|
KeyString += FString::Printf(TEXT("_MTLSTD%u_"), ShaderVersion);
|
|
|
|
bool bAllowFastIntrinsics = false;
|
|
bool bEnableMathOptimisations = true;
|
|
bool bForceFloats = false;
|
|
int32 IndirectArgumentTier = 0;
|
|
if (IsPCPlatform(Platform))
|
|
{
|
|
GConfig->GetBool(TEXT("/Script/MacTargetPlatform.MacTargetSettings"), TEXT("UseFastIntrinsics"), bAllowFastIntrinsics, GEngineIni);
|
|
GConfig->GetBool(TEXT("/Script/MacTargetPlatform.MacTargetSettings"), TEXT("EnableMathOptimisations"), bEnableMathOptimisations, GEngineIni);
|
|
GConfig->GetBool(TEXT("/Script/MacTargetPlatform.MacTargetSettings"), TEXT("ForceFloats"), bForceFloats, GEngineIni);
|
|
GConfig->GetInt(TEXT("/Script/MacTargetPlatform.MacTargetSettings"), TEXT("IndirectArgumentTier"), IndirectArgumentTier, 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);
|
|
}
|
|
|
|
if (bAllowFastIntrinsics)
|
|
{
|
|
KeyString += TEXT("_MTLSL_FastIntrin");
|
|
}
|
|
|
|
// Same as console-variable above, but that's global and this is per-platform, per-project
|
|
if (!bEnableMathOptimisations)
|
|
{
|
|
KeyString += TEXT("_NoFastMath");
|
|
}
|
|
|
|
if (bForceFloats)
|
|
{
|
|
KeyString += TEXT("_FP32");
|
|
}
|
|
|
|
KeyString += FString::Printf(TEXT("_IAB%d"), 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)
|
|
{
|
|
KeyString += TEXT("_ARCHIVE");
|
|
}
|
|
}
|
|
|
|
// Is DXC shader compiler enabled for this platform?
|
|
KeyString += (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)
|
|
{
|
|
KeyString += TEXT("_SD");
|
|
}
|
|
}
|
|
|
|
ITargetPlatform* TargetPlatform = GetTargetPlatformManager()->FindTargetPlatformWithSupport(TEXT("ShaderFormat"), ShaderFormatName);
|
|
|
|
{
|
|
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)
|
|
{
|
|
KeyString += TEXT("_FS");
|
|
}
|
|
}
|
|
|
|
{
|
|
static const auto CVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.PostProcessing.PropagateAlpha"));
|
|
if (CVar && CVar->GetValueOnAnyThread() > 0)
|
|
{
|
|
if (CVar->GetValueOnAnyThread() == 2)
|
|
{
|
|
KeyString += TEXT("_SA2");
|
|
}
|
|
else
|
|
{
|
|
KeyString += TEXT("_SA");
|
|
}
|
|
}
|
|
}
|
|
|
|
{
|
|
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)
|
|
{
|
|
KeyString += TEXT("_VFO");
|
|
}
|
|
}
|
|
|
|
{
|
|
static const auto CVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.SupportSkyAtmosphere"));
|
|
if (CVar && CVar->GetValueOnAnyThread() > 0)
|
|
{
|
|
KeyString += TEXT("_SKYATM");
|
|
|
|
static const auto CVarHeightFog = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.SupportSkyAtmosphereAffectsHeightFog"));
|
|
if (CVarHeightFog && CVarHeightFog->GetValueOnAnyThread() > 0)
|
|
{
|
|
KeyString += TEXT("_SKYHF");
|
|
}
|
|
}
|
|
}
|
|
|
|
{
|
|
static const auto CVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.Strata"));
|
|
const bool bStrataEnabled = CVar && CVar->GetValueOnAnyThread() > 0;
|
|
if (bStrataEnabled)
|
|
{
|
|
KeyString += TEXT("_STRATA");
|
|
}
|
|
|
|
static const auto CVarBudget = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.Strata.BytesPerPixel"));
|
|
if (bStrataEnabled && CVarBudget)
|
|
{
|
|
KeyString += FString::Printf(TEXT("_BUDGET%u"), CVarBudget->GetValueOnAnyThread());
|
|
}
|
|
|
|
static const auto CVarBackCompatibility = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.StrataBackCompatibility"));
|
|
if (bStrataEnabled && CVarBackCompatibility && CVarBackCompatibility->GetValueOnAnyThread()>0)
|
|
{
|
|
KeyString += FString::Printf(TEXT("_BACKCOMPAT"));
|
|
}
|
|
}
|
|
|
|
{
|
|
if (MaskedInEarlyPass(Platform))
|
|
{
|
|
KeyString += TEXT("_EZPMM");
|
|
}
|
|
}
|
|
|
|
{
|
|
static const auto CVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.GPUSkin.Limit2BoneInfluences"));
|
|
if (CVar && CVar->GetValueOnAnyThread() != 0)
|
|
{
|
|
KeyString += TEXT("_2bi");
|
|
}
|
|
}
|
|
{
|
|
if(UseGPUScene(Platform, GetMaxSupportedFeatureLevel(Platform)))
|
|
{
|
|
KeyString += TEXT("_gs1");
|
|
}
|
|
else
|
|
{
|
|
KeyString += TEXT("_gs0");
|
|
}
|
|
}
|
|
|
|
{
|
|
static const auto CVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.GBufferDiffuseSampleOcclusion"));
|
|
if (CVar && CVar->GetValueOnAnyThread() != 0)
|
|
{
|
|
KeyString += 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 CVarMobileVirtualTexture = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.Mobile.VirtualTextures"));
|
|
if (IsMobilePlatform(Platform) && VTTextures)
|
|
{
|
|
VTTextures = (CVarMobileVirtualTexture->GetValueOnAnyThread() != 0);
|
|
}
|
|
|
|
const bool VTSupported = TargetPlatform != nullptr && TargetPlatform->SupportsFeature(ETargetPlatformFeatures::VirtualTextureStreaming);
|
|
|
|
static const auto CVarVTAnisotropic = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.VT.AnisotropicFiltering"));
|
|
const bool VTAnisotropic = CVarVTAnisotropic && CVarVTAnisotropic->GetValueOnAnyThread() != 0;
|
|
|
|
static const auto CVarVTFactor = IConsoleManager::Get().FindConsoleVariable(TEXT("r.vt.FeedbackFactor")); check(CVarVTFactor);
|
|
const int32 VTFeedbackFactor = CVarVTFactor->GetInt();
|
|
|
|
auto tt = FString::Printf(TEXT("_VT-%d-%d-%d-%d-%d"), VTLightmaps, VTTextures, VTSupported, VTFeedbackFactor, VTAnisotropic);
|
|
KeyString += tt;
|
|
}
|
|
|
|
if (RHISupportsRenderTargetWriteMask(Platform))
|
|
{
|
|
KeyString += TEXT("_RTWM");
|
|
}
|
|
|
|
if (IsUsingPerPixelDBufferMask(Platform))
|
|
{
|
|
KeyString += TEXT("_PPDBM");
|
|
}
|
|
|
|
if (RHISupportsMeshShaders(Platform))
|
|
{
|
|
KeyString += TEXT("_MS");
|
|
}
|
|
|
|
if (ShouldCompileRayTracingShadersForProject(Platform))
|
|
{
|
|
static const auto CVarCompileCHS = IConsoleManager::Get().FindConsoleVariable(TEXT("r.RayTracing.CompileMaterialCHS"));
|
|
static const auto CVarCompileAHS = IConsoleManager::Get().FindConsoleVariable(TEXT("r.RayTracing.CompileMaterialAHS"));
|
|
static const auto CVarTextureLod = IConsoleManager::Get().FindConsoleVariable(TEXT("r.RayTracing.UseTextureLod"));
|
|
|
|
KeyString += FString::Printf(TEXT("_RAY-CHS%dAHS%dLOD%d"),
|
|
CVarCompileCHS && CVarCompileCHS->GetBool() ? 1 : 0,
|
|
CVarCompileAHS && CVarCompileAHS->GetBool() ? 1 : 0,
|
|
CVarTextureLod && CVarTextureLod->GetBool() ? 1 : 0);
|
|
}
|
|
|
|
if (ForceSimpleSkyDiffuse(Platform))
|
|
{
|
|
KeyString += TEXT("_SSD");
|
|
}
|
|
|
|
if (VelocityEncodeDepth(Platform))
|
|
{
|
|
KeyString += TEXT("_VED");
|
|
}
|
|
|
|
{
|
|
const bool bSupportsAnisotropicMaterials = FDataDrivenShaderPlatformInfo::GetSupportsAnisotropicMaterials(Platform);
|
|
KeyString += FString::Printf(TEXT("_Aniso-%d"), bSupportsAnisotropicMaterials ? 1 : 0);
|
|
}
|
|
|
|
{
|
|
// add shader compression format
|
|
KeyString += TEXT("_Compr");
|
|
KeyString += GetShaderCompressionFormat().ToString();
|
|
}
|
|
}
|