Files
UnrealEngineUWP/Engine/Source/Runtime/RenderCore/Private/ShaderParameters.cpp
christopher waters 1e01768e33 Bindless shader parameters
- Adding support for bindless resources/samplers. The loose resources have to be wrapped in a macro that generates an index and proxy resource when bindless is enabled. Reflection has to parse these differently from other resources since they're parsed as uint parameters. The runtime also has to bind these differently since they end up needing to find their real resource in the binding data and then update a constant buffer.
- To assist in conversions, the shader compiler will detect if a shader is compiled with both bindless and non-bindless resources/samplers and will emit errors if that is the case.
- Adding support for bindless Uniform Buffer resources/samplers. Because Uniform Buffers aren't per-shader, they'll always update their constants with the bindless indices of their resources.
- Adding more flags to FShaderCodePackedResourceCounts required changing all the array initializations to individual parameters since not all the new flags are used everywhere.
- No shaders have been configured to support bindless resources/samplers. Yet.

#jira UE-139616
#rb zach.bethel, arciel.rekman
#preflight 6282b8ec44349a6581a21a39

[CL 20250348 by christopher waters in ue5-main branch]
2022-05-17 16:30:48 -04:00

585 lines
24 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
/*=============================================================================
ShaderParameters.cpp: Shader parameter implementation.
=============================================================================*/
#include "ShaderParameters.h"
#include "Containers/List.h"
#include "UniformBuffer.h"
#include "ShaderCore.h"
#include "Shader.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(FRWShaderParameter);
IMPLEMENT_TYPE_LAYOUT(FShaderUniformBufferParameter);
void FShaderParameter::Bind(const FShaderParameterMap& ParameterMap,const TCHAR* ParameterName,EShaderParameterFlags Flags)
{
if (!ParameterMap.FindParameterAllocation(ParameterName,BufferIndex,BaseIndex,NumBytes) && Flags == SPF_Mandatory)
{
if (!UE_LOG_ACTIVE(LogShaders, Log))
{
UE_LOG(LogShaders, Fatal,TEXT("Failure to bind non-optional shader parameter %s! The parameter is either not present in the shader, or the shader compiler optimized it out."),ParameterName);
}
else
{
// 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"));
}
}
}
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)
{
uint16 UnusedBufferIndex = 0;
if(!ParameterMap.FindParameterAllocation(ParameterName,UnusedBufferIndex,BaseIndex,NumResources) && Flags == SPF_Mandatory)
{
if (!UE_LOG_ACTIVE(LogShaders, Log))
{
UE_LOG(LogShaders, Fatal,TEXT("Failure to bind non-optional shader resource parameter %s! The parameter is either not present in the shader, or the shader compiler optimized it out."),ParameterName);
}
else
{
// 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"));
}
}
}
FArchive& operator<<(FArchive& Ar,FShaderResourceParameter& P)
{
return Ar << P.BaseIndex << P.NumResources;
}
void FShaderUniformBufferParameter::ModifyCompilationEnvironment(const TCHAR* ParameterName,const FShaderParametersMetadata& Struct,EShaderPlatform Platform,FShaderCompilerEnvironment& OutEnvironment)
{
const FString IncludeName = FString::Printf(TEXT("/Engine/Generated/UniformBuffers/%s.ush"),ParameterName);
// Add the uniform buffer declaration to the compilation environment as an include: UniformBuffers/<ParameterName>.usf
FString Declaration;
CreateUniformBufferShaderDeclaration(ParameterName, Struct, Platform, Declaration);
OutEnvironment.IncludeVirtualPathToContentsMap.Add(IncludeName, Declaration);
FString& GeneratedUniformBuffersInclude = OutEnvironment.IncludeVirtualPathToContentsMap.FindOrAdd("/Engine/Generated/GeneratedUniformBuffers.ush");
FString Include = FString::Printf(TEXT("#include \"/Engine/Generated/UniformBuffers/%s.ush\"") LINE_TERMINATOR, ParameterName);
GeneratedUniformBuffersInclude.Append(Include);
Struct.AddResourceTableEntries(OutEnvironment.ResourceTableMap, OutEnvironment.UniformBufferMap);
}
void FShaderUniformBufferParameter::Bind(const FShaderParameterMap& ParameterMap,const TCHAR* ParameterName,EShaderParameterFlags Flags)
{
uint16 UnusedBaseIndex = 0;
uint16 UnusedNumBytes = 0;
if(!ParameterMap.FindParameterAllocation(ParameterName,BaseIndex,UnusedBaseIndex,UnusedNumBytes))
{
BaseIndex = 0xffff;
if(Flags == SPF_Mandatory)
{
if (!UE_LOG_ACTIVE(LogShaders, Log))
{
UE_LOG(LogShaders, Fatal,TEXT("Failure to bind non-optional shader resource parameter %s! The parameter is either not present in the shader, or the shader compiler optimized it out."),ParameterName);
}
else
{
// 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"));
}
}
}
else
{
check(IsBound());
}
}
/** The individual bits of a uniform buffer declaration. */
struct FUniformBufferDecl
{
/** Members to place in the constant buffer. */
FString ConstantBufferMembers;
/** Members to place in the resource table. */
FString ResourceMembers;
/** Members in the struct HLSL shader code will access. */
FString StructMembers;
/** The HLSL initializer that will copy constants and resources in to the struct. */
FString Initializer;
};
/** Generates a HLSL struct declaration for a uniform buffer struct. */
static void CreateHLSLUniformBufferStructMembersDeclaration(
const FShaderParametersMetadata& UniformBufferStruct,
const FString& NamePrefix,
uint32 StructOffset,
EShaderPlatform Platform,
FUniformBufferDecl& Decl,
uint32& HLSLBaseOffset)
{
const TArray<FShaderParametersMetadata::FMember>& StructMembers = UniformBufferStruct.GetMembers();
FString PreviousBaseTypeName = TEXT("float");
for (int32 MemberIndex = 0; MemberIndex < StructMembers.Num(); ++MemberIndex)
{
const FShaderParametersMetadata::FMember& Member = StructMembers[MemberIndex];
FString ArrayDim;
if(Member.GetNumElements() > 0)
{
ArrayDim = FString::Printf(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."));
Decl.StructMembers += TEXT("struct {\r\n");
Decl.Initializer += TEXT("{");
CreateHLSLUniformBufferStructMembersDeclaration(*Member.GetStructMetadata(), FString::Printf(TEXT("%s%s_"), *NamePrefix, Member.GetName()), StructOffset + Member.GetOffset(), Platform, Decl, HLSLBaseOffset);
Decl.Initializer += TEXT("},");
Decl.StructMembers += FString::Printf(TEXT("} %s%s;\r\n"),Member.GetName(),*ArrayDim);
}
else if (Member.GetBaseType() == UBMT_INCLUDED_STRUCT)
{
CreateHLSLUniformBufferStructMembersDeclaration(*Member.GetStructMetadata(), NamePrefix, StructOffset + Member.GetOffset(), Platform, Decl, HLSLBaseOffset);
}
else if (IsShaderParameterTypeForUniformBufferLayout(Member.GetBaseType()))
{
// Generate the type dimensions for vectors and matrices.
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 += FString::Printf(TEXT("\t%s PrePadding_%s%u;\r\n"), *PreviousBaseTypeName, *NamePrefix, HLSLBaseOffset);
HLSLBaseOffset += 4;
};
check(HLSLBaseOffset == AbsoluteMemberOffset);
}
PreviousBaseTypeName = TEXT("uint");
HLSLBaseOffset = AbsoluteMemberOffset + HLSLMemberSize;
// Generate the member declaration.
const FString ParameterName = FString::Printf(TEXT("%s%s"), *NamePrefix, Member.GetName());
if (Member.GetBaseType() == UBMT_SAMPLER)
{
Decl.ConstantBufferMembers += FString::Printf(TEXT("\tDEFINE_SAMPLER_INDEX(%s);\r\n"), *ParameterName);
}
else
{
Decl.ConstantBufferMembers += FString::Printf(TEXT("\tDEFINE_RESOURCE_INDEX(%s);\r\n"), *ParameterName);
}
}
else
{
// Generate the base type name.
FString BaseTypeName;
switch(Member.GetBaseType())
{
case UBMT_INT32: BaseTypeName = TEXT("int"); break;
case UBMT_UINT32: BaseTypeName = TEXT("uint"); break;
case UBMT_FLOAT32:
if (Member.GetPrecision() == EShaderPrecisionModifier::Float || !SupportShaderPrecisionModifier(Platform))
{
BaseTypeName = TEXT("float");
}
else if (Member.GetPrecision() == EShaderPrecisionModifier::Half)
{
BaseTypeName = TEXT("half");
}
else if (Member.GetPrecision() == EShaderPrecisionModifier::Fixed)
{
BaseTypeName = TEXT("fixed");
}
break;
default: UE_LOG(LogShaders, Fatal,TEXT("Unrecognized uniform buffer struct member base type."));
};
// Generate the type dimensions for vectors and matrices.
FString TypeDim;
uint32 HLSLMemberSize = 4;
if(Member.GetNumRows() > 1)
{
TypeDim = FString::Printf(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 = FString::Printf(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 += FString::Printf(TEXT("\t%s PrePadding_%s%u;\r\n"), *PreviousBaseTypeName, *NamePrefix, HLSLBaseOffset);
HLSLBaseOffset += 4;
};
check(HLSLBaseOffset == AbsoluteMemberOffset);
}
PreviousBaseTypeName = BaseTypeName;
HLSLBaseOffset = AbsoluteMemberOffset + HLSLMemberSize;
// Generate the member declaration.
FString ParameterName = FString::Printf(TEXT("%s%s"),*NamePrefix,Member.GetName());
Decl.ConstantBufferMembers += FString::Printf(TEXT("\t%s%s %s%s;\r\n"),*BaseTypeName,*TypeDim,*ParameterName,*ArrayDim);
Decl.StructMembers += FString::Printf(TEXT("\t%s%s %s%s;\r\n"),*BaseTypeName,*TypeDim,Member.GetName(),*ArrayDim);
Decl.Initializer += FString::Printf(TEXT("%s,"),*ParameterName);
}
}
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."));
const FString ParameterName = FString::Printf(TEXT("%s%s"), *NamePrefix, Member.GetName());
if (Member.GetBaseType() == UBMT_SAMPLER)
{
Decl.ResourceMembers += FString::Printf(TEXT("DEFINE_SAMPLER_VARIABLE(%s, %s);\r\n"), Member.GetShaderType(), *ParameterName);
Decl.StructMembers += FString::Printf(TEXT("\t%s %s;\r\n"), Member.GetShaderType(), Member.GetName());
Decl.Initializer += FString::Printf(TEXT("GET_SAMPLER(%s),"), *ParameterName);
}
else if (Member.GetBaseType() == UBMT_SRV)
{
Decl.ResourceMembers += FString::Printf(TEXT("PLATFORM_SUPPORTS_SRV_UB_MACRO( DEFINE_RESOURCE_VARIABLE(%s, %s); ) \r\n"), Member.GetShaderType(), *ParameterName);
Decl.StructMembers += FString::Printf(TEXT("\tPLATFORM_SUPPORTS_SRV_UB_MACRO( %s %s; ) \r\n"), Member.GetShaderType(), Member.GetName());
Decl.Initializer += FString::Printf(TEXT(" PLATFORM_SUPPORTS_SRV_UB_MACRO( GET_RESOURCE(%s), ) "), *ParameterName);
}
else
{
Decl.ResourceMembers += FString::Printf(TEXT("DEFINE_RESOURCE_VARIABLE(%s, %s);\r\n"), Member.GetShaderType(), *ParameterName);
Decl.StructMembers += FString::Printf(TEXT("\t%s %s;\r\n"), Member.GetShaderType(), Member.GetName());
Decl.Initializer += FString::Printf(TEXT("GET_RESOURCE(%s),"), *ParameterName);
}
}
}
}
/** Creates a HLSL declaration of a uniform buffer with the given structure. */
static FString CreateHLSLUniformBufferDeclaration(const TCHAR* Name,const FShaderParametersMetadata& UniformBufferStruct, EShaderPlatform Platform)
{
// 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)
{
FString NamePrefix(FString(Name) + FString(TEXT("_")));
FUniformBufferDecl Decl;
uint32 HLSLBaseOffset = 0;
CreateHLSLUniformBufferStructMembersDeclaration(UniformBufferStruct, NamePrefix, 0, Platform, Decl, HLSLBaseOffset);
return FString::Printf(
TEXT("#ifndef __UniformBuffer_%s_Definition__\r\n")
TEXT("#define __UniformBuffer_%s_Definition__\r\n")
TEXT("cbuffer UB_CB_NAME(%s)\r\n")
TEXT("{\r\n")
TEXT("%s")
TEXT("}\r\n")
TEXT("%s")
TEXT("static const struct\r\n")
TEXT("{\r\n")
TEXT("%s")
TEXT("} %s = {%s};\r\n")
TEXT("#endif\r\n"),
Name,
Name,
Name,
*Decl.ConstantBufferMembers,
*Decl.ResourceMembers,
*Decl.StructMembers,
Name,
*Decl.Initializer
);
}
return FString(TEXT("\n"));
}
RENDERCORE_API void CreateUniformBufferShaderDeclaration(const TCHAR* Name,const FShaderParametersMetadata& UniformBufferStruct, EShaderPlatform Platform, FString& OutDeclaration)
{
OutDeclaration = CreateHLSLUniformBufferDeclaration(Name, UniformBufferStruct, Platform);
}
RENDERCORE_API void CacheUniformBufferIncludes(TMap<const TCHAR*,FCachedUniformBufferDeclaration>& Cache, EShaderPlatform Platform)
{
for (TMap<const TCHAR*,FCachedUniformBufferDeclaration>::TIterator It(Cache); It; ++It)
{
FCachedUniformBufferDeclaration& BufferDeclaration = It.Value();
check(BufferDeclaration.Declaration.Get() == NULL);
for (TLinkedList<FShaderParametersMetadata*>::TIterator StructIt(FShaderParametersMetadata::GetStructList()); StructIt; StructIt.Next())
{
if (It.Key() == StructIt->GetShaderVariableName())
{
FString* NewDeclaration = new FString();
CreateUniformBufferShaderDeclaration(StructIt->GetShaderVariableName(), **StructIt, Platform, *NewDeclaration);
check(!NewDeclaration->IsEmpty());
BufferDeclaration.Declaration = MakeShareable(NewDeclaration);
break;
}
}
}
}
static const uint32 NumUniformBufferLocks = 16u;
static FRWLock UniformBufferLocks[NumUniformBufferLocks];
void FShaderType::FlushShaderFileCache(const TMap<FString, TArray<const TCHAR*> >& ShaderFileToUniformBufferVariables)
{
if (CachedUniformBufferPlatform != SP_NumPlatforms)
{
const uint32 LockIndex = HashedName.GetHash() % NumUniformBufferLocks;
FWriteScopeLock Lock(UniformBufferLocks[LockIndex]);
if (CachedUniformBufferPlatform != SP_NumPlatforms)
{
ReferencedUniformBufferStructsCache.Empty();
GenerateReferencedUniformBuffers(SourceFilename, Name, ShaderFileToUniformBufferVariables, ReferencedUniformBufferStructsCache);
CachedUniformBufferPlatform = SP_NumPlatforms;
}
}
}
void FShaderType::AddReferencedUniformBufferIncludes(FShaderCompilerEnvironment& OutEnvironment, FString& OutSourceFilePrefix, EShaderPlatform Platform) const
{
TRACE_CPUPROFILER_EVENT_SCOPE(FShaderType::AddReferencedUniformBufferIncludes);
const uint32 LockIndex = HashedName.GetHash() % NumUniformBufferLocks;
TOptional<FReadScopeLock> ReadScopeLock; // TOptional is so we can unlock and lock manually; the FReadScopeLock api does not support that
ReadScopeLock.Emplace(UniformBufferLocks[LockIndex]);
for (;;)
{
// Cache uniform buffer struct declarations referenced by this shader type's files
if (CachedUniformBufferPlatform == Platform)
{
break;
}
// Drop the ReadLock, acquire a write lock
// Write the ReferencedUniformBufferStructsCache for the new Platform
// Drop the WriteLock, reacquire the ReadLock
ReadScopeLock.Reset();
{
FWriteScopeLock WriteScopeLock(UniformBufferLocks[LockIndex]);
if (CachedUniformBufferPlatform != Platform)
{
// If there is already a cache but for another platform, keep the keys but reset the values
if (CachedUniformBufferPlatform != SP_NumPlatforms)
{
for (TMap<const TCHAR*, FCachedUniformBufferDeclaration>::TIterator It(ReferencedUniformBufferStructsCache); It; ++It)
{
It.Value() = FCachedUniformBufferDeclaration();
}
}
CacheUniformBufferIncludes(ReferencedUniformBufferStructsCache, Platform);
CachedUniformBufferPlatform = Platform;
}
}
ReadScopeLock.Emplace(UniformBufferLocks[LockIndex]);
}
FString UniformBufferIncludes;
for (TMap<const TCHAR*,FCachedUniformBufferDeclaration>::TConstIterator It(ReferencedUniformBufferStructsCache); It; ++It)
{
const FCachedUniformBufferDeclaration& Value = It.Value();
check(Value.Declaration.Get() != NULL);
check(!Value.Declaration.Get()->IsEmpty());
UniformBufferIncludes += FString::Printf(TEXT("#include \"/Engine/Generated/UniformBuffers/%s.ush\"") LINE_TERMINATOR, It.Key());
OutEnvironment.IncludeVirtualPathToExternalContentsMap.Add(
FString::Printf(TEXT("/Engine/Generated/UniformBuffers/%s.ush"),It.Key()),
Value.Declaration
);
for (TLinkedList<FShaderParametersMetadata*>::TIterator StructIt(FShaderParametersMetadata::GetStructList()); StructIt; StructIt.Next())
{
if (It.Key() == StructIt->GetShaderVariableName())
{
StructIt->AddResourceTableEntries(OutEnvironment.ResourceTableMap, OutEnvironment.UniformBufferMap);
}
}
}
FString& GeneratedUniformBuffersInclude = OutEnvironment.IncludeVirtualPathToContentsMap.FindOrAdd("/Engine/Generated/GeneratedUniformBuffers.ush");
GeneratedUniformBuffersInclude.Append(UniformBufferIncludes);
OutEnvironment.SetDefine(TEXT("PLATFORM_SUPPORTS_SRV_UB"), TEXT("1"));
}
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
}
void FShaderType::GetShaderStableKeyParts(FStableShaderKeyAndValue& SaveKeyVal)
{
#if WITH_EDITOR
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"));
#endif
}
void FVertexFactoryType::FlushShaderFileCache(const TMap<FString, TArray<const TCHAR*> >& ShaderFileToUniformBufferVariables)
{
if (CachedUniformBufferPlatform != SP_NumPlatforms)
{
const uint32 LockIndex = HashedName.GetHash() % NumUniformBufferLocks;
FWriteScopeLock Lock(UniformBufferLocks[LockIndex]);
if (CachedUniformBufferPlatform != SP_NumPlatforms)
{
ReferencedUniformBufferStructsCache.Empty();
GenerateReferencedUniformBuffers(ShaderFilename, Name, ShaderFileToUniformBufferVariables, ReferencedUniformBufferStructsCache);
CachedUniformBufferPlatform = SP_NumPlatforms;
}
}
}
void FVertexFactoryType::AddReferencedUniformBufferIncludes(FShaderCompilerEnvironment& OutEnvironment, FString& OutSourceFilePrefix, EShaderPlatform Platform) const
{
const uint32 LockIndex = HashedName.GetHash() % NumUniformBufferLocks;
TOptional<FReadScopeLock> ReadScopeLock; // TOptional is so we can unlock and lock manually; the FReadScopeLock api does not support that
ReadScopeLock.Emplace(UniformBufferLocks[LockIndex]);
for (;;)
{
// Cache uniform buffer struct declarations referenced by this shader type's files
if (CachedUniformBufferPlatform == Platform)
{
break;
}
// Drop the ReadLock, acquire a write lock
// Write the ReferencedUniformBufferStructsCache for the new Platform
// Drop the WriteLock, reacquire the ReadLock
ReadScopeLock.Reset();
{
FWriteScopeLock WriteScopeLock(UniformBufferLocks[LockIndex]);
if (CachedUniformBufferPlatform != Platform)
{
// If there is already a cache but for another platform, keep the keys but reset the values
if (CachedUniformBufferPlatform != SP_NumPlatforms)
{
for (TMap<const TCHAR*, FCachedUniformBufferDeclaration>::TIterator It(ReferencedUniformBufferStructsCache); It; ++It)
{
It.Value() = FCachedUniformBufferDeclaration();
}
}
CacheUniformBufferIncludes(ReferencedUniformBufferStructsCache, Platform);
CachedUniformBufferPlatform = Platform;
}
}
ReadScopeLock.Emplace(UniformBufferLocks[LockIndex]);
}
FString UniformBufferIncludes;
for (TMap<const TCHAR*,FCachedUniformBufferDeclaration>::TConstIterator It(ReferencedUniformBufferStructsCache); It; ++It)
{
check(It.Value().Declaration.Get() != NULL);
check(!It.Value().Declaration.Get()->IsEmpty());
UniformBufferIncludes += FString::Printf(TEXT("#include \"/Engine/Generated/UniformBuffers/%s.ush\"") LINE_TERMINATOR, It.Key());
OutEnvironment.IncludeVirtualPathToExternalContentsMap.Add(
*FString::Printf(TEXT("/Engine/Generated/UniformBuffers/%s.ush"),It.Key()),
It.Value().Declaration
);
for (TLinkedList<FShaderParametersMetadata*>::TIterator StructIt(FShaderParametersMetadata::GetStructList()); StructIt; StructIt.Next())
{
if (It.Key() == StructIt->GetShaderVariableName())
{
StructIt->AddResourceTableEntries(OutEnvironment.ResourceTableMap, OutEnvironment.UniformBufferMap);
}
}
}
FString& GeneratedUniformBuffersInclude = OutEnvironment.IncludeVirtualPathToContentsMap.FindOrAdd("/Engine/Generated/GeneratedUniformBuffers.ush");
GeneratedUniformBuffersInclude.Append(UniformBufferIncludes);
OutEnvironment.SetDefine(TEXT("PLATFORM_SUPPORTS_SRV_UB"), TEXT("1"));
}