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

567 lines
21 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
/*=============================================================================
ShaderParameters.cpp: Shader parameter implementation.
=============================================================================*/
#include "ShaderParameters.h"
#include "Containers/List.h"
#include "ShaderCore.h"
#include "Shader.h"
#include "ShaderParameterParser.h"
#include "VertexFactory.h"
#include "ShaderCodeLibrary.h"
#include "Misc/ScopeRWLock.h"
#include "Misc/ScopeLock.h"
IMPLEMENT_TYPE_LAYOUT(FShaderParameter);
IMPLEMENT_TYPE_LAYOUT(FShaderResourceParameter);
IMPLEMENT_TYPE_LAYOUT(FShaderUniformBufferParameter);
IMPLEMENT_TYPE_LAYOUT(FShaderUniformBufferMemberParameter);
static void FailureToBindNonOptionalParameter(const TCHAR* ParameterType, const TCHAR* ParameterName)
{
if (!UE_LOG_ACTIVE(LogShaders, Log))
{
UE_LOG(LogShaders, Fatal, TEXT("Failure to bind non-optional %s %s! The parameter is either not present in the shader, or the shader compiler optimized it out."), ParameterType, ParameterName);
}
else
{
UE_LOG(LogShaders, Log, TEXT("Failure to bind non-optional %s %s! The parameter is either not present in the shader, or the shader compiler optimized it out."), ParameterType, ParameterName);
// We use a non-Slate message box to avoid problem where we haven't compiled the shaders for Slate.
FPlatformMisc::MessageBoxExt(EAppMsgType::Ok, *FText::Format(
NSLOCTEXT("UnrealEd", "Error_FailedToBindShaderParameter", "Failure to bind non-optional shader parameter {0}! The parameter is either not present in the shader, or the shader compiler optimized it out. This will be an assert with LogShaders suppressed!"),
FText::FromString(ParameterName)).ToString(), TEXT("Warning"));
}
}
void FShaderParameter::Bind(const FShaderParameterMap& ParameterMap,const TCHAR* ParameterName,EShaderParameterFlags Flags)
{
if (!ParameterMap.FindParameterAllocation(ParameterName,BufferIndex,BaseIndex,NumBytes) && Flags == SPF_Mandatory)
{
FailureToBindNonOptionalParameter(TEXT("shader parameter"), ParameterName);
}
}
FArchive& operator<<(FArchive& Ar,FShaderParameter& P)
{
uint16& PBufferIndex = P.BufferIndex;
return Ar << P.BaseIndex << P.NumBytes << PBufferIndex;
}
void FShaderResourceParameter::Bind(const FShaderParameterMap& ParameterMap, const TCHAR* ParameterName, EShaderParameterFlags Flags)
{
if (TOptional<FParameterAllocation> Allocation = ParameterMap.FindParameterAllocation(ParameterName))
{
if (IsParameterBindless(Allocation->Type))
{
checkf(Allocation->BufferIndex == 0, TEXT("Unexpected buffer index (%d) for bindless index. Global bindless parameters are expected to be in the global constant buffer (buffer index 0)."), Allocation->BufferIndex);
}
BaseIndex = Allocation->BaseIndex;
NumResources = Allocation->Size;
Type = Allocation->Type;
}
else if (Flags == SPF_Mandatory)
{
FailureToBindNonOptionalParameter(TEXT("shader resource parameter"), ParameterName);
}
}
FArchive& operator<<(FArchive& Ar,FShaderResourceParameter& P)
{
return Ar << P.BaseIndex << P.NumResources;
}
void FShaderUniformBufferMemberParameter::Bind(const FShaderParameterMap& ParameterMap, const TCHAR* ParameterName)
{
bIsBound = ParameterMap.ContainsParameterAllocation(ParameterName) ? 1 : 0;
}
FArchive& operator<<(FArchive& Ar, FShaderUniformBufferMemberParameter& P)
{
return Ar << P.bIsBound;
}
#if WITH_EDITOR
void FShaderUniformBufferParameter::ModifyCompilationEnvironment(const TCHAR* ParameterName, const FShaderParametersMetadata& Struct, EShaderPlatform Platform, FShaderCompilerEnvironment& OutEnvironment)
{
// Add the uniform buffer declaration to the compilation environment as an include: /Engine/Generated/UniformBuffers/<ParameterName>.usf
const FString IncludeName = FString::Printf(TEXT("/Engine/Generated/UniformBuffers/%s.ush"), ParameterName);
// if the name matches the struct's name, use the struct's cached version; otherwise, generate it now with the correct variable name.
if (FCString::Strcmp(ParameterName, Struct.GetShaderVariableName()) != 0)
{
const FString Declaration = UE::ShaderParameters::CreateUniformBufferShaderDeclaration(ParameterName, Struct, nullptr);
OutEnvironment.IncludeVirtualPathToContentsMap.Add(IncludeName, Declaration);
}
else
{
OutEnvironment.IncludeVirtualPathToContentsMap.Add(IncludeName, Struct.GetUniformBufferDeclaration());
}
FString& GeneratedUniformBuffersInclude = OutEnvironment.IncludeVirtualPathToContentsMap.FindOrAdd("/Engine/Generated/GeneratedUniformBuffers.ush");
const FString Include = FString::Printf(TEXT("#include \"%s\"") HLSL_LINE_TERMINATOR, *IncludeName);
GeneratedUniformBuffersInclude.Append(Include);
Struct.AddResourceTableEntries(OutEnvironment.ResourceTableMap, OutEnvironment.UniformBufferMap);
}
#endif // WITH_EDITOR
void FShaderUniformBufferParameter::Bind(const FShaderParameterMap& ParameterMap,const TCHAR* ParameterName,EShaderParameterFlags Flags)
{
uint16 UnusedBaseIndex = 0;
uint16 UnusedNumBytes = 0;
if (TOptional<FParameterAllocation> Parameter = ParameterMap.FindParameterAllocation(ParameterName))
{
// NOTE: the name difference is intentional (and confusing)
BaseIndex = Parameter->BufferIndex;
checkf(IsBound(), TEXT("UniformBuffer Parameter '%s' was not bound with a valid index. This can cause instability at runtime."), ParameterName);
}
else
{
BaseIndex = 0xffff;
if (Flags == SPF_Mandatory)
{
FailureToBindNonOptionalParameter(TEXT("shader resource parameter"), ParameterName);
}
}
}
#if WITH_EDITOR
/** The individual bits of a uniform buffer declaration. */
struct FUniformBufferDecl
{
/** Members to place in the constant buffer. */
FStringBuilderBase ConstantBufferMembers;
/** Members to place in the resource table. */
FStringBuilderBase ResourceMembers;
/** Members in the struct HLSL shader code will access. */
FStringBuilderBase StructMembers;
};
/** Generates a HLSL struct declaration for a uniform buffer struct. */
static void CreateHLSLUniformBufferStructMembersDeclaration(
const FShaderParametersMetadata& UniformBufferStruct,
const FString& UniformBufferName,
const FString& StructPrefix,
const FString& GlobalPrefix,
uint32 StructOffset,
FUniformBufferDecl& Decl,
uint32& HLSLBaseOffset)
{
const TArray<FShaderParametersMetadata::FMember>& StructMembers = UniformBufferStruct.GetMembers();
auto AddStructMember = [&](const FShaderParametersMetadata::FMember& Member, bool bResource)
{
// UB_DECL_PARAMETER(UniformBufferName, StructPrefix_Name, GlobalPrefix_Name)
Decl.StructMembers << (bResource ? TEXT("UB_DECL_RESOURCE(") : TEXT("UB_DECL_PARAMETER(")) << UniformBufferName << TEXT(",");
if (!StructPrefix.IsEmpty())
{
Decl.StructMembers << StructPrefix << TEXT(".");
}
Decl.StructMembers << Member.GetName() << TEXT(",");
if (!GlobalPrefix.IsEmpty())
{
Decl.StructMembers << GlobalPrefix << TEXT("_");
}
Decl.StructMembers << Member.GetName() << TEXT(");\n");
};
if (EnumHasAnyFlags(UniformBufferStruct.GetUsageFlags(), FShaderParametersMetadata::EUsageFlags::UniformView))
{
// UniformView struct is expected to have a single SRV member which serves as a uniform view
check(StructMembers.Num() == 1);
const FShaderParametersMetadata::FMember& Member = StructMembers[0];
check(Member.GetBaseType() == UBMT_SRV || Member.GetBaseType() == UBMT_RDG_BUFFER_SRV);
TStringBuilder<256> ParameterName;
if (!GlobalPrefix.IsEmpty())
{
ParameterName << GlobalPrefix << TEXT("_");
}
ParameterName << Member.GetName();
Decl.ConstantBufferMembers.Appendf(TEXT("UB_CB_UNIFORM_BLOCK(%s, %s);\n"), *UniformBufferName, *ParameterName);
AddStructMember(Member, false);
return;
}
const TCHAR* PreviousBaseTypeName = TEXT("UB_FLOAT");
for (int32 MemberIndex = 0; MemberIndex < StructMembers.Num(); ++MemberIndex)
{
const FShaderParametersMetadata::FMember& Member = StructMembers[MemberIndex];
TStringBuilder<8> ArrayDim;
if (Member.GetNumElements() > 0)
{
ArrayDim.Appendf(TEXT("[%u]"), Member.GetNumElements());
}
if (Member.GetBaseType() == UBMT_NESTED_STRUCT)
{
checkf(Member.GetNumElements() == 0, TEXT("SHADER_PARAMETER_STRUCT_ARRAY() is not supported in uniform buffer yet."));
FString NewStructPrefix = StructPrefix.IsEmpty() ? FString(Member.GetName()) : FString::Printf(TEXT("%s.%s"), *StructPrefix, Member.GetName());
FString NewGlobalPrefix = GlobalPrefix.IsEmpty() ? FString(Member.GetName()) : FString::Printf(TEXT("%s_%s"), *GlobalPrefix, Member.GetName());
CreateHLSLUniformBufferStructMembersDeclaration(*Member.GetStructMetadata(), UniformBufferName, NewStructPrefix, NewGlobalPrefix, StructOffset + Member.GetOffset(), Decl, HLSLBaseOffset);
}
else if (Member.GetBaseType() == UBMT_INCLUDED_STRUCT)
{
CreateHLSLUniformBufferStructMembersDeclaration(*Member.GetStructMetadata(), UniformBufferName, StructPrefix, GlobalPrefix, StructOffset + Member.GetOffset(), Decl, HLSLBaseOffset);
}
else if (IsShaderParameterTypeForUniformBufferLayout(Member.GetBaseType()))
{
// Add the constant buffer entry for bindless resource indices
constexpr uint32 HLSLMemberSize = 4;
const uint32 AbsoluteMemberOffset = StructOffset + Member.GetOffset();
// If the HLSL offset doesn't match the C++ offset, generate padding to fix it.
if (HLSLBaseOffset != AbsoluteMemberOffset)
{
check(HLSLBaseOffset < AbsoluteMemberOffset);
while (HLSLBaseOffset < AbsoluteMemberOffset)
{
Decl.ConstantBufferMembers.Appendf(TEXT("\t%s() UB_CB_MEMBER_NAME(%s, Padding%u);\n"), PreviousBaseTypeName, *UniformBufferName, HLSLBaseOffset);
HLSLBaseOffset += 4;
};
check(HLSLBaseOffset == AbsoluteMemberOffset);
}
PreviousBaseTypeName = TEXT("UB_UINT");
HLSLBaseOffset = AbsoluteMemberOffset + HLSLMemberSize;
auto GetBindlessPrefix = [](EUniformBufferBaseType InBaseType)
{
if (InBaseType == UBMT_SAMPLER)
{
return FShaderParameterParser::kBindlessSamplerPrefix;
}
if (InBaseType == UBMT_UAV || InBaseType == UBMT_RDG_TEXTURE_UAV || InBaseType == UBMT_RDG_BUFFER_UAV)
{
return FShaderParameterParser::kBindlessUAVPrefix;
}
return FShaderParameterParser::kBindlessSRVPrefix;
};
// Generate the member declaration.
const TCHAR* MemberPrefix = GetBindlessPrefix(Member.GetBaseType());
TStringBuilder<256> ParameterName;
if (!GlobalPrefix.IsEmpty())
{
ParameterName << GlobalPrefix << TEXT("_");
}
ParameterName << Member.GetName();
Decl.ConstantBufferMembers.Appendf(TEXT("\tUB_UINT() UB_CB_PREFIXED_MEMBER_NAME(%s, %s, %s);\n"), *UniformBufferName, MemberPrefix, *ParameterName);
}
else
{
// Generate the base type name.
const TCHAR* BaseTypeName = TEXT("");
switch (Member.GetBaseType())
{
case UBMT_INT32: BaseTypeName = TEXT("UB_INT"); break;
case UBMT_UINT32: BaseTypeName = TEXT("UB_UINT"); break;
case UBMT_FLOAT32:
if (Member.GetPrecision() == EShaderPrecisionModifier::Float)
{
BaseTypeName = TEXT("UB_FLOAT");
}
else if (Member.GetPrecision() == EShaderPrecisionModifier::Half)
{
BaseTypeName = TEXT("UB_HALF_FLOAT");
}
else if (Member.GetPrecision() == EShaderPrecisionModifier::Fixed)
{
BaseTypeName = TEXT("UB_FIXED_FLOAT");
}
break;
default: UE_LOG(LogShaders, Fatal, TEXT("Unrecognized uniform buffer struct member base type."));
};
// Generate the type dimensions for vectors and matrices.
TStringBuilder<16> TypeDim;
uint32 HLSLMemberSize = 4;
if (Member.GetNumRows() > 1)
{
TypeDim.Appendf(TEXT("%ux%u"), Member.GetNumRows(), Member.GetNumColumns());
// Each row of a matrix is 16 byte aligned.
HLSLMemberSize = (Member.GetNumRows() - 1) * 16 + Member.GetNumColumns() * 4;
}
else if (Member.GetNumColumns() > 1)
{
TypeDim.Appendf(TEXT("%u"), Member.GetNumColumns());
HLSLMemberSize = Member.GetNumColumns() * 4;
}
// Array elements are 16 byte aligned.
if (Member.GetNumElements() > 0)
{
HLSLMemberSize = (Member.GetNumElements() - 1) * Align(HLSLMemberSize, 16) + HLSLMemberSize;
}
const uint32 AbsoluteMemberOffset = StructOffset + Member.GetOffset();
// If the HLSL offset doesn't match the C++ offset, generate padding to fix it.
if (HLSLBaseOffset != AbsoluteMemberOffset)
{
check(HLSLBaseOffset < AbsoluteMemberOffset);
while (HLSLBaseOffset < AbsoluteMemberOffset)
{
Decl.ConstantBufferMembers.Appendf(TEXT("\t%s() UB_CB_MEMBER_NAME(%s, Padding%u);\n"), PreviousBaseTypeName, *UniformBufferName, HLSLBaseOffset);
HLSLBaseOffset += 4;
};
check(HLSLBaseOffset == AbsoluteMemberOffset);
}
PreviousBaseTypeName = BaseTypeName;
HLSLBaseOffset = AbsoluteMemberOffset + HLSLMemberSize;
TStringBuilder<256> ParameterName;
if (!GlobalPrefix.IsEmpty())
{
ParameterName << GlobalPrefix << TEXT("_");
}
ParameterName << Member.GetName();
Decl.ConstantBufferMembers.Appendf(TEXT("\t%s(%s) UB_CB_MEMBER_NAME(%s, %s%s);\n"), BaseTypeName, *TypeDim, *UniformBufferName, *ParameterName, *ArrayDim);
AddStructMember(Member, false);
}
}
for (int32 MemberIndex = 0; MemberIndex < StructMembers.Num(); ++MemberIndex)
{
const FShaderParametersMetadata::FMember& Member = StructMembers[MemberIndex];
if (IsShaderParameterTypeForUniformBufferLayout(Member.GetBaseType()))
{
// TODO: handle arrays?
checkf(!IsRDGResourceAccessType(Member.GetBaseType()), TEXT("RDG access parameter types (e.g. RDG_TEXTURE_ACCESS) are not allowed in uniform buffers."));
TStringBuilder<256> ParameterName;
if (!GlobalPrefix.IsEmpty())
{
ParameterName << GlobalPrefix << TEXT("_");
}
ParameterName << Member.GetName();
if (Member.GetBaseType() == UBMT_SAMPLER)
{
Decl.ResourceMembers.Appendf(TEXT("UB_RESOURCE_MEMBER_SAMPLER(%s, %s, %s);\n"), Member.GetShaderType(), *UniformBufferName, *ParameterName);
AddStructMember(Member, true);
}
else if (Member.GetBaseType() == UBMT_UAV || Member.GetBaseType() == UBMT_RDG_TEXTURE_UAV || Member.GetBaseType() == UBMT_RDG_BUFFER_UAV)
{
Decl.ResourceMembers.Appendf(TEXT("UB_RESOURCE_MEMBER_UAV(%s, %s, %s);\n"), Member.GetShaderType(), *UniformBufferName, *ParameterName);
AddStructMember(Member, true);
}
else
{
Decl.ResourceMembers.Appendf(TEXT("UB_RESOURCE_MEMBER_SRV(%s, %s, %s);\n"), Member.GetShaderType(), *UniformBufferName, *ParameterName);
AddStructMember(Member, true);
}
}
}
}
/** Creates a HLSL declaration of a uniform buffer with the given structure. */
static FString CreateHLSLUniformBufferDeclaration(const TCHAR* UniformBufferName, const FShaderParametersMetadata& UniformBufferStruct, const FRHIUniformBufferShaderBindingLayout* UniformBufferSBLayout)
{
// If the uniform buffer has no members, we don't want to write out anything. Shader compilers throw errors when faced with empty cbuffers and structs.
if (UniformBufferStruct.GetMembers().Num() > 0)
{
FUniformBufferDecl Decl;
uint32 HLSLBaseOffset = 0;
CreateHLSLUniformBufferStructMembersDeclaration(UniformBufferStruct, UniformBufferName, TEXT(""), TEXT(""), 0, Decl, HLSLBaseOffset);
if (UniformBufferSBLayout && UniformBufferSBLayout->RegisterSpace > 0)
{
return FString::Printf(
TEXT("#pragma once\n")
TEXT("UB_STATIC_CB_DEFINITION_START(%s,%d,%d)\n")
TEXT("%s")
TEXT("UB_CB_DEFINITION_END(%s)\n")
TEXT("%s")
TEXT("UniformBuffer %s\n")
TEXT("{\n")
TEXT("%s")
TEXT("};\n"),
UniformBufferName,
UniformBufferSBLayout->CBVResourceIndex,
UniformBufferSBLayout->RegisterSpace,
*Decl.ConstantBufferMembers,
UniformBufferName,
*Decl.ResourceMembers,
UniformBufferName,
*Decl.StructMembers
);
}
else
{
return FString::Printf(
TEXT("#pragma once\n")
TEXT("UB_CB_DEFINITION_START(%s)\n")
TEXT("%s")
TEXT("UB_CB_DEFINITION_END(%s)\n")
TEXT("%s")
TEXT("UniformBuffer %s\n")
TEXT("{\n")
TEXT("%s")
TEXT("};\n"),
UniformBufferName,
*Decl.ConstantBufferMembers,
UniformBufferName,
*Decl.ResourceMembers,
UniformBufferName,
*Decl.StructMembers
);
}
}
return FString(TEXT("\n"));
}
FString UE::ShaderParameters::CreateUniformBufferShaderDeclaration(const TCHAR* UniformBufferName, const FShaderParametersMetadata& UniformBufferStruct, const FRHIUniformBufferShaderBindingLayout* UniformBufferSBLayout)
{
return CreateHLSLUniformBufferDeclaration(UniformBufferName, UniformBufferStruct, UniformBufferSBLayout);
}
void UE::ShaderParameters::AddUniformBufferIncludesToEnvironment(FShaderCompilerEnvironment& OutEnvironment, const TSet<const FShaderParametersMetadata*>& InUniformBuffers)
{
TRACE_CPUPROFILER_EVENT_SCOPE(UE::ShaderParameters::AddUniformBufferIncludesToEnvironment);
FString UniformBufferIncludes;
auto AddUniformBufferData = [&OutEnvironment, &UniformBufferIncludes](const FShaderParametersMetadata* Metadata, FThreadSafeSharedAnsiStringPtr UniformBufferDeclaration)
{
FStringView UniformBufferNameView(Metadata->GetShaderVariableName());
uint32 UniformBufferNameHash = GetTypeHash(UniformBufferNameView);
if (!OutEnvironment.UniformBufferMap.FindByHash(UniformBufferNameHash, UniformBufferNameView))
{
check(UniformBufferDeclaration.Get() != NULL);
check(!UniformBufferDeclaration.Get()->IsEmpty());
UniformBufferIncludes += Metadata->GetUniformBufferInclude();
OutEnvironment.IncludeVirtualPathToSharedContentsMap.AddByHash(Metadata->GetUniformBufferPathHash(), Metadata->GetUniformBufferPath(), UniformBufferDeclaration);
Metadata->AddResourceTableEntries(OutEnvironment.ResourceTableMap, OutEnvironment.UniformBufferMap);
}
};
// If there is a shader binding layout then always add all the entries (duplicated from InUniformBuffers will not be added twice because of Find function below)
if (OutEnvironment.ShaderBindingLayout)
{
for (auto Iter = OutEnvironment.ShaderBindingLayout->GetUniformBufferDeclarations().CreateConstIterator(); Iter; ++Iter)
{
const FShaderParametersMetadata* UniformBufferStruct = FindUniformBufferStructByName(*Iter.Key());
if (UniformBufferStruct)
{
AddUniformBufferData(UniformBufferStruct, Iter.Value());
}
}
}
for (const FShaderParametersMetadata* Metadata : InUniformBuffers)
{
FStringView UniformBufferNameView(Metadata->GetShaderVariableName());
uint32 UniformBufferNameHash = GetTypeHash(UniformBufferNameView);
if (!OutEnvironment.UniformBufferMap.FindByHash(UniformBufferNameHash, UniformBufferNameView))
{
FThreadSafeSharedAnsiStringPtr UniformBufferDeclaration = Metadata->GetUniformBufferDeclarationAnsiPtr();
AddUniformBufferData(Metadata, UniformBufferDeclaration);
}
}
FString& GeneratedUniformBuffersInclude = OutEnvironment.IncludeVirtualPathToContentsMap.FindOrAdd("/Engine/Generated/GeneratedUniformBuffers.ush");
GeneratedUniformBuffersInclude.Append(UniformBufferIncludes);
}
void FShaderType::AddUniformBufferIncludesToEnvironment(FShaderCompilerEnvironment& OutEnvironment, EShaderPlatform Platform) const
{
TRACE_CPUPROFILER_EVENT_SCOPE(FShaderType::AddReferencedUniformBufferIncludes);
UE::ShaderParameters::AddUniformBufferIncludesToEnvironment(OutEnvironment, ReferencedUniformBuffers);
}
#endif // WITH_EDITOR
void FShaderType::DumpDebugInfo()
{
UE_LOG(LogConsoleResponse, Display, TEXT("----------------------------- GLobalShader %s"), GetName());
UE_LOG(LogConsoleResponse, Display, TEXT(" :Target %s"), GetShaderFrequencyString(GetFrequency()));
UE_LOG(LogConsoleResponse, Display, TEXT(" :TotalPermutationCount %d"), TotalPermutationCount);
#if WITH_EDITOR
UE_LOG(LogConsoleResponse, Display, TEXT(" :SourceHash %s"), *GetSourceHash(GMaxRHIShaderPlatform).ToString());
#endif
switch (ShaderTypeForDynamicCast)
{
case EShaderTypeForDynamicCast::Global:
UE_LOG(LogConsoleResponse, Display, TEXT(" :ShaderType Global"));
break;
case EShaderTypeForDynamicCast::Material:
UE_LOG(LogConsoleResponse, Display, TEXT(" :ShaderType Material"));
break;
case EShaderTypeForDynamicCast::MeshMaterial:
UE_LOG(LogConsoleResponse, Display, TEXT(" :ShaderType MeshMaterial"));
break;
case EShaderTypeForDynamicCast::Niagara:
UE_LOG(LogConsoleResponse, Display, TEXT(" :ShaderType Niagara"));
break;
}
#if 0
UE_LOG(LogConsoleResponse, Display, TEXT(" --- %d shaders"), ShaderIdMap.Num());
int32 Index = 0;
for (auto& KeyValue : ShaderIdMap)
{
UE_LOG(LogConsoleResponse, Display, TEXT(" --- shader %d"), Index);
FShader* Shader = KeyValue.Value;
Shader->DumpDebugInfo();
Index++;
}
#endif
}
#if WITH_EDITOR
void FShaderType::GetShaderStableKeyParts(FStableShaderKeyAndValue& SaveKeyVal)
{
static FName NAME_Material(TEXT("Material"));
static FName NAME_MeshMaterial(TEXT("MeshMaterial"));
static FName NAME_Niagara(TEXT("Niagara"));
switch (ShaderTypeForDynamicCast)
{
case EShaderTypeForDynamicCast::Global:
SaveKeyVal.ShaderClass = NAME_Global;
break;
case EShaderTypeForDynamicCast::Material:
SaveKeyVal.ShaderClass = NAME_Material;
break;
case EShaderTypeForDynamicCast::MeshMaterial:
SaveKeyVal.ShaderClass = NAME_MeshMaterial;
break;
case EShaderTypeForDynamicCast::Niagara:
SaveKeyVal.ShaderClass = NAME_Niagara;
break;
}
SaveKeyVal.ShaderType = FName(GetName() ? GetName() : TEXT("null"));
}
void FVertexFactoryType::AddUniformBufferIncludesToEnvironment(FShaderCompilerEnvironment& OutEnvironment, EShaderPlatform Platform) const
{
UE::ShaderParameters::AddUniformBufferIncludesToEnvironment(OutEnvironment, ReferencedUniformBuffers);
}
#endif // WITH_EDITOR