Files
UnrealEngine/Engine/Source/Runtime/RenderCore/Private/ShaderSerialization.cpp
2025-05-18 13:04:45 +08:00

171 lines
6.1 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "ShaderSerialization.h"
#include "DerivedDataCacheKey.h"
#include "DerivedDataCacheRecord.h"
#include "DerivedDataValue.h"
#include "Serialization/CompactBinaryWriter.h"
#include "Serialization/MemoryReader.h"
#include "Serialization/MemoryWriter.h"
#include "ShaderCore.h"
FShaderCacheSaveContext::FShaderCacheSaveContext()
{
Reset();
}
#if WITH_EDITOR
const UE::DerivedData::FValueId ShaderObjectDataValue = UE::DerivedData::FValueId::FromName(TEXT("ShaderObjectData"));
const UE::DerivedData::FValueId ShaderCodeDataValue = UE::DerivedData::FValueId::FromName(TEXT("ShaderCodeData"));
const UE::DerivedData::FValueId ShaderSymbolsDataValue = UE::DerivedData::FValueId::FromName(TEXT("ShaderSymbolsData"));
const FAnsiStringView CodeCountMetaField = ANSITEXTVIEW("CodeCount");
const FAnsiStringView HasSymbolsMetaField = ANSITEXTVIEW("bHasSymbols");
#endif
void FShaderCacheSaveContext::SerializeCode(FShaderCodeResource& Resource, int32 Index)
{
OwnedShaderCode.Add(Resource.GetCacheBuffer());
// reset the array view any time an entry is added; we do this instead of calling Resize in the reserve delegate
// and setting it there since not all code paths (i.e. single job cache records) call reserve
ShaderCode = OwnedShaderCode;
OwnedShaderSymbols.Add(Resource.GetSymbolsBuffer());
ShaderSymbols = OwnedShaderSymbols;
checkf(ShaderCode.Num() == ShaderSymbols.Num(), TEXT("It is required to serialize a (possibly empty, but non-null) symbols buffer for every code buffer."));
}
void FShaderCacheSaveContext::ReserveCode(int32 Count)
{
OwnedShaderCode.Reserve(Count);
OwnedShaderSymbols.Reserve(Count);
}
void FShaderCacheSaveContext::Reset()
{
ShaderObjectData.Reset();
OwnedShaderCode.Reset();
OwnedShaderSymbols.Reset();
Writer = MakeUnique<FMemoryWriter64>(ShaderObjectRawData);
Ar = Writer.Get();
}
void FShaderCacheSaveContext::Finalize()
{
if (!ShaderObjectData)
{
ShaderObjectData = MakeSharedBufferFromArray(MoveTemp(ShaderObjectRawData));
}
}
#if WITH_EDITOR
UE::DerivedData::FCacheRecord FShaderCacheSaveContext::BuildCacheRecord(const UE::DerivedData::FCacheKey& Key)
{
Finalize();
UE::DerivedData::FCacheRecordBuilder RecordBuilder(Key);
RecordBuilder.AddValue(ShaderObjectDataValue, ShaderObjectData);
int32 CodeIndex = 0;
// Code buffers are already compressed, don't waste cycles attempting (and failing) to recompress them
const ECompressedBufferCompressor CodeComp = ECompressedBufferCompressor::NotSet;
const ECompressedBufferCompressionLevel CodeCompLevel = ECompressedBufferCompressionLevel::None;
// Use a meta field to indicate whether we are adding symbol values to this record; we do so to prevent per-value overhead of symbol buffers if they are empty
// (each value in a cache record has a 64 byte overhead, which can be significant when we're pushing millions of individual shader cache records)
bool bHasSymbols = false;
for (FCompressedBuffer& SymbolsBuf : ShaderSymbols)
{
bHasSymbols |= (SymbolsBuf.GetRawSize() > 0);
}
for (FCompositeBuffer& CodeBuf : ShaderCode)
{
RecordBuilder.AddValue(ShaderCodeDataValue.MakeIndexed(CodeIndex), UE::DerivedData::FValue(FCompressedBuffer::Compress(CodeBuf, CodeComp, CodeCompLevel)));
if (bHasSymbols)
{
RecordBuilder.AddValue(ShaderSymbolsDataValue.MakeIndexed(CodeIndex), UE::DerivedData::FValue(ShaderSymbols[CodeIndex]));
}
CodeIndex++;
}
TCbWriter<16> MetaWriter;
MetaWriter.BeginObject();
MetaWriter.AddInteger(CodeCountMetaField, ShaderCode.Num());
MetaWriter.AddBool(HasSymbolsMetaField, bHasSymbols);
MetaWriter.EndObject();
RecordBuilder.SetMeta(MetaWriter.Save().AsObject());
return RecordBuilder.Build();
}
#endif
FShaderCacheLoadContext::FShaderCacheLoadContext(FSharedBuffer InShaderObjectData, TArrayView<FCompositeBuffer> InCodeBuffers, TArrayView<FCompressedBuffer> InSymbolBuffers)
{
Reset(InShaderObjectData, InCodeBuffers, InSymbolBuffers);
}
void FShaderCacheLoadContext::Reset(FSharedBuffer InShaderObjectData, TArrayView<FCompositeBuffer> InCodeBuffers, TArrayView<FCompressedBuffer> InSymbolBuffers)
{
ShaderObjectData = InShaderObjectData;
ShaderCode = InCodeBuffers;
ShaderSymbols = InSymbolBuffers;
checkf(ShaderCode.Num() == ShaderSymbols.Num(), TEXT("It is required to serialize a (possibly empty, but non-null) symbols buffer for every code buffer."));
Reader = MakeUnique<FMemoryReaderView>(ShaderObjectData);
Ar = Reader.Get();
}
void FShaderCacheLoadContext::SerializeCode(FShaderCodeResource& Resource, int32 Index)
{
Resource.PopulateFromComposite(ShaderCode[Index], ShaderSymbols[Index]);
}
void FShaderCacheLoadContext::Reuse()
{
Reader->Seek(0u);
}
#if WITH_EDITOR
void FShaderCacheLoadContext::ReadFromRecord(const UE::DerivedData::FCacheRecord& Record, bool bIsPersistent)
{
ShaderObjectData = Record.GetValue(ShaderObjectDataValue).GetData().Decompress();
// Must initialize a memory reader (and the base class archive pointer) after reading the base shadermap data buffer
// from the DDC record
Reader = MakeUnique<FMemoryReaderView>(ShaderObjectData, bIsPersistent);
Ar = Reader.Get();
int32 CodeCount = Record.GetMeta()[CodeCountMetaField].AsInt32();
OwnedShaderCode.Reserve(CodeCount);
OwnedShaderSymbols.Reserve(CodeCount);
const bool bHasSymbols = Record.GetMeta()[HasSymbolsMetaField].AsBool();
for (int32 CodeIndex = 0; CodeIndex < CodeCount; ++CodeIndex)
{
FSharedBuffer CombinedBuffer = Record.GetValue(ShaderCodeDataValue.MakeIndexed(CodeIndex)).GetData().Decompress();
OwnedShaderCode.Add(FShaderCodeResource::Unpack(CombinedBuffer));
if (bHasSymbols)
{
UE::DerivedData::FValueWithId Value = Record.GetValue(ShaderSymbolsDataValue.MakeIndexed(CodeIndex));
FCompressedBuffer SymbolsBuffer = Value.GetData();
OwnedShaderSymbols.Add(SymbolsBuffer);
}
else
{
// We need to set an empty symbol buffer if symbols were not saved to the cache, instead of a null one
// This is due to FCompressedBuffer serialization not supporting null buffers
static FCompressedBuffer Empty = FCompressedBuffer::Compress(FUniqueBuffer::Alloc(0).MoveToShared());
OwnedShaderSymbols.Add(Empty);
}
}
ShaderCode = OwnedShaderCode;
ShaderSymbols = OwnedShaderSymbols;
}
#endif