public void CalculateEntityOffsetsReturns0IfEntity()
        {
            var offsets = EntityRemapUtility.CalculateEntityOffsets(typeof(Entity));

            Assert.AreEqual(1, offsets.Length);
            Assert.AreEqual(0, offsets[0].Offset);
        }
Exemplo n.º 2
0
        static unsafe int GenerateRemapInfo(EntityManager entityManager, EntityArchetype[] archetypeArray, NativeArray <EntityRemapUtility.EntityRemapInfo> entityRemapInfos)
        {
            int nextEntityId = 1; //0 is reserved for Entity.Null;

            int totalChunkCount = 0;

            for (int archetypeIndex = 0; archetypeIndex < archetypeArray.Length; ++archetypeIndex)
            {
                var archetype = archetypeArray[archetypeIndex].Archetype;
                for (int i = 0; i < archetype->Chunks.Count; ++i)
                {
                    var chunk = archetype->Chunks.p[i];
                    for (int iEntity = 0; iEntity < chunk->Count; ++iEntity)
                    {
                        var entity = *(Entity *)ChunkDataUtility.GetComponentDataRO(chunk, iEntity, 0);
                        EntityRemapUtility.AddEntityRemapping(ref entityRemapInfos, entity, new Entity {
                            Version = 0, Index = nextEntityId
                        });
                        ++nextEntityId;
                    }

                    totalChunkCount += 1;
                }
            }

            return(totalChunkCount);
        }
Exemplo n.º 3
0
        static unsafe int GenerateRemapInfo(EntityManager entityManager, EntityArchetype[] archetypeArray, out NativeArray <EntityRemapUtility.EntityRemapInfo> entityRemapInfos)
        {
            int nextEntityId = 1; //0 is reserved for Entity.Null;

            entityRemapInfos = new NativeArray <EntityRemapUtility.EntityRemapInfo>(entityManager.EntityCapacity, Allocator.Temp);

            int totalChunkCount = 0;

            for (int archetypeIndex = 0; archetypeIndex < archetypeArray.Length; ++archetypeIndex)
            {
                var archetype = archetypeArray[archetypeIndex].Archetype;
                for (var c = (Chunk *)archetype->ChunkList.Begin; c != archetype->ChunkList.End; c = (Chunk *)c->ChunkListNode.Next)
                {
                    for (int iEntity = 0; iEntity < c->Count; ++iEntity)
                    {
                        var entity = *(Entity *)ChunkDataUtility.GetComponentDataRO(c, iEntity, 0);
                        EntityRemapUtility.AddEntityRemapping(ref entityRemapInfos, entity, new Entity {
                            Version = 0, Index = nextEntityId
                        });
                        ++nextEntityId;
                    }

                    totalChunkCount += 1;
                }
            }

            return(totalChunkCount);
        }
        public void CalculateEntityOffsetsReturnsOffsetsOfEmbeddedEntities()
        {
            var offsets = EntityRemapUtility.CalculateEntityOffsets(typeof(EmbeddedEntityStruct));

            Assert.AreEqual(2, offsets.Length);
            Assert.AreEqual(4, offsets[0].Offset);
            Assert.AreEqual(16, offsets[1].Offset);
        }
        public void RemapEntityMapsSourceToTarget()
        {
            var a = new Entity {
                Index = 1, Version = 2
            };
            var b = new Entity {
                Index = 3, Version = 5
            };

            EntityRemapUtility.AddEntityRemapping(ref m_Remapping, a, b);

            Assert.AreEqual(b, EntityRemapUtility.RemapEntity(ref m_Remapping, a));
        }
        public void RemapEntityMapsNonExistentSourceToNull()
        {
            var a = new Entity {
                Index = 1, Version = 2
            };
            var b = new Entity {
                Index = 3, Version = 5
            };
            var oldA = new Entity {
                Index = 1, Version = 1
            };

            EntityRemapUtility.AddEntityRemapping(ref m_Remapping, a, b);

            Assert.AreEqual(Entity.Null, EntityRemapUtility.RemapEntity(ref m_Remapping, oldA));
        }
