/// <summary> /// Copies all entities from srcEntityManager and replaces all entities in this EntityManager /// </summary> /// <remarks> /// Guarantees that the chunk layout & order of the entities will match exactly, thus this method can be used for deterministic rollback. /// This feature is not complete and only supports a subset of the EntityManager features at the moment: /// * Currently it copies all SystemStateComponents (They should not be copied) /// * Currently does not support class based components /// </remarks> public void CopyAndReplaceEntitiesFrom(EntityManager srcEntityManager) { srcEntityManager.CompleteAllJobs(); CompleteAllJobs(); var srcAccess = srcEntityManager.GetCheckedEntityDataAccess(); var selfAccess = GetCheckedEntityDataAccess(); using (var srcChunks = srcAccess->ManagedEntityDataAccess.m_UniversalQueryWithChunks.CreateArchetypeChunkArrayAsync(Allocator.TempJob, out var srcChunksJob)) using (var dstChunks = selfAccess->ManagedEntityDataAccess.m_UniversalQueryWithChunks.CreateArchetypeChunkArrayAsync(Allocator.TempJob, out var dstChunksJob)) { using (var archetypeChunkChanges = EntityDiffer.GetArchetypeChunkChanges( srcChunks, dstChunks, Allocator.TempJob, jobHandle: out var archetypeChunkChangesJob, dependsOn: JobHandle.CombineDependencies(srcChunksJob, dstChunksJob))) { archetypeChunkChangesJob.Complete(); EntityDiffer.CopyAndReplaceChunks(srcEntityManager, this, selfAccess->ManagedEntityDataAccess.m_UniversalQueryWithChunks, archetypeChunkChanges); Unity.Entities.EntityComponentStore.AssertSameEntities(srcAccess->EntityComponentStore, selfAccess->EntityComponentStore); } } }
/// <summary> /// Copies all entities from srcEntityManager and replaces all entities in this EntityManager /// </summary> /// <remarks> /// Guarantees that the chunk layout & order of the entities will match exactly, thus this method can be used for deterministic rollback. /// This feature is not complete and only supports a subset of the EntityManager features at the moment: /// * Currently it copies all SystemStateComponents (They should not be copied) /// * Currently does not support class based components /// </remarks> public void CopyAndReplaceEntitiesFrom(EntityManager srcEntityManager) { #if ENABLE_UNITY_COLLECTIONS_CHECKS if (srcEntityManager == null || !srcEntityManager.IsCreated) { throw new ArgumentNullException(nameof(srcEntityManager)); } if (!IsCreated) { throw new ArgumentException("This EntityManager has been destroyed"); } #endif srcEntityManager.CompleteAllJobs(); CompleteAllJobs(); using (var srcChunks = srcEntityManager.m_UniversalQueryWithChunks.CreateArchetypeChunkArray(Allocator.TempJob, out var srcChunksJob)) using (var dstChunks = m_UniversalQueryWithChunks.CreateArchetypeChunkArray(Allocator.TempJob, out var dstChunksJob)) { using (var archetypeChunkChanges = EntityDiffer.GetArchetypeChunkChanges( srcChunks, dstChunks, Allocator.TempJob, jobHandle: out var archetypeChunkChangesJob, dependsOn: JobHandle.CombineDependencies(srcChunksJob, dstChunksJob))) { archetypeChunkChangesJob.Complete(); EntityDiffer.CopyAndReplaceChunks(srcEntityManager, this, m_UniversalQueryWithChunks, archetypeChunkChanges); Unity.Entities.EntityComponentStore.AssertSameEntities(srcEntityManager.EntityComponentStore, EntityComponentStore); } } }
protected override void OnDestroyManager() { base.OnDestroyManager(); EntityManager.CompleteAllJobs(); nativeManager.ProcessDisposables(new JobHandle()); nativeManager.DisposePersistents(); nativeManager.DisposeTempJobs(); }
/// <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="Entities.EntityChanges"/> must be disposed when no longer needed. /// </summary> /// <remarks> /// 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 EntityChanges GetChanges( EntityManager srcEntityManager, EntityManager dstEntityManager, EntityManagerDifferOptions options, EntityQueryDesc entityQueryDesc, BlobAssetCache blobAssetCache, Allocator allocator) { #if ENABLE_PROFILER || UNITY_EDITOR s_GetChangesProfilerMarker.Begin(); #endif CheckEntityGuidComponent(entityQueryDesc); var changes = default(EntityChanges); if (options == EntityManagerDifferOptions.None) { return(changes); } srcEntityManager.CompleteAllJobs(); dstEntityManager.CompleteAllJobs(); var srcEntityQuery = srcEntityManager.CreateEntityQuery(entityQueryDesc); var dstEntityQuery = dstEntityManager.CreateEntityQuery(entityQueryDesc); // Gather a set of a chunks to consider for diffing in both the src and dst worlds. using (var srcChunks = srcEntityQuery.CreateArchetypeChunkArray(Allocator.TempJob, out var srcChunksJob)) using (var dstChunks = dstEntityQuery.CreateArchetypeChunkArray(Allocator.TempJob, out var dstChunksJob)) { JobHandle clearMissingReferencesJob = default; if (CheckOption(options, EntityManagerDifferOptions.ClearMissingReferences)) { // Opt-in feature. // This is a special user case for references to destroyed entities. // If entity is destroyed, any references to that entity will remain set but become invalid (i.e. broken). // This option ensures that references to non-existent entities will be explicitly set to Entity.Null which // will force it to be picked up in the change set. ClearMissingReferences(srcEntityManager, srcChunks, out clearMissingReferencesJob, srcChunksJob); } // @TODO NET_DOTS does not support JobHandle.CombineDependencies with 3 arguments. #if NET_DOTS var archetypeChunkChangesJobDependencies = CombineDependencies(srcChunksJob, dstChunksJob, clearMissingReferencesJob); #else var archetypeChunkChangesJobDependencies = JobHandle.CombineDependencies(srcChunksJob, dstChunksJob, clearMissingReferencesJob); #endif // Broad phased chunk comparison. using (var archetypeChunkChanges = GetArchetypeChunkChanges( srcChunks: srcChunks, dstChunks: dstChunks, allocator: Allocator.TempJob, jobHandle: out var archetypeChunkChangesJob, dependsOn: archetypeChunkChangesJobDependencies)) { // Explicitly sync at this point to parallelize subsequent jobs by chunk. archetypeChunkChangesJob.Complete(); // Gather a sorted set of entities based on which chunks have changes. using (var srcEntities = GetSortedEntitiesInChunk( archetypeChunkChanges.CreatedSrcChunks, Allocator.TempJob, jobHandle: out var srcEntitiesJob)) using (var dstEntities = GetSortedEntitiesInChunk( archetypeChunkChanges.DestroyedDstChunks, Allocator.TempJob, jobHandle: out var dstEntitiesJob)) using (var srcBlobAssets = GetReferencedBlobAssets( srcChunks, Allocator.TempJob, jobHandle: out var srcBlobAssetsJob)) using (var dstBlobAssets = blobAssetCache.BlobAssetBatch->ToNativeList(Allocator.TempJob)) { var duplicateEntityGuids = default(NativeList <DuplicateEntityGuid>); var forwardEntityChanges = default(EntityInChunkChanges); var reverseEntityChanges = default(EntityInChunkChanges); var forwardComponentChanges = default(ComponentChanges); var reverseComponentChanges = default(ComponentChanges); var forwardBlobAssetChanges = default(BlobAssetChanges); var reverseBlobAssetChanges = default(BlobAssetChanges); try { JobHandle getDuplicateEntityGuidsJob = default; JobHandle forwardChangesJob = default; JobHandle reverseChangesJob = default; if (CheckOption(options, EntityManagerDifferOptions.ValidateUniqueEntityGuid)) { // Guid validation will happen incrementally and only consider changed entities in the source world. duplicateEntityGuids = GetDuplicateEntityGuids( srcEntities, Allocator.TempJob, jobHandle: out getDuplicateEntityGuidsJob, dependsOn: srcEntitiesJob); } if (CheckOption(options, EntityManagerDifferOptions.IncludeForwardChangeSet)) { forwardEntityChanges = GetEntityInChunkChanges( srcEntityManager, dstEntityManager, srcEntities, dstEntities, Allocator.TempJob, jobHandle: out var forwardEntityChangesJob, dependsOn: JobHandle.CombineDependencies(srcEntitiesJob, dstEntitiesJob)); forwardComponentChanges = GetComponentChanges( forwardEntityChanges,
/// <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