You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
#preflight 619ce604f934c1a291f81cfe #rb aleksander.netzel #ROBOMERGE-AUTHOR: yuriy.odonnell #ROBOMERGE-SOURCE: CL 18270186 in //UE5/Release-5.0/... via CL 18270194 #ROBOMERGE-BOT: STARSHIP (Release-Engine-Staging -> Release-Engine-Test) (v895-18170469) [CL 18270205 by yuriy odonnell in ue5-release-engine-test branch]
1406 lines
48 KiB
C++
1406 lines
48 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "ShaderCodeArchive.h"
|
|
#include "ShaderCodeLibrary.h"
|
|
#include "Stats/Stats.h"
|
|
#include "ProfilingDebugging/LoadTimeTracker.h"
|
|
#include "Misc/ScopeRWLock.h"
|
|
#include "Misc/MemStack.h"
|
|
#include "Policies/PrettyJsonPrintPolicy.h"
|
|
#include "Serialization/JsonSerializer.h"
|
|
#include "Misc/FileHelper.h"
|
|
#include "Serialization/MemoryReader.h"
|
|
#if WITH_EDITOR
|
|
#include "Misc/StringBuilder.h"
|
|
#include "Templates/Greater.h"
|
|
#endif
|
|
|
|
int32 GShaderCodeLibraryAsyncLoadingPriority = int32(AIOP_Normal);
|
|
static FAutoConsoleVariableRef CVarShaderCodeLibraryAsyncLoadingPriority(
|
|
TEXT("r.ShaderCodeLibrary.DefaultAsyncIOPriority"),
|
|
GShaderCodeLibraryAsyncLoadingPriority,
|
|
TEXT(""),
|
|
ECVF_Default
|
|
);
|
|
|
|
int32 GShaderCodeLibraryAsyncLoadingAllowDontCache = 0;
|
|
static FAutoConsoleVariableRef CVarShaderCodeLibraryAsyncLoadingAllowDontCache(
|
|
TEXT("r.ShaderCodeLibrary.AsyncIOAllowDontCache"),
|
|
GShaderCodeLibraryAsyncLoadingAllowDontCache,
|
|
TEXT(""),
|
|
ECVF_Default
|
|
);
|
|
|
|
int32 FSerializedShaderArchive::FindShaderMapWithKey(const FSHAHash& Hash, uint32 Key) const
|
|
{
|
|
for (uint32 Index = ShaderMapHashTable.First(Key); ShaderMapHashTable.IsValid(Index); Index = ShaderMapHashTable.Next(Index))
|
|
{
|
|
if (ShaderMapHashes[Index] == Hash)
|
|
{
|
|
return Index;
|
|
}
|
|
}
|
|
return INDEX_NONE;
|
|
}
|
|
|
|
int32 FSerializedShaderArchive::FindShaderMap(const FSHAHash& Hash) const
|
|
{
|
|
const uint32 Key = GetTypeHash(Hash);
|
|
return FindShaderMapWithKey(Hash, Key);
|
|
}
|
|
|
|
bool FSerializedShaderArchive::FindOrAddShaderMap(const FSHAHash& Hash, int32& OutIndex, const FShaderMapAssetPaths* AssociatedAssets)
|
|
{
|
|
const uint32 Key = GetTypeHash(Hash);
|
|
int32 Index = FindShaderMapWithKey(Hash, Key);
|
|
bool bAdded = false;
|
|
if (Index == INDEX_NONE)
|
|
{
|
|
Index = ShaderMapHashes.Add(Hash);
|
|
ShaderMapEntries.AddDefaulted();
|
|
check(ShaderMapEntries.Num() == ShaderMapHashes.Num());
|
|
ShaderMapHashTable.Add(Key, Index);
|
|
#if WITH_EDITOR
|
|
if (AssociatedAssets && AssociatedAssets->Num() > 0)
|
|
{
|
|
ShaderCodeToAssets.Add(Hash, *AssociatedAssets);
|
|
}
|
|
#endif
|
|
bAdded = true;
|
|
}
|
|
else
|
|
{
|
|
#if WITH_EDITOR
|
|
// check if we need to replace or merge assets
|
|
if (AssociatedAssets && AssociatedAssets->Num())
|
|
{
|
|
FShaderMapAssetPaths* PrevAssets = ShaderCodeToAssets.Find(Hash);
|
|
if (PrevAssets)
|
|
{
|
|
int PrevAssetsNum = PrevAssets->Num();
|
|
PrevAssets->Append(*AssociatedAssets);
|
|
}
|
|
else
|
|
{
|
|
ShaderCodeToAssets.Add(Hash, *AssociatedAssets);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
OutIndex = Index;
|
|
return bAdded;
|
|
}
|
|
|
|
int32 FSerializedShaderArchive::FindShaderWithKey(const FSHAHash& Hash, uint32 Key) const
|
|
{
|
|
for (uint32 Index = ShaderHashTable.First(Key); ShaderHashTable.IsValid(Index); Index = ShaderHashTable.Next(Index))
|
|
{
|
|
if (ShaderHashes[Index] == Hash)
|
|
{
|
|
return Index;
|
|
}
|
|
}
|
|
return INDEX_NONE;
|
|
}
|
|
|
|
int32 FSerializedShaderArchive::FindShader(const FSHAHash& Hash) const
|
|
{
|
|
const uint32 Key = GetTypeHash(Hash);
|
|
return FindShaderWithKey(Hash, Key);
|
|
}
|
|
|
|
bool FSerializedShaderArchive::FindOrAddShader(const FSHAHash& Hash, int32& OutIndex)
|
|
{
|
|
const uint32 Key = GetTypeHash(Hash);
|
|
int32 Index = FindShaderWithKey(Hash, Key);
|
|
bool bAdded = false;
|
|
if (Index == INDEX_NONE)
|
|
{
|
|
Index = ShaderHashes.Add(Hash);
|
|
ShaderEntries.AddDefaulted();
|
|
check(ShaderEntries.Num() == ShaderHashes.Num());
|
|
ShaderHashTable.Add(Key, Index);
|
|
bAdded = true;
|
|
}
|
|
|
|
OutIndex = Index;
|
|
return bAdded;
|
|
}
|
|
|
|
void FSerializedShaderArchive::DecompressShader(int32 Index, const TArray<TArray<uint8>>& ShaderCode, TArray<uint8>& OutDecompressedShader) const
|
|
{
|
|
const FShaderCodeEntry& Entry = ShaderEntries[Index];
|
|
OutDecompressedShader.SetNum(Entry.UncompressedSize, false);
|
|
if (Entry.Size == Entry.UncompressedSize)
|
|
{
|
|
FMemory::Memcpy(OutDecompressedShader.GetData(), ShaderCode[Index].GetData(), Entry.UncompressedSize);
|
|
}
|
|
else
|
|
{
|
|
bool bSucceed = FCompression::UncompressMemory(GetShaderCompressionFormat(), OutDecompressedShader.GetData(), Entry.UncompressedSize, ShaderCode[Index].GetData(), Entry.Size);
|
|
check(bSucceed);
|
|
}
|
|
}
|
|
|
|
void FSerializedShaderArchive::Finalize()
|
|
{
|
|
// Set the correct offsets
|
|
{
|
|
uint64 Offset = 0u;
|
|
for (FShaderCodeEntry& Entry : ShaderEntries)
|
|
{
|
|
Entry.Offset = Offset;
|
|
Offset += Entry.Size;
|
|
}
|
|
}
|
|
|
|
PreloadEntries.Empty();
|
|
for (FShaderMapEntry& ShaderMapEntry : ShaderMapEntries)
|
|
{
|
|
check(ShaderMapEntry.NumShaders > 0u);
|
|
TArray<FFileCachePreloadEntry> SortedPreloadEntries;
|
|
SortedPreloadEntries.Empty(ShaderMapEntry.NumShaders + 1);
|
|
for (uint32 i = 0; i < ShaderMapEntry.NumShaders; ++i)
|
|
{
|
|
const int32 ShaderIndex = ShaderIndices[ShaderMapEntry.ShaderIndicesOffset + i];
|
|
const FShaderCodeEntry& ShaderEntry = ShaderEntries[ShaderIndex];
|
|
SortedPreloadEntries.Add(FFileCachePreloadEntry(ShaderEntry.Offset, ShaderEntry.Size));
|
|
}
|
|
SortedPreloadEntries.Sort([](const FFileCachePreloadEntry& Lhs, const FFileCachePreloadEntry& Rhs) { return Lhs.Offset < Rhs.Offset; });
|
|
SortedPreloadEntries.Add(FFileCachePreloadEntry(INT64_MAX, 0));
|
|
|
|
ShaderMapEntry.FirstPreloadIndex = PreloadEntries.Num();
|
|
FFileCachePreloadEntry CurrentPreloadEntry = SortedPreloadEntries[0];
|
|
for (uint32 PreloadIndex = 1; PreloadIndex <= ShaderMapEntry.NumShaders; ++PreloadIndex)
|
|
{
|
|
const FFileCachePreloadEntry& PreloadEntry = SortedPreloadEntries[PreloadIndex];
|
|
const int64 Gap = PreloadEntry.Offset - CurrentPreloadEntry.Offset - CurrentPreloadEntry.Size;
|
|
checkf(Gap >= 0, TEXT("Overlapping preload entries, [%lld-%lld), [%lld-%lld)"),
|
|
CurrentPreloadEntry.Offset, CurrentPreloadEntry.Offset + CurrentPreloadEntry.Size, PreloadEntry.Offset, PreloadEntry.Offset + PreloadEntry.Size);
|
|
if (Gap > 1024)
|
|
{
|
|
++ShaderMapEntry.NumPreloadEntries;
|
|
PreloadEntries.Add(CurrentPreloadEntry);
|
|
CurrentPreloadEntry = PreloadEntry;
|
|
}
|
|
else
|
|
{
|
|
CurrentPreloadEntry.Size = PreloadEntry.Offset + PreloadEntry.Size - CurrentPreloadEntry.Offset;
|
|
}
|
|
}
|
|
check(ShaderMapEntry.NumPreloadEntries > 0u);
|
|
check(CurrentPreloadEntry.Size == 0);
|
|
}
|
|
}
|
|
|
|
void FSerializedShaderArchive::Serialize(FArchive& Ar)
|
|
{
|
|
Ar << ShaderMapHashes;
|
|
Ar << ShaderHashes;
|
|
Ar << ShaderMapEntries;
|
|
Ar << ShaderEntries;
|
|
Ar << PreloadEntries;
|
|
Ar << ShaderIndices;
|
|
|
|
check(ShaderHashes.Num() == ShaderEntries.Num());
|
|
check(ShaderMapHashes.Num() == ShaderMapEntries.Num());
|
|
|
|
if (Ar.IsLoading())
|
|
{
|
|
{
|
|
const uint32 HashSize = FMath::Min<uint32>(0x10000, 1u << FMath::CeilLogTwo(ShaderMapHashes.Num()));
|
|
ShaderMapHashTable.Clear(HashSize, ShaderMapHashes.Num());
|
|
for (int32 Index = 0; Index < ShaderMapHashes.Num(); ++Index)
|
|
{
|
|
const uint32 Key = GetTypeHash(ShaderMapHashes[Index]);
|
|
ShaderMapHashTable.Add(Key, Index);
|
|
}
|
|
}
|
|
{
|
|
const uint32 HashSize = FMath::Min<uint32>(0x10000, 1u << FMath::CeilLogTwo(ShaderHashes.Num()));
|
|
ShaderHashTable.Clear(HashSize, ShaderHashes.Num());
|
|
for (int32 Index = 0; Index < ShaderHashes.Num(); ++Index)
|
|
{
|
|
const uint32 Key = GetTypeHash(ShaderHashes[Index]);
|
|
ShaderHashTable.Add(Key, Index);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#if WITH_EDITOR
|
|
void FSerializedShaderArchive::SaveAssetInfo(FArchive& Ar)
|
|
{
|
|
if (Ar.IsSaving())
|
|
{
|
|
FString JsonTcharText;
|
|
{
|
|
TSharedRef<TJsonWriter<TCHAR, TPrettyJsonPrintPolicy<TCHAR>>> Writer = TJsonWriterFactory<TCHAR, TPrettyJsonPrintPolicy<TCHAR>>::Create(&JsonTcharText);
|
|
Writer->WriteObjectStart();
|
|
|
|
Writer->WriteValue(TEXT("AssetInfoVersion"), static_cast<int32>(EAssetInfoVersion::CurrentVersion));
|
|
|
|
Writer->WriteArrayStart(TEXT("ShaderCodeToAssets"));
|
|
for (TMap<FSHAHash, FShaderMapAssetPaths>::TConstIterator Iter(ShaderCodeToAssets); Iter; ++Iter)
|
|
{
|
|
Writer->WriteObjectStart();
|
|
const FSHAHash& Hash = Iter.Key();
|
|
Writer->WriteValue(TEXT("ShaderMapHash"), Hash.ToString());
|
|
const FShaderMapAssetPaths& Assets = Iter.Value();
|
|
Writer->WriteArrayStart(TEXT("Assets"));
|
|
for (FShaderMapAssetPaths::TConstIterator AssetIter(Assets); AssetIter; ++AssetIter)
|
|
{
|
|
Writer->WriteValue((*AssetIter).ToString());
|
|
}
|
|
Writer->WriteArrayEnd();
|
|
Writer->WriteObjectEnd();
|
|
}
|
|
Writer->WriteArrayEnd();
|
|
|
|
Writer->WriteObjectEnd();
|
|
Writer->Close();
|
|
}
|
|
|
|
FTCHARToUTF8 JsonUtf8(*JsonTcharText);
|
|
Ar.Serialize(const_cast<void *>(reinterpret_cast<const void*>(JsonUtf8.Get())), JsonUtf8.Length() * sizeof(UTF8CHAR));
|
|
}
|
|
}
|
|
|
|
bool FSerializedShaderArchive::LoadAssetInfo(const FString& Filename)
|
|
{
|
|
TArray<uint8> FileData;
|
|
if (!FFileHelper::LoadFileToArray(FileData, *Filename))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
FString JsonText;
|
|
FFileHelper::BufferToString(JsonText, FileData.GetData(), FileData.Num());
|
|
|
|
TSharedPtr<FJsonObject> JsonObject;
|
|
TSharedRef<TJsonReader<TCHAR>> Reader = TJsonReaderFactory<TCHAR>::Create(JsonText);
|
|
|
|
// Attempt to deserialize JSON
|
|
if (!FJsonSerializer::Deserialize(Reader, JsonObject) || !JsonObject.IsValid())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
TSharedPtr<FJsonValue> AssetInfoVersion = JsonObject->Values.FindRef(TEXT("AssetInfoVersion"));
|
|
if (!AssetInfoVersion.IsValid())
|
|
{
|
|
UE_LOG(LogShaderLibrary, Warning, TEXT("Rejecting asset info file %s: missing AssetInfoVersion (damaged file?)"),
|
|
*Filename);
|
|
return false;
|
|
}
|
|
|
|
const EAssetInfoVersion FileVersion = static_cast<EAssetInfoVersion>(static_cast<int64>(AssetInfoVersion->AsNumber()));
|
|
if (FileVersion != EAssetInfoVersion::CurrentVersion)
|
|
{
|
|
UE_LOG(LogShaderLibrary, Warning, TEXT("Rejecting asset info file %s: expected version %d, got unsupported version %d."),
|
|
*Filename, static_cast<int32>(EAssetInfoVersion::CurrentVersion), static_cast<int32>(FileVersion));
|
|
return false;
|
|
}
|
|
|
|
TSharedPtr<FJsonValue> AssetInfoArrayValue = JsonObject->Values.FindRef(TEXT("ShaderCodeToAssets"));
|
|
if (!AssetInfoArrayValue.IsValid())
|
|
{
|
|
UE_LOG(LogShaderLibrary, Warning, TEXT("Rejecting asset info file %s: missing ShaderCodeToAssets array (damaged file?)"),
|
|
*Filename);
|
|
return false;
|
|
}
|
|
|
|
TArray<TSharedPtr<FJsonValue>> AssetInfoArray = AssetInfoArrayValue->AsArray();
|
|
UE_LOG(LogShaderLibrary, Display, TEXT("Reading asset info file %s: found %d existing mappings"),
|
|
*Filename, AssetInfoArray.Num());
|
|
|
|
for (int32 IdxPair = 0, NumPairs = AssetInfoArray.Num(); IdxPair < NumPairs; ++IdxPair)
|
|
{
|
|
TSharedPtr<FJsonObject> Pair = AssetInfoArray[IdxPair]->AsObject();
|
|
if (UNLIKELY(!Pair.IsValid()))
|
|
{
|
|
UE_LOG(LogShaderLibrary, Warning, TEXT("Rejecting asset info file %s: ShaderCodeToAssets array contains unreadable mapping #%d (damaged file?)"),
|
|
*Filename,
|
|
IdxPair
|
|
);
|
|
return false;
|
|
}
|
|
|
|
TSharedPtr<FJsonValue> ShaderMapHashJson = Pair->Values.FindRef(TEXT("ShaderMapHash"));
|
|
if (UNLIKELY(!ShaderMapHashJson.IsValid()))
|
|
{
|
|
UE_LOG(LogShaderLibrary, Warning, TEXT("Rejecting asset info file %s: ShaderCodeToAssets array contains unreadable ShaderMapHash for mapping %d (damaged file?)"),
|
|
*Filename,
|
|
IdxPair
|
|
);
|
|
return false;
|
|
}
|
|
|
|
FSHAHash ShaderMapHash;
|
|
ShaderMapHash.FromString(ShaderMapHashJson->AsString());
|
|
|
|
TSharedPtr<FJsonValue> AssetPathsArrayValue = Pair->Values.FindRef(TEXT("Assets"));
|
|
if (UNLIKELY(!AssetPathsArrayValue.IsValid()))
|
|
{
|
|
UE_LOG(LogShaderLibrary, Warning, TEXT("Rejecting asset info file %s: ShaderCodeToAssets array contains unreadable Assets array for mapping %d (damaged file?)"),
|
|
*Filename,
|
|
IdxPair
|
|
);
|
|
return false;
|
|
}
|
|
|
|
FShaderMapAssetPaths Paths;
|
|
TArray<TSharedPtr<FJsonValue>> AssetPathsArray = AssetPathsArrayValue->AsArray();
|
|
for (int32 IdxAsset = 0, NumAssets = AssetPathsArray.Num(); IdxAsset < NumAssets; ++IdxAsset)
|
|
{
|
|
Paths.Add(FName(*AssetPathsArray[IdxAsset]->AsString()));
|
|
}
|
|
|
|
ShaderCodeToAssets.Add(ShaderMapHash, Paths);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void FSerializedShaderArchive::CreateAsChunkFrom(const FSerializedShaderArchive& Parent, const TSet<FName>& PackagesInChunk, TArray<int32>& OutShaderCodeEntriesNeeded)
|
|
{
|
|
// we should begin with a clean slate
|
|
checkf(ShaderMapHashes.Num() == 0 && ShaderHashes.Num() == 0 && ShaderMapEntries.Num() == 0 && ShaderEntries.Num() == 0 && PreloadEntries.Num() == 0 && ShaderIndices.Num() == 0,
|
|
TEXT("Expecting a new, uninitialized FSerializedShaderArchive instance for creating a chunk."));
|
|
|
|
// go through parent's shadermap hashes in the order of their addition
|
|
for (int32 IdxSM = 0, NumSMs = Parent.ShaderMapHashes.Num(); IdxSM < NumSMs; ++IdxSM)
|
|
{
|
|
const FSHAHash& ShaderMapHash = Parent.ShaderMapHashes[IdxSM];
|
|
const FShaderMapAssetPaths* Assets = Parent.ShaderCodeToAssets.Find(ShaderMapHash);
|
|
bool bIncludeSM = false;
|
|
if (UNLIKELY(Assets == nullptr))
|
|
{
|
|
UE_LOG(LogShaderLibrary, Warning, TEXT("Shadermap %s is not associated with any asset. Including it in every chunk"), *ShaderMapHash.ToString());
|
|
bIncludeSM = true;
|
|
}
|
|
else
|
|
{
|
|
// if any asset is in the chunk, include
|
|
for (const FName& Asset : *Assets)
|
|
{
|
|
if (PackagesInChunk.Contains(Asset))
|
|
{
|
|
bIncludeSM = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bIncludeSM)
|
|
{
|
|
// add this shader map
|
|
int32 ShaderMapIndex = INDEX_NONE;
|
|
if (FindOrAddShaderMap(ShaderMapHash, ShaderMapIndex, Assets))
|
|
{
|
|
// if we're in this scope, it means it's a new shadermap for the chunk and we need more information about it from the parent
|
|
int32 ParentShaderMapIndex = IdxSM;
|
|
const FShaderMapEntry& ParentShaderMapDescriptor = Parent.ShaderMapEntries[ParentShaderMapIndex];
|
|
|
|
const int32 NumShaders = ParentShaderMapDescriptor.NumShaders;
|
|
|
|
FShaderMapEntry& ShaderMapDescriptor = ShaderMapEntries[ShaderMapIndex];
|
|
ShaderMapDescriptor.NumShaders = NumShaders;
|
|
ShaderMapDescriptor.ShaderIndicesOffset = ShaderIndices.AddZeroed(NumShaders);
|
|
|
|
// add shader by shader
|
|
for (int32 ShaderIdx = 0; ShaderIdx < NumShaders; ++ShaderIdx)
|
|
{
|
|
int32 ParentShaderIndex = Parent.ShaderIndices[ParentShaderMapDescriptor.ShaderIndicesOffset + ShaderIdx];
|
|
|
|
int32 ShaderIndex = INDEX_NONE;
|
|
if (FindOrAddShader(Parent.ShaderHashes[ParentShaderIndex], ShaderIndex))
|
|
{
|
|
// new shader! add it to the mapping of parent shadercode entries to ours. and check the integrity of the mapping
|
|
checkf(OutShaderCodeEntriesNeeded.Num() == ShaderIndex, TEXT("Mapping between the shader indices in a chunk and the whole archive is inconsistent"));
|
|
OutShaderCodeEntriesNeeded.Add(ParentShaderIndex);
|
|
|
|
// copy the entry as is
|
|
ShaderEntries[ShaderIndex] = Parent.ShaderEntries[ParentShaderIndex];
|
|
}
|
|
ShaderIndices[ShaderMapDescriptor.ShaderIndicesOffset + ShaderIdx] = ShaderIndex;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void FSerializedShaderArchive::CollectStatsAndDebugInfo(FDebugStats& OutDebugStats, FExtendedDebugStats* OutExtendedDebugStats)
|
|
{
|
|
// collect the light-weight stats first
|
|
FMemory::Memzero(OutDebugStats);
|
|
OutDebugStats.NumUniqueShaders = ShaderHashes.Num();
|
|
OutDebugStats.NumShaderMaps = ShaderMapHashes.Num();
|
|
int32 TotalShaders = 0;
|
|
int64 TotalShaderSize = 0;
|
|
uint32 MinSMSizeInShaders = UINT_MAX;
|
|
uint32 MaxSMSizeInShaders = 0;
|
|
for (const FShaderMapEntry& SMEntry : ShaderMapEntries)
|
|
{
|
|
MinSMSizeInShaders = FMath::Min(MinSMSizeInShaders, SMEntry.NumShaders);
|
|
MaxSMSizeInShaders = FMath::Max(MaxSMSizeInShaders, SMEntry.NumShaders);
|
|
TotalShaders += SMEntry.NumShaders;
|
|
|
|
const int32 ThisSMShaders = SMEntry.NumShaders;
|
|
for (int32 ShaderIdx = 0; ShaderIdx < ThisSMShaders; ++ShaderIdx)
|
|
{
|
|
TotalShaderSize += ShaderEntries[ShaderIndices[SMEntry.ShaderIndicesOffset + ShaderIdx]].Size;
|
|
}
|
|
}
|
|
OutDebugStats.NumShaders = TotalShaders;
|
|
OutDebugStats.ShadersSize = TotalShaderSize;
|
|
|
|
// this is moderately expensive, consider moving to ExtendedStats?
|
|
{
|
|
TSet<FName> AllAssets;
|
|
for (TMap<FSHAHash, FShaderMapAssetPaths>::TConstIterator Iter(ShaderCodeToAssets); Iter; ++Iter)
|
|
{
|
|
for (const FName& AssetName : Iter.Value())
|
|
{
|
|
AllAssets.Add(AssetName);
|
|
}
|
|
}
|
|
OutDebugStats.NumAssets = AllAssets.Num();
|
|
}
|
|
|
|
int64 ActuallySavedShaderSize = 0;
|
|
for (const FShaderCodeEntry& ShaderEntry : ShaderEntries)
|
|
{
|
|
ActuallySavedShaderSize += ShaderEntry.Size;
|
|
}
|
|
OutDebugStats.ShadersUniqueSize = ActuallySavedShaderSize;
|
|
|
|
// If OutExtendedDebugStats pointer is passed, we're asked to fill out a heavy-weight stats.
|
|
if (OutExtendedDebugStats)
|
|
{
|
|
// textual rep
|
|
DumpContentsInPlaintext(OutExtendedDebugStats->TextualRepresentation);
|
|
|
|
OutExtendedDebugStats->MinNumberOfShadersPerSM = MinSMSizeInShaders;
|
|
OutExtendedDebugStats->MaxNumberofShadersPerSM = MaxSMSizeInShaders;
|
|
|
|
// median SM size in shaders
|
|
TArray<int32> ShadersInSM;
|
|
|
|
// shader usage
|
|
TMap<int32, int32> ShaderToUsageMap;
|
|
|
|
for (const FShaderMapEntry& SMEntry : ShaderMapEntries)
|
|
{
|
|
const int32 ThisSMShaders = SMEntry.NumShaders;
|
|
ShadersInSM.Add(ThisSMShaders);
|
|
|
|
for (int32 ShaderIdx = 0; ShaderIdx < ThisSMShaders; ++ShaderIdx)
|
|
{
|
|
int ShaderIndex = ShaderIndices[SMEntry.ShaderIndicesOffset + ShaderIdx];
|
|
int32& Usage = ShaderToUsageMap.FindOrAdd(ShaderIndex, 0);
|
|
++Usage;
|
|
}
|
|
}
|
|
|
|
ShadersInSM.Sort();
|
|
OutExtendedDebugStats->MedianNumberOfShadersPerSM = ShadersInSM.Num() ? ShadersInSM[ShadersInSM.Num() / 2] : 0;
|
|
|
|
ShaderToUsageMap.ValueSort(TGreater<int32>());
|
|
// add top 10 shaders
|
|
for (const TTuple<int32, int32>& UsagePair : ShaderToUsageMap)
|
|
{
|
|
OutExtendedDebugStats->TopShaderUsages.Add(UsagePair.Value);
|
|
if (OutExtendedDebugStats->TopShaderUsages.Num() >= 10)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
#if 0 // graph visualization - maybe one day we'll return to this
|
|
// enumerate all shaders first (so they can be identified by people looking them up in other debug output)
|
|
int32 IdxShaderNum = 0;
|
|
for (const FSHAHash& ShaderHash : ShaderHashes)
|
|
{
|
|
FString Numeral = FString::Printf(TEXT("Shd_%d"), IdxShaderNum);
|
|
OutRelationshipGraph->Add(TTuple<FString, FString>(Numeral, FString("Hash_") + ShaderHash.ToString()));
|
|
++IdxShaderNum;
|
|
}
|
|
|
|
// add all assets if any
|
|
for (TMap<FName, FSHAHash>::TConstIterator Iter(AssetToShaderCode); Iter; ++Iter)
|
|
{
|
|
int32 SMIndex = FindShaderMap(Iter.Value());
|
|
OutRelationshipGraph->Add(TTuple<FString, FString>(Iter.Key().ToString(), FString::Printf(TEXT("SM_%d"), SMIndex)));
|
|
}
|
|
|
|
// shadermaps to shaders
|
|
int NumSMs = ShaderMapHashes.Num();
|
|
for (int32 IdxSM = 0; IdxSM < NumSMs; ++IdxSM)
|
|
{
|
|
FString SMId = FString::Printf(TEXT("SM_%d"), IdxSM);
|
|
const FShaderMapEntry& SMEntry = ShaderMapEntries[IdxSM];
|
|
|
|
const int32 ThisSMShaders = SMEntry.NumShaders;
|
|
for (int32 ShaderIdx = 0; ShaderIdx < ThisSMShaders; ++ShaderIdx)
|
|
{
|
|
FString ReferencedShader = FString::Printf(TEXT("Shd_%d"), ShaderIndices[SMEntry.ShaderIndicesOffset + ShaderIdx]);
|
|
OutRelationshipGraph->Add(TTuple<FString, FString>(SMId, ReferencedShader));
|
|
}
|
|
}
|
|
#endif // 0
|
|
}
|
|
|
|
void FSerializedShaderArchive::DumpContentsInPlaintext(FString& OutText) const
|
|
{
|
|
TStringBuilder<256> Out;
|
|
Out << TEXT("FSerializedShaderArchive\n{\n");
|
|
{
|
|
Out << TEXT("\tShaderMapHashes\n\t{\n");
|
|
for (int32 IdxMapHash = 0, NumMapHashes = ShaderMapHashes.Num(); IdxMapHash < NumMapHashes; ++IdxMapHash)
|
|
{
|
|
Out << TEXT("\t\t");
|
|
Out << ShaderMapHashes[IdxMapHash].ToString();
|
|
Out << TEXT("\n");
|
|
}
|
|
Out << TEXT("\t}\n");
|
|
}
|
|
|
|
{
|
|
Out << TEXT("\tShaderHashes\n\t{\n");
|
|
for (int32 IdxHash = 0, NumHashes = ShaderHashes.Num(); IdxHash < NumHashes; ++IdxHash)
|
|
{
|
|
Out << TEXT("\t\t");
|
|
Out << ShaderHashes[IdxHash].ToString();
|
|
Out << TEXT("\n");
|
|
}
|
|
Out << TEXT("\t}\n");
|
|
}
|
|
|
|
{
|
|
Out << TEXT("\tShaderMapEntries\n\t{\n");
|
|
for (int32 IdxEntry = 0, NumEntries = ShaderMapEntries.Num(); IdxEntry < NumEntries; ++IdxEntry)
|
|
{
|
|
Out << TEXT("\t\tFShaderMapEntry\n\t\t{\n");
|
|
|
|
Out << TEXT("\t\t\tShaderIndicesOffset : ");
|
|
Out << ShaderMapEntries[IdxEntry].ShaderIndicesOffset;
|
|
Out << TEXT("\n");
|
|
|
|
Out << TEXT("\t\t\tNumShaders : ");
|
|
Out << ShaderMapEntries[IdxEntry].NumShaders;
|
|
Out << TEXT("\n");
|
|
|
|
Out << TEXT("\t\t\tFirstPreloadIndex : ");
|
|
Out << ShaderMapEntries[IdxEntry].FirstPreloadIndex;
|
|
Out << TEXT("\n");
|
|
|
|
Out << TEXT("\t\t\tNumPreloadEntries : ");
|
|
Out << ShaderMapEntries[IdxEntry].NumPreloadEntries;
|
|
Out << TEXT("\n");
|
|
|
|
Out << TEXT("\t\t}\n");
|
|
}
|
|
Out << TEXT("\t}\n");
|
|
}
|
|
|
|
{
|
|
Out << TEXT("\tShaderEntries\n\t{\n");
|
|
for (int32 IdxEntry = 0, NumEntries = ShaderEntries.Num(); IdxEntry < NumEntries; ++IdxEntry)
|
|
{
|
|
Out << TEXT("\t\tFShaderCodeEntry\n\t\t{\n");
|
|
|
|
Out << TEXT("\t\t\tOffset : ");
|
|
Out << ShaderEntries[IdxEntry].Offset;
|
|
Out << TEXT("\n");
|
|
|
|
Out << TEXT("\t\t\tSize : ");
|
|
Out << ShaderEntries[IdxEntry].Size;
|
|
Out << TEXT("\n");
|
|
|
|
Out << TEXT("\t\t\tUncompressedSize : ");
|
|
Out << ShaderEntries[IdxEntry].UncompressedSize;
|
|
Out << TEXT("\n");
|
|
|
|
Out << TEXT("\t\t\tFrequency : ");
|
|
Out << ShaderEntries[IdxEntry].Frequency;
|
|
Out << TEXT("\n");
|
|
|
|
Out << TEXT("\t\t}\n");
|
|
}
|
|
Out << TEXT("\t}\n");
|
|
}
|
|
|
|
{
|
|
Out << TEXT("\tPreloadEntries\n\t{\n");
|
|
for (int32 IdxEntry = 0, NumEntries = PreloadEntries.Num(); IdxEntry < NumEntries; ++IdxEntry)
|
|
{
|
|
Out << TEXT("\t\tFFileCachePreloadEntry\n\t\t{\n");
|
|
|
|
Out << TEXT("\t\t\tOffset : ");
|
|
Out << PreloadEntries[IdxEntry].Offset;
|
|
Out << TEXT("\n");
|
|
|
|
Out << TEXT("\t\t\tSize : ");
|
|
Out << PreloadEntries[IdxEntry].Size;
|
|
Out << TEXT("\n");
|
|
|
|
Out << TEXT("\t\t}\n");
|
|
}
|
|
Out << TEXT("\t}\n");
|
|
}
|
|
|
|
{
|
|
Out << TEXT("\tShaderIndices\n\t{\n");
|
|
// split it by shadermaps
|
|
int32 IdxSMEntry = 0;
|
|
int32 NumShadersLeftInSM = ShaderMapEntries.Num() ? ShaderMapEntries[0].NumShaders : 0;
|
|
bool bNewSM = true;
|
|
for (int32 IdxEntry = 0, NumEntries = ShaderIndices.Num(); IdxEntry < NumEntries; ++IdxEntry)
|
|
{
|
|
if (UNLIKELY(bNewSM))
|
|
{
|
|
Out << TEXT("\t\t");
|
|
bNewSM = false;
|
|
}
|
|
else
|
|
{
|
|
Out << TEXT(", ");
|
|
}
|
|
Out << ShaderIndices[IdxEntry];
|
|
|
|
--NumShadersLeftInSM;
|
|
while (NumShadersLeftInSM == 0)
|
|
{
|
|
bNewSM = true;
|
|
++IdxSMEntry;
|
|
if (IdxSMEntry >= ShaderMapEntries.Num())
|
|
{
|
|
break;
|
|
}
|
|
NumShadersLeftInSM = ShaderMapEntries[IdxSMEntry].NumShaders;
|
|
}
|
|
|
|
if (bNewSM)
|
|
{
|
|
Out << TEXT("\n");
|
|
}
|
|
}
|
|
Out << TEXT("\t}\n");
|
|
}
|
|
|
|
Out << TEXT("}\n");
|
|
OutText = FStringView(Out);
|
|
}
|
|
|
|
#endif // WITH_EDITOR
|
|
|
|
FShaderCodeArchive* FShaderCodeArchive::Create(EShaderPlatform InPlatform, FArchive& Ar, const FString& InDestFilePath, const FString& InLibraryDir, const FString& InLibraryName)
|
|
{
|
|
FShaderCodeArchive* Library = new FShaderCodeArchive(InPlatform, InLibraryDir, InLibraryName);
|
|
Ar << Library->SerializedShaders;
|
|
Library->ShaderPreloads.SetNum(Library->SerializedShaders.GetNumShaders());
|
|
Library->LibraryCodeOffset = Ar.Tell();
|
|
|
|
// Open library for async reads
|
|
Library->FileCacheHandle = IFileCacheHandle::CreateFileCacheHandle(*InDestFilePath);
|
|
|
|
UE_LOG(LogShaderLibrary, Display, TEXT("Using %s for material shader code. Total %d unique shaders."), *InDestFilePath, Library->SerializedShaders.ShaderEntries.Num());
|
|
|
|
INC_DWORD_STAT_BY(STAT_Shaders_ShaderResourceMemory, Library->GetSizeBytes());
|
|
|
|
return Library;
|
|
}
|
|
|
|
FShaderCodeArchive::FShaderCodeArchive(EShaderPlatform InPlatform, const FString& InLibraryDir, const FString& InLibraryName)
|
|
: FRHIShaderLibrary(InPlatform, InLibraryName)
|
|
, LibraryDir(InLibraryDir)
|
|
, LibraryCodeOffset(0)
|
|
, FileCacheHandle(nullptr)
|
|
{
|
|
}
|
|
|
|
FShaderCodeArchive::~FShaderCodeArchive()
|
|
{
|
|
DEC_DWORD_STAT_BY(STAT_Shaders_ShaderResourceMemory, GetSizeBytes());
|
|
Teardown();
|
|
}
|
|
|
|
void FShaderCodeArchive::Teardown()
|
|
{
|
|
if (FileCacheHandle)
|
|
{
|
|
delete FileCacheHandle;
|
|
FileCacheHandle = nullptr;
|
|
}
|
|
|
|
for (int32 ShaderIndex = 0; ShaderIndex < SerializedShaders.GetNumShaders(); ++ShaderIndex)
|
|
{
|
|
FShaderPreloadEntry& ShaderPreloadEntry = ShaderPreloads[ShaderIndex];
|
|
if (ShaderPreloadEntry.Code)
|
|
{
|
|
const FShaderCodeEntry& ShaderEntry = SerializedShaders.ShaderEntries[ShaderIndex];
|
|
FMemory::Free(ShaderPreloadEntry.Code);
|
|
ShaderPreloadEntry.Code = nullptr;
|
|
DEC_DWORD_STAT_BY(STAT_Shaders_ShaderPreloadMemory, ShaderEntry.Size);
|
|
}
|
|
}
|
|
}
|
|
|
|
void FShaderCodeArchive::OnShaderPreloadFinished(int32 ShaderIndex, const IMemoryReadStreamRef& PreloadData)
|
|
{
|
|
const FShaderCodeEntry& ShaderEntry = SerializedShaders.ShaderEntries[ShaderIndex];
|
|
PreloadData->EnsureReadNonBlocking(); // Ensure data is ready before taking the lock
|
|
{
|
|
FWriteScopeLock Lock(ShaderPreloadLock);
|
|
FShaderPreloadEntry& ShaderPreloadEntry = ShaderPreloads[ShaderIndex];
|
|
PreloadData->CopyTo(ShaderPreloadEntry.Code, 0, ShaderEntry.Size);
|
|
ShaderPreloadEntry.PreloadEvent.SafeRelease();
|
|
}
|
|
}
|
|
|
|
struct FPreloadShaderTask
|
|
{
|
|
explicit FPreloadShaderTask(FShaderCodeArchive* InArchive, int32 InShaderIndex, const IMemoryReadStreamRef& InData)
|
|
: Archive(InArchive), Data(InData), ShaderIndex(InShaderIndex)
|
|
{}
|
|
|
|
FShaderCodeArchive* Archive;
|
|
IMemoryReadStreamRef Data;
|
|
int32 ShaderIndex;
|
|
|
|
void DoTask(ENamedThreads::Type CurrentThread, const FGraphEventRef& MyCompletionGraphEvent)
|
|
{
|
|
Archive->OnShaderPreloadFinished(ShaderIndex, Data);
|
|
Data.SafeRelease();
|
|
}
|
|
|
|
FORCEINLINE static ESubsequentsMode::Type GetSubsequentsMode() { return ESubsequentsMode::TrackSubsequents; }
|
|
FORCEINLINE ENamedThreads::Type GetDesiredThread() { return ENamedThreads::AnyNormalThreadNormalTask; }
|
|
FORCEINLINE TStatId GetStatId() const { return TStatId(); }
|
|
};
|
|
|
|
bool FShaderCodeArchive::PreloadShader(int32 ShaderIndex, FGraphEventArray& OutCompletionEvents)
|
|
{
|
|
LLM_SCOPE(ELLMTag::Shaders);
|
|
|
|
FWriteScopeLock Lock(ShaderPreloadLock);
|
|
|
|
FShaderPreloadEntry& ShaderPreloadEntry = ShaderPreloads[ShaderIndex];
|
|
checkf(!ShaderPreloadEntry.bNeverToBePreloaded, TEXT("We are preloading a shader that shouldn't be preloaded in this run (e.g. raytracing shader on D3D11)."));
|
|
const uint32 ShaderNumRefs = ShaderPreloadEntry.NumRefs++;
|
|
if (ShaderNumRefs == 0u)
|
|
{
|
|
check(!ShaderPreloadEntry.PreloadEvent);
|
|
|
|
const FShaderCodeEntry& ShaderEntry = SerializedShaders.ShaderEntries[ShaderIndex];
|
|
ShaderPreloadEntry.Code = FMemory::Malloc(ShaderEntry.Size);
|
|
ShaderPreloadEntry.FramePreloadStarted = GFrameNumber;
|
|
|
|
const EAsyncIOPriorityAndFlags IOPriority = (EAsyncIOPriorityAndFlags)GShaderCodeLibraryAsyncLoadingPriority;
|
|
|
|
FGraphEventArray ReadCompletionEvents;
|
|
|
|
EAsyncIOPriorityAndFlags DontCache = GShaderCodeLibraryAsyncLoadingAllowDontCache ? AIOP_FLAG_DONTCACHE : AIOP_MIN;
|
|
IMemoryReadStreamRef PreloadData = FileCacheHandle->ReadData(ReadCompletionEvents, LibraryCodeOffset + ShaderEntry.Offset, ShaderEntry.Size, IOPriority | DontCache);
|
|
auto Task = TGraphTask<FPreloadShaderTask>::CreateTask(&ReadCompletionEvents).ConstructAndHold(this, ShaderIndex, MoveTemp(PreloadData));
|
|
ShaderPreloadEntry.PreloadEvent = Task->GetCompletionEvent();
|
|
Task->Unlock();
|
|
|
|
INC_DWORD_STAT_BY(STAT_Shaders_ShaderPreloadMemory, ShaderEntry.Size);
|
|
}
|
|
|
|
if (ShaderPreloadEntry.PreloadEvent)
|
|
{
|
|
OutCompletionEvents.Add(ShaderPreloadEntry.PreloadEvent);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool FShaderCodeArchive::PreloadShaderMap(int32 ShaderMapIndex, FGraphEventArray& OutCompletionEvents)
|
|
{
|
|
LLM_SCOPE(ELLMTag::Shaders);
|
|
|
|
const FShaderMapEntry& ShaderMapEntry = SerializedShaders.ShaderMapEntries[ShaderMapIndex];
|
|
const EAsyncIOPriorityAndFlags IOPriority = (EAsyncIOPriorityAndFlags)GShaderCodeLibraryAsyncLoadingPriority;
|
|
const uint32 FrameNumber = GFrameNumber;
|
|
uint32 PreloadMemory = 0u;
|
|
|
|
FWriteScopeLock Lock(ShaderPreloadLock);
|
|
|
|
for (uint32 i = 0u; i < ShaderMapEntry.NumShaders; ++i)
|
|
{
|
|
const int32 ShaderIndex = SerializedShaders.ShaderIndices[ShaderMapEntry.ShaderIndicesOffset + i];
|
|
FShaderPreloadEntry& ShaderPreloadEntry = ShaderPreloads[ShaderIndex];
|
|
const FShaderCodeEntry& ShaderEntry = SerializedShaders.ShaderEntries[ShaderIndex];
|
|
|
|
#if RHI_RAYTRACING
|
|
if (!IsRayTracingEnabled() && IsRayTracingShaderFrequency(static_cast<EShaderFrequency>(ShaderEntry.Frequency)))
|
|
{
|
|
ShaderPreloadEntry.bNeverToBePreloaded = 1;
|
|
continue;
|
|
}
|
|
#endif
|
|
|
|
const uint32 ShaderNumRefs = ShaderPreloadEntry.NumRefs++;
|
|
if (ShaderNumRefs == 0u)
|
|
{
|
|
check(!ShaderPreloadEntry.PreloadEvent);
|
|
ShaderPreloadEntry.Code = FMemory::Malloc(ShaderEntry.Size);
|
|
ShaderPreloadEntry.FramePreloadStarted = FrameNumber;
|
|
PreloadMemory += ShaderEntry.Size;
|
|
|
|
FGraphEventArray ReadCompletionEvents;
|
|
EAsyncIOPriorityAndFlags DontCache = GShaderCodeLibraryAsyncLoadingAllowDontCache ? AIOP_FLAG_DONTCACHE : AIOP_MIN;
|
|
IMemoryReadStreamRef PreloadData = FileCacheHandle->ReadData(ReadCompletionEvents, LibraryCodeOffset + ShaderEntry.Offset, ShaderEntry.Size, IOPriority | DontCache);
|
|
auto Task = TGraphTask<FPreloadShaderTask>::CreateTask(&ReadCompletionEvents).ConstructAndHold(this, ShaderIndex, MoveTemp(PreloadData));
|
|
ShaderPreloadEntry.PreloadEvent = Task->GetCompletionEvent();
|
|
Task->Unlock();
|
|
OutCompletionEvents.Add(ShaderPreloadEntry.PreloadEvent);
|
|
}
|
|
else if (ShaderPreloadEntry.PreloadEvent)
|
|
{
|
|
OutCompletionEvents.Add(ShaderPreloadEntry.PreloadEvent);
|
|
}
|
|
}
|
|
|
|
INC_DWORD_STAT_BY(STAT_Shaders_ShaderPreloadMemory, PreloadMemory);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool FShaderCodeArchive::WaitForPreload(FShaderPreloadEntry& ShaderPreloadEntry)
|
|
{
|
|
FGraphEventRef Event;
|
|
{
|
|
FReadScopeLock Lock(ShaderPreloadLock);
|
|
if(ShaderPreloadEntry.NumRefs > 0u)
|
|
{
|
|
Event = ShaderPreloadEntry.PreloadEvent;
|
|
}
|
|
else
|
|
{
|
|
check(!ShaderPreloadEntry.PreloadEvent);
|
|
}
|
|
}
|
|
|
|
const bool bNeedToWait = Event && !Event->IsComplete();
|
|
if (bNeedToWait)
|
|
{
|
|
FTaskGraphInterface::Get().WaitUntilTaskCompletes(Event);
|
|
}
|
|
return bNeedToWait;
|
|
}
|
|
|
|
void FShaderCodeArchive::ReleasePreloadedShader(int32 ShaderIndex)
|
|
{
|
|
FShaderPreloadEntry& ShaderPreloadEntry = ShaderPreloads[ShaderIndex];
|
|
if (!ShaderPreloadEntry.bNeverToBePreloaded)
|
|
{
|
|
WaitForPreload(ShaderPreloadEntry);
|
|
|
|
FWriteScopeLock Lock(ShaderPreloadLock);
|
|
|
|
ShaderPreloadEntry.PreloadEvent.SafeRelease();
|
|
|
|
const uint32 ShaderNumRefs = ShaderPreloadEntry.NumRefs--;
|
|
check(ShaderPreloadEntry.Code);
|
|
check(ShaderNumRefs > 0u);
|
|
if (ShaderNumRefs == 1u)
|
|
{
|
|
FMemory::Free(ShaderPreloadEntry.Code);
|
|
ShaderPreloadEntry.Code = nullptr;
|
|
const FShaderCodeEntry& ShaderEntry = SerializedShaders.ShaderEntries[ShaderIndex];
|
|
DEC_DWORD_STAT_BY(STAT_Shaders_ShaderPreloadMemory, ShaderEntry.Size);
|
|
}
|
|
}
|
|
}
|
|
|
|
TRefCountPtr<FRHIShader> FShaderCodeArchive::CreateShader(int32 Index)
|
|
{
|
|
LLM_SCOPE(ELLMTag::Shaders);
|
|
#if STATS
|
|
double TimeFunctionEntered = FPlatformTime::Seconds();
|
|
ON_SCOPE_EXIT
|
|
{
|
|
if (IsInRenderingThread())
|
|
{
|
|
double ShaderCreationTime = FPlatformTime::Seconds() - TimeFunctionEntered;
|
|
INC_FLOAT_STAT_BY(STAT_Shaders_TotalRTShaderInitForRenderingTime, ShaderCreationTime);
|
|
}
|
|
};
|
|
#endif
|
|
|
|
TRefCountPtr<FRHIShader> Shader;
|
|
|
|
FMemStackBase& MemStack = FMemStack::Get();
|
|
FMemMark Mark(MemStack);
|
|
|
|
const FShaderCodeEntry& ShaderEntry = SerializedShaders.ShaderEntries[Index];
|
|
FShaderPreloadEntry& ShaderPreloadEntry = ShaderPreloads[Index];
|
|
checkf(!ShaderPreloadEntry.bNeverToBePreloaded, TEXT("We are creating a shader that shouldn't be preloaded in this run (e.g. raytracing shader on D3D11)."));
|
|
|
|
void* PreloadedShaderCode = nullptr;
|
|
{
|
|
const bool bNeededToWait = WaitForPreload(ShaderPreloadEntry);
|
|
if (bNeededToWait)
|
|
{
|
|
UE_LOG(LogShaderLibrary, Warning, TEXT("Blocking wait for shader preload, NumRefs: %d, FramePreloadStarted: %d"), ShaderPreloadEntry.NumRefs, ShaderPreloadEntry.FramePreloadStarted);
|
|
}
|
|
|
|
FWriteScopeLock Lock(ShaderPreloadLock);
|
|
if (ShaderPreloadEntry.NumRefs > 0u)
|
|
{
|
|
check(!ShaderPreloadEntry.PreloadEvent || ShaderPreloadEntry.PreloadEvent->IsComplete());
|
|
ShaderPreloadEntry.PreloadEvent.SafeRelease();
|
|
|
|
ShaderPreloadEntry.NumRefs++; // Hold a reference to code while we're using it to create shader
|
|
PreloadedShaderCode = ShaderPreloadEntry.Code;
|
|
check(PreloadedShaderCode);
|
|
}
|
|
}
|
|
|
|
const uint8* ShaderCode = (uint8*)PreloadedShaderCode;
|
|
if (!ShaderCode)
|
|
{
|
|
UE_LOG(LogShaderLibrary, Warning, TEXT("Blocking shader load, NumRefs: %d, FramePreloadStarted: %d"), ShaderPreloadEntry.NumRefs, ShaderPreloadEntry.FramePreloadStarted);
|
|
|
|
FGraphEventArray ReadCompleteEvents;
|
|
EAsyncIOPriorityAndFlags DontCache = GShaderCodeLibraryAsyncLoadingAllowDontCache ? AIOP_FLAG_DONTCACHE : AIOP_MIN;
|
|
IMemoryReadStreamRef LoadedCode = FileCacheHandle->ReadData(ReadCompleteEvents, LibraryCodeOffset + ShaderEntry.Offset, ShaderEntry.Size, AIOP_CriticalPath | DontCache);
|
|
if (ReadCompleteEvents.Num() > 0)
|
|
{
|
|
FTaskGraphInterface::Get().WaitUntilTasksComplete(ReadCompleteEvents);
|
|
}
|
|
void* LoadedShaderCode = MemStack.Alloc(ShaderEntry.Size, 16);
|
|
LoadedCode->CopyTo(LoadedShaderCode, 0, ShaderEntry.Size);
|
|
ShaderCode = (uint8*)LoadedShaderCode;
|
|
}
|
|
|
|
if (ShaderEntry.UncompressedSize != ShaderEntry.Size)
|
|
{
|
|
void* UncompressedCode = MemStack.Alloc(ShaderEntry.UncompressedSize, 16);
|
|
const bool bDecompressResult = FCompression::UncompressMemory(GetShaderCompressionFormat(), UncompressedCode, ShaderEntry.UncompressedSize, ShaderCode, ShaderEntry.Size);
|
|
check(bDecompressResult);
|
|
ShaderCode = (uint8*)UncompressedCode;
|
|
}
|
|
|
|
// detect the breach of contract early
|
|
ensureAlwaysMsgf(IsInRenderingThread() || GRHISupportsMultithreadedShaderCreation, TEXT("More than one thread is creating shaders, but GRHISupportsMultithreadedShaderCreation is false."));
|
|
|
|
const auto ShaderCodeView = MakeArrayView(ShaderCode, ShaderEntry.UncompressedSize);
|
|
const FSHAHash& ShaderHash = SerializedShaders.ShaderHashes[Index];
|
|
switch (ShaderEntry.Frequency)
|
|
{
|
|
case SF_Vertex: Shader = RHICreateVertexShader(ShaderCodeView, ShaderHash); CheckShaderCreation(Shader, Index); break;
|
|
case SF_Mesh: Shader = RHICreateMeshShader(ShaderCodeView, ShaderHash); CheckShaderCreation(Shader, Index); break;
|
|
case SF_Amplification: Shader = RHICreateAmplificationShader(ShaderCodeView, ShaderHash); CheckShaderCreation(Shader, Index); break;
|
|
case SF_Pixel: Shader = RHICreatePixelShader(ShaderCodeView, ShaderHash); CheckShaderCreation(Shader, Index); break;
|
|
case SF_Geometry: Shader = RHICreateGeometryShader(ShaderCodeView, ShaderHash); CheckShaderCreation(Shader, Index); break;
|
|
case SF_Compute: Shader = RHICreateComputeShader(ShaderCodeView, ShaderHash); CheckShaderCreation(Shader, Index); break;
|
|
case SF_RayGen: case SF_RayMiss: case SF_RayHitGroup: case SF_RayCallable:
|
|
#if RHI_RAYTRACING
|
|
if (GRHISupportsRayTracing && GRHISupportsRayTracingShaders)
|
|
{
|
|
Shader = RHICreateRayTracingShader(ShaderCodeView, ShaderHash, ShaderEntry.GetFrequency());
|
|
CheckShaderCreation(Shader, Index);
|
|
}
|
|
#endif // RHI_RAYTRACING
|
|
break;
|
|
default: checkNoEntry(); break;
|
|
}
|
|
|
|
// Release the refernece we were holding
|
|
if (PreloadedShaderCode)
|
|
{
|
|
FWriteScopeLock Lock(ShaderPreloadLock);
|
|
check(ShaderPreloadEntry.NumRefs > 1u); // we shouldn't be holding the last ref here
|
|
--ShaderPreloadEntry.NumRefs;
|
|
PreloadedShaderCode = nullptr;
|
|
}
|
|
|
|
if (Shader)
|
|
{
|
|
INC_DWORD_STAT(STAT_Shaders_NumShadersUsedForRendering);
|
|
Shader->SetHash(ShaderHash);
|
|
}
|
|
|
|
return Shader;
|
|
}
|
|
|
|
FIoChunkId FIoStoreShaderCodeArchive::GetShaderCodeArchiveChunkId(const FString& LibraryName, FName FormatName)
|
|
{
|
|
FString Name = FString::Printf(TEXT("%s-%s"), *LibraryName, *FormatName.ToString());
|
|
Name.ToLowerInline();
|
|
uint64 Hash = CityHash64(reinterpret_cast<const char*>(*Name), Name.Len() * sizeof(TCHAR));
|
|
return CreateIoChunkId(Hash, 0, EIoChunkType::ShaderCodeLibrary);
|
|
}
|
|
|
|
FIoChunkId FIoStoreShaderCodeArchive::GetShaderCodeChunkId(const FSHAHash& ShaderHash)
|
|
{
|
|
uint8 Data[12];
|
|
FMemory::Memcpy(Data, ShaderHash.Hash, 11);
|
|
*reinterpret_cast<uint8*>(&Data[11]) = static_cast<uint8>(EIoChunkType::ShaderCode);
|
|
FIoChunkId ChunkId;
|
|
ChunkId.Set(Data, 12);
|
|
return ChunkId;
|
|
}
|
|
|
|
void FIoStoreShaderCodeArchive::SaveIoStoreShaderCodeArchive(const FSerializedShaderArchive& SerializedShaders, FArchive& OutLibraryAr)
|
|
{
|
|
uint32 Version = CurrentVersion;
|
|
OutLibraryAr << Version;
|
|
OutLibraryAr << const_cast<FSerializedShaderArchive&>(SerializedShaders).ShaderMapHashes;
|
|
OutLibraryAr << const_cast<FSerializedShaderArchive&>(SerializedShaders).ShaderHashes;
|
|
TArray<FIoStoreShaderMapEntry> ShaderMapEntries;
|
|
ShaderMapEntries.Reserve(SerializedShaders.ShaderMapEntries.Num());
|
|
for (const FShaderMapEntry& ShaderMapEntry : SerializedShaders.ShaderMapEntries)
|
|
{
|
|
FIoStoreShaderMapEntry& IoStoreShaderMapEntry = ShaderMapEntries.AddDefaulted_GetRef();
|
|
IoStoreShaderMapEntry.ShaderIndicesOffset = ShaderMapEntry.ShaderIndicesOffset;
|
|
IoStoreShaderMapEntry.NumShaders = ShaderMapEntry.NumShaders;
|
|
}
|
|
OutLibraryAr << ShaderMapEntries;
|
|
TArray<FIoStoreShaderCodeEntry> ShaderEntries;
|
|
ShaderEntries.Reserve(SerializedShaders.ShaderEntries.Num());
|
|
for (const FShaderCodeEntry& ShaderEntry : SerializedShaders.ShaderEntries)
|
|
{
|
|
FIoStoreShaderCodeEntry& IoStoreShaderEntry = ShaderEntries.AddDefaulted_GetRef();
|
|
IoStoreShaderEntry.UncompressedSize = ShaderEntry.UncompressedSize;
|
|
IoStoreShaderEntry.CompressedSize = ShaderEntry.Size;
|
|
IoStoreShaderEntry.Frequency = ShaderEntry.Frequency;
|
|
}
|
|
OutLibraryAr << ShaderEntries;
|
|
OutLibraryAr << const_cast<FSerializedShaderArchive&>(SerializedShaders).ShaderIndices;
|
|
}
|
|
|
|
FIoStoreShaderCodeArchive* FIoStoreShaderCodeArchive::Create(EShaderPlatform InPlatform, const FString& InLibraryName, FIoDispatcher& InIoDispatcher)
|
|
{
|
|
const FName PlatformName = LegacyShaderPlatformToShaderFormat(InPlatform);
|
|
FIoChunkId ChunkId = GetShaderCodeArchiveChunkId(InLibraryName, PlatformName);
|
|
if (InIoDispatcher.DoesChunkExist(ChunkId))
|
|
{
|
|
FIoBatch IoBatch = InIoDispatcher.NewBatch();
|
|
FIoRequest IoRequest = IoBatch.Read(ChunkId, FIoReadOptions(), IoDispatcherPriority_Max);
|
|
FEvent* Event = FPlatformProcess::GetSynchEventFromPool();
|
|
IoBatch.IssueAndTriggerEvent(Event);
|
|
Event->Wait();
|
|
FPlatformProcess::ReturnSynchEventToPool(Event);
|
|
const FIoBuffer& IoBuffer = IoRequest.GetResultOrDie();
|
|
FMemoryReaderView Ar(MakeArrayView(IoBuffer.Data(), IoBuffer.DataSize()));
|
|
uint32 Version = 0;
|
|
Ar << Version;
|
|
if (Version == CurrentVersion)
|
|
{
|
|
FIoStoreShaderCodeArchive* Library = new FIoStoreShaderCodeArchive(InPlatform, InLibraryName, InIoDispatcher);
|
|
Ar << Library->ShaderMapHashes;
|
|
Ar << Library->ShaderHashes;
|
|
Ar << Library->ShaderMapEntries;
|
|
Ar << Library->ShaderEntries;
|
|
Ar << Library->ShaderIndices;
|
|
Library->ShaderPreloads.SetNum(Library->GetNumShaders());
|
|
{
|
|
const uint32 HashSize = FMath::Min<uint32>(0x10000, 1u << FMath::CeilLogTwo(Library->ShaderMapHashes.Num()));
|
|
Library->ShaderMapHashTable.Clear(HashSize, Library->ShaderMapHashes.Num());
|
|
for (int32 Index = 0; Index < Library->ShaderMapHashes.Num(); ++Index)
|
|
{
|
|
const uint32 Key = GetTypeHash(Library->ShaderMapHashes[Index]);
|
|
Library->ShaderMapHashTable.Add(Key, Index);
|
|
}
|
|
}
|
|
{
|
|
const uint32 HashSize = FMath::Min<uint32>(0x10000, 1u << FMath::CeilLogTwo(Library->ShaderHashes.Num()));
|
|
Library->ShaderHashTable.Clear(HashSize, Library->ShaderHashes.Num());
|
|
for (int32 Index = 0; Index < Library->ShaderHashes.Num(); ++Index)
|
|
{
|
|
const uint32 Key = GetTypeHash(Library->ShaderHashes[Index]);
|
|
Library->ShaderHashTable.Add(Key, Index);
|
|
}
|
|
}
|
|
|
|
UE_LOG(LogShaderLibrary, Display, TEXT("Using IoDispatcher for shader code library %s. Total %d unique shaders."), *InLibraryName, Library->ShaderEntries.Num());
|
|
INC_DWORD_STAT_BY(STAT_Shaders_ShaderResourceMemory, Library->GetSizeBytes());
|
|
return Library;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
FIoStoreShaderCodeArchive::FIoStoreShaderCodeArchive(EShaderPlatform InPlatform, const FString& InLibraryName, FIoDispatcher& InIoDispatcher)
|
|
: FRHIShaderLibrary(InPlatform, InLibraryName)
|
|
, IoDispatcher(InIoDispatcher)
|
|
{
|
|
}
|
|
|
|
FIoStoreShaderCodeArchive::~FIoStoreShaderCodeArchive()
|
|
{
|
|
DEC_DWORD_STAT_BY(STAT_Shaders_ShaderResourceMemory, GetSizeBytes());
|
|
Teardown();
|
|
}
|
|
|
|
void FIoStoreShaderCodeArchive::Teardown()
|
|
{
|
|
for (int32 ShaderIndex = 0; ShaderIndex < ShaderPreloads.Num(); ++ShaderIndex)
|
|
{
|
|
FShaderPreloadEntry& ShaderPreloadEntry = ShaderPreloads[ShaderIndex];
|
|
check(!ShaderPreloadEntry.NumRefs);
|
|
}
|
|
}
|
|
|
|
bool FIoStoreShaderCodeArchive::PreloadShader(int32 ShaderIndex, FGraphEventArray& OutCompletionEvents)
|
|
{
|
|
LLM_SCOPE(ELLMTag::Shaders);
|
|
|
|
FWriteScopeLock Lock(ShaderPreloadLock);
|
|
|
|
FShaderPreloadEntry& ShaderPreloadEntry = ShaderPreloads[ShaderIndex];
|
|
const uint32 ShaderNumRefs = ShaderPreloadEntry.NumRefs++;
|
|
if (ShaderNumRefs == 0u)
|
|
{
|
|
check(!ShaderPreloadEntry.PreloadEvent);
|
|
|
|
const FIoStoreShaderCodeEntry& ShaderEntry = ShaderEntries[ShaderIndex];
|
|
ShaderPreloadEntry.FramePreloadStarted = GFrameNumber;
|
|
|
|
ShaderPreloadEntry.PreloadEvent = FGraphEvent::CreateGraphEvent();
|
|
FIoBatch IoBatch = IoDispatcher.NewBatch();
|
|
ShaderPreloadEntry.IoRequest = IoBatch.Read(GetShaderCodeChunkId(ShaderHashes[ShaderIndex]), FIoReadOptions(), IoDispatcherPriority_Medium);
|
|
IoBatch.IssueAndDispatchSubsequents(ShaderPreloadEntry.PreloadEvent);
|
|
INC_DWORD_STAT_BY(STAT_Shaders_ShaderPreloadMemory, ShaderEntry.CompressedSize);
|
|
}
|
|
|
|
if (ShaderPreloadEntry.PreloadEvent && !ShaderPreloadEntry.PreloadEvent->IsComplete())
|
|
{
|
|
OutCompletionEvents.Add(ShaderPreloadEntry.PreloadEvent);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool FIoStoreShaderCodeArchive::PreloadShaderMap(int32 ShaderMapIndex, FGraphEventArray& OutCompletionEvents)
|
|
{
|
|
LLM_SCOPE(ELLMTag::Shaders);
|
|
|
|
const FIoStoreShaderMapEntry& ShaderMapEntry = ShaderMapEntries[ShaderMapIndex];
|
|
const uint32 FrameNumber = GFrameNumber;
|
|
uint32 PreloadMemory = 0u;
|
|
uint32 PreloadCount = 0u;
|
|
|
|
FWriteScopeLock Lock(ShaderPreloadLock);
|
|
|
|
for (uint32 i = 0u; i < ShaderMapEntry.NumShaders; ++i)
|
|
{
|
|
const int32 ShaderIndex = ShaderIndices[ShaderMapEntry.ShaderIndicesOffset + i];
|
|
FShaderPreloadEntry& ShaderPreloadEntry = ShaderPreloads[ShaderIndex];
|
|
const FIoStoreShaderCodeEntry& ShaderEntry = ShaderEntries[ShaderIndex];
|
|
|
|
#if RHI_RAYTRACING
|
|
if (!IsRayTracingEnabled() && IsRayTracingShaderFrequency(static_cast<EShaderFrequency>(ShaderEntry.Frequency)))
|
|
{
|
|
ShaderPreloadEntry.bNeverToBePreloaded = 1;
|
|
continue;
|
|
}
|
|
#endif
|
|
|
|
const uint32 ShaderNumRefs = ShaderPreloadEntry.NumRefs++;
|
|
if (ShaderNumRefs == 0u)
|
|
{
|
|
check(!ShaderPreloadEntry.PreloadEvent);
|
|
ShaderPreloadEntry.FramePreloadStarted = FrameNumber;
|
|
PreloadMemory += ShaderEntry.CompressedSize;
|
|
++PreloadCount;
|
|
ShaderPreloadEntry.PreloadEvent = FGraphEvent::CreateGraphEvent();
|
|
FIoBatch IoBatch = IoDispatcher.NewBatch();
|
|
ShaderPreloadEntry.IoRequest = IoBatch.Read(GetShaderCodeChunkId(ShaderHashes[ShaderIndex]), FIoReadOptions(), IoDispatcherPriority_Medium);
|
|
IoBatch.IssueAndDispatchSubsequents(ShaderPreloadEntry.PreloadEvent);
|
|
}
|
|
if (ShaderPreloadEntry.PreloadEvent && !ShaderPreloadEntry.PreloadEvent->IsComplete())
|
|
{
|
|
OutCompletionEvents.Add(ShaderPreloadEntry.PreloadEvent);
|
|
}
|
|
}
|
|
|
|
INC_DWORD_STAT_BY(STAT_Shaders_ShaderPreloadMemory, PreloadMemory);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool FIoStoreShaderCodeArchive::PreloadShaderMap(int32 ShaderMapIndex, FCoreDelegates::FAttachShaderReadRequestFunc AttachShaderReadRequestFunc)
|
|
{
|
|
LLM_SCOPE(ELLMTag::Shaders);
|
|
|
|
const FIoStoreShaderMapEntry& ShaderMapEntry = ShaderMapEntries[ShaderMapIndex];
|
|
const uint32 FrameNumber = GFrameNumber;
|
|
uint32 PreloadMemory = 0u;
|
|
|
|
FWriteScopeLock Lock(ShaderPreloadLock);
|
|
|
|
for (uint32 i = 0u; i < ShaderMapEntry.NumShaders; ++i)
|
|
{
|
|
const int32 ShaderIndex = ShaderIndices[ShaderMapEntry.ShaderIndicesOffset + i];
|
|
FShaderPreloadEntry& ShaderPreloadEntry = ShaderPreloads[ShaderIndex];
|
|
const FIoStoreShaderCodeEntry& ShaderEntry = ShaderEntries[ShaderIndex];
|
|
|
|
#if RHI_RAYTRACING
|
|
if (!IsRayTracingEnabled() && IsRayTracingShaderFrequency(static_cast<EShaderFrequency>(ShaderEntry.Frequency)))
|
|
{
|
|
ShaderPreloadEntry.bNeverToBePreloaded = 1;
|
|
continue;
|
|
}
|
|
#endif
|
|
|
|
const uint32 ShaderNumRefs = ShaderPreloadEntry.NumRefs++;
|
|
if (ShaderNumRefs == 0u)
|
|
{
|
|
check(!ShaderPreloadEntry.PreloadEvent);
|
|
ShaderPreloadEntry.FramePreloadStarted = FrameNumber;
|
|
PreloadMemory += ShaderEntry.CompressedSize;
|
|
ShaderPreloadEntry.PreloadEvent = FGraphEvent::CreateGraphEvent();
|
|
ShaderPreloadEntry.IoRequest = AttachShaderReadRequestFunc(GetShaderCodeChunkId(ShaderHashes[ShaderIndex]), ShaderPreloadEntry.PreloadEvent);
|
|
}
|
|
}
|
|
INC_DWORD_STAT_BY(STAT_Shaders_ShaderPreloadMemory, PreloadMemory);
|
|
return true;
|
|
}
|
|
|
|
bool FIoStoreShaderCodeArchive::ReleaseRef(int32 ShaderIndex)
|
|
{
|
|
FShaderPreloadEntry& ShaderPreloadEntry = ShaderPreloads[ShaderIndex];
|
|
if (!ShaderPreloadEntry.bNeverToBePreloaded)
|
|
{
|
|
const uint32 ShaderNumRefs = ShaderPreloadEntry.NumRefs--;
|
|
check(ShaderNumRefs > 0u);
|
|
if (ShaderNumRefs == 1u)
|
|
{
|
|
ShaderPreloadEntry.IoRequest = FIoRequest();
|
|
ShaderPreloadEntry.PreloadEvent.SafeRelease();
|
|
const FIoStoreShaderCodeEntry& ShaderEntry = ShaderEntries[ShaderIndex];
|
|
DEC_DWORD_STAT_BY(STAT_Shaders_ShaderPreloadMemory, ShaderEntry.CompressedSize);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void FIoStoreShaderCodeArchive::ReleasePreloadedShader(int32 ShaderIndex)
|
|
{
|
|
FWriteScopeLock Lock(ShaderPreloadLock);
|
|
ReleaseRef(ShaderIndex);
|
|
}
|
|
|
|
int32 FIoStoreShaderCodeArchive::FindShaderMapIndex(const FSHAHash& Hash)
|
|
{
|
|
const uint32 Key = GetTypeHash(Hash);
|
|
for (uint32 Index = ShaderMapHashTable.First(Key); ShaderMapHashTable.IsValid(Index); Index = ShaderMapHashTable.Next(Index))
|
|
{
|
|
if (ShaderMapHashes[Index] == Hash)
|
|
{
|
|
return Index;
|
|
}
|
|
}
|
|
return INDEX_NONE;
|
|
}
|
|
|
|
int32 FIoStoreShaderCodeArchive::FindShaderIndex(const FSHAHash& Hash)
|
|
{
|
|
const uint32 Key = GetTypeHash(Hash);
|
|
for (uint32 Index = ShaderHashTable.First(Key); ShaderHashTable.IsValid(Index); Index = ShaderHashTable.Next(Index))
|
|
{
|
|
if (ShaderHashes[Index] == Hash)
|
|
{
|
|
return Index;
|
|
}
|
|
}
|
|
return INDEX_NONE;
|
|
}
|
|
|
|
TRefCountPtr<FRHIShader> FIoStoreShaderCodeArchive::CreateShader(int32 Index)
|
|
{
|
|
LLM_SCOPE(ELLMTag::Shaders);
|
|
TRefCountPtr<FRHIShader> Shader;
|
|
|
|
const FIoStoreShaderCodeEntry& ShaderEntry = ShaderEntries[Index];
|
|
FShaderPreloadEntry& ShaderPreloadEntry = ShaderPreloads[Index];
|
|
|
|
FGraphEventRef Event;
|
|
{
|
|
FWriteScopeLock Lock(ShaderPreloadLock);
|
|
uint32 ShaderNumRefs = ShaderPreloadEntry.NumRefs++;
|
|
if (ShaderNumRefs == 0u)
|
|
{
|
|
check(!ShaderPreloadEntry.PreloadEvent);
|
|
ShaderPreloadEntry.PreloadEvent = FGraphEvent::CreateGraphEvent();
|
|
FIoBatch IoBatch = IoDispatcher.NewBatch();
|
|
ShaderPreloadEntry.IoRequest = IoBatch.Read(GetShaderCodeChunkId(ShaderHashes[Index]), FIoReadOptions(), IoDispatcherPriority_Max);
|
|
IoBatch.IssueAndDispatchSubsequents(ShaderPreloadEntry.PreloadEvent);
|
|
}
|
|
else if (!ShaderPreloadEntry.IoRequest.Status().IsCompleted())
|
|
{
|
|
ShaderPreloadEntry.IoRequest.UpdatePriority(IoDispatcherPriority_Max);
|
|
}
|
|
Event = ShaderPreloadEntry.PreloadEvent;
|
|
}
|
|
|
|
const bool bNeededToWait = !Event->IsComplete();
|
|
if (bNeededToWait)
|
|
{
|
|
TRACE_CPUPROFILER_EVENT_SCOPE(BlockingShaderLoad);
|
|
UE_LOG(LogShaderLibrary, Warning, TEXT("Blocking wait for shader preload, NumRefs: %d, FramePreloadStarted: %d"), ShaderPreloadEntry.NumRefs, ShaderPreloadEntry.FramePreloadStarted);
|
|
FTaskGraphInterface::Get().WaitUntilTaskCompletes(Event);
|
|
}
|
|
|
|
const uint8* ShaderCode = ShaderPreloadEntry.IoRequest.GetResultOrDie().Data();
|
|
|
|
FMemStackBase& MemStack = FMemStack::Get();
|
|
FMemMark Mark(MemStack);
|
|
if (ShaderEntry.UncompressedSize != ShaderEntry.CompressedSize)
|
|
{
|
|
void* UncompressedCode = MemStack.Alloc(ShaderEntry.UncompressedSize, 16);
|
|
const bool bDecompressResult = FCompression::UncompressMemory(GetShaderCompressionFormat(), UncompressedCode, ShaderEntry.UncompressedSize, ShaderCode, ShaderEntry.CompressedSize);
|
|
check(bDecompressResult);
|
|
ShaderCode = (uint8*)UncompressedCode;
|
|
}
|
|
|
|
const auto ShaderCodeView = MakeArrayView(ShaderCode, ShaderEntry.UncompressedSize);
|
|
const FSHAHash& ShaderHash = ShaderHashes[Index];
|
|
switch (ShaderEntry.Frequency)
|
|
{
|
|
case SF_Vertex: Shader = RHICreateVertexShader(ShaderCodeView, ShaderHash); break;
|
|
case SF_Mesh: Shader = RHICreateMeshShader(ShaderCodeView, ShaderHash); break;
|
|
case SF_Amplification: Shader = RHICreateAmplificationShader(ShaderCodeView, ShaderHash); break;
|
|
case SF_Pixel: Shader = RHICreatePixelShader(ShaderCodeView, ShaderHash); break;
|
|
case SF_Geometry: Shader = RHICreateGeometryShader(ShaderCodeView, ShaderHash); break;
|
|
case SF_Compute: Shader = RHICreateComputeShader(ShaderCodeView, ShaderHash); break;
|
|
case SF_RayGen: case SF_RayMiss: case SF_RayHitGroup: case SF_RayCallable:
|
|
#if RHI_RAYTRACING
|
|
if (GRHISupportsRayTracing && GRHISupportsRayTracingShaders)
|
|
{
|
|
Shader = RHICreateRayTracingShader(ShaderCodeView, ShaderHash, ShaderEntry.GetFrequency());
|
|
}
|
|
#endif // RHI_RAYTRACING
|
|
break;
|
|
default: checkNoEntry(); break;
|
|
}
|
|
|
|
{
|
|
FWriteScopeLock Lock(ShaderPreloadLock);
|
|
const uint32 ShaderNumRefs = ShaderPreloadEntry.NumRefs--;
|
|
check(ShaderNumRefs > 0u);
|
|
if (ShaderNumRefs == 1u)
|
|
{
|
|
ShaderPreloadEntry.PreloadEvent.SafeRelease();
|
|
ShaderPreloadEntry.IoRequest = FIoRequest();
|
|
}
|
|
}
|
|
|
|
if (Shader)
|
|
{
|
|
Shader->SetHash(ShaderHash);
|
|
}
|
|
|
|
return Shader;
|
|
}
|
|
|