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

283 lines
10 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "MassExecutionContext.h"
#include "MassArchetypeData.h"
#include "MassEntityManager.h"
#if WITH_MASSENTITY_DEBUG
#include "MassDebugger.h"
#endif // WITH_MASSENTITY_DEBUG
#include "MassTestableEnsures.h"
//-----------------------------------------------------------------------------
// FMassExecutionContext
//-----------------------------------------------------------------------------
FMassExecutionContext::FMassExecutionContext(FMassEntityManager& InEntityManager, const float InDeltaTimeSeconds, const bool bInFlushDeferredCommands)
: SubsystemAccess(InEntityManager.GetWorld())
, DeltaTimeSeconds(InDeltaTimeSeconds)
, EntityManager(InEntityManager.AsShared())
, bFlushDeferredCommands(bInFlushDeferredCommands)
{
}
FMassExecutionContext::FMassExecutionContext(const FMassExecutionContext& Other)
: EntityManager(Other.EntityManager)
{
// we're using assignment operator here as a setup helper, as per operator=='s comment.
*this = Other;
QueriesStack.Reset();
}
FMassExecutionContext::FMassExecutionContext(const FMassExecutionContext& Other, FMassEntityQuery& Query, const TSharedPtr<FMassCommandBuffer>& InCommandBuffer)
: FMassExecutionContext(Other)
{
ensureMsgf(Other.QueriesStack.Last().Query == &Query, TEXT("Creating a single-query execution context but the Query doesn't match the source context."));
QueriesStack.Add(Other.QueriesStack.Last());
SetDeferredCommandBuffer(InCommandBuffer);
}
FMassExecutionContext::~FMassExecutionContext()
{
ensureMsgf(QueriesStack.Num() == 0, TEXT("Destroying a FMassExecutionContext instance while not all queries have been popped is unexpected."));
}
void FMassExecutionContext::FlushDeferred()
{
if (bFlushDeferredCommands && DeferredCommandBuffer)
{
EntityManager->FlushCommands(DeferredCommandBuffer);
}
}
void FMassExecutionContext::ClearExecutionData()
{
FragmentViews.Reset();
ChunkFragmentViews.Reset();
ConstSharedFragmentViews.Reset();
SharedFragmentViews.Reset();
CurrentArchetypeCompositionDescriptor.Reset();
EntityListView = {};
ChunkSerialModificationNumber = -1;
#if WITH_MASSENTITY_DEBUG
DebugColor = FColor();
#endif // WITH_MASSENTITY_DEBUG
}
bool FMassExecutionContext::CacheSubsystemRequirements(const FMassSubsystemRequirements& SubsystemRequirements)
{
return SubsystemAccess.CacheSubsystemRequirements(SubsystemRequirements);
}
void FMassExecutionContext::SetEntityCollection(const FMassArchetypeEntityCollection& InEntityCollection)
{
check(EntityCollection.IsEmpty());
EntityCollection = InEntityCollection;
}
void FMassExecutionContext::SetEntityCollection(FMassArchetypeEntityCollection&& InEntityCollection)
{
check(EntityCollection.IsEmpty());
check(InEntityCollection.IsUpToDate());
EntityCollection = MoveTemp(InEntityCollection);
}
void FMassExecutionContext::SetFragmentRequirements(const FMassFragmentRequirements& FragmentRequirements)
{
FragmentViews.Reset();
for (const FMassFragmentRequirementDescription& Requirement : FragmentRequirements.GetFragmentRequirements())
{
if (Requirement.RequiresBinding())
{
FragmentViews.Emplace(Requirement);
}
}
ChunkFragmentViews.Reset();
for (const FMassFragmentRequirementDescription& Requirement : FragmentRequirements.GetChunkFragmentRequirements())
{
if (Requirement.RequiresBinding())
{
ChunkFragmentViews.Emplace(Requirement);
}
}
ConstSharedFragmentViews.Reset();
for (const FMassFragmentRequirementDescription& Requirement : FragmentRequirements.GetConstSharedFragmentRequirements())
{
if (Requirement.RequiresBinding())
{
ConstSharedFragmentViews.Emplace(Requirement);
}
}
SharedFragmentViews.Reset();
for (const FMassFragmentRequirementDescription& Requirement : FragmentRequirements.GetSharedFragmentRequirements())
{
if (Requirement.RequiresBinding())
{
SharedFragmentViews.Emplace(Requirement);
}
}
}
UWorld* FMassExecutionContext::GetWorld()
{
return EntityManager->GetWorld();
}
void FMassExecutionContext::PushQuery(FMassEntityQuery& InQuery)
{
FQueryTransientRuntime& RuntimeData = QueriesStack.Add_GetRef({&InQuery});
GetSubsystemRequirementBits(RuntimeData.ConstSubsystemsBitSet, RuntimeData.MutableSubsystemsBitSet);
#if WITH_MASSENTITY_DEBUG
// check if this could possibly trigger a break before iterating to avoid extraneous breakpoint checks
FMassEntityManager& EntityManagerRef = GetEntityManagerChecked();
RuntimeData.bCheckProcessorBreaks = FMassDebugger::HasAnyProcessorBreakpoints(EntityManagerRef, DebugGetProcessor());
if (UNLIKELY(FMassDebugger::HasAnyFragmentWriteBreakpoints(EntityManagerRef)))
{
auto CheckFragmentRequirement = [&RuntimeData, &EntityManagerRef](TConstArrayView<FMassFragmentRequirementDescription> Requirements) -> void
{
for (const FMassFragmentRequirementDescription& Req : Requirements)
{
if (Req.AccessMode == EMassFragmentAccess::ReadWrite &&
FMassDebugger::HasAnyFragmentWriteBreakpoints(EntityManagerRef, Req.StructType))
{
if (ensureMsgf(RuntimeData.BreakFragmentsCount < FQueryTransientRuntime::MaxFragmentBreakpointCount,
TEXT("Fragment write breakpoint count limit exceeded for this query.")))
{
RuntimeData.FragmentTypesToBreakOn[RuntimeData.BreakFragmentsCount++] = Req.StructType;
}
}
}
};
// don't need to check ConstSharedFragmentRequirements because those can't write
CheckFragmentRequirement(InQuery.GetFragmentRequirements());
CheckFragmentRequirement(InQuery.GetChunkFragmentRequirements());
CheckFragmentRequirement(InQuery.GetSharedFragmentRequirements());
}
#endif // WITH_MASSENTITY_DEBUG
RuntimeData.IteratorSerialNumber = ++IteratorSerialNumberGenerator;
}
void FMassExecutionContext::PopQuery(const FMassEntityQuery& InQuery)
{
const FQueryTransientRuntime& RuntimeData = QueriesStack.Last();
checkf(&InQuery == RuntimeData.Query, TEXT("Queries are stored in a stack and as such it requires elements to be added in LIFO order"));
SetSubsystemRequirementBits(RuntimeData.ConstSubsystemsBitSet, RuntimeData.MutableSubsystemsBitSet);
QueriesStack.RemoveAt(QueriesStack.Num() - 1, EAllowShrinking::No);
}
FMassExecutionContext::FEntityIterator FMassExecutionContext::CreateEntityIterator()
{
if (!testableEnsureMsgf(QueriesStack.Num(), TEXT("Attempting to create an Entity Iterator when no entity query is being executed.")))
{
return FEntityIterator(*this);
}
return FEntityIterator(*this, QueriesStack.Last());
}
FMassExecutionContext& FMassExecutionContext::GetDummyInstance()
{
static FMassExecutionContext DummyContext(*TSharedRef<FMassEntityManager>(MakeShareable(new FMassEntityManager())));
return DummyContext;
}
//-----------------------------------------------------------------------------
// FMassExecutionContext::FQueryTransientRuntime
//-----------------------------------------------------------------------------
FMassExecutionContext::FQueryTransientRuntime& FMassExecutionContext::FQueryTransientRuntime::GetDummyInstance()
{
static FMassEntityQuery DummyQuery;
static FQueryTransientRuntime DummyInstance = { &DummyQuery };
return DummyInstance;
}
//-----------------------------------------------------------------------------
// FMassExecutionContext::FEntityIterator
//-----------------------------------------------------------------------------
FMassExecutionContext::FEntityIterator::FEntityIterator()
: ExecutionContext(FMassExecutionContext::GetDummyInstance())
, QueryRuntime(FQueryTransientRuntime::GetDummyInstance())
{
}
FMassExecutionContext::FEntityIterator::FEntityIterator(FMassExecutionContext& InExecutionContext)
: ExecutionContext(InExecutionContext)
, QueryRuntime(FQueryTransientRuntime::GetDummyInstance())
{
}
FMassExecutionContext::FEntityIterator::FEntityIterator(FMassExecutionContext& InExecutionContext, FQueryTransientRuntime& InQueryRuntime)
: ExecutionContext(InExecutionContext)
, QueryRuntime(InQueryRuntime)
, NumEntities(InExecutionContext.GetNumEntities())
, SerialNumber(InQueryRuntime.IteratorSerialNumber)
{
this->operator++();
}
#if WITH_MASSENTITY_DEBUG
UE_DISABLE_OPTIMIZATION_SHIP
void FMassExecutionContext::FEntityIterator::TestBreakpoints()
{
FMassEntityManager& EntityManagerRef = ExecutionContext.GetEntityManagerChecked();
FMassEntityHandle Entity = ExecutionContext.GetEntity(EntityIndex);
if (QueryRuntime.bCheckProcessorBreaks && FMassDebugger::ShouldProcessorBreak(EntityManagerRef, ExecutionContext.DebugGetProcessor(), Entity))
{
bool bDisableThisBreakpoint = false;
//====================================================================
//= A breakpoint for this entity set in the MassDebugger has triggered
//= Step out of this function to debug the actual code being run for the entity
//=
//= To disable this specific breakpoint use the Watch window do set
//= bDisableThisBreakpoint to `true` or 1
//====================================================================
UE_DEBUG_BREAK();
if (bDisableThisBreakpoint)
{
FMassDebugger::ClearProcessorBreakpoint(EntityManagerRef, ExecutionContext.DebugGetProcessor(), Entity);
}
// bailing out, no point to hit multiple breakpoints for the given entity/processor pair
return;
}
for (const UScriptStruct* Fragment : QueryRuntime.FragmentTypesToBreakOn)
{
if (FMassDebugger::ShouldBreakOnFragmentWrite(EntityManagerRef, Fragment, Entity))
{
bool bDisableThisBreakpoint = false;
//====================================================================
//= A breakpoint for this entity set in the MassDebugger has triggered
//= Step out of this function to debug the actual code being run for the entity
//
//= To disable this specific breakpoint use the Watch window do set
//= bDisableThisBreakpoint to `true` or 1
//====================================================================
UE_DEBUG_BREAK();
if (bDisableThisBreakpoint)
{
FMassDebugger::ClearFragmentWriteBreak(EntityManagerRef, Fragment, Entity);
}
// bailing out, no point to hit multiple breakpoints for the given entity/processor pair
return;
}
}
}
UE_ENABLE_OPTIMIZATION_SHIP
#endif // WITH_MASSENTITY_DEBUG