Files
UnrealEngine/Engine/Source/Editor/UnrealEd/Private/Cooker/ShaderLibraryCooking.cpp
Brandyn / Techy fcc1b09210 init
2026-04-04 15:40:51 -05:00

641 lines
25 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "Cooker/ShaderLibraryCooking.h"
#include "Commandlets/AssetRegistryGenerator.h"
#include "Commandlets/ShaderPipelineCacheToolsCommandlet.h"
#include "Cooker/CookDirector.h"
#include "Cooker/CookOnTheFlyServerInterface.h"
#include "Cooker/CookPlatformManager.h"
#include "Cooker/CookTypes.h"
#include "Cooker/CookWorkerClient.h"
#include "Cooker/OnDemandShaderCompilation.h"
#include "Cooker/PackageTracker.h"
#include "CookOnTheSide/CookLog.h"
#include "CookOnTheSide/CookOnTheFlyServer.h"
#include "CoreGlobals.h"
#include "GlobalShader.h"
#include "HAL/ConsoleManager.h"
#include "HAL/FileManager.h"
#include "HAL/LowLevelMemTracker.h"
#include "Interfaces/IShaderFormat.h"
#include "Interfaces/ITargetPlatform.h"
#include "Interfaces/ITargetPlatformManagerModule.h"
#include "LayeredCookArtifactReader.h"
#include "Misc/App.h"
#include "Misc/CommandLine.h"
#include "Misc/Parse.h"
#include "Misc/Paths.h"
#include "PipelineCacheChunkDataGenerator.h"
#include "Serialization/CompactBinary.h"
#include "Serialization/CompactBinaryWriter.h"
#include "Settings/ProjectPackagingSettings.h"
#include "ShaderCodeLibrary.h"
#include "ShaderCompiler.h"
#include "ShaderLibraryChunkDataGenerator.h"
#include "ShaderStats.h"
#include "ShaderStatsCollector.h"
// Include RHIShaderFormatDefinitions.inl only after all header includes; it defines some static variables,
// used by UpdateShaderCookingCVars.
#include "RHIShaderFormatDefinitions.inl"
bool bDisableShaderCompilationDuringCookOnTheFly = false;
static FAutoConsoleVariableRef CVarDisableShaderCompilationDuringCookOnTheFly(
TEXT("Cook.OnTheFly.ShaderCompilationDisabled"),
bDisableShaderCompilationDuringCookOnTheFly,
TEXT("Controls whether shader compilation is disabled during cook on the fly\n"),
ECVF_Default);
bool bAllowIncompleteShaderMapsDuringCookOnTheFly = false;
static FAutoConsoleVariableRef CVarAllowEmptyShaderMapsDuringCookOnTheFly(
TEXT("Cook.OnTheFly.AllowIncompleteShadermaps"),
bAllowIncompleteShaderMapsDuringCookOnTheFly,
TEXT("Controls whether incomplete shader maps are processed during cook on the fly\n"),
ECVF_Default);
static void GenerateAutogenShaderHeadersForAllShaderFormats(const TArray<ITargetPlatform*>& Platforms)
{
for (const ITargetPlatform* Platform : Platforms)
{
TArray<FName> ShaderPlatformNameArray;
Platform->GetAllTargetedShaderFormats(ShaderPlatformNameArray);
for (const FName& ShaderPlatformName : ShaderPlatformNameArray)
{
FShaderCompileUtilities::GenerateBrdfHeaders(ShaderPlatformName);
}
}
}
bool UCookOnTheFlyServer::IsUsingShaderCodeLibrary() const
{
const UProjectPackagingSettings* const PackagingSettings = GetDefault<UProjectPackagingSettings>();
return IsDirectorCookByTheBook() && AllowShaderCompiling() && PackagingSettings->bShareMaterialShaderCode;
}
namespace UE::Cook
{
void TickShaderCompilingManager(UE::Cook::FTickStackData& StackData)
{
UE_SCOPED_COOKTIMER_AND_DURATION(CookByTheBook_ShaderProcessAsync, DetailedCookStats::TickLoopShaderProcessAsyncResultsTimeSec);
GShaderCompilingManager->ProcessAsyncResults(StackData.Timer.GetActionTimeSlice(), false);
}
} // namespace UE::Cook
void UCookOnTheFlyServer::TickRecompileShaderRequestsPrivate(UE::Cook::FTickStackData& StackData)
{
using namespace UE::Cook;
// try to pull off a request
FRecompileShaderRequest RecompileShaderRequest;
bool bProcessedRequests = false;
if (PackageTracker->RecompileRequests.Dequeue(&RecompileShaderRequest))
{
if (RecompileShaderRequest.RecompileArguments.CommandType != ODSCRecompileCommand::ResetMaterialCache)
{
RecompileShadersForRemote(RecompileShaderRequest.RecompileArguments, GetSandboxDirectory(RecompileShaderRequest.RecompileArguments.PlatformName));
}
RecompileShaderRequest.CompletionCallback();
bProcessedRequests = true;
}
if (PackageTracker->RecompileRequests.HasItems())
{
RecompileRequestsPollable->Trigger(*this);
}
if (bProcessedRequests)
{
// Ask for GC to run again when we processed some shaders requests to ensure material get evicted and we don't keep their package open
StackData.ResultFlags |= COSR_RequiresGC | COSR_RequiresGC_Periodic | COSR_YieldTick;
}
}
namespace UE::Cook::CVarControl
{
void UpdateShaderCookingCVars(ITargetPlatformManagerModule* TPM, int32 CookTimeCVarControl,
const ITargetPlatform* Platform, FName PlatformName)
{
// for now, only do this if we are performing any cvar control modes
if (CookTimeCVarControl != 0)
{
if (Platform != TPM->GetRunningTargetPlatform())
{
// register that when we cook for this platform's shaderplatforms, we will want to use the given platform when looking up cvars
TArray<FName> ShaderFormats;
Platform->GetAllTargetedShaderFormats(ShaderFormats);
for (FName SF : ShaderFormats)
{
EShaderPlatform SP = ShaderFormatNameToShaderPlatform(SF);
ConsoleVariablePlatformMapping::RegisterShaderPlatformToPlatformMapping((int)SP, PlatformName);
}
}
}
}
} // namespace UE::Cook::CVarControl
void UCookOnTheFlyServer::SaveGlobalShaderMapFiles(const TArrayView<const ITargetPlatform* const>& Platforms, ODSCRecompileCommand RecompileCommand)
{
LLM_SCOPE(ELLMTag::Shaders);
check(!IsCookingDLC()); // GlobalShaderMapFiles are not supported when cooking DLC
check(IsInGameThread());
for (const ITargetPlatform* TargetPlatform : Platforms)
{
const FString& PlatformName = TargetPlatform->PlatformName();
UE_LOG(LogCook, Display, TEXT("Compiling global%s shaders for platform '%s'"),
RecompileCommand == ODSCRecompileCommand::Changed ? TEXT(" changed") : TEXT(""), *PlatformName);
TArray<uint8> GlobalShaderMap;
FShaderRecompileData RecompileData(PlatformName, SP_NumPlatforms, RecompileCommand, nullptr, nullptr, &GlobalShaderMap);
RecompileData.ODSCCustomLoadMaterial = &UE::Cook::FODSCClientData::FindMaterial;
RecompileShadersForRemote(RecompileData, GetSandboxDirectory(PlatformName));
}
}
namespace UE::Cook
{
FGuid FShaderLibraryCollector::MessageType(TEXT("4DF3B36BBA2F4E04A846E894E24EB2C4"));
void FShaderLibraryCollector::ClientTick(FMPCollectorClientTickContext& Context)
{
constexpr int32 NumLoopsPerWarning = 100;
for (int32 NumMessages = 0; ; ++NumMessages)
{
// Maximum size for a message is 1GB. Caller will crash if we go over that.
// Provide a maximum shader size of half that because CopyToCompactBinaryAndClear adds on additional
// small amounts of data beyond the shader size limit.
const int64 MaximumSize = UE::CompactBinaryTCP::MaxOSPacketSize / 2;
bool bHasData;
bool bRanOutOfRoom;
FCbWriter Writer;
Writer.BeginObject();
Writer.SetName(RootObjectID);
FShaderLibraryCooker::CopyToCompactBinaryAndClear(Writer, bHasData, bRanOutOfRoom, MaximumSize);
if (bHasData)
{
Writer.EndObject();
Context.AddMessage(Writer.Save().AsObject());
}
if (!bRanOutOfRoom)
{
break;
}
if (NumMessages > 0 && NumMessages % NumLoopsPerWarning == 0)
{
UE_LOG(LogCook, Warning, TEXT("FShaderLibraryCollector::ClientTick has an unexpectedly high number of loops. Infinite loop?"))
}
}
}
void FShaderLibraryCollector::ServerReceiveMessage(FMPCollectorServerMessageContext& Context, FCbObjectView Message)
{
bool bSuccessful = FShaderLibraryCooker::AppendFromCompactBinary(Message[RootObjectID]);
UE_CLOG(!bSuccessful, LogCook, Error,
TEXT("Corrupt message received from CookWorker when replicating ShaderLibrary. Shaders will be missing from the cook."));
}
const TCHAR* TEXT_ShaderLibrary(TEXT("ShaderLibrary"));
FShaderLibraryCookArtifact::FShaderLibraryCookArtifact(UCookOnTheFlyServer& InCOTFS)
: COTFS(InCOTFS)
{
}
FString FShaderLibraryCookArtifact::GetArtifactName() const
{
return TEXT_ShaderLibrary;
}
void FShaderLibraryCookArtifact::OnFullRecook(const ITargetPlatform* TargetPlatform)
{
CleanIntermediateFiles(TargetPlatform);
}
void FShaderLibraryCookArtifact::CleanIntermediateFiles(const ITargetPlatform* TargetPlatform)
{
TArray<FName> ShaderFormats;
TargetPlatform->GetAllTargetedShaderFormats(ShaderFormats);
if (ShaderFormats.Num() > 0)
{
FShaderLibraryCooker::CleanDirectories(ShaderFormats);
}
}
} // namespace UE::Cook
void UCookOnTheFlyServer::RegisterShaderLibraryIncrementalCookArtifact(FBeginCookContext& BeginContext)
{
if (IsCookWorkerMode() || !IsUsingShaderCodeLibrary())
{
return;
}
RegisterArtifact(new UE::Cook::FShaderLibraryCookArtifact(*this));
}
void UCookOnTheFlyServer::BeginCookStartShaderCodeLibrary(FBeginCookContext& BeginContext)
{
const UProjectPackagingSettings* const PackagingSettings = GetDefault<UProjectPackagingSettings>();
if (IsUsingShaderCodeLibrary())
{
FShaderLibraryCooker::InitForCooking(PackagingSettings->bSharedMaterialNativeLibraries, AllContextArtifactReader.Get());
bool bAllPlatformsNeedStableKeys = false;
// support setting without Hungarian prefix for the compatibility, but allow newer one to override
GConfig->GetBool(TEXT("DevOptions.Shaders"), TEXT("NeedsShaderStableKeys"), bAllPlatformsNeedStableKeys, GEngineIni);
GConfig->GetBool(TEXT("DevOptions.Shaders"), TEXT("bNeedsShaderStableKeys"), bAllPlatformsNeedStableKeys, GEngineIni);
// PSO manual cache in DLC is not currently supported. Although stable keys can have other uses, disable this for DLC as it will
// also make it cook faster.
bAllPlatformsNeedStableKeys &= !IsCookingDLC();
for (const ITargetPlatform* TargetPlatform : BeginContext.TargetPlatforms)
{
// Find out if this platform requires stable shader keys, by reading the platform setting file.
// Stable shader keys are needed if we are going to create a PSO cache.
bool bNeedShaderStableKeys = bAllPlatformsNeedStableKeys;
FConfigFile PlatformIniFile;
FConfigCacheIni::LoadLocalIniFile(PlatformIniFile, TEXT("Engine"), true, *TargetPlatform->IniPlatformName());
PlatformIniFile.GetBool(TEXT("DevOptions.Shaders"), TEXT("NeedsShaderStableKeys"), bNeedShaderStableKeys);
PlatformIniFile.GetBool(TEXT("DevOptions.Shaders"), TEXT("bNeedsShaderStableKeys"), bNeedShaderStableKeys);
bool bNeedsDeterministicOrder = PackagingSettings->bDeterministicShaderCodeOrder;
FConfigFile PlatformGameIniFile;
FConfigCacheIni::LoadLocalIniFile(PlatformGameIniFile, TEXT("Game"), true, *TargetPlatform->IniPlatformName());
PlatformGameIniFile.GetBool(TEXT("/Script/UnrealEd.ProjectPackagingSettings"), TEXT("bDeterministicShaderCodeOrder"), bNeedsDeterministicOrder);
TArray<FName> ShaderFormats;
TargetPlatform->GetAllTargetedShaderFormats(ShaderFormats);
TArray<FShaderLibraryCooker::FShaderFormatDescriptor> ShaderFormatsWithStableKeys;
for (FName& Format : ShaderFormats)
{
FShaderLibraryCooker::FShaderFormatDescriptor NewDesc;
NewDesc.ShaderFormat = Format;
NewDesc.bNeedsStableKeys = bNeedShaderStableKeys;
NewDesc.bNeedsDeterministicOrder = bNeedsDeterministicOrder;
ShaderFormatsWithStableKeys.Push(NewDesc);
}
if (ShaderFormats.Num() > 0)
{
FShaderLibraryCooker::CookShaderFormats(ShaderFormatsWithStableKeys);
}
}
if (CookDirector)
{
CookDirector->Register(new UE::Cook::FShaderLibraryCollector());
}
else if (CookWorkerClient)
{
CookWorkerClient->Register(new UE::Cook::FShaderLibraryCollector());
}
}
if (CookDirector)
{
CookDirector->Register(new FShaderStatsAggregator(FShaderStatsAggregator::EMode::Director));
}
else if (CookWorkerClient)
{
CookWorkerClient->Register(new FShaderStatsAggregator(FShaderStatsAggregator::EMode::Worker));
}
}
void UCookOnTheFlyServer::BeginCookFinishShaderCodeLibrary(FBeginCookContext& BeginContext)
{
check(IsDirectorCookByTheBook()); // CookByTheBook only for now
// Generate the AutogenShaderHeaders.usd files for every shader platforms we are about to cook. Those files can be hashed and used
// in dependencies so they need to be present. Only do this for the director.
bool bIsCookWorker = IsCookWorkerMode();
if (!bIsCookWorker)
{
GenerateAutogenShaderHeadersForAllShaderFormats(BeginContext.TargetPlatforms);
}
// don't resave the global shader map files in dlc
if (!bIsCookWorker && !IsCookingDLC() && !EnumHasAnyFlags(CookByTheBookOptions->StartupOptions, ECookByTheBookOptions::ForceDisableSaveGlobalShaders))
{
OpenGlobalShaderLibrary();
// make sure global shaders are up to date!
SaveGlobalShaderMapFiles(BeginContext.TargetPlatforms, ODSCRecompileCommand::Changed);
SaveAndCloseGlobalShaderLibrary();
}
// Open the shader code library for the current project or the current DLC pack, depending on which we are cooking
FString LibraryName = GetProjectShaderLibraryName();
check(!LibraryName.IsEmpty());
OpenShaderLibrary(LibraryName);
}
void UCookOnTheFlyServer::RegisterShaderChunkDataGenerator()
{
check(!IsCookWorkerMode());
// add shader library and PSO cache chunkers
FString LibraryName = GetProjectShaderLibraryName();
for (const ITargetPlatform* TargetPlatform : PlatformManager->GetSessionPlatforms())
{
FAssetRegistryGenerator& RegistryGenerator = *(PlatformManager->GetPlatformData(TargetPlatform)->RegistryGenerator);
RegistryGenerator.RegisterChunkDataGenerator(MakeShared<FShaderLibraryChunkDataGenerator>(*this, TargetPlatform));
RegistryGenerator.RegisterChunkDataGenerator(MakeShared<FPipelineCacheChunkDataGenerator>(TargetPlatform, LibraryName));
}
}
FString UCookOnTheFlyServer::GetProjectShaderLibraryName() const
{
static FString ShaderLibraryName = [this]()
{
FString OverrideShaderLibraryName;
if (FParse::Value(FCommandLine::Get(), TEXT("OverrideShaderLibraryName="), OverrideShaderLibraryName))
{
return OverrideShaderLibraryName;
}
if (!IsCookingDLC())
{
FString Result = FApp::GetProjectName();
if (Result.IsEmpty())
{
Result = TEXT("UnrealGame");
}
return Result;
}
else
{
return CookByTheBookOptions->DlcName;
}
}();
return ShaderLibraryName;
}
static FString GenerateShaderCodeLibraryName(const FString& Name, bool bIsLegacyIterativeSharedBuild)
{
FString ActualName = (!bIsLegacyIterativeSharedBuild) ? Name : Name + TEXT("_SC");
return ActualName;
}
void UCookOnTheFlyServer::OpenGlobalShaderLibrary()
{
const bool bCacheShaderLibraries = IsUsingShaderCodeLibrary();
if (bCacheShaderLibraries)
{
const TCHAR* GlobalShaderLibName = TEXT("Global");
FString ActualName = GenerateShaderCodeLibraryName(GlobalShaderLibName, IsCookFlagSet(ECookInitializationFlags::LegacyIterativeSharedBuild));
// The shader code library directory doesn't matter while cooking
FShaderLibraryCooker::BeginCookingLibrary(ActualName);
}
}
void UCookOnTheFlyServer::OpenShaderLibrary(FString const& Name)
{
const bool bCacheShaderLibraries = IsUsingShaderCodeLibrary();
if (bCacheShaderLibraries)
{
FString ActualName = GenerateShaderCodeLibraryName(Name, IsCookFlagSet(ECookInitializationFlags::LegacyIterativeSharedBuild));
// The shader code library directory doesn't matter while cooking
FShaderLibraryCooker::BeginCookingLibrary(ActualName);
}
}
void UCookOnTheFlyServer::CreatePipelineCache(const ITargetPlatform* TargetPlatform, const FString& LibraryName)
{
// make sure we have a registry generated for all the platforms
const FString TargetPlatformName = TargetPlatform->PlatformName();
TArray<FString>* SCLCSVPaths = OutSCLCSVPaths.Find(FName(TargetPlatformName));
if (SCLCSVPaths && SCLCSVPaths->Num())
{
TArray<FName> ShaderFormats;
TargetPlatform->GetAllTargetedShaderFormats(ShaderFormats);
for (FName ShaderFormat : ShaderFormats)
{
const FString StablePCDir = FPaths::ProjectDir() / TEXT("Build") / TargetPlatform->IniPlatformName() / TEXT("PipelineCaches");
// look for the new binary format for stable pipeline cache - spc
const FString StablePCBinary = StablePCDir / FString::Printf(TEXT("*%s_%s.spc"), *LibraryName, *ShaderFormat.ToString());
bool bBinaryStablePipelineCacheFilesFound = [&StablePCBinary]()
{
TArray<FString> ExpandedFiles;
IFileManager::Get().FindFilesRecursive(ExpandedFiles, *FPaths::GetPath(StablePCBinary), *FPaths::GetCleanFilename(StablePCBinary), true, false, false);
return ExpandedFiles.Num() > 0;
}();
// for now, also look for the older *stablepc.csv or *stablepc.csv.compressed
const FString StablePCTextual = StablePCDir / FString::Printf(TEXT("*%s_%s.stablepc.csv"), *LibraryName, *ShaderFormat.ToString());
const FString StablePCTextualCompressed = StablePCTextual + TEXT(".compressed");
bool bTextualStablePipelineCacheFilesFound = [&StablePCTextual, &StablePCTextualCompressed]()
{
TArray<FString> ExpandedFiles;
IFileManager::Get().FindFilesRecursive(ExpandedFiles, *FPaths::GetPath(StablePCTextual), *FPaths::GetCleanFilename(StablePCTextual), true, false, false);
IFileManager::Get().FindFilesRecursive(ExpandedFiles, *FPaths::GetPath(StablePCTextualCompressed), *FPaths::GetCleanFilename(StablePCTextualCompressed), true, false, false);
return ExpandedFiles.Num() > 0;
}();
// because of the compute shaders that are cached directly from stable shader keys files, we need to run this also if we have stable keys (which is pretty much always)
static const IConsoleVariable* CVarIncludeComputePSOsDuringCook = IConsoleManager::Get().FindConsoleVariable(TEXT("r.ShaderPipelineCacheTools.IncludeComputePSODuringCook"));
const bool bIncludeComputePSOsDuringCook = CVarIncludeComputePSOsDuringCook && CVarIncludeComputePSOsDuringCook->GetInt() >= 1;
if (!bBinaryStablePipelineCacheFilesFound && !bTextualStablePipelineCacheFilesFound && !bIncludeComputePSOsDuringCook)
{
UE_LOG(LogCook, Display, TEXT("---- NOT Running UShaderPipelineCacheToolsCommandlet for platform %s shader format %s, no files found at %s, and either no stable keys or not including compute PSOs during the cook"), *TargetPlatformName, *ShaderFormat.ToString(), *StablePCDir);
}
else
{
UE_LOG(LogCook, Display, TEXT("---- Running UShaderPipelineCacheToolsCommandlet for platform %s shader format %s"), *TargetPlatformName, *ShaderFormat.ToString());
const FString OutFilename = FString::Printf(TEXT("%s_%s.stable.upipelinecache"), *LibraryName, *ShaderFormat.ToString());
const FString PCUncookedPath = FPaths::ProjectDir() / TEXT("Content") / TEXT("PipelineCaches") / TargetPlatform->IniPlatformName() / OutFilename;
if (IFileManager::Get().FileExists(*PCUncookedPath))
{
UE_LOG(LogCook, Warning, TEXT("Deleting %s, cooked data doesn't belong here."), *PCUncookedPath);
IFileManager::Get().Delete(*PCUncookedPath, false, true);
}
const FString PCCookedPath = ConvertToFullSandboxPath(*PCUncookedPath, true);
const FString PCPath = PCCookedPath.Replace(TEXT("[Platform]"), *TargetPlatformName);
FString Args(TEXT("build "));
if (bBinaryStablePipelineCacheFilesFound)
{
Args += TEXT("\"");
Args += StablePCBinary;
Args += TEXT("\" ");
}
if (bTextualStablePipelineCacheFilesFound)
{
Args += TEXT("\"");
Args += StablePCTextual;
Args += TEXT("\" ");
}
int32 NumMatched = 0;
for (int32 Index = 0; Index < SCLCSVPaths->Num(); Index++)
{
if (!(*SCLCSVPaths)[Index].Contains(ShaderFormat.ToString()))
{
continue;
}
NumMatched++;
Args += TEXT(" ");
Args += TEXT("\"");
Args += (*SCLCSVPaths)[Index];
Args += TEXT("\"");
}
if (!NumMatched)
{
UE_LOG(LogCook, Warning, TEXT("Shader format %s for platform %s had stable pipeline cache files, but no stable keys files."), *ShaderFormat.ToString(), *TargetPlatformName);
for (int32 Index = 0; Index < SCLCSVPaths->Num(); Index++)
{
UE_LOG(LogCook, Warning, TEXT(" stable keys file: %s"), *((*SCLCSVPaths)[Index]));
}
continue;
}
Args += TEXT(" -chunkinfodir=\"");
Args += ConvertToFullSandboxPath(FPaths::ProjectDir() / TEXT("Content"), true).Replace(TEXT("[Platform]"), *TargetPlatformName);
Args += TEXT("\" ");
Args += TEXT(" -library=");
Args += LibraryName;
Args += TEXT(" ");
Args += TEXT(" -platform=");
Args += TargetPlatformName;
Args += TEXT(" ");
Args += TEXT("\"");
Args += PCPath;
Args += TEXT("\"");
UE_LOG(LogCook, Display, TEXT(" With Args: %s"), *Args);
int32 Result = UShaderPipelineCacheToolsCommandlet::StaticMain(Args);
if (Result)
{
LogCookerMessage(FString::Printf(TEXT("UShaderPipelineCacheToolsCommandlet failed %d"), Result), EMessageSeverity::Error);
}
else
{
UE_LOG(LogCook, Display, TEXT("---- Done running UShaderPipelineCacheToolsCommandlet for platform %s"), *TargetPlatformName);
// copy the resulting file to metadata for easier examination later
if (IFileManager::Get().FileExists(*PCPath))
{
const FString RootPipelineCacheMetadataPath = GetMetadataDirectory() / TEXT("PipelineCaches");
const FString PipelineCacheMetadataPathSB = ConvertToFullSandboxPath(*RootPipelineCacheMetadataPath, true);
const FString PipelineCacheMetadataPath = PipelineCacheMetadataPathSB.Replace(TEXT("[Platform]"), *TargetPlatform->PlatformName());
const FString PipelineCacheMetadataFileName = PipelineCacheMetadataPath / OutFilename;
UE_LOG(LogCook, Display, TEXT("Copying the binary PSO cache file %s to %s."), *PCPath, *PipelineCacheMetadataFileName);
if (IFileManager::Get().Copy(*PipelineCacheMetadataFileName, *PCPath) != COPY_OK)
{
UE_LOG(LogCook, Warning, TEXT("Failed to copy the binary PSO cache file %s to %s."), *PCPath, *PipelineCacheMetadataFileName);
}
}
}
}
}
}
}
void UCookOnTheFlyServer::SaveAndCloseGlobalShaderLibrary()
{
const bool bCacheShaderLibraries = IsUsingShaderCodeLibrary();
if (bCacheShaderLibraries)
{
const TCHAR* GlobalShaderLibName = TEXT("Global");
FString ActualName = GenerateShaderCodeLibraryName(GlobalShaderLibName, IsCookFlagSet(ECookInitializationFlags::LegacyIterativeSharedBuild));
// Save shader code map - cleaning directories is deliberately a separate loop here as we open the cache once per shader platform and we don't assume that they can't be shared across target platforms.
for (const ITargetPlatform* TargetPlatform : PlatformManager->GetSessionPlatforms())
{
FinishPopulateShaderLibrary(TargetPlatform, GlobalShaderLibName);
SaveShaderLibrary(TargetPlatform, GlobalShaderLibName);
}
FShaderLibraryCooker::EndCookingLibrary(ActualName);
}
}
void UCookOnTheFlyServer::GetShaderLibraryPaths(const ITargetPlatform* TargetPlatform,
FString& OutShaderCodeDir, FString& OutMetaDataPath, bool bUseProjectDirForDLC)
{
// TODO: Saving ShaderChunks into the DLC directory currently does not work, so we have the bUseProjectDirForDLC arg to save to Project
const FString BasePath = (!IsCookingDLC() || bUseProjectDirForDLC) ? FPaths::ProjectContentDir() : GetContentDirectoryForDLC();
OutShaderCodeDir = ConvertToFullSandboxPath(*BasePath, true, TargetPlatform->PlatformName());
const FString RootMetaDataPath = GetMetadataDirectory() / TEXT("PipelineCaches");
OutMetaDataPath = ConvertToFullSandboxPath(*RootMetaDataPath, true, *TargetPlatform->PlatformName());
}
void UCookOnTheFlyServer::FinishPopulateShaderLibrary(const ITargetPlatform* TargetPlatform, const FString& Name)
{
FString ShaderCodeDir;
FString MetaDataPath;
GetShaderLibraryPaths(TargetPlatform, ShaderCodeDir, MetaDataPath);
FShaderLibraryCooker::FinishPopulateShaderLibrary(TargetPlatform, Name, ShaderCodeDir, MetaDataPath);
}
void UCookOnTheFlyServer::SaveShaderLibrary(const ITargetPlatform* TargetPlatform, const FString& Name)
{
FString ShaderCodeDir;
FString MetaDataPath;
GetShaderLibraryPaths(TargetPlatform, ShaderCodeDir, MetaDataPath);
TArray<FString>& PlatformSCLCSVPaths = OutSCLCSVPaths.FindOrAdd(FName(TargetPlatform->PlatformName()));
FString ErrorString;
bool bHasData;
if (!FShaderLibraryCooker::SaveShaderLibraryWithoutChunking(TargetPlatform, Name, ShaderCodeDir, MetaDataPath,
PlatformSCLCSVPaths, ErrorString, bHasData))
{
// This is fatal - In this case we should cancel any launch on device operation or package write but we don't want to assert and crash the editor
LogCookerMessage(FString::Printf(TEXT("%s"), *ErrorString), EMessageSeverity::Error);
}
else if (bHasData)
{
for (const FString& Item : PlatformSCLCSVPaths)
{
UE_LOG(LogCook, Display, TEXT("Saved scl.csv %s for platform %s, %d bytes"), *Item, *TargetPlatform->PlatformName(),
IFileManager::Get().FileSize(*Item));
}
}
}
void UCookOnTheFlyServer::ShutdownShaderLibraryCookerAndCompilers(const FString& ProjectShaderLibraryName)
{
FString ActualLibraryName = GenerateShaderCodeLibraryName(ProjectShaderLibraryName,
IsCookFlagSet(ECookInitializationFlags::LegacyIterativeSharedBuild));
FShaderLibraryCooker::EndCookingLibrary(ActualLibraryName);
FShaderLibraryCooker::Shutdown();
ShutdownShaderCompilers(PlatformManager->GetSessionPlatforms());
}
void UCookOnTheFlyServer::DumpShaderTypeStats(const FString& PlatformNameString)
{
FShaderLibraryCooker::DumpShaderTypeStats(
GShaderCompilingManager->GetAbsoluteShaderDebugInfoDirectory(),
ConvertToFullSandboxPath(GetMetadataDirectory(), true, PlatformNameString));
}
bool UCookOnTheFlyServer::RecompileChangedShaders(const TArray<const ITargetPlatform*>& TargetPlatforms)
{
bool bShadersRecompiled = false;
for (const ITargetPlatform* TargetPlatform : TargetPlatforms)
{
bShadersRecompiled |= RecompileChangedShadersForPlatform(TargetPlatform->PlatformName());
}
return bShadersRecompiled;
}