static void DestroyChunks(EntityManager entityManager, NativeList <ArchetypeChunk> chunks) { s_DestroyChunksProfilerMarker.Begin(); new DestroyChunksJob { EntityManager = entityManager, Chunks = chunks }.Run(); s_PlaybackManagedChangesMarker.Begin(); var access = entityManager.GetCheckedEntityDataAccess(); var ecs = access->EntityComponentStore; var mcs = access->ManagedComponentStore; mcs.Playback(ref ecs->ManagedChangesTracker); s_PlaybackManagedChangesMarker.End(); s_DestroyChunksProfilerMarker.End(); }
/// <summary> /// Generates a detailed change set for the world. /// All entities to be considered for diffing must have the <see cref="EntityGuid"/> component with a unique value. /// The resulting <see cref="EntityChanges"/> must be disposed when no longer needed. /// </summary> /// <param name="options">A set of options which can be toggled.</param> /// <param name="allocator">The allocator to use for the results object.</param> /// <returns>A Change set containing the differences between the two worlds.</returns> public EntityChanges GetChanges(EntityManagerDifferOptions options, Allocator allocator) { s_GetChangesMarker.Begin(); var changes = EntityManagerDifferUtility.GetChanges( m_SrcWorld.EntityManager, m_SrcWorldEntityQuery, m_ShadowWorld.EntityManager, m_ShadowWorldEntityQuery, m_TypeInfoStream, options, allocator); s_GetChangesMarker.End(); return(changes); }
/// <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,
static void CloneAndAddChunks(EntityManager srcEntityManager, EntityManager dstEntityManager, NativeList <ArchetypeChunk> chunks) { s_CloneAndAddChunksProfilerMarker.Begin(); // sort chunks by archetype and clone chunks var srcSharedComponentIndices = new NativeList <int>(128, Allocator.TempJob); new CollectSharedComponentIndices { Chunks = chunks, SharedComponentIndices = srcSharedComponentIndices, }.Run(); // copy shared components s_CopySharedComponentsMarker.Begin(); var srcAccess = srcEntityManager.GetCheckedEntityDataAccess(); var dstAccess = dstEntityManager.GetCheckedEntityDataAccess(); var srcManagedComponentStore = srcAccess->ManagedComponentStore; var dstManagedComponentStore = dstAccess->ManagedComponentStore; var dstSharedComponentIndicesRemapped = new NativeArray <int>(srcSharedComponentIndices, Allocator.TempJob); dstManagedComponentStore.CopySharedComponents(srcManagedComponentStore, (int *)dstSharedComponentIndicesRemapped.GetUnsafeReadOnlyPtr(), dstSharedComponentIndicesRemapped.Length); s_CopySharedComponentsMarker.End(); // clone chunks var cloned = new NativeArray <ArchetypeChunk>(chunks.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); new CreateNewChunks { Chunks = chunks, ClonedChunks = cloned, DstEntityManager = dstEntityManager, SrcSharedComponentIndices = srcSharedComponentIndices, DstSharedComponentIndices = dstSharedComponentIndicesRemapped, }.Run(); var copyJob = new CopyChunkBuffers { Chunks = chunks, ClonedChunks = cloned }.Schedule(chunks.Length, default); JobHandle.ScheduleBatchedJobs(); srcSharedComponentIndices.Dispose(); s_PlaybackManagedChangesMarker.Begin(); dstManagedComponentStore.Playback(ref dstAccess->EntityComponentStore->ManagedChangesTracker); // Release any references obtained by CopySharedComponents above for (var i = 0; i < dstSharedComponentIndicesRemapped.Length; i++) { dstAccess->RemoveSharedComponentReference(dstSharedComponentIndicesRemapped[i]); } s_PlaybackManagedChangesMarker.End(); dstSharedComponentIndicesRemapped.Dispose(); copyJob.Complete(); s_CopyManagedComponentsMarker.Begin(); for (int i = 0; i < cloned.Length; i++) { var dstChunk = cloned[i].m_Chunk; var dstArchetype = dstChunk->Archetype; var numManagedComponents = dstArchetype->NumManagedComponents; var hasHybridComponents = dstArchetype->HasHybridComponents; for (int t = 0; t < numManagedComponents; ++t) { int indexInArchetype = t + dstChunk->Archetype->FirstManagedComponent; var offset = dstChunk->Archetype->Offsets[indexInArchetype]; var a = (int *)(dstChunk->Buffer + offset); int count = dstChunk->Count; if (hasHybridComponents) { // We consider hybrid components as always different, there's no reason to clone those at this point. var typeCategory = TypeManager.GetTypeInfo(dstChunk->Archetype->Types[indexInArchetype].TypeIndex).Category; if (typeCategory == TypeManager.TypeCategory.UnityEngineObject) { // We still need to patch their indices, because otherwise they might point to invalid memory in // the managed component store. Setting them to the invalid index 0 is harmless, assuming nobody // actually operates on the shadow world. UnsafeUtility.MemSet(a, 0, sizeof(int) * count); continue; } } dstManagedComponentStore.CloneManagedComponentsFromDifferentWorld(a, count, srcManagedComponentStore, ref *dstAccess->EntityComponentStore); } } s_CopyManagedComponentsMarker.End(); // Ensure capacity in the dst world before we start linking entities. dstAccess->EntityComponentStore->EnsureCapacity(srcEntityManager.EntityCapacity); dstAccess->EntityComponentStore->CopyNextFreeEntityIndex(srcAccess->EntityComponentStore); new PatchAndAddClonedChunks { SrcChunks = chunks, DstChunks = cloned, DstEntityComponentStore = dstAccess->EntityComponentStore }.Schedule(chunks.Length, 64).Complete(); cloned.Dispose(); s_CloneAndAddChunksProfilerMarker.End(); }