1088 lines
39 KiB
C++
1088 lines
39 KiB
C++
// 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 <initializer_list>
|
|
|
|
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 <typename T> struct TReadOptional;
|
|
template <typename T> 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<FMovieSceneEntityID>* 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<FMovieSceneEntityID>* 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<typename T, typename ValueType>
|
|
void AddComponent(FMovieSceneEntityID EntityID, TComponentTypeID<T> 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<T*>(Header.GetValuePtr(Entry.Data.ComponentOffset));
|
|
*Component = Forward<ValueType>(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<FChildEntityInitializer>&& 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<const int32> 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<typename T>
|
|
TComponentLock<TReadOptional<T>> ReadComponent(FMovieSceneEntityID Entity, TComponentTypeID<T> ComponentTypeID) const
|
|
{
|
|
check(Entity);
|
|
|
|
if (!ComponentTypeID)
|
|
{
|
|
return TComponentLock<TReadOptional<T>>();
|
|
}
|
|
|
|
FEntityLocation Location = EntityLocations[Entity.AsIndex()];
|
|
if (!Location.IsValid() || !EntityAllocationMasks[Location.GetAllocationIndex()].Contains(ComponentTypeID))
|
|
{
|
|
return TComponentLock<TReadOptional<T>>();
|
|
}
|
|
|
|
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<TReadOptional<T>>(&Header, LockMode, ComponentOffset);
|
|
}
|
|
}
|
|
|
|
return TComponentLock<TReadOptional<T>>();
|
|
}
|
|
|
|
|
|
/**
|
|
* 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<typename T>
|
|
T ReadComponentChecked(FMovieSceneEntityID Entity, TComponentTypeID<T> ComponentTypeID) const
|
|
{
|
|
TComponentLock<TReadOptional<T>> 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<typename T>
|
|
TComponentLock<TWriteOptional<T>> WriteComponent(FMovieSceneEntityID Entity, TComponentTypeID<T> ComponentTypeID)
|
|
{
|
|
check(Entity);
|
|
|
|
if (!ComponentTypeID)
|
|
{
|
|
return TComponentLock<TWriteOptional<T>>();
|
|
}
|
|
|
|
FEntityLocation Location = EntityLocations[Entity.AsIndex()];
|
|
if (!Location.IsValid() || !EntityAllocationMasks[Location.GetAllocationIndex()].Contains(ComponentTypeID))
|
|
{
|
|
return TComponentLock<TWriteOptional<T>>();
|
|
}
|
|
|
|
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<TWriteOptional<T>>(&Header, LockMode, FEntityAllocationWriteContext(*this), ComponentOffset);
|
|
}
|
|
}
|
|
|
|
return TComponentLock<TWriteOptional<T>>();
|
|
}
|
|
|
|
|
|
/**
|
|
* 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<typename T, typename ValueType>
|
|
void WriteComponentChecked(FMovieSceneEntityID Entity, TComponentTypeID<T> ComponentTypeID, ValueType&& Value)
|
|
{
|
|
TComponentLock<TWriteOptional<T>> ComponentPtr = WriteComponent(Entity, ComponentTypeID);
|
|
check(ComponentPtr);
|
|
*ComponentPtr = Forward<ValueType>(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<FMovieSceneEntityID>& 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<FMovieSceneEntityID>& 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<typename IteratorType>
|
|
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<typename IteratorType>
|
|
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<FComponentTypeID> 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<FComponentTypeID> 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<FComponentMask> EntityAllocationMasks;
|
|
|
|
/** Bit mask of allocations that currently have capacity */
|
|
TBitArray<> AllocationsWithCapacity;
|
|
|
|
/** Sparse array of entity allocation pointers. Allocations are heap allocated. */
|
|
TSparseArray<FEntityAllocation*> EntityAllocations;
|
|
|
|
/** Sparse array of entity location information for each allocated entity ID. */
|
|
TSparseArray<FEntityLocation> EntityLocations;
|
|
|
|
/** Map of parent -> child entities */
|
|
TMultiMap<FMovieSceneEntityID, FMovieSceneEntityID> ParentToChild;
|
|
|
|
TSparseArray<TInlineValue<FChildEntityInitializer>> InstancedChildInitializers;
|
|
|
|
/** Map of entity ID to the generation its handle was created in. FEntityHandle::HandleGeneration matches the value if it is still valid */
|
|
TMap<FMovieSceneEntityID, uint32> 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<int32, FAllocationMask> AllocationsToEntities;
|
|
|
|
/** Additional array of entitiy IDs that need destroying despite not belonging to an allocation */
|
|
TArray<FMovieSceneEntityID> EmptyEntities;
|
|
};
|
|
|
|
FCommitData Commit() const;
|
|
|
|
friend FEntityManager;
|
|
|
|
/** */
|
|
TArray<int32> AllocationsToDestroy;
|
|
|
|
/** */
|
|
TArray<FMovieSceneEntityID> LooseEntitiesToDestroy;
|
|
|
|
FEntityManager* EntityManager;
|
|
};
|
|
|
|
extern MOVIESCENE_API FEntityManager* GEntityManagerForDebuggingVisualizers;
|
|
|
|
|
|
} // namespace MovieScene
|
|
} // namespace UE
|