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);
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; } } }
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(); }