Files
UnrealEngineUWP/Engine/Source/Runtime/RenderCore/Private/GlobalShader.cpp
dan elksnitis c4deae64ff [shaders] bytecode sharing between shader and shadermap DDC buckets
- create utility classes (serialization context objects) which allow serialization of shader code blobs independently of "shader object" (i.e. whatever type of thing we're pushing to the cache, shader job or shader map) structure
- commonize bytecode format in cache entries for shadermap and shader DDC (FShaderCodeResource struct)
- rework per-shader caching to use the serialization helpers to separate serialization of job output struct data and bytecode
- rework material and global shadermap DDC serialization to use the serialization helpers to separate serialization of shadermap structural data and bytecode

#rb Laura.Hermanns, Zousar.Shaker
#jira UE-196556

(resubmit with monolithic build fix)

[CL 36153164 by dan elksnitis in 5.5 branch]
2024-09-10 13:06:22 -04:00

850 lines
27 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
/*=============================================================================
GlobalShader.cpp: Global shader implementation.
=============================================================================*/
#include "GlobalShader.h"
#include "Containers/StaticBitArray.h"
#include "Interfaces/ITargetPlatform.h"
#include "Interfaces/ITargetPlatformManagerModule.h"
#include "Misc/ConfigCacheIni.h"
#include "Misc/CoreMisc.h"
#include "Misc/Paths.h"
#include "Serialization/MemoryWriter.h"
#include "ShaderCodeLibrary.h"
#include "ShaderSerialization.h"
/** The global shader map. */
FGlobalShaderMap* GGlobalShaderMap[SP_NumPlatforms] = {};
IMPLEMENT_TYPE_LAYOUT(FGlobalShader);
IMPLEMENT_TYPE_LAYOUT(FGlobalShaderMapContent);
IMPLEMENT_SHADER_TYPE(,FNULLPS,TEXT("/Engine/Private/NullPixelShader.usf"),TEXT("Main"),SF_Pixel);
/** A per-platform map of global shader defines that come from config .ini */
class FGlobalShaderConfigDefines
{
public:
static void ApplyConfigDefines(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
FName ShaderFormat;
if (const FPlatformInfo* Platform = GetPlatformInfoAndErrorCheck(Parameters.Platform, ShaderFormat))
{
if (const TArray<FDefine>* ShaderDefines = Platform->ShaderDefines.Find(Parameters.GlobalShaderName))
{
for (const FDefine& Define : *ShaderDefines)
{
if (Define.IsRelevant(ShaderFormat, Parameters.PermutationId))
{
OutEnvironment.SetDefineAndCompileArgument(*Define.Name, Define.Value);
}
}
}
}
}
static void AppendKeyString(FString& KeyString, FName ShaderName, int32 PermutationID, FName IniPlatform, EShaderPlatform ShaderPlatform)
{
FName ShaderFormat;
if (const FPlatformInfo* Platform = GetPlatformInfoAndErrorCheck(ShaderPlatform, ShaderFormat))
{
if (const TArray<FDefine>* ShaderDefines = Platform->ShaderDefines.Find(ShaderName))
{
TArray<FDefine> FilteredDefines;
for (const FDefine& Define : *ShaderDefines)
{
if (Define.IsRelevant(ShaderFormat, PermutationID))
{
FilteredDefines.Add(Define);
}
}
if (FilteredDefines.Num() > 0)
{
// Sort and hash the applicable defines
FilteredDefines.Sort();
FSHA1 HashState;
for (const FDefine& Define : FilteredDefines)
{
HashState.UpdateWithString(*Define.Name, Define.Name.Len());
HashState.UpdateWithString(*Define.Value, Define.Value.Len());
const uint32 ShaderFormatHash = GetTypeHash(Define.ShaderFormat);
HashState.Update((const uint8*)&ShaderFormatHash, sizeof(ShaderFormatHash));
HashState.Update((const uint8*)&Define.PermutationID, sizeof(Define.PermutationID));
}
KeyString += HashState.Finalize().ToString();
}
}
}
}
private:
struct FDefine
{
FString Name;
FString Value;
FName ShaderFormat = NAME_None;
int32 PermutationID = INDEX_NONE;
bool IsRelevant(FName InShaderFormat, int32 InPermutationID) const
{
if (ShaderFormat != NAME_None && ShaderFormat != InShaderFormat)
{
// This define is inappropriate for the target shader format
return false;
}
if (PermutationID != INDEX_NONE && PermutationID != InPermutationID)
{
// This define is inappropriate for the permutation being compiled
return false;
}
return true;
}
int32 Compare(const FDefine& Other) const
{
int Cmp = Name.Compare(Other.Name, ESearchCase::IgnoreCase);
if (Cmp == 0)
{
Cmp = Value.Compare(Other.Value, ESearchCase::CaseSensitive);
if (Cmp == 0)
{
Cmp = ShaderFormat.Compare(Other.ShaderFormat);
if (Cmp == 0)
{
Cmp = Other.PermutationID - PermutationID;
}
}
}
return Cmp;
}
bool operator==(const FDefine& Other) const
{
return Name.Equals(Other.Name, ESearchCase::IgnoreCase)
&& Value == Other.Value
&& ShaderFormat == Other.ShaderFormat
&& PermutationID == Other.PermutationID;
}
bool operator!=(const FDefine& Other) const
{
return !(*this == Other);
}
bool operator<(const FDefine& Other) const
{
return Compare(Other) < 0;
}
};
using FShaderDefines = TMap<FName, TArray<FDefine>>;
struct FPlatformInfo
{
FName Name;
bool bInitialized = false;
FShaderDefines ShaderDefines;
};
static TArray<FPlatformInfo> ConfigDefines;
static TStaticArray<const FPlatformInfo*, EShaderPlatform::SP_NumPlatforms> PerPlatformConfigs;
static TStaticBitArray<EShaderPlatform::SP_NumPlatforms> ErrorCheckedPlatforms;
static const FPlatformInfo* GetPlatformInfoAndErrorCheck(EShaderPlatform ShaderPlatform, FName& OutShaderFormat)
{
OutShaderFormat = LegacyShaderPlatformToShaderFormat(ShaderPlatform);
const TArray<ITargetPlatform*> AllPlatforms = GetTargetPlatformManagerRef().GetTargetPlatforms();
if (AllPlatforms.Num() != ConfigDefines.Num())
{
// if the number of platforms has changed since this was last called, we need to resize the array
// and reset PerPlatformConfigs since the pointers will now be invalid.
ConfigDefines.SetNum(AllPlatforms.Num());
for (uint16 SpIndex = 0; SpIndex < EShaderPlatform::SP_NumPlatforms; ++SpIndex)
{
PerPlatformConfigs[SpIndex] = nullptr;
}
}
if (PerPlatformConfigs[ShaderPlatform] != nullptr)
{
return PerPlatformConfigs[ShaderPlatform];
}
// Search for all target platforms that support this shader platform
TArray<const ITargetPlatform*, TInlineAllocator<16>> IniPlatforms;
for(const ITargetPlatform* TP : AllPlatforms)
{
check(TP);
TArray<FName> PlatformShaderFormats;
TP->GetAllPossibleShaderFormats(PlatformShaderFormats);
if (PlatformShaderFormats.Contains(OutShaderFormat))
{
IniPlatforms.AddUnique(TP);
}
}
if (IniPlatforms.Num() == 0)
{
// No found platforms that support this shader format
return nullptr;
}
// first add all platforms to populate the array
for (int32 PlatformIndex = 0; PlatformIndex < IniPlatforms.Num(); ++PlatformIndex)
{
int32 PlatformOrdinal = IniPlatforms[PlatformIndex]->GetPlatformOrdinal();
if (ConfigDefines.Num() < PlatformOrdinal + 1)
{
ConfigDefines.SetNum(PlatformOrdinal + 1);
}
FPlatformInfo& Platform = ConfigDefines[PlatformOrdinal];
if (!Platform.bInitialized)
{
InitializePlatform(Platform, IniPlatforms[PlatformIndex]);
check(Platform.bInitialized);
}
}
if (!ErrorCheckedPlatforms[ShaderPlatform])
{
ErrorCheckedPlatforms[ShaderPlatform] = true;
if (IniPlatforms.Num() > 1)
{
// This shader platform is shared by multiple target platforms that can be configured independently. We need to make sure all config defines
// match up, and that no platform-specific defines exist that might introduce shader compiler output that diverges between target platforms
const FPlatformInfo& Platform0 = ConfigDefines[IniPlatforms[0]->GetPlatformOrdinal()];
check(Platform0.bInitialized);
for (int32 PlatformIndex = 1; PlatformIndex < IniPlatforms.Num(); ++PlatformIndex)
{
const FPlatformInfo& OtherPlatform = ConfigDefines[IniPlatforms[PlatformIndex]->GetPlatformOrdinal()];
check(OtherPlatform.bInitialized);
ErrorCheckPlatformsForShaderFormat(Platform0, OtherPlatform, OutShaderFormat);
}
}
}
const FPlatformInfo& Platform = ConfigDefines[IniPlatforms[0]->GetPlatformOrdinal()];
PerPlatformConfigs[ShaderPlatform] = &Platform;
return &Platform;
}
static void InitializePlatform(FPlatformInfo& Platform, const ITargetPlatform* TP)
{
Platform.Name = FName(TP->IniPlatformName());
if (FConfigCacheIni* ConfigCache = FConfigCacheIni::ForPlatform(Platform.Name))
{
TArray<FString> DefineStrings;
ConfigCache->GetArray(TEXT("GlobalShaderDefines"), TEXT("Definitions"), DefineStrings, GEngineIni);
for (const FString& DefineString : DefineStrings)
{
FString ShaderName, DefineName;
if (FParse::Value(*DefineString, TEXT("Shader="), ShaderName) &&
FParse::Value(*DefineString, TEXT("Define="), DefineName))
{
FName ShaderFormat = NAME_None;
FParse::Value(*DefineString, TEXT("ShaderFormat="), ShaderFormat);
int32 PermutationID = INDEX_NONE;
FParse::Value(*DefineString, TEXT("PermutationID="), PermutationID);
FString DefineValue;
int32 EqualsIndex = INDEX_NONE;
if (DefineName.FindChar(TCHAR('='), EqualsIndex))
{
DefineValue = DefineName.Mid(EqualsIndex + 1);
DefineName.MidInline(0, EqualsIndex, EAllowShrinking::No);
}
const FShaderType* ShaderType = FindShaderTypeByName(ShaderName);
if (ShaderType == nullptr || ShaderType->GetGlobalShaderType() == nullptr)
{
// This global shader doesn't actually exist
UE_LOG(LogShaders, Warning, TEXT("Global shader definition '%s' found in engine config for global shader '%s', which does not exist"), *DefineName, *ShaderName);
continue;
}
TArray<FDefine>& ShaderDefines = Platform.ShaderDefines.FindOrAdd(FName(ShaderName));
FDefine* Define = ShaderDefines.FindByPredicate(
[&](const FDefine& Define)
{
return Define.Name.Equals(DefineName, ESearchCase::IgnoreCase) &&
Define.ShaderFormat == ShaderFormat &&
Define.PermutationID == PermutationID;
});
if (!Define)
{
Define = &ShaderDefines.AddDefaulted_GetRef();
Define->Name = DefineName;
Define->ShaderFormat = ShaderFormat;
Define->PermutationID = PermutationID;
}
Define->Value = DefineValue;
}
}
}
Platform.bInitialized = true;
}
static void ErrorCheckPlatformsForShaderFormat(const FPlatformInfo& PlatformA, const FPlatformInfo& PlatformB, FName ShaderFormat)
{
auto FilterShaderDefines = [ShaderFormat](const FShaderDefines& ShaderDefines) -> FShaderDefines
{
FShaderDefines FilteredShaderDefines;
for (const auto& ShaderIt : ShaderDefines)
{
TArray<FDefine> FilteredDefines = ShaderIt.Value.FilterByPredicate(
[ShaderFormat](const FDefine& Define)
{
return Define.ShaderFormat == NAME_None || Define.ShaderFormat == ShaderFormat;
});
if (FilteredDefines.Num() > 0)
{
// Sort the filtered defines for every shader for the purposes of comparison in error checking
FilteredDefines.Sort();
FilteredShaderDefines.Add(ShaderIt.Key, MoveTemp(FilteredDefines));
}
}
return MoveTemp(FilteredShaderDefines);
};
FShaderDefines FilteredDefinesA = FilterShaderDefines(PlatformA.ShaderDefines);
FShaderDefines FilteredDefinesB = FilterShaderDefines(PlatformB.ShaderDefines);
TSet<FName> ShadersWithError;
// Check if there are any keys in A that aren't in B, and vice versa
{
TSet<FName> ShaderNamesA, ShaderNamesB;
FilteredDefinesA.GetKeys(ShaderNamesA);
FilteredDefinesB.GetKeys(ShaderNamesB);
ShadersWithError.Append(ShaderNamesA.Difference(ShaderNamesB));
ShadersWithError.Append(ShaderNamesB.Difference(ShaderNamesA));
}
// Check if the defines (that are relevant to this shader format) are different between A or B
for (const auto& ShaderIt : FilteredDefinesA)
{
const TArray<FDefine>& DefinesA = ShaderIt.Value;
const TArray<FDefine>* DefinesB = FilteredDefinesB.Find(ShaderIt.Key);
if (DefinesB && DefinesA != *DefinesB)
{
ShadersWithError.Add(ShaderIt.Key);
}
}
if (ShadersWithError.Num() > 0)
{
FString ShaderListString = *FString::JoinBy(ShadersWithError, TEXT("\n "), [](const FName& A) { return A.ToString(); });
UE_LOG(LogShaders, Fatal, TEXT("It has been detected that one or more global shaders are configured differently between platform '%s' and '%s' for ")
TEXT("shader format '%s'. This is unsupported, and could result in data mismatches in the Derived Data Cache. Please check that all entries ")
TEXT("of '[GlobalShaderDefines]` in the various engine config .ini files are the same for these shaders on all platforms that use this shader ")
TEXT("model.\n\nGlobal shaders with errors:\n %s\n"),
*PlatformA.Name.ToString(), *PlatformB.Name.ToString(), *ShaderFormat.ToString(), *ShaderListString);
}
}
};
TArray<FGlobalShaderConfigDefines::FPlatformInfo> FGlobalShaderConfigDefines::ConfigDefines;
TStaticArray<const FGlobalShaderConfigDefines::FPlatformInfo*, EShaderPlatform::SP_NumPlatforms> FGlobalShaderConfigDefines::PerPlatformConfigs;
TStaticBitArray<EShaderPlatform::SP_NumPlatforms> FGlobalShaderConfigDefines::ErrorCheckedPlatforms;
/** Used to identify the global shader map in compile queues. */
const int32 GlobalShaderMapId = 0;
FGlobalShaderMapId::FGlobalShaderMapId(EShaderPlatform Platform, const ITargetPlatform* TargetPlatform)
{
if (TargetPlatform == nullptr)
{
TargetPlatform = GetTargetPlatformManagerRef().GetRunningTargetPlatform();
check(TargetPlatform);
}
ShaderPlatform = Platform;
IniPlatformName = FName(TargetPlatform->IniPlatformName());
LayoutParams.InitializeForPlatform(TargetPlatform);
const EShaderPermutationFlags PermutationFlags = GetShaderPermutationFlags();
TArray<FShaderType*> ShaderTypes;
TArray<const FShaderPipelineType*> ShaderPipelineTypes;
for (TLinkedList<FShaderType*>::TIterator ShaderTypeIt(FShaderType::GetTypeList()); ShaderTypeIt; ShaderTypeIt.Next())
{
FGlobalShaderType* GlobalShaderType = ShaderTypeIt->GetGlobalShaderType();
if (!GlobalShaderType)
{
continue;
}
bool bList = false;
for (int32 PermutationId = 0; PermutationId < GlobalShaderType->GetPermutationCount(); PermutationId++)
{
if (GlobalShaderType->ShouldCompilePermutation(Platform, PermutationId, PermutationFlags))
{
bList = true;
break;
}
}
if (bList)
{
ShaderTypes.Add(GlobalShaderType);
}
}
const TArray<FShaderPipelineType*>& SortedMaterialPipelineTypes = FShaderPipelineType::GetSortedTypes(FShaderType::EShaderTypeForDynamicCast::Global);
for (FShaderPipelineType* Pipeline : SortedMaterialPipelineTypes)
{
check(Pipeline);
if (Pipeline->IsGlobalTypePipeline())
{
int32 NumStagesNeeded = 0;
auto& StageTypes = Pipeline->GetStages();
for (const FShaderType* Shader : StageTypes)
{
const FGlobalShaderType* GlobalShaderType = Shader->GetGlobalShaderType();
if (GlobalShaderType->ShouldCompilePermutation(Platform, /* PermutationId = */ 0, PermutationFlags))
{
++NumStagesNeeded;
}
else
{
break;
}
}
if (NumStagesNeeded == StageTypes.Num())
{
ShaderPipelineTypes.Add(Pipeline);
}
}
}
// Individual shader dependencies
ShaderTypes.Sort(FCompareShaderTypes());
for (int32 TypeIndex = 0; TypeIndex < ShaderTypes.Num(); TypeIndex++)
{
FShaderType* ShaderType = ShaderTypes[TypeIndex];
FShaderTypeDependency Dependency(ShaderType, Platform);
const TCHAR* ShaderFilename = ShaderType->GetShaderFilename();
TArray<FShaderTypeDependency>& Dependencies = ShaderFilenameToDependenciesMap.FindOrAdd(ShaderFilename);
Dependencies.Add(Dependency);
}
for (int32 TypeIndex = 0; TypeIndex < ShaderPipelineTypes.Num(); TypeIndex++)
{
const FShaderPipelineType* Pipeline = ShaderPipelineTypes[TypeIndex];
FShaderPipelineTypeDependency Dependency(Pipeline, Platform);
ShaderPipelineTypeDependencies.Add(Dependency);
}
}
void FGlobalShaderMapId::AppendKeyString(FString& KeyString, const TArray<FShaderTypeDependency>& Dependencies) const
{
#if WITH_EDITOR
LayoutParams.AppendKeyString(KeyString);
{
const FSHAHash LayoutHash = GetShaderTypeLayoutHash(StaticGetTypeLayoutDesc<FGlobalShaderMapContent>(), LayoutParams);
KeyString.AppendChar('_');
LayoutHash.AppendString(KeyString);
KeyString.AppendChar('_');
}
AppendKeyStringShaderDependencies(Dependencies, ShaderPipelineTypeDependencies, TConstArrayView<FVertexFactoryTypeDependency>(), LayoutParams, KeyString);
// Extra logic, just for Global shaders
for (const FShaderTypeDependency& ShaderTypeDependency : Dependencies)
{
const FShaderType* ShaderType = FindShaderTypeByName(ShaderTypeDependency.ShaderTypeName);
// Add the config define hash, if any defines exist in config
FGlobalShaderConfigDefines::AppendKeyString(KeyString, ShaderType->GetFName(), ShaderTypeDependency.PermutationId, IniPlatformName, ShaderPlatform);
}
#endif // WITH_EDITOR
}
bool FGlobalShaderMapId::WithEditorOnly() const
{
return LayoutParams.WithEditorOnly();
}
bool FGlobalShaderType::ShouldCompilePipeline(const FShaderPipelineType* ShaderPipelineType, EShaderPlatform Platform, EShaderPermutationFlags Flags)
{
for (const FShaderType* ShaderType : ShaderPipelineType->GetStages())
{
const FGlobalShaderPermutationParameters Parameters(ShaderType->GetFName(), Platform, kUniqueShaderPermutationId, Flags);
checkSlow(ShaderType->GetGlobalShaderType());
if (!ShaderType->ShouldCompilePermutation(Parameters))
{
return false;
}
}
return true;
}
FGlobalShader::FGlobalShader(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
: FShader(Initializer)
{}
#if WITH_EDITOR
void FGlobalShaderType::SetupCompileEnvironment(EShaderPlatform Platform, int32 PermutationId, EShaderPermutationFlags Flags, FShaderCompilerEnvironment& Environment) const
{
FGlobalShaderPermutationParameters Parameters(GetFName(), Platform, PermutationId, Flags);
// Modify the compilation environment based on platform.
FGlobalShaderConfigDefines::ApplyConfigDefines(Parameters, Environment);
// Allow the shader type to modify its compile environment.
ModifyCompilationEnvironment(Parameters, Environment);
}
#endif // WITH_EDITOR
FGlobalShaderMap* GetGlobalShaderMap(EShaderPlatform Platform)
{
// If the global shader map hasn't been created yet
check(GGlobalShaderMap[Platform]);
return GGlobalShaderMap[Platform];
}
FGlobalShaderMapSection* FGlobalShaderMapSection::CreateFromArchive(FArchive& Ar)
{
FGlobalShaderMapSection* Section = new FGlobalShaderMapSection();
FShaderSerializeContext Ctx(Ar);
if (Section->Serialize(Ctx))
{
return Section;
}
delete Section;
return nullptr;
}
#if WITH_EDITOR
FGlobalShaderMapSection* FGlobalShaderMapSection::CreateFromCache(FShaderCacheLoadContext& Ctx)
{
FGlobalShaderMapSection* Section = new FGlobalShaderMapSection();
if (Section->Serialize(Ctx))
{
return Section;
}
delete Section;
return nullptr;
}
#endif
bool FGlobalShaderMapSection::Serialize(FShaderSerializeContext& Ctx)
{
return Super::Serialize(Ctx);
}
TShaderRef<FShader> FGlobalShaderMapSection::GetShader(FShaderType* ShaderType, int32 PermutationId) const
{
FShader* Shader = GetContent()->GetShader(ShaderType, PermutationId);
return Shader ? TShaderRef<FShader>(Shader, *this) : TShaderRef<FShader>();
}
FShaderPipelineRef FGlobalShaderMapSection::GetShaderPipeline(const FShaderPipelineType* PipelineType) const
{
FShaderPipeline* Pipeline = GetContent()->GetShaderPipeline(PipelineType);
return Pipeline ? FShaderPipelineRef(Pipeline, *this) : FShaderPipelineRef();
}
void FGlobalShaderMapSection::GetShaderList(TMap<FHashedName, TShaderRef<FShader>>& OutShaders) const
{
GetContent()->GetShaderList(*this, OutShaders);
}
void FGlobalShaderMapSection::GetShaderPipelineList(TArray<FShaderPipelineRef>& OutShaderPipelines) const
{
GetContent()->GetShaderPipelineList(*this, OutShaderPipelines, FShaderPipeline::EAll);
}
FGlobalShaderMap::FGlobalShaderMap(EShaderPlatform InPlatform)
: Platform(InPlatform)
{
}
FGlobalShaderMap::~FGlobalShaderMap()
{
ReleaseAllSections();
}
TShaderRef<FShader> FGlobalShaderMap::GetShader(FShaderType* ShaderType, int32 PermutationId) const
{
FGlobalShaderMapSection* const* Section = SectionMap.Find(ShaderType->GetHashedShaderFilename());
return Section ? (*Section)->GetShader(ShaderType, PermutationId) : TShaderRef<FShader>();
}
FShaderPipelineRef FGlobalShaderMap::GetShaderPipeline(const FShaderPipelineType* ShaderPipelineType) const
{
FGlobalShaderMapSection* const* Section = SectionMap.Find(ShaderPipelineType->GetHashedPrimaryShaderFilename());
return Section ? (*Section)->GetShaderPipeline(ShaderPipelineType) : FShaderPipelineRef();
}
void FGlobalShaderMap::BeginCreateAllShaders()
{
for (const auto& It : SectionMap)
{
It.Value->GetResource()->BeginCreateAllShaders();
}
}
#if WITH_EDITOR
void FGlobalShaderMap::GetOutdatedTypes(TArray<const FShaderType*>& OutdatedShaderTypes, TArray<const FShaderPipelineType*>& OutdatedShaderPipelineTypes, TArray<const FVertexFactoryType*>& OutdatedFactoryTypes) const
{
for (const auto& It : SectionMap)
{
It.Value->GetOutdatedTypes(OutdatedShaderTypes, OutdatedShaderPipelineTypes, OutdatedFactoryTypes);
}
}
void FGlobalShaderMap::SaveShaderStableKeys(EShaderPlatform TargetShaderPlatform)
{
FStableShaderKeyAndValue SaveKeyVal;
for (const auto& It : SectionMap)
{
It.Value->SaveShaderStableKeys(TargetShaderPlatform, SaveKeyVal);
}
}
#endif // WITH_EDITOR
bool FGlobalShaderMap::IsEmpty() const
{
for (const auto& It : SectionMap)
{
if (!It.Value->GetContent()->IsEmpty())
{
return false;
}
}
return true;
}
bool FGlobalShaderMap::IsComplete(const ITargetPlatform* TargetPlatform) const
{
FGlobalShaderMapId ShaderMapId(Platform, TargetPlatform);
const EShaderPermutationFlags PermutationFlags = ShaderMapId.GetShaderPermutationFlags();
// traverse all global shader types
for (TLinkedList<FShaderType*>::TIterator ShaderTypeIt(FShaderType::GetTypeList()); ShaderTypeIt; ShaderTypeIt.Next())
{
FGlobalShaderType* GlobalShaderType = ShaderTypeIt->GetGlobalShaderType();
if (!GlobalShaderType)
{
continue;
}
int32 PermutationCountToCompile = 0;
for (int32 PermutationId = 0; PermutationId < GlobalShaderType->GetPermutationCount(); PermutationId++)
{
if (GlobalShaderType->ShouldCompilePermutation(Platform, PermutationId, PermutationFlags)
&& !HasShader(GlobalShaderType, PermutationId))
{
return false;
}
}
}
// traverse all pipelines. Note that there's no ShouldCompile call for them. Materials instead test individual stages, but it leads to another problems
// like including the standalone types even if they are not going to be used. This code follows VerifyGlobalShaders() logic that includes all global pipelines unconditionally.
for (TLinkedList<FShaderPipelineType*>::TIterator ShaderPipelineIt(FShaderPipelineType::GetTypeList()); ShaderPipelineIt; ShaderPipelineIt.Next())
{
const FShaderPipelineType* Pipeline = *ShaderPipelineIt;
if (Pipeline->IsGlobalTypePipeline()
&& FGlobalShaderType::ShouldCompilePipeline(Pipeline, Platform, PermutationFlags)
&& !HasShaderPipeline(Pipeline))
{
return false;
}
}
return true;
}
void FGlobalShaderMap::Empty()
{
for (const auto& It : SectionMap)
{
It.Value->GetMutableContent()->Empty();
}
}
void FGlobalShaderMap::ReleaseAllSections()
{
for (auto& It : SectionMap)
{
delete It.Value;
}
SectionMap.Empty();
}
FShader* FGlobalShaderMap::FindOrAddShader(const FShaderType* ShaderType, int32 PermutationId, FShader* Shader)
{
const FHashedName HashedFilename(ShaderType->GetHashedShaderFilename());
FGlobalShaderMapSection*& Section = SectionMap.FindOrAdd(HashedFilename);
if (!Section)
{
Section = new FGlobalShaderMapSection(Platform, HashedFilename);
}
return Section->GetMutableContent()->FindOrAddShader(ShaderType->GetHashedName(), PermutationId, Shader);
}
FShaderPipeline* FGlobalShaderMap::FindOrAddShaderPipeline(const FShaderPipelineType* ShaderPipelineType, FShaderPipeline* ShaderPipeline)
{
FGlobalShaderMapSection*& Section = SectionMap.FindOrAdd(ShaderPipelineType->GetHashedPrimaryShaderFilename());
if (!Section)
{
Section = new FGlobalShaderMapSection(Platform, ShaderPipelineType->GetHashedPrimaryShaderFilename());
}
return Section->GetMutableContent()->FindOrAddShaderPipeline(ShaderPipeline);
}
void FGlobalShaderMap::RemoveShaderTypePermutaion(const FShaderType* Type, int32 PermutationId)
{
FGlobalShaderMapSection** Section = SectionMap.Find(Type->GetHashedShaderFilename());
if (Section)
{
(*Section)->GetMutableContent()->RemoveShaderTypePermutaion(Type->GetHashedName(), PermutationId);
}
}
void FGlobalShaderMap::RemoveShaderPipelineType(const FShaderPipelineType* ShaderPipelineType)
{
FGlobalShaderMapSection** Section = SectionMap.Find(ShaderPipelineType->GetHashedPrimaryShaderFilename());
if (Section)
{
(*Section)->GetMutableContent()->RemoveShaderPipelineType(ShaderPipelineType);
}
}
void FGlobalShaderMap::AddSection(FGlobalShaderMapSection* InSection)
{
check(InSection);
const FGlobalShaderMapContent* Content = InSection->GetContent();
const FHashedName& HashedFilename = Content->HashedSourceFilename;
SectionMap.Add(HashedFilename, InSection);
}
FGlobalShaderMapSection* FGlobalShaderMap::FindSection(const FHashedName& HashedShaderFilename)
{
FGlobalShaderMapSection* const* Section = SectionMap.Find(HashedShaderFilename);
return Section ? *Section : nullptr;
}
FGlobalShaderMapSection* FGlobalShaderMap::FindOrAddSection(const FShaderType* ShaderType)
{
const FHashedName HashedFilename(ShaderType->GetHashedShaderFilename());
FGlobalShaderMapSection* Section = FindSection(HashedFilename);
if(!Section)
{
Section = new FGlobalShaderMapSection(Platform, HashedFilename);
AddSection(Section);
}
return Section;
}
void FGlobalShaderMap::SaveToGlobalArchive(FArchive& Ar)
{
int32 NumSections = SectionMap.Num();
Ar << NumSections;
for (const auto& It : SectionMap)
{
FShaderSerializeContext Ctx(Ar);
It.Value->Serialize(Ctx);
}
}
void FGlobalShaderMap::LoadFromGlobalArchive(FArchive& Ar)
{
int32 NumSections = 0;
Ar << NumSections;
for (int32 i = 0; i < NumSections; ++i)
{
FGlobalShaderMapSection* Section = FGlobalShaderMapSection::CreateFromArchive(Ar);
if (Section)
{
AddSection(Section);
}
else
{
UE_LOG(LogShaders, Fatal, TEXT("Could not load section %d (of %d) of the global shadermap."), i, NumSections);
}
}
}
RENDERCORE_API ERecursiveShader GRequiredRecursiveShaders = ERecursiveShader::None;
void ForceInitGlobalShaderType(FShaderType& ShaderType)
{
FGlobalShaderMap* GlobalShaderMap = GetGlobalShaderMap(GMaxRHIFeatureLevel);
for (int32 Permutation = 0; Permutation < ShaderType.GetPermutationCount(); ++Permutation)
{
TShaderRef<FShader> ShaderRef = GlobalShaderMap->GetShader(&ShaderType, Permutation);
if (ShaderRef.IsValid())
{
FShaderMapResource& MapResource = ShaderRef.GetResourceChecked();
for (int32 Index = 0; Index < MapResource.GetNumShaders(); ++Index)
{
MapResource.GetShader(Index);
}
}
}
}
RENDERCORE_API void CreateRecursiveShaders()
{
ensureMsgf(!GRHISupportsMultithreadedShaderCreation, TEXT("CreateRecursiveShaders() is called while GRHISupportsMultithreadedShaderCreation is true. This is an unnecessary call."));
ensureMsgf(IsInRenderingThread(), TEXT("CreateRecursiveShaders() is expected to be called from the render thread."));
if (EnumHasAnyFlags(GRequiredRecursiveShaders, ERecursiveShader::Resolve))
{
extern void CreateResolveShaders();
CreateResolveShaders();
}
if (EnumHasAnyFlags(GRequiredRecursiveShaders, ERecursiveShader::Clear))
{
extern void CreateClearReplacementShaders();
CreateClearReplacementShaders();
}
if (EnumHasAnyFlags(GRequiredRecursiveShaders, ERecursiveShader::Null))
{
ForceInitGlobalShaderType<FNULLPS>();
}
}