void ConstructChunk(Archetype *archetype, Chunk *chunk, SharedComponentValues sharedComponentValues) { chunk->Archetype = archetype; chunk->Count = 0; chunk->Capacity = archetype->ChunkCapacity; chunk->SequenceNumber = AssignSequenceNumber(chunk); chunk->metaChunkEntity = Entity.Null; var numSharedComponents = archetype->NumSharedComponents; if (numSharedComponents > 0) { for (var i = 0; i < archetype->NumSharedComponents; ++i) { var sharedComponentIndex = sharedComponentValues[i]; ManagedChangesTracker.AddReference(sharedComponentIndex); } } archetype->AddToChunkList(chunk, sharedComponentValues, GlobalSystemVersion); Assert.IsTrue(archetype->Chunks.Count != 0); // Chunk can't be locked at at construction time archetype->EmptySlotTrackingAddChunk(chunk); if (numSharedComponents == 0) { Assert.IsTrue(archetype->ChunksWithEmptySlots.Length != 0); } else { Assert.IsTrue(archetype->FreeChunksBySharedComponents.TryGet(chunk->SharedComponentValues, archetype->NumSharedComponents) != null); } if (archetype->NumManagedArrays > 0) { int managedArrayIndex; if (!m_ManagedArrayFreeIndex.IsEmpty) { managedArrayIndex = m_ManagedArrayFreeIndex.Pop <int>(); } else { managedArrayIndex = m_ManagedArrayIndex; m_ManagedArrayIndex++; } ManagedChangesTracker.AllocateManagedArrayStorage(managedArrayIndex, archetype->NumManagedArrays * chunk->Capacity); chunk->ManagedArrayIndex = managedArrayIndex; } else { chunk->ManagedArrayIndex = -1; } chunk->Flags = 0; }
void ReleaseChunk(Chunk *chunk) { // Remove references to shared components if (chunk->Archetype->NumSharedComponents > 0) { var sharedComponentValueArray = chunk->SharedComponentValues; for (var i = 0; i < chunk->Archetype->NumSharedComponents; ++i) { ManagedChangesTracker.RemoveReference(sharedComponentValueArray[i]); } } if (chunk->ManagedArrayIndex != -1) { ManagedChangesTracker.DeallocateManagedArrayStorage(chunk->ManagedArrayIndex); m_ManagedArrayFreeIndex.Add(chunk->ManagedArrayIndex); chunk->ManagedArrayIndex = -1; } // this chunk is going away, so it shouldn't be in the empty slot list. if (chunk->Count < chunk->Capacity) { chunk->Archetype->EmptySlotTrackingRemoveChunk(chunk); } chunk->Archetype->RemoveFromChunkList(chunk); chunk->Archetype = null; FreeChunk(chunk); }
void DeleteChunk(Chunk *chunk) { var entityCount = chunk->Count; DeallocateDataEntitiesInChunk(chunk, 0, chunk->Count); ManagedChangesTracker.IncrementComponentOrderVersion(chunk->Archetype, chunk->SharedComponentValues); IncrementComponentTypeOrderVersion(chunk->Archetype); chunk->Archetype->EntityCount -= entityCount; SetChunkCount(chunk, 0); }
void DestroyBatch(Entity *entities, Chunk *chunk, int indexInChunk, int batchCount) { var archetype = chunk->Archetype; if (!archetype->SystemStateCleanupNeeded) { DeallocateDataEntitiesInChunk(entities, chunk, indexInChunk, batchCount); ManagedChangesTracker.IncrementComponentOrderVersion(archetype, chunk->SharedComponentValues); IncrementComponentTypeOrderVersion(archetype); if (chunk->ManagedArrayIndex >= 0) { // We can just chop-off the end, no need to copy anything if (chunk->Count != indexInChunk + batchCount) { ManagedChangesTracker.CopyManagedObjects(chunk, chunk->Count - batchCount, chunk, indexInChunk, batchCount); } ManagedChangesTracker.ClearManagedObjects(chunk, chunk->Count - batchCount, batchCount); } chunk->Archetype->EntityCount -= batchCount; SetChunkCount(chunk, chunk->Count - batchCount); } else { var dstArchetypeChunkFilter = new ArchetypeChunkFilter(); dstArchetypeChunkFilter.Archetype = archetype->SystemStateResidueArchetype; if (RequiresBuildingResidueSharedComponentIndices(archetype, dstArchetypeChunkFilter.Archetype)) { BuildResidueSharedComponentIndices(archetype, dstArchetypeChunkFilter.Archetype, chunk->SharedComponentValues, dstArchetypeChunkFilter.SharedComponentValues); } if (batchCount == chunk->Count) { Move(chunk, ref dstArchetypeChunkFilter); } else { Move(new EntityBatchInChunk { Chunk = chunk, StartIndex = indexInChunk, Count = batchCount }, ref dstArchetypeChunkFilter); } } }
void DestroyBatch(Chunk *chunk, int indexInChunk, int batchCount) { var archetype = chunk->Archetype; if (!archetype->SystemStateCleanupNeeded) { DeallocateDataEntitiesInChunk(chunk, indexInChunk, batchCount); ManagedChangesTracker.IncrementComponentOrderVersion(archetype, chunk->SharedComponentValues); IncrementComponentTypeOrderVersion(archetype); chunk->Archetype->EntityCount -= batchCount; SetChunkCount(chunk, chunk->Count - batchCount); } else { var systemStateResidueArchetype = archetype->SystemStateResidueArchetype; if (archetype == systemStateResidueArchetype) { return; } var dstArchetypeChunkFilter = new ArchetypeChunkFilter(); dstArchetypeChunkFilter.Archetype = systemStateResidueArchetype; if (RequiresBuildingResidueSharedComponentIndices(archetype, dstArchetypeChunkFilter.Archetype)) { BuildResidueSharedComponentIndices(archetype, dstArchetypeChunkFilter.Archetype, chunk->SharedComponentValues, dstArchetypeChunkFilter.SharedComponentValues); } else { chunk->SharedComponentValues.CopyTo(dstArchetypeChunkFilter.SharedComponentValues, 0, archetype->NumSharedComponents); } if (batchCount == chunk->Count) { Move(chunk, ref dstArchetypeChunkFilter); } else { Move(new EntityBatchInChunk { Chunk = chunk, StartIndex = indexInChunk, Count = batchCount }, ref dstArchetypeChunkFilter); } } }
// ---------------------------------------------------------------------------------------------------------- // PUBLIC // ---------------------------------------------------------------------------------------------------------- public void AddExistingChunk(Chunk *chunk, int *sharedComponentIndices) { var archetype = chunk->Archetype; archetype->AddToChunkList(chunk, sharedComponentIndices, GlobalSystemVersion); archetype->EntityCount += chunk->Count; for (var i = 0; i < archetype->NumSharedComponents; ++i) { ManagedChangesTracker.AddReference(sharedComponentIndices[i]); } if (chunk->Count < chunk->Capacity) { archetype->EmptySlotTrackingAddChunk(chunk); } AddExistingEntitiesInChunk(chunk); }
public void ReserveManagedObjectArrays(NativeArray <int> indices) { int available = m_ManagedArrayFreeIndex.Size / UnsafeUtility.SizeOf <int>(); if (available > indices.Length) { available = indices.Length; } m_ManagedArrayFreeIndex.Pop(indices.GetUnsafePtr(), available * UnsafeUtility.SizeOf <int>()); int remainder = indices.Length - available; if (remainder > 0) { for (int i = available; i < indices.Length; ++i) { indices[i] = m_ManagedArrayIndex + i - available; } m_ManagedArrayIndex += remainder; ManagedChangesTracker.ReserveManagedArrayStorage(remainder); } }
void InstantiateEntitiesGroup(Entity *srcEntities, int srcEntityCount, Entity *outputRootEntities, int instanceCount) { int totalCount = srcEntityCount * instanceCount; var tempAllocSize = sizeof(EntityRemapUtility.SparseEntityRemapInfo) * totalCount + sizeof(InstantiateRemapChunk) * totalCount + sizeof(Entity) * instanceCount; byte * allocation; const int kMaxStackAllocSize = 16 * 1024; if (tempAllocSize > kMaxStackAllocSize) { allocation = (byte *)UnsafeUtility.Malloc(tempAllocSize, 16, Allocator.Temp); } else { var temp = stackalloc byte[tempAllocSize]; allocation = temp; } var entityRemap = (EntityRemapUtility.SparseEntityRemapInfo *)allocation; var remapChunks = (InstantiateRemapChunk *)(entityRemap + totalCount); var outputEntities = (Entity *)(remapChunks + totalCount); var remapChunksCount = 0; for (int i = 0; i != srcEntityCount; i++) { var srcEntity = srcEntities[i]; remapChunksCount = InstantiateEntitiesOne(srcEntity, outputEntities, instanceCount, remapChunks, remapChunksCount); for (int r = 0; r != instanceCount; r++) { var ptr = entityRemap + (r * srcEntityCount + i); ptr->Src = srcEntity; ptr->Target = outputEntities[r]; } if (i == 0) { for (int r = 0; r != instanceCount; r++) { outputRootEntities[r] = outputEntities[r]; } } } for (int i = 0; i != remapChunksCount; i++) { var chunk = remapChunks[i].Chunk; var dstArchetype = chunk->Archetype; var allocatedCount = remapChunks[i].AllocatedCount; var indexInChunk = remapChunks[i].IndexInChunk; var instanceBeginIndex = remapChunks[i].InstanceBeginIndex; var localRemap = entityRemap + instanceBeginIndex * srcEntityCount; EntityRemapUtility.PatchEntitiesForPrefab(dstArchetype->ScalarEntityPatches + 1, dstArchetype->ScalarEntityPatchCount - 1, dstArchetype->BufferEntityPatches, dstArchetype->BufferEntityPatchCount, chunk->Buffer, indexInChunk, allocatedCount, localRemap, srcEntityCount); ManagedChangesTracker.PatchEntitiesForPrefab(dstArchetype, chunk, indexInChunk, allocatedCount, localRemap, srcEntityCount, Allocator.Temp); } if (tempAllocSize > kMaxStackAllocSize) { UnsafeUtility.Free(allocation, Allocator.Temp); } }
int InstantiateEntitiesOne(Entity srcEntity, Entity *outputEntities, int instanceCount, InstantiateRemapChunk *remapChunks, int remapChunksCount) { var src = GetEntityInChunk(srcEntity); var srcArchetype = src.Chunk->Archetype; var dstArchetype = srcArchetype->InstantiableArchetype; var archetypeChunkFilter = new ArchetypeChunkFilter(); archetypeChunkFilter.Archetype = dstArchetype; if (RequiresBuildingResidueSharedComponentIndices(srcArchetype, dstArchetype)) { BuildResidueSharedComponentIndices(srcArchetype, dstArchetype, src.Chunk->SharedComponentValues, archetypeChunkFilter.SharedComponentValues); } else { // Always copy shared component indices since GetChunkWithEmptySlots might reallocate the storage of SharedComponentValues src.Chunk->SharedComponentValues.CopyTo(archetypeChunkFilter.SharedComponentValues, 0, dstArchetype->NumSharedComponents); } Chunk *chunk = null; int instanceBeginIndex = 0; while (instanceBeginIndex != instanceCount) { chunk = GetChunkWithEmptySlots(ref archetypeChunkFilter); int indexInChunk; var allocatedCount = AllocateIntoChunk(chunk, instanceCount - instanceBeginIndex, out indexInChunk); ChunkDataUtility.ReplicateComponents(src.Chunk, src.IndexInChunk, chunk, indexInChunk, allocatedCount); AllocateEntities(dstArchetype, chunk, indexInChunk, allocatedCount, outputEntities + instanceBeginIndex); if (srcArchetype->NumManagedArrays > 0) { ManagedChangesTracker.ReplicateManagedObjects(src.Chunk, src.IndexInChunk, chunk, indexInChunk, allocatedCount, srcEntity, outputEntities + instanceBeginIndex); } chunk->SetAllChangeVersions(GlobalSystemVersion); #if UNITY_EDITOR for (var i = 0; i < allocatedCount; ++i) { CopyName(outputEntities[i + instanceBeginIndex], srcEntity); } #endif if (remapChunks != null) { remapChunks[remapChunksCount].Chunk = chunk; remapChunks[remapChunksCount].IndexInChunk = indexInChunk; remapChunks[remapChunksCount].AllocatedCount = allocatedCount; remapChunks[remapChunksCount].InstanceBeginIndex = instanceBeginIndex; remapChunksCount++; } instanceBeginIndex += allocatedCount; } if (chunk != null) { ManagedChangesTracker.IncrementComponentOrderVersion(dstArchetype, chunk->SharedComponentValues); IncrementComponentTypeOrderVersion(dstArchetype); } return(remapChunksCount); }
/// <summary> /// Fix-up the chunk to refer to a different (but layout compatible) archetype. /// - Should only be called by Move(chunk) /// </summary> void ChangeArchetypeInPlace(Chunk *srcChunk, ref ArchetypeChunkFilter dstArchetypeChunkFilter) { var dstArchetype = dstArchetypeChunkFilter.Archetype; fixed(int *sharedComponentValues = dstArchetypeChunkFilter.SharedComponentValues) { var srcArchetype = srcChunk->Archetype; ChunkDataUtility.AssertAreLayoutCompatible(srcArchetype, dstArchetype); var fixupSharedComponentReferences = (srcArchetype->NumSharedComponents > 0) || (dstArchetype->NumSharedComponents > 0); if (fixupSharedComponentReferences) { int srcFirstShared = srcArchetype->FirstSharedComponent; int dstFirstShared = dstArchetype->FirstSharedComponent; int srcCount = srcArchetype->NumSharedComponents; int dstCount = dstArchetype->NumSharedComponents; int o = 0; int n = 0; for (; n < dstCount && o < srcCount;) { int srcType = srcArchetype->Types[o + srcFirstShared].TypeIndex; int dstType = dstArchetype->Types[n + dstFirstShared].TypeIndex; if (srcType == dstType) { var srcSharedComponentDataIndex = srcChunk->SharedComponentValues[o]; var dstSharedComponentDataIndex = sharedComponentValues[n]; if (srcSharedComponentDataIndex != dstSharedComponentDataIndex) { ManagedChangesTracker.RemoveReference(srcSharedComponentDataIndex); ManagedChangesTracker.AddReference(dstSharedComponentDataIndex); } n++; o++; } else if (dstType > srcType) // removed from dstArchetype { var sharedComponentDataIndex = srcChunk->SharedComponentValues[o]; ManagedChangesTracker.RemoveReference(sharedComponentDataIndex); o++; } else // added to dstArchetype { var sharedComponentDataIndex = sharedComponentValues[n]; ManagedChangesTracker.AddReference(sharedComponentDataIndex); n++; } } for (; n < dstCount; n++) // added to dstArchetype { var sharedComponentDataIndex = sharedComponentValues[n]; ManagedChangesTracker.AddReference(sharedComponentDataIndex); } for (; o < srcCount; o++) // removed from dstArchetype { var sharedComponentDataIndex = srcChunk->SharedComponentValues[o]; ManagedChangesTracker.RemoveReference(sharedComponentDataIndex); } } var fixupManagedComponents = (srcArchetype->NumManagedArrays > 0) || (dstArchetype->NumManagedArrays > 0); if (fixupManagedComponents) { if (dstArchetype->NumManagedArrays == 0) // removed all { ManagedChangesTracker.DeallocateManagedArrayStorage(srcChunk->ManagedArrayIndex); m_ManagedArrayFreeIndex.Add(srcChunk->ManagedArrayIndex); srcChunk->ManagedArrayIndex = -1; } else { // We have changed the managed array order + size so allocate a new managed array // copy the unchanged values into it int dstManagedArrayIndex; if (!m_ManagedArrayFreeIndex.IsEmpty) { dstManagedArrayIndex = m_ManagedArrayFreeIndex.Pop <int>(); } else { dstManagedArrayIndex = m_ManagedArrayIndex; m_ManagedArrayIndex++; } ManagedChangesTracker.AllocateManagedArrayStorage(dstManagedArrayIndex, dstArchetype->NumManagedArrays * srcChunk->Capacity); int srcManagedArrayIndex = srcChunk->ManagedArrayIndex; srcChunk->ManagedArrayIndex = dstManagedArrayIndex; if (srcManagedArrayIndex != -1) { ManagedChangesTracker.MoveChunksManagedObjects(srcArchetype, srcManagedArrayIndex, dstArchetype, dstManagedArrayIndex, srcChunk->Capacity, srcChunk->Count); ManagedChangesTracker.DeallocateManagedArrayStorage(srcManagedArrayIndex); m_ManagedArrayFreeIndex.Add(srcManagedArrayIndex); } } } var count = srcChunk->Count; bool hasEmptySlots = count < srcChunk->Capacity; if (hasEmptySlots) { srcArchetype->EmptySlotTrackingRemoveChunk(srcChunk); } int chunkIndexInSrcArchetype = srcChunk->ListIndex; var dstTypes = dstArchetype->Types; var srcTypes = srcArchetype->Types; //Change version is overriden below dstArchetype->AddToChunkList(srcChunk, sharedComponentValues, 0); int chunkIndexInDstArchetype = srcChunk->ListIndex; //Copy change versions from src to dst archetype for (int isrcType = srcArchetype->TypesCount - 1, idstType = dstArchetype->TypesCount - 1; idstType >= 0; --idstType) { var dstType = dstTypes[idstType]; while (srcTypes[isrcType] > dstType) { --isrcType; } var version = srcTypes[isrcType] == dstType ? srcArchetype->Chunks.GetChangeVersion(isrcType, chunkIndexInSrcArchetype) : GlobalSystemVersion; dstArchetype->Chunks.SetChangeVersion(idstType, chunkIndexInDstArchetype, version); } srcChunk->ListIndex = chunkIndexInSrcArchetype; srcArchetype->RemoveFromChunkList(srcChunk); srcChunk->ListIndex = chunkIndexInDstArchetype; if (hasEmptySlots) { dstArchetype->EmptySlotTrackingAddChunk(srcChunk); } SetArchetype(srcChunk, dstArchetype); srcArchetype->EntityCount -= count; dstArchetype->EntityCount += count; if (srcArchetype->MetaChunkArchetype != dstArchetype->MetaChunkArchetype) { if (srcArchetype->MetaChunkArchetype == null) { CreateMetaEntityForChunk(srcChunk); } else if (dstArchetype->MetaChunkArchetype == null) { DestroyMetaChunkEntity(srcChunk->metaChunkEntity); srcChunk->metaChunkEntity = Entity.Null; } else { var metaChunk = GetChunk(srcChunk->metaChunkEntity); var archetypeChunkFilter = new ArchetypeChunkFilter(dstArchetype->MetaChunkArchetype, metaChunk->SharedComponentValues); Move(srcChunk->metaChunkEntity, ref archetypeChunkFilter); } } } }
// ---------------------------------------------------------------------------------------------------------- // Core, self-contained functions to change chunks. No other functions should actually move data from // one Chunk to another, or otherwise change the structure of a Chunk after creation. // ---------------------------------------------------------------------------------------------------------- /// <summary> /// Move subset of chunk data into another chunk. /// - Chunks can be of same archetype (but differ by shared component values) /// - Returns number moved. Caller handles if less than indicated in srcBatch. /// </summary> /// <returns></returns> int Move(EntityBatchInChunk srcBatch, Chunk *dstChunk) { var srcChunk = srcBatch.Chunk; var srcChunkIndex = srcBatch.StartIndex; var srcCount = srcBatch.Count; var srcArchetype = srcChunk->Archetype; var dstArchetype = dstChunk->Archetype; // Note (srcArchetype == dstArchetype) is valid // Archetypes can the the same, but chunks still differ because filter is different (e.g. shared component) int dstChunkIndex; var dstCount = AllocateIntoChunk(dstChunk, srcCount, out dstChunkIndex); // If can only move partial batch, move from the end so that remainder of batch isn't affected. srcChunkIndex = srcChunkIndex + srcCount - dstCount; ChunkDataUtility.Convert(srcChunk, srcChunkIndex, dstChunk, dstChunkIndex, dstCount); if (dstChunk->ManagedArrayIndex >= 0 && srcChunk->ManagedArrayIndex >= 0) { ManagedChangesTracker.CopyManagedObjects(srcChunk, srcChunkIndex, dstChunk, dstChunkIndex, dstCount); } var dstEntities = (Entity *)ChunkDataUtility.GetComponentDataRO(dstChunk, dstChunkIndex, 0); for (int i = 0; i < dstCount; i++) { var entity = dstEntities[i]; SetArchetype(entity, dstArchetype); SetEntityInChunk(entity, new EntityInChunk { Chunk = dstChunk, IndexInChunk = dstChunkIndex + i }); } // Fill in moved component data from the end. var srcTailIndex = srcChunkIndex + dstCount; var srcTailCount = srcChunk->Count - srcTailIndex; var fillCount = math.min(dstCount, srcTailCount); if (fillCount > 0) { var fillStartIndex = srcChunk->Count - fillCount; ChunkDataUtility.Copy(srcChunk, fillStartIndex, srcChunk, srcChunkIndex, fillCount); var fillEntities = (Entity *)ChunkDataUtility.GetComponentDataRO(srcChunk, srcChunkIndex, 0); for (int i = 0; i < fillCount; i++) { var entity = fillEntities[i]; SetEntityInChunk(entity, new EntityInChunk { Chunk = srcChunk, IndexInChunk = srcChunkIndex + i }); } if (srcChunk->ManagedArrayIndex >= 0) { ManagedChangesTracker.CopyManagedObjects(srcChunk, fillStartIndex, srcChunk, srcChunkIndex, fillCount); } } if (srcChunk->ManagedArrayIndex >= 0) { ManagedChangesTracker.ClearManagedObjects(srcChunk, srcChunk->Count - dstCount, dstCount); } srcArchetype->EntityCount -= dstCount; dstChunk->SetAllChangeVersions(GlobalSystemVersion); srcChunk->SetAllChangeVersions(GlobalSystemVersion); ManagedChangesTracker.IncrementComponentOrderVersion(srcArchetype, srcChunk->SharedComponentValues); IncrementComponentTypeOrderVersion(srcArchetype); ManagedChangesTracker.IncrementComponentOrderVersion(dstArchetype, dstChunk->SharedComponentValues); IncrementComponentTypeOrderVersion(dstArchetype); SetChunkCount(srcChunk, srcChunk->Count - dstCount); // Cannot DestroyEntities unless SystemStateCleanupComplete on the entity chunk. if (dstChunk->Archetype->SystemStateCleanupComplete) { DestroyEntities(dstEntities, dstCount); } return(dstCount); }
void DestroyBatch(Entity *entities, Chunk *chunk, int indexInChunk, int batchCount) { var archetype = chunk->Archetype; if (!archetype->SystemStateCleanupNeeded) { DeallocateDataEntitiesInChunk(entities, chunk, indexInChunk, batchCount); ManagedChangesTracker.IncrementComponentOrderVersion(archetype, chunk->SharedComponentValues); IncrementComponentTypeOrderVersion(archetype); if (chunk->ManagedArrayIndex >= 0) { // We can just chop-off the end, no need to copy anything if (chunk->Count != indexInChunk + batchCount) { ManagedChangesTracker.CopyManagedObjects(chunk, chunk->Count - batchCount, chunk, indexInChunk, batchCount); } ManagedChangesTracker.ClearManagedObjects(chunk, chunk->Count - batchCount, batchCount); } chunk->Archetype->EntityCount -= batchCount; SetChunkCount(chunk, chunk->Count - batchCount); } else { var newType = archetype->SystemStateResidueArchetype; var sharedComponentValues = chunk->SharedComponentValues; if (RequiresBuildingResidueSharedComponentIndices(archetype, newType)) { var tempAlloc = stackalloc int[newType->NumSharedComponents]; BuildResidueSharedComponentIndices(archetype, newType, sharedComponentValues, tempAlloc); sharedComponentValues = tempAlloc; } // See: https://github.com/Unity-Technologies/dots/issues/1387 // For Locked Order Chunks specfically, need to make sure that structural changes are always done per-chunk. // If trying to muutate structure in a way that is not per chunk, will hit an exception in the else clause anyway. // This ultimately needs to be replaced by entity batch interface. if (batchCount == chunk->Count) { ManagedChangesTracker.IncrementComponentOrderVersion(archetype, chunk->SharedComponentValues); IncrementComponentTypeOrderVersion(archetype); SetArchetype(chunk, newType, sharedComponentValues); } else { for (var i = 0; i < batchCount; i++) { var entity = entities[i]; ManagedChangesTracker.IncrementComponentOrderVersion(archetype, GetChunk(entity)->SharedComponentValues); IncrementComponentTypeOrderVersion(archetype); SetArchetype(entity, newType, sharedComponentValues); } } } }