public void Execute() { var knownChunks = ShadowChunksBySequenceNumber.GetKeyArray(Allocator.Temp); var processedChunks = new NativeHashMap <ulong, byte>(Chunks.Length, Allocator.Temp); for (var index = 0; index < Chunks.Length; index++) { var chunk = Chunks[index].m_Chunk; var archetype = chunk->Archetype; var indexInTypeArray = ChunkDataUtility.GetIndexInTypeArray(archetype, TypeIndex); if (indexInTypeArray == -1) // Archetype doesn't match required component { continue; } var version = chunk->GetChangeVersion(0); var sequenceNumber = chunk->SequenceNumber; processedChunks[sequenceNumber] = 0; var entityDataPtr = chunk->Buffer + archetype->Offsets[0]; if (ShadowChunksBySequenceNumber.TryGetValue(sequenceNumber, out var shadow)) { if (!ChangeVersionUtility.DidChange(version, shadow.Version)) { continue; } UnsafeUtility.MemCpy(shadow.EntityDataBuffer, entityDataPtr, chunk->Count * sizeof(Entity)); shadow.EntityCount = chunk->Count; shadow.Version = version; ShadowChunksBySequenceNumber[sequenceNumber] = shadow; } else { ShadowChunksBySequenceNumber.Add(sequenceNumber, *(AllocatedShadowChunks + index)); } } for (var i = 0; i < knownChunks.Length; i++) { var chunkSequenceNumber = knownChunks[i]; if (!processedChunks.ContainsKey(chunkSequenceNumber)) { // This is a missing chunk RemovedChunks.Add(chunkSequenceNumber); } } knownChunks.Dispose(); processedChunks.Dispose(); }
public void SwapComponents(ArchetypeChunk leftChunk, int leftIndex, ArchetypeChunk rightChunk, int rightIndex) { if (m_IsMainThread) { BeforeStructuralChange(); } var globalSystemVersion = EntityComponentStore->GlobalSystemVersion; ChunkDataUtility.SwapComponents(leftChunk.m_Chunk, leftIndex, rightChunk.m_Chunk, rightIndex, 1, globalSystemVersion, globalSystemVersion); }
public void SIZ_TagComponentZeroSize() { var entity0 = m_Manager.CreateEntity(typeof(EcsTestTag)); unsafe { // a system ran, the version should match the global var chunk0 = m_Manager.Entities->GetComponentChunk(entity0); var archetype0 = chunk0->Archetype; var indexInTypeArray = ChunkDataUtility.GetIndexInTypeArray(chunk0->Archetype, TypeManager.GetTypeIndex <EcsTestTag>()); Assert.AreEqual(0, archetype0->SizeOfs[indexInTypeArray]); } }
unsafe bool DidChangeInternal(World world, IEnumerable <GameObject> targets, IEnumerable <int> selectedComponentTypes) { if (world != m_LastWorld) { m_LastWorld = world; return(true); } if (m_LastConversionData == EntityConversionData.Null) { // @TODO Handle multiple targets correctly. m_LastConversionData = EntityConversionUtility.GetConversionData(targets, world).FirstOrDefault(); if (m_LastConversionData == EntityConversionData.Null) { return(false); } m_LastChunk = world.EntityManager.GetChunk(m_LastConversionData.PrimaryEntity); return(true); } var chunk = world.EntityManager.GetChunk(m_LastConversionData.PrimaryEntity); if (null == chunk.m_Chunk || chunk != m_LastChunk) { m_LastChunk = chunk; return(true); } foreach (var typeIndex in selectedComponentTypes) { var typeIndexInArchetype = ChunkDataUtility.GetIndexInTypeArray(m_LastChunk.m_Chunk->Archetype, typeIndex); if (typeIndexInArchetype == -1) { continue; } var typeChangeVersion = m_LastChunk.m_Chunk->GetChangeVersion(typeIndexInArchetype); if (ChangeVersionUtility.DidChange(typeChangeVersion, m_LastGlobalSystemVersion)) { m_LastGlobalSystemVersion = world.EntityManager.GlobalSystemVersion; return(true); } } return(false); }
public void SIZ_TagComponentDoesNotChangeCapacity() { var entity0 = m_Manager.CreateEntity(typeof(EcsTestData)); var entity1 = m_Manager.CreateEntity(typeof(EcsTestData), typeof(EcsTestTag)); unsafe { // a system ran, the version should match the global var chunk0 = m_Manager.Entities->GetComponentChunk(entity0); var chunk1 = m_Manager.Entities->GetComponentChunk(entity1); var archetype0 = chunk0->Archetype; var archetype1 = chunk1->Archetype; ChunkDataUtility.GetIndexInTypeArray(chunk0->Archetype, TypeManager.GetTypeIndex <EcsTestData2>()); Assert.AreEqual(archetype0->BytesPerInstance, archetype1->BytesPerInstance); } }
public void SIZ_TagComponentDoesNotChangeCapacity() { var entity0 = m_Manager.CreateEntity(typeof(EcsTestData)); var entity1 = m_Manager.CreateEntity(typeof(EcsTestData), typeof(EcsTestTag)); unsafe { // a system ran, the version should match the global var chunk0 = m_Manager.EntityComponentStore->GetChunk(entity0); var chunk1 = m_Manager.EntityComponentStore->GetChunk(entity1); var archetype0 = chunk0->Archetype; var archetype1 = chunk1->Archetype; ChunkDataUtility.GetIndexInTypeArray(chunk0->Archetype, TypeManager.GetTypeIndex <EcsTestData2>()); Assert.True(ChunkDataUtility.AreLayoutCompatible(archetype0, archetype1)); } }
public void AddSharedComponentToMultipleEntitiesIncompatibleChunkLayouts() { // The goal of this test is to verify that the moved IComponentData keeps the same values from // before the addition of the shared component. var archetype = m_Manager.CreateArchetype(typeof(EcsTestData)); var archetypeWithShared = m_Manager.CreateArchetype(typeof(EcsTestData), typeof(EcsTestSharedCompWithMaxChunkCapacity)); unsafe { Assert.IsFalse(ChunkDataUtility.AreLayoutCompatible(archetype.Archetype, archetypeWithShared.Archetype)); } var query = m_Manager.CreateEntityQuery(typeof(EcsTestData)); const int numEntities = 5000; using (var entities = new NativeArray <Entity>(numEntities, Allocator.Persistent)) { m_Manager.CreateEntity(archetype, entities); for (int i = 0; i < entities.Length; ++i) { m_Manager.SetComponentData(entities[i], new EcsTestData(i)); } foreach (var e in entities) { Assert.IsFalse(m_Manager.HasComponent <EcsTestSharedCompWithMaxChunkCapacity>(e)); } m_Manager.AddSharedComponentData(query, new EcsTestSharedCompWithMaxChunkCapacity(17)); var chunk = m_Manager.GetChunk(entities[0]); int maxChunkCapacity = TypeManager.GetTypeInfo <EcsTestSharedCompWithMaxChunkCapacity>().MaximumChunkCapacity; int expectedChunkCount = (numEntities + maxChunkCapacity - 1) / maxChunkCapacity; Assert.AreEqual(expectedChunkCount, chunk.Archetype.ChunkCount); // Ensure that the moved components have the correct values. for (int i = 0; i < entities.Length; ++i) { Assert.IsTrue(m_Manager.HasComponent <EcsTestSharedCompWithMaxChunkCapacity>(entities[i])); Assert.AreEqual(17, m_Manager.GetSharedComponentData <EcsTestSharedCompWithMaxChunkCapacity>(entities[i]).Value); Assert.AreEqual(i, m_Manager.GetComponentData <EcsTestData>(entities[i]).value); } } }
public unsafe void ArchetypeIsManaged() { var types = new ComponentType[] { typeof(EcsTestData), typeof(ConversionTestHybridComponent), #if !UNITY_DISABLE_MANAGED_COMPONENTS typeof(EcsTestManagedComponent) #endif }; var archetype = m_Manager.CreateArchetype(types).Archetype; Assert.IsFalse(archetype->IsManaged(ChunkDataUtility.GetIndexInTypeArray(archetype, types[0].TypeIndex))); Assert.IsTrue(archetype->IsManaged(ChunkDataUtility.GetIndexInTypeArray(archetype, types[1].TypeIndex))); #if !UNITY_DISABLE_MANAGED_COMPONENTS Assert.IsTrue(archetype->IsManaged(ChunkDataUtility.GetIndexInTypeArray(archetype, types[2].TypeIndex))); #endif }
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 void AddSharedComponentIncompatibleChunkLayouts() { var archetype = m_Manager.CreateArchetype(typeof(EcsTestData)); var archetypeWithShared = m_Manager.CreateArchetype(typeof(EcsTestData), typeof(EcsTestSharedCompWithMaxChunkCapacity)); unsafe { Assert.IsFalse(ChunkDataUtility.AreLayoutCompatible(archetype.Archetype, archetypeWithShared.Archetype)); } var query = m_Manager.CreateEntityQuery(typeof(EcsTestData)); Entity e = m_Manager.CreateEntity(archetype); Assert.IsFalse(m_Manager.HasComponent <EcsTestSharedCompWithMaxChunkCapacity>(e)); m_Manager.AddSharedComponentData(query, new EcsTestSharedCompWithMaxChunkCapacity(17)); Assert.IsTrue(m_Manager.HasComponent <EcsTestSharedCompWithMaxChunkCapacity>(e)); Assert.AreEqual(17, m_Manager.GetSharedComponentData <EcsTestSharedCompWithMaxChunkCapacity>(e).Value); }
private static unsafe void ClearChunkHeaderComponents(Chunk *chunk) { int chunkHeaderTypeIndex = TypeManager.GetTypeIndex <ChunkHeader>(); var archetype = chunk->Archetype; var typeIndexInArchetype = ChunkDataUtility.GetIndexInTypeArray(chunk->Archetype, chunkHeaderTypeIndex); if (typeIndexInArchetype == -1) { return; } var buffer = chunk->Buffer; var length = chunk->Count; var startOffset = archetype->Offsets[typeIndexInArchetype]; var chunkHeaders = (ChunkHeader *)(buffer + startOffset); for (int i = 0; i < length; ++i) { chunkHeaders[i].chunk = null; } }
public void AddSharedComponentViaAddComponentWithIncompatibleChunkLayouts() { var archetype = m_Manager.CreateArchetype(typeof(EcsTestData)); var archetypeWithShared = m_Manager.CreateArchetype(typeof(EcsTestData), typeof(EcsTestSharedCompWithMaxChunkCapacity)); unsafe { Assert.IsFalse(ChunkDataUtility.AreLayoutCompatible(archetype.Archetype, archetypeWithShared.Archetype)); } using (var entities = new NativeArray <Entity>(1, Allocator.TempJob)) { m_Manager.CreateEntity(archetype, entities); Assert.IsFalse(m_Manager.HasComponent <EcsTestSharedCompWithMaxChunkCapacity>(entities[0])); m_Manager.AddComponent(entities, typeof(EcsTestSharedCompWithMaxChunkCapacity)); Assert.IsTrue(m_Manager.HasComponent <EcsTestSharedCompWithMaxChunkCapacity>(entities[0])); Assert.AreEqual(0, m_Manager.GetSharedComponentData <EcsTestSharedCompWithMaxChunkCapacity>(entities[0]).Value); } }
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); }
[StandaloneFixme] // IJob public void CHG_IncrementedOnInjection() { var entity0 = m_Manager.CreateEntity(typeof(EcsTestData), typeof(EcsTestData2)); var bumpSystem = World.CreateManager <BumpVersionSystem>(); var oldGlobalVersion = m_Manager.GlobalSystemVersion; bumpSystem.Update(); var value0 = m_Manager.GetComponentData <EcsTestData2>(entity0).value0; Assert.AreEqual(10, value0); Assert.That(m_Manager.GlobalSystemVersion > oldGlobalVersion); unsafe { // a system ran, the version should match the global var chunk0 = m_Manager.Entities->GetComponentChunk(entity0); var td2index0 = ChunkDataUtility.GetIndexInTypeArray(chunk0->Archetype, TypeManager.GetTypeIndex <EcsTestData2>()); Assert.AreEqual(m_Manager.GlobalSystemVersion, chunk0->GetChangeVersion(td2index0)); } }
public static unsafe void *GetComponentDataWithTypeRO(this ArchetypeChunk chunk, int chunkIndex, int typeIndex) { chunk.AssertEntityHasComponent(typeIndex); return(ChunkDataUtility.GetComponentDataWithTypeRO(chunk.m_Chunk, chunkIndex, typeIndex)); }
static unsafe void WriteChunkData(YamlWriter writer, EntityManager entityManager, Chunk *initialChunk, Archetype *archetype, bool dumpChunkRawData) { using (writer.WriteCollection(k_ChunkDataCollectionTag)) { using (writer.WriteCollection("Header")) { WriteEntity(writer, nameof(Chunk.metaChunkEntity), initialChunk->metaChunkEntity); writer.WriteKeyValue(nameof(Chunk.Capacity), initialChunk->Capacity) .WriteKeyValue(nameof(Chunk.Count), initialChunk->Count); if (dumpChunkRawData) { writer.WriteFormattedBinaryData("RawData", initialChunk, 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); var typeInfo = TypeManager.GetTypeInfo(componentType->TypeIndex); if (componentType->IsChunkComponent) { chunkComponentDataList.Add(typeI); } // Is it a Component Data ? else if (typeof(IComponentData).IsAssignableFrom(type) || typeof(Entity).IsAssignableFrom(type) || typeof(IBufferElementData).IsAssignableFrom(type)) { // Ignore Tag Component, no data to dump if (typeInfo.IsZeroSized) { continue; } if (typeof(Entity).IsAssignableFrom(type)) { componentDataList.Insert(0, typeI); for (int i = 0; i < initialChunk->Count;) { var entity = (Entity)Marshal.PtrToStructure((IntPtr)initialChunk->Buffer + archetype->SizeOfs[0] * i, type); if (entityManager.Exists(entity)) { entitiesByChunkIndex.Add(i, entity); i++; } } } else { componentDataList.Add(typeI); } } } // Parse the Component Data for this chunk and store them using (writer.WriteCollection(k_ComponentDataCollectionTag)) { foreach (var typeI in componentDataList) { var componentTypeInArchetype = &chunkTypes[typeI]; var componentType = TypeManager.GetType(componentTypeInArchetype->TypeIndex); var componentTypeInfo = TypeManager.GetTypeInfo(componentTypeInArchetype->TypeIndex); var componentExtractedInfo = TypeDataExtractor.GetTypeExtractedInfo(componentType); using (writer.WriteCollection(k_ComponentDataTag)) { writer.WriteInlineMap("info", new[] { new KeyValuePair <object, object>(nameof(Type), componentType.Name), new KeyValuePair <object, object>(nameof(TypeManager.TypeInfo.SizeInChunk), componentTypeInfo.SizeInChunk) }); using (writer.WriteCollection("Entities")) { var indexInTypeArray = ChunkDataUtility.GetIndexInTypeArray(archetype, componentTypeInArchetype->TypeIndex); var componentOffsetInChunk = archetype->Offsets[indexInTypeArray]; var componentSize = archetype->SizeOfs[indexInTypeArray]; var componentsBuffer = initialChunk->Buffer + componentOffsetInChunk; var entityData = new Dictionary <string, string>(); // Dump all entities in this chunk foreach (var kvp in entitiesByChunkIndex) { var entity = kvp.Value; entityData.Clear(); // Get the location of the component data var compData = componentsBuffer + kvp.Key * componentSize; // If the component we are dumping is a Dynamic Buffer if (typeof(IBufferElementData).IsAssignableFrom(componentType)) { var header = (BufferHeader *)compData; var begin = BufferHeader.GetElementPointer(header); var size = Marshal.SizeOf(componentType); using (writer.WriteCollection(entity.ToString())) { for (var it = 0; it < header->Length; it++) { var item = begin + (size * it); entityData.Clear(); // Dump each field of the current entity's component data foreach (var componentFieldInfo in componentExtractedInfo.Fields) { var compDataObject = Marshal.PtrToStructure((IntPtr)item + componentFieldInfo.Offset, componentFieldInfo.Type); entityData.Add(componentFieldInfo.Name, compDataObject.ToString()); } writer.WriteInlineMap($"{it:0000}", entityData); } } } // If it's a Component Data else { // Dump each field of the current entity's component data foreach (var componentFieldInfo in componentExtractedInfo.Fields) { var compDataObject = Marshal.PtrToStructure((IntPtr)compData + componentFieldInfo.Offset, componentFieldInfo.Type); entityData.Add(componentFieldInfo.Name, compDataObject.ToString()); } writer.WriteInlineMap(entity.ToString(), entityData); } } if (dumpChunkRawData) { var csize = EntityComponentStore.GetComponentArraySize(componentSize, archetype->ChunkCapacity); writer.WriteFormattedBinaryData("RawData", componentsBuffer, csize, componentOffsetInChunk + Chunk.kBufferOffset); } } } } } } }
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(); }
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 void Execute(int index) { var chunk = Chunks[index].m_Chunk; var archetype = chunk->Archetype; var indexInTypeArray = ChunkDataUtility.GetIndexInTypeArray(archetype, TypeIndex); if (indexInTypeArray == -1) // Archetype doesn't match required component { return; } var changesForChunk = GatheredChanges + index; if (ShadowChunksBySequenceNumber.TryGetValue(chunk->SequenceNumber, out var shadow)) { if (!ChangeVersionUtility.DidChange(chunk->GetChangeVersion(indexInTypeArray), shadow.ComponentVersion) && !ChangeVersionUtility.DidChange(chunk->GetChangeVersion(0), shadow.EntityVersion)) { return; } if (!changesForChunk->AddedComponentEntities.IsCreated) { changesForChunk->AddedComponentEntities = new UnsafeList(Allocator.TempJob); changesForChunk->AddedComponentDataBuffer = new UnsafeList(Allocator.TempJob); changesForChunk->RemovedComponentEntities = new UnsafeList(Allocator.TempJob); changesForChunk->RemovedComponentDataBuffer = new UnsafeList(Allocator.TempJob); } var entityDataPtr = chunk->Buffer + archetype->Offsets[0]; var componentDataPtr = chunk->Buffer + archetype->Offsets[indexInTypeArray]; var currentCount = chunk->Count; var previousCount = shadow.Count; var i = 0; for (; i < currentCount && i < previousCount; i++) { var currentComponentData = componentDataPtr + ComponentSize * i; var previousComponentData = shadow.ComponentDataBuffer + ComponentSize * i; var entity = *(Entity *)(entityDataPtr + sizeof(Entity) * i); var previousEntity = *(Entity *)(shadow.EntityDataBuffer + sizeof(Entity) * i); if (entity != previousEntity || UnsafeUtility.MemCmp(currentComponentData, previousComponentData, ComponentSize) != 0) { // CHANGED COMPONENT DATA! OnRemovedComponent(changesForChunk, previousEntity, previousComponentData, ComponentSize); OnNewComponent(changesForChunk, entity, currentComponentData, ComponentSize); } } for (; i < currentCount; i++) { // NEW COMPONENT DATA! var entity = *(Entity *)(entityDataPtr + sizeof(Entity) * i); var currentComponentData = componentDataPtr + ComponentSize * i; OnNewComponent(changesForChunk, entity, currentComponentData, ComponentSize); } for (; i < previousCount; i++) { // REMOVED COMPONENT DATA! var entity = *(Entity *)(entityDataPtr + sizeof(Entity) * i); var previousComponentData = shadow.ComponentDataBuffer + ComponentSize * i; OnRemovedComponent(changesForChunk, entity, previousComponentData, ComponentSize); } } else { // This is a new chunk var addedComponentDataBuffer = new UnsafeList(ComponentSize, 4, chunk->Count, Allocator.TempJob); var addedComponentEntities = new UnsafeList(sizeof(Entity), 4, chunk->Count, Allocator.TempJob); var entityDataPtr = chunk->Buffer + archetype->Offsets[0]; var componentDataPtr = chunk->Buffer + archetype->Offsets[indexInTypeArray]; addedComponentDataBuffer.AddRange <byte>(componentDataPtr, chunk->Count * ComponentSize); addedComponentEntities.AddRange <Entity>(entityDataPtr, chunk->Count); changesForChunk->AddedComponentDataBuffer = addedComponentDataBuffer; changesForChunk->AddedComponentEntities = addedComponentEntities; } }
public void Execute(int index) { var chunk = Chunks[index].m_Chunk; var archetype = chunk->Archetype; var indexInTypeArray = ChunkDataUtility.GetIndexInTypeArray(archetype, TypeIndex); if (indexInTypeArray == -1) // Archetype doesn't match required component { return; } var changesForChunk = GatheredChanges + index; if (ShadowChunksBySequenceNumber.TryGetValue(chunk->SequenceNumber, out var shadow)) { if (!ChangeVersionUtility.DidChange(chunk->GetChangeVersion(0), shadow.Version)) { return; } if (!changesForChunk->AddedEntities.IsCreated) { changesForChunk->Chunk = chunk; changesForChunk->AddedEntities = new UnsafeList(Allocator.TempJob); changesForChunk->RemovedEntities = new UnsafeList(Allocator.TempJob); } var entityDataPtr = (Entity *)(chunk->Buffer + archetype->Offsets[0]); var currentCount = chunk->Count; var previousCount = shadow.EntityCount; var i = 0; for (; i < currentCount && i < previousCount; i++) { var currentEntity = entityDataPtr[i]; var previousEntity = shadow.EntityDataBuffer[i]; if (currentEntity != previousEntity) { // CHANGED ENTITY! changesForChunk->RemovedEntities.Add(previousEntity); changesForChunk->AddedEntities.Add(currentEntity); } } for (; i < currentCount; i++) { // NEW ENTITY! changesForChunk->AddedEntities.Add(entityDataPtr[i]); } for (; i < previousCount; i++) { // REMOVED ENTITY! changesForChunk->RemovedEntities.Add(shadow.EntityDataBuffer[i]); } } else { // This is a new chunk var addedEntities = new UnsafeList(sizeof(Entity), 4, chunk->Count, Allocator.TempJob); var entityDataPtr = chunk->Buffer + archetype->Offsets[0]; addedEntities.AddRange <Entity>(entityDataPtr, chunk->Count); changesForChunk->Chunk = chunk; changesForChunk->AddedEntities = addedEntities; } }
public void AreLayoutCompatibleSameArchetype() { var a = m_Manager.CreateArchetype(); Assert.IsTrue(ChunkDataUtility.AreLayoutCompatible(a.Archetype, a.Archetype)); }
public void Execute() { var addedSharedComponentsCount = 0; var removedSharedComponentsCount = 0; var removedEntityCurrentCount = 0; var addedEntityCurrentCount = 0; for (var i = 0; i < RemovedShadowChunks.Length; i++) { var chunkSequenceNumber = RemovedShadowChunks[i]; var shadowChunk = ShadowChunksBySequenceNumber[chunkSequenceNumber]; UnsafeUtility.MemCpy((Entity *)RemovedEntities.GetUnsafePtr() + removedEntityCurrentCount, shadowChunk.EntityDataBuffer, shadowChunk.EntityCount * sizeof(Entity)); UnsafeUtility.MemCpyReplicate((int *)RemovedEntitiesMappingToComponent.GetUnsafePtr() + removedEntityCurrentCount, &removedSharedComponentsCount, sizeof(int), shadowChunk.EntityCount); removedEntityCurrentCount += shadowChunk.EntityCount; IndicesInManagedComponentStore[removedSharedComponentsCount++] = SharedComponentValueIndexByChunk[chunkSequenceNumber]; ShadowChunksBySequenceNumber.Remove(chunkSequenceNumber); SharedComponentValueIndexByChunk.Remove(chunkSequenceNumber); UnsafeUtility.Free(shadowChunk.EntityDataBuffer, Allocator.Persistent); } for (var i = 0; i < GatheredChanges.Length; i++) { var changes = GatheredChanges[i]; if (changes.RemovedEntities.Length == 0) { continue; } UnsafeUtility.MemCpy((Entity *)RemovedEntities.GetUnsafePtr() + removedEntityCurrentCount, changes.RemovedEntities.Ptr, changes.RemovedEntities.Length * sizeof(Entity)); UnsafeUtility.MemCpyReplicate((int *)RemovedEntitiesMappingToComponent.GetUnsafePtr() + removedEntityCurrentCount, &removedSharedComponentsCount, sizeof(int), changes.RemovedEntities.Length); removedEntityCurrentCount += changes.RemovedEntities.Length; IndicesInManagedComponentStore[removedSharedComponentsCount++] = SharedComponentValueIndexByChunk[changes.Chunk->SequenceNumber]; } for (var i = 0; i < GatheredChanges.Length; i++) { var changes = GatheredChanges[i]; if (changes.AddedEntities.Length == 0) { continue; } var chunkSequenceNumber = changes.Chunk->SequenceNumber; if (changes.AddedEntities.Length > 0) { var archetype = changes.Chunk->Archetype; var indexInTypeArray = ChunkDataUtility.GetIndexInTypeArray(archetype, TypeIndex); var sharedComponentValueArray = changes.Chunk->SharedComponentValues; var sharedComponentOffset = indexInTypeArray - archetype->FirstSharedComponent; var sharedComponentDataIndex = sharedComponentValueArray[sharedComponentOffset]; SharedComponentValueIndexByChunk[chunkSequenceNumber] = sharedComponentDataIndex; UnsafeUtility.MemCpy((Entity *)AddedEntities.GetUnsafePtr() + addedEntityCurrentCount, changes.AddedEntities.Ptr, changes.AddedEntities.Length * sizeof(Entity)); var index = removedSharedComponentsCount + addedSharedComponentsCount; UnsafeUtility.MemCpyReplicate((int *)AddedEntitiesMappingToComponent.GetUnsafePtr() + addedEntityCurrentCount, &index, sizeof(int), changes.AddedEntities.Length); addedEntityCurrentCount += changes.AddedEntities.Length; IndicesInManagedComponentStore[index] = sharedComponentDataIndex; addedSharedComponentsCount++; } } }
public static unsafe void *GetComponentDataWithTypeRW(this ArchetypeChunk chunk, int chunkIndex, int typeIndex, uint globalSystemVersion) { chunk.AssertEntityHasComponent(typeIndex); return(ChunkDataUtility.GetComponentDataWithTypeRW(chunk.m_Chunk, chunkIndex, typeIndex, globalSystemVersion)); }
public static unsafe int GetIndexInTypeArray(this ArchetypeChunk chunk, int typeIndex) { return(ChunkDataUtility.GetIndexInTypeArray(chunk.m_Chunk->Archetype, typeIndex)); }