Files
UnrealEngineUWP/Engine/Source/Runtime/RenderCore/Private/ShaderMap.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

928 lines
29 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
/*=============================================================================
Shader.cpp: Shader implementation.
=============================================================================*/
#include "Shader.h"
#include "Misc/App.h"
#include "Misc/CoreMisc.h"
#include "Misc/StringBuilder.h"
#include "VertexFactory.h"
#include "ShaderCodeLibrary.h"
#include "ShaderCore.h"
#include "Misc/ScopeLock.h"
#include "UObject/RenderingObjectVersion.h"
#include "HAL/LowLevelMemTracker.h"
#include "DataDrivenShaderPlatformInfo.h"
#include "ProfilingDebugging/CsvProfiler.h"
static EShaderPermutationFlags GetCurrentShaderPermutationFlags()
{
EShaderPermutationFlags Result = EShaderPermutationFlags::None;
#if WITH_EDITORONLY_DATA
Result |= EShaderPermutationFlags::HasEditorOnlyData;
#endif
return Result;
}
#ifndef ALLOW_SHADERMAP_TRACKING
#define ALLOW_SHADERMAP_TRACKING 0
#endif
#if ALLOW_SHADERMAP_TRACKING
static TArray<FShaderMapBase*> GAllShaderMaps;
static FCriticalSection GAllShaderMapsGuard;
TAutoConsoleVariable<bool> CVarEnableShaderMapTracking(
TEXT("r.TrackShaderMaps"),
WITH_EDITOR,
TEXT("Enables the tracking of every shadermap instantiated. Required to run ListShaderMaps command."),
ECVF_ReadOnly);
FAutoConsoleCommandWithArgsAndOutputDevice GListShaderMapsCmd(
TEXT("ListShaderMaps"),
TEXT("Spits out a csv table containing stats of all shadermaps"),
FConsoleCommandWithArgsAndOutputDeviceDelegate::CreateStatic(
[](const TArray<FString>& Params, FOutputDevice& Out)
{
if (!CVarEnableShaderMapTracking.GetValueOnGameThread())
{
UE_LOG(LogShaders, Warning, TEXT("Enable r.TrackShaderMaps in ini config to enable the functionality"));
return;
}
Out.Logf(TEXT("ShaderMapName,OwnerName,NumShaders,NumPipelines,SizeKb,bUsedForRendering"));
FScopeLock SMAccess(&GAllShaderMapsGuard);
for (FShaderMapBase* ShaderMap : GAllShaderMaps)
{
FString FriendlyName = TEXT("Unknown");
FString OwnerName = TEXT("Unknown");
uint32 CodeSize = ShaderMap->GetFrozenContentSize();
bool bUseForRendering = false;
if (const FShaderMapResource* Resource = ShaderMap->GetResource())
{
FriendlyName = Resource->GetFriendlyName();
OwnerName = Resource->GetOwnerName().ToString();
CodeSize += Resource->GetSizeBytes();
bUseForRendering = Resource->ContainsAtLeastOneRHIShaderCreated();
}
TMap<FHashedName, TShaderRef<FShader>> Shaders;
ShaderMap->GetShaderList(Shaders);
TArray<FShaderPipelineRef> Pipelines;
ShaderMap->GetShaderPipelineList(Pipelines);
// Editor doesn't have the size baked, so grab it from shaders themselves.
if (CodeSize == 0)
{
for (auto& [Hash, Shader] : Shaders)
{
CodeSize += Shader->GetCodeSize();
}
}
Out.Logf(TEXT("%s,%s,%d,%d,%.3f,%s"),
*FriendlyName,
*OwnerName,
Shaders.Num(),
Pipelines.Num(),
CodeSize / 1024.f,
bUseForRendering ? TEXT("YES") : TEXT("NO")
);
}
}));
#endif // ALLOW_SHADERMAP_TRACKING
FShaderMapBase::FShaderMapBase()
: PointerTable(nullptr)
, NumFrozenShaders(0u)
{
PermutationFlags = GetCurrentShaderPermutationFlags();
#if ALLOW_SHADERMAP_TRACKING
if (CVarEnableShaderMapTracking.GetValueOnAnyThread())
{
LLM_SCOPE_BYNAME(TEXT("Debug/ShaderMapsTracking"));
FScopeLock SMAccess(&GAllShaderMapsGuard);
GAllShaderMaps.Add(this);
}
#endif // ALLOW_SHADERMAP_TRACKING
}
FShaderMapBase::~FShaderMapBase()
{
DestroyContent();
if (PointerTable)
{
delete PointerTable;
}
#if ALLOW_SHADERMAP_TRACKING
if (CVarEnableShaderMapTracking.GetValueOnAnyThread())
{
FScopeLock SMAccess(&GAllShaderMapsGuard);
GAllShaderMaps.RemoveSingleSwap(this, EAllowShrinking::No);
}
#endif // ALLOW_SHADERMAP_TRACKING
}
FShaderMapResourceCode* FShaderMapBase::GetResourceCode()
{
if (!Code)
{
Code = new FShaderMapResourceCode();
}
return Code;
}
void FShaderMapBase::AssignContent(TMemoryImageObject<FShaderMapContent> InContent)
{
check(!Content.Object);
check(!PointerTable);
const FTypeLayoutDesc& ExpectedTypeDesc = GetContentTypeDesc();
checkf(*InContent.TypeDesc == ExpectedTypeDesc, TEXT("FShaderMapBase expected content of type %s, got %s"), ExpectedTypeDesc.Name, InContent.TypeDesc->Name);
Content = InContent;
PointerTable = CreatePointerTable();
PostFinalizeContent();
}
void FShaderMapBase::AssignCopy(const FShaderMapBase& Source)
{
check(!PointerTable);
check(!Code);
check(Source.Content.Object);
if (Source.Content.FrozenSize == 0u)
{
PointerTable = CreatePointerTable();
Content = TMemoryImageObject<FShaderMapContent>(FreezeMemoryImageObject(Source.Content.Object, *Source.Content.TypeDesc, PointerTable));
}
else
{
PointerTable = Source.PointerTable->Clone();
Content.TypeDesc = Source.Content.TypeDesc;
Content.FrozenSize = Source.Content.FrozenSize;
Content.Object = static_cast<FShaderMapContent*>(FMemory::Malloc(Content.FrozenSize));
FMemory::Memcpy(Content.Object, Source.Content.Object, Content.FrozenSize);
}
NumFrozenShaders = Content.Object->GetNumShaders();
INC_DWORD_STAT_BY(STAT_Shaders_ShaderMemory, Content.FrozenSize);
INC_DWORD_STAT_BY(STAT_Shaders_NumShadersLoaded, NumFrozenShaders);
#if (CSV_PROFILER_STATS && !UE_BUILD_SHIPPING)
TCsvPersistentCustomStat<float>* CsvStatShaderMemoryMB = FCsvProfiler::Get()->GetOrCreatePersistentCustomStatFloat(TEXT("ShaderMemoryMB"), CSV_CATEGORY_INDEX(Shaders));
TCsvPersistentCustomStat<int>* CsvStatNumShadersLoaded = FCsvProfiler::Get()->GetOrCreatePersistentCustomStatInt(TEXT("NumShadersLoaded"), CSV_CATEGORY_INDEX(Shaders));
CsvStatShaderMemoryMB->Add(float(Content.FrozenSize) / (1024.0f * 1024.f));
CsvStatNumShadersLoaded->Add(NumFrozenShaders);
#endif
Code = new FShaderMapResourceCode(*Source.Code);
InitResource();
}
void FShaderMapBase::InitResource()
{
Resource.SafeRelease();
if (Code)
{
Code->Finalize();
Resource = new FShaderMapResource_InlineCode(GetShaderPlatform(), Code);
BeginInitResource(Resource);
}
PostFinalizeContent();
}
void FShaderMapBase::FinalizeContent()
{
if (Content.Freeze(PointerTable))
{
NumFrozenShaders = Content.Object->GetNumShaders();
INC_DWORD_STAT_BY(STAT_Shaders_ShaderMemory, Content.FrozenSize);
INC_DWORD_STAT_BY(STAT_Shaders_NumShadersLoaded, NumFrozenShaders);
#if (CSV_PROFILER_STATS && !UE_BUILD_SHIPPING)
TCsvPersistentCustomStat<float>* CsvStatShaderMemoryMB = FCsvProfiler::Get()->GetOrCreatePersistentCustomStatFloat(TEXT("ShaderMemoryMB"), CSV_CATEGORY_INDEX(Shaders));
TCsvPersistentCustomStat<int>* CsvStatNumShadersLoaded = FCsvProfiler::Get()->GetOrCreatePersistentCustomStatInt(TEXT("NumShadersLoaded"), CSV_CATEGORY_INDEX(Shaders));
CsvStatShaderMemoryMB->Add(float(Content.FrozenSize) / (1024.0f * 1024.f));
CsvStatNumShadersLoaded->Add(NumFrozenShaders);
#endif
}
InitResource();
}
void FShaderMapBase::UnfreezeContent()
{
DEC_DWORD_STAT_BY(STAT_Shaders_ShaderMemory, Content.FrozenSize);
DEC_DWORD_STAT_BY(STAT_Shaders_NumShadersLoaded, NumFrozenShaders);
#if (CSV_PROFILER_STATS && !UE_BUILD_SHIPPING)
TCsvPersistentCustomStat<float>* CsvStatShaderMemoryMB = FCsvProfiler::Get()->GetOrCreatePersistentCustomStatFloat(TEXT("ShaderMemoryMB"), CSV_CATEGORY_INDEX(Shaders));
TCsvPersistentCustomStat<int>* CsvStatNumShadersLoaded = FCsvProfiler::Get()->GetOrCreatePersistentCustomStatInt(TEXT("NumShadersLoaded"), CSV_CATEGORY_INDEX(Shaders));
CsvStatShaderMemoryMB->Sub(float(Content.FrozenSize) / (1024.0f * 1024.f));
CsvStatNumShadersLoaded->Sub(NumFrozenShaders);
#endif
Content.Unfreeze(PointerTable);
NumFrozenShaders = 0u;
}
#define CHECK_SHADERMAP_DEPENDENCIES (WITH_EDITOR || !(UE_BUILD_SHIPPING || UE_BUILD_TEST))
bool FShaderMapBase::Serialize(FShaderSerializeContext& Ctx)
{
FArchive& Ar = Ctx.GetMainArchive();
LLM_SCOPE(ELLMTag::Shaders);
if (Ar.IsSaving())
{
check(Content.Object);
Content.Object->Validate(*this);
{
TUniquePtr<FShaderMapPointerTable> SavePointerTable(CreatePointerTable());
FMemoryImage MemoryImage;
MemoryImage.PrevPointerTable = PointerTable;
MemoryImage.PointerTable = SavePointerTable.Get();
MemoryImage.TargetLayoutParameters.InitializeForArchive(Ar);
FMemoryImageWriter Writer(MemoryImage);
Writer.WriteRootObject(Content.Object, *Content.TypeDesc);
FMemoryImageResult MemoryImageResult;
MemoryImage.Flatten(MemoryImageResult, true);
MemoryImageResult.SaveToArchive(Ar);
}
bool bShareCode = false;
#if WITH_EDITOR
bShareCode = FShaderLibraryCooker::IsShaderLibraryEnabled() && Ar.IsCooking();
#endif // WITH_EDITOR
Ar << bShareCode;
#if WITH_EDITOR
// Serialize a copy of ShaderPlatform directly into the archive
// This will allow us to correctly deserialize the stream, even if we're not able to load the frozen content
const EShaderPlatform ShaderPlatform = GetShaderPlatform();
FName ShaderPlatformName = FDataDrivenShaderPlatformInfo::GetName(ShaderPlatform);
Ar << ShaderPlatformName;
if (Ar.IsCooking())
{
const FName ShaderFormat = LegacyShaderPlatformToShaderFormat(ShaderPlatform);
if (ShaderFormat != NAME_None)
{
Code->NotifyShadersCompiled(ShaderFormat);
}
}
if (bShareCode)
{
FSHAHash ResourceHash = Code->ResourceHash;
Ar << ResourceHash;
FShaderLibraryCooker::AddShaderCode(ShaderPlatform, Code, GetAssociatedAssets());
}
else
#endif // WITH_EDITOR
{
Code->Serialize(Ctx);
}
}
else
{
check(!PointerTable);
PointerTable = CreatePointerTable();
FPlatformTypeLayoutParameters LayoutParameters;
FMemoryImageObject LoadedContent = FMemoryImageResult::LoadFromArchive(Ar, GetContentTypeDesc(), PointerTable, LayoutParameters);
PermutationFlags = GetShaderPermutationFlags(LayoutParameters);
bool bShareCode = false;
Ar << bShareCode;
FName ShaderPlatformName;
Ar << ShaderPlatformName;
const EShaderPlatform ShaderPlatform = FDataDrivenShaderPlatformInfo::GetShaderPlatformFromName(ShaderPlatformName);
if (bShareCode)
{
FSHAHash ResourceHash;
Ar << ResourceHash;
Resource = FShaderCodeLibrary::LoadResource(ResourceHash, &Ar);
if (!Resource)
{
// do not warn when running -nullrhi (the resource cannot be created as the shader library will not be uninitialized),
// also do not warn for shader platforms other than current (if the game targets more than one RHI)
if (FApp::CanEverRender() && ShaderPlatform == GMaxRHIShaderPlatform)
{
UE_LOG(LogShaders, Error, TEXT("Missing shader resource for hash '%s' for shader platform '%s' in the shader library while serializing asset %s"), *ResourceHash.ToString(),
*LexToString(ShaderPlatform),
*Ctx.SerializingAsset.ToString());
}
}
}
else
{
Code = new FShaderMapResourceCode();
Code->Serialize(Ctx);
Resource = new FShaderMapResource_InlineCode(ShaderPlatform, Code);
}
if (LoadedContent.Object && Resource)
{
Content = TMemoryImageObject<FShaderMapContent>(LoadedContent);
// Possible we've loaded/converted unfrozen content, make sure it's frozen for the current platform before trying to render anything
if (Content.FrozenSize == 0u)
{
Content.Freeze(PointerTable);
}
PostFinalizeContent();
NumFrozenShaders = Content.Object->GetNumShaders();
INC_DWORD_STAT_BY(STAT_Shaders_ShaderMemory, Content.FrozenSize);
INC_DWORD_STAT_BY(STAT_Shaders_NumShadersLoaded, NumFrozenShaders);
#if (CSV_PROFILER_STATS && !UE_BUILD_SHIPPING)
TCsvPersistentCustomStat<float>* CsvStatShaderMemoryMB = FCsvProfiler::Get()->GetOrCreatePersistentCustomStatFloat(TEXT("ShaderMemoryMB"), CSV_CATEGORY_INDEX(Shaders));
TCsvPersistentCustomStat<int>* CsvStatNumShadersLoaded = FCsvProfiler::Get()->GetOrCreatePersistentCustomStatInt(TEXT("NumShadersLoaded"), CSV_CATEGORY_INDEX(Shaders));
CsvStatShaderMemoryMB->Add(float(Content.FrozenSize) / (1024.0f * 1024.f));
CsvStatNumShadersLoaded->Add(NumFrozenShaders);
#endif
BeginInitResource(Resource);
INC_DWORD_STAT_BY(STAT_Shaders_ShaderResourceMemory, Resource->GetSizeBytes());
}
else
{
// Missing either content and/or resource
// In either case, shader map has failed to load
LoadedContent.Destroy(PointerTable);
Resource.SafeRelease();
}
}
return (bool)Content.Object;
}
#if WITH_EDITORONLY_DATA
TArray<FGenericShaderStat> FShaderMapBase::GetShaderStatistics(FShaderType* ShaderType) const
{
TArray<FGenericShaderStat> ShaderStatistics;
FShader* Shader = GetContent()->GetShader(ShaderType);
if (Shader)
{
ShaderStatistics = GetShaderStatistics(*Shader);
}
return ShaderStatistics;
}
TArray<FGenericShaderStat> FShaderMapBase::GetShaderStatistics(FShader& Shader) const
{
TArray<FGenericShaderStat> ShaderStatistics;
if (Code)
{
const int32 ShaderIndex = Code->FindShaderIndex(Shader.GetOutputHash());
if (Code->ShaderEditorOnlyDataEntries.IsValidIndex(ShaderIndex))
{
for (const FGenericShaderStat& Stat : Code->ShaderEditorOnlyDataEntries[ShaderIndex].ShaderStatistics)
{
if (!EnumHasAnyFlags(Stat.Flags, FGenericShaderStat::EFlags::Hidden))
{
ShaderStatistics.Add(Stat);
}
}
}
}
return ShaderStatistics;
}
#endif // WITH_EDITORONLY_DATA
FString FShaderMapBase::ToString() const
{
TStringBuilder<32000> String;
{
FMemoryToStringContext Context;
Context.PrevPointerTable = PointerTable;
Context.String = &String;
FPlatformTypeLayoutParameters LayoutParams;
LayoutParams.InitializeForCurrent();
Content.TypeDesc->ToStringFunc(Content.Object, *Content.TypeDesc, LayoutParams, Context);
}
if (Code)
{
Code->ToString(String);
}
return String.ToString();
}
void FShaderMapBase::DestroyContent()
{
DEC_DWORD_STAT_BY(STAT_Shaders_ShaderMemory, Content.FrozenSize);
DEC_DWORD_STAT_BY(STAT_Shaders_NumShadersLoaded, NumFrozenShaders);
#if (CSV_PROFILER_STATS && !UE_BUILD_SHIPPING)
TCsvPersistentCustomStat<float>* CsvStatShaderMemoryMB = FCsvProfiler::Get()->GetOrCreatePersistentCustomStatFloat(TEXT("ShaderMemoryMB"), CSV_CATEGORY_INDEX(Shaders));
TCsvPersistentCustomStat<int>* CsvStatNumShadersLoaded = FCsvProfiler::Get()->GetOrCreatePersistentCustomStatInt(TEXT("NumShadersLoaded"), CSV_CATEGORY_INDEX(Shaders));
CsvStatShaderMemoryMB->Sub(float(Content.FrozenSize) / (1024.0f * 1024.f));
CsvStatNumShadersLoaded->Sub(NumFrozenShaders);
#endif
Content.Destroy(PointerTable);
NumFrozenShaders = 0u;
}
static uint16 MakeShaderHash(const FHashedName& TypeName, int32 PermutationId)
{
return (uint16)CityHash128to64({ TypeName.GetHash(), (uint64)PermutationId });
}
FShaderMapContent::FShaderMapContent(EShaderPlatform InPlatform)
: ShaderHash(128u), ShaderPlatformName(FDataDrivenShaderPlatformInfo::GetName(InPlatform))
{}
FShaderMapContent::~FShaderMapContent()
{
Empty();
}
EShaderPlatform FShaderMapContent::GetShaderPlatform() const
{
return FDataDrivenShaderPlatformInfo::GetShaderPlatformFromName(ShaderPlatformName);
}
FShader* FShaderMapContent::GetShader(const FHashedName& TypeName, int32 PermutationId) const
{
// TRACE_CPUPROFILER_EVENT_SCOPE(FShaderMapContent::GetShader); -- this function is called too frequently, so don't add the scope by default
const uint16 Hash = MakeShaderHash(TypeName, PermutationId);
const FHashedName* RESTRICT LocalShaderTypes = ShaderTypes.GetData();
const int32* RESTRICT LocalShaderPermutations = ShaderPermutations.GetData();
const uint32* RESTRICT LocalNextHashIndices = ShaderHash.GetNextIndices();
const uint32 NumShaders = Shaders.Num();
for (uint32 Index = ShaderHash.First(Hash); ShaderHash.IsValid(Index); Index = LocalNextHashIndices[Index])
{
checkSlow(Index < NumShaders);
if (LocalShaderTypes[Index] == TypeName && LocalShaderPermutations[Index] == PermutationId)
{
return Shaders[Index].GetChecked();
}
}
return nullptr;
}
void FShaderMapContent::AddShader(const FHashedName& TypeName, int32 PermutationId, FShader* Shader)
{
check(!Shader->IsFrozen());
checkSlow(!HasShader(TypeName, PermutationId));
const uint16 Hash = MakeShaderHash(TypeName, PermutationId);
const int32 Index = Shaders.Add(Shader);
ShaderTypes.Add(TypeName);
ShaderPermutations.Add(PermutationId);
check(ShaderTypes.Num() == Shaders.Num());
check(ShaderPermutations.Num() == Shaders.Num());
ShaderHash.Add(Hash, Index);
}
FShader* FShaderMapContent::FindOrAddShader(const FHashedName& TypeName, int32 PermutationId, FShader* Shader)
{
check(!Shader->IsFrozen());
const uint16 Hash = MakeShaderHash(TypeName, PermutationId);
for (uint32 Index = ShaderHash.First(Hash); ShaderHash.IsValid(Index); Index = ShaderHash.Next(Index))
{
if (ShaderTypes[Index] == TypeName && ShaderPermutations[Index] == PermutationId)
{
DeleteObjectFromLayout(Shader);
return Shaders[Index].GetChecked();
}
}
const int32 Index = Shaders.Add(Shader);
ShaderHash.Add(Hash, Index);
ShaderTypes.Add(TypeName);
ShaderPermutations.Add(PermutationId);
check(ShaderTypes.Num() == Shaders.Num());
check(ShaderPermutations.Num() == Shaders.Num());
return Shader;
}
void FShaderMapContent::AddShaderPipeline(FShaderPipeline* Pipeline)
{
checkSlow(!HasShaderPipeline(Pipeline->TypeName));
const int32 Index = Algo::LowerBoundBy(ShaderPipelines, Pipeline->TypeName, FProjectShaderPipelineToKey());
ShaderPipelines.Insert(Pipeline, Index);
}
FShaderPipeline* FShaderMapContent::FindOrAddShaderPipeline(FShaderPipeline* Pipeline)
{
const int32 Index = Algo::LowerBoundBy(ShaderPipelines, Pipeline->TypeName, FProjectShaderPipelineToKey());
if (Index < ShaderPipelines.Num())
{
FShaderPipeline* PrevShaderPipeline = ShaderPipelines[Index];
if (PrevShaderPipeline->TypeName == Pipeline->TypeName)
{
delete Pipeline;
return PrevShaderPipeline;
}
}
ShaderPipelines.Insert(Pipeline, Index);
return Pipeline;
}
/**
* Removes the shader of the given type from the shader map
* @param Type Shader type to remove the entry for
*/
void FShaderMapContent::RemoveShaderTypePermutaion(const FHashedName& TypeName, int32 PermutationId)
{
const uint16 Hash = MakeShaderHash(TypeName, PermutationId);
for (uint32 Index = ShaderHash.First(Hash); ShaderHash.IsValid(Index); Index = ShaderHash.Next(Index))
{
FShader* Shader = Shaders[Index].GetChecked();
if (ShaderTypes[Index] == TypeName && ShaderPermutations[Index] == PermutationId)
{
DeleteObjectFromLayout(Shader);
// Replace the shader we're removing with the last shader in the list
Shaders.RemoveAtSwap(Index, 1, EAllowShrinking::No);
ShaderTypes.RemoveAtSwap(Index, 1, EAllowShrinking::No);
ShaderPermutations.RemoveAtSwap(Index, 1, EAllowShrinking::No);
check(ShaderTypes.Num() == Shaders.Num());
check(ShaderPermutations.Num() == Shaders.Num());
ShaderHash.Remove(Hash, Index);
// SwapIndex is the old index of the shader at the end of the list, that's now been moved to replace the current shader
const int32 SwapIndex = Shaders.Num();
if (Index != SwapIndex)
{
// We need to update the hash table to reflect shader previously at SwapIndex being moved to Index
// Here we construct the hash from values at Index, since type/permutation have already been moved
const uint16 SwapHash = MakeShaderHash(ShaderTypes[Index], ShaderPermutations[Index]);
ShaderHash.Remove(SwapHash, SwapIndex);
ShaderHash.Add(SwapHash, Index);
}
break;
}
}
}
void FShaderMapContent::RemoveShaderPipelineType(const FShaderPipelineType* ShaderPipelineType)
{
const int32 Index = Algo::BinarySearchBy(ShaderPipelines, ShaderPipelineType->GetHashedName(), FProjectShaderPipelineToKey());
if (Index != INDEX_NONE)
{
FShaderPipeline* Pipeline = ShaderPipelines[Index];
delete Pipeline;
ShaderPipelines.RemoveAt(Index, EAllowShrinking::No);
}
}
void FShaderMapContent::GetShaderList(const FShaderMapBase& InShaderMap, const FSHAHash& InMaterialShaderMapHash, TMap<FShaderId, TShaderRef<FShader>>& OutShaders) const
{
for (int32 ShaderIndex = 0; ShaderIndex < Shaders.Num(); ++ShaderIndex)
{
FShader* Shader = Shaders[ShaderIndex].GetChecked();
const FShaderId ShaderId(
Shader->GetType(InShaderMap.GetPointerTable()),
InMaterialShaderMapHash,
FHashedName(),
Shader->GetVertexFactoryType(InShaderMap.GetPointerTable()),
ShaderPermutations[ShaderIndex],
GetShaderPlatform());
OutShaders.Add(ShaderId, TShaderRef<FShader>(Shader, InShaderMap));
}
for (const FShaderPipeline* ShaderPipeline : ShaderPipelines)
{
for (uint32 Frequency = 0u; Frequency < SF_NumGraphicsFrequencies; ++Frequency)
{
FShader* Shader = ShaderPipeline->Shaders[Frequency].Get();
if (Shader)
{
const FShaderId ShaderId(
Shader->GetType(InShaderMap.GetPointerTable()),
InMaterialShaderMapHash,
ShaderPipeline->TypeName,
Shader->GetVertexFactoryType(InShaderMap.GetPointerTable()),
ShaderPipeline->PermutationIds[Frequency],
GetShaderPlatform());
OutShaders.Add(ShaderId, TShaderRef<FShader>(Shader, InShaderMap));
}
}
}
}
void FShaderMapContent::GetShaderList(const FShaderMapBase& InShaderMap, TMap<FHashedName, TShaderRef<FShader>>& OutShaders) const
{
for (int32 ShaderIndex = 0; ShaderIndex < Shaders.Num(); ++ShaderIndex)
{
FShader* Shader = Shaders[ShaderIndex].Get();
if (ensure(Shader))
{
OutShaders.Add(ShaderTypes[ShaderIndex], TShaderRef<FShader>(Shader, InShaderMap));
}
}
for (const FShaderPipeline* ShaderPipeline : ShaderPipelines)
{
for (const TShaderRef<FShader>& Shader : ShaderPipeline->GetShaders(InShaderMap))
{
OutShaders.Add(Shader.GetType()->GetHashedName(), Shader);
}
}
}
void FShaderMapContent::GetShaderPipelineList(const FShaderMapBase& InShaderMap, TArray<FShaderPipelineRef>& OutShaderPipelines, FShaderPipeline::EFilter Filter) const
{
const EShaderPlatform ShaderPlatform = GetShaderPlatform();
for (FShaderPipeline* Pipeline : ShaderPipelines)
{
const FShaderPipelineType* PipelineType = FShaderPipelineType::GetShaderPipelineTypeByName(Pipeline->TypeName);
if (PipelineType->ShouldOptimizeUnusedOutputs(ShaderPlatform) && Filter == FShaderPipeline::EOnlyShared)
{
continue;
}
else if (!PipelineType->ShouldOptimizeUnusedOutputs(ShaderPlatform) && Filter == FShaderPipeline::EOnlyUnique)
{
continue;
}
OutShaderPipelines.Add(FShaderPipelineRef(Pipeline, InShaderMap));
}
}
void FShaderMapContent::Validate(const FShaderMapBase& InShaderMap) const
{
for (const FShader* Shader : Shaders)
{
checkf(Shader->GetResourceIndex() != INDEX_NONE, TEXT("Missing resource for %s"), Shader->GetType(InShaderMap.GetPointerTable())->GetName());
}
/*for(FShaderPipeline* Pipeline : ShaderPipelines)
{
for(const TShaderRef<FShader>& Shader : Pipeline->GetShaders(InPtrTable))
{
checkf(Shader.GetResource(), TEXT("Missing resource for %s"), Shader.GetType()->GetName());
}
}*/
}
#if WITH_EDITOR
static bool CheckOutdatedShaderType(EShaderPlatform Platform, const TShaderRef<FShader>& Shader, TArray<const FShaderType*>& OutdatedShaderTypes, TArray<const FVertexFactoryType*>& OutdatedFactoryTypes)
{
const FShaderType* Type = Shader.GetType();
const bool bOutdatedShader = Type->GetSourceHash(Platform) != Shader->GetHash();
const FVertexFactoryType* VFType = Shader.GetVertexFactoryType();
const bool bOutdatedVertexFactory = VFType && VFType->GetSourceHash(Platform) != Shader->GetVertexFactoryHash();
if (bOutdatedShader)
{
OutdatedShaderTypes.AddUnique(Type);
}
if (bOutdatedVertexFactory)
{
OutdatedFactoryTypes.AddUnique(VFType);
}
return bOutdatedShader || bOutdatedVertexFactory;
}
void FShaderMapContent::GetOutdatedTypes(const FShaderMapBase& InShaderMap, TArray<const FShaderType*>& OutdatedShaderTypes, TArray<const FShaderPipelineType*>& OutdatedShaderPipelineTypes, TArray<const FVertexFactoryType*>& OutdatedFactoryTypes) const
{
for (FShader* Shader : Shaders)
{
CheckOutdatedShaderType(GetShaderPlatform(), TShaderRef<FShader>(Shader, InShaderMap), OutdatedShaderTypes, OutdatedFactoryTypes);
}
for (const FShaderPipeline* Pipeline : ShaderPipelines)
{
for (const TShaderRef<FShader>& Shader : Pipeline->GetShaders(InShaderMap))
{
if (CheckOutdatedShaderType(GetShaderPlatform(), Shader, OutdatedShaderTypes, OutdatedFactoryTypes))
{
const FShaderPipelineType* PipelineType = FShaderPipelineType::GetShaderPipelineTypeByName(Pipeline->TypeName);
check(PipelineType);
OutdatedShaderPipelineTypes.AddUnique(PipelineType);
}
}
}
}
void FShaderMapContent::SaveShaderStableKeys(const FShaderMapBase& InShaderMap, EShaderPlatform TargetShaderPlatform, const struct FStableShaderKeyAndValue& SaveKeyVal)
{
for (int32 ShaderIndex = 0; ShaderIndex < Shaders.Num(); ++ShaderIndex)
{
const int32 PermutationId = ShaderPermutations[ShaderIndex];
Shaders[ShaderIndex]->SaveShaderStableKeys(InShaderMap.GetPointerTable(), TargetShaderPlatform, PermutationId, SaveKeyVal);
}
for (const FShaderPipeline* Pipeline : ShaderPipelines)
{
Pipeline->SaveShaderStableKeys(InShaderMap.GetPointerTable(), TargetShaderPlatform, SaveKeyVal);
}
}
uint32 FShaderMapContent::GetMaxTextureSamplersShaderMap(const FShaderMapBase& InShaderMap) const
{
uint32 MaxTextureSamplers = 0;
for (FShader* Shader : Shaders)
{
if (ensure(Shader))
{
MaxTextureSamplers = FMath::Max(MaxTextureSamplers, Shader->GetNumTextureSamplers());
}
}
for (FShaderPipeline* Pipeline : ShaderPipelines)
{
for (const TShaderRef<FShader>& Shader : Pipeline->GetShaders(InShaderMap))
{
MaxTextureSamplers = FMath::Max(MaxTextureSamplers, Shader->GetNumTextureSamplers());
}
}
return MaxTextureSamplers;
}
#endif // WITH_EDITOR
uint32 FShaderMapContent::GetNumShaders() const
{
uint32 NumShaders = Shaders.Num();
for (FShaderPipeline* Pipeline : ShaderPipelines)
{
NumShaders += Pipeline->GetNumShaders();
}
return NumShaders;
}
uint32 FShaderMapContent::GetMaxNumInstructionsForShader(const FShaderMapBase& InShaderMap, FShaderType* ShaderType) const
{
uint32 MaxNumInstructions = 0;
FShader* Shader = GetShader(ShaderType);
if (Shader)
{
MaxNumInstructions = FMath::Max(MaxNumInstructions, Shader->GetNumInstructions());
}
for (FShaderPipeline* Pipeline : ShaderPipelines)
{
FShader* PipelineShader = Pipeline->GetShader(ShaderType->GetFrequency());
if (PipelineShader)
{
const FShaderType* PipelineShaderType = PipelineShader->GetType(InShaderMap.GetPointerTable());
if (PipelineShaderType &&
(PipelineShaderType == ShaderType))
{
MaxNumInstructions = FMath::Max(MaxNumInstructions, PipelineShader->GetNumInstructions());
}
}
}
return MaxNumInstructions;
}
#if WITH_EDITORONLY_DATA
TArray<FGenericShaderStat> FShaderMapContent::GetShaderStatistics(const FShaderMapBase& InShaderMap, FShaderType* ShaderType) const
{
TArray<FGenericShaderStat> ShaderStatistics;
FShader* Shader = GetShader(ShaderType);
if (Shader)
{
ShaderStatistics = InShaderMap.GetShaderStatistics(*Shader);
}
return ShaderStatistics;
}
#endif // WITH_EDITORONLY_DATA
struct FSortedShaderEntry
{
FHashedName TypeName;
int32 PermutationId;
int32 Index;
friend bool operator<(const FSortedShaderEntry& Lhs, const FSortedShaderEntry& Rhs)
{
if (Lhs.TypeName != Rhs.TypeName)
{
return Lhs.TypeName < Rhs.TypeName;
}
return Lhs.PermutationId < Rhs.PermutationId;
}
};
void FShaderMapContent::Finalize(const FShaderMapResourceCode* Code)
{
check(Code);
for (FShader* Shader : Shaders)
{
Shader->Finalize(Code);
}
for (FShaderPipeline* Pipeline : ShaderPipelines)
{
Pipeline->Finalize(Code);
}
// Sort the shaders by type/permutation, so they are consistently ordered
TArray<FSortedShaderEntry> SortedEntries;
SortedEntries.Empty(Shaders.Num());
for (int32 ShaderIndex = 0; ShaderIndex < Shaders.Num(); ++ShaderIndex)
{
FSortedShaderEntry& Entry = SortedEntries.AddDefaulted_GetRef();
Entry.TypeName = ShaderTypes[ShaderIndex];
Entry.PermutationId = ShaderPermutations[ShaderIndex];
Entry.Index = ShaderIndex;
}
SortedEntries.Sort();
// Choose a good hash size based on the number of shaders we have
const uint32 HashSize = FMath::RoundUpToPowerOfTwo(FMath::Max((Shaders.Num() * 3) / 2, 1));
FMemoryImageHashTable NewShaderHash(HashSize, Shaders.Num());
TMemoryImageArray<TMemoryImagePtr<FShader>> NewShaders;
NewShaders.Empty(Shaders.Num());
ShaderTypes.Empty(Shaders.Num());
ShaderPermutations.Empty(Shaders.Num());
for (int32 SortedIndex = 0; SortedIndex < SortedEntries.Num(); ++SortedIndex)
{
const FSortedShaderEntry& SortedEntry = SortedEntries[SortedIndex];
const uint16 Key = MakeShaderHash(SortedEntry.TypeName, SortedEntry.PermutationId);
NewShaders.Add(Shaders[SortedEntry.Index]);
ShaderTypes.Add(SortedEntry.TypeName);
ShaderPermutations.Add(SortedEntry.PermutationId);
NewShaderHash.Add(Key, SortedIndex);
}
Shaders = MoveTemp(NewShaders);
ShaderHash = MoveTemp(NewShaderHash);
}
void FShaderMapContent::UpdateHash(FSHA1& Hasher) const
{
for (int32 ShaderIndex = 0; ShaderIndex < Shaders.Num(); ++ShaderIndex)
{
const uint64 TypeNameHash = ShaderTypes[ShaderIndex].GetHash();
const int32 PermutationId = ShaderPermutations[ShaderIndex];
Hasher.Update((uint8*)&TypeNameHash, sizeof(TypeNameHash));
Hasher.Update((uint8*)&PermutationId, sizeof(PermutationId));
}
for (const FShaderPipeline* Pipeline : GetShaderPipelines())
{
const uint64 TypeNameHash = Pipeline->TypeName.GetHash();
Hasher.Update((uint8*)&TypeNameHash, sizeof(TypeNameHash));
}
}
void FShaderMapContent::Empty()
{
EmptyShaderPipelines();
for (int32 i = 0; i < Shaders.Num(); ++i)
{
TMemoryImagePtr<FShader>& Shader = Shaders[i];
Shader.SafeDelete();
}
Shaders.Empty();
ShaderTypes.Empty();
ShaderPermutations.Empty();
ShaderHash.Clear();
}
void FShaderMapContent::EmptyShaderPipelines()
{
for (TMemoryImagePtr<FShaderPipeline>& Pipeline : ShaderPipelines)
{
Pipeline.SafeDelete();
}
ShaderPipelines.Empty();
}