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