Exemplo n.º 7
0
        static unsafe void WriteChunkData(YamlWriter writer, EntityManager entityManager, NativeArray <EntityRemapUtility.EntityRemapInfo> entityRemapInfos, Chunk *initialChunk, Archetype *archetype, int archetypeIndex, bool dumpChunkRawData)
        {
            var    tempChunkMem = stackalloc byte[Chunk.kChunkSize];
            Chunk *tempChunk    = (Chunk *)tempChunkMem;

            if (dumpChunkRawData)
            {
                UnsafeUtility.MemCpy(tempChunk, initialChunk, Chunk.kChunkSize);

                byte *tempChunkBuffer = tempChunk->Buffer;

                BufferHeader.PatchAfterCloningChunk(tempChunk);
                EntityRemapUtility.PatchEntities(archetype->ScalarEntityPatches, archetype->ScalarEntityPatchCount, archetype->BufferEntityPatches, archetype->BufferEntityPatchCount, tempChunkBuffer, tempChunk->Count, ref entityRemapInfos);
                ClearChunkHeaderComponents(tempChunk);
                ChunkDataUtility.MemsetUnusedChunkData(tempChunk, 0);

                tempChunk->Archetype = (Archetype *)archetypeIndex;
            }

            using (writer.WriteCollection(k_ChunkDataCollectionTag))
            {
                using (writer.WriteCollection("Header"))
                {
                    WriteEntity(writer, nameof(Chunk.metaChunkEntity), initialChunk->metaChunkEntity);
                    writer.WriteKeyValue(nameof(Chunk.Capacity), initialChunk->Capacity);
                    writer.WriteKeyValue(nameof(Chunk.Count), initialChunk->Count);

                    if (dumpChunkRawData)
                    {
                        writer.WriteFormattedBinaryData("Header-RawData", tempChunk, Chunk.kBufferOffset);
                    }
                }

                // First pass to sort by component type
                var entitiesByChunkIndex   = new Dictionary <int, Entity>();
                var componentDataList      = new List <int>();
                var chunkComponentDataList = new List <int>();
                var chunkTypes             = archetype->Types;
                for (int typeI = 0; typeI < archetype->TypesCount; typeI++)
                {
                    var componentType         = &chunkTypes[typeI];
                    var type                  = TypeManager.GetType(componentType->TypeIndex);
                    ref readonly var typeInfo = ref TypeManager.GetTypeInfo(componentType->TypeIndex);
        static TypeManager.EntityOffsetInfo[] GetEntityOffsets(System.Type type)
        {
#if !UNITY_DOTSPLAYER
            return(EntityRemapUtility.CalculateEntityOffsets(type));
#else
            unsafe {
                var info = TypeManager.GetTypeInfo(TypeManager.GetTypeIndex(type));
                if (info.EntityOffsetCount > 0)
                {
                    TypeManager.EntityOffsetInfo[] ei = new TypeManager.EntityOffsetInfo[info.EntityOffsetCount];
                    for (var i = 0; i < info.EntityOffsetCount; ++i)
                    {
                        ei[i] = TypeManager.GetEntityOffsets(info)[i];
                    }
                    return(ei);
                }
                return(null);
            }
#endif
        }
        static TypeManager.EntityOffsetInfo[] GetEntityOffsets(System.Type type)
        {
#if !UNITY_DOTSRUNTIME // Work needed to make CalculateEntityOffsets compatible with DOTS Runtime (comment with explanation at that code)
            return(EntityRemapUtility.CalculateEntityOffsets(type));
#else
            unsafe {
                var info = TypeManager.GetTypeInfo(TypeManager.GetTypeIndex(type));
                if (info.EntityOffsetCount > 0)
                {
                    TypeManager.EntityOffsetInfo[] ei = new TypeManager.EntityOffsetInfo[info.EntityOffsetCount];
                    for (var i = 0; i < info.EntityOffsetCount; ++i)
                    {
                        ei[i] = TypeManager.GetEntityOffsets(info)[i];
                    }
                    return(ei);
                }
                return(null);
            }
#endif
        }
Exemplo n.º 10
0
        private static unsafe int GenerateRemapInfo(EntityManager entityManager, EntityArchetype[] archetypeArray, NativeArray <EntityRemapUtility.EntityRemapInfo> entityRemapInfos)
        {
            int num   = 1;
            int num2  = 0;
            int index = 0;

            while (index < archetypeArray.Length)
            {
                Archetype *archetype = archetypeArray[index].Archetype;
                Chunk *    begin     = (Chunk *)archetype->ChunkList.Begin;
                while (true)
                {
                    if (begin == archetype->ChunkList.End)
                    {
                        index++;
                        break;
                    }
                    int num4 = 0;
                    while (true)
                    {
                        if (num4 >= begin->Count)
                        {
                            num2++;
                            begin = (Chunk *)begin->ChunkListNode.Next;
                            break;
                        }
                        Entity source = *((Entity *)ChunkDataUtility.GetComponentDataRO(begin, num4, 0));
                        Entity target = new Entity {
                            Version = 0,
                            Index   = num
                        };
                        EntityRemapUtility.AddEntityRemapping(ref entityRemapInfos, source, target);
                        num++;
                        num4++;
                    }
                }
            }
            return(num2);
        }
Exemplo n.º 11
0
        public static unsafe void SerializeWorld(EntityManager entityManager, BinaryWriter writer, out int[] sharedComponentsToSerialize, NativeArray <EntityRemapUtility.EntityRemapInfo> entityRemapInfos)
        {
            writer.Write(CurrentFileFormatVersion);
            var archetypeManager = entityManager.ArchetypeManager;

            Dictionary <EntityArchetype, int> archetypeToIndex;

            EntityArchetype[] archetypeArray;
            GetAllArchetypes(archetypeManager, out archetypeToIndex, out archetypeArray);

            var typeindices = new HashSet <int>();

            foreach (var archetype in archetypeArray)
            {
                for (int iType = 0; iType < archetype.Archetype->TypesCount; ++iType)
                {
                    typeindices.Add(archetype.Archetype->Types[iType].TypeIndex & TypeManager.ClearFlagsMask);
                }
            }

            var typeArray = typeindices.Select(index =>
            {
                var type = TypeManager.GetType(index);
                var name = TypeManager.GetType(index).AssemblyQualifiedName;
                var hash = TypeManager.GetTypeInfo(index).StableTypeHash;
                return(new
                {
                    index,
                    type,
                    name,
                    hash,
                    utf8Name = Encoding.UTF8.GetBytes(name)
                });
            }).OrderBy(t => t.name).ToArray();

            int typeNameBufferSize = typeArray.Sum(t => t.utf8Name.Length + 1);

            writer.Write(typeArray.Length);
            foreach (var n in typeArray)
            {
                writer.Write(n.hash);
            }

            writer.Write(typeNameBufferSize);
            foreach (var n in typeArray)
            {
                writer.Write(n.utf8Name);
                writer.Write((byte)0);
            }

            var typeIndexMap = new Dictionary <int, int>();

            for (int i = 0; i < typeArray.Length; ++i)
            {
                typeIndexMap[typeArray[i].index] = i;
            }

            WriteArchetypes(writer, archetypeArray, typeIndexMap);
            var sharedComponentMapping = GatherSharedComponents(archetypeArray, out var sharedComponentArraysTotalCount);
            var sharedComponentArrays  = new NativeArray <int>(sharedComponentArraysTotalCount, Allocator.Temp);

            FillSharedComponentArrays(sharedComponentArrays, archetypeArray, sharedComponentMapping);
            writer.Write(sharedComponentArrays.Length);
            writer.WriteArray(sharedComponentArrays);
            sharedComponentArrays.Dispose();

            //TODO: ensure chunks are defragged?

            var bufferPatches   = new NativeList <BufferPatchRecord>(128, Allocator.Temp);
            var totalChunkCount = GenerateRemapInfo(entityManager, archetypeArray, entityRemapInfos);

            writer.Write(totalChunkCount);

            GatherAllUsedBlobAssets(archetypeArray, out var blobAssetRefs, out var blobAssets);

            var blobAssetOffsets   = new NativeArray <int>(blobAssets.Length, Allocator.Temp);
            int totalBlobAssetSize = 0;

            int Align16(int x) => (x + 15) & ~15;

            for (int i = 0; i < blobAssets.Length; ++i)
            {
                totalBlobAssetSize += sizeof(BlobAssetHeader);
                blobAssetOffsets[i] = totalBlobAssetSize;
                totalBlobAssetSize += Align16(blobAssets[i].header->Length);
            }

            writer.Write(totalBlobAssetSize);

            var zeroBytes = int4.zero;

            for (int i = 0; i < blobAssets.Length; ++i)
            {
                var             blobAssetLength = blobAssets[i].header->Length;
                BlobAssetHeader header          = new BlobAssetHeader
                {
                    ValidationPtr = null, Allocator = Allocator.None, Length = Align16(blobAssetLength)
                };
                writer.WriteBytes(&header, sizeof(BlobAssetHeader));
                writer.WriteBytes(blobAssets[i].header + 1, blobAssetLength);
                writer.WriteBytes(&zeroBytes, header.Length - blobAssetLength);
            }

            var tempChunk = (Chunk *)UnsafeUtility.Malloc(Chunk.kChunkSize, 16, Allocator.Temp);

            for (int archetypeIndex = 0; archetypeIndex < archetypeArray.Length; ++archetypeIndex)
            {
                var archetype = archetypeArray[archetypeIndex].Archetype;
                for (var ci = 0; ci < archetype->Chunks.Count; ++ci)
                {
                    var chunk = archetype->Chunks.p[ci];
                    bufferPatches.Clear();

                    UnsafeUtility.MemCpy(tempChunk, chunk, Chunk.kChunkSize);
                    tempChunk->metaChunkEntity = EntityRemapUtility.RemapEntity(ref entityRemapInfos, tempChunk->metaChunkEntity);

                    // Prevent patching from touching buffers allocated memory
                    BufferHeader.PatchAfterCloningChunk(tempChunk);

                    byte *tempChunkBuffer = tempChunk->Buffer;
                    EntityRemapUtility.PatchEntities(archetype->ScalarEntityPatches, archetype->ScalarEntityPatchCount, archetype->BufferEntityPatches, archetype->BufferEntityPatchCount, tempChunkBuffer, tempChunk->Count, ref entityRemapInfos);
                    if (archetype->ContainsBlobAssetRefs)
                    {
                        PatchBlobAssetsInChunkBeforeSave(tempChunk, chunk, blobAssetOffsets, blobAssetRefs);
                    }

                    FillPatchRecordsForChunk(chunk, bufferPatches);

                    ClearChunkHeaderComponents(tempChunk);
                    ChunkDataUtility.MemsetUnusedChunkData(tempChunk, 0);
                    tempChunk->Archetype = (Archetype *)archetypeIndex;

                    if (archetype->NumManagedArrays != 0)
                    {
                        throw new ArgumentException("Serialization of GameObject components is not supported for pure entity scenes");
                    }

                    writer.WriteBytes(tempChunk, Chunk.kChunkSize);

                    writer.Write(bufferPatches.Length);

                    if (bufferPatches.Length > 0)
                    {
                        writer.WriteList(bufferPatches);

                        // Write heap backed data for each required patch.
                        // TODO: PERF: Investigate static-only deserialization could manage one block and mark in pointers somehow that they are not indiviual
                        for (int i = 0; i < bufferPatches.Length; ++i)
                        {
                            var patch  = bufferPatches[i];
                            var header = (BufferHeader *)OffsetFromPointer(tempChunk->Buffer, patch.ChunkOffset);
                            writer.WriteBytes(header->Pointer, patch.AllocSizeBytes);
                            BufferHeader.Destroy(header);
                        }
                    }
                }
            }

            blobAssetRefs.Dispose();
            blobAssets.Dispose();

            bufferPatches.Dispose();
            UnsafeUtility.Free(tempChunk, Allocator.Temp);

            sharedComponentsToSerialize = new int[sharedComponentMapping.Count - 1];

            foreach (var i in sharedComponentMapping)
            {
                if (i.Key != 0)
                {
                    sharedComponentsToSerialize[i.Value - 1] = i.Key;
                }
            }
        }
Exemplo n.º 12
0
 public void RemapEntityMapsNullSourceToNull()
 {
     Assert.AreEqual(Entity.Null, EntityRemapUtility.RemapEntity(ref m_Remapping, Entity.Null));
 }
Exemplo n.º 13
0
        public static SceneData[] WriteEntityScene(Scene scene, Hash128 sceneGUID, ConversionFlags conversionFlags)
        {
            var world         = new World("ConversionWorld");
            var entityManager = world.GetOrCreateManager <EntityManager>();

            var boundsEntity = entityManager.CreateEntity(typeof(SceneBoundingVolume));

            entityManager.SetComponentData(boundsEntity, new SceneBoundingVolume {
                Value = MinMaxAABB.Empty
            });

            ConvertScene(scene, sceneGUID, world, conversionFlags);
            EntitySceneOptimization.Optimize(world);

            var bounds = entityManager.GetComponentData <SceneBoundingVolume>(boundsEntity).Value;

            entityManager.DestroyEntity(boundsEntity);

            var sceneSections = new List <SceneData>();

            var subSectionList = new List <SceneSection>();

            entityManager.GetAllUniqueSharedComponentData(subSectionList);
            var extRefInfoEntities = new NativeArray <Entity>(subSectionList.Count, Allocator.Temp);

            NativeArray <Entity> entitiesInMainSection;

            var sectionGrp = entityManager.CreateComponentGroup(
                new EntityArchetypeQuery
            {
                All     = new[] { ComponentType.ReadWrite <SceneSection>() },
                Options = EntityArchetypeQueryOptions.IncludePrefab | EntityArchetypeQueryOptions.IncludeDisabled
            }
                );

            {
                var section = new SceneSection {
                    SceneGUID = sceneGUID, Section = 0
                };
                sectionGrp.SetFilter(new SceneSection {
                    SceneGUID = sceneGUID, Section = 0
                });
                entitiesInMainSection = sectionGrp.ToEntityArray(Allocator.TempJob);

                // Each section will be serialized in its own world, entities that don't have a section are part of the main scene.
                // An entity that holds the array of external references to the main scene is required for each section.
                // We need to create them all before we start moving entities to section scenes,
                // otherwise they would reuse entities that have been moved and mess up the remapping tables.
                for (int sectionIndex = 1; sectionIndex < subSectionList.Count; ++sectionIndex)
                {
                    var extRefInfoEntity = entityManager.CreateEntity();
                    entityManager.AddSharedComponentData(extRefInfoEntity, subSectionList[sectionIndex]);
                    extRefInfoEntities[sectionIndex] = extRefInfoEntity;
                }

                // Public references array, only on the main section.
                var refInfoEntity = entityManager.CreateEntity();
                entityManager.AddBuffer <PublicEntityRef>(refInfoEntity);
                entityManager.AddSharedComponentData(refInfoEntity, section);
                var publicRefs = entityManager.GetBuffer <PublicEntityRef>(refInfoEntity);

//                entityManager.Debug.CheckInternalConsistency();

                //@TODO do we need to keep this index? doesn't carry any additional info
                for (int i = 0; i < entitiesInMainSection.Length; ++i)
                {
                    PublicEntityRef.Add(ref publicRefs,
                                        new PublicEntityRef {
                        entityIndex = i, targetEntity = entitiesInMainSection[i]
                    });
                }

                Debug.Assert(publicRefs.Length == entitiesInMainSection.Length);

                // Save main section
                var sectionWorld   = new World("SectionWorld");
                var sectionManager = sectionWorld.GetOrCreateManager <EntityManager>();

                var entityRemapping = entityManager.CreateEntityRemapArray(Allocator.TempJob);
                sectionManager.MoveEntitiesFrom(entityManager, sectionGrp, entityRemapping);

                // The section component is only there to break the conversion world into different sections
                // We don't want to store that on the disk
                //@TODO: Component should be removed but currently leads to corrupt data file. Figure out why.
                //sectionManager.RemoveComponent(sectionManager.UniversalGroup, typeof(SceneSection));

                var sectionFileSize = WriteEntityScene(sectionManager, sceneGUID, "0");
                sceneSections.Add(new SceneData
                {
                    FileSize             = sectionFileSize,
                    SceneGUID            = sceneGUID,
                    SharedComponentCount = sectionManager.GetSharedComponentCount() - 1,
                    SubSectionIndex      = 0,
                    BoundingVolume       = bounds
                });

                entityRemapping.Dispose();
                sectionWorld.Dispose();
            }

            {
                // Index 0 is the default value of the shared component, not an actual section
                for (int subSectionIndex = 0; subSectionIndex < subSectionList.Count; ++subSectionIndex)
                {
                    var subSection = subSectionList[subSectionIndex];
                    if (subSection.Section == 0)
                    {
                        continue;
                    }

                    sectionGrp.SetFilter(subSection);
                    var entitiesInSection = sectionGrp.ToEntityArray(Allocator.TempJob);

                    if (entitiesInSection.Length > 0)
                    {
                        // Fetch back the external reference entity we created earlier to not disturb the mapping
                        var refInfoEntity = extRefInfoEntities[subSectionIndex];
                        entityManager.AddBuffer <ExternalEntityRef>(refInfoEntity);
                        var externRefs = entityManager.GetBuffer <ExternalEntityRef>(refInfoEntity);

                        // Store the mapping to everything in the main section
                        //@TODO maybe we don't need all that? is this worth worrying about?
                        for (int i = 0; i < entitiesInMainSection.Length; ++i)
                        {
                            ExternalEntityRef.Add(ref externRefs, new ExternalEntityRef {
                                entityIndex = i
                            });
                        }

                        // Entities will be remapped to a contiguous range in the section world,
                        // so any range after that is fine for the external references
                        //@TODO why are we not mapping anything to entity 0? we use the range [1;count], hence +1
                        var externEntityIndexStart = entitiesInSection.Length + 1;

                        entityManager.AddComponentData(refInfoEntity,
                                                       new ExternalEntityRefInfo
                        {
                            SceneGUID        = sceneGUID,
                            EntityIndexStart = externEntityIndexStart
                        });

                        var sectionWorld   = new World("SectionWorld");
                        var sectionManager = sectionWorld.GetOrCreateManager <EntityManager>();

                        var entityRemapping = entityManager.CreateEntityRemapArray(Allocator.TempJob);

                        // Insert mapping for external references, conversion world entity to virtual index in section
                        for (int i = 0; i < entitiesInMainSection.Length; ++i)
                        {
                            EntityRemapUtility.AddEntityRemapping(ref entityRemapping, entitiesInMainSection[i],
                                                                  new Entity {
                                Index = i + externEntityIndexStart, Version = 1
                            });
                        }

                        sectionManager.MoveEntitiesFrom(entityManager, sectionGrp, entityRemapping);

                        // When writing the scene, references to missing entities are set to Entity.Null by default
                        // We obviously don't want that to happen to our external references, so we add explicit mapping
                        for (int i = 0; i < entitiesInMainSection.Length; ++i)
                        {
                            var entity = new Entity {
                                Index = i + externEntityIndexStart, Version = 1
                            };
                            EntityRemapUtility.AddEntityRemapping(ref entityRemapping, entity, entity);
                        }

                        // The section component is only there to break the conversion world into different sections
                        // We don't want to store that on the disk
                        //@TODO: Component should be removed but currently leads to corrupt data file. Figure out why.
                        //sectionManager.RemoveComponent(sectionManager.UniversalGroup, typeof(SceneSection));

                        var fileSize = WriteEntityScene(sectionManager, sceneGUID, subSection.Section.ToString(), entityRemapping);
                        sceneSections.Add(new SceneData
                        {
                            FileSize             = fileSize,
                            SceneGUID            = sceneGUID,
                            SharedComponentCount = sectionManager.GetSharedComponentCount() - 1,
                            SubSectionIndex      = subSection.Section,
                            BoundingVolume       = bounds
                        });

                        entityRemapping.Dispose();
                        sectionWorld.Dispose();
                    }

                    entitiesInSection.Dispose();
                }
            }

            {
                var noSectionGrp = entityManager.CreateComponentGroup(
                    new EntityArchetypeQuery
                {
                    None    = new[] { ComponentType.ReadWrite <SceneSection>() },
                    Options = EntityArchetypeQueryOptions.IncludePrefab | EntityArchetypeQueryOptions.IncludeDisabled
                }
                    );
                if (noSectionGrp.CalculateLength() != 0)
                {
                    Debug.LogWarning($"{noSectionGrp.CalculateLength()} entities in the scene '{scene.path}' had no SceneSection and as a result were not serialized at all.");
                }
            }

            sectionGrp.Dispose();
            entitiesInMainSection.Dispose();
            world.Dispose();

            // Save the new header
            var header = ScriptableObject.CreateInstance <SubSceneHeader>();

            header.Sections = sceneSections.ToArray();

            WriteHeader(sceneGUID, header);

            return(sceneSections.ToArray());
        }
Exemplo n.º 14
0
        public static unsafe void SerializeWorld(EntityManager entityManager, BinaryWriter writer, out int[] sharedComponentsToSerialize, NativeArray <EntityRemapUtility.EntityRemapInfo> entityRemapInfos)
        {
            writer.Write(CurrentFileFormatVersion);
            var entityComponentStore = entityManager.EntityComponentStore;

            NativeHashMap <EntityArchetype, int> archetypeToIndex;

            EntityArchetype[] archetypeArray;
            GetAllArchetypes(entityComponentStore, out archetypeToIndex, out archetypeArray);

            var typeHashes = new NativeHashMap <ulong, int>(1024, Allocator.Temp);

            foreach (var archetype in archetypeArray)
            {
                for (int iType = 0; iType < archetype.Archetype->TypesCount; ++iType)
                {
                    var typeIndex = archetype.Archetype->Types[iType].TypeIndex;
                    var ti        = TypeManager.GetTypeInfo(typeIndex);
                    var hash      = ti.StableTypeHash;
                    typeHashes.TryAdd(hash, 0);
                }
            }
            var typeHashSet = typeHashes.GetKeyArray(Allocator.Temp);

            writer.Write(typeHashSet.Length);
            foreach (ulong hash in typeHashSet)
            {
                writer.Write(hash);
            }

            var typeHashToIndexMap = new NativeHashMap <ulong, int>(typeHashSet.Length, Allocator.Temp);

            for (int i = 0; i < typeHashes.Length; ++i)
            {
                typeHashToIndexMap.TryAdd(typeHashSet[i], i);
            }

            WriteArchetypes(writer, archetypeArray, typeHashToIndexMap);
            var sharedComponentMapping = GatherSharedComponents(archetypeArray, out var sharedComponentArraysTotalCount);
            var sharedComponentArrays  = new NativeArray <int>(sharedComponentArraysTotalCount, Allocator.Temp);

            FillSharedComponentArrays(sharedComponentArrays, archetypeArray, sharedComponentMapping);
            writer.Write(sharedComponentArrays.Length);
            writer.WriteArray(sharedComponentArrays);
            sharedComponentArrays.Dispose();

            //TODO: ensure chunks are defragged?

            var bufferPatches   = new NativeList <BufferPatchRecord>(128, Allocator.Temp);
            var totalChunkCount = GenerateRemapInfo(entityManager, archetypeArray, entityRemapInfos);

            writer.Write(totalChunkCount);

            GatherAllUsedBlobAssets(archetypeArray, out var blobAssetRefs, out var blobAssets);

            var blobAssetOffsets   = new NativeArray <int>(blobAssets.Length, Allocator.Temp);
            int totalBlobAssetSize = 0;

            int Align16(int x) => (x + 15) & ~15;

            for (int i = 0; i < blobAssets.Length; ++i)
            {
                totalBlobAssetSize += sizeof(BlobAssetHeader);
                blobAssetOffsets[i] = totalBlobAssetSize;
                totalBlobAssetSize += Align16(blobAssets[i].header->Length);
            }

            writer.Write(totalBlobAssetSize);

            var zeroBytes = int4.zero;

            for (int i = 0; i < blobAssets.Length; ++i)
            {
                var             blobAssetLength = blobAssets[i].header->Length;
                BlobAssetHeader header          = new BlobAssetHeader
                {
                    ValidationPtr = null, Allocator = Allocator.None, Length = Align16(blobAssetLength)
                };
                writer.WriteBytes(&header, sizeof(BlobAssetHeader));
                writer.WriteBytes(blobAssets[i].header + 1, blobAssetLength);
                writer.WriteBytes(&zeroBytes, header.Length - blobAssetLength);
            }

            var tempChunk = (Chunk *)UnsafeUtility.Malloc(Chunk.kChunkSize, 16, Allocator.Temp);

            for (int archetypeIndex = 0; archetypeIndex < archetypeArray.Length; ++archetypeIndex)
            {
                var archetype = archetypeArray[archetypeIndex].Archetype;
                for (var ci = 0; ci < archetype->Chunks.Count; ++ci)
                {
                    var chunk = archetype->Chunks.p[ci];
                    bufferPatches.Clear();

                    UnsafeUtility.MemCpy(tempChunk, chunk, Chunk.kChunkSize);
                    tempChunk->metaChunkEntity = EntityRemapUtility.RemapEntity(ref entityRemapInfos, tempChunk->metaChunkEntity);

                    // Prevent patching from touching buffers allocated memory
                    BufferHeader.PatchAfterCloningChunk(tempChunk);

                    byte *tempChunkBuffer = tempChunk->Buffer;
                    EntityRemapUtility.PatchEntities(archetype->ScalarEntityPatches, archetype->ScalarEntityPatchCount, archetype->BufferEntityPatches, archetype->BufferEntityPatchCount, tempChunkBuffer, tempChunk->Count, ref entityRemapInfos);
                    if (archetype->ContainsBlobAssetRefs)
                    {
                        PatchBlobAssetsInChunkBeforeSave(tempChunk, chunk, blobAssetOffsets, blobAssetRefs);
                    }

                    FillPatchRecordsForChunk(chunk, bufferPatches);

                    ClearChunkHeaderComponents(tempChunk);
                    ChunkDataUtility.MemsetUnusedChunkData(tempChunk, 0);
                    tempChunk->Archetype = (Archetype *)archetypeIndex;

                    if (archetype->NumManagedArrays != 0)
                    {
                        throw new ArgumentException("Serialization of GameObject components is not supported for pure entity scenes");
                    }

                    writer.WriteBytes(tempChunk, Chunk.kChunkSize);

                    writer.Write(bufferPatches.Length);

                    if (bufferPatches.Length > 0)
                    {
                        writer.WriteList(bufferPatches);

                        // Write heap backed data for each required patch.
                        // TODO: PERF: Investigate static-only deserialization could manage one block and mark in pointers somehow that they are not indiviual
                        for (int i = 0; i < bufferPatches.Length; ++i)
                        {
                            var patch  = bufferPatches[i];
                            var header = (BufferHeader *)OffsetFromPointer(tempChunk->Buffer, patch.ChunkOffset);
                            writer.WriteBytes(header->Pointer, patch.AllocSizeBytes);
                            BufferHeader.Destroy(header);
                        }
                    }
                }
            }

            blobAssetRefs.Dispose();
            blobAssets.Dispose();

            bufferPatches.Dispose();
            UnsafeUtility.Free(tempChunk, Allocator.Temp);

            sharedComponentsToSerialize = new int[sharedComponentMapping.Length - 1];

            using (var keyArray = sharedComponentMapping.GetKeyArray(Allocator.Temp))
                foreach (var key in keyArray)
                {
                    if (key == 0)
                    {
                        continue;
                    }

                    if (sharedComponentMapping.TryGetValue(key, out var val))
                    {
                        sharedComponentsToSerialize[val - 1] = key;
                    }
                }

            archetypeToIndex.Dispose();
            typeHashes.Dispose();
            typeHashSet.Dispose();
            typeHashToIndexMap.Dispose();
        }
Exemplo n.º 15
0
        public static SceneSectionData[] WriteEntityScene(Scene scene, GameObjectConversionSettings settings, List <ReferencedUnityObjects> sectionRefObjs = null)
        {
            int framesToRetainBlobAssets = RetainBlobAssetsSetting.GetFramesToRetainBlobAssets(settings.BuildConfiguration);

            var world         = new World("ConversionWorld");
            var entityManager = world.EntityManager;

            settings.DestinationWorld = world;

            bool disposeBlobAssetCache = false;

            if (settings.BlobAssetStore == null)
            {
                settings.BlobAssetStore = new BlobAssetStore();
                disposeBlobAssetCache   = true;
            }

            List <(int, LogEventData)> journalData = null;

            settings.ConversionWorldPreDispose += conversionWorld =>
            {
                var mappingSystem = conversionWorld.GetExistingSystem <GameObjectConversionMappingSystem>();
                journalData = mappingSystem.JournalData.SelectLogEventsOrdered().ToList();
            };

            ConvertScene(scene, settings);
            EntitySceneOptimization.Optimize(world);

            if (settings.AssetImportContext != null)
            {
                using (var allTypes = new NativeHashMap <ComponentType, int>(100, Allocator.Temp))
                    using (var archetypes = new NativeList <EntityArchetype>(Allocator.Temp))
                    {
                        entityManager.GetAllArchetypes(archetypes);
                        foreach (var archetype in archetypes)
                        {
                            using (var componentTypes = archetype.GetComponentTypes())
                                foreach (var componentType in componentTypes)
                                {
                                    if (allTypes.TryAdd(componentType, 0))
                                    {
                                        TypeDependencyCache.AddDependency(settings.AssetImportContext, componentType);
                                    }
                                }
                        }
                    }

                TypeDependencyCache.AddAllSystemsDependency(settings.AssetImportContext);
            }


            var sceneSections = new List <SceneSectionData>();

            var subSectionList = new List <SceneSection>();

            entityManager.GetAllUniqueSharedComponentData(subSectionList);
            var extRefInfoEntities = new NativeArray <Entity>(subSectionList.Count, Allocator.Temp);

            NativeArray <Entity> entitiesInMainSection;

            var sectionQuery = entityManager.CreateEntityQuery(
                new EntityQueryDesc
            {
                All     = new[] { ComponentType.ReadWrite <SceneSection>() },
                Options = EntityQueryOptions.IncludePrefab | EntityQueryOptions.IncludeDisabled
            }
                );

            var sectionBoundsQuery = entityManager.CreateEntityQuery(
                new EntityQueryDesc
            {
                All     = new[] { ComponentType.ReadWrite <SceneBoundingVolume>(), ComponentType.ReadWrite <SceneSection>() },
                Options = EntityQueryOptions.IncludePrefab | EntityQueryOptions.IncludeDisabled
            }
                );

            var sceneGUID = settings.SceneGUID;

            {
                var section = new SceneSection {
                    SceneGUID = sceneGUID, Section = 0
                };
                sectionQuery.SetSharedComponentFilter(new SceneSection {
                    SceneGUID = sceneGUID, Section = 0
                });
                sectionBoundsQuery.SetSharedComponentFilter(new SceneSection {
                    SceneGUID = sceneGUID, Section = 0
                });
                entitiesInMainSection = sectionQuery.ToEntityArray(Allocator.TempJob);


                var bounds = GetBoundsAndRemove(entityManager, sectionBoundsQuery);

                // Each section will be serialized in its own world, entities that don't have a section are part of the main scene.
                // An entity that holds the array of external references to the main scene is required for each section.
                // We need to create them all before we start moving entities to section scenes,
                // otherwise they would reuse entities that have been moved and mess up the remapping tables.
                for (int sectionIndex = 1; sectionIndex < subSectionList.Count; ++sectionIndex)
                {
                    if (subSectionList[sectionIndex].Section == 0)
                    {
                        // Main section, the only one that doesn't need an external ref array
                        continue;
                    }

                    var extRefInfoEntity = entityManager.CreateEntity();
                    entityManager.AddSharedComponentData(extRefInfoEntity, subSectionList[sectionIndex]);
                    extRefInfoEntities[sectionIndex] = extRefInfoEntity;
                }

                // Public references array, only on the main section.
                var refInfoEntity = entityManager.CreateEntity();
                entityManager.AddBuffer <PublicEntityRef>(refInfoEntity);
                entityManager.AddSharedComponentData(refInfoEntity, section);
                var publicRefs = entityManager.GetBuffer <PublicEntityRef>(refInfoEntity);

//                entityManager.Debug.CheckInternalConsistency();

                //@TODO do we need to keep this index? doesn't carry any additional info
                for (int i = 0; i < entitiesInMainSection.Length; ++i)
                {
                    PublicEntityRef.Add(ref publicRefs,
                                        new PublicEntityRef {
                        entityIndex = i, targetEntity = entitiesInMainSection[i]
                    });
                }

                UnityEngine.Debug.Assert(publicRefs.Length == entitiesInMainSection.Length);

                // Save main section
                var sectionWorld   = new World("SectionWorld");
                var sectionManager = sectionWorld.EntityManager;

                var entityRemapping = entityManager.CreateEntityRemapArray(Allocator.TempJob);
                sectionManager.MoveEntitiesFrom(entityManager, sectionQuery, entityRemapping);

                AddRetainBlobAssetsEntity(sectionManager, framesToRetainBlobAssets);

                // The section component is only there to break the conversion world into different sections
                // We don't want to store that on the disk
                //@TODO: Component should be removed but currently leads to corrupt data file. Figure out why.
                //sectionManager.RemoveComponent(sectionManager.UniversalQuery, typeof(SceneSection));

                var sectionFileSize = WriteEntityScene(sectionManager, sceneGUID, "0", settings, out var objectRefCount, out var objRefs);
                sectionRefObjs?.Add(objRefs);
                sceneSections.Add(new SceneSectionData
                {
                    FileSize             = sectionFileSize,
                    SceneGUID            = sceneGUID,
                    ObjectReferenceCount = objectRefCount,
                    SubSectionIndex      = 0,
                    BoundingVolume       = bounds
                });

                entityRemapping.Dispose();
                sectionWorld.Dispose();
            }

            {
                // Index 0 is the default value of the shared component, not an actual section
                for (int subSectionIndex = 0; subSectionIndex < subSectionList.Count; ++subSectionIndex)
                {
                    var subSection = subSectionList[subSectionIndex];
                    if (subSection.Section == 0)
                    {
                        continue;
                    }

                    sectionQuery.SetSharedComponentFilter(subSection);
                    sectionBoundsQuery.SetSharedComponentFilter(subSection);

                    var bounds = GetBoundsAndRemove(entityManager, sectionBoundsQuery);

                    var entitiesInSection = sectionQuery.ToEntityArray(Allocator.TempJob);

                    if (entitiesInSection.Length > 0)
                    {
                        // Fetch back the external reference entity we created earlier to not disturb the mapping
                        var refInfoEntity = extRefInfoEntities[subSectionIndex];
                        entityManager.AddBuffer <ExternalEntityRef>(refInfoEntity);
                        var externRefs = entityManager.GetBuffer <ExternalEntityRef>(refInfoEntity);

                        // Store the mapping to everything in the main section
                        //@TODO maybe we don't need all that? is this worth worrying about?
                        for (int i = 0; i < entitiesInMainSection.Length; ++i)
                        {
                            ExternalEntityRef.Add(ref externRefs, new ExternalEntityRef {
                                entityIndex = i
                            });
                        }

                        var entityRemapping = entityManager.CreateEntityRemapArray(Allocator.TempJob);

                        // Entities will be remapped to a contiguous range in the section world, but they will
                        // also come with an unpredictable amount of meta entities. We have the guarantee that
                        // the entities in the main section won't be moved over, so there's a free range of that
                        // size at the end of the remapping table. So we use that range for external references.
                        var externEntityIndexStart = entityRemapping.Length - entitiesInMainSection.Length;

                        entityManager.AddComponentData(refInfoEntity,
                                                       new ExternalEntityRefInfo
                        {
                            SceneGUID        = sceneGUID,
                            EntityIndexStart = externEntityIndexStart
                        });

                        var sectionWorld   = new World("SectionWorld");
                        var sectionManager = sectionWorld.EntityManager;

                        // Insert mapping for external references, conversion world entity to virtual index in section
                        for (int i = 0; i < entitiesInMainSection.Length; ++i)
                        {
                            EntityRemapUtility.AddEntityRemapping(ref entityRemapping, entitiesInMainSection[i],
                                                                  new Entity {
                                Index = i + externEntityIndexStart, Version = 1
                            });
                        }

                        sectionManager.MoveEntitiesFrom(entityManager, sectionQuery, entityRemapping);

                        AddRetainBlobAssetsEntity(sectionManager, framesToRetainBlobAssets);
                        // Now that all the required entities have been moved over, we can get rid of the gap between
                        // real entities and external references. This allows remapping during load to deal with a
                        // smaller remap table, containing only useful entries.

                        int highestEntityIndexInUse = 0;
                        for (int i = 0; i < externEntityIndexStart; ++i)
                        {
                            var targetIndex = entityRemapping[i].Target.Index;
                            if (targetIndex < externEntityIndexStart && targetIndex > highestEntityIndexInUse)
                            {
                                highestEntityIndexInUse = targetIndex;
                            }
                        }

                        var oldExternEntityIndexStart = externEntityIndexStart;
                        externEntityIndexStart = highestEntityIndexInUse + 1;

                        sectionManager.SetComponentData
                        (
                            EntityRemapUtility.RemapEntity(ref entityRemapping, refInfoEntity),
                            new ExternalEntityRefInfo
                        {
                            SceneGUID        = sceneGUID,
                            EntityIndexStart = externEntityIndexStart
                        }
                        );

                        // When writing the scene, references to missing entities are set to Entity.Null by default
                        // (but only if they have been used, otherwise they remain untouched)
                        // We obviously don't want that to happen to our external references, so we add explicit mapping
                        // And at the same time, we put them back at the end of the effective range of real entities.
                        for (int i = 0; i < entitiesInMainSection.Length; ++i)
                        {
                            var src = new Entity {
                                Index = i + oldExternEntityIndexStart, Version = 1
                            };
                            var dst = new Entity {
                                Index = i + externEntityIndexStart, Version = 1
                            };
                            EntityRemapUtility.AddEntityRemapping(ref entityRemapping, src, dst);
                        }

                        // The section component is only there to break the conversion world into different sections
                        // We don't want to store that on the disk
                        //@TODO: Component should be removed but currently leads to corrupt data file. Figure out why.
                        //sectionManager.RemoveComponent(sectionManager.UniversalQuery, typeof(SceneSection));
                        var fileSize = WriteEntityScene(sectionManager, sceneGUID, subSection.Section.ToString(), settings, out var objectRefCount, out var objRefs, entityRemapping);
                        sectionRefObjs?.Add(objRefs);
                        sceneSections.Add(new SceneSectionData
                        {
                            FileSize             = fileSize,
                            SceneGUID            = sceneGUID,
                            ObjectReferenceCount = objectRefCount,
                            SubSectionIndex      = subSection.Section,
                            BoundingVolume       = bounds
                        });

                        entityRemapping.Dispose();
                        sectionWorld.Dispose();
                    }

                    entitiesInSection.Dispose();
                }
            }

            {
                var noSectionQuery = entityManager.CreateEntityQuery(
                    new EntityQueryDesc
                {
                    None    = new[] { ComponentType.ReadWrite <SceneSection>() },
                    Options = EntityQueryOptions.IncludePrefab | EntityQueryOptions.IncludeDisabled
                }
                    );
                if (noSectionQuery.CalculateEntityCount() != 0)
                {
                    Debug.LogWarning($"{noSectionQuery.CalculateEntityCount()} entities in the scene '{scene.path}' had no SceneSection and as a result were not serialized at all.");
                }
            }

            sectionQuery.Dispose();
            sectionBoundsQuery.Dispose();
            entitiesInMainSection.Dispose();
            world.Dispose();

            // Save the new header

            var sceneSectionsArray = sceneSections.ToArray();

            WriteHeader(sceneGUID, sceneSectionsArray, scene.name, settings.AssetImportContext);

            // If we are writing assets to assets folder directly, then we need to make sure the asset database see them so they can be loaded.
            if (settings.AssetImportContext == null)
            {
                AssetDatabase.Refresh();
            }

            if (disposeBlobAssetCache)
            {
                settings.BlobAssetStore.Dispose();
            }

            // Save the log of issues that happened during conversion

            WriteConversionLog(sceneGUID, journalData, scene.name, settings.AssetImportContext);

            return(sceneSectionsArray);
        }
        public void CalculateEntityOffsetsReturnsNullIfNoEntities()
        {
            var offsets = EntityRemapUtility.CalculateEntityOffsets(typeof(EmptyStruct));

            Assert.AreEqual(null, offsets);
        }
Exemplo n.º 17
0
        public static SceneData[] WriteEntityScene(Scene scene, Hash128 sceneGUID, ConversionFlags conversionFlags)
        {
            var world         = new World("ConversionWorld");
            var entityManager = world.EntityManager;

            ConvertScene(scene, sceneGUID, world, conversionFlags);
            EntitySceneOptimization.Optimize(world);

            var sceneSections = new List <SceneData>();

            var subSectionList = new List <SceneSection>();

            entityManager.GetAllUniqueSharedComponentData(subSectionList);
            var extRefInfoEntities = new NativeArray <Entity>(subSectionList.Count, Allocator.Temp);

            NativeArray <Entity> entitiesInMainSection;

            var sectionQuery = entityManager.CreateEntityQuery(
                new EntityQueryDesc
            {
                All     = new[] { ComponentType.ReadWrite <SceneSection>() },
                Options = EntityQueryOptions.IncludePrefab | EntityQueryOptions.IncludeDisabled
            }
                );

            var sectionBoundsQuery = entityManager.CreateEntityQuery(
                new EntityQueryDesc
            {
                All     = new[] { ComponentType.ReadWrite <SceneBoundingVolume>(), ComponentType.ReadWrite <SceneSection>() },
                Options = EntityQueryOptions.IncludePrefab | EntityQueryOptions.IncludeDisabled
            }
                );

            {
                var section = new SceneSection {
                    SceneGUID = sceneGUID, Section = 0
                };
                sectionQuery.SetFilter(new SceneSection {
                    SceneGUID = sceneGUID, Section = 0
                });
                sectionBoundsQuery.SetFilter(new SceneSection {
                    SceneGUID = sceneGUID, Section = 0
                });
                entitiesInMainSection = sectionQuery.ToEntityArray(Allocator.TempJob);


                var bounds = GetBoundsAndDestroy(entityManager, sectionBoundsQuery);

                // Each section will be serialized in its own world, entities that don't have a section are part of the main scene.
                // An entity that holds the array of external references to the main scene is required for each section.
                // We need to create them all before we start moving entities to section scenes,
                // otherwise they would reuse entities that have been moved and mess up the remapping tables.
                for (int sectionIndex = 1; sectionIndex < subSectionList.Count; ++sectionIndex)
                {
                    if (subSectionList[sectionIndex].Section == 0)
                    {
                        // Main section, the only one that doesn't need an external ref array
                        continue;
                    }

                    var extRefInfoEntity = entityManager.CreateEntity();
                    entityManager.AddSharedComponentData(extRefInfoEntity, subSectionList[sectionIndex]);
                    extRefInfoEntities[sectionIndex] = extRefInfoEntity;
                }

                // Public references array, only on the main section.
                var refInfoEntity = entityManager.CreateEntity();
                entityManager.AddBuffer <PublicEntityRef>(refInfoEntity);
                entityManager.AddSharedComponentData(refInfoEntity, section);
                var publicRefs = entityManager.GetBuffer <PublicEntityRef>(refInfoEntity);

//                entityManager.Debug.CheckInternalConsistency();

                //@TODO do we need to keep this index? doesn't carry any additional info
                for (int i = 0; i < entitiesInMainSection.Length; ++i)
                {
                    PublicEntityRef.Add(ref publicRefs,
                                        new PublicEntityRef {
                        entityIndex = i, targetEntity = entitiesInMainSection[i]
                    });
                }

                Debug.Assert(publicRefs.Length == entitiesInMainSection.Length);

                // Save main section
                var sectionWorld   = new World("SectionWorld");
                var sectionManager = sectionWorld.EntityManager;

                var entityRemapping = entityManager.CreateEntityRemapArray(Allocator.TempJob);
                sectionManager.MoveEntitiesFrom(entityManager, sectionQuery, entityRemapping);

                // The section component is only there to break the conversion world into different sections
                // We don't want to store that on the disk
                //@TODO: Component should be removed but currently leads to corrupt data file. Figure out why.
                //sectionManager.RemoveComponent(sectionManager.UniversalQuery, typeof(SceneSection));

                var sectionFileSize = WriteEntityScene(sectionManager, sceneGUID, "0");
                sceneSections.Add(new SceneData
                {
                    FileSize             = sectionFileSize,
                    SceneGUID            = sceneGUID,
                    SharedComponentCount = sectionManager.GetSharedComponentCount() - 1,
                    SubSectionIndex      = 0,
                    BoundingVolume       = bounds
                });

                entityRemapping.Dispose();
                sectionWorld.Dispose();
            }

            {
                // Index 0 is the default value of the shared component, not an actual section
                for (int subSectionIndex = 0; subSectionIndex < subSectionList.Count; ++subSectionIndex)
                {
                    var subSection = subSectionList[subSectionIndex];
                    if (subSection.Section == 0)
                    {
                        continue;
                    }

                    sectionQuery.SetFilter(subSection);
                    sectionBoundsQuery.SetFilter(subSection);

                    var bounds = GetBoundsAndDestroy(entityManager, sectionBoundsQuery);

                    var entitiesInSection = sectionQuery.ToEntityArray(Allocator.TempJob);

                    if (entitiesInSection.Length > 0)
                    {
                        // Fetch back the external reference entity we created earlier to not disturb the mapping
                        var refInfoEntity = extRefInfoEntities[subSectionIndex];
                        entityManager.AddBuffer <ExternalEntityRef>(refInfoEntity);
                        var externRefs = entityManager.GetBuffer <ExternalEntityRef>(refInfoEntity);

                        // Store the mapping to everything in the main section
                        //@TODO maybe we don't need all that? is this worth worrying about?
                        for (int i = 0; i < entitiesInMainSection.Length; ++i)
                        {
                            ExternalEntityRef.Add(ref externRefs, new ExternalEntityRef {
                                entityIndex = i
                            });
                        }

                        var entityRemapping = entityManager.CreateEntityRemapArray(Allocator.TempJob);

                        // Entities will be remapped to a contiguous range in the section world, but they will
                        // also come with an unpredictable amount of meta entities. We have the guarantee that
                        // the entities in the main section won't be moved over, so there's a free range of that
                        // size at the end of the remapping table. So we use that range for external references.
                        var externEntityIndexStart = entityRemapping.Length - entitiesInMainSection.Length;

                        entityManager.AddComponentData(refInfoEntity,
                                                       new ExternalEntityRefInfo
                        {
                            SceneGUID        = sceneGUID,
                            EntityIndexStart = externEntityIndexStart
                        });

                        var sectionWorld   = new World("SectionWorld");
                        var sectionManager = sectionWorld.EntityManager;

                        // Insert mapping for external references, conversion world entity to virtual index in section
                        for (int i = 0; i < entitiesInMainSection.Length; ++i)
                        {
                            EntityRemapUtility.AddEntityRemapping(ref entityRemapping, entitiesInMainSection[i],
                                                                  new Entity {
                                Index = i + externEntityIndexStart, Version = 1
                            });
                        }

                        sectionManager.MoveEntitiesFrom(entityManager, sectionQuery, entityRemapping);

                        // Now that all the required entities have been moved over, we can get rid of the gap between
                        // real entities and external references. This allows remapping during load to deal with a
                        // smaller remap table, containing only useful entries.

                        int highestEntityIndexInUse = 0;
                        for (int i = 0; i < externEntityIndexStart; ++i)
                        {
                            var targetIndex = entityRemapping[i].Target.Index;
                            if (targetIndex < externEntityIndexStart && targetIndex > highestEntityIndexInUse)
                            {
                                highestEntityIndexInUse = targetIndex;
                            }
                        }

                        var oldExternEntityIndexStart = externEntityIndexStart;
                        externEntityIndexStart = highestEntityIndexInUse + 1;

                        sectionManager.SetComponentData
                        (
                            EntityRemapUtility.RemapEntity(ref entityRemapping, refInfoEntity),
                            new ExternalEntityRefInfo
                        {
                            SceneGUID        = sceneGUID,
                            EntityIndexStart = externEntityIndexStart
                        }
                        );

                        // When writing the scene, references to missing entities are set to Entity.Null by default
                        // (but only if they have been used, otherwise they remain untouched)
                        // We obviously don't want that to happen to our external references, so we add explicit mapping
                        // And at the same time, we put them back at the end of the effective range of real entities.
                        for (int i = 0; i < entitiesInMainSection.Length; ++i)
                        {
                            var src = new Entity {
                                Index = i + oldExternEntityIndexStart, Version = 1
                            };
                            var dst = new Entity {
                                Index = i + externEntityIndexStart, Version = 1
                            };
                            EntityRemapUtility.AddEntityRemapping(ref entityRemapping, src, dst);
                        }

                        // The section component is only there to break the conversion world into different sections
                        // We don't want to store that on the disk
                        //@TODO: Component should be removed but currently leads to corrupt data file. Figure out why.
                        //sectionManager.RemoveComponent(sectionManager.UniversalQuery, typeof(SceneSection));

                        var fileSize = WriteEntityScene(sectionManager, sceneGUID, subSection.Section.ToString(), entityRemapping);
                        sceneSections.Add(new SceneData
                        {
                            FileSize             = fileSize,
                            SceneGUID            = sceneGUID,
                            SharedComponentCount = sectionManager.GetSharedComponentCount() - 1,
                            SubSectionIndex      = subSection.Section,
                            BoundingVolume       = bounds
                        });

                        entityRemapping.Dispose();
                        sectionWorld.Dispose();
                    }

                    entitiesInSection.Dispose();
                }
            }

            {
                var noSectionQuery = entityManager.CreateEntityQuery(
                    new EntityQueryDesc
                {
                    None    = new[] { ComponentType.ReadWrite <SceneSection>() },
                    Options = EntityQueryOptions.IncludePrefab | EntityQueryOptions.IncludeDisabled
                }
                    );
                if (noSectionQuery.CalculateLength() != 0)
                {
                    Debug.LogWarning($"{noSectionQuery.CalculateLength()} entities in the scene '{scene.path}' had no SceneSection and as a result were not serialized at all.");
                }
            }

            sectionQuery.Dispose();
            sectionBoundsQuery.Dispose();
            entitiesInMainSection.Dispose();
            world.Dispose();

            // Save the new header
            var header = ScriptableObject.CreateInstance <SubSceneHeader>();

            header.Sections = sceneSections.ToArray();

            WriteHeader(sceneGUID, header);

            return(sceneSections.ToArray());
        }
Exemplo n.º 18
0
        public static unsafe void SerializeWorld(EntityManager entityManager, BinaryWriter writer, out int[] sharedComponentsToSerialize)
        {
            writer.Write(CurrentFileFormatVersion);
            var archetypeManager = entityManager.ArchetypeManager;

            Dictionary <EntityArchetype, int> archetypeToIndex;

            EntityArchetype[] archetypeArray;
            GetAllArchetypes(archetypeManager, out archetypeToIndex, out archetypeArray);

            var typeindices = new HashSet <int>();

            foreach (var archetype in archetypeArray)
            {
                for (int iType = 0; iType < archetype.Archetype->TypesCount; ++iType)
                {
                    typeindices.Add(archetype.Archetype->Types[iType].TypeIndex);
                }
            }

            var typeArray = typeindices.Select(index =>
            {
                var type = TypeManager.GetType(index);
                var name = TypeManager.GetType(index).AssemblyQualifiedName;
                var hash = TypeManager.GetTypeInfo(index).FastEqualityTypeInfo.Hash;
                return(new
                {
                    index,
                    type,
                    name,
                    hash,
                    asciiName = Encoding.ASCII.GetBytes(name)
                });
            }).OrderBy(t => t.name).ToArray();

            int typeNameBufferSize = typeArray.Sum(t => t.asciiName.Length + 1);

            writer.Write(typeArray.Length);
            foreach (var n in typeArray)
            {
                writer.Write(n.hash);
            }

            writer.Write(typeNameBufferSize);
            foreach (var n in typeArray)
            {
                writer.Write(n.asciiName);
                writer.Write((byte)0);
            }

            var typeIndexMap = new Dictionary <int, int>();

            for (int i = 0; i < typeArray.Length; ++i)
            {
                typeIndexMap[typeArray[i].index] = i;
            }

            WriteArchetypes(writer, archetypeArray, typeIndexMap);

            //TODO: ensure chunks are defragged?

            NativeArray <EntityRemapUtility.EntityRemapInfo> entityRemapInfos;
            var bufferPatches   = new NativeList <BufferPatchRecord>(128, Allocator.Temp);
            var totalChunkCount = GenerateRemapInfo(entityManager, archetypeArray, out entityRemapInfos);

            writer.Write(totalChunkCount);

            var tempChunk = (Chunk *)UnsafeUtility.Malloc(Chunk.kChunkSize, 16, Allocator.Temp);

            var sharedIndexToSerialize = new Dictionary <int, int>();

            for (int archetypeIndex = 0; archetypeIndex < archetypeArray.Length; ++archetypeIndex)
            {
                var archetype = archetypeArray[archetypeIndex].Archetype;
                for (var c = (Chunk *)archetype->ChunkList.Begin; c != archetype->ChunkList.End; c = (Chunk *)c->ChunkListNode.Next)
                {
                    bufferPatches.Clear();

                    UnsafeUtility.MemCpy(tempChunk, c, Chunk.kChunkSize);
                    tempChunk->SharedComponentValueArray = (int *)((byte *)(tempChunk) + Chunk.GetSharedComponentOffset(archetype->NumSharedComponents));

                    byte *tempChunkBuffer = tempChunk->Buffer;
                    EntityRemapUtility.PatchEntities(archetype->ScalarEntityPatches, archetype->ScalarEntityPatchCount, archetype->BufferEntityPatches, archetype->BufferEntityPatchCount, tempChunkBuffer, tempChunk->Count, ref entityRemapInfos);

                    // Find all buffer pointer locations and work out how much memory the deserializer must allocate on load.
                    for (int ti = 0; ti < archetype->TypesCount; ++ti)
                    {
                        int index = archetype->TypeMemoryOrder[ti];

                        if (!archetype->Types[index].IsBuffer)
                        {
                            continue;
                        }

                        int           subArrayOffset = archetype->Offsets[index];
                        BufferHeader *header         = (BufferHeader *)OffsetFromPointer(tempChunkBuffer, subArrayOffset);
                        int           stride         = archetype->SizeOfs[index];
                        int           count          = c->Count;
                        var           ct             = TypeManager.GetTypeInfo(archetype->Types[index].TypeIndex);

                        for (int bi = 0; bi < count; ++bi)
                        {
                            if (header->Pointer != null)
                            {
                                header->Pointer = null;
                                bufferPatches.Add(new BufferPatchRecord
                                {
                                    ChunkOffset    = (int)(((byte *)header) - (byte *)tempChunkBuffer),
                                    AllocSizeBytes = ct.ElementSize * header->Capacity,
                                });
                            }
                            header = (BufferHeader *)OffsetFromPointer(header, stride);
                        }
                    }

                    ClearUnusedChunkData(tempChunk);
                    tempChunk->ChunkListNode.Next = null;
                    tempChunk->ChunkListNode.Prev = null;
                    tempChunk->ChunkListWithEmptySlotsNode.Next = null;
                    tempChunk->ChunkListWithEmptySlotsNode.Prev = null;
                    tempChunk->Archetype = (Archetype *)archetypeIndex;

                    if (archetype->NumManagedArrays != 0)
                    {
                        throw new ArgumentException("Serialization of GameObject components is not supported for pure entity scenes");
                    }

                    for (int i = 0; i != archetype->NumSharedComponents; i++)
                    {
                        int sharedComponentIndex = tempChunk->SharedComponentValueArray[i];
                        int newIndex;

                        if (tempChunk->SharedComponentValueArray[i] != 0)
                        {
                            if (sharedIndexToSerialize.TryGetValue(sharedComponentIndex, out newIndex))
                            {
                                tempChunk->SharedComponentValueArray[i] = newIndex;
                            }
                            else
                            {
                                // 0 is reserved for null types in shared components
                                newIndex = sharedIndexToSerialize.Count + 1;
                                sharedIndexToSerialize[sharedComponentIndex] = newIndex;

                                tempChunk->SharedComponentValueArray[i] = newIndex;
                            }
                        }
                    }

                    writer.WriteBytes(tempChunk, Chunk.kChunkSize);

                    writer.Write(bufferPatches.Length);

                    if (bufferPatches.Length > 0)
                    {
                        writer.WriteList(bufferPatches);

                        // Write heap backed data for each required patch.
                        // TODO: PERF: Investigate static-only deserialization could manage one block and mark in pointers somehow that they are not indiviual
                        for (int i = 0; i < bufferPatches.Length; ++i)
                        {
                            var patch = bufferPatches[i];
                            // NOTE that this reads the pointer from the original, unpatched chunk.
                            // We have nulled out the pointer in the serialized data above.
                            var header = (BufferHeader *)OffsetFromPointer(c->Buffer, patch.ChunkOffset);
                            writer.WriteBytes(header->Pointer, patch.AllocSizeBytes);
                        }
                    }
                }
            }

            bufferPatches.Dispose();
            entityRemapInfos.Dispose();
            UnsafeUtility.Free(tempChunk, Allocator.Temp);

            sharedComponentsToSerialize = new int[sharedIndexToSerialize.Count];

            foreach (var i in sharedIndexToSerialize)
            {
                sharedComponentsToSerialize[i.Value - 1] = i.Key;
            }
        }
Exemplo n.º 19
0
        public static unsafe void SerializeWorld(EntityManager entityManager, BinaryWriter writer, out int[] sharedComponentsToSerialize, NativeArray <EntityRemapUtility.EntityRemapInfo> entityRemapInfos)
        {
            Dictionary <EntityArchetype, int> dictionary;

            EntityArchetype[] archetypeArray;
            writer.Write(CurrentFileFormatVersion);
            GetAllArchetypes(entityManager.ArchetypeManager, out dictionary, out archetypeArray);
            HashSet <int> source = new HashSet <int>();

            EntityArchetype[] archetypeArray2 = archetypeArray;
            int index = 0;

            while (index < archetypeArray2.Length)
            {
                EntityArchetype archetype = archetypeArray2[index];
                int             num4      = 0;
                while (true)
                {
                    if (num4 >= archetype.Archetype.TypesCount)
                    {
                        index++;
                        break;
                    }
                    source.Add(archetype.Archetype.Types[num4].TypeIndex);
                    num4++;
                }
            }
            var typeArray = (from t in source.Select(delegate(int index) {
                Type type = TypeManager.GetType(index);
                string assemblyQualifiedName = TypeManager.GetType(index).AssemblyQualifiedName;
                return(new {
                    index = index,
                    type = type,
                    name = assemblyQualifiedName,
                    hash = TypeManager.GetTypeInfo(index).FastEqualityTypeInfo.Hash,
                    asciiName = Encoding.ASCII.GetBytes(assemblyQualifiedName)
                });
            })
                             orderby t.name
                             select t).ToArray();
            int num = typeArray.Sum(t => t.asciiName.Length + 1);

            writer.Write(typeArray.Length);
            foreach (var type in typeArray)
            {
                writer.Write(type.hash);
            }
            writer.Write(num);
            foreach (var type2 in typeArray)
            {
                writer.Write(type2.asciiName);
                writer.Write((byte)0);
            }
            Dictionary <int, int> typeIndexMap = new Dictionary <int, int>();
            int num7 = 0;

            while (true)
            {
                if (num7 >= typeArray.Length)
                {
                    WriteArchetypes(writer, archetypeArray, typeIndexMap);
                    NativeList <BufferPatchRecord> data = new NativeList <BufferPatchRecord>(0x80, Allocator.Temp);
                    int num2 = GenerateRemapInfo(entityManager, archetypeArray, entityRemapInfos);
                    writer.Write(num2);
                    Chunk *chunk = (Chunk *)UnsafeUtility.Malloc(0x3f00L, 0x10, Allocator.Temp);
                    Dictionary <int, int> dictionary3 = new Dictionary <int, int>();
                    int num8 = 0;
                    while (true)
                    {
                        if (num8 >= archetypeArray.Length)
                        {
                            data.Dispose();
                            UnsafeUtility.Free((void *)chunk, Allocator.Temp);
                            sharedComponentsToSerialize = new int[dictionary3.Count];
                            foreach (KeyValuePair <int, int> pair in dictionary3)
                            {
                                sharedComponentsToSerialize[pair.Value - 1] = pair.Key;
                            }
                            return;
                        }
                        Archetype *archetype = archetypeArray[num8].Archetype;
                        Chunk *    begin     = (Chunk *)archetype->ChunkList.Begin;
                        while (true)
                        {
                            if (begin == archetype->ChunkList.End)
                            {
                                num8++;
                                break;
                            }
                            data.Clear();
                            UnsafeUtility.MemCpy((void *)chunk, (void *)begin, 0x3f00L);
                            chunk->SharedComponentValueArray = (int *)(chunk + Chunk.GetSharedComponentOffset(archetype->NumSharedComponents));
                            byte *numPtr = &chunk->Buffer.FixedElementField;
                            EntityRemapUtility.PatchEntities(archetype->ScalarEntityPatches, archetype->ScalarEntityPatchCount, archetype->BufferEntityPatches, archetype->BufferEntityPatchCount, numPtr, chunk->Count, ref entityRemapInfos);
                            int num9 = 0;
                            while (true)
                            {
                                if (num9 >= archetype->TypesCount)
                                {
                                    ClearUnusedChunkData(chunk);
                                    chunk->ChunkListNode.Next = null;
                                    chunk->ChunkListNode.Prev = null;
                                    chunk->ChunkListWithEmptySlotsNode.Next = null;
                                    chunk->ChunkListWithEmptySlotsNode.Prev = null;
                                    chunk->Archetype = (Archetype *)num8;
                                    if (archetype->NumManagedArrays != 0)
                                    {
                                        throw new ArgumentException("Serialization of GameObject components is not supported for pure entity scenes");
                                    }
                                    int num15 = 0;
                                    while (true)
                                    {
                                        if (num15 == archetype->NumSharedComponents)
                                        {
                                            writer.WriteBytes((void *)chunk, 0x3f00);
                                            writer.Write(data.Length);
                                            if (data.Length > 0)
                                            {
                                                writer.WriteList <BufferPatchRecord>(data);
                                                int num18 = 0;
                                                while (true)
                                                {
                                                    if (num18 >= data.Length)
                                                    {
                                                        break;
                                                    }
                                                    BufferPatchRecord record2    = data[num18];
                                                    BufferHeader *    headerPtr2 = (BufferHeader *)ref OffsetFromPointer((void *)&begin->Buffer.FixedElementField, record2.ChunkOffset);
                                                    writer.WriteBytes((void *)headerPtr2->Pointer, record2.AllocSizeBytes);
                                                    num18++;
                                                }
                                            }
                                            begin = (Chunk *)begin->ChunkListNode.Next;
                                            break;
                                        }
                                        int key = chunk->SharedComponentValueArray[num15];
                                        if (chunk->SharedComponentValueArray[num15] != 0)
                                        {
                                            int num17;
                                            if (dictionary3.TryGetValue(key, out num17))
                                            {
                                                chunk->SharedComponentValueArray[num15] = num17;
                                            }
                                            else
                                            {
                                                num17 = dictionary3.Count + 1;
                                                dictionary3.set_Item(key, num17);
                                                chunk->SharedComponentValueArray[num15] = num17;
                                            }
                                        }
                                        num15++;
                                    }
                                    break;
                                }
                                int num10 = archetype->TypeMemoryOrder[num9];
                                if ((archetype->Types + num10).IsBuffer)
                                {
                                    BufferHeader *       headerPtr = (BufferHeader *)OffsetFromPointer((void *)numPtr, archetype->Offsets[num10]);
                                    int                  offset    = archetype->SizeOfs[num10];
                                    int                  count     = begin->Count;
                                    TypeManager.TypeInfo typeInfo  = TypeManager.GetTypeInfo(archetype->Types[num10].TypeIndex);
                                    int                  num14     = 0;
                                    while (true)
                                    {
                                        if (num14 >= count)
                                        {
                                            break;
                                        }
                                        if (headerPtr->Pointer != null)
                                        {
                                            headerPtr->Pointer = null;
                                            BufferPatchRecord element = new BufferPatchRecord {
                                                ChunkOffset    = (int)((long)((headerPtr - numPtr) / 1)),
                                                AllocSizeBytes = typeInfo.ElementSize * headerPtr->Capacity
                                            };
                                            data.Add(element);
                                        }
                                        headerPtr = (BufferHeader *)OffsetFromPointer((void *)headerPtr, offset);
                                        num14++;
                                    }
                                }
                                num9++;
                            }
                        }
                    }
                }
                typeIndexMap.set_Item(typeArray[num7].index, num7);
                num7++;
            }
        }
Exemplo n.º 20
0
        public static unsafe void SerializeWorld(EntityManager entityManager, BinaryWriter writer, out int[] sharedComponentsToSerialize)
        {
            writer.Write(CurrentFileFormatVersion);
            var archetypeManager = entityManager.ArchetypeManager;

            GetAllArchetypes(archetypeManager, out var archetypeToIndex, out var archetypeArray);

            var typeindices = new HashSet <int>();

            foreach (var archetype in archetypeArray)
            {
                for (int iType = 0; iType < archetype.Archetype->TypesCount; ++iType)
                {
                    typeindices.Add(archetype.Archetype->Types[iType].TypeIndex);
                }
            }

            var typeArray = typeindices.Select(index =>
            {
                var type = TypeManager.GetType(index);
                var name = TypeManager.GetType(index).AssemblyQualifiedName;
                return(new
                {
                    index,
                    type,
                    name,
                    asciiName = Encoding.ASCII.GetBytes(name)
                });
            }).OrderBy(t => t.name).ToArray();

            int typeNameBufferSize = typeArray.Sum(t => t.asciiName.Length + 1);

            writer.Write(typeArray.Length);
            writer.Write(typeNameBufferSize);
            foreach (var n in typeArray)
            {
                writer.Write(n.asciiName);
                writer.Write((byte)0);
            }

            var typeIndexMap = new Dictionary <int, int>();

            for (int i = 0; i < typeArray.Length; ++i)
            {
                typeIndexMap[typeArray[i].index] = i;
            }

            WriteArchetypes(writer, archetypeArray, typeIndexMap);

            //TODO: ensure chunks are defragged?

            NativeArray <EntityRemapUtility.EntityRemapInfo> entityRemapInfos;
            var totalChunkCount = GenerateRemapInfo(entityManager, archetypeArray, out entityRemapInfos);

            writer.Write(totalChunkCount);

            var entityPatchInfos = new NativeList <EntityRemapUtility.EntityPatchInfo>(128, Allocator.Temp);
            var tempChunk        = (Chunk *)UnsafeUtility.Malloc(Chunk.kChunkSize, 16, Allocator.Temp);

            var sharedIndexToSerialize = new Dictionary <int, int>();

            for (int archetypeIndex = 0; archetypeIndex < archetypeArray.Length; ++archetypeIndex)
            {
                var archetype = archetypeArray[archetypeIndex].Archetype;
                for (var c = (Chunk *)archetype->ChunkList.Begin; c != archetype->ChunkList.End; c = (Chunk *)c->ChunkListNode.Next)
                {
                    UnsafeUtility.MemCpy(tempChunk, c, Chunk.kChunkSize);

                    entityPatchInfos.Clear();
                    for (var i = 0; i != archetype->TypesCount; ++i)
                    {
                        EntityRemapUtility.AppendEntityPatches(ref entityPatchInfos, TypeManager.GetComponentType(archetype->Types[i].TypeIndex).EntityOffsets, archetype->Offsets[i], archetype->SizeOfs[i]);
                    }

                    EntityRemapUtility.PatchEntities(ref entityPatchInfos, tempChunk->Buffer, tempChunk->Count, ref entityRemapInfos);

                    ClearUnusedChunkData(tempChunk);
                    tempChunk->ChunkListNode.Next = null;
                    tempChunk->ChunkListNode.Prev = null;
                    tempChunk->ChunkListWithEmptySlotsNode.Next = null;
                    tempChunk->ChunkListWithEmptySlotsNode.Prev = null;
                    tempChunk->Archetype = (Archetype *)archetypeIndex;

                    if (archetype->NumManagedArrays != 0)
                    {
                        throw new ArgumentException("Serialization of GameObject components is not supported for pure entity scenes");
                    }

                    for (int i = 0; i != archetype->NumSharedComponents; i++)
                    {
                        int sharedComponentIndex = tempChunk->SharedComponentValueArray[i];
                        int newIndex;

                        if (tempChunk->SharedComponentValueArray[i] != 0)
                        {
                            if (sharedIndexToSerialize.TryGetValue(sharedComponentIndex, out newIndex))
                            {
                                tempChunk->SharedComponentValueArray[i] = newIndex;
                            }
                            else
                            {
                                // 0 is reserved for null types in shared components
                                newIndex = sharedIndexToSerialize.Count + 1;
                                sharedIndexToSerialize[sharedComponentIndex] = newIndex;

                                tempChunk->SharedComponentValueArray[i] = newIndex;
                            }
                        }
                    }

                    writer.WriteBytes(tempChunk, Chunk.kChunkSize);
                }
            }

            entityRemapInfos.Dispose();
            entityPatchInfos.Dispose();
            UnsafeUtility.Free(tempChunk, Allocator.Temp);

            sharedComponentsToSerialize = new int[sharedIndexToSerialize.Count];

            foreach (var i in sharedIndexToSerialize)
            {
                sharedComponentsToSerialize[i.Value - 1] = i.Key;
            }
        }