852 lines
27 KiB
C++
852 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);
|
|
|
|
// IsODSCOnly shaders aren't in shader map so don't include when gathering for IsComplete().
|
|
const EShaderPermutationFlags PermutationFlags = ShaderMapId.GetShaderPermutationFlags() & ~EShaderPermutationFlags::IsODSCOnly;
|
|
|
|
// 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>();
|
|
}
|
|
}
|