Пример #1
0
 /// <summary>
 /// Creates a stateful change tracker over the given world.
 /// </summary>
 /// <param name="srcWorld">The input world to track changes for.</param>
 /// <param name="allocator">Allocator used for the cached state.</param>
 public EntityManagerDiffer(World srcWorld, Allocator allocator)
 {
     m_SrcWorld               = srcWorld ?? throw new ArgumentNullException(nameof(srcWorld));
     m_ShadowWorld            = new World(srcWorld.Name + " (Shadow)");
     m_SrcWorldEntityQuery    = EntityManagerDifferUtility.CreateQuery(srcWorld.EntityManager);
     m_ShadowWorldEntityQuery = EntityManagerDifferUtility.CreateQuery(m_ShadowWorld.EntityManager);
     m_TypeInfoStream         = new TypeInfoStream(allocator);
 }
Пример #2
0
 /// <summary>
 /// Clears out any references to non-existent entities.
 /// </summary>
 /// <remarks>
 /// This can potentially mutate the world and increment the changed version of chunks.
 /// </remarks>
 public static unsafe void ClearMissingReferences(
     NativeArray <ArchetypeChunk> chunks,
     EntityComponentStore *entityComponentStore,
     uint globalSystemVersion,
     TypeInfoStream typeInfoStream
     )
 {
     new SetMissingEntityReferencesToNull
     {
         GlobalSystemVersion = globalSystemVersion,
         Chunks = chunks,
         EntityComponentStore = entityComponentStore,
         TypeInfoStream       = typeInfoStream
     }.Schedule(chunks.Length, 64).Complete();
 }
 public unsafe BuildChangeSetTask(WorldState beforeState, WorldState afterState, TypeInfoStream typeInfoStream, Allocator allocator)
 {
     m_BeforeState                = beforeState;
     m_AfterState                 = afterState;
     m_TypeInfoStream             = typeInfoStream;
     m_CreatedEntities            = new NativeList <EntityInChunkWithComponent <EntityGuid> >(1, allocator);
     m_ModifiedEntities           = new NativeList <ModifiedEntity>(1, allocator);
     m_DestroyedEntities          = new NativeList <EntityInChunkWithComponent <EntityGuid> >(1, allocator);
     m_PackedEntities             = new PackedCollection <EntityGuid>(1, allocator);
     m_PackedStableTypeHashes     = new PackedCollection <ComponentTypeHash>(1, allocator);
     m_AddComponents              = new NativeList <PackedComponent>(1, allocator);
     m_RemoveComponents           = new NativeList <PackedComponent>(1, allocator);
     m_ComponentDataStream        = new ComponentDataStream(allocator);
     m_EntityReference            = new NativeList <EntityReferenceChange>(1, allocator);
     m_LinkedEntityGroupAdditions = new NativeList <LinkedEntityGroupChange>(1, allocator);
     m_LinkedEntityGroupRemovals  = new NativeList <LinkedEntityGroupChange>(1, allocator);
     m_BeforeEntityToEntityGuid   = new EntityToComponentMap <EntityGuid>(m_BeforeState.EntityComponentStore, allocator);
     m_AfterEntityToEntityGuid    = new EntityToComponentMap <EntityGuid>(m_AfterState.EntityComponentStore, allocator);
     IsCreated = true;
 }
Пример #4
0
        /// <summary>
        /// Generates a change set between the given entity managers using the given options.
        /// </summary>
        /// <remarks>
        /// No assumptions are made about the relationship between the given worlds. This means a full
        /// deep compare is done to generate the change set.
        /// </remarks>
        /// <param name="srcEntityManager">The src world to compute changes from.</param>
        /// <param name="dstEntityManager">The dst world to compute changes to.</param>
        /// <param name="options">Options to specify to tailor the diffing based on specific requirements. See <see cref="EntityManagerDifferOptions"/> for more information.</param>
        /// <param name="allocator">Allocator to use for the returned set.</param>
        /// <returns>A set of changes between src and dst worlds.</returns>
        public static EntityChangeSet GetChanges(
            EntityManager srcEntityManager,
            EntityManager dstEntityManager,
            EntityManagerDifferOptions options,
            Allocator allocator)
        {
            using (var typeInfoStream = new TypeInfoStream(Allocator.TempJob))
            {
                var changes = GetChanges(
                    srcEntityManager,
                    CreateQuery(srcEntityManager),
                    dstEntityManager,
                    CreateQuery(dstEntityManager),
                    typeInfoStream,
                    options,
                    allocator);

                // @NOTE We are not disposing the changes object itself.
                // In this case since we only allocated the forward and we are returning it to the caller.
                return(changes.ForwardChangeSet);
            }
        }
Пример #5
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="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