/// <summary> /// Moves a selection of the entities managed by the specified EntityManager to the <see cref="World"/> of this EntityManager /// and fills an array with their <see cref="Entity"/> objects. /// </summary> /// <remarks> /// After the move, the entities are managed by this EntityManager. Use the `output` array to make post-move /// changes to the transferred entities. /// /// Each world has one EntityManager, which manages all the entities in that world. This function /// allows you to transfer entities from one World to another. /// /// **Important:** This function creates a sync point, which means that the EntityManager waits for all /// currently running Jobs to complete before moving the entities and no additional Jobs can start before /// the function is finished. A sync point can cause a drop in performance because the ECS framework may not /// be able to make use of the processing power of all available cores. /// </remarks> /// <param name="output">An array to receive the Entity objects of the transferred entities.</param> /// <param name="srcEntities">The EntityManager whose entities are appropriated.</param> /// <param name="filter">A EntityQuery that defines the entities to move. Must be part of the source /// World.</param> /// <param name="entityRemapping">A set of entity transformations to make during the transfer.</param> /// <exception cref="ArgumentException"></exception> public void MoveEntitiesFrom(out NativeArray <Entity> output, EntityManager srcEntities, EntityQuery filter, NativeArray <EntityRemapUtility.EntityRemapInfo> entityRemapping) { #if ENABLE_UNITY_COLLECTIONS_CHECKS if (filter.EntityComponentStore != srcEntities.EntityComponentStore) { throw new ArgumentException( "EntityManager.MoveEntitiesFrom failed - srcEntities and filter must belong to the same World)"); } #endif using (var chunks = filter.CreateArchetypeChunkArray(Allocator.TempJob)) { MoveEntitiesFrom(out output, srcEntities, chunks, entityRemapping); } }
/// <summary> /// Sets the shared component of all entities in the query. /// </summary> /// <remarks> /// The component data stays in the same chunk, the internal shared component data indices will be adjusted. /// /// **Important:** This function creates a sync point, which means that the EntityManager waits for all /// currently running Jobs to complete before setting the component and no additional Jobs can start before /// the function is finished. A sync point can cause a drop in performance because the ECS framework may not /// be able to make use of the processing power of all available cores. /// </remarks> /// <param name="entity">The entity</param> /// <param name="componentData">A shared component object containing the values to set.</param> /// <typeparam name="T">The shared component type.</typeparam> public void SetSharedComponentData <T>(EntityQuery query, T componentData) where T : struct, ISharedComponentData { using (var chunks = query.CreateArchetypeChunkArray(Allocator.TempJob)) { if (chunks.Length == 0) { return; } BeforeStructuralChange(); var type = ComponentType.ReadWrite <T>(); RemoveComponent(chunks, type); int sharedComponentIndex = ManagedComponentStore.InsertSharedComponent(componentData); AddSharedComponentData(chunks, sharedComponentIndex, type); } }
/// <summary> /// Removes a component from the chunks identified by a EntityQuery. /// </summary> /// <remarks> /// A chunk component is common to all entities in a chunk. You can access a chunk <see cref="IComponentData"/> /// instance through either the chunk itself or through an entity stored in that chunk. /// /// **Important:** This function creates a sync point, which means that the EntityManager waits for all /// currently running Jobs to complete before removing the component and no additional Jobs can start before /// the function is finished. A sync point can cause a drop in performance because the ECS framework may not /// be able to make use of the processing power of all available cores. /// </remarks> /// <param name="entityQuery">The EntityQuery identifying the chunks to modify.</param> /// <typeparam name="T">The type of component to remove.</typeparam> public void RemoveChunkComponentData <T>(EntityQuery entityQuery) { using (var chunks = entityQuery.CreateArchetypeChunkArray(Allocator.TempJob)) { if (chunks.Length == 0) { return; } BeforeStructuralChange(); var archetypeChanges = EntityComponentStore->BeginArchetypeChangeTracking(); EntityManagerChangeArchetypeUtility.RemoveComponent(chunks, ComponentType.ChunkComponent <T>(), EntityComponentStore, ManagedComponentStore); var changedArchetypes = EntityComponentStore->EndArchetypeChangeTracking(archetypeChanges); EntityGroupManager.AddAdditionalArchetypes(changedArchetypes); } }
internal static void CopyAndReplaceChunks( EntityManager srcEntityManager, EntityManager dstEntityManager, EntityQuery dstEntityQuery, ArchetypeChunkChanges archetypeChunkChanges) { s_CopyAndReplaceChunksProfilerMarker.Begin(); var dstAccess = dstEntityManager.GetCheckedEntityDataAccess(); var srcAccess = srcEntityManager.GetCheckedEntityDataAccess(); var archetypeChanges = dstAccess->EntityComponentStore->BeginArchetypeChangeTracking(); DestroyChunks(dstEntityManager, archetypeChunkChanges.DestroyedDstChunks.Chunks); CloneAndAddChunks(srcEntityManager, dstEntityManager, archetypeChunkChanges.CreatedSrcChunks.Chunks); dstAccess->EntityComponentStore->EndArchetypeChangeTracking(archetypeChanges, dstAccess->EntityQueryManager); srcAccess->EntityComponentStore->InvalidateChunkListCacheForChangedArchetypes(); dstAccess->EntityComponentStore->InvalidateChunkListCacheForChangedArchetypes(); //@TODO-opt: use a query that searches for all chunks that have chunk components on it //@TODO-opt: Move this into a job // Any chunk might have been recreated, so the ChunkHeader might be invalid using (var allDstChunks = dstEntityQuery.CreateArchetypeChunkArray(Allocator.TempJob)) { foreach (var chunk in allDstChunks) { var metaEntity = chunk.m_Chunk->metaChunkEntity; if (metaEntity != Entity.Null) { if (dstEntityManager.Exists(metaEntity)) { dstEntityManager.SetComponentData(metaEntity, new ChunkHeader { ArchetypeChunk = chunk }); } } } } srcAccess->EntityComponentStore->IncrementGlobalSystemVersion(); dstAccess->EntityComponentStore->IncrementGlobalSystemVersion(); s_CopyAndReplaceChunksProfilerMarker.End(); }
/// <summary> /// Adds a shared component to a set of entities defined by a EntityQuery. /// </summary> /// <remarks> /// The fields of the `componentData` parameter are assigned to all of the added shared components. /// /// Adding a component to an entity changes its archetype and results in the entity being moved to a /// different chunk. The entity moves to a chunk with other entities that have the same shared component values. /// A new chunk is created if no chunk with the same archetype and shared component values currently exists. /// /// **Important:** This function creates a sync point, which means that the EntityManager waits for all /// currently running Jobs to complete before adding the component and no additional Jobs can start before /// the function is finished. A sync point can cause a drop in performance because the ECS framework may not /// be able to make use of the processing power of all available cores. /// </remarks> /// <param name="entityQuery">The EntityQuery defining a set of entities to modify.</param> /// <param name="componentData">The data to set.</param> /// <typeparam name="T">The data type of the shared component.</typeparam> public void AddSharedComponentData <T>(EntityQuery entityQuery, T componentData) where T : struct, ISharedComponentData { var componentType = ComponentType.ReadWrite <T>(); using (var chunks = entityQuery.CreateArchetypeChunkArray(Allocator.TempJob)) { if (chunks.Length == 0) { return; } BeforeStructuralChange(); var newSharedComponentDataIndex = m_ManagedComponentStore.InsertSharedComponent(componentData); EntityComponentStore->AssertCanAddComponent(chunks, componentType); EntityManagerChangeArchetypeUtility.AddSharedComponent(chunks, componentType, newSharedComponentDataIndex, EntityComponentStore, ManagedComponentStore, EntityGroupManager); m_ManagedComponentStore.RemoveReference(newSharedComponentDataIndex); } }
internal static unsafe JobHandle ScheduleInternal <T>(ref T jobData, EntityQuery query, JobHandle dependsOn, ScheduleMode mode) where T : struct, IJobChunk { using (var chunks = query.CreateArchetypeChunkArray(Allocator.Temp)) { int currentChunk = 0; int currentEntity = 0; foreach (var chunk in chunks) { jobData.Execute(chunk, currentChunk, currentEntity); currentChunk++; currentEntity += chunk.Count; } } DoDeallocateOnJobCompletion(jobData); return(new JobHandle()); }
// ---------------------------------------------------------------------------------------------------------- // INTERNAL // ---------------------------------------------------------------------------------------------------------- void MoveEntitiesFromInternalQuery(EntityManager srcEntities, EntityQuery filter, NativeArray <EntityRemapUtility.EntityRemapInfo> entityRemapping) { var srcAccess = srcEntities.GetCheckedEntityDataAccess(); var selfAccess = GetCheckedEntityDataAccess(); #if ENABLE_UNITY_COLLECTIONS_CHECKS if (filter._GetImpl()->_Access != srcAccess) { throw new ArgumentException( "EntityManager.MoveEntitiesFrom failed - srcEntities and filter must belong to the same World)"); } if (srcEntities.m_EntityDataAccess == m_EntityDataAccess) { throw new ArgumentException("srcEntities must not be the same as this EntityManager."); } #endif BeforeStructuralChange(); srcEntities.BeforeStructuralChange(); using (var chunks = filter.CreateArchetypeChunkArray(Allocator.TempJob)) { #if ENABLE_UNITY_COLLECTIONS_CHECKS for (int i = 0; i < chunks.Length; ++i) { if (chunks[i].m_Chunk->Archetype->HasChunkHeader) { throw new ArgumentException("MoveEntitiesFrom can not move chunks that contain ChunkHeader components."); } } #endif var archetypeChanges = selfAccess->EntityComponentStore->BeginArchetypeChangeTracking(); MoveChunksFromFiltered(chunks, entityRemapping, srcAccess->EntityComponentStore, srcAccess->ManagedComponentStore); selfAccess->EntityComponentStore->EndArchetypeChangeTracking(archetypeChanges, selfAccess->EntityQueryManager); selfAccess->EntityComponentStore->InvalidateChunkListCacheForChangedArchetypes(); srcAccess->EntityComponentStore->InvalidateChunkListCacheForChangedArchetypes(); } }
public void SetSharedComponentData <T>(EntityQuery query, T componentData) where T : struct, ISharedComponentData { using (var chunks = query.CreateArchetypeChunkArray(Allocator.TempJob)) { if (chunks.Length == 0) { return; } var ecs = GetCheckedEntityDataAccess(); ecs->BeforeStructuralChange(); var type = ComponentType.ReadWrite <T>(); ecs->RemoveComponent(chunks, type); int sharedComponentIndex = ecs->InsertSharedComponent(componentData); ecs->AddSharedComponentData(chunks, sharedComponentIndex, type); ecs->RemoveSharedComponentReference(sharedComponentIndex); } }
/// <summary> /// Adds a component to each of the chunks identified by a EntityQuery and set the component values. /// </summary> /// <remarks> /// This function finds all chunks whose archetype satisfies the EntityQuery and adds the specified /// component to them. /// /// A chunk component is common to all entities in a chunk. You can access a chunk <see cref="IComponentData"/> /// instance through either the chunk itself or through an entity stored in that chunk. /// /// **Important:** This function creates a sync point, which means that the EntityManager waits for all /// currently running Jobs to complete before adding the component and no additional Jobs can start before /// the function is finished. A sync point can cause a drop in performance because the ECS framework may not /// be able to make use of the processing power of all available cores. /// </remarks> /// <param name="entityQuery">The EntityQuery identifying the chunks to modify.</param> /// <param name="componentData">The data to set.</param> /// <typeparam name="T">The type of component, which must implement IComponentData.</typeparam> public void AddChunkComponentData <T>(EntityQuery entityQuery, T componentData) where T : struct, IComponentData { using (var chunks = entityQuery.CreateArchetypeChunkArray(Allocator.TempJob)) { if (chunks.Length == 0) { return; } BeforeStructuralChange(); EntityComponentStore->AssertCanAddChunkComponent(chunks, ComponentType.ChunkComponent <T>()); var type = ComponentType.ReadWrite <T>(); var chunkType = ComponentType.FromTypeIndex(TypeManager.MakeChunkComponentTypeIndex(type.TypeIndex)); using (var entityBatchList = m_EntityComponentStore->CreateEntityBatchList(chunks)) { EntityManagerChangeArchetypeUtility.AddComponent(entityBatchList, chunkType, 0, EntityComponentStore, ManagedComponentStore, EntityGroupManager); m_EntityComponentStore->SetChunkComponent <T>(entityBatchList, componentData); } } }
/// <summary> /// Generates a detailed change set between <see cref="srcEntityManager"/> and <see cref="dstEntityManager"/>. /// All entities to be considered must have the <see cref="EntityGuid"/> component with a unique value. /// The resulting <see cref="EntityChanges"/> must be disposed when no longer needed. /// </summary> /// <remarks> /// For performance the <see cref="dstToSrcSequenceNumbers"/> is used to map chunks between the given worlds. If this is not provided, or is empty /// the tracker must evaluate all chunks, entities, and component data. /// /// When using the <see cref="EntityManagerDifferOptions.FastForwardShadowWorld"/> the destination world must be a direct ancestor to /// the source world, and must only be updated using this call or similar methods. There should be no direct changes to destination world. /// </remarks> internal static unsafe EntityChanges GetChanges( EntityManager srcEntityManager, EntityQuery srcEntityQuery, EntityManager dstEntityManager, EntityQuery dstEntityQuery, TypeInfoStream typeInfoStream, EntityManagerDifferOptions options, Allocator allocator) { var changes = new EntityChanges(); srcEntityManager.CompleteAllJobs(); dstEntityManager.CompleteAllJobs(); if (options != EntityManagerDifferOptions.None) { var includeForwardChangeSet = (options & EntityManagerDifferOptions.IncludeForwardChangeSet) == EntityManagerDifferOptions.IncludeForwardChangeSet; var includeReverseChangeSet = (options & EntityManagerDifferOptions.IncludeReverseChangeSet) == EntityManagerDifferOptions.IncludeReverseChangeSet; var fastForwardShadowWorld = (options & EntityManagerDifferOptions.FastForwardShadowWorld) == EntityManagerDifferOptions.FastForwardShadowWorld; var clearMissingReferences = (options & EntityManagerDifferOptions.ClearMissingReferences) == EntityManagerDifferOptions.ClearMissingReferences; var validateUniqueEntityGuids = (options & EntityManagerDifferOptions.ValidateUniqueEntityGuid) == EntityManagerDifferOptions.ValidateUniqueEntityGuid; // Query chunks that should be considered for change tracking. using (var srcChunks = srcEntityQuery.CreateArchetypeChunkArray(Allocator.TempJob)) using (var dstChunks = dstEntityQuery.CreateArchetypeChunkArray(Allocator.TempJob)) { if (clearMissingReferences) { ArchetypeChunkChangeUtility.ClearMissingReferences(srcChunks, srcEntityManager.EntityComponentStore, srcEntityManager.GlobalSystemVersion, typeInfoStream); } // Compare the chunks and get a set of all changed chunks. // @NOTE A modified chunk will appear as destroyed and then created. using (var archetypeChunkChanges = ArchetypeChunkChangeUtility.GetArchetypeChunkChanges(srcChunks, dstChunks, Allocator.TempJob)) { // If we have no chunk-level changes then there is no work to be done. if (archetypeChunkChanges.HasChanges) { if (includeForwardChangeSet || includeReverseChangeSet) { BuildComponentDataToEntityLookupTask <EntityGuid> buildSrcEntityGuidToEntityLookupTask = default; try { using (var buildSrcEntitiesTask = new BuildEntityInChunkWithComponentTask <EntityGuid>( archetypeChunkChanges.CreatedSrcChunks, Allocator.TempJob)) using (var buildDstEntitiesTask = new BuildEntityInChunkWithComponentTask <EntityGuid>( archetypeChunkChanges.DestroyedDstChunks, Allocator.TempJob)) { var handle = JobHandle.CombineDependencies(buildSrcEntitiesTask.Schedule(), buildDstEntitiesTask.Schedule()); if (validateUniqueEntityGuids) { // Validation is expensive since we must run over the entire world. This is opt in. buildSrcEntityGuidToEntityLookupTask = new BuildComponentDataToEntityLookupTask <EntityGuid>( srcEntityQuery.CalculateEntityCount(), Allocator.TempJob); handle = JobHandle.CombineDependencies(handle, buildSrcEntityGuidToEntityLookupTask.Schedule(srcChunks)); } handle.Complete(); if (validateUniqueEntityGuids && TryGetDuplicateComponents( buildSrcEntityGuidToEntityLookupTask.GetComponentDataToEntityMap(), out var duplicates)) { throw new DuplicateEntityGuidException(message: $"Found {duplicates.Length} {nameof(EntityGuid)} components that are shared by more than one Entity, " + $"see the {nameof(DuplicateEntityGuidException.DuplicateEntityGuids)} property for more information.") { DuplicateEntityGuids = duplicates }; } var srcState = new WorldState( srcEntityManager, buildSrcEntitiesTask.GetEntities() ); var dstState = new WorldState( dstEntityManager, buildDstEntitiesTask.GetEntities() ); BuildChangeSetTask buildForwardChangeSetTask = default; BuildChangeSetTask buildReverseChangeSetTask = default; try { JobHandle buildForwardChangeSetJob = default; JobHandle buildReverseChangeSetJob = default; if (includeForwardChangeSet) { buildForwardChangeSetTask = new BuildChangeSetTask(dstState, srcState, typeInfoStream, Allocator.TempJob); buildForwardChangeSetJob = buildForwardChangeSetTask.Schedule(); } if (includeReverseChangeSet) { buildReverseChangeSetTask = new BuildChangeSetTask(srcState, dstState, typeInfoStream, Allocator.TempJob); buildReverseChangeSetJob = buildReverseChangeSetTask.Schedule(); } JobHandle.CombineDependencies(buildForwardChangeSetJob, buildReverseChangeSetJob).Complete(); changes = new EntityChanges( includeForwardChangeSet ? buildForwardChangeSetTask.GetChangeSet(allocator) : default, includeReverseChangeSet ? buildReverseChangeSetTask.GetChangeSet(allocator) : default