Files
UnrealEngine/Engine/Plugins/FX/Niagara/Source/NiagaraEditor/Private/NiagaraCompilationTasks.cpp
Brandyn / Techy fcc1b09210 init
2026-04-04 15:40:51 -05:00

2817 lines
104 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "NiagaraCompilationTasks.h"
#include "Algo/RemoveIf.h"
#include "AssetCompilingManager.h"
#include "DataDrivenShaderPlatformInfo.h"
#include "Interfaces/ITargetPlatformManagerModule.h"
#include "Misc/PathViews.h"
#include "NiagaraCompilationTypes.h"
#include "NiagaraDigestDatabase.h"
#include "NiagaraEmitter.h"
#include "NiagaraGraphDigest.h"
#include "NiagaraCompilationBridge.h"
#include "NiagaraScriptSource.h"
#include "NiagaraSettings.h"
#include "NiagaraShader.h"
#include "NiagaraShaderParametersBuilder.h"
#include "NiagaraSimulationStageBase.h"
#include "NiagaraSystem.h"
#include "NiagaraSystemCompilingManager.h"
#include "ProfilingDebugging/CookStats.h"
#include "Serialization/CompactBinary.h"
#include "Serialization/CompactBinaryWriter.h"
#include "Serialization/ObjectAndNameAsStringProxyArchive.h"
#include "ShaderParameterMetadataBuilder.h"
#include "ShaderSerialization.h"
#include "VectorVM.h"
#include "VectorVMBridge.h"
#include "VectorVMOptimizer.h"
// needed for AsType...pretty ugly
#include "NiagaraNodeOutput.h"
#include "NiagaraNodeFunctionCall.h"
#if ENABLE_COOK_STATS
namespace NiagaraSystemCookStats
{
extern FCookStats::FDDCResourceUsageStats UsageStats;
static int32 ScriptTranslationCount = 0;
static int32 CpuScriptCompileCount = 0;
static int32 CpuScriptDdcCacheHitCount = 0;
static int32 CpuScriptDdcCacheMissCount = 0;
static int32 GpuScriptCompileCount = 0;
static int32 GpuScriptDdcCacheHitCount = 0;
static int32 GpuScriptDdcCacheMissCount = 0;
static FCookStatsManager::FAutoRegisterCallback RegisterNiagaraCompilationCookStats([](FCookStatsManager::AddStatFuncRef AddStat)
{
AddStat(TEXT("Niagara"), FCookStatsManager::CreateKeyValueArray(TEXT("NiagaraScriptTranslationCount"), ScriptTranslationCount));
AddStat(TEXT("Niagara"), FCookStatsManager::CreateKeyValueArray(TEXT("NiagaraCpuScriptCompileCount"), CpuScriptCompileCount));
AddStat(TEXT("Niagara"), FCookStatsManager::CreateKeyValueArray(TEXT("NiagaraCpuScriptDdcCacheHitCount"), CpuScriptDdcCacheHitCount));
AddStat(TEXT("Niagara"), FCookStatsManager::CreateKeyValueArray(TEXT("NiagaraCpuScriptDdcCacheMissCount"), CpuScriptDdcCacheMissCount));
AddStat(TEXT("Niagara"), FCookStatsManager::CreateKeyValueArray(TEXT("NiagaraGpuScriptCompileCount"), GpuScriptCompileCount));
AddStat(TEXT("Niagara"), FCookStatsManager::CreateKeyValueArray(TEXT("NiagaraGpuScriptDdcCacheHitCount"), GpuScriptDdcCacheHitCount));
AddStat(TEXT("Niagara"), FCookStatsManager::CreateKeyValueArray(TEXT("NiagaraGpuScriptDdcCacheMissCount"), GpuScriptDdcCacheMissCount));
});
}
#endif
namespace NiagaraCompilationTasksImpl
{
static const FGuid UE_NIAGARA_ASYNC_TASK_COMPILER_VER = FGuid(0x73F57E8B, 0xD0AC44E8, 0xBEE372D4, 0x1CD85A69);
static const UE::DerivedData::FCacheBucket NiagaraDDCBucket("NiagaraScript");
static const UE::DerivedData::FValueId ScriptDataValue = UE::DerivedData::FValueId::FromName(TEXT("ScriptData"));
static bool GDebugParseCompileMetaDataOnGet = false;
static FAutoConsoleVariableRef CVarGDebugParseCompileMetaDataOnGet(
TEXT("fx.Niagara.ParseCompileMetaDataOnGet"),
GDebugParseCompileMetaDataOnGet,
TEXT("When true the compilation metadata from DDC will be parsed on get."),
ECVF_Default
);
void GetUsagesToDuplicate(ENiagaraScriptUsage TargetUsage, TArray<ENiagaraScriptUsage>& DuplicateUsages)
{
// For now we need to include both spawn and update for each target usage, otherwise attribute lists in the precompiled histories aren't generated correctly.
switch (TargetUsage)
{
case ENiagaraScriptUsage::SystemSpawnScript:
case ENiagaraScriptUsage::SystemUpdateScript:
DuplicateUsages.Add(ENiagaraScriptUsage::SystemSpawnScript);
DuplicateUsages.Add(ENiagaraScriptUsage::SystemUpdateScript);
DuplicateUsages.Add(ENiagaraScriptUsage::EmitterSpawnScript);
DuplicateUsages.Add(ENiagaraScriptUsage::EmitterUpdateScript);
break;
case ENiagaraScriptUsage::ParticleSpawnScript:
case ENiagaraScriptUsage::ParticleSpawnScriptInterpolated:
case ENiagaraScriptUsage::ParticleUpdateScript:
case ENiagaraScriptUsage::ParticleEventScript:
case ENiagaraScriptUsage::ParticleSimulationStageScript:
case ENiagaraScriptUsage::ParticleGPUComputeScript:
DuplicateUsages.Add(ENiagaraScriptUsage::ParticleSpawnScript);
DuplicateUsages.Add(ENiagaraScriptUsage::ParticleSpawnScriptInterpolated);
DuplicateUsages.Add(ENiagaraScriptUsage::ParticleUpdateScript);
DuplicateUsages.Add(ENiagaraScriptUsage::ParticleEventScript);
DuplicateUsages.Add(ENiagaraScriptUsage::ParticleSimulationStageScript);
DuplicateUsages.Add(ENiagaraScriptUsage::ParticleGPUComputeScript);
break;
}
}
TArray<UNiagaraScript*> FindDependentScripts(UNiagaraSystem* System, FVersionedNiagaraEmitterData* EmitterData, UNiagaraScript* Script)
{
TArray<UNiagaraScript*> DependentScripts;
const ENiagaraScriptUsage ScriptUsage = Script->GetUsage();
if (System)
{
if (UNiagaraScript::IsEquivalentUsage(ScriptUsage, ENiagaraScriptUsage::EmitterSpawnScript))
{
DependentScripts.Add(System->GetSystemSpawnScript());
}
if (UNiagaraScript::IsEquivalentUsage(ScriptUsage, ENiagaraScriptUsage::EmitterUpdateScript))
{
DependentScripts.Add(System->GetSystemUpdateScript());
}
}
if (EmitterData)
{
const bool bIsGpuEmitter = EmitterData->SimTarget == ENiagaraSimTarget::GPUComputeSim;
if (UNiagaraScript::IsEquivalentUsage(ScriptUsage, ENiagaraScriptUsage::ParticleSpawnScript))
{
if (bIsGpuEmitter)
{
DependentScripts.Add(EmitterData->GetGPUComputeScript());
}
}
if (UNiagaraScript::IsEquivalentUsage(ScriptUsage, ENiagaraScriptUsage::ParticleUpdateScript))
{
if (bIsGpuEmitter)
{
DependentScripts.Add(EmitterData->GetGPUComputeScript());
}
else if (EmitterData->UsesInterpolatedSpawning())
{
DependentScripts.Add(EmitterData->SpawnScriptProps.Script);
}
}
}
return DependentScripts;
}
static UE::DerivedData::FCacheKey BuildNiagaraDDCCacheKey(const FNiagaraVMExecutableDataId& CompileId, const FString& ScriptPath)
{
FString KeyString;
KeyString.Reserve(1024);
NiagaraCompilationTasksImpl::UE_NIAGARA_ASYNC_TASK_COMPILER_VER.AppendString(KeyString);
KeyString.AppendChar(TCHAR('_'));
KeyString.Appendf(TEXT("%i"), GNiagaraSkipVectorVMBackendOptimizations);
KeyString.AppendChar(TCHAR('_'));
KeyString.Append(ScriptPath);
KeyString.AppendChar(TCHAR('_'));
CompileId.AppendKeyString(KeyString);
return { NiagaraDDCBucket, FIoHash::HashBuffer(MakeMemoryView(FTCHARToUTF8(KeyString))) };
}
static UE::DerivedData::FCacheKey BuildNiagaraComputeDDCCacheKey(const FNiagaraShaderMapId& ShaderMapId, EShaderPlatform ShaderPlatform, const FString& ScriptPath)
{
static const FString NIAGARASHADERMAP_DERIVEDDATA_VER = FDevSystemGuids::GetSystemGuid(FDevSystemGuids::Get().NIAGARASHADERMAP_DERIVEDDATA_VER).ToString(EGuidFormats::DigitsWithHyphens);
FName Format = LegacyShaderPlatformToShaderFormat(ShaderPlatform);
FString KeyString;
KeyString.Reserve(1024);
Format.ToString(KeyString);
KeyString.Appendf(TEXT("_%d_"), GetTargetPlatformManagerRef().ShaderFormatVersion(Format));
ShaderMapAppendKeyString(ShaderPlatform, KeyString);
ShaderMapId.AppendKeyString(KeyString);
KeyString.AppendChar(TCHAR('_'));
KeyString.Append(ScriptPath);
KeyString.AppendChar(TCHAR('_'));
KeyString.Append(NIAGARASHADERMAP_DERIVEDDATA_VER);
return { NiagaraDDCBucket, FIoHash::HashBuffer(MakeMemoryView(FTCHARToUTF8(KeyString))) };
}
static bool ValidateExecData(const UNiagaraScript* Script, const FNiagaraVMExecutableData& ExecData, FString& ErrorString)
{
bool IsValid = true;
for (const auto& Attribute : ExecData.Attributes)
{
if (!Attribute.IsValid())
{
ErrorString.Appendf(TEXT("Failure - %s - Attribute [%s] is invalid!\n"), Script ? *Script->GetFullName() : TEXT("<unknown>"), *Attribute.GetName().ToString());
IsValid = false;
}
}
for (const auto& Parameter : ExecData.Parameters.Parameters)
{
if (!Parameter.IsValid())
{
ErrorString.Appendf(TEXT("Failure - %s - Parameter [%s] is invalid!\n"), Script ? *Script->GetFullName() : TEXT("<unknown>"), *Parameter.GetName().ToString());
IsValid = false;
}
}
return IsValid;
}
static bool BinaryToExecData(const UNiagaraScript* Script, FSharedBuffer SharedBuffer, FNiagaraVMExecutableData& OutExecData)
{
if (!UNiagaraScript::ShouldUseDDC())
{
return false;
}
if (SharedBuffer.GetSize() == 0)
{
return false;
}
FMemoryReaderView Ar(SharedBuffer.GetView(), true);
// Read the archive version from the header of the payload so we can setup our reader with the right version
FPackageFileVersion ArchiveVersion;
Ar << ArchiveVersion;
if (!ArchiveVersion.IsCompatible(GOldestLoadablePackageFileUEVersion))
{
UE_LOG(LogNiagaraEditor, Display, TEXT("Failed to validate FNiagaraVMExecutableData received from DDC, rejecting! Reasons:\nDeprecated object version"));
return false;
}
FObjectAndNameAsStringProxyArchive SafeAr(Ar, false);
SafeAr.SetUEVer(ArchiveVersion);
OutExecData.SerializeData(SafeAr, true);
FString ValidationErrors;
if (!ValidateExecData(Script, OutExecData, ValidationErrors))
{
UE_LOG(LogNiagaraEditor, Display, TEXT("Failed to validate FNiagaraVMExecutableData received from DDC, rejecting! Reasons:\n%s"), *ValidationErrors);
return false;
}
return !SafeAr.IsError();
}
static FSharedBuffer ExecToBinaryData(const UNiagaraScript* Script, FNiagaraVMExecutableData& InExecData)
{
if (!UNiagaraScript::ShouldUseDDC())
{
return FSharedBuffer();
}
FString ValidationErrors;
if (!ValidateExecData(Script, InExecData, ValidationErrors))
{
UE_LOG(LogNiagaraEditor, Error, TEXT("Failed to validate FNiagaraVMExecutableData being pushed to DDC, rejecting! Errors:\n%s"), *ValidationErrors);
return FSharedBuffer();
}
TArray<uint8> BinaryData;
FMemoryWriter Ar(BinaryData, true);
// include the archive version into the payload since we're using struct serialization for the ExecData
FPackageFileVersion ArchiveVersion = GPackageFileUEVersion;
Ar << ArchiveVersion;
FObjectAndNameAsStringProxyArchive SafeAr(Ar, false);
InExecData.SerializeData(SafeAr, true);
if (!BinaryData.IsEmpty() && !SafeAr.IsError())
{
return MakeSharedBufferFromArray(MoveTemp(BinaryData));
}
return FSharedBuffer();
}
static FSharedBuffer ShaderMapToBinaryData(FNiagaraShaderMap* ShaderMap)
{
if (!UNiagaraScript::ShouldUseDDC())
{
return FSharedBuffer();
}
TArray<uint8> BinaryData;
FMemoryWriter Ar(BinaryData, true);
FShaderSerializeContext Ctx(Ar);
ShaderMap->Serialize(Ctx);
if (!BinaryData.IsEmpty() && !Ar.IsError())
{
return MakeSharedBufferFromArray(MoveTemp(BinaryData));
}
return FSharedBuffer();
}
static FNiagaraShaderMapRef BinaryDataToShaderMap(FSharedBuffer SharedBuffer)
{
if (!UNiagaraScript::ShouldUseDDC() || SharedBuffer.GetSize() == 0)
{
return nullptr;
}
FMemoryReaderView Ar(SharedBuffer.GetView(), true);
FNiagaraShaderMapRef ShaderMap = new FNiagaraShaderMap(FNiagaraShaderMap::WorkerThread);
FShaderSerializeContext Ctx(Ar);
ShaderMap->Serialize(Ctx);
if (Ar.IsError())
{
return nullptr;
}
return ShaderMap;
}
static TArray<FNiagaraVariable> ExtractInputVariablesFromHistory(const TNiagaraParameterMapHistory<FNiagaraCompilationDigestBridge>& History, const FNiagaraCompilationGraph* FunctionGraph)
{
TArray<FNiagaraVariable> InputVariables;
const int32 ModuleNamespaceLength = FNiagaraConstants::ModuleNamespaceString.Len();
for (int32 i = 0; i < History.Variables.Num(); i++)
{
const FNiagaraVariable& Variable = History.Variables[i];
const TArray<TNiagaraParameterMapHistory<FNiagaraCompilationDigestBridge>::FReadHistory>& ReadHistory = History.PerVariableReadHistory[i];
// A read is only really exposed if it's the first read and it has no corresponding write.
if (ReadHistory.Num() > 0 && ReadHistory[0].PreviousWritePin.Pin == nullptr)
{
const FNiagaraCompilationPin* InputPin = ReadHistory[0].ReadPin.Pin;
if (FNiagaraStackGraphUtilities::IsRapidIterationType(InputPin->Variable.GetType()))
{
// Make sure that the module input is from the called graph, and not a nested graph.
const FNiagaraCompilationGraph* NodeGraph = InputPin->OwningNode->OwningGraph;
if (NodeGraph == FunctionGraph)
{
FNameBuilder InputPinName(InputPin->PinName);
FStringView InputPinView(InputPinName);
if (InputPinView.StartsWith(FNiagaraConstants::ModuleNamespaceString)
&& InputPinView.Len() > ModuleNamespaceLength
&& InputPinView[ModuleNamespaceLength] == TCHAR('.'))
{
InputVariables.Add(InputPin->Variable);
}
}
}
}
}
return InputVariables;
}
struct FScriptCompileMetaData
{
FScriptCompileMetaData(const FCbObject& MetaData)
{
Version = static_cast<EMetaDataVersion>(MetaData.Find(ANSITEXTVIEW("Version")).AsInt32());
if (Version == EMetaDataVersion::Latest)
{
BuildString = FString(MetaData.Find(ANSITEXTVIEW("Build")).AsString());
DateTime = MetaData.Find(ANSITEXTVIEW("Time")).AsDateTime();
CompileIdString = FString(MetaData.Find(ANSITEXTVIEW("CompileId")).AsString());
CompileTaskId = FGuid(FString(MetaData.Find(ANSITEXTVIEW("CompileTaskId")).AsString()));
const FCbArray RIParamArray = MetaData.Find(ANSITEXTVIEW("RIParam")).AsArray();
for (FCbFieldViewIterator It = RIParamArray.CreateViewIterator(); It; ++It)
{
TTuple<FName, FNiagaraVariant>& NamedVariant = BakedRapidIterationParameters.AddDefaulted_GetRef();
FCbObjectView RIParamView = It->AsObjectView();
NamedVariant.Key = *FString(RIParamView.FindView(ANSITEXTVIEW("VarName")).AsString());
FMemoryView VariantDataView = RIParamView.FindView(ANSITEXTVIEW("VarData")).AsBinaryView();
NamedVariant.Value = FNiagaraVariant(VariantDataView.GetData(), VariantDataView.GetSize());
}
}
}
FScriptCompileMetaData(const FNiagaraSystemCompilationTask* SystemCompileTask, const FNiagaraSystemCompilationTask::FCompileTaskInfo& CompileTask)
{
Version = EMetaDataVersion::Latest;
BuildString = FApp::GetBuildVersion();
DateTime = FDateTime::UtcNow();
CompileTask.CompileId.AppendKeyString(CompileIdString);
CompileTaskId = SystemCompileTask->GetCompilationTaskId();
BakedRapidIterationParameters.Reserve(CompileTask.BakedRapidIterationParameters.Num());
Algo::Transform(CompileTask.BakedRapidIterationParameters, BakedRapidIterationParameters, [](const FNiagaraVariable& InVar) -> TTuple<FName, FNiagaraVariant>
{
return MakeTuple(InVar.GetName(), FNiagaraVariant(InVar.GetData(), InVar.GetAllocatedSizeInBytes()));
});
}
FCbObject ToCompactBinary() const
{
FCbWriter Writer;
Writer.BeginObject();
{
Writer.AddInteger(ANSITEXTVIEW("Version"), static_cast<int32>(Version));
Writer.AddString(ANSITEXTVIEW("Build"), BuildString);
Writer.AddDateTime(ANSITEXTVIEW("Time"), DateTime);
Writer.AddString(ANSITEXTVIEW("CompileId"), CompileIdString);
Writer.AddString(ANSITEXTVIEW("CompileTaskId"), CompileTaskId.ToString());
Writer.BeginArray(ANSITEXTVIEW("RIParam"));
{
for (const TTuple<FName, FNiagaraVariant>& Var : BakedRapidIterationParameters)
{
Writer.BeginObject();
{
Writer.AddString(ANSITEXTVIEW("VarName"), *Var.Key.ToString());
Writer.AddBinary(ANSITEXTVIEW("VarData"), Var.Value.GetBytes(), Var.Value.GetNumBytes());
}
Writer.EndObject();
}
}
Writer.EndArray();
}
Writer.EndObject();
return Writer.Save().AsObject();
}
enum class EMetaDataVersion : int32
{
Invalid = 0,
// new entries
WithCompilationTaskId,
//
Last,
Latest = Last - 1,
};
EMetaDataVersion Version = EMetaDataVersion::Invalid;
FString BuildString;
FDateTime DateTime;
FString CompileIdString;
FGuid CompileTaskId;
TArray<TTuple<FName, FNiagaraVariant>> BakedRapidIterationParameters;
};
static FCbObject GenerateScriptCompileMetaData(const FNiagaraSystemCompilationTask* SystemCompileTask, const FNiagaraSystemCompilationTask::FCompileTaskInfo& CompileTask)
{
return FScriptCompileMetaData(SystemCompileTask, CompileTask).ToCompactBinary();
}
static void ParseScriptCompileMetaData(const FCbObject& MetaData)
{
FScriptCompileMetaData Data(MetaData);
}
void AddRapidIterationParameter(
const FNiagaraParameterHandle& ParameterHandle,
const FNiagaraVariable& InputVariable,
const FNiagaraVariable& Parameter,
const FNiagaraCompilationNodeFunctionCall* FunctionCallNode,
FNiagaraParameterStore& ParameterStore)
{
const int32 ExistingParameterIndex = ParameterStore.IndexOf(Parameter);
if (ExistingParameterIndex == INDEX_NONE)
{
// we only try to add the variable if we've already determined that it's got a reasonable
// default value and there's no override pin for the function input
if (Parameter.IsDataAllocated() && !FunctionCallNode->HasOverridePin(ParameterHandle))
{
constexpr bool bAddParameterIfMissing = true;
ParameterStore.SetParameterData(Parameter.GetData(), Parameter, bAddParameterIfMissing);
}
}
}
void FixupRenamedParameter(
const FNiagaraVariable& InputVariable,
const FNiagaraVariable& Parameter,
const FNiagaraCompilationNodeFunctionCall* FunctionCallNode,
const TMultiMap<FGuid, FNiagaraVariable>& GuidMapping,
FNiagaraParameterStore& ParameterStore)
{
TOptional<FNiagaraVariableMetaData> VariableMetaData = FunctionCallNode->CalledGraph->GetMetaData(InputVariable);
if (!VariableMetaData.IsSet())
{
return;
}
TArray<FNiagaraVariable> ExistingParameters;
GuidMapping.MultiFind(VariableMetaData->GetVariableGuid(), ExistingParameters);
// if we didn't match anything then there's nothing left for us to do
if (ExistingParameters.IsEmpty())
{
return;
}
for (const FNiagaraVariable& ExistingParameter : ExistingParameters)
{
// if we've found a match then there's nothing to rename
if (ExistingParameter.GetName() == Parameter.GetName())
{
return;
}
}
// next go through to see if we may need to actually do a rename. We'll only do this if we have something
// that matches the GUID and is in the same namespace
FNameBuilder NameBuilder(Parameter.GetName());
int32 NamespaceStrCount;
if (NameBuilder.ToView().FindLastChar(TEXT('.'), NamespaceStrCount))
{
FStringView ParameterNamespace = NameBuilder.ToView().Left(NamespaceStrCount);
for (const FNiagaraVariable& ExistingParameter : ExistingParameters)
{
if (ExistingParameter.IsInNameSpace(ParameterNamespace))
{
ParameterStore.RenameParameter(ExistingParameter, Parameter.GetName());
return;
}
}
}
}
template<typename T, typename InAllocatorType, typename OutAllocatorType>
static void AppendUnique(const TArray<T, InAllocatorType>& Input, TArray<T, OutAllocatorType>& Output)
{
Output.Reserve(Input.Num() + Output.Num());
for (const T& InputValue : Input)
{
Output.AddUnique(InputValue);
}
}
const UNiagaraGraph* GetGraphFromScriptSource(const UNiagaraScriptSourceBase* ScriptSourceBase)
{
if (const UNiagaraScriptSource* ScriptSource = CastChecked<const UNiagaraScriptSource>(ScriptSourceBase))
{
return ScriptSource->NodeGraph;
}
return nullptr;
}
const UNiagaraGraph* GetGraphFromScript(const UNiagaraScript* Script)
{
if (Script)
{
return GetGraphFromScriptSource(Script->GetLatestSource());
}
return nullptr;
}
TSharedPtr<FNiagaraShaderScriptParametersMetadata> BuildScriptParametersMetadata(const FNiagaraCompilationCopyData* CompilationCopyData, const FNiagaraShaderScriptParametersMetadata& InScriptParametersMetadata)
{
TSharedPtr<FNiagaraShaderScriptParametersMetadata> ScriptParametersMetadata = MakeShared<FNiagaraShaderScriptParametersMetadata>();
ScriptParametersMetadata->DataInterfaceParamInfo = InScriptParametersMetadata.DataInterfaceParamInfo;
FShaderParametersMetadataBuilder ShaderMetadataBuilder(TShaderParameterStructTypeInfo<FNiagaraShader::FParameters>::GetStructMetadata());
auto FindDataInterfaceByName = [CompilationCopyData](const FString& DIName) -> const UNiagaraDataInterfaceBase*
{
for (const auto& DataInterfacePair : CompilationCopyData->AggregatedDataInterfaceCDODuplicates)
{
if (DataInterfacePair.Key->GetName() == DIName)
{
return DataInterfacePair.Value;
}
}
return nullptr;
};
// Build meta data for each data interface
for (FNiagaraDataInterfaceGPUParamInfo& DataInterfaceParamInfo : ScriptParametersMetadata->DataInterfaceParamInfo)
{
const UNiagaraDataInterfaceBase* DataInterface = FindDataInterfaceByName(DataInterfaceParamInfo.DIClassName);
if (ensure(DataInterface))
{
const uint32 NextMemberOffset = ShaderMetadataBuilder.GetNextMemberOffset();
FNiagaraShaderParametersBuilder ShaderParametersBuilder(DataInterfaceParamInfo, ScriptParametersMetadata->LooseMetadataNames, ScriptParametersMetadata->StructIncludeInfos, ShaderMetadataBuilder);
DataInterface->BuildShaderParameters(ShaderParametersBuilder);
DataInterfaceParamInfo.ShaderParametersOffset = NextMemberOffset;
}
}
ScriptParametersMetadata->ShaderParametersMetadata = MakeShareable<FShaderParametersMetadata>(ShaderMetadataBuilder.Build(FShaderParametersMetadata::EUseCase::ShaderParameterStruct, TEXT("FNiagaraShaderScript")));
return ScriptParametersMetadata;
}
void BuildDebugGroupName(const FNiagaraPrecompileData& PrecompileData, const FNiagaraCompileOptions& CompileOptions, FStringBuilderBase& DebugGroupName)
{
FPathViews::Append(DebugGroupName, PrecompileData.SourceName);
FPathViews::Append(DebugGroupName, PrecompileData.EmitterUniqueName);
FPathViews::Append(DebugGroupName, PrecompileData.ENiagaraScriptUsageEnum->GetNameStringByValue((int64)CompileOptions.TargetUsage));
if (CompileOptions.TargetUsageId.IsValid())
{
DebugGroupName << TEXT("_");
CompileOptions.TargetUsageId.AppendString(DebugGroupName, EGuidFormats::Digits);
}
}
} // NiagaraCompilationTasksImpl
FNiagaraSystemCompilationTask::FNiagaraSystemCompilationTask(FNiagaraCompilationTaskHandle InTaskHandle, UNiagaraSystem* InSystem, ENiagaraCompileRIParamMode InRIParamMode)
: TaskHandle(InTaskHandle)
, RIParamMode(InRIParamMode)
, System_GT(InSystem)
{
bGenerateCompileMetaData = GetDefault<UNiagaraSettings>()->bGenerateMetaDataOnCompile;
CompilationTaskId = FGuid::NewGuid();
}
void FNiagaraSystemCompilationTask::Abort()
{
bAborting = true;
CompileCompletionEvent.Trigger();
}
FString FNiagaraSystemCompilationTask::GetDescription() const
{
FString TaskDescription;
TaskDescription.Append(SystemInfo.SystemName);
if (bCompileForEdit)
{
TaskDescription.Append(TEXT(" [Edit]"));
}
return TaskDescription;
}
FString FNiagaraSystemCompilationTask::GetStatusString() const
{
FString StatusString;
switch (CompilationState)
{
case EState::Invalid:
{
StatusString = TEXT("Invalid");
break;
}
case EState::WaitingForProcessing:
{
StatusString = TEXT("WaitingForProcessing");
break;
}
case EState::ResultsProcessed:
{
StatusString = TEXT("ResultsProcessed");
break;
}
case EState::Completed:
{
StatusString = TEXT("Completed");
break;
}
case EState::Aborted:
{
StatusString = TEXT("Aborted");
break;
}
}
return StatusString;
}
const FNiagaraSystemCompilationTask::FEmitterInfo* FNiagaraSystemCompilationTask::FSystemInfo::EmitterInfoBySourceEmitter(int32 InSourceEmitterIndex) const
{
return EmitterInfo.FindByPredicate([InSourceEmitterIndex](const FEmitterInfo& Info) -> bool
{
return Info.SourceEmitterIndex == InSourceEmitterIndex;
});
}
FNiagaraSystemCompilationTask::FEmitterInfo* FNiagaraSystemCompilationTask::FSystemInfo::EmitterInfoBySourceEmitter(int32 InSourceEmitterIndex)
{
return EmitterInfo.FindByPredicate([InSourceEmitterIndex](const FEmitterInfo& Info) -> bool
{
return Info.SourceEmitterIndex == InSourceEmitterIndex;
});
}
FNiagaraSystemCompilationTask::FCompileGroupInfo::FCompileGroupInfo(int32 InSourceEmitterIndex)
: SourceEmitterIndex(InSourceEmitterIndex)
{}
bool FNiagaraSystemCompilationTask::FCompileGroupInfo::HasOutstandingCompileTasks(const FNiagaraSystemCompilationTask& ParentTask) const
{
for (int32 CompileTaskIndex : CompileTaskIndices)
{
const FCompileTaskInfo& CompileTask = ParentTask.CompileTasks[CompileTaskIndex];
if (CompileTask.IsOutstanding(ParentTask))
{
return true;
}
}
return false;
}
void FNiagaraSystemCompilationTask::FCompileGroupInfo::InstantiateCompileGraph(const FNiagaraSystemCompilationTask& ParentTask)
{
TRACE_CPUPROFILER_EVENT_SCOPE(NiagaraAsyncTask_InstantiateGraph);
CompilationCopy = MakeShared<FNiagaraCompilationCopyData, ESPMode::ThreadSafe>();
FNiagaraCompilationCopyData& BasePtr = *CompilationCopy.Get();
TArray<TSharedPtr<FNiagaraCompilationCopyData, ESPMode::ThreadSafe>> DependentRequests;
FCompileConstantResolver EmptyResolver;
BasePtr.ValidUsages = ValidUsages;
// First deep copy all the emitter graphs referenced by the system so that we can later hook up emitter handles in the system traversal.
const int32 EmitterCount = ParentTask.SystemInfo.EmitterInfo.Num();
BasePtr.EmitterData.Reserve(EmitterCount);
for (const FEmitterInfo& EmitterInfo : ParentTask.SystemInfo.EmitterInfo)
{
TSharedPtr<FNiagaraCompilationCopyData, ESPMode::ThreadSafe> EmitterPtr = MakeShared<FNiagaraCompilationCopyData, ESPMode::ThreadSafe>();
EmitterPtr->EmitterUniqueName = EmitterInfo.UniqueEmitterName;
EmitterPtr->ValidUsages = BasePtr.ValidUsages;
// Don't need to copy the graph if we aren't going to use it.
if (EmitterInfo.Enabled && ((SourceEmitterIndex == INDEX_NONE) || (SourceEmitterIndex == EmitterInfo.SourceEmitterIndex)))
{
const FNiagaraPrecompileData* EmitterRequestData = static_cast<const FNiagaraPrecompileData*>(ParentTask.SystemPrecompileData->GetDependentRequest(EmitterInfo.DigestedEmitterIndex).Get());
if (const FNiagaraCompilationGraphDigested* SourceGraph = EmitterInfo.SourceGraph.Get())
{
EmitterPtr->InstantiateCompilationCopy(*SourceGraph, EmitterRequestData, ENiagaraScriptUsage::EmitterSpawnScript, EmitterInfo.ConstantResolver);
}
}
EmitterPtr->ValidUsages = BasePtr.ValidUsages;
BasePtr.EmitterData.Add(EmitterPtr);
}
// Now deep copy the system graphs, skipping traversal into any emitter references.
{
TArray<FNiagaraVariable> EncounterableSystemVariables;
ParentTask.SystemPrecompileData->GatherPreCompiledVariables(FString(), EncounterableSystemVariables);
// skip the deep copy if we're not compiling the system scripts
if (BasePtr.ValidUsages.Contains(ENiagaraScriptUsage::SystemSpawnScript))
{
if (const FNiagaraCompilationGraphDigested* SourceGraph = ParentTask.SystemInfo.SystemSourceGraph.Get())
{
BasePtr.InstantiateCompilationCopy(*SourceGraph, ParentTask.SystemPrecompileData.Get(), ENiagaraScriptUsage::SystemSpawnScript, ParentTask.SystemInfo.ConstantResolver);
}
BasePtr.CreateParameterMapHistory(ParentTask, EncounterableSystemVariables, ParentTask.SystemInfo.StaticVariableResults, ParentTask.SystemInfo.ConstantResolver, {});
// bubble up the referenced DI classes to the system
for (const FNiagaraCompilationCopyData::FSharedCompilationCopy& EmitterData : BasePtr.EmitterData)
{
BasePtr.AggregatedDataInterfaceCDODuplicates.Append(EmitterData->AggregatedDataInterfaceCDODuplicates);
}
}
}
// Now we can finish off the emitters.
for (const FEmitterInfo& EmitterInfo : ParentTask.SystemInfo.EmitterInfo)
{
TArray<FNiagaraVariable> EncounterableEmitterVariables;
ParentTask.SystemPrecompileData->GetDependentRequest(EmitterInfo.DigestedEmitterIndex)->GatherPreCompiledVariables(FString(), EncounterableEmitterVariables);
if (EmitterInfo.Enabled && ((SourceEmitterIndex == INDEX_NONE) || (SourceEmitterIndex == EmitterInfo.SourceEmitterIndex)))
{
TArray<FNiagaraVariable> StaticVariablesFromEmitter = ParentTask.SystemInfo.StaticVariableResults;
StaticVariablesFromEmitter.Append(EmitterInfo.StaticVariableResults);
BasePtr.EmitterData[EmitterInfo.DigestedEmitterIndex]->CreateParameterMapHistory(ParentTask, EncounterableEmitterVariables, StaticVariablesFromEmitter, EmitterInfo.ConstantResolver, EmitterInfo.SimStages);
}
}
}
bool FNiagaraSystemCompilationTask::FCompileComputeShaderTaskInfo::IsOutstanding() const
{
return !ShaderMap.IsValid();
}
bool FNiagaraSystemCompilationTask::FCompileTaskInfo::IsOutstanding(const FNiagaraSystemCompilationTask& ParentTask) const
{
if (!ExeData.IsValid())
{
return true;
}
for (int32 TaskIndex : ComputeShaderTaskIndices)
{
if (ParentTask.CompileComputeShaderTasks[TaskIndex].IsOutstanding())
{
return true;
}
}
return false;
}
FNiagaraSystemCompilationTask::FCompileGroupInfo* FNiagaraSystemCompilationTask::GetCompileGroupInfo(int32 EmitterIndex)
{
return CompileGroups.FindByPredicate([EmitterIndex](const FCompileGroupInfo& GroupInfo) -> bool
{
return GroupInfo.SourceEmitterIndex == EmitterIndex;
});
}
const FNiagaraSystemCompilationTask::FScriptInfo* FNiagaraSystemCompilationTask::GetScriptInfo(const TObjectKey<UNiagaraScript>& ScriptKey) const
{
return DigestedScriptInfo.Find(ScriptKey);
}
void FNiagaraSystemCompilationTask::FCompileTaskInfo::CollectNamedDataInterfaces(FNiagaraSystemCompilationTask* SystemCompileTask, const FCompileGroupInfo& GroupInfo)
{
FString UniqueEmitterName;
const FNiagaraCompilationCopyData* CompilationCopyData = nullptr;
if (const FEmitterInfo* EmitterInfo = SystemCompileTask->SystemInfo.EmitterInfoBySourceEmitter(GroupInfo.SourceEmitterIndex))
{
CompilationCopyData = static_cast<const FNiagaraCompilationCopyData*>(GroupInfo.CompilationCopy->GetDependentRequest(EmitterInfo->DigestedEmitterIndex).Get());
UniqueEmitterName = EmitterInfo->UniqueEmitterName;
}
else
{
CompilationCopyData = GroupInfo.CompilationCopy.Get();
}
const FScriptInfo& ScriptInfo = SystemCompileTask->DigestedScriptInfo.FindChecked(ScriptKey);
auto AccumulateInputDataInterfaces = [this, &UniqueEmitterName](const FNiagaraCompilationNode& Node) -> bool
{
if (const FNiagaraCompilationNodeInput* InputNode = Node.AsType<FNiagaraCompilationNodeInput>())
{
if (InputNode->DataInterfaceName != NAME_None && InputNode->DuplicatedDataInterface)
{
constexpr bool bIsParameterMapDataInterface = false;
FName DIName = FNiagaraHlslTranslator::GetDataInterfaceName(InputNode->DataInterfaceName, UniqueEmitterName, bIsParameterMapDataInterface);
NamedDataInterfaces.Add(DIName, InputNode->DuplicatedDataInterface);
}
}
return true;
};
auto DefaultNodeFinder = [&ScriptInfo](const FNiagaraCompilationNodeOutput& OutputNode) -> bool
{
if (UNiagaraScript::IsEquivalentUsage(OutputNode.Usage, ScriptInfo.Usage) && OutputNode.UsageId == ScriptInfo.UsageId)
{
return true;
}
if (ScriptInfo.Usage == ENiagaraScriptUsage::ParticleGPUComputeScript)
{
return UNiagaraScript::IsParticleScript(OutputNode.Usage);
}
else if (ScriptInfo.Usage == ENiagaraScriptUsage::ParticleSpawnScriptInterpolated)
{
return UNiagaraScript::IsEquivalentUsage(OutputNode.Usage, ENiagaraScriptUsage::ParticleUpdateScript);
}
return OutputNode.Usage == ScriptInfo.Usage && OutputNode.UsageId == ScriptInfo.UsageId;
};
CompilationCopyData->InstantiatedGraph->NodeTraversal(true, false, DefaultNodeFinder, AccumulateInputDataInterfaces);
if (UNiagaraScript::IsSystemScript(ScriptInfo.Usage))
{
check(GroupInfo.SourceEmitterIndex == INDEX_NONE);
const ENiagaraScriptUsage EmitterUsage = ScriptInfo.Usage == ENiagaraScriptUsage::SystemSpawnScript
? ENiagaraScriptUsage::EmitterSpawnScript
: ENiagaraScriptUsage::EmitterUpdateScript;
const FGuid EmitterUsageId = ScriptInfo.UsageId;
auto EmitterNodeFinder = [EmitterUsage, EmitterUsageId](const FNiagaraCompilationNodeOutput& OutputNode) -> bool
{
return UNiagaraScript::IsEquivalentUsage(OutputNode.Usage, EmitterUsage) && OutputNode.UsageId == EmitterUsageId;
};
for (const FNiagaraCompilationCopyData::FSharedCompilationCopy& EmitterCompilationCopy : CompilationCopyData->EmitterData)
{
if (const FNiagaraCompilationGraph* EmitterGraph = EmitterCompilationCopy->InstantiatedGraph.Get())
{
UniqueEmitterName = EmitterCompilationCopy->EmitterUniqueName;
EmitterGraph->NodeTraversal(true, false, EmitterNodeFinder, AccumulateInputDataInterfaces);
}
}
}
}
void FNiagaraSystemCompilationTask::FCompileTaskInfo::Translate(FNiagaraSystemCompilationTask* SystemCompileTask, const FCompileGroupInfo& GroupInfo)
{
TRACE_CPUPROFILER_EVENT_SCOPE(NiagaraAsyncTask_Translate);
COOK_STAT(NiagaraSystemCookStats::ScriptTranslationCount++);
const FNiagaraPrecompileData* PrecompileData = nullptr;
const FNiagaraCompilationCopyData* CompilationCopyData = nullptr;
if (const FEmitterInfo* EmitterInfo = SystemCompileTask->SystemInfo.EmitterInfoBySourceEmitter(GroupInfo.SourceEmitterIndex))
{
PrecompileData = static_cast<const FNiagaraPrecompileData*>(SystemCompileTask->SystemPrecompileData->GetDependentRequest(EmitterInfo->DigestedEmitterIndex).Get());
CompilationCopyData = static_cast<const FNiagaraCompilationCopyData*>(GroupInfo.CompilationCopy->GetDependentRequest(EmitterInfo->DigestedEmitterIndex).Get());
}
else
{
PrecompileData = SystemCompileTask->SystemPrecompileData.Get();
CompilationCopyData = GroupInfo.CompilationCopy.Get();
}
const FScriptInfo& ScriptInfo = SystemCompileTask->DigestedScriptInfo.FindChecked(ScriptKey);
// TRANSLATION
{
double TranslationStartTime = FPlatformTime::Seconds();
BakedRapidIterationParameters = PrecompileData->BakedRapidIterationParameters;
FHlslNiagaraTranslatorOptions TranslateOptions;
if (CompileOptions.TargetUsage == ENiagaraScriptUsage::ParticleGPUComputeScript)
{
TranslateOptions.SimTarget = ENiagaraSimTarget::GPUComputeSim;
}
else
{
TranslateOptions.SimTarget = ENiagaraSimTarget::CPUSim;
}
TranslateOptions.OverrideModuleConstants = BakedRapidIterationParameters;
TranslateOptions.bParameterRapidIteration = PrecompileData->GetUseRapidIterationParams();
TranslateOptions.bDisableDebugSwitches = PrecompileData->GetDisableDebugSwitches();
FString PathName = SourceScript->GetPathName();
TUniquePtr<INiagaraHlslTranslator> Translator = INiagaraHlslTranslator::CreateTranslator(PrecompileData, CompilationCopyData);
TranslateResults = Translator->Translate(CompileOptions, TranslateOptions);
TranslateOutput = Translator->GetTranslateOutput();
TranslatedHlsl = Translator->GetTranslatedHLSL();
TranslationTime = (float) (FPlatformTime::Seconds() - TranslationStartTime);
}
}
void FNiagaraSystemCompilationTask::FCompileTaskInfo::IssueCompileVm(FNiagaraSystemCompilationTask* SystemCompileTask, const FCompileGroupInfo& GroupInfo)
{
COOK_STAT(NiagaraSystemCookStats::CpuScriptCompileCount++);
const FNiagaraPrecompileData* PrecompileData = nullptr;
if (const FEmitterInfo* EmitterInfo = SystemCompileTask->SystemInfo.EmitterInfoBySourceEmitter(GroupInfo.SourceEmitterIndex))
{
PrecompileData = static_cast<const FNiagaraPrecompileData*>(SystemCompileTask->SystemPrecompileData->GetDependentRequest(EmitterInfo->DigestedEmitterIndex).Get());
}
else
{
PrecompileData = SystemCompileTask->SystemPrecompileData.Get();
}
// COMPILATION
TStringBuilder<512> DebugGroupName;
NiagaraCompilationTasksImpl::BuildDebugGroupName(*PrecompileData, CompileOptions, DebugGroupName);
ScriptCompiler = MakeUnique<FHlslNiagaraCompiler>();
ScriptCompilationJobId = ScriptCompiler->CompileScriptVM(DebugGroupName, CompileOptions, TranslateResults, TranslateOutput, TranslatedHlsl, SystemCompileTask->NiagaraShaderType);
}
void FNiagaraSystemCompilationTask::FCompileTaskInfo::IssueTranslateGpu(FNiagaraSystemCompilationTask* SystemCompileTask, const FCompileGroupInfo& GroupInfo)
{
const FNiagaraPrecompileData* PrecompileData = nullptr;
if (const FEmitterInfo* EmitterInfo = SystemCompileTask->SystemInfo.EmitterInfoBySourceEmitter(GroupInfo.SourceEmitterIndex))
{
PrecompileData = static_cast<const FNiagaraPrecompileData*>(SystemCompileTask->SystemPrecompileData->GetDependentRequest(EmitterInfo->DigestedEmitterIndex).Get());
}
else
{
PrecompileData = SystemCompileTask->SystemPrecompileData.Get();
}
// COMPILATION
TStringBuilder<512> DebugGroupName;
NiagaraCompilationTasksImpl::BuildDebugGroupName(*PrecompileData, CompileOptions, DebugGroupName);
ScriptCompiler = MakeUnique<FHlslNiagaraCompiler>();
ScriptCompilationJobId = ScriptCompiler->CreateShaderIntermediateData(DebugGroupName, CompileOptions, TranslateResults, TranslateOutput, TranslatedHlsl);
}
void FNiagaraSystemCompilationTask::FCompileTaskInfo::IssueCompileGpu(FNiagaraSystemCompilationTask* SystemCompileTask, const FCompileGroupInfo& GroupInfo)
{
COOK_STAT(NiagaraSystemCookStats::GpuScriptCompileCount++);
// if we don't have a valid shader file, then there's no point in issuing a compilation
if (!TranslateResults.bHLSLGenSucceeded)
{
return;
}
const FNiagaraPrecompileData* PrecompileData = nullptr;
const FNiagaraCompilationCopyData* CompilationCopyData = nullptr;
if (const FEmitterInfo* EmitterInfo = SystemCompileTask->SystemInfo.EmitterInfoBySourceEmitter(GroupInfo.SourceEmitterIndex))
{
PrecompileData = static_cast<const FNiagaraPrecompileData*>(SystemCompileTask->SystemPrecompileData->GetDependentRequest(EmitterInfo->DigestedEmitterIndex).Get());
CompilationCopyData = static_cast<const FNiagaraCompilationCopyData*>(GroupInfo.CompilationCopy->GetDependentRequest(EmitterInfo->DigestedEmitterIndex).Get());
}
else
{
PrecompileData = SystemCompileTask->SystemPrecompileData.Get();
CompilationCopyData = GroupInfo.CompilationCopy.Get();
}
check(!ComputeShaderTaskIndices.IsEmpty());
TStringBuilder<512> DebugGroupName;
NiagaraCompilationTasksImpl::BuildDebugGroupName(*PrecompileData, CompileOptions, DebugGroupName);
TSharedPtr<FNiagaraShaderScriptParametersMetadata> ShaderParameters =
NiagaraCompilationTasksImpl::BuildScriptParametersMetadata(CompilationCopyData, TranslateOutput.ScriptData.ShaderScriptParametersMetadata);
ShaderMapCompiler = MakeUnique<FNiagaraShaderMapCompiler>(SystemCompileTask->NiagaraShaderType, ShaderParameters);
for (int32 ShaderTaskIndex : ComputeShaderTaskIndices)
{
const FCompileComputeShaderTaskInfo& ComputeInfo = SystemCompileTask->CompileComputeShaderTasks[ShaderTaskIndex];
ShaderMapCompiler->AddShaderPlatform(ComputeInfo.ShaderMapId, ComputeInfo.ShaderPlatform);
}
TArray<UNiagaraDataInterface*> UniqueDI;
for (const auto& It : CompilationCopyData->AggregatedDataInterfaceCDODuplicates)
{
UniqueDI.AddUnique(It.Value);
}
ShaderMapCompiler->CompileScript(CompileId, PrecompileData->SourceName, DebugGroupName, CompileOptions, TranslateResults, TranslateOutput, TranslatedHlsl, UniqueDI);
}
TOptional<FNiagaraCompileResults> FNiagaraSystemCompilationTask::FCompileTaskInfo::HandleDeprecatedGpuScriptResults() const
{
return TOptional<FNiagaraCompileResults>();
}
/** Returns true if the task has valid results */
bool FNiagaraSystemCompilationTask::FCompileTaskInfo::RetrieveCompilationResult(bool bWait)
{
TRACE_CPUPROFILER_EVENT_SCOPE(NiagaraAsyncTask_RetrieveResult);
check(CompileResultsReadyEvent.IsValid() && CompileResultsReadyEvent->IsValid());
if (CompileResultsReadyEvent->IsCompleted())
{
return true;
}
check(!ExeData.IsValid());
check(ScriptCompiler.IsValid());
// in cases where asynchronous shader compiling isn't allowed we'll simply block execution here till the task
// is complete
if (!GShaderCompilingManager->AllowAsynchronousShaderCompiling())
{
bWait = true;
}
TOptional<FNiagaraCompileResults> CompileResult = ScriptCompiler->GetCompileResult(ScriptCompilationJobId, bWait);
if (!CompileResult)
{
return false;
}
FNiagaraCompileResults& Results = CompileResult.GetValue();
ExeData = Results.Data;
RetrieveTranslateResult();
ExeData->LastCompileStatus = (FNiagaraCompileResults::CompileResultsToSummary(&Results));
if (ExeData->LastCompileStatus != ENiagaraScriptCompileStatus::NCS_Error)
{
// When there are no errors the compile events get emptied, so add them back here.
ExeData->LastCompileEvents.Append(Results.CompileEvents);
}
CompilerWallTime = Results.CompilerWallTime;
CompilerPreprocessTime = Results.CompilerPreprocessTime;
CompilerWorkerTime = Results.CompilerWorkerTime;
return true;
}
void FNiagaraSystemCompilationTask::FCompileTaskInfo::RetrieveTranslateResult()
{
if (!ExeData.IsValid())
{
ExeData = MakeShared<FNiagaraVMExecutableData>(TranslateOutput.ScriptData);
}
for (const FNiagaraCompileEvent& Message : TranslateResults.CompileEvents)
{
#if defined(NIAGARA_SCRIPT_COMPILE_LOGGING_MEDIUM)
UE_LOG(LogNiagaraEditor, Log, TEXT("%s"), *Message.Message);
#endif
if (Message.Severity == FNiagaraCompileEventSeverity::Error)
{
// Write the error messages to the string as well so that they can be echoed up the chain.
if (ExeData->ErrorMsg.Len() > 0)
{
ExeData->ErrorMsg += "\n";
}
ExeData->ErrorMsg += Message.Message;
}
}
for (const FNiagaraCompileEvent& Event : TranslateResults.CompileEvents)
{
ExeData->LastCompileEvents.AddUnique(Event);
}
ExeData->ExternalDependencies = TranslateResults.CompileDependencies;
ExeData->CompileTags = TranslateResults.CompileTags;
ExeData->CompileTagsEditorOnly = TranslateResults.CompileTagsEditorOnly;
// we also need to include the information about the rapid iteration parameters that were used
// to generate the ExeData
ExeData->BakedRapidIterationParameters = BakedRapidIterationParameters;
const bool bHasTranslationErrors = TranslateResults.NumErrors > 0;
const bool bHasTranslationWarnings = TranslateResults.NumWarnings > 0;
ExeData->LastCompileStatus = bHasTranslationErrors
? ENiagaraScriptCompileStatus::NCS_Error
: bHasTranslationWarnings
? ENiagaraScriptCompileStatus::NCS_UpToDateWithWarnings
: ENiagaraScriptCompileStatus::NCS_UpToDate;
}
bool FNiagaraSystemCompilationTask::FCompileTaskInfo::RetrieveShaderMap(bool bWait)
{
check(CompileResultsReadyEvent.IsValid() && CompileResultsReadyEvent->IsValid());
if (CompileResultsReadyEvent->IsCompleted())
{
return true;
}
// if we don't have a shader map compiler, then just return the translation results
if (!ShaderMapCompiler.IsValid())
{
RetrieveTranslateResult();
return true;
}
ExeData = ShaderMapCompiler->ReadScriptMetaData();
if (ShaderMapCompiler->ProcessCompileResults(bWait))
{
ExeData->LastCompileStatus = ENiagaraScriptCompileStatus::NCS_UpToDate;
return true;
}
return false;
}
void FNiagaraSystemCompilationTask::FCompileTaskInfo::GenerateOptimizedVMByteCode(FNiagaraSystemCompilationTask* SystemCompileTask, const FCompileGroupInfo& GroupInfo)
{
TRACE_CPUPROFILER_EVENT_SCOPE(NiagaraAsyncTask_OptimizeVM);
if (const uint8* ByteCode = ExeData->ByteCode.GetDataPtr())
{
const double OptimizeStartTime = FPlatformTime::Seconds();
//this is just necessary because VectorVM doesn't know about FVMExternalFunctionBindingInfo
const int32 NumExtFns = ExeData->CalledVMExternalFunctions.Num();
TArray<FVectorVMExtFunctionData> ExtFnTable;
ExtFnTable.SetNumZeroed(NumExtFns);
for (int i = 0; i < NumExtFns; ++i)
{
ExtFnTable[i].NumInputs = ExeData->CalledVMExternalFunctions[i].GetNumInputs();
ExtFnTable[i].NumOutputs = ExeData->CalledVMExternalFunctions[i].GetNumOutputs();
}
{
const uint64 AssetPathHash = CityHash64((char*)AssetPath.GetCharArray().GetData(), AssetPath.GetCharArray().Num());
VectorVM::Optimizer::FVectorVMOptimizerContext* OptimizerContext = VectorVM::Optimizer::AllocOptimizerContext();
VectorVM::Optimizer::OptimizeVectorVMScript(ByteCode, ExeData->ByteCode.GetLength(), ExtFnTable.GetData(), ExtFnTable.Num(), OptimizerContext, AssetPathHash, VectorVM::VVMFlag_OptOmitStats | VectorVM::VVMFlag_OptSaveIntermediateState);
// extract a human readable version of the script
VectorVM::Optimizer::GenerateHumanReadableScript(*OptimizerContext, ExeData->LastExperimentalAssemblyScript);
// freeze the OptimizeContext
VectorVM::Bridge::FreezeOptimizerContext(*OptimizerContext, ExeData->ExperimentalContextData);
VectorVM::Optimizer::FreeOptimizerContext(OptimizerContext);
}
ByteCodeOptimizeTime = (float)(OptimizeStartTime - FPlatformTime::Seconds());
}
}
void FNiagaraSystemCompilationTask::FCompileTaskInfo::ProcessCompilation(FNiagaraSystemCompilationTask* SystemCompileTask, const FCompileGroupInfo& GroupInfo)
{
ON_SCOPE_EXIT
{
CompileResultsProcessedEvent->Trigger();
};
if (ScriptCompileType == EScriptCompileType::CompileForVm)
{
GenerateOptimizedVMByteCode(SystemCompileTask, GroupInfo);
}
// we need to update our ComputeShaderTasks with the generated shader map so that we can put the data to the DDC
if (ShaderMapCompiler.IsValid())
{
check(ScriptCompileType == EScriptCompileType::CompileForGpu);
for (int32 ComputeShaderTaskIndex : ComputeShaderTaskIndices)
{
FCompileComputeShaderTaskInfo& ShaderTaskInfo = SystemCompileTask->CompileComputeShaderTasks[ComputeShaderTaskIndex];
ShaderMapCompiler->GetShaderMap(ShaderTaskInfo.ShaderMapId, ShaderTaskInfo.ShaderMap, ShaderTaskInfo.CompilationErrors);
}
}
}
bool FNiagaraSystemCompilationTask::FDDCTaskInfo::ResolveGet(bool bSuccess)
{
if (DataCacheGetKey != UE::DerivedData::FCacheKey::Empty)
{
if (bSuccess)
{
bFromDerivedDataCache = true;
}
else
{
DataCachePutKeys.AddUnique(DataCacheGetKey);
}
}
return bSuccess;
}
FNiagaraSystemCompilationTask::FDataCacheRequestBase::FDataCacheRequestBase()
: RequestOwner(UE::DerivedData::EPriority::Normal)
{
RequestOwner.KeepAlive();
}
void FNiagaraSystemCompilationTask::FDispatchAndProcessDataCacheGetRequests::Launch(FNiagaraSystemCompilationTask* SystemCompileTask)
{
// todo - incorporate something here where we skip if we try to update the compileid (post ri collection)
// and there's no actual change to the compileid...in that case we shouldn't worry about trying to grab
// stuff from the ddc again
union FDDCUserData
{
uint64 UserData;
struct
{
int32 CompileTaskIndex;
bool bShaderCompileTask;
};
};
using namespace UE::DerivedData;
TArray<FCacheGetRequest> GetRequests;
const int32 CompileTaskCount = SystemCompileTask->CompileTasks.Num();
const int32 ShaderCompileTaskCount = SystemCompileTask->CompileComputeShaderTasks.Num();
GetRequests.Reserve(CompileTaskCount + ShaderCompileTaskCount);
for (int32 CompileTaskIt = 0; CompileTaskIt < CompileTaskCount; ++CompileTaskIt)
{
FNiagaraSystemCompilationTask::FCompileTaskInfo& CompileTask = SystemCompileTask->CompileTasks[CompileTaskIt];
if (!CompileTask.ExeData.IsValid())
{
const UE::DerivedData::FCacheKey CacheKey = NiagaraCompilationTasksImpl::BuildNiagaraDDCCacheKey(CompileTask.CompileId, CompileTask.AssetPath);
if (CacheKey != CompileTask.DDCTaskInfo.DataCacheGetKey)
{
FDDCUserData Index;
Index.CompileTaskIndex = CompileTaskIt;
Index.bShaderCompileTask = false;
FCacheGetRequest& GetValueRequest = GetRequests.AddDefaulted_GetRef();
GetValueRequest.Name = CompileTask.AssetPath;
GetValueRequest.Key = CacheKey;
GetValueRequest.UserData = Index.UserData;
CompileTask.DDCTaskInfo.DataCacheGetKey = CacheKey;
}
}
}
for (int32 ShaderCompileTaskIt = 0; ShaderCompileTaskIt < ShaderCompileTaskCount; ++ShaderCompileTaskIt)
{
FNiagaraSystemCompilationTask::FCompileComputeShaderTaskInfo& ShaderCompileTask = SystemCompileTask->CompileComputeShaderTasks[ShaderCompileTaskIt];
if (ShaderCompileTask.IsOutstanding())
{
FNiagaraSystemCompilationTask::FCompileTaskInfo& CompileTask = SystemCompileTask->CompileTasks[ShaderCompileTask.ParentCompileTaskIndex];
const UE::DerivedData::FCacheKey CacheKey = NiagaraCompilationTasksImpl::BuildNiagaraComputeDDCCacheKey(ShaderCompileTask.ShaderMapId, ShaderCompileTask.ShaderPlatform, CompileTask.AssetPath);
if (CacheKey != ShaderCompileTask.DDCTaskInfo.DataCacheGetKey)
{
FDDCUserData Index;
Index.CompileTaskIndex = ShaderCompileTaskIt;
Index.bShaderCompileTask = true;
FCacheGetRequest& ShaderGetValueRequest = GetRequests.AddDefaulted_GetRef();
ShaderGetValueRequest.Name = CompileTask.AssetPath;
ShaderGetValueRequest.Key = CacheKey;
ShaderGetValueRequest.UserData = Index.UserData;
ShaderCompileTask.DDCTaskInfo.DataCacheGetKey = CacheKey;
}
}
}
PendingGetRequestCount = GetRequests.Num();
if (PendingGetRequestCount > 0)
{
FRequestBarrier RequestBarrier(RequestOwner);
GetCache().Get(GetRequests, RequestOwner, [this, SystemCompileTask = SystemCompileTask->AsShared()](FCacheGetResponse&& Response)
{
FDDCUserData Index;
Index.UserData = Response.UserData;
FDDCTaskInfo* DDCTaskInfo = nullptr;
if (Index.bShaderCompileTask)
{
if (ensure(SystemCompileTask->CompileComputeShaderTasks.IsValidIndex(Index.CompileTaskIndex)))
{
DDCTaskInfo = &SystemCompileTask->CompileComputeShaderTasks[Index.CompileTaskIndex].DDCTaskInfo;
}
}
else
{
if (ensure(SystemCompileTask->CompileTasks.IsValidIndex(Index.CompileTaskIndex)))
{
DDCTaskInfo = &SystemCompileTask->CompileTasks[Index.CompileTaskIndex].DDCTaskInfo;
}
}
if (DDCTaskInfo && Response.Status == EStatus::Ok)
{
const FValueWithId& ValueWithId = Response.Record.GetValue(NiagaraCompilationTasksImpl::ScriptDataValue);
DDCTaskInfo->PendingDDCData = ValueWithId.GetData().Decompress().MoveToUnique();
if (NiagaraCompilationTasksImpl::GDebugParseCompileMetaDataOnGet)
{
if (!Index.bShaderCompileTask)
{
NiagaraCompilationTasksImpl::ParseScriptCompileMetaData(Response.Record.GetMeta());
}
}
}
if (PendingGetRequestCount.fetch_sub(1, std::memory_order_relaxed) == 1)
{
// in order to make sure that processing the binary data from the DDC can properly serialize
// objects (based on their path name) we must run BinaryToExecData() on the game thread (to
// avoid conflicts with GC or async loading).
FNiagaraSystemCompilingManager::Get().QueueGameThreadFunction([CompletionEvent = this->CompletionEvent, SystemCompileTask]() mutable
{
check(IsInGameThread());
for (FNiagaraSystemCompilationTask::FCompileTaskInfo& CompileTask : SystemCompileTask->CompileTasks)
{
if (!CompileTask.DDCTaskInfo.PendingDDCData.IsNull())
{
FNiagaraVMExecutableData ExeData;
if (NiagaraCompilationTasksImpl::BinaryToExecData(CompileTask.SourceScript.Get(), CompileTask.DDCTaskInfo.PendingDDCData.MoveToShared(), ExeData))
{
CompileTask.ExeData = MakeShared<FNiagaraVMExecutableData>(MoveTemp(ExeData));
}
}
if (CompileTask.DDCTaskInfo.ResolveGet(CompileTask.ExeData.IsValid()))
{
COOK_STAT(NiagaraSystemCookStats::CpuScriptDdcCacheHitCount++);
}
else
{
COOK_STAT(NiagaraSystemCookStats::CpuScriptDdcCacheMissCount++);
}
}
for (FNiagaraSystemCompilationTask::FCompileComputeShaderTaskInfo& CompileTask : SystemCompileTask->CompileComputeShaderTasks)
{
if (!CompileTask.DDCTaskInfo.PendingDDCData.IsNull())
{
CompileTask.ShaderMap = NiagaraCompilationTasksImpl::BinaryDataToShaderMap(CompileTask.DDCTaskInfo.PendingDDCData.MoveToShared());
}
if (CompileTask.DDCTaskInfo.ResolveGet(CompileTask.ShaderMap.IsValid()))
{
COOK_STAT(NiagaraSystemCookStats::GpuScriptDdcCacheHitCount++);
}
else
{
COOK_STAT(NiagaraSystemCookStats::GpuScriptDdcCacheMissCount++);
}
}
CompletionEvent.Trigger();
});
}
});
}
else
{
CompletionEvent.Trigger();
}
}
void FNiagaraSystemCompilationTask::FDispatchDataCachePutRequests::Launch(FNiagaraSystemCompilationTask* SystemCompileTask)
{
using namespace UE::DerivedData;
const bool bIncludeMetaData = SystemCompileTask->GenerateCompileMetaData();
TArray<FCachePutRequest> PutRequests;
const int32 CompileTaskCount = SystemCompileTask->CompileTasks.Num();
const int32 ShaderCompileTaskCount = SystemCompileTask->CompileComputeShaderTasks.Num();
PutRequests.Reserve(CompileTaskCount + ShaderCompileTaskCount);
for (const FNiagaraSystemCompilationTask::FCompileTaskInfo& CompileTask : SystemCompileTask->CompileTasks)
{
if (CompileTask.ExeData.IsValid() && !CompileTask.DDCTaskInfo.DataCachePutKeys.IsEmpty())
{
if (FSharedBuffer SharedBuffer = NiagaraCompilationTasksImpl::ExecToBinaryData(CompileTask.SourceScript.Get(), *CompileTask.ExeData))
{
FValue PutValue = FValue::Compress(SharedBuffer);
for (const FCacheKey& CachePutKey : CompileTask.DDCTaskInfo.DataCachePutKeys)
{
FCacheRecordBuilder RecordBuilder(CachePutKey);
RecordBuilder.AddValue(NiagaraCompilationTasksImpl::ScriptDataValue, PutValue);
if (bIncludeMetaData)
{
RecordBuilder.SetMeta(NiagaraCompilationTasksImpl::GenerateScriptCompileMetaData(SystemCompileTask, CompileTask));
}
FCachePutRequest& PutRequest = PutRequests.AddZeroed_GetRef();
PutRequest.Name = CompileTask.AssetPath;
PutRequest.Record = RecordBuilder.Build();
PutRequest.Policy = ECachePolicy::Default;
}
}
}
}
for (const FNiagaraSystemCompilationTask::FCompileComputeShaderTaskInfo& CompileTask : SystemCompileTask->CompileComputeShaderTasks)
{
if (CompileTask.ShaderMap.IsValid() && CompileTask.ShaderMap->IsValid() && !CompileTask.DDCTaskInfo.DataCachePutKeys.IsEmpty())
{
const FNiagaraSystemCompilationTask::FCompileTaskInfo& ParentCompileTask = SystemCompileTask->CompileTasks[CompileTask.ParentCompileTaskIndex];
if (FSharedBuffer SharedBuffer = NiagaraCompilationTasksImpl::ShaderMapToBinaryData(CompileTask.ShaderMap.GetReference()))
{
FValue PutValue = FValue::Compress(SharedBuffer);
for (const FCacheKey& CachePutKey : CompileTask.DDCTaskInfo.DataCachePutKeys)
{
FCacheRecordBuilder RecordBuilder(CachePutKey);
RecordBuilder.AddValue(NiagaraCompilationTasksImpl::ScriptDataValue, PutValue);
FCachePutRequest& PutRequest = PutRequests.AddZeroed_GetRef();
PutRequest.Name = ParentCompileTask.AssetPath;
PutRequest.Record = RecordBuilder.Build();
PutRequest.Policy = ECachePolicy::Default;
}
}
}
}
PendingPutRequestCount = PutRequests.Num();
if (!PutRequests.IsEmpty())
{
FRequestBarrier RequestBarrier(RequestOwner);
GetCache().Put(PutRequests, RequestOwner, [this, SystemCompileTask = SystemCompileTask->AsShared()](FCachePutResponse&& Response)
{
if (PendingPutRequestCount.fetch_sub(1, std::memory_order_relaxed) == 1)
{
CompletionEvent.Trigger();
}
});
}
else
{
CompletionEvent.Trigger();
}
}
void FNiagaraSystemCompilationTask::DigestSystemInfo()
{
TRACE_CPUPROFILER_EVENT_SCOPE(NiagaraAsyncTask_DigestSystem);
using namespace NiagaraCompilationTasksImpl;
if (RIParamMode == ENiagaraCompileRIParamMode::GameThread)
{
TRACE_CPUPROFILER_EVENT_SCOPE(NiagaraAsyncTask_PrepareRIParam);
System_GT->PrepareRapidIterationParametersForCompilation();
}
FNiagaraDigestDatabase& DigestDatabase = FNiagaraDigestDatabase::Get();
const UNiagaraScript* SystemSpawnScript = System_GT->GetSystemSpawnScript();
const UNiagaraScript* SystemUpdateScript = System_GT->GetSystemUpdateScript();
const UNiagaraGraph* SystemGraph = GetGraphFromScript(SystemSpawnScript);
check(SystemGraph == GetGraphFromScript(SystemUpdateScript));
FNiagaraGraphChangeIdBuilder ChangeIdBuilder;
ChangeIdBuilder.ParseReferencedGraphs(SystemGraph);
SystemInfo.SystemName = System_GT->GetName();
SystemInfo.SystemPackageName = System_GT->GetOutermost()->GetFName();
SystemInfo.bUseRapidIterationParams = System_GT->ShouldUseRapidIterationParameters();
SystemInfo.bDisableDebugSwitches = System_GT->ShouldDisableDebugSwitches();
SystemInfo.ConstantResolver = FNiagaraFixedConstantResolver(FCompileConstantResolver(System_GT.Get(), ENiagaraScriptUsage::SystemSpawnScript));
SystemInfo.OwnedScriptKeys = { SystemSpawnScript, SystemUpdateScript };
SystemInfo.SystemSourceGraph = DigestDatabase.CreateGraphDigest(SystemGraph, ChangeIdBuilder);
if (RIParamMode == ENiagaraCompileRIParamMode::GameThread)
{
TSharedPtr<FNiagaraGraphCachedDataBase, ESPMode::ThreadSafe> CachedTraversalSystemData = System_GT->GetCachedTraversalData();
SystemInfo.StaticVariableResults = static_cast<FNiagaraGraphCachedBuiltHistory*>(CachedTraversalSystemData.Get())->StaticVariables;
}
else
{
TArray<FNiagaraVariable> StaticVariablesFromEmitters;
System_GT->GatherStaticVariables(SystemInfo.InitialStaticVariables, StaticVariablesFromEmitters);
for (const FNiagaraVariable& EmitterVariable : StaticVariablesFromEmitters)
{
SystemInfo.InitialStaticVariables.AddUnique(EmitterVariable);
}
}
const TArray<FNiagaraEmitterHandle>& EmitterHandles = System_GT->GetEmitterHandles();
const int32 SourceEmitterCount = EmitterHandles.Num();
SystemInfo.EmitterInfo.Reserve(SourceEmitterCount);
for (int32 SourceEmitterIndex = 0; SourceEmitterIndex < SourceEmitterCount; ++SourceEmitterIndex)
{
const FNiagaraEmitterHandle& Handle = EmitterHandles[SourceEmitterIndex];
if (!Handle.GetIsEnabled())
{
continue;
}
const int32 DigestedEmitterIndex = SystemInfo.EmitterInfo.Num();
const FVersionedNiagaraEmitter& HandleInstance = Handle.GetInstance();
if (FVersionedNiagaraEmitterData* EmitterData = Handle.GetEmitterData())
{
FEmitterInfo& EmitterInfo = SystemInfo.EmitterInfo.AddDefaulted_GetRef();
const UNiagaraGraph* EmitterGraph = GetGraphFromScriptSource(EmitterData->GraphSource);
ChangeIdBuilder.ParseReferencedGraphs(EmitterGraph);
if (const UNiagaraEmitter* Emitter = HandleInstance.Emitter)
{
EmitterInfo.UniqueEmitterName = HandleInstance.Emitter->GetUniqueEmitterName();
EmitterInfo.EmitterHandleId = Handle.GetId();
EmitterInfo.UniqueInstanceName = Handle.GetUniqueInstanceName();
EmitterInfo.Enabled = Handle.GetIsEnabled();
EmitterInfo.ConstantResolver = FNiagaraFixedConstantResolver(FCompileConstantResolver(HandleInstance, ENiagaraScriptUsage::EmitterSpawnScript));
EmitterInfo.SourceEmitterIndex = SourceEmitterIndex;
EmitterInfo.DigestedEmitterIndex = DigestedEmitterIndex;
EmitterInfo.SourceGraph = DigestDatabase.CreateGraphDigest(EmitterGraph, ChangeIdBuilder);
if (RIParamMode == ENiagaraCompileRIParamMode::GameThread)
{
EmitterInfo.StaticVariableResults = SystemInfo.StaticVariableResults;
TSharedPtr<FNiagaraGraphCachedDataBase, ESPMode::ThreadSafe> CachedTraversalEmitterData = Handle.GetInstance().Emitter->GetCachedTraversalData(Handle.GetInstance().Version);
EmitterInfo.StaticVariableResults.Append(static_cast<FNiagaraGraphCachedBuiltHistory*>(CachedTraversalEmitterData.Get())->StaticVariables);
}
else
{
EmitterData->GatherStaticVariables(EmitterInfo.InitialStaticVariables);
}
// be sure to incorporate our constant resolver into the top level SystemInfo.ConstantResolver
SystemInfo.ConstantResolver.AddChildResolver(EmitterInfo.EmitterHandleId, EmitterInfo.ConstantResolver);
}
{
constexpr bool bCompilableOnly = false;
constexpr bool bEnabledOnly = false;
TArray<UNiagaraScript*> EmitterScripts;
EmitterData->GetScripts(EmitterScripts, bCompilableOnly, bEnabledOnly);
EmitterInfo.OwnedScriptKeys.Reserve(EmitterScripts.Num());
for (const UNiagaraScript* EmitterScript : EmitterScripts)
{
EmitterInfo.OwnedScriptKeys.Add(EmitterScript);
}
}
const TArray<UNiagaraSimulationStageBase*> SimStages = EmitterData->GetSimulationStages();
EmitterInfo.SimStages.Reserve(SimStages.Num());
for (const UNiagaraSimulationStageBase* SourceSimStage : SimStages)
{
if (SourceSimStage)
{
FNiagaraSimulationStageInfo& SimStageInfo = EmitterInfo.SimStages.AddDefaulted_GetRef();
SimStageInfo.bEnabled = SourceSimStage->bEnabled;
SimStageInfo.StageId = SourceSimStage->Script->GetUsageId();
if (const UNiagaraSimulationStageGeneric* SourceGenericSimStage = Cast<const UNiagaraSimulationStageGeneric>(SourceSimStage))
{
SimStageInfo.bGenericStage = true;
SimStageInfo.IterationSource = SourceGenericSimStage->IterationSource;
if (SimStageInfo.IterationSource == ENiagaraIterationSource::DataInterface)
{
SimStageInfo.DataInterfaceBindingName = SourceGenericSimStage->DataInterface.BoundVariable.GetName();
}
}
{
TArray<FNiagaraSimulationStageCompilationData> SimStageCompilationData;
SimStageInfo.bHasCompilationData = SourceSimStage->FillCompilationData(SimStageCompilationData);
if (SimStageInfo.bHasCompilationData)
{
SimStageInfo.CompilationData = SimStageCompilationData[0];
}
}
}
}
}
}
System_GT->GetExposedParameters().GetParameters(SystemInfo.OriginalExposedParams);
}
void FNiagaraSystemCompilationTask::DigestParameterCollections(TConstArrayView<TWeakObjectPtr<UNiagaraParameterCollection>> Collections)
{
DigestedParameterCollections.Reserve(Collections.Num());
for (TWeakObjectPtr<UNiagaraParameterCollection> Collection : Collections)
{
if (const UNiagaraParameterCollection* CollectionPtr = Collection.Get())
{
DigestedParameterCollections.Add(CollectionPtr, FNiagaraDigestDatabase::Get().CreateCompilationCopy(CollectionPtr));
}
}
}
void FNiagaraSystemCompilationTask::DigestShaderInfo(const ITargetPlatform* InTargetPlatform, FNiagaraShaderType* InShaderType)
{
TargetPlatform = InTargetPlatform;
NiagaraShaderType = InShaderType;
}
void FNiagaraSystemCompilationTask::AddScript(int32 SourceEmitterIndex, UNiagaraScript* Script, const FNiagaraVMExecutableDataId& CompileId, bool bRequiresCompilation, TConstArrayView<FShaderCompileRequest> ShaderRequests)
{
if (!Script)
{
return;
}
if (bRequiresCompilation)
{
FVersionedNiagaraScriptData* ScriptData = Script->GetScriptData(CompileId.ScriptVersionID);
if (ensure(ScriptData))
{
FCompileGroupInfo* GroupInfo = GetCompileGroupInfo(SourceEmitterIndex);
if (!GroupInfo)
{
GroupInfo = &CompileGroups.Emplace_GetRef(SourceEmitterIndex);
}
TArray<ENiagaraScriptUsage> DuplicateUsages;
NiagaraCompilationTasksImpl::GetUsagesToDuplicate(Script->GetUsage(), DuplicateUsages);
for (ENiagaraScriptUsage DuplicateUsage : DuplicateUsages)
{
GroupInfo->ValidUsages.AddUnique(DuplicateUsage);
}
const int32 CompileTaskIndex = CompileTasks.AddDefaulted();
GroupInfo->CompileTaskIndices.Add(CompileTaskIndex);
FCompileTaskInfo& TaskInfo = CompileTasks[CompileTaskIndex];
TaskInfo.CompileOptions = FNiagaraCompileOptions(
Script->GetUsage(),
Script->GetUsageId(),
ScriptData->ModuleUsageBitmask,
Script->GetPathName(),
Script->GetFullName(),
Script->GetName());
TaskInfo.CompileOptions.AdditionalDefines = CompileId.AdditionalDefines;
TaskInfo.CompileOptions.AdditionalVariables = CompileId.AdditionalVariables;
TaskInfo.SourceScript = Script;
TaskInfo.ScriptKey = Script;
TaskInfo.AssetPath = Script->GetPathName();
TaskInfo.CompileId = CompileId;
const bool bIsGpuScript = TaskInfo.CompileOptions.IsGpuScript() && UNiagaraScript::IsGPUScript(TaskInfo.CompileOptions.TargetUsage);
TaskInfo.ScriptCompileType = bIsGpuScript
? UNiagaraScript::AreGpuScriptsCompiledBySystem()
? EScriptCompileType::CompileForGpu
: EScriptCompileType::TranslateForGpu
: TaskInfo.CompileOptions.IsGpuScript()
? EScriptCompileType::DummyCompileForCpuStubs
: EScriptCompileType::CompileForVm;
if (TaskInfo.ScriptCompileType == EScriptCompileType::CompileForGpu)
{
const FEmitterInfo* EmitterInfo = SystemInfo.EmitterInfoBySourceEmitter(SourceEmitterIndex);
if (EmitterInfo && EmitterInfo->Enabled)
{
TaskInfo.ComputeShaderTaskIndices.Reserve(ShaderRequests.Num());
for (const FShaderCompileRequest& ShaderRequest : ShaderRequests)
{
TaskInfo.ComputeShaderTaskIndices.Add(CompileComputeShaderTasks.Num());
FCompileComputeShaderTaskInfo& ShaderTaskInfo = CompileComputeShaderTasks.AddDefaulted_GetRef();
ShaderTaskInfo.ParentCompileTaskIndex = CompileTaskIndex;
ShaderTaskInfo.ShaderMapId = ShaderRequest.ShaderMapId;
ShaderTaskInfo.ShaderPlatform = ShaderRequest.ShaderPlatform;
}
}
}
TaskInfo.TaskStartTime = FPlatformTime::Seconds();
}
}
FVersionedNiagaraEmitterData* EmitterData = nullptr;
if (SourceEmitterIndex != INDEX_NONE)
{
EmitterData = System_GT->GetEmitterHandle(SourceEmitterIndex).GetEmitterData();
}
FScriptInfo& Info = DigestedScriptInfo.Add(Script);
Script->RapidIterationParameters.CopyParametersTo(Info.RapidIterationParameters, false, FNiagaraParameterStore::EDataInterfaceCopyMethod::None);
Info.RapidIterationParameters.ParameterGuidMapping = Script->RapidIterationParameters.ParameterGuidMapping;
{
const FNiagaraTypeDefinition& PositionTypeDef = FNiagaraTypeDefinition::GetPositionDef();
const FNiagaraTypeDefinition& Vec3TypeDef = FNiagaraTypeDefinition::GetVec3Def();
// sanitize the guid mapping to account for parameters that might have been auto upgraded from Vec3 to Position
// for LWC. The ParameterStore will be updated, but the guid mapping may not have been
for (const FNiagaraVariableWithOffset& ParamStoreVariable : Info.RapidIterationParameters.ReadParameterVariables())
{
if (ParamStoreVariable.GetType() == PositionTypeDef)
{
if (!Info.RapidIterationParameters.ParameterGuidMapping.Contains(ParamStoreVariable))
{
FNiagaraVariable VariableAsVector(Vec3TypeDef, ParamStoreVariable.GetName());
FGuid ExistingGuid;
if (Info.RapidIterationParameters.ParameterGuidMapping.RemoveAndCopyValue(ParamStoreVariable, ExistingGuid))
{
Info.RapidIterationParameters.ParameterGuidMapping.Add(ParamStoreVariable, ExistingGuid);
}
}
}
}
}
Info.Usage = Script->GetUsage();
Info.UsageId = Script->GetUsageId();
Info.SourceEmitterIndex = SourceEmitterIndex;
for (UNiagaraScript* DependentScript : NiagaraCompilationTasksImpl::FindDependentScripts(System_GT.Get(), EmitterData, Script))
{
Info.DependentScripts.AddUnique(DependentScript);
}
}
void FNiagaraSystemCompilationTask::Tick()
{
switch (CompilationState)
{
case EState::WaitingForProcessing:
{
// check to see if the task's compiler has completed
for (FCompileTaskInfo& CompileTask : CompileTasks)
{
const bool HasPendingCompilationTask = CompileTask.CompileResultsReadyEvent.IsValid()
&& CompileTask.CompileResultsReadyEvent->IsValid()
&& !CompileTask.CompileResultsReadyEvent->IsCompleted();
if (HasPendingCompilationTask)
{
bool bRetrieveCompleted = false;
switch (CompileTask.ScriptCompileType)
{
case EScriptCompileType::CompileForVm:
case EScriptCompileType::TranslateForGpu:
bRetrieveCompleted = CompileTask.RetrieveCompilationResult(false);
break;
case EScriptCompileType::DummyCompileForCpuStubs:
CompileTask.RetrieveTranslateResult();
bRetrieveCompleted = true;
break;
case EScriptCompileType::CompileForGpu:
bRetrieveCompleted = CompileTask.RetrieveShaderMap(false);
break;
}
if (bRetrieveCompleted)
{
CompileTask.CompileResultsReadyEvent->Trigger();
}
}
}
} break;
case EState::Completed:
{
if (bAborting)
{
CompilationState = EState::Aborted;
}
} break;
}
}
bool FNiagaraSystemCompilationTask::Poll(FNiagaraSystemAsyncCompileResults& Results) const
{
// ignore actually collecting the data if we're in the middle of aborting and just let the caller know
// that there's no tasks waiting for them
if (bAborting)
{
return true;
}
switch (CompilationState)
{
case EState::ResultsProcessed:
case EState::Completed:
{
for (const FCompileTaskInfo& TaskInfo : CompileTasks)
{
if (UNiagaraScript* SourceScript = TaskInfo.SourceScript.Get())
{
FNiagaraScriptAsyncCompileData& CompileData = Results.CompileResultMap.Add(SourceScript);
CompileData.bFromDerivedDataCache = TaskInfo.DDCTaskInfo.bFromDerivedDataCache;
CompileData.CompileId = TaskInfo.CompileId;
CompileData.ExeData = TaskInfo.ExeData;
for (int32 ShaderTaskIndex : TaskInfo.ComputeShaderTaskIndices)
{
const FCompileComputeShaderTaskInfo& ShaderTaskInfo = CompileComputeShaderTasks[ShaderTaskIndex];
FNiagaraCompiledShaderInfo& ResultsInfo = CompileData.CompiledShaders.AddDefaulted_GetRef();
ResultsInfo.TargetPlatform = TargetPlatform;
ResultsInfo.ShaderPlatform = ShaderTaskInfo.ShaderPlatform;
ResultsInfo.FeatureLevel = ShaderTaskInfo.ShaderMapId.FeatureLevel;
ResultsInfo.CompiledShader = ShaderTaskInfo.ShaderMap;
ResultsInfo.CompilationErrors = ShaderTaskInfo.CompilationErrors;
}
CompileData.NamedDataInterfaces.Reserve(TaskInfo.NamedDataInterfaces.Num());
Algo::Transform(TaskInfo.NamedDataInterfaces, CompileData.NamedDataInterfaces, [](const TTuple<FName, UNiagaraDataInterface*>& InElement)
{
return MakeTuple(InElement.Key, InElement.Value);
});
CompileData.CompileMetrics.TaskWallTime = (float) (FPlatformTime::Seconds() - TaskInfo.TaskStartTime);
CompileData.CompileMetrics.DDCFetchTime = 0.0f;
CompileData.CompileMetrics.CompilerWallTime = TaskInfo.CompilerWallTime;
CompileData.CompileMetrics.CompilerWorkerTime = TaskInfo.CompilerWorkerTime;
CompileData.CompileMetrics.CompilerPreprocessTime = TaskInfo.CompilerPreprocessTime;
CompileData.CompileMetrics.TranslateTime = TaskInfo.TranslationTime;
CompileData.CompileMetrics.ByteCodeOptimizeTime = TaskInfo.ByteCodeOptimizeTime;
if (const FScriptInfo* ScriptInfo = DigestedScriptInfo.Find(SourceScript))
{
if (const FEmitterInfo* EmitterInfo = SystemInfo.EmitterInfoBySourceEmitter(ScriptInfo->SourceEmitterIndex))
{
CompileData.UniqueEmitterName = EmitterInfo->UniqueEmitterName;
}
// we also need to incorporate the rapid iteration parameters that we encountered
TConstArrayView<FNiagaraVariableWithOffset> RapidIterationParameters = ScriptInfo->RapidIterationParameters.ReadParameterVariables();
CompileData.RapidIterationParameters.Reserve(RapidIterationParameters.Num());
for (const FNiagaraVariableWithOffset& ParamWithOffset : RapidIterationParameters)
{
FNiagaraVariable& Parameter = CompileData.RapidIterationParameters.Add_GetRef(ParamWithOffset);
Parameter.SetData(ScriptInfo->RapidIterationParameters.GetParameterData(ParamWithOffset));
}
}
}
}
Results.bForced = bForced;
Results.ExposedVariables = SystemExposedVariables.Array();
return true;
}
}
return false;
}
bool FNiagaraSystemCompilationTask::CanRemove() const
{
return (CompilationState == EState::Completed && ResultsRetrieved)
|| (CompilationState == EState::Aborted);
}
bool FNiagaraSystemCompilationTask::AreResultsPending() const
{
return CompilationState == EState::Completed && !ResultsRetrieved;
}
void FNiagaraSystemCompilationTask::WaitTillCompileCompletion()
{
const FTimespan WaitTimeout = FTimespan::FromMilliseconds(50.0);
while (!CompileCompletionEvent.IsCompleted())
{
// if the wait doesn't complete the task then we need to make sure to poke the
// compilation manager since there we may be waiting on it
if (!CompileCompletionEvent.Wait(WaitTimeout))
{
FNiagaraSystemCompilingManager::Get().AdvanceAsyncTasks();
}
}
}
void FNiagaraSystemCompilationTask::WaitTillCachePutCompletion()
{
if (PutRequestHelper.IsValid())
{
PutRequestHelper->CompletionEvent.Wait();
}
}
void FNiagaraSystemCompilationTask::Precompile()
{
TRACE_CPUPROFILER_EVENT_SCOPE(NiagaraAsyncTask_Precompile);
SystemPrecompileData = MakeShared<FNiagaraPrecompileData, ESPMode::ThreadSafe>();
SystemPrecompileData->SharedCompileDataInterfaceData = MakeShared<TArray<FNiagaraPrecompileData::FCompileDataInterfaceData>>();
TArray<TSharedPtr<FNiagaraPrecompileData, ESPMode::ThreadSafe>> DependentRequests;
FCompileConstantResolver EmptyResolver;
SystemPrecompileData->SourceName = SystemInfo.SystemName;
SystemPrecompileData->DigestedSourceGraph = SystemInfo.SystemSourceGraph;
SystemPrecompileData->bUseRapidIterationParams = SystemInfo.bUseRapidIterationParams;
SystemPrecompileData->bDisableDebugSwitches = SystemInfo.bDisableDebugSwitches;
TArray<FString> EmitterNames;
// Create an array of variables that we might encounter when traversing the graphs (include the originally exposed vars above)
TArray<FNiagaraVariable> EncounterableVars = SystemInfo.OriginalExposedParams;
const int32 EmitterCount = SystemInfo.EmitterInfo.Num();
SystemPrecompileData->EmitterData.Empty(EmitterCount);
for (const FEmitterInfo& EmitterInfo : SystemInfo.EmitterInfo)
{
TSharedPtr<FNiagaraPrecompileData, ESPMode::ThreadSafe> EmitterPtr = MakeShared<FNiagaraPrecompileData, ESPMode::ThreadSafe>();
EmitterPtr->EmitterUniqueName = EmitterInfo.UniqueEmitterName;
EmitterPtr->EmitterID = FNiagaraEmitterID(EmitterInfo.SourceEmitterIndex);
EmitterPtr->SourceName = SystemPrecompileData->SourceName;
EmitterPtr->DigestedSourceGraph = EmitterInfo.SourceGraph;
EmitterPtr->bUseRapidIterationParams = SystemPrecompileData->bUseRapidIterationParams;
EmitterPtr->bDisableDebugSwitches = SystemPrecompileData->bDisableDebugSwitches;
EmitterPtr->SharedCompileDataInterfaceData = SystemPrecompileData->SharedCompileDataInterfaceData;
SystemPrecompileData->EmitterData.Add(EmitterPtr);
EmitterNames.Add(EmitterInfo.UniqueInstanceName);
}
// Now deep copy the system graphs, skipping traversal into any emitter references.
{
SystemPrecompileData->FinishPrecompile(
*this,
EncounterableVars,
SystemInfo.StaticVariableResults,
SystemInfo.ConstantResolver,
{ ENiagaraScriptUsage::SystemSpawnScript, ENiagaraScriptUsage::SystemUpdateScript },
{},
EmitterNames);
SystemPrecompileData->CollectBakedRapidIterationParameters(*this, SystemInfo.OwnedScriptKeys);
}
// Add the User and System variables that we did encounter to the list that emitters might also encounter.
TArray<FNiagaraVariable> SystemEncounteredUserVariables;
SystemPrecompileData->GatherPreCompiledVariables(FNiagaraConstants::UserNamespaceString, SystemEncounteredUserVariables);
NiagaraCompilationTasksImpl::AppendUnique(SystemEncounteredUserVariables, EncounterableVars);
SystemExposedVariables.Append(SystemEncounteredUserVariables);
SystemPrecompileData->GatherPreCompiledVariables(FNiagaraConstants::SystemNamespaceString, EncounterableVars);
// Now we can finish off the emitters.
for (int32 EmitterIt = 0; EmitterIt < EmitterCount; ++EmitterIt)
{
const FEmitterInfo& EmitterInfo = SystemInfo.EmitterInfo[EmitterIt];
if (EmitterInfo.Enabled) // Don't pull in the emitter if it isn't going to be used.
{
TArray<FNiagaraVariable> StaticVariablesFromEmitter = SystemInfo.StaticVariableResults;
StaticVariablesFromEmitter.Append(EmitterInfo.StaticVariableResults);
FNiagaraPrecompileData* EmitterPrecompileData = SystemPrecompileData->EmitterData[EmitterIt].Get();
EmitterPrecompileData->FinishPrecompile(
*this,
EncounterableVars,
StaticVariablesFromEmitter,
EmitterInfo.ConstantResolver,
{ ENiagaraScriptUsage::EmitterSpawnScript, ENiagaraScriptUsage::EmitterUpdateScript },
{},
EmitterNames);
// Then finish the precompile for the particle scripts once we've gathered the emitter vars which might be referenced.
TArray<FNiagaraVariable> ParticleEncounterableVars = EncounterableVars;
EmitterPrecompileData->GatherPreCompiledVariables(FNiagaraConstants::EmitterNamespaceString, ParticleEncounterableVars);
TArray<FNiagaraVariable> EmitterEncounteredUserVariables;
EmitterPrecompileData->GatherPreCompiledVariables(FNiagaraConstants::UserNamespaceString, EmitterEncounteredUserVariables);
SystemExposedVariables.Append(EmitterEncounteredUserVariables);
TArray<FNiagaraVariable> OldStaticVars = EmitterPrecompileData->StaticVariables;
EmitterPrecompileData->FinishPrecompile(
*this,
ParticleEncounterableVars,
OldStaticVars,
EmitterInfo.ConstantResolver,
{
ENiagaraScriptUsage::ParticleSpawnScript,
ENiagaraScriptUsage::ParticleSpawnScriptInterpolated,
ENiagaraScriptUsage::ParticleUpdateScript,
ENiagaraScriptUsage::ParticleEventScript,
ENiagaraScriptUsage::ParticleGPUComputeScript,
ENiagaraScriptUsage::ParticleSimulationStageScript
},
EmitterInfo.SimStages,
EmitterNames);
EmitterPrecompileData->CollectBakedRapidIterationParameters(*this, EmitterInfo.OwnedScriptKeys);
}
}
}
void FNiagaraSystemCompilationTask::GetAvailableCollections(TArray<FNiagaraCompilationNPCHandle>& OutCollections) const
{
DigestedParameterCollections.GenerateValueArray(OutCollections);
}
bool FNiagaraSystemCompilationTask::GenerateCompileMetaData() const
{
return bGenerateCompileMetaData;
}
const FGuid& FNiagaraSystemCompilationTask::GetCompilationTaskId() const
{
return CompilationTaskId;
}
bool FNiagaraSystemCompilationTask::GetStageName(int32 EmitterIndex, const FNiagaraCompilationNodeOutput* OutputNode, FName& OutStageName) const
{
OutStageName = NAME_None;
if (EmitterIndex == INDEX_NONE)
{
return true;
}
bool bStageEnabled = true;
if (OutputNode->Usage == ENiagaraScriptUsage::ParticleSimulationStageScript)
{
const FNiagaraSimulationStageInfo* SimStageInfo = SystemInfo.EmitterInfo[EmitterIndex].SimStages.FindByPredicate([OutputNode](const FNiagaraSimulationStageInfo& SimStageInfo)
{
return SimStageInfo.StageId == OutputNode->UsageId;
});
bStageEnabled = SimStageInfo && SimStageInfo->bEnabled;
if (bStageEnabled && SimStageInfo->bGenericStage)
{
OutStageName = SimStageInfo->DataInterfaceBindingName;
}
}
return bStageEnabled;
}
struct FNiagaraSystemCompilationTask::FCollectStaticVariablesTaskBuilder
{
FCollectStaticVariablesTaskBuilder(FNiagaraSystemCompilationTask& CompilationTask)
: FoundStaticVariables(CompilationTask.SystemInfo.StaticVariableResults)
{
if (FNiagaraCompilationGraphDigested* SystemGraph = CompilationTask.SystemInfo.SystemSourceGraph.Get())
{
FStaticVariableBuilderTaskHandle BuilderTask = MakeShared<FStaticVariableBuilderTask, ESPMode::ThreadSafe>();
BuilderTask->CompilationTask = &CompilationTask;
BuilderTask->ConstantResolver = CompilationTask.SystemInfo.ConstantResolver;
BuilderTask->GraphContext = SystemGraph;
BuilderTask->InitialStaticVariables = CompilationTask.SystemInfo.InitialStaticVariables;
SystemGraph->FindOutputNodes(BuilderTask->OutputNodes);
BuilderTask->StageNames.Init(NAME_None, BuilderTask->OutputNodes.Num());
StaticVariableTasksToProcess.Add(BuilderTask);
}
}
FCollectStaticVariablesTaskBuilder(const FNiagaraSystemCompilationTask& CompilationTask, FEmitterInfo& EmitterInfo)
: FoundStaticVariables(EmitterInfo.StaticVariableResults)
{
if (FNiagaraCompilationGraphDigested* EmitterGraph = EmitterInfo.SourceGraph.Get())
{
TArray<const FNiagaraCompilationNodeOutput*> OutputNodes;
EmitterGraph->FindOutputNodes(OutputNodes);
// collect sim stage info for each of the output nodes (if they are even sim stages)
TArray<TTuple<bool, FName>> OutputNodeStageInfo;
OutputNodeStageInfo.Reserve(OutputNodes.Num());
int32 EnabledOutputNodeCount = 0;
Algo::Transform(OutputNodes, OutputNodeStageInfo, [&EmitterInfo, &EnabledOutputNodeCount](const FNiagaraCompilationNodeOutput* OutputNode) -> TTuple<bool, FName>
{
bool bEnabled = true;
FName StageName = NAME_None;
if (OutputNode->Usage == ENiagaraScriptUsage::ParticleSimulationStageScript)
{
const FNiagaraSimulationStageInfo* SimStage = EmitterInfo.SimStages.FindByPredicate([OutputNode](const FNiagaraSimulationStageInfo& StageInfo) -> bool
{
return StageInfo.StageId == OutputNode->UsageId;
});
if (SimStage)
{
bEnabled = SimStage->bEnabled;
StageName = SimStage->DataInterfaceBindingName;
}
}
if (bEnabled)
{
++EnabledOutputNodeCount;
}
return MakeTuple(bEnabled, StageName);
});
if (EnabledOutputNodeCount)
{
FStaticVariableBuilderTaskHandle BuilderTask = MakeShared<FStaticVariableBuilderTask, ESPMode::ThreadSafe>();
BuilderTask->CompilationTask = &CompilationTask;
BuilderTask->ConstantResolver = EmitterInfo.ConstantResolver;
BuilderTask->GraphContext = EmitterGraph;
BuilderTask->UniqueEmitterName = EmitterInfo.UniqueEmitterName;
// the initial static variables needs to come from gathering data from the Emitter
// but also what has been calculated in the SystemInfo
BuilderTask->InitialStaticVariables = CompilationTask.SystemInfo.StaticVariableResults;
for (const FNiagaraVariable& EmitterGatheredVariable : EmitterInfo.InitialStaticVariables)
{
BuilderTask->InitialStaticVariables.AddUnique(EmitterGatheredVariable);
}
// Only use the static variables that match up with our expectations for this script. IE for emitters, filter things out for resolution.
{ // FNiagaraParameterUtilities::FilterToRelevantStaticVariables
FNiagaraAliasContext RenameContext(ENiagaraScriptUsage::ParticleSpawnScript);
RenameContext.ChangeEmitterName(EmitterInfo.UniqueEmitterName, FNiagaraConstants::EmitterNamespaceString);
for (FNiagaraVariable& StaticVariable : BuilderTask->InitialStaticVariables)
{
const FNiagaraVariable ResolvedVariable = FNiagaraUtilities::ResolveAliases(StaticVariable, RenameContext);
StaticVariable.SetName(ResolvedVariable.GetName());
}
}
//BuilderTask->InitialStaticVariables = EmitterInfo.InitialStaticVariables;
BuilderTask->OutputNodes.Reserve(EnabledOutputNodeCount);
BuilderTask->StageNames.Reserve(EnabledOutputNodeCount);
for (int32 OutputNodeIt = 0; OutputNodeIt < OutputNodes.Num(); ++OutputNodeIt)
{
if (OutputNodeStageInfo[OutputNodeIt].Key)
{
BuilderTask->OutputNodes.Add(OutputNodes[OutputNodeIt]);
BuilderTask->StageNames.Add(OutputNodeStageInfo[OutputNodeIt].Value);
}
}
StaticVariableTasksToProcess.Add(BuilderTask);
}
}
}
struct FStaticVariableBuilderTask
{
const FNiagaraSystemCompilationTask* CompilationTask = nullptr;
FNiagaraFixedConstantResolver ConstantResolver;
const FNiagaraCompilationGraph* GraphContext = nullptr;
FString UniqueEmitterName;
TArray<const FNiagaraCompilationNodeOutput*> OutputNodes;
TArray<FNiagaraVariable> InitialStaticVariables;
TArray<FName> StageNames;
TArray<FNiagaraVariable> FoundStaticVariables;
};
using FStaticVariableBuilderTaskHandle = TSharedPtr<FStaticVariableBuilderTask, ESPMode::ThreadSafe>;
static UE::Tasks::FTask LaunchCollectedTasks(FCollectStaticVariablesTaskBuilder&& Context)
{
using namespace UE::Tasks;
if (!Context.StaticVariableTasksToProcess.IsEmpty())
{
// fire off the tasks related to collecting static variables
const int32 TaskCount = Context.StaticVariableTasksToProcess.Num();
TArray<FTask> PendingTasks;
PendingTasks.Reserve(TaskCount);
for (FStaticVariableBuilderTaskHandle& BuilderTask : Context.StaticVariableTasksToProcess)
{
PendingTasks.Add(Launch(UE_SOURCE_LOCATION, [BuilderTask]
{
TArray<FNiagaraVariable> ActiveStaticVariables = BuilderTask->InitialStaticVariables;
const int32 OutputNodeCount = BuilderTask->OutputNodes.Num();
for (int32 OutputNodeIt = 0; OutputNodeIt < OutputNodeCount; ++OutputNodeIt)
{
const FNiagaraCompilationNodeOutput* OutputNode = BuilderTask->OutputNodes[OutputNodeIt];
TNiagaraParameterMapHistoryWithMetaDataBuilder<FNiagaraCompilationDigestBridge> Builder;
*Builder.ConstantResolver = BuilderTask->ConstantResolver;
Builder.AddGraphToCallingGraphContextStack(BuilderTask->GraphContext);
Builder.RegisterExternalStaticVariables(ActiveStaticVariables);
BuilderTask->CompilationTask->GetAvailableCollections(Builder.AvailableCollections->EditCollections());
Builder.BeginTranslation(FNiagaraConstants::EmitterNamespaceString);
Builder.EnableScriptAllowList(true, OutputNode->Usage);
Builder.IncludeStaticVariablesOnly();
Builder.BeginUsage(OutputNode->Usage, BuilderTask->StageNames[OutputNodeIt]);
Builder.BuildParameterMaps(OutputNode, true);
Builder.EndUsage();
for (const FNiagaraVariable& FoundVariable : Builder.StaticVariables)
{
ActiveStaticVariables.AddUnique(FoundVariable);
}
}
BuilderTask->FoundStaticVariables = MoveTemp(ActiveStaticVariables);
}));
}
TArray<FStaticVariableBuilderTaskHandle> MergeTaskInput = Context.StaticVariableTasksToProcess;
TArray<FNiagaraVariable>& MergeTaskOutput = Context.FoundStaticVariables;
return Launch(UE_SOURCE_LOCATION, [MergeTaskInput, &MergeTaskOutput]
{
if (MergeTaskInput.Num() == 1)
{
MergeTaskOutput = MergeTaskInput[0]->FoundStaticVariables;
}
else
{
for (const FStaticVariableBuilderTaskHandle& BuilderTask : MergeTaskInput)
{
for (const FNiagaraVariable& BuilderTaskVariable : BuilderTask->FoundStaticVariables)
{
MergeTaskOutput.AddUnique(BuilderTaskVariable);
}
}
}
}, PendingTasks);
}
return FTask();
}
TArray<FStaticVariableBuilderTaskHandle> StaticVariableTasksToProcess;
TArray<FNiagaraVariable>& FoundStaticVariables;
};
struct FNiagaraSystemCompilationTask::FBuildRapidIterationTaskBuilder
{
FBuildRapidIterationTaskBuilder(FNiagaraSystemCompilationTask& CompilationTask, FScriptInfo& ScriptInfo)
: ParameterStore(ScriptInfo.RapidIterationParameters)
{
const FNiagaraCompilationGraph* Graph;
FString UniqueEmitterName;
FNiagaraFixedConstantResolver ConstantResolver;
if (const FEmitterInfo* EmitterInfo = CompilationTask.SystemInfo.EmitterInfoBySourceEmitter(ScriptInfo.SourceEmitterIndex))
{
Graph = EmitterInfo->SourceGraph.Get();
FoundStaticVariables = &EmitterInfo->StaticVariableResults;
UniqueEmitterName = EmitterInfo->UniqueEmitterName;
ConstantResolver = EmitterInfo->ConstantResolver;
}
else
{
Graph = CompilationTask.SystemInfo.SystemSourceGraph.Get();
FoundStaticVariables = &CompilationTask.SystemInfo.StaticVariableResults;
ConstantResolver = CompilationTask.SystemInfo.ConstantResolver;
}
if (Graph)
{
TArray<const FNiagaraCompilationNodeOutput*> OutputNodes;
if (ScriptInfo.Usage == ENiagaraScriptUsage::ParticleGPUComputeScript)
{
Graph->FindOutputNodes(OutputNodes);
OutputNodes.SetNum(Algo::RemoveIf(OutputNodes, [](const FNiagaraCompilationNodeOutput* OutputNode) -> bool
{
return !UNiagaraScript::IsParticleScript(OutputNode->Usage);
}));
}
else
{
if (const FNiagaraCompilationNodeOutput* OutputNode = Graph->FindEquivalentOutputNode(ScriptInfo.Usage, ScriptInfo.UsageId))
{
OutputNodes.Add(OutputNode);
}
}
for (const FNiagaraCompilationNodeOutput* OutputNode : OutputNodes)
{
TArray<const FNiagaraCompilationNode*> TraversalNodes;
Graph->BuildTraversal(OutputNode, TraversalNodes);
for (const FNiagaraCompilationNode* TraversalNode : TraversalNodes)
{
if (const FNiagaraCompilationNodeFunctionCall* FunctionCallNode = TraversalNode->AsType<FNiagaraCompilationNodeFunctionCall>())
{
if (FunctionCallNode->CalledGraph.IsValid())
{
FRapidIterationBuilderTaskHandle BuilderTask = MakeShared<FRapidIterationBuilderTask, ESPMode::ThreadSafe>();
BuilderTask->CompilationTask = &CompilationTask;
BuilderTask->ConstantResolver = ConstantResolver;
BuilderTask->FunctionCallNode = FunctionCallNode;
BuilderTask->UniqueEmitterName = UniqueEmitterName;
BuilderTask->ScriptUsage = ScriptInfo.Usage;
RapidIterationTasksToProcess.Add(BuilderTask);
}
}
}
}
}
}
struct FRapidIterationBuilderTask
{
const FNiagaraSystemCompilationTask* CompilationTask = nullptr;
FNiagaraFixedConstantResolver ConstantResolver;
const FNiagaraCompilationNodeFunctionCall* FunctionCallNode = nullptr;
TArray<FNiagaraVariable> ModuleInputVariables;
FString UniqueEmitterName;
ENiagaraScriptUsage ScriptUsage;
};
using FRapidIterationBuilderTaskHandle = TSharedPtr<FRapidIterationBuilderTask, ESPMode::ThreadSafe>;
UE::Tasks::FTask LaunchCollectedTasks()
{
using namespace UE::Tasks;
const int32 TaskCount = RapidIterationTasksToProcess.Num();
TArray<FTask> PendingTasks;
PendingTasks.Reserve(TaskCount);
const TArray<FNiagaraVariable>* OwnerStaticVariables = FoundStaticVariables;
for (FRapidIterationBuilderTaskHandle& BuilderTask : RapidIterationTasksToProcess)
{
PendingTasks.Add(Launch(UE_SOURCE_LOCATION, [BuilderTask, OwnerStaticVariables]
{
constexpr bool bIgnoreDisabled = false;
constexpr bool bFilterForCompilation = false;
TNiagaraParameterMapHistoryBuilder<FNiagaraCompilationDigestBridge> Builder;
Builder.SetIgnoreDisabled(bIgnoreDisabled);
*Builder.ConstantResolver = BuilderTask->ConstantResolver;
Builder.RegisterExternalStaticVariables(*OwnerStaticVariables);
BuilderTask->CompilationTask->GetAvailableCollections(Builder.AvailableCollections->EditCollections());
/* The line below represents a compromise. It basically says don't look up the static variable from the
static variables list set in RegisterExternalStaticVariables as a rapid iteration parameter. Why? Rapid Iteration Parameters
may be stale and linger around from prior changes that have been abandoned. We keep them around so that when you toggle back
in the UI, you haven't also lost your last set values for that version. So therefore, we can't officially remove them.
The issue comes in when a static variable is set via some other means, linking to another variable for example. When generating
the UI, it *should* show anything that would match up to the linked variable, but because this is a parameter map history
traversal that is in isolation on the node and doesn't include upstream set nodes, it will fail and just look up the rapid iteration
version if left to its' own devices.
Trial 1 at a fix was to cache all the values at the root of the graph and look them up when we build the static variables list
we use above. That isn't good because that version skips all disabled modules and we want the UI to be consistent regardless
of disabled module status.
Trial 2 was to back up and include the precursor gets/sets. This fails for the same reason, ultimately something will
be bound to a variable that will assume that it needs to look up a bogus rapid iteration value and we end up back here.
Trial 3 just says, we know the static variables list above includes basically all relevant intermediate static values. So if
we just circumvent the logic in the parameter map history traversal that looks up by rapid iteration parameter, it will
find the right "cached" value. So we just do that below.
*/
Builder.SetIgnoreStaticRapidIterationParameters(true);
// if we are only dealing with the module input pins then we don't need to delve deep into the graph
Builder.MaxGraphDepthTraversal = 1;
BuilderTask->FunctionCallNode->BuildParameterMapHistory(Builder, false, bFilterForCompilation);
if (Builder.Histories.Num() == 1)
{
BuilderTask->ModuleInputVariables = NiagaraCompilationTasksImpl::ExtractInputVariablesFromHistory(
Builder.Histories[0], BuilderTask->FunctionCallNode->CalledGraph.Get());
BuilderTask->FunctionCallNode->MultiFindParameterMapDefaultValues(BuilderTask->ScriptUsage, BuilderTask->ConstantResolver, BuilderTask->ModuleInputVariables);
}
}));
}
TArray<FRapidIterationBuilderTaskHandle> MergeTaskInput = RapidIterationTasksToProcess;
FNiagaraParameterStore& MergeTaskOutput = ParameterStore;
return Launch(UE_SOURCE_LOCATION, [MergeTaskInput, &MergeTaskOutput]
{
TSet<FName> ValidParameterNames;
// we need to build a reverse mapping of the ParameterGuidMapping so that we can quickly find renamed parameters
TMultiMap<FGuid, FNiagaraVariable> GuidMapping;
for (const FNiagaraVariableBase& RapidIterationParameter : MergeTaskOutput.ReadParameterVariables())
{
if (FGuid* ParameterGuid = MergeTaskOutput.ParameterGuidMapping.Find(RapidIterationParameter))
{
GuidMapping.Add(*ParameterGuid, RapidIterationParameter);
}
}
for (FRapidIterationBuilderTaskHandle InputTask : MergeTaskInput)
{
for (const FNiagaraVariable& ModuleInputVariable : InputTask->ModuleInputVariables)
{
if (FNiagaraStackGraphUtilities::IsRapidIterationType(ModuleInputVariable.GetType()))
{
const FName FunctionName = FName(InputTask->FunctionCallNode->FunctionName);
FNiagaraParameterHandle AliasedFunctionInputHandle = FNiagaraParameterHandle::CreateAliasedModuleParameterHandle(ModuleInputVariable.GetName(), FunctionName);
const FName AliasedName = AliasedFunctionInputHandle.GetParameterHandleString();
FNiagaraVariable RapidIterationParameter(ModuleInputVariable);
const TCHAR* EmitterName = InputTask->UniqueEmitterName.IsEmpty() ? nullptr : *InputTask->UniqueEmitterName;
const FString RapidIterationConstantName = FNiagaraUtilities::CreateRapidIterationConstantName(AliasedName, EmitterName, InputTask->ScriptUsage);
RapidIterationParameter.SetName(*RapidIterationConstantName);
ValidParameterNames.Add(RapidIterationParameter.GetName());
NiagaraCompilationTasksImpl::FixupRenamedParameter(
ModuleInputVariable,
RapidIterationParameter,
InputTask->FunctionCallNode,
GuidMapping,
MergeTaskOutput);
NiagaraCompilationTasksImpl::AddRapidIterationParameter(
AliasedFunctionInputHandle,
ModuleInputVariable,
RapidIterationParameter,
InputTask->FunctionCallNode,
MergeTaskOutput);
}
}
}
// go through and remove any entries in the ParameterStore that weren't found
TArray<FNiagaraVariableBase> ParametersToRemove;
for (const FNiagaraVariableWithOffset& Parameter : MergeTaskOutput.ReadParameterVariables())
{
if (!ValidParameterNames.Contains(Parameter.GetName()))
{
ParametersToRemove.Add(Parameter);
}
}
for (const FNiagaraVariableBase& ParameterToRemove : ParametersToRemove)
{
MergeTaskOutput.RemoveParameter(ParameterToRemove);
}
}, PendingTasks);
}
TArray<FRapidIterationBuilderTaskHandle> RapidIterationTasksToProcess;
const TArray<FNiagaraVariable>* FoundStaticVariables = nullptr;
FNiagaraParameterStore& ParameterStore;
};
UE::Tasks::FTask FNiagaraSystemCompilationTask::BuildRapidIterationParametersAsync()
{
using namespace UE::Tasks;
COOK_STAT(auto Timer = NiagaraSystemCookStats::UsageStats.TimeSyncWork(); Timer.TrackCyclesOnly(););
TMap<int32 /*EmitterIndex*/, FTask> CollectStaticVariableTasks;
FTask SystemCollectStaticVariableTask = FCollectStaticVariablesTaskBuilder::LaunchCollectedTasks({*this});
CollectStaticVariableTasks.Add(INDEX_NONE, SystemCollectStaticVariableTask);
if (SystemCollectStaticVariableTask.IsValid())
{
for (FEmitterInfo& EmitterInfo : SystemInfo.EmitterInfo)
{
if (!EmitterInfo.Enabled)
{
continue;
}
FTask EmitterCollectStaticVariableTask = Launch(UE_SOURCE_LOCATION, [Self = AsShared(), &EmitterInfo]
{
AddNested(FCollectStaticVariablesTaskBuilder::LaunchCollectedTasks({*Self, EmitterInfo}));
}, SystemCollectStaticVariableTask);
CollectStaticVariableTasks.Add(EmitterInfo.DigestedEmitterIndex, EmitterCollectStaticVariableTask);
}
TArray<FTask> PendingTasks;
for (TMap<TObjectKey<UNiagaraScript>, FScriptInfo>::ElementType& CurrentIt : DigestedScriptInfo)
{
FScriptInfo& ScriptInfo = CurrentIt.Value;
const FEmitterInfo* EmitterInfo = SystemInfo.EmitterInfoBySourceEmitter(ScriptInfo.SourceEmitterIndex);
PendingTasks.Add(Launch(UE_SOURCE_LOCATION, [Self = AsShared(), &ScriptInfo]
{
FBuildRapidIterationTaskBuilder TaskBuilder(*Self, ScriptInfo);
AddNested(TaskBuilder.LaunchCollectedTasks());
}, CollectStaticVariableTasks.FindRef(EmitterInfo ? EmitterInfo->DigestedEmitterIndex : INDEX_NONE)));
}
// We need a single task to copy over parameters between dependent scripts and make sure that the static variable
// list is up to date
TMap<TObjectKey<UNiagaraScript>, FScriptInfo>* ScriptInfoMapPtr = &DigestedScriptInfo;
return Launch(UE_SOURCE_LOCATION, [ScriptInfoMapPtr, Self = AsShared()]
{
for (TMap<TObjectKey<UNiagaraScript>, FScriptInfo>::ElementType& CurrentIt : *ScriptInfoMapPtr)
{
FScriptInfo& SrcScriptInfo = CurrentIt.Value;
for (TObjectKey<UNiagaraScript>& DependentScriptKey : SrcScriptInfo.DependentScripts)
{
FScriptInfo& DstScriptInfo = ScriptInfoMapPtr->FindChecked(DependentScriptKey);
SrcScriptInfo.RapidIterationParameters.CopyParametersTo(DstScriptInfo.RapidIterationParameters, false, FNiagaraParameterStore::EDataInterfaceCopyMethod::None);
}
// make sure that all rapid iteration parameters on the script have made it to the system/emitter info
for (const FNiagaraVariableWithOffset& ScriptRI : SrcScriptInfo.RapidIterationParameters.ReadParameterVariables())
{
if (ScriptRI.GetType().IsStatic())
{
FNiagaraVariable StaticVariable = FNiagaraVariable(ScriptRI);
StaticVariable.SetData(SrcScriptInfo.RapidIterationParameters.GetParameterData(ScriptRI));
if (SrcScriptInfo.SourceEmitterIndex == INDEX_NONE)
{
Self->SystemInfo.StaticVariableResults.AddUnique(StaticVariable);
}
else if (FEmitterInfo* EmitterInfo = Self->SystemInfo.EmitterInfoBySourceEmitter(SrcScriptInfo.SourceEmitterIndex))
{
EmitterInfo->StaticVariableResults.AddUnique(StaticVariable);
}
}
}
}
}, PendingTasks);
}
return FTask();
}
void FNiagaraSystemCompilationTask::BuildAndApplyRapidIterationParameters()
{
check(IsInGameThread());
{
COOK_STAT(auto Timer = NiagaraSystemCookStats::UsageStats.TimeAsyncWait(); Timer.TrackCyclesOnly(););
BuildRapidIterationParametersAsync().Wait();
}
// see FNiagaraActiveCompilationAsyncTask::Apply() for why we don't allow removal
constexpr bool bAllowParameterRemoval = false;
for (const TMap<TObjectKey<UNiagaraScript>, FScriptInfo>::ElementType& CurrentIt : DigestedScriptInfo)
{
if (UNiagaraScript* Script = CurrentIt.Key.ResolveObjectPtr())
{
const FScriptInfo& ScriptInfo = CurrentIt.Value;
TConstArrayView<FNiagaraVariableWithOffset> SrcRapidIterationParameters = ScriptInfo.RapidIterationParameters.ReadParameterVariables();
TArray<FNiagaraVariable> DstRapidIterationParameters;
DstRapidIterationParameters.Reserve(SrcRapidIterationParameters.Num());
for (const FNiagaraVariableWithOffset& ParamWithOffset : SrcRapidIterationParameters)
{
FNiagaraVariable& Parameter = DstRapidIterationParameters.Add_GetRef(ParamWithOffset);
Parameter.SetData(ScriptInfo.RapidIterationParameters.GetParameterData(ParamWithOffset));
}
Script->ApplyRapidIterationParameters(DstRapidIterationParameters, bAllowParameterRemoval);
}
}
}
void FNiagaraSystemCompilationTask::IssuePostResultsProcessedTasks()
{
CompileCompletionEvent.Trigger();
CompilationState = EState::ResultsProcessed;
PutRequestHelper = MakeUnique<FDispatchDataCachePutRequests>();
PutRequestHelper->Launch(this);
Launch(UE_SOURCE_LOCATION, [Self = AsShared()]
{
Self->CompilationState = EState::Completed;
}, PutRequestHelper->CompletionEvent);
}
void FNiagaraSystemCompilationTask::IssueCompilationTasks()
{
using namespace UE::Tasks;
TArray<FTask> IssuedCompilationTasks;
TArray<FTaskEvent> CompilationTasks;
if (HasOutstandingCompileTasks())
{
FTask PrecompileTask = Launch(UE_SOURCE_LOCATION, [Self = AsShared()]
{
COOK_STAT(auto Timer = NiagaraSystemCookStats::UsageStats.TimeSyncWork(); Timer.TrackCyclesOnly(););
Self->Precompile();
});
// async compilation copy tasks
for (FCompileGroupInfo& GroupInfo : CompileGroups)
{
if (GroupInfo.HasOutstandingCompileTasks(*this))
{
FTask InstantiateGraphTask = Launch(UE_SOURCE_LOCATION, [Self = AsShared(), &GroupInfo]
{
COOK_STAT(auto Timer = NiagaraSystemCookStats::UsageStats.TimeSyncWork(); Timer.TrackCyclesOnly(););
GroupInfo.InstantiateCompileGraph(*Self);
}, PrecompileTask);
for (int32 CompileTaskIndex : GroupInfo.CompileTaskIndices)
{
FCompileTaskInfo& CompileTask = CompileTasks[CompileTaskIndex];
if (CompileTask.IsOutstanding(*this))
{
CompileTask.CompileResultsReadyEvent = MakeUnique<FTaskEvent>(UE_SOURCE_LOCATION);
CompileTask.CompileResultsProcessedEvent = MakeUnique<FTaskEvent>(UE_SOURCE_LOCATION);
IssuedCompilationTasks.Add(Launch(UE_SOURCE_LOCATION, [Self = AsShared(), &GroupInfo, &CompileTask]
{
COOK_STAT(auto Timer = NiagaraSystemCookStats::UsageStats.TimeSyncWork(); Timer.TrackCyclesOnly(););
FNiagaraSystemCompilationTask* TaskPtr = &Self.Get();
CompileTask.CollectNamedDataInterfaces(TaskPtr, GroupInfo);
switch (CompileTask.ScriptCompileType)
{
case EScriptCompileType::CompileForVm:
CompileTask.Translate(TaskPtr, GroupInfo);
CompileTask.IssueCompileVm(TaskPtr, GroupInfo);
break;
case EScriptCompileType::CompileForGpu:
CompileTask.Translate(TaskPtr, GroupInfo);
CompileTask.IssueCompileGpu(TaskPtr, GroupInfo);
break;
// path for handling the translation of GPU scripts but we're not handling the compilation of
// the compute shaders ourselves, rather it's using the old path of the FNiagaraShaderScript getting
// cached by the UNiagaraScript post CPU translation/compilation
case EScriptCompileType::TranslateForGpu:
CompileTask.Translate(TaskPtr, GroupInfo);
CompileTask.IssueTranslateGpu(TaskPtr, GroupInfo);
break;
// path for dealing with the CPU scripts (Particle spawn/update) when the emitter is GPU
// These scripts shouldn't need to be processed, but currently they are required for supplying
// RI parameters to the GPU execution context
case EScriptCompileType::DummyCompileForCpuStubs:
CompileTask.Translate(TaskPtr, GroupInfo);
break;
}
}, InstantiateGraphTask));
Launch(UE_SOURCE_LOCATION, [Self = AsShared(), &GroupInfo, &CompileTask]
{
COOK_STAT(auto Timer = NiagaraSystemCookStats::UsageStats.TimeSyncWork(); Timer.TrackCyclesOnly(););
CompileTask.ProcessCompilation(&Self.Get(), GroupInfo);
}, *CompileTask.CompileResultsReadyEvent);
CompilationTasks.Add(*CompileTask.CompileResultsProcessedEvent);
}
}
}
}
}
Launch(UE_SOURCE_LOCATION, [Self = AsShared()]
{
Self->CompilationState = EState::WaitingForProcessing;
}, IssuedCompilationTasks);
Launch(UE_SOURCE_LOCATION, [Self = AsShared()]
{
Self->IssuePostResultsProcessedTasks();
}, CompilationTasks);
}
bool FNiagaraSystemCompilationTask::HasOutstandingCompileTasks() const
{
for (const FCompileTaskInfo& CompileTask : CompileTasks)
{
if (CompileTask.IsOutstanding(*this))
{
return true;
}
}
return false;
}
UE::Tasks::FTask FNiagaraSystemCompilationTask::BeginTasks()
{
TRACE_CPUPROFILER_EVENT_SCOPE(NiagaraAsyncTask_BeginTasks);
using namespace UE::Tasks;
COOK_STAT(auto Timer = NiagaraSystemCookStats::UsageStats.TimeSyncWork(); Timer.TrackCyclesOnly(););
InitialGetRequestHelper.Launch(this);
TArray<FTask> SystemTaskPrerequisites;
SystemTaskPrerequisites.Add(InitialGetRequestHelper.CompletionEvent);
// if we are baking rapid iteration parameters then we can rely on the results from the DDC to
// supply everything that we need. If however we're not baking the rapid iteration parameters
// then we need to supplement the results from the DDC with our own collection of the RI so
// that we can apply them to the script if something has changed.
if (SystemInfo.bUseRapidIterationParams)
{
constexpr bool bSyncPrepareRapidIterationParameter = true;
if (RIParamMode == ENiagaraCompileRIParamMode::WorkerThread_Synchronous)
{
BuildAndApplyRapidIterationParameters();
}
else if (RIParamMode == ENiagaraCompileRIParamMode::WorkerThread_Asynchronous)
{
SystemTaskPrerequisites.Add(BuildRapidIterationParametersAsync());
}
}
FTask SystemTask = Launch(UE_SOURCE_LOCATION, [Self = AsShared()]
{
COOK_STAT(auto Timer = NiagaraSystemCookStats::UsageStats.TimeSyncWork(); Timer.TrackCyclesOnly(););
if (Self->HasOutstandingCompileTasks())
{
TArray<FTask> CompilationTaskPrerequisites;
if (Self->RIParamMode != ENiagaraCompileRIParamMode::GameThread && !Self->SystemInfo.bUseRapidIterationParams)
{
FTask BuildRIParamTask = Self->BuildRapidIterationParametersAsync();
Self->PostRIParameterGetRequestHelper = MakeUnique<FDispatchAndProcessDataCacheGetRequests>();
Launch(UE_SOURCE_LOCATION, [Self]
{
Self->PostRIParameterGetRequestHelper->Launch(&Self.Get());
}, BuildRIParamTask);
CompilationTaskPrerequisites.Add(Self->PostRIParameterGetRequestHelper->CompletionEvent);
}
FTask InnerTask = Launch(UE_SOURCE_LOCATION, [Self]
{
Self->IssueCompilationTasks();
}, CompilationTaskPrerequisites);
}
else
{
Self->IssuePostResultsProcessedTasks();
}
}, SystemTaskPrerequisites);
return SystemTask;
}