You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
* moved UMassProcessor's inlines to the end of the header to keep the API look clean * moved the virtuals defined in the header over to the cpp * comments spellig fixes and improvements. #rb mieszko.zielinski [CL 36426024 by mieszko zielinski in 5.5 branch]
580 lines
20 KiB
C++
580 lines
20 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "MassProcessor.h"
|
|
#include "MassEntitySettings.h"
|
|
#include "MassProcessorDependencySolver.h"
|
|
#include "VisualLogger/VisualLogger.h"
|
|
#include "Engine/World.h"
|
|
#include "MassCommandBuffer.h"
|
|
#include "MassExecutionContext.h"
|
|
|
|
#include UE_INLINE_GENERATED_CPP_BY_NAME(MassProcessor)
|
|
|
|
DECLARE_CYCLE_STAT(TEXT("MassProcessor Group Completed"), Mass_GroupCompletedTask, STATGROUP_TaskGraphTasks);
|
|
DECLARE_CYCLE_STAT(TEXT("Mass Processor Task"), STAT_Mass_DoTask, STATGROUP_Mass);
|
|
|
|
#if WITH_MASSENTITY_DEBUG
|
|
namespace UE::Mass::Debug
|
|
{
|
|
bool bLogProcessingGraph = false;
|
|
FAutoConsoleVariableRef CVarLogProcessingGraph(TEXT("mass.LogProcessingGraph"), bLogProcessingGraph
|
|
, TEXT("When enabled will log task graph tasks created while dispatching processors to other threads, along with their dependencies"), ECVF_Cheat);
|
|
}
|
|
|
|
#define PROCESSOR_LOG(Verbosity, Fmt, ...) UE_VLOG_UELOG(this, LogMass, Verbosity, Fmt, ##__VA_ARGS__)
|
|
|
|
// change to 1 to enable more detailed processing tasks logging
|
|
#if 0
|
|
#define PROCESSOR_TASK_LOG(Fmt, ...) UE_VLOG_UELOG(this, LogMass, Verbose, Fmt, ##__VA_ARGS__)
|
|
#else
|
|
#define PROCESSOR_TASK_LOG(...)
|
|
#endif // 0
|
|
|
|
#else
|
|
#define PROCESSOR_LOG(...)
|
|
#define PROCESSOR_TASK_LOG(...)
|
|
#endif // WITH_MASSENTITY_DEBUG
|
|
|
|
class FMassProcessorTask
|
|
{
|
|
public:
|
|
FMassProcessorTask(const TSharedPtr<FMassEntityManager>& InEntityManager, const FMassExecutionContext& InExecutionContext, UMassProcessor& InProc, bool bInManageCommandBuffer = true)
|
|
: EntityManager(InEntityManager)
|
|
, ExecutionContext(InExecutionContext)
|
|
, Processor(&InProc)
|
|
, bManageCommandBuffer(bInManageCommandBuffer)
|
|
{}
|
|
|
|
static TStatId GetStatId()
|
|
{
|
|
RETURN_QUICK_DECLARE_CYCLE_STAT(FMassProcessorTask, STATGROUP_TaskGraphTasks);
|
|
}
|
|
|
|
static ESubsequentsMode::Type GetSubsequentsMode() { return ESubsequentsMode::TrackSubsequents; }
|
|
|
|
static ENamedThreads::Type GetDesiredThread()
|
|
{
|
|
return ENamedThreads::AnyHiPriThreadHiPriTask;
|
|
}
|
|
|
|
void DoTask(ENamedThreads::Type CurrentThread, const FGraphEventRef& MyCompletionGraphEvent)
|
|
{
|
|
checkf(Processor, TEXT("Expecting a valid processor to execute"));
|
|
|
|
PROCESSOR_TASK_LOG(TEXT("+--+ Task %s started on %u"), *Processor->GetProcessorName(), FPlatformTLS::GetCurrentThreadId());
|
|
SCOPE_CYCLE_COUNTER(STAT_Mass_DoTask);
|
|
SCOPE_CYCLE_COUNTER(STAT_Mass_Total);
|
|
|
|
check(EntityManager);
|
|
FMassEntityManager& EntityManagerRef = *EntityManager.Get();
|
|
FMassEntityManager::FScopedProcessing ProcessingScope = EntityManagerRef.NewProcessingScope();
|
|
|
|
TRACE_CPUPROFILER_EVENT_SCOPE_STR("Mass Processor Task");
|
|
|
|
if (bManageCommandBuffer)
|
|
{
|
|
TSharedPtr<FMassCommandBuffer> MainSharedPtr = ExecutionContext.GetSharedDeferredCommandBuffer();
|
|
ExecutionContext.SetDeferredCommandBuffer(MakeShareable(new FMassCommandBuffer()));
|
|
Processor->CallExecute(EntityManagerRef, ExecutionContext);
|
|
MainSharedPtr->MoveAppend(ExecutionContext.Defer());
|
|
}
|
|
else
|
|
{
|
|
Processor->CallExecute(EntityManagerRef, ExecutionContext);
|
|
}
|
|
PROCESSOR_TASK_LOG(TEXT("+--+ Task %s finished"), *Processor->GetProcessorName());
|
|
}
|
|
|
|
private:
|
|
TSharedPtr<FMassEntityManager> EntityManager;
|
|
FMassExecutionContext ExecutionContext;
|
|
UMassProcessor* Processor = nullptr;
|
|
/**
|
|
* indicates whether this task is responsible for creation of a dedicated command buffer and transferring over the
|
|
* commands after processor's execution;
|
|
*/
|
|
bool bManageCommandBuffer = true;
|
|
};
|
|
|
|
class FMassProcessorsTask_GameThread : public FMassProcessorTask
|
|
{
|
|
public:
|
|
FMassProcessorsTask_GameThread(const TSharedPtr<FMassEntityManager>& InEntityManager, const FMassExecutionContext& InExecutionContext, UMassProcessor& InProc)
|
|
: FMassProcessorTask(InEntityManager, InExecutionContext, InProc)
|
|
{}
|
|
|
|
static ENamedThreads::Type GetDesiredThread()
|
|
{
|
|
return ENamedThreads::GameThread;
|
|
}
|
|
};
|
|
|
|
//----------------------------------------------------------------------//
|
|
// UMassProcessor
|
|
//----------------------------------------------------------------------//
|
|
UMassProcessor::UMassProcessor(const FObjectInitializer& ObjectInitializer)
|
|
: UMassProcessor()
|
|
{
|
|
}
|
|
|
|
UMassProcessor::UMassProcessor()
|
|
: ExecutionFlags((int32)(EProcessorExecutionFlags::Server | EProcessorExecutionFlags::Standalone))
|
|
{
|
|
}
|
|
|
|
void UMassProcessor::Initialize(UObject& Owner)
|
|
{
|
|
bInitialized = true;
|
|
}
|
|
|
|
void UMassProcessor::SetShouldAutoRegisterWithGlobalList(const bool bAutoRegister)
|
|
{
|
|
if (ensureMsgf(HasAnyFlags(RF_ClassDefaultObject), TEXT("Setting bAutoRegisterWithProcessingPhases for non-CDOs has no effect")))
|
|
{
|
|
bAutoRegisterWithProcessingPhases = bAutoRegister;
|
|
#if WITH_EDITOR
|
|
if (UClass* Class = GetClass())
|
|
{
|
|
if (FProperty* AutoRegisterProperty = Class->FindPropertyByName(GET_MEMBER_NAME_CHECKED(UMassProcessor, bAutoRegisterWithProcessingPhases)))
|
|
{
|
|
UpdateSinglePropertyInConfigFile(AutoRegisterProperty, *GetDefaultConfigFilename());
|
|
}
|
|
}
|
|
#endif // WITH_EDITOR
|
|
}
|
|
}
|
|
|
|
void UMassProcessor::GetArchetypesMatchingOwnedQueries(const FMassEntityManager& EntityManager, TArray<FMassArchetypeHandle>& OutArchetype)
|
|
{
|
|
for (FMassEntityQuery* QueryPtr : OwnedQueries)
|
|
{
|
|
CA_ASSUME(QueryPtr);
|
|
QueryPtr->CacheArchetypes(EntityManager);
|
|
|
|
for (const FMassArchetypeHandle& ArchetypeHandle : QueryPtr->GetArchetypes())
|
|
{
|
|
OutArchetype.AddUnique(ArchetypeHandle);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool UMassProcessor::DoesAnyArchetypeMatchOwnedQueries(const FMassEntityManager& EntityManager)
|
|
{
|
|
for (FMassEntityQuery* QueryPtr : OwnedQueries)
|
|
{
|
|
CA_ASSUME(QueryPtr);
|
|
QueryPtr->CacheArchetypes(EntityManager);
|
|
|
|
if (QueryPtr->GetArchetypes().Num() > 0)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void UMassProcessor::PostInitProperties()
|
|
{
|
|
Super::PostInitProperties();
|
|
|
|
// We want even the CDO processors to be fully set up so that we can reason about processors based on CDOs, without
|
|
// needing to instantiate processors based on a given class.
|
|
// Note that we skip abstract processors because we're not going to instantiate them at runtime anyway.
|
|
if (GetClass()->HasAnyClassFlags(CLASS_Abstract) == false)
|
|
{
|
|
ConfigureQueries();
|
|
|
|
bool bNeedsGameThread = ProcessorRequirements.DoesRequireGameThreadExecution();
|
|
for (const FMassEntityQuery* QueryPtr : OwnedQueries)
|
|
{
|
|
CA_ASSUME(QueryPtr);
|
|
bNeedsGameThread = (bNeedsGameThread || QueryPtr->DoesRequireGameThreadExecution());
|
|
}
|
|
|
|
UE_CLOG(bRequiresGameThreadExecution != bNeedsGameThread, LogMass, Verbose
|
|
, TEXT("%s is marked bRequiresGameThreadExecution = %s, while the registered queries' or processor requirements indicate the opposite")
|
|
, *GetProcessorName(), bRequiresGameThreadExecution ? TEXT("TRUE") : TEXT("FALSE"));
|
|
|
|
// better safe than sorry - if queries or processor requirements indicate the game thread execution is required, then we marked the whole processor as such
|
|
bRequiresGameThreadExecution = bRequiresGameThreadExecution || bNeedsGameThread;
|
|
}
|
|
#if CPUPROFILERTRACE_ENABLED
|
|
StatId = GetProcessorName();
|
|
#endif
|
|
}
|
|
|
|
void UMassProcessor::CallExecute(FMassEntityManager& EntityManager, FMassExecutionContext& Context)
|
|
{
|
|
TRACE_CPUPROFILER_EVENT_SCOPE_TEXT(*StatId);
|
|
LLM_SCOPE_BYNAME(TEXT("Mass/ExecuteProcessor"));
|
|
// Not using a more specific scope by default (i.e. LLM_SCOPE_BYNAME(*StatId)) since LLM is more strict regarding the provided string (no spaces or '_')
|
|
|
|
#if WITH_MASSENTITY_DEBUG
|
|
Context.DebugSetExecutionDesc(FString::Printf(TEXT("%s (%s)"), *GetProcessorName(), EntityManager.GetWorld() ? *ToString(EntityManager.GetWorld()->GetNetMode()) : TEXT("No World")));
|
|
#endif
|
|
// CacheSubsystemRequirements will return true only if all requirements declared with ProcessorRequirements are met
|
|
// meaning if it fails there's no point in calling Execute.
|
|
// Note that we're not testing individual queries in OwnedQueries - processors can function just fine with some
|
|
// of their queries not having anything to do.
|
|
if (Context.CacheSubsystemRequirements(ProcessorRequirements))
|
|
{
|
|
Execute(EntityManager, Context);
|
|
}
|
|
else
|
|
{
|
|
PROCESSOR_LOG(VeryVerbose, TEXT("%s Skipping Execute due to subsystem requirements not being met"), *GetProcessorName());
|
|
}
|
|
}
|
|
|
|
bool UMassProcessor::ShouldAllowQueryBasedPruning(const bool bRuntimeMode) const
|
|
{
|
|
return bRuntimeMode;
|
|
}
|
|
|
|
EMassProcessingPhase UMassProcessor::GetProcessingPhase() const
|
|
{
|
|
return ProcessingPhase;
|
|
}
|
|
|
|
void UMassProcessor::SetProcessingPhase(EMassProcessingPhase Phase)
|
|
{
|
|
ProcessingPhase = Phase;
|
|
}
|
|
|
|
void UMassProcessor::ExportRequirements(FMassExecutionRequirements& OutRequirements) const
|
|
{
|
|
for (FMassEntityQuery* Query : OwnedQueries)
|
|
{
|
|
CA_ASSUME(Query);
|
|
Query->ExportRequirements(OutRequirements);
|
|
}
|
|
}
|
|
|
|
void UMassProcessor::RegisterQuery(FMassEntityQuery& Query)
|
|
{
|
|
const uintptr_t ThisStart = (uintptr_t)this;
|
|
const uintptr_t ThisEnd = ThisStart + GetClass()->GetStructureSize();
|
|
const uintptr_t QueryStart = (uintptr_t)&Query;
|
|
const uintptr_t QueryEnd = QueryStart + sizeof(FMassEntityQuery);
|
|
|
|
if (QueryStart >= ThisStart && QueryEnd <= ThisEnd)
|
|
{
|
|
OwnedQueries.AddUnique(&Query);
|
|
}
|
|
else
|
|
{
|
|
static constexpr TCHAR MessageFormat[] = TEXT("Registering entity query for %s while the query is not given processor's member variable. Skipping.");
|
|
checkf(false, MessageFormat, *GetProcessorName());
|
|
UE_LOG(LogMass, Error, MessageFormat, *GetProcessorName());
|
|
}
|
|
}
|
|
|
|
FGraphEventRef UMassProcessor::DispatchProcessorTasks(const TSharedPtr<FMassEntityManager>& EntityManager, FMassExecutionContext& ExecutionContext, const FGraphEventArray& Prerequisites)
|
|
{
|
|
FGraphEventRef ReturnVal;
|
|
if (bRequiresGameThreadExecution)
|
|
{
|
|
ReturnVal = TGraphTask<FMassProcessorsTask_GameThread>::CreateTask(&Prerequisites).ConstructAndDispatchWhenReady(EntityManager, ExecutionContext, *this);
|
|
}
|
|
else
|
|
{
|
|
ReturnVal = TGraphTask<FMassProcessorTask>::CreateTask(&Prerequisites).ConstructAndDispatchWhenReady(EntityManager, ExecutionContext, *this);
|
|
}
|
|
return ReturnVal;
|
|
}
|
|
|
|
FString UMassProcessor::GetProcessorName() const
|
|
{
|
|
return GetName();
|
|
}
|
|
|
|
void UMassProcessor::DebugOutputDescription(FOutputDevice& Ar, int32 Indent) const
|
|
{
|
|
#if WITH_MASSENTITY_DEBUG
|
|
Ar.Logf(TEXT("%*s%s"), Indent, TEXT(""), *GetProcessorName());
|
|
#endif // WITH_MASSENTITY_DEBUG
|
|
}
|
|
|
|
//----------------------------------------------------------------------//
|
|
// UMassCompositeProcessor
|
|
//----------------------------------------------------------------------//
|
|
UMassCompositeProcessor::UMassCompositeProcessor()
|
|
: GroupName(TEXT("None"))
|
|
{
|
|
// not auto-registering composite processors since the idea of the global processors list is to indicate all
|
|
// the processors doing the work while composite processors are just containers. Having said that subclasses
|
|
// can change this behavior if need be.
|
|
bAutoRegisterWithProcessingPhases = false;
|
|
}
|
|
|
|
void UMassCompositeProcessor::SetChildProcessors(TArray<UMassProcessor*>&& InProcessors)
|
|
{
|
|
ChildPipeline.SetProcessors(MoveTemp(InProcessors));
|
|
}
|
|
|
|
void UMassCompositeProcessor::ConfigureQueries()
|
|
{
|
|
// nothing to do here since ConfigureQueries will get automatically called for all the processors in ChildPipeline
|
|
// via their individual PostInitProperties call
|
|
}
|
|
|
|
FGraphEventRef UMassCompositeProcessor::DispatchProcessorTasks(const TSharedPtr<FMassEntityManager>& EntityManager, FMassExecutionContext& ExecutionContext, const FGraphEventArray& InPrerequisites)
|
|
{
|
|
FGraphEventArray Events;
|
|
Events.Reserve(FlatProcessingGraph.Num());
|
|
|
|
for (FDependencyNode& ProcessingNode : FlatProcessingGraph)
|
|
{
|
|
FGraphEventArray Prerequisites;
|
|
for (const int32 DependencyIndex : ProcessingNode.Dependencies)
|
|
{
|
|
Prerequisites.Add(Events[DependencyIndex]);
|
|
}
|
|
|
|
// we don't expect any group nodes at this point. If we get any there's a bug in dependencies solving
|
|
if (ensure(ProcessingNode.Processor))
|
|
{
|
|
Events.Add(ProcessingNode.Processor->DispatchProcessorTasks(EntityManager, ExecutionContext, Prerequisites));
|
|
}
|
|
}
|
|
|
|
|
|
#if WITH_MASSENTITY_DEBUG
|
|
if (UE::Mass::Debug::bLogProcessingGraph)
|
|
{
|
|
for (int i = 0; i < FlatProcessingGraph.Num(); ++i)
|
|
{
|
|
FDependencyNode& ProcessingNode = FlatProcessingGraph[i];
|
|
FString DependenciesDesc;
|
|
for (const int32 DependencyIndex : ProcessingNode.Dependencies)
|
|
{
|
|
DependenciesDesc += FString::Printf(TEXT("%s, "), *FlatProcessingGraph[DependencyIndex].Name.ToString());
|
|
}
|
|
|
|
check(ProcessingNode.Processor);
|
|
PROCESSOR_TASK_LOG(TEXT("Task %u %s%s%s"), Events[i]->GetTraceId(), *ProcessingNode.Processor->GetProcessorName()
|
|
, DependenciesDesc.Len() > 0 ? TEXT(" depends on ") : TEXT(""), *DependenciesDesc);
|
|
}
|
|
}
|
|
#endif // WITH_MASSENTITY_DEBUG
|
|
|
|
FGraphEventRef CompletionEvent = FFunctionGraphTask::CreateAndDispatchWhenReady([this](){}
|
|
, GET_STATID(Mass_GroupCompletedTask), &Events, ENamedThreads::AnyHiPriThreadHiPriTask);
|
|
|
|
return CompletionEvent;
|
|
}
|
|
|
|
void UMassCompositeProcessor::Execute(FMassEntityManager& EntityManager, FMassExecutionContext& Context)
|
|
{
|
|
for (UMassProcessor* Proc : ChildPipeline.GetMutableProcessors())
|
|
{
|
|
check(Proc);
|
|
Proc->CallExecute(EntityManager, Context);
|
|
}
|
|
}
|
|
|
|
void UMassCompositeProcessor::Initialize(UObject& Owner)
|
|
{
|
|
ChildPipeline.Initialize(Owner);
|
|
Super::Initialize(Owner);
|
|
}
|
|
|
|
void UMassCompositeProcessor::SetProcessors(TArrayView<UMassProcessor*> InProcessorInstances, const TSharedPtr<FMassEntityManager>& EntityManager)
|
|
{
|
|
// figure out dependencies
|
|
FMassProcessorDependencySolver Solver(InProcessorInstances);
|
|
TArray<FMassProcessorOrderInfo> SortedProcessors;
|
|
Solver.ResolveDependencies(SortedProcessors, EntityManager);
|
|
|
|
UpdateProcessorsCollection(SortedProcessors);
|
|
|
|
if (Solver.IsSolvingForSingleThread() == false)
|
|
{
|
|
BuildFlatProcessingGraph(SortedProcessors);
|
|
}
|
|
}
|
|
|
|
void UMassCompositeProcessor::BuildFlatProcessingGraph(TConstArrayView<FMassProcessorOrderInfo> SortedProcessors)
|
|
{
|
|
TRACE_CPUPROFILER_EVENT_SCOPE(Mass_BuildFlatProcessingGraph);
|
|
#if !MASS_DO_PARALLEL
|
|
UE_LOG(LogMass, Warning
|
|
, TEXT("MassCompositeProcessor::BuildFlatProcessingGraph is not expected to run in a single-threaded Mass setup. The flat graph will not be used at runtime."));
|
|
#endif // MASS_DO_PARALLEL
|
|
|
|
FlatProcessingGraph.Reset();
|
|
|
|
// this part is creating an ordered, flat list of processors that can be executed in sequence
|
|
// with subsequent task only depending on the elements prior on the list
|
|
TMap<FName, int32> NameToDependencyIndex;
|
|
NameToDependencyIndex.Reserve(SortedProcessors.Num());
|
|
TArray<int32> SuperGroupDependency;
|
|
for (const FMassProcessorOrderInfo& Element : SortedProcessors)
|
|
{
|
|
NameToDependencyIndex.Add(Element.Name, FlatProcessingGraph.Num());
|
|
|
|
// we don't expect to get any "group" nodes here. If it happens it indicates a bug in dependency solving
|
|
checkSlow(Element.Processor);
|
|
FDependencyNode& Node = FlatProcessingGraph.Add_GetRef({ Element.Name, Element.Processor });
|
|
Node.Dependencies.Reserve(Element.Dependencies.Num());
|
|
for (FName DependencyName : Element.Dependencies)
|
|
{
|
|
checkSlow(DependencyName.IsNone() == false);
|
|
Node.Dependencies.Add(NameToDependencyIndex.FindChecked(DependencyName));
|
|
}
|
|
#if WITH_MASSENTITY_DEBUG
|
|
Node.SequenceIndex = Element.SequenceIndex;
|
|
#endif // WITH_MASSENTITY_DEBUG
|
|
}
|
|
|
|
#if WITH_MASSENTITY_DEBUG
|
|
FScopedCategoryAndVerbosityOverride LogOverride(TEXT("LogMass"), ELogVerbosity::Log);
|
|
UE_LOG(LogMass, Log, TEXT("%s flat processing graph:"), *GroupName.ToString());
|
|
|
|
int32 Index = 0;
|
|
for (const FDependencyNode& ProcessingNode : FlatProcessingGraph)
|
|
{
|
|
FString DependenciesDesc;
|
|
for (const int32 DependencyIndex : ProcessingNode.Dependencies)
|
|
{
|
|
DependenciesDesc += FString::Printf(TEXT("%d, "), DependencyIndex);
|
|
}
|
|
if (ProcessingNode.Processor)
|
|
{
|
|
UE_LOG(LogMass, Log, TEXT("[%2d]%*s%s%s%s"), Index, ProcessingNode.SequenceIndex * 2, TEXT(""), *ProcessingNode.Processor->GetProcessorName()
|
|
, DependenciesDesc.Len() > 0 ? TEXT(" depends on ") : TEXT(""), *DependenciesDesc);
|
|
}
|
|
++Index;
|
|
}
|
|
#endif // WITH_MASSENTITY_DEBUG
|
|
}
|
|
|
|
void UMassCompositeProcessor::UpdateProcessorsCollection(TArrayView<FMassProcessorOrderInfo> InOutOrderedProcessors, EProcessorExecutionFlags InWorldExecutionFlags)
|
|
{
|
|
TArray<TObjectPtr<UMassProcessor>> ExistingProcessors(ChildPipeline.GetMutableProcessors());
|
|
ChildPipeline.Reset();
|
|
|
|
const UWorld* World = GetWorld();
|
|
const EProcessorExecutionFlags WorldExecutionFlags = UE::Mass::Utils::DetermineProcessorExecutionFlags(World, InWorldExecutionFlags);
|
|
const FMassProcessingPhaseConfig& PhaseConfig = GET_MASS_CONFIG_VALUE(GetProcessingPhaseConfig(ProcessingPhase));
|
|
|
|
for (FMassProcessorOrderInfo& ProcessorInfo : InOutOrderedProcessors)
|
|
{
|
|
if (ensureMsgf(ProcessorInfo.NodeType == FMassProcessorOrderInfo::EDependencyNodeType::Processor, TEXT("Encountered unexpected FMassProcessorOrderInfo::EDependencyNodeType while populating %s"), *GetGroupName().ToString()))
|
|
{
|
|
checkSlow(ProcessorInfo.Processor);
|
|
if (ProcessorInfo.Processor->ShouldExecute(WorldExecutionFlags))
|
|
{
|
|
// we want to reuse existing processors to maintain state. It's recommended to keep processors state-less
|
|
// but we already have processors that do have some state, like signaling processors.
|
|
// the following search only makes sense for "single instance" processors
|
|
if (ProcessorInfo.Processor->ShouldAllowMultipleInstances() == false)
|
|
{
|
|
TObjectPtr<UMassProcessor>* FoundProcessor = ExistingProcessors.FindByPredicate([ProcessorClass = ProcessorInfo.Processor->GetClass()](TObjectPtr<UMassProcessor>& Element)
|
|
{
|
|
return Element && (Element->GetClass() == ProcessorClass);
|
|
});
|
|
|
|
if (FoundProcessor)
|
|
{
|
|
// overriding the stored value since the InOutOrderedProcessors can get used after the call and it
|
|
// needs to reflect the actual work performed
|
|
ProcessorInfo.Processor = FoundProcessor->Get();
|
|
}
|
|
}
|
|
|
|
CA_ASSUME(ProcessorInfo.Processor);
|
|
ChildPipeline.AppendProcessor(*ProcessorInfo.Processor);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
FString UMassCompositeProcessor::GetProcessorName() const
|
|
{
|
|
return GroupName.ToString();
|
|
}
|
|
|
|
void UMassCompositeProcessor::DebugOutputDescription(FOutputDevice& Ar, int32 Indent) const
|
|
{
|
|
#if WITH_MASSENTITY_DEBUG
|
|
if (ChildPipeline.Num() == 0)
|
|
{
|
|
Ar.Logf(TEXT("%*sGroup %s: []"), Indent, TEXT(""), *GroupName.ToString());
|
|
}
|
|
else
|
|
{
|
|
Ar.Logf(TEXT("%*sGroup %s:"), Indent, TEXT(""), *GroupName.ToString());
|
|
for (UMassProcessor* Proc : ChildPipeline.GetProcessors())
|
|
{
|
|
check(Proc);
|
|
Ar.Logf(TEXT("\n"));
|
|
Proc->DebugOutputDescription(Ar, Indent + 3);
|
|
}
|
|
}
|
|
#endif // WITH_MASSENTITY_DEBUG
|
|
}
|
|
|
|
void UMassCompositeProcessor::SetProcessingPhase(EMassProcessingPhase Phase)
|
|
{
|
|
Super::SetProcessingPhase(Phase);
|
|
for (UMassProcessor* Proc : ChildPipeline.GetMutableProcessors())
|
|
{
|
|
Proc->SetProcessingPhase(Phase);
|
|
}
|
|
}
|
|
|
|
void UMassCompositeProcessor::SetGroupName(FName NewName)
|
|
{
|
|
GroupName = NewName;
|
|
#if CPUPROFILERTRACE_ENABLED
|
|
StatId = GroupName.ToString();
|
|
#endif
|
|
}
|
|
|
|
void UMassCompositeProcessor::AddGroupedProcessor(FName RequestedGroupName, UMassProcessor& Processor)
|
|
{
|
|
if (RequestedGroupName.IsNone() || RequestedGroupName == GroupName)
|
|
{
|
|
ChildPipeline.AppendProcessor(Processor);
|
|
}
|
|
else
|
|
{
|
|
FString RemainingGroupName;
|
|
UMassCompositeProcessor* GroupProcessor = FindOrAddGroupProcessor(RequestedGroupName, &RemainingGroupName);
|
|
check(GroupProcessor);
|
|
GroupProcessor->AddGroupedProcessor(FName(*RemainingGroupName), Processor);
|
|
}
|
|
}
|
|
|
|
UMassCompositeProcessor* UMassCompositeProcessor::FindOrAddGroupProcessor(FName RequestedGroupName, FString* OutRemainingGroupName)
|
|
{
|
|
UMassCompositeProcessor* GroupProcessor = nullptr;
|
|
const FString NameAsString = RequestedGroupName.ToString();
|
|
FString TopGroupName;
|
|
if (NameAsString.Split(TEXT("."), &TopGroupName, OutRemainingGroupName))
|
|
{
|
|
RequestedGroupName = FName(*TopGroupName);
|
|
}
|
|
GroupProcessor = ChildPipeline.FindTopLevelGroupByName(RequestedGroupName);
|
|
|
|
if (GroupProcessor == nullptr)
|
|
{
|
|
check(GetOuter());
|
|
GroupProcessor = NewObject<UMassCompositeProcessor>(GetOuter());
|
|
GroupProcessor->SetGroupName(RequestedGroupName);
|
|
ChildPipeline.AppendProcessor(*GroupProcessor);
|
|
}
|
|
|
|
return GroupProcessor;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// DEPRECATED
|
|
//-----------------------------------------------------------------------------
|
|
void UMassCompositeProcessor::Populate(TConstArrayView<FMassProcessorOrderInfo> OrderedProcessors)
|
|
{
|
|
TArray<FMassProcessorOrderInfo> OrderedProcessorsCopy(OrderedProcessors);
|
|
UpdateProcessorsCollection(OrderedProcessorsCopy, EProcessorExecutionFlags::None);
|
|
}
|