Example #1
0
        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();
        }
Example #2
0
        /// <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);
        }
Example #3
0
        /// <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,
Example #4
0
        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();
        }