// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "Algo/AllOf.h" #include "Algo/AnyOf.h" #include "Async/TaskGraphInterfaces.h" #include "Containers/Array.h" #include "Containers/ArrayView.h" #include "Containers/BitArray.h" #include "Containers/Map.h" #include "Containers/Set.h" #include "Containers/SparseArray.h" #include "Containers/UnrealString.h" #include "CoreTypes.h" #include "EntitySystem/MovieSceneComponentTypeHandler.h" #include "EntitySystem/MovieSceneComponentTypeInfo.h" #include "EntitySystem/MovieSceneEntityFactoryTypes.h" #include "EntitySystem/MovieSceneEntityIDs.h" #include "EntitySystem/MovieSceneEntitySystemTypes.h" #include "EntitySystem/MovieSceneMaybeAtomic.h" #include "Evaluation/MovieScenePlayback.h" #include "HAL/CriticalSection.h" #include "HAL/PlatformCrt.h" #include "Misc/AssertionMacros.h" #include "Misc/EnumClassFlags.h" #include "Misc/InlineValue.h" #include "MovieSceneSequenceID.h" #include "Templates/UnrealTemplate.h" #include "UObject/StrongObjectPtr.h" #include "UObject/UObjectArray.h" #include class FArchive; class FReferenceCollector; class UObjectBase; struct FGuid; struct FMovieSceneExportedEntity; namespace UE { namespace MovieScene { struct FChildEntityInitializer; struct FComponentRegistry; struct FEntityAllocationIteratorProxy; struct FFreeEntityOperation; struct FMutualEntityInitializer; struct IComponentTypeHandler; struct IMovieSceneEntityMutation; template struct TReadOptional; template struct TWriteOptional; struct IMovieSceneConditionalEntityMutation; enum class EMutuallyInclusiveComponentType : uint8; enum class EEntityRecursion : uint8 { /** Perform the action on this entity (ie, the entity passed in) */ This = 1 << 0, /** Perform the action on this entity's children recursively */ Children = 1 << 1, /** Perform the action on this entity and all its children recursively */ Full = This | Children, }; ENUM_CLASS_FLAGS(EEntityRecursion); /** * Top-level manager class that is responsible for all entity data and interaction/ * * An entity is a stable index into the manager's EntityLocations, which defines the location for that entity's data. This allows the manager to relocate entity data at will without invalidating client held entity IDs. * An entity may contain 0 or more components. Components are concrete pieces of data with a unique type identifier. Additionally, tags can be added to entities or whole batches of entities with zero memory overhead. * * Entity Component data is stored in allocations organized by each unique combination of components. Each component type is stored as a contiguous array within each allocation, with each entity's component being a fixed * offset from the start of each component array. This enables efficient read/write access into specific component arrays and makes issuing parallel tasks that require component data trivial. See FEntityAllocation for * a more detailed explanation of entity component data layout. */ class FEntityManager : public FUObjectArray::FUObjectDeleteListener { public: MOVIESCENE_API FEntityManager(); MOVIESCENE_API ~FEntityManager(); FEntityManager(const FEntityManager&) = delete; void operator=(const FEntityManager&) = delete; FEntityManager(FEntityManager&&) = delete; void operator=(FEntityManager&&) = delete; FComponentRegistry* GetComponents() const { return ComponentRegistry; } void SetComponentRegistry(FComponentRegistry* InComponents) { ComponentRegistry = InComponents; } public: /** * Destroy this entity manager and all the entities and components contained within it, resetting it back to its default state */ MOVIESCENE_API void Destroy(); /** * Allocate a new entity with no components * * @return A stable ID that relates to this entity. Will remain valid until the entity is freed */ MOVIESCENE_API FMovieSceneEntityID AllocateEntity(); /** * Free an entity and relinquish its entity ID * * @param EntityID A valid entity ID to free * @return The number of entities released */ MOVIESCENE_API void FreeEntity(FMovieSceneEntityID EntityID); /** * Free all entities that match the specified filter * * @param Filter A filter that defines the entities to free. Any entity that passes the filter will be destroyed. * @param OutFreedEntities (Optional) A set to populate with all the entities that were freed (including any children) * @return The number of entities released */ MOVIESCENE_API int32 FreeEntities(const FEntityComponentFilter& Filter, TSet* OutFreedEntities = nullptr); /** * Free all entities defined by the specified operation * * @param Operation A pre-populated free entity operation definition * @param OutFreedEntities (Optional) A set to populate with all the entities that were freed (including any children) * @return The number of entities released */ MOVIESCENE_API int32 FreeEntities(const FFreeEntityOperation& Operation, TSet* OutFreedEntities = nullptr); /** * Attempt to allocate a number of entities of the same type contiguously in memory * @note May allocate fewer than the desired number due to allocation capacity constraints. As such, subsequent calls may be required. * * @param EntityComponentMask Defines the components that should exist on the allocated components. Set bits denote allocated components and tags. * @param InOutNum A valid pointer to the desired number to allocate. Is overwritten with the number that were actually allocated. * @return A structure that points to the first entity that was allocated. */ MOVIESCENE_API FEntityDataLocation AllocateContiguousEntities(const FComponentMask& EntityComponentMask, int32* InOutNum); /** * Allocate a single entity with the specified components * * @param EntityComponentMask Defines the components that should exist on the allocated components. Set bits denote allocated components and tags. * @return A structure that points to the allocated entity. */ MOVIESCENE_API FEntityInfo AllocateEntity(const FComponentMask& EntityComponentMask); /** * Retrieve an entity's allocation and component offset from its ID * * @return A structure that points to the allocated entity. */ MOVIESCENE_API FEntityInfo GetEntity(FMovieSceneEntityID EntityID) const; /** * Retrieve a handle to an entity * * @return A handle that points to the allocated entity. */ MOVIESCENE_API FEntityHandle GetEntityHandle(FMovieSceneEntityID EntityID); /** * Check whether an entity is allocated. * @note: does not check serial numbers - should only be used when you are certain that another entity can not have been allocated over the top of the specified entity ID * * @return true if the entity ID is allocated, false otherwise */ bool IsAllocated(FMovieSceneEntityID EntityID) const { return EntityID && EntityLocations.IsValidIndex(EntityID.AsIndex()); } /** * Check whether the specified entity handle is still valid * * @return true if the entity ID is allocated, false otherwise */ MOVIESCENE_API bool IsHandleValid(FEntityHandle EntityID) const; /** * Compute and return this entity manager's threading model. Does not change the current cached threading model. */ MOVIESCENE_API EEntityThreadingModel ComputeThreadingModel() const; /** * Compute and store the current threading model. */ MOVIESCENE_API void UpdateThreadingModel(); /** * Get this entitiy manager's current threading model based on the last time UpdateThreadingModel was called. */ MOVIESCENE_API EEntityThreadingModel GetThreadingModel() const; public: /** * Add the specified component value to an entity * * @param EntityID The ID of the entity to add the component to * @param ComponentTypeID The ID of the component type to add to the entity * @param InValue The value of the component to add */ template void AddComponent(FMovieSceneEntityID EntityID, TComponentTypeID ComponentTypeID, ValueType&& InValue) { AddComponent(EntityID, ComponentTypeID); FEntityInfo Entry = GetEntity(EntityID); if (Entry.Data.Allocation != nullptr) { const FComponentHeader& Header = Entry.Data.Allocation->GetComponentHeaderChecked(ComponentTypeID); FScopedHeaderWriteLock WriteLock(&Header, Entry.Data.Allocation->GetCurrentLockMode(), FEntityAllocationWriteContext::NewAllocation()); T* Component = reinterpret_cast(Header.GetValuePtr(Entry.Data.ComponentOffset)); *Component = Forward(InValue); } } /** * Add the specified component type to an entity. The component value will be default-initialized. * * @param EntityID The ID of the entity to add the component to * @param ComponentTypeID The ID of the component type to add to the entity */ MOVIESCENE_API void AddComponent(FMovieSceneEntityID EntityID, FComponentTypeID ComponentTypeID); MOVIESCENE_API void AddComponent(FMovieSceneEntityID EntityID, FComponentTypeID ComponentTypeID, EEntityRecursion Recursion); /** * Add the specified components to an entity. Existing components will remain unchanged. New component values will be default-initialized. * * @param EntityID The ID of the entity to add the components to * @param EntityComponentMask A mask constituting the components that should be added (set bits indicate components to add) */ MOVIESCENE_API void AddComponents(FMovieSceneEntityID EntityID, const FComponentMask& EntityComponentMask); MOVIESCENE_API void AddComponents(FMovieSceneEntityID EntityID, const FComponentMask& EntityComponentMask, EEntityRecursion Recursion); /** * Remove the specified component type from an entity if it exists. * * @param EntityID The ID of the entity to remove the component from * @param ComponentTypeID The ID of the component type to remove from the entity */ MOVIESCENE_API void RemoveComponent(FMovieSceneEntityID EntityID, FComponentTypeID ComponentTypeID); MOVIESCENE_API void RemoveComponent(FMovieSceneEntityID EntityID, FComponentTypeID ComponentTypeID, EEntityRecursion Recursion); /** * Remove the specified components from an entity, if they exist. * * @param EntityID The ID of the entity to remove the components from * @param ComponentsToRemove A mask constituting the components that should be removed (set bits indicate components to add) */ MOVIESCENE_API void RemoveComponents(FMovieSceneEntityID EntityID, const FComponentMask& ComponentsToRemove); MOVIESCENE_API void RemoveComponents(FMovieSceneEntityID EntityID, const FComponentMask& ComponentsToRemove, EEntityRecursion Recursion); /** * Copy the specified component type from an entity if it exists. * * @param SrcEntityID The ID of the entity to copy from * @param DstEntityID The ID of the entity to copy to * @param ComponentTypeID The ID of the component type to copy. Must exist on SrcEntityID */ MOVIESCENE_API bool CopyComponent(FMovieSceneEntityID SrcEntityID, FMovieSceneEntityID DstEntityID, FComponentTypeID ComponentTypeID); /** * Copy any and all the specified component types from one entity onto another. Missing components on the source entity are handled gracefully. * * @param SrcEntityID The ID of the entity to copy from * @param DstEntityID The ID of the entity to copy to * @param ComponentsToCopy A mask constituting the components that should be copied (set bits indicate components to copy) */ MOVIESCENE_API void CopyComponents(FMovieSceneEntityID SrcEntityID, FMovieSceneEntityID DstEntityID, const FComponentMask& ComponentsToCopy); /** * Check whether the specified component has a component of the specified type * * @param EntityID The ID of the entity to check * @param ComponentTypeID The type of the component that is being tested for */ MOVIESCENE_API bool HasComponent(FMovieSceneEntityID EntityID, FComponentTypeID ComponentTypeID) const; /** * Retrieve the type mask for this component * * @param InEntity The ID of the entity * @return The type mask for this entity, or an empty mask if it has no components */ MOVIESCENE_API const FComponentMask& GetEntityType(FMovieSceneEntityID InEntity) const; /** * Changes the components that exist on an entity to a new mask * * @param InEntity The ID of the entity * @param InNewMask The new mask of the entity that defines which components it should have */ MOVIESCENE_API void ChangeEntityType(FMovieSceneEntityID InEntity, const FComponentMask& InNewMask); /** * Remove all but the specified components from an entity. * * @param EntityID The ID of the entity to remove the components from * @param EntitiesToKeep A mask constituting the components that should be kept on the entity */ MOVIESCENE_API void FilterComponents(FMovieSceneEntityID EntityID, const FComponentMask& EntitiesToKeep); /** * Combine the components from one entity into another, overwriting any pre-existing component values. Does not free any entities. * * @param DestinationEntityID The ID of the entity to add new components to * @param SourceEntityID The ID of the entity to copy components from * @param OptionalMask (Optional) A mask constituting the components that should be copied */ MOVIESCENE_API void CombineComponents(FMovieSceneEntityID DestinationEntityID, FMovieSceneEntityID SourceEntityID, const FComponentMask* OptionalMask = nullptr); /** * Duplicate an entity, by creating an exact copy with a new ID * * @param InOther The ID of the entity to duplicate * @return An ID to a new entity that has exactly the same combination and value of components as InOther */ MOVIESCENE_API FMovieSceneEntityID DuplicateEntity(FMovieSceneEntityID InOther); /** * Duplicate an entity over the top of an existing entity ID * * @param InOutEntity The ID of the entity to overwrite. Doesn't have to be valid. * @param InEntityToDuplicate The ID of the entity to duplicate */ MOVIESCENE_API void OverwriteEntityWithDuplicate(FMovieSceneEntityID& InOutEntity, FMovieSceneEntityID InEntityToDuplicate); /** * Defines a new child initializer that applies only to entities factoried within this entity manager * * @param InInitializer The initializer to insert * @return An index into the array of initializers that should be used for removal */ int32 DefineInstancedChildInitializer(TInlineValue&& InInitializer) { check(InInitializer.IsValid()); return InstancedChildInitializers.Add(MoveTemp(InInitializer)); } /** * Runs all initializers for the specified parent/child allocation */ MOVIESCENE_API void InitializeChildAllocation(const FComponentMask& ParentType, const FComponentMask& ChildType, const FEntityAllocation* ParentAllocation, TArrayView ParentAllocationOffsets, const FEntityRange& InChildEntityRange); /** * Destroy a previously registered instanced child initializer using its index */ void DestroyInstancedChildInitializer(int32 Index) { InstancedChildInitializers.RemoveAt(Index); } /** * Run through all entities in this entity manager, ensuring that all mutual components exist */ MOVIESCENE_API void AddMutualComponents(); /** * Run through all entities in this entity manager, ensuring that all mutual components exist for any component types that match the specified filter */ MOVIESCENE_API void AddMutualComponents(const FEntityComponentFilter& InFilter); public: /** * Attempt to read a component from an entity. * @note this is a general purpose convenience utility which should not be used for high-performance runtime code. See FEntityTaskBuilder. * * @param Entity The ID of the entity to read from * @param ComponentTypeID The component that is to be read * @return A scoped component ptr that points directly to the entity's component, or nullptr if it does not exist */ template TComponentLock> ReadComponent(FMovieSceneEntityID Entity, TComponentTypeID ComponentTypeID) const { check(Entity); if (!ComponentTypeID) { return TComponentLock>(); } FEntityLocation Location = EntityLocations[Entity.AsIndex()]; if (!Location.IsValid() || !EntityAllocationMasks[Location.GetAllocationIndex()].Contains(ComponentTypeID)) { return TComponentLock>(); } FEntityAllocation* Allocation = EntityAllocations[Location.GetAllocationIndex()]; const int32 ComponentOffset = Location.GetEntryIndexWithinAllocation(); EComponentHeaderLockMode LockMode = GetThreadingModel() == EEntityThreadingModel::NoThreading ? EComponentHeaderLockMode::LockFree : EComponentHeaderLockMode::Mutex; for (FComponentHeader& Header : Allocation->GetComponentHeaders()) { if (Header.ComponentType == ComponentTypeID) { return TComponentLock>(&Header, LockMode, ComponentOffset); } } return TComponentLock>(); } /** * Read a component value from an entity that is known to exist. * @note this is a general purpose convenience utility which should not be used for high-performance runtime code. See FEntityTaskBuilder. * * @param Entity The ID of the entity to read from * @param ComponentTypeID The component that is to be read * @return The component value */ template T ReadComponentChecked(FMovieSceneEntityID Entity, TComponentTypeID ComponentTypeID) const { TComponentLock> Value = ReadComponent(Entity, ComponentTypeID); check(Value); return *Value; } /** * Attempt to write to an entity's component. * @note this is a general purpose convenience utility which should not be used for high-performance runtime code. See FEntityTaskBuilder. * * @param Entity The ID of the entity to read from * @param ComponentTypeID The component that is to be read * @return A scoped component ptr that points directly to the entity's component, or nullptr if it does not exist */ template TComponentLock> WriteComponent(FMovieSceneEntityID Entity, TComponentTypeID ComponentTypeID) { check(Entity); if (!ComponentTypeID) { return TComponentLock>(); } FEntityLocation Location = EntityLocations[Entity.AsIndex()]; if (!Location.IsValid() || !EntityAllocationMasks[Location.GetAllocationIndex()].Contains(ComponentTypeID)) { return TComponentLock>(); } FEntityAllocation* Allocation = EntityAllocations[Location.GetAllocationIndex()]; const int32 ComponentOffset = Location.GetEntryIndexWithinAllocation(); EComponentHeaderLockMode LockMode = GetThreadingModel() == EEntityThreadingModel::NoThreading ? EComponentHeaderLockMode::LockFree : EComponentHeaderLockMode::Mutex; for (FComponentHeader& Header : Allocation->GetComponentHeaders()) { if (Header.ComponentType == ComponentTypeID) { return TComponentLock>(&Header, LockMode, FEntityAllocationWriteContext(*this), ComponentOffset); } } return TComponentLock>(); } /** * Write a component value that is known to exist to an entity. * @note this is a general purpose convenience utility which should not be used for high-performance runtime code. See FEntityTaskBuilder. * * @param Entity The ID of the entity to read from * @param ComponentTypeID The component that is to be read * @param Value The value to write */ template void WriteComponentChecked(FMovieSceneEntityID Entity, TComponentTypeID ComponentTypeID, ValueType&& Value) { TComponentLock> ComponentPtr = WriteComponent(Entity, ComponentTypeID); check(ComponentPtr); *ComponentPtr = Forward(Value); } public: void SetGatherThread(ENamedThreads::Type InGatherThread) { GatherThread = InGatherThread; } ENamedThreads::Type GetGatherThread() const { return GatherThread; } void SetDispatchThread(ENamedThreads::Type InDispatchThread) { DispatchThread = InDispatchThread; } ENamedThreads::Type GetDispatchThread() const { return DispatchThread; } /** * Goes through all entity data and compacts like-for-like allocations into as few allocations as possible, resulting in the optimal data layout */ MOVIESCENE_API void Compact(); /** * Efficiently mutate all entities that match a filter. Mutations can add or remove components from batches of entity data. * * @param Filter The filter to match entity allocations against. Only entities that match the filter will be mutated * @param Mutation Implementation that defines how to mutate the entities that match the filter * @return The number of entities that were mutated, or 0 if none were matched */ MOVIESCENE_API int32 MutateAll(const FEntityComponentFilter& Filter, const IMovieSceneEntityMutation& Mutation, EMutuallyInclusiveComponentType MutualTypes = EMutuallyInclusiveComponentType::Mandatory); /** * Efficiently mutate all entities that match a filter. Mutations can add or remove components from batches of entity data. * * @param Filter The filter to match entity allocations against. Only entities that match the filter will be mutated * @param Mutation Implementation that defines how to mutate the entities that match the filter * @return The number of entities that were mutated, or 0 if none were matched */ MOVIESCENE_API int32 MutateConditional(const FEntityComponentFilter& Filter, const IMovieSceneConditionalEntityMutation& Mutation, EMutuallyInclusiveComponentType MutualTypes = EMutuallyInclusiveComponentType::Mandatory); /** * Touch the specified entity, cause the allocation and component serial numbers to be incremented. Will invalidate any transient caches maintained for serial such numbers. * @note Allocation and component serial numbers relate to the entire allocation of entities that this entity resides within. As such, it will invalidate downstream caches * for any cache that relates to the entire allocation * * @param EntityID The ID of the entity to touch. */ MOVIESCENE_API void TouchEntity(FMovieSceneEntityID EntityID); /** * Set up an entity to be a child of another. Child entities will only be cleaned up if their parents are marked Unlink, and TagOrphanedChildren is called * * @param ParentID The ID of the parent * @param ChildID The ID of the child */ MOVIESCENE_API void AddChild(FMovieSceneEntityID ParentID, FMovieSceneEntityID ChildID); /** * Retrieve the immediate children of the specified entity * @note: the array will not be emptied by this function - that is the responsibility of the callee, if desired. * * @param ParentID The ID of the parent * @param OutChildren (out) Array to populate with child entities. */ MOVIESCENE_API void GetImmediateChildren(FMovieSceneEntityID ParentID, TArray& OutChildren) const; /** * Retrieve all children, grandchildren etc of the specified entity using a parent first traversal * @note: the array will not be emptied by this function - that is the responsibility of the callee, if desired. * * @param ParentID The ID of the parent * @param OutChildren (out) Array to populate with child entities. */ MOVIESCENE_API void GetChildren_ParentFirst(FMovieSceneEntityID ParentID, TArray& OutChildren) const; /** * Itereate the immediate children of the specified entity * @note: the array will not be emptied by this function - that is the responsibility of the callee, if desired. * * @param ParentID The ID of the parent * @param OutChildren (out) Array to populate with child entities. */ template void IterateImmediateChildren(FMovieSceneEntityID ParentID, IteratorType&& Iterator) const { for (auto ChildIt = ParentToChild.CreateConstKeyIterator(ParentID); ChildIt; ++ChildIt) { Iterator(ChildIt.Value()); } } /** * Iterate all children, grandchildren etc of the specified entity using a parent first traversal * * @param ParentID The ID of the parent * @param OutChildren (out) Array to populate with child entities. */ template void IterateChildren_ParentFirst(FMovieSceneEntityID ParentID, IteratorType&& Iterator) const { for (auto ChildIt = ParentToChild.CreateConstKeyIterator(ParentID); ChildIt; ++ChildIt) { Iterator(ChildIt.Value()); IterateChildren_ParentFirst(ChildIt.Value(), Iterator); } } public: /** * Run a serialization routine over the specified entity to approximate the memory it is using * @note Does not consider per-allocation overhead such as header sizes and other per-allocation meta-data * * @param Ar The archive to save to. Ar.IsCountingMemory() must be true. * @param EntityID Identifier for the entity to replace object references within */ MOVIESCENE_API void CountMemory(FArchive& Ar, FMovieSceneEntityID EntityID); /** * Find a component type hander from its registered GUID */ static MOVIESCENE_API IComponentTypeHandler* FindComponentTypeHandler(const FGuid& ComponentGuid); public: /** * Iterate through all the allocations that match the specified component filter * * @param InFilter (required, non-null) The filter to match allocations against. Filter is copied into the iterator. * @return An iterator object that walks over all allocations matching the filter. */ MOVIESCENE_API FEntityAllocationIteratorProxy Iterate(const FEntityComponentFilter* InFilter) const; /** * Efficiently test whether this entity manager contains any allocations that match the specified filter * * @param InFilter The filter to match allocations against. * @return True if the entity manager contains any allocations that match the filter, false otherwise */ MOVIESCENE_API bool Contains(const FEntityComponentFilter& InFilter) const; /** * Check whether any entity in this manager has the specified component * * @param ComponentTypeID The type of the component that is being tested for * @return true if the specified component exists anywhere, false otherwise */ bool ContainsComponent(FComponentTypeID ComponentTypeID) const { return GetAccumulatedMask().Contains(ComponentTypeID); } /** * Check whether any entity in this manager has any of the specified components * * @param ComponentTypeIDs The types of the component that are being tested for * @return true if any one of the specified components exist anywhere in this manager, false otherwise */ bool ContainsAnyComponent(std::initializer_list ComponentTypeIDs) const { const FComponentMask& Mask = GetAccumulatedMask(); return Algo::AnyOf(ComponentTypeIDs, [&Mask](FComponentTypeID In){ return Mask.Contains(In); }); } /** * Check whether any entity in this manager has any of the specified components * * @param ComponentTypeIDs The types of the component that are being tested for * @return true if any one of the specified components exist anywhere in this manager, false otherwise */ bool ContainsAnyComponent(const FComponentMask& ComponentTypeIDs) const { const FComponentMask& Mask = GetAccumulatedMask(); return FComponentMask::BitwiseAND(Mask, ComponentTypeIDs, EBitwiseOperatorFlags::MinSize).First() != FComponentTypeID::Invalid(); } /** * Check whether all of the specified components exist anywhere in this entity manager * @note: Does not check whether the components exist on the same entity, just that they are present in the manager. Use Contains for a thorough filter match. * * @param ComponentTypeIDs The types of the components that are being tested for * @return true if each of the specified components exist anywhere in this manager, false otherwise */ bool ContainsAllComponents(std::initializer_list ComponentTypeIDs) const { const FComponentMask& Mask = GetAccumulatedMask(); return Algo::AllOf(ComponentTypeIDs, [&Mask](FComponentTypeID In){ return Mask.Contains(In); }); } /** * Accumulate a mask from all entity types that match the specified filter * * @param InFilter The filter to match allocations against. Filter is copied into the iterator. * @param OutMask The mask to receive the binary OR accumulation of all entities that pass the filter */ MOVIESCENE_API void AccumulateMask(const FEntityComponentFilter& InFilter, FComponentMask& OutMask) const; /** * Retrieve an up-to-date accumulation of all components present on entities in this manager */ MOVIESCENE_API const FComponentMask& GetAccumulatedMask() const; /** * Efficiently test whether this entity manager contains any allocations that match the specified filter * * @param InFilter The filter to match allocations against. * @return True if the entity manager contains any allocations that match the filter, false otherwise */ MOVIESCENE_API void EnterIteration() const; MOVIESCENE_API void ExitIteration() const; void CheckCanChangeStructure() const { checkf(IterationCount.Load(ThreadingModel) == 0, TEXT("Mutation of entities is not permissible while entities are being iterated")); checkf(LockdownState == ELockdownState::Unlocked, TEXT("Structural changes to the entity manager are not permitted while it is locked down")); } bool IsLockedDown() const { return LockdownState == ELockdownState::Locked; } void LockDown() { LockdownState = ELockdownState::Locked; } void ReleaseLockDown() { LockdownState = ELockdownState::Unlocked; } /** * Retrieve the entity filter that should be used for any entity iteration. Can be used to constrain all iterations to specific types */ const FEntityComponentFilter& GetGlobalIterationFilter() const { return GlobalIterationFilter; } /** * Modify the entity filter that should be used for any entity iteration. * @note: Care should be take to reset this back to its original state when done, * as leaving this populated during the instantiation phase can result in * undefined results. */ FEntityComponentFilter& ModifyGlobalIterationFilter() { ensureMsgf(!IsLockedDown() && IterationCount.Load(ThreadingModel) == 0, TEXT("Manipulating the global iteration filter while locked down or iterating is not recommended")); return GlobalIterationFilter; } /** * Increment the current serial number for systems observing this manager. Should be called after any system is run */ void IncrementSystemSerial(uint64 IncAmount = 1) { SystemSerialNumber += IncAmount; } /** * Get the current serial number for any system ovserving this entity manager. This serial number acts as a timestamp for * the current state of this entity manager - any serial number more recent than this dictates more recent logic or state. */ uint64 GetSystemSerial() const { return SystemSerialNumber; } /** * Check whether the structure of this entity manager has changed at all since the specified serial number */ bool HasStructureChangedSince(uint64 CachedSerial) const { return CachedSerial == 0 || StructureMutationSystemSerialNumber > CachedSerial; } /** * Called in order to mimic the entity structure changing, even if it has not */ void MimicStructureChanged() { OnStructureChanged(); } public: /** * Set a debug name for this entity manager */ void SetDebugName(FString&& InNewDebugName) { ManagerDebugName = MoveTemp(InNewDebugName); } /** * Explicitly add referenced objects from an external source. This is not called by default for the global entity manager because each entity owner should do so to avoid cyclic dependencies. */ MOVIESCENE_API void AddReferencedObjects(FReferenceCollector& ReferenceCollector); /** * Replace an entity ID with the components from another, discarding the provided entity ID in the process * * @param InOutEntity The entity ID to be reassigned. If it is already valid, the existing entity will be freed * @param EntityToDiscard The entity containing the components to replace InOutEntity with. This Entity ID will be invalid after this function call. */ MOVIESCENE_API void ReplaceEntityID(FMovieSceneEntityID& InOutEntity, FMovieSceneEntityID EntityToDiscard); uint32 GetHandleGeneration() const { if (bHandleGenerationStale) { ++CurrentHandleGeneration; } bHandleGenerationStale = false; return CurrentHandleGeneration; } private: friend struct FEntityInitializer; enum class EMemoryType { Uninitialized, DefaultConstructed }; MOVIESCENE_API void OnStructureChanged(); MOVIESCENE_API FEntityAllocation* CreateEntityAllocation(const FComponentMask& EntityComponentMask, uint16 InitialCapacity, uint16 MaxCapacity, FEntityAllocation* MigrateComponentDataFrom = nullptr); MOVIESCENE_API int32 CreateEntityAllocationEntry(const FComponentMask& EntityComponentMask, uint16 InitialCapacity, uint16 MaxCapacity); MOVIESCENE_API int32 GetOrCreateAllocationWithSlack(const FComponentMask& EntityComponentMask, int32* InOutDesiredSlack = nullptr); MOVIESCENE_API int32 CreateAllocationWithSlack(const FComponentMask& EntityComponentMask, int32* InOutDesiredSlack = nullptr); MOVIESCENE_API int32 MigrateEntity(int32 DestIndex, int32 SourceIndex, int32 SourceEntryIndexWithinAllocation); MOVIESCENE_API void CopyComponents(int32 DestAllocationIndex, int32 DestEntityIndex, int32 SourceAllocationIndex, int32 SourceEntityIndex, const FComponentMask* OptionalMask = nullptr); MOVIESCENE_API int32 AddEntityToAllocation(int32 AllocationIndex, FMovieSceneEntityID ID, EMemoryType MemoryType = EMemoryType::DefaultConstructed); MOVIESCENE_API void RemoveEntityFromAllocation(int32 AllocationIndex, int32 SourceEntryIndexWithinAllocation); MOVIESCENE_API FEntityAllocation* MigrateAllocation(int32 AllocationIndex, const FComponentMask& NewComponentMask); MOVIESCENE_API void CombineAllocations(int32 DestinationIndex, int32 SourceIndex); FEntityAllocation* GetAllocation(int32 AllocationIndex) const { return EntityAllocations[AllocationIndex]; } MOVIESCENE_API int32 ReserveAllocation(int32 AllocationIndex, int32 NumToReserve); MOVIESCENE_API FEntityAllocation* GrowAllocation(int32 AllocationIndex, int32 MinNumToGrowBy = 1); MOVIESCENE_API void DestroyAllocation(FEntityAllocation* Allocation, bool bDestructComponentData = true); MOVIESCENE_API virtual void NotifyUObjectDeleted(const UObjectBase* Object, int32 Index) override; MOVIESCENE_API virtual void OnUObjectArrayShutdown() override; MOVIESCENE_API virtual FString GetReferencerName() const; MOVIESCENE_API void CheckInvariants(); virtual SIZE_T GetAllocatedSize() const override { return 0; } friend struct FEntityAllocationProxy; friend struct FEntityAllocationIterator; friend struct FEntityAllocationIteratorProxy; friend FFreeEntityOperation; struct FEntityLocation { FEntityLocation() : AllocationIndex(INVALID) , EntryIndexWithinAllocation(0) {} void Reset() { AllocationIndex = INVALID; EntryIndexWithinAllocation = 0; } void Set(int32 InAllocationIndex, int32 InEntryIndexWithinAllocation) { check((InAllocationIndex & 0xFFFF0000) == 0 && (InEntryIndexWithinAllocation & 0xFFFF0000) == 0); AllocationIndex = (uint16)InAllocationIndex; EntryIndexWithinAllocation = (uint16)InEntryIndexWithinAllocation; } int32 GetAllocationIndex() const { return AllocationIndex; } int32 GetEntryIndexWithinAllocation() const { return EntryIndexWithinAllocation; } bool IsValid() const { return AllocationIndex != INVALID; } void SetParentID(FMovieSceneEntityID InParentID) { ParentID = InParentID; } FMovieSceneEntityID GetParentID() const { return ParentID; } private: static const uint16 INVALID = ~uint16(0); uint16 AllocationIndex; uint16 EntryIndexWithinAllocation; FMovieSceneEntityID ParentID; }; /** Sparse array of masks that define each index within EntityAllocations' component types */ TSparseArray EntityAllocationMasks; /** Bit mask of allocations that currently have capacity */ TBitArray<> AllocationsWithCapacity; /** Sparse array of entity allocation pointers. Allocations are heap allocated. */ TSparseArray EntityAllocations; /** Sparse array of entity location information for each allocated entity ID. */ TSparseArray EntityLocations; /** Map of parent -> child entities */ TMultiMap ParentToChild; TSparseArray> InstancedChildInitializers; /** Map of entity ID to the generation its handle was created in. FEntityHandle::HandleGeneration matches the value if it is still valid */ TMap EntityGenerationMap; FComponentMask AccumulatedMask; FEntityComponentFilter GlobalIterationFilter; FComponentRegistry* ComponentRegistry; /** Debug name for this entity manager */ FString ManagerDebugName; /** System serial number incremented for every run of any system */ uint64 SystemSerialNumber; /** The value of this manager's SystemSerialNumber the last time any entities were allocated, freed, or mutated in some way (this does not include component values being written to) */ uint64 StructureMutationSystemSerialNumber; /** Debugging ptr for natvis */ #if UE_MOVIESCENE_ENTITY_DEBUG const bool* RichComponentDebuggingPtr; #endif /** Serially incrementing unique identifier that is assigned to each new entity allocation that is created */ uint32 NextAllocationID; /** The current generation of entities - incremented any time an entity is destroyed */ mutable uint32 CurrentHandleGeneration; mutable bool bHandleGenerationStale; mutable bool bAccumulatedMaskStale; /** Atomic counter that is incremented when an iteration begins, and decremented when it finishes */ mutable FEntitySystemMaybeAtomicInt32 IterationCount; ENamedThreads::Type GatherThread; ENamedThreads::Type DispatchThread; EEntityThreadingModel ThreadingModel; enum class ELockdownState { Locked, Unlocked }; ELockdownState LockdownState; }; struct FFreeEntityOperation { FFreeEntityOperation(FEntityManager* InEntityManager) : EntityManager(InEntityManager) {} void MarkAllocationForFree(int32 AllocationIndex); void MarkEntityForFree(FMovieSceneEntityID EntityID); private: struct FAllocationMask { TBitArray<> Mask; bool bDestroyAllocation = false; }; struct FCommitData { /** Contains specific entities to destroy from allocations that are not to be destroyed completely */ TMap AllocationsToEntities; /** Additional array of entitiy IDs that need destroying despite not belonging to an allocation */ TArray EmptyEntities; }; FCommitData Commit() const; friend FEntityManager; /** */ TArray AllocationsToDestroy; /** */ TArray LooseEntitiesToDestroy; FEntityManager* EntityManager; }; extern MOVIESCENE_API FEntityManager* GEntityManagerForDebuggingVisualizers; } // namespace MovieScene } // namespace UE