From 742ca00b09d20c3d52ce42aeee94330bc77fe638 Mon Sep 17 00:00:00 2001 From: Lars Date: Wed, 16 Oct 2024 00:45:05 +0200 Subject: [PATCH] WIP --- .../ArchetypeIterationBenchmark.cs | 12 +- .../ArchetypeIterationTechnqiquesBenchmark.cs | 16 +- src/Arch.Benchmarks/QueryBenchmark.cs | 2 +- src/Arch.SourceGen/Fundamentals/Set.cs | 2 +- src/Arch.SourceGen/Queries/InlineQuery.cs | 6 +- src/Arch.SourceGen/Queries/Job.cs | 6 +- src/Arch.Tests/ArchetypeTest.cs | 144 +++++++-- src/Arch.Tests/ChunkTest.cs | 10 +- src/Arch.Tests/EnumeratorTest.cs | 6 +- src/Arch.Tests/WorldTest.cs | 57 ++-- src/Arch/Arch.csproj | 9 + src/Arch/Core/Archetype.cs | 281 ++++++++---------- src/Arch/Core/Chunk.cs | 113 ++++++- src/Arch/Core/EntityInfo.cs | 2 +- src/Arch/Core/Enumerators.cs | 2 +- .../Dangerous/DangerousArchetypeExtensions.cs | 5 +- .../Dangerous/DangerousChunkExtensions.cs | 2 +- src/Arch/Core/World.cs | 22 +- src/Arch/Templates/World.CreateBulk.tt | 4 +- 19 files changed, 428 insertions(+), 273 deletions(-) diff --git a/src/Arch.Benchmarks/ArchetypeIterationBenchmark.cs b/src/Arch.Benchmarks/ArchetypeIterationBenchmark.cs index 40d894c9..bf075bdc 100644 --- a/src/Arch.Benchmarks/ArchetypeIterationBenchmark.cs +++ b/src/Arch.Benchmarks/ArchetypeIterationBenchmark.cs @@ -22,7 +22,7 @@ public void Setup() // jobScheduler = new JobScheduler(); _globalArchetype = new Archetype(_group); - _globalArchetype.Reserve(Amount); + _globalArchetype.EnsureEntityCapacity(Amount); for (var index = 0; index < Amount; index++) { @@ -45,7 +45,7 @@ public void IterationNormalTwoComponents() for (var chunkIndex = 0; chunkIndex < size; chunkIndex++) { ref readonly var chunk = ref chunks[chunkIndex]; - var chunkSize = chunk.Size; + var chunkSize = chunk.Count; var transforms = chunk.GetArray(); var rotations = chunk.GetArray(); @@ -69,7 +69,7 @@ public void IterationUnsafeAddTwoComponents() for (var chunkIndex = 0; chunkIndex < size; chunkIndex++) { ref readonly var currentChunk = ref Unsafe.Add(ref chunk, chunkIndex); - var chunkSize = chunk.Size; + var chunkSize = chunk.Count; var transforms = currentChunk.GetArray(); var rotations = currentChunk.GetArray(); @@ -103,7 +103,7 @@ public void IterationParallelUnsafeAdd() for (var chunkIndex = 0; chunkIndex < end - start; chunkIndex++) { ref readonly var currentChunk = ref Unsafe.Add(ref chunk, chunkIndex); - var chunkSize = chunk.Size; + var chunkSize = chunk.Count; var transforms = currentChunk.GetArray(); var rotations = currentChunk.GetArray(); @@ -132,7 +132,7 @@ public void IterationNormalEntityTwoComponents() for (var chunkIndex = 0; chunkIndex < size; chunkIndex++) { ref readonly var chunk = ref chunks[chunkIndex]; - var chunkSize = chunk.Size; + var chunkSize = chunk.Count; var entities = chunk.Entities; var transforms = chunk.GetArray(); @@ -160,7 +160,7 @@ public void IterationUnsafeAddEntityTwoComponents() for (var chunkIndex = 0; chunkIndex < size; chunkIndex++) { ref readonly var currentChunk = ref Unsafe.Add(ref chunk, chunkIndex); - var chunkSize = chunk.Size; + var chunkSize = chunk.Count; var entities = currentChunk.Entities; var transforms = currentChunk.GetArray(); diff --git a/src/Arch.Benchmarks/ArchetypeIterationTechnqiquesBenchmark.cs b/src/Arch.Benchmarks/ArchetypeIterationTechnqiquesBenchmark.cs index 0ee2e7d5..c076f598 100644 --- a/src/Arch.Benchmarks/ArchetypeIterationTechnqiquesBenchmark.cs +++ b/src/Arch.Benchmarks/ArchetypeIterationTechnqiquesBenchmark.cs @@ -25,7 +25,7 @@ public void Setup() _consumer = new Consumer(); _globalArchetype = new Archetype(_group); - _globalArchetype.Reserve(Amount); + _globalArchetype.EnsureEntityCapacity(Amount); for (var index = 0; index < Amount; index++) { @@ -51,7 +51,7 @@ public void IterationNormalTwoComponents() var transforms = chunk.GetArray(); var rotations = chunk.GetArray(); - for (var index = 0; index < chunk.Size; index++) + for (var index = 0; index < chunk.Count; index++) { ref var transform = ref transforms[index]; ref var rotation = ref rotations[index]; @@ -76,7 +76,7 @@ public void IterationUnsafeAddTwoComponents() ref var transform = ref transforms[0]; ref var rotation = ref rotations[0]; - for (var index = 0; index < currentChunk.Size; index++) + for (var index = 0; index < currentChunk.Count; index++) { ref var currentTransform = ref Unsafe.Add(ref transform, index); ref var currentRotation = ref Unsafe.Add(ref rotation, index); @@ -98,7 +98,7 @@ public void IterationNormalTwoComponentsSpan() var transforms = chunk.GetSpan(); var rotations = chunk.GetSpan(); - for (var index = 0; index < chunk.Size; index++) + for (var index = 0; index < chunk.Count; index++) { ref var transform = ref transforms[index]; ref var rotation = ref rotations[index]; @@ -123,7 +123,7 @@ public void IterationUnsafeAddTwoComponentsSpan() ref var transform = ref transforms[0]; ref var rotation = ref rotations[0]; - for (var index = 0; index < currentChunk.Size; index++) + for (var index = 0; index < currentChunk.Count; index++) { ref var currentTransform = ref Unsafe.Add(ref transform, index); ref var currentRotation = ref Unsafe.Add(ref rotation, index); @@ -142,7 +142,7 @@ public void IterationBackwardsUnsafeAdd() for (var chunkIndex = 0; chunkIndex < _globalArchetype.ChunkCount; chunkIndex++) { ref var currentChunk = ref Unsafe.Add(ref chunk, chunkIndex); - var chunkSize = currentChunk.Size; + var chunkSize = currentChunk.Count; ref var entityFirstElement = ref ArrayExtensions.DangerousGetReference(currentChunk.Entities); for (var entityIndex = chunkSize - 1; entityIndex >= 0; --entityIndex) @@ -160,7 +160,7 @@ public void IterationBackwardsUnsafeSubstract() for (var chunkIndex = 0; chunkIndex < _globalArchetype.ChunkCount; chunkIndex++) { ref var currentChunk = ref Unsafe.Add(ref chunk, chunkIndex); - var chunkSize = currentChunk.Size; + var chunkSize = currentChunk.Count; ref var entityLastElement = ref ArrayExtensions.DangerousGetReferenceAt(currentChunk.Entities, chunkSize - 1); for (var entityIndex = 0; entityIndex < chunkSize; ++entityIndex) @@ -178,7 +178,7 @@ public void IterationBackwardsLoop() for (var chunkIndex = 0; chunkIndex < _globalArchetype.ChunkCount; chunkIndex++) { ref var currentChunk = ref Unsafe.Add(ref chunk, chunkIndex); - var chunkSize = currentChunk.Size; + var chunkSize = currentChunk.Count; ref var entityFirstElement = ref ArrayExtensions.DangerousGetReference(currentChunk.Entities); ref var entityLastElement = ref ArrayExtensions.DangerousGetReferenceAt(currentChunk.Entities, chunkSize - 1); diff --git a/src/Arch.Benchmarks/QueryBenchmark.cs b/src/Arch.Benchmarks/QueryBenchmark.cs index 7f4a9dd3..a7bf451b 100644 --- a/src/Arch.Benchmarks/QueryBenchmark.cs +++ b/src/Arch.Benchmarks/QueryBenchmark.cs @@ -21,7 +21,7 @@ public class QueryBenchmark public void Setup() { _world = World.Create(); - _world.Reserve(_group, Amount); + _world.EnsureCapacity(_group, Amount); for (var index = 0; index < Amount; index++) { diff --git a/src/Arch.SourceGen/Fundamentals/Set.cs b/src/Arch.SourceGen/Fundamentals/Set.cs index 41d5c0a1..46f39704 100644 --- a/src/Arch.SourceGen/Fundamentals/Set.cs +++ b/src/Arch.SourceGen/Fundamentals/Set.cs @@ -104,7 +104,7 @@ public static StringBuilder AppendArchetypeSetRange(this StringBuilder sb, int a var isStart = chunkIndex == from.ChunkIndex; var isEnd = chunkIndex == to.ChunkIndex; - var upper = isStart ? from.Index : chunk.Size-1; + var upper = isStart ? from.Index : chunk.Count-1; var lower = isEnd ? to.Index : 0; for (var entityIndex = upper; entityIndex >= lower; --entityIndex) diff --git a/src/Arch.SourceGen/Queries/InlineQuery.cs b/src/Arch.SourceGen/Queries/InlineQuery.cs index 34b0fe28..3ee53e0c 100644 --- a/src/Arch.SourceGen/Queries/InlineQuery.cs +++ b/src/Arch.SourceGen/Queries/InlineQuery.cs @@ -125,7 +125,7 @@ public static StringBuilder AppendQueryInterfaceMethods(this StringBuilder build var query = Query(in description); foreach (ref var chunk in query) { - var chunkSize = chunk.Size; + var chunkSize = chunk.Count; {{getFirstElement}} foreach(var entityIndex in chunk) @@ -160,7 +160,7 @@ public static StringBuilder AppendEntityQueryInterfaceMethods(this StringBuilder var query = Query(in description); foreach (ref var chunk in query) { - var chunkSize = chunk.Size; + var chunkSize = chunk.Count; ref var entityFirstElement = ref chunk.Entity(0); {{getFirstElement}} @@ -195,7 +195,7 @@ public static StringBuilder AppendEntityQueryInterfaceMethods(this StringBuilder var query = Query(in description); foreach (ref var chunk in query) { - var chunkSize = chunk.Size; + var chunkSize = chunk.Count; ref var entityFirstElement = ref chunk.Entity(0); {{getFirstElement}} diff --git a/src/Arch.SourceGen/Queries/Job.cs b/src/Arch.SourceGen/Queries/Job.cs index bcabcd03..d2c51134 100644 --- a/src/Arch.SourceGen/Queries/Job.cs +++ b/src/Arch.SourceGen/Queries/Job.cs @@ -26,7 +26,7 @@ public struct ForEachJob<{{generics}}> : IChunkJob public void Execute(ref Chunk chunk) { - var chunkSize = chunk.Size; + var chunkSize = chunk.Count; {{getFirstElement}} foreach(var entityIndex in chunk) @@ -106,7 +106,7 @@ public struct IForEachJob : IChunkJob where T : struct, IForEach public void Execute(ref Chunk chunk) { - var chunkSize = chunk.Size; + var chunkSize = chunk.Count; {{getFirstElement}} foreach(var entityIndex in chunk) @@ -145,7 +145,7 @@ public struct IForEachWithEntityJob : IChunkJob where T : struct public void Execute(ref Chunk chunk) { - var chunkSize = chunk.Size; + var chunkSize = chunk.Count; ref var entityFirstElement = ref chunk.Entity(0); {{getFirstElement}} diff --git a/src/Arch.Tests/ArchetypeTest.cs b/src/Arch.Tests/ArchetypeTest.cs index bd22803a..1951e699 100644 --- a/src/Arch.Tests/ArchetypeTest.cs +++ b/src/Arch.Tests/ArchetypeTest.cs @@ -27,7 +27,7 @@ public void CreateChunk() { // Create archetype var archetype = new Archetype(_group); - var entities = Archetype.CalculateEntitiesPerChunk(archetype.ChunkSizeInBytes, _group); + var entities = Archetype.GetEntityCountFor(archetype.ChunkSizeInBytes, _group); // Fill archetype for (var index = 0; index < entities; index++) @@ -36,7 +36,38 @@ public void CreateChunk() archetype.Add(entity, out _, out _); } + That(archetype.EntityCount, Is.EqualTo(entities)); + That(archetype.EntityCapacity, Is.EqualTo(entities)); That(archetype.ChunkCount, Is.EqualTo(1)); // Since we filled it with n entities, it must have one single chunk. + That(archetype.Count, Is.EqualTo(0)); // Since we filled it with n entities, it must have one single chunk. + That(archetype.GetChunk(0).Count, Is.EqualTo(entities)); + } + + /// + /// Tests if s and their are created correctly. + /// + [Test] + public void CreateAllChunk() + { + // Create archetype + var archetype = new Archetype(_group); + var count = Archetype.GetEntityCountFor(archetype.ChunkSizeInBytes, _group); + Span entities = stackalloc Entity[count]; + + // Fill archetype + for (var index = 0; index < count; index++) + { + var entity = new Entity(index, 0); + entities[index] = entity; + } + + archetype.AddAll(entities, count); + + That(archetype.EntityCount, Is.EqualTo(count)); + That(archetype.EntityCapacity, Is.EqualTo(count)); + That(archetype.ChunkCount, Is.EqualTo(1)); + That(archetype.Count, Is.EqualTo(0)); + That(archetype.GetChunk(0).Count, Is.EqualTo(count)); } /// @@ -56,7 +87,7 @@ public void ScaleChunkCapacity() public void CreateMultipleChunk() { var archetype = new Archetype(_group); - var entities = Archetype.CalculateEntitiesPerChunk(archetype.ChunkSizeInBytes, _group) * 2; + var entities = Archetype.GetEntityCountFor(archetype.ChunkSizeInBytes, _group) * 2; for (var index = 0; index < entities; index++) { @@ -64,18 +95,22 @@ public void CreateMultipleChunk() archetype.Add(entity, out _, out _); } + That(archetype.EntityCount, Is.EqualTo(entities)); + That(archetype.EntityCapacity, Is.EqualTo(entities)); That(archetype.ChunkCount, Is.EqualTo(2)); + That(archetype.Count, Is.EqualTo(1)); } /// /// Checks if an is able to reserve enough memory for a number of s and their components. /// [Test] - public void Reserve() + public void Ensure() { var archetype = new Archetype(_group); - var entities = Archetype.CalculateEntitiesPerChunk(archetype.ChunkSizeInBytes, _group) * 10; - archetype.Reserve(entities); + var entitiesPerChunk = Archetype.GetEntityCountFor(archetype.ChunkSizeInBytes, _group); + var entities = entitiesPerChunk * 10; + archetype.EnsureEntityCapacity(entities); for (var index = 0; index < entities; index++) { @@ -83,18 +118,21 @@ public void Reserve() archetype.Add(entity, out _, out _); } - That(archetype.ChunkCount, Is.EqualTo(10)); - That(archetype.ChunkCapacity, Is.EqualTo(10)); + That(archetype.EntityCount, Is.EqualTo(entities)); + That(archetype.EntityCapacity, Is.EqualTo(entities)); + That(archetype.ChunkCount, Is.EqualTo(archetype.EntityCount/entitiesPerChunk)); + That(archetype.ChunkCapacity, Is.EqualTo(archetype.EntityCount/entitiesPerChunk)); + That(archetype.Count, Is.EqualTo((archetype.EntityCount/entitiesPerChunk)-1)); } /// - /// Checks if removing an from the causes another to move to that position. + /// Checks whether empty s are deleted and their effect on capacity. /// [Test] - public void RemoveFromChunkWithReplacement() + public void RemoveChunk() { var archetype = new Archetype(_group); - var entities = Archetype.CalculateEntitiesPerChunk(archetype.ChunkSizeInBytes, _group) + 50; + var entities = Archetype.GetEntityCountFor(archetype.ChunkSizeInBytes, _group) + 1; for (var index = 0; index < entities; index++) { @@ -105,21 +143,21 @@ public void RemoveFromChunkWithReplacement() var slot = new Slot(0, 0); archetype.Remove(slot, out _); + That(archetype.Count, Is.EqualTo(0)); That(archetype.ChunkCount, Is.EqualTo(2)); That(archetype.ChunkCapacity, Is.EqualTo(2)); - That(archetype.Chunks[0].Size, Is.EqualTo(entities - 50)); - That(archetype.Chunks[1].Size, Is.EqualTo(49)); - That(archetype.Chunks[0].Entities[0].Id, Is.EqualTo( Archetype.CalculateEntitiesPerChunk(archetype.ChunkSizeInBytes, _group) + 50 - 1)); // Last entity from second chunk now replaced the removed entity and is in the first chunk + That(archetype.Chunks[0].Count, Is.EqualTo(entities - 1)); + That(archetype.Chunks[0].Entities[0].Id, Is.EqualTo( Archetype.GetEntityCountFor(archetype.ChunkSizeInBytes, _group))); // Last entity from second chunk now replaced the removed entity and is in the first chunk } /// - /// Checks whether empty s are deleted and their effect on capacity. + /// Checks if removing an from the causes another to move to that position. /// [Test] - public void RemoveChunk() + public void RemoveFromChunkWithReplacement() { var archetype = new Archetype(_group); - var entities = Archetype.CalculateEntitiesPerChunk(archetype.ChunkSizeInBytes, _group) + 1; + var entities = Archetype.GetEntityCountFor(archetype.ChunkSizeInBytes, _group) + 50; for (var index = 0; index < entities; index++) { @@ -130,10 +168,12 @@ public void RemoveChunk() var slot = new Slot(0, 0); archetype.Remove(slot, out _); - That(archetype.ChunkCount, Is.EqualTo(1)); + That(archetype.Count, Is.EqualTo(1)); + That(archetype.ChunkCount, Is.EqualTo(2)); That(archetype.ChunkCapacity, Is.EqualTo(2)); - That(archetype.Chunks[0].Size, Is.EqualTo(entities - 1)); - That(archetype.Chunks[0].Entities[0].Id, Is.EqualTo( Archetype.CalculateEntitiesPerChunk(archetype.ChunkSizeInBytes, _group))); // Last entity from second chunk now replaced the removed entity and is in the first chunk + That(archetype.Chunks[0].Count, Is.EqualTo(entities - 50)); + That(archetype.Chunks[1].Count, Is.EqualTo(49)); + That(archetype.Chunks[0].Entities[0].Id, Is.EqualTo( Archetype.GetEntityCountFor(archetype.ChunkSizeInBytes, _group) + 50 - 1)); // Last entity from second chunk now replaced the removed entity and is in the first chunk } /// @@ -159,8 +199,8 @@ public void Move() Archetype.CopyComponents(archetype, ref entityOneSlot,otherArchetype, ref newSlot); archetype.Remove(entityOneSlot, out _); - That(archetype.Chunks[0].Size, Is.EqualTo(0)); - That(otherArchetype.Chunks[0].Size, Is.EqualTo(2)); + That(archetype.Chunks[0].Count, Is.EqualTo(0)); + That(otherArchetype.Chunks[0].Count, Is.EqualTo(2)); That(otherArchetype.Get(ref newSlot).X, Is.EqualTo(10)); That(otherArchetype.Get(ref newSlot).Y, Is.EqualTo(10)); That(otherArchetype.Get(ref newSlot).X, Is.EqualTo(10)); @@ -259,6 +299,19 @@ public void CopyTo([Values(1111,2222,3333)] int sourceAmount, [Values(1111,2222, That(destination.EntityCount, Is.EqualTo(sourceAmount+destinationAmount)); That(countedSourceItems, Is.EqualTo(sourceAmount)); That(countedDestinationItems, Is.EqualTo(destinationAmount)); + + var requiredChunksForSource = Archetype.GetChunkCapacityFor(source.EntitiesPerChunk, sourceAmount); + var requiredChunksForDestination = Archetype.GetChunkCapacityFor(destination.EntitiesPerChunk, sourceAmount+destinationAmount); + + That(source.EntityCount, Is.EqualTo(0)); + That(source.ChunkCount, Is.EqualTo(requiredChunksForSource)); + That(source.ChunkCapacity, Is.EqualTo(requiredChunksForSource)); + That(source.Count, Is.EqualTo(0)); + + That(destination.EntityCount, Is.EqualTo(sourceAmount+destinationAmount)); + That(destination.ChunkCount, Is.EqualTo(requiredChunksForDestination)); + That(destination.ChunkCapacity, Is.EqualTo(requiredChunksForDestination)); + That(destination.Count, Is.EqualTo(requiredChunksForDestination - 1)); // -1 Since its the index that points towards the Chunk, not the count } /// @@ -301,7 +354,54 @@ public void CopyToShift([Values(1111,2222,3333)] int sourceAmount, [Values(1111, Archetype.Copy(source, destination); source.Clear(); - That(destination.EntityCount, Is.EqualTo(sourceAmount+destinationAmount)); + var requiredChunksForSource = Archetype.GetChunkCapacityFor(source.EntitiesPerChunk, sourceAmount); + var requiredChunksForDestination = Archetype.GetChunkCapacityFor(destination.EntitiesPerChunk, sourceAmount + destinationAmount); + That(source.Entity(ref sourceSlot), Is.EqualTo(destination.Entity(ref resultSlot))); // Make sure entities were copied correctly. + That(source.EntityCount, Is.EqualTo(0)); + That(source.ChunkCount, Is.EqualTo(requiredChunksForSource)); + That(source.ChunkCapacity, Is.EqualTo(requiredChunksForSource)); + That(source.Count, Is.EqualTo(0)); + + That(destination.EntityCount, Is.EqualTo(sourceAmount+destinationAmount)); + That(destination.ChunkCount, Is.EqualTo(requiredChunksForDestination)); + That(destination.ChunkCapacity, Is.EqualTo(requiredChunksForDestination)); + That(destination.Count, Is.EqualTo(requiredChunksForDestination - 1)); + } + + /// + /// Checks whether the next available slots of the archetype can be calculated correctly. + /// + [Test] + public void GetNextSlots() + { + var archetype = new Archetype(_group); + var entitiesPerChunk = Archetype.GetEntityCountFor(archetype.ChunkSizeInBytes, _group); + var entities = entitiesPerChunk/2; + + for (var index = 0; index < entities; index++) + { + var entity = new Entity(index, 0); + archetype.Add(entity, out _, out _); + } + + Span slots = stackalloc Slot[archetype.EntitiesPerChunk]; + var created = Archetype.GetNextSlots(archetype, slots, archetype.EntitiesPerChunk); + + // Create next n entities in the chunk to see if they are created correctly + for (var index = 0; index < created; index++) + { + var entity = new Entity(entities+index, 0); + archetype.Add(entity, out _, out var createdIn); + That(slots[index], Is.EqualTo(createdIn)); + } + + var requiredChunksForSource = Archetype.GetChunkCapacityFor(archetype.EntitiesPerChunk, entities+created); + + That(archetype.EntityCount, Is.EqualTo(entities+created)); + That(archetype.EntityCapacity, Is.EqualTo(entities+created)); + That(archetype.ChunkCount, Is.EqualTo(requiredChunksForSource)); + That(archetype.ChunkCapacity, Is.EqualTo(requiredChunksForSource)); + That(archetype.Count, Is.EqualTo(requiredChunksForSource - 1)); } } diff --git a/src/Arch.Tests/ChunkTest.cs b/src/Arch.Tests/ChunkTest.cs index 8cb232fb..ec3b5c51 100644 --- a/src/Arch.Tests/ChunkTest.cs +++ b/src/Arch.Tests/ChunkTest.cs @@ -34,7 +34,7 @@ public void ChunkSet() } // Make sure the amount fits - That(_chunk.Size, Is.EqualTo(_chunk.Capacity)); + That(_chunk.Count, Is.EqualTo(_chunk.Capacity)); } /// @@ -57,7 +57,7 @@ public void ChunkRemove() } // Get last one, remove first one - var last = _chunk.Entities[_chunk.Size - 1]; + var last = _chunk.Entities[_chunk.Count - 1]; _chunk.Remove(0); // Check if the first one was replaced with the last one correctly @@ -84,13 +84,13 @@ public void ChunkRemoveAll() } // Backward delete all since forward does not work while keeping the array dense - for (var index = _chunk.Size - 1; index >= 0; index--) + for (var index = _chunk.Count - 1; index >= 0; index--) { _chunk.Remove(index); } // Check if the first one was replaced with the last one correctly - That(_chunk.Size, Is.EqualTo(0)); + That(_chunk.Count, Is.EqualTo(0)); That(_chunk.Entities[0].Id, Is.EqualTo(0)); // Needs to be 1, because it will be the last one getting removed and being moved to that position } @@ -112,7 +112,7 @@ public void ChunkRemoveAndSetAgain() _chunk.Add(newEntity); // Check if the first one was replaced with the last one correctly - That(_chunk.Size, Is.EqualTo(2)); + That(_chunk.Count, Is.EqualTo(2)); That(_chunk.Entities[0].Id, Is.EqualTo(2)); // Needs to be 1, because it will be the last one getting removed and being moved to that position That(_chunk.Entities[1].Id, Is.EqualTo(1)); // Needs to be 1, because it will be the last one getting removed and being moved to that position } diff --git a/src/Arch.Tests/EnumeratorTest.cs b/src/Arch.Tests/EnumeratorTest.cs index 586ffacd..0b0e9d8c 100644 --- a/src/Arch.Tests/EnumeratorTest.cs +++ b/src/Arch.Tests/EnumeratorTest.cs @@ -25,8 +25,8 @@ public sealed class EnumeratorTest public void Setup() { _world = World.Create(); - _world.Reserve(_group, 10000); - _world.Reserve(_otherGroup, 10000); + _world.EnsureCapacity(_group, 10000); + _world.EnsureCapacity(_otherGroup, 10000); for (var index = 0; index < 10000; index++) { @@ -71,7 +71,7 @@ public void ArchetypeChunkEnumeration() counter++; } - That(counter, Is.EqualTo((int)Math.Ceiling((float)10000 / Archetype.CalculateEntitiesPerChunk(archetype.ChunkSizeInBytes, _group)))); + That(counter, Is.EqualTo((int)Math.Ceiling((float)10000 / Archetype.GetEntityCountFor(archetype.ChunkSizeInBytes, _group)))); } /// diff --git a/src/Arch.Tests/WorldTest.cs b/src/Arch.Tests/WorldTest.cs index d2197953..5b9475c9 100644 --- a/src/Arch.Tests/WorldTest.cs +++ b/src/Arch.Tests/WorldTest.cs @@ -61,11 +61,16 @@ public void WorldRecycle() [Test] public void Create() { - var size = _world.Size; - var entity = _world.Create(_entityGroup); + using var world = World.Create(); - That(_world.Size, Is.EqualTo(size + 1)); - True(_world.IsAlive(entity)); + var size = world.Size; + var entity = world.Create(_entityGroup); + + That(entity.Id, Is.EqualTo(0)); + That(world.Size, Is.EqualTo(size + 1)); + That(world.Capacity, Is.EqualTo(world.Archetypes[0].EntityCapacity)); + That(world.Version(entity), Is.EqualTo(1)); + True(world.IsAlive(entity)); } /// @@ -102,8 +107,8 @@ public void DestroyAll() } That(_world.Size, Is.EqualTo(0)); - That(_world.Archetypes[0].ChunkCount, Is.EqualTo(1)); - That(_world.Archetypes[1].ChunkCount, Is.EqualTo(1)); + That(_world.Archetypes[0].Count, Is.EqualTo(0)); + That(_world.Archetypes[1].Count, Is.EqualTo(0)); } /// @@ -112,11 +117,14 @@ public void DestroyAll() [Test] public void DestroyEdgeCase() { + + using var world = World.Create(); + var entitiesToChangeColor = new QueryDescription().WithAll(); var entities = new List(); - for (var i = 0; i < 1000; i++) + for (var i = 0; i < 10_000; i++) { - var ent = _world.Create(_entityGroup); + var ent = world.Create(_entityGroup); entities.Add(ent); } @@ -129,10 +137,10 @@ public void DestroyEdgeCase() } // A demonstration of bulk adding and removing components. - _world.Add(in entitiesToChangeColor, 1); - _world.Remove(in entitiesToChangeColor); + world.Add(in entitiesToChangeColor, 1); + world.Remove(in entitiesToChangeColor); - _world.Destroy(ent); + world.Destroy(ent); } } @@ -229,20 +237,15 @@ public void CapacityTest() /// Checks if the reserves memory for s correctly. /// [Test] - public void Reserve() + public void EnsureCapacity() { - var beforeSize = _world.Size; - var beforeCapacity = _world.Capacity; - - _world.Reserve(_entityGroup, 10000); - for (var index = 0; index < 10000; index++) - { - _world.Create(_entityGroup); - } + using var world = World.Create(); + var archetype = world.EnsureCapacity(_entityGroup, 10000); - Greater(_world.Size, beforeSize); - That(_world.Size, Is.EqualTo(beforeSize + 10000)); - That(_world.Capacity, Is.EqualTo(beforeCapacity + 10000)); + That(world.Size, Is.EqualTo(0)); + That(world.Capacity, Is.EqualTo(archetype.EntityCapacity)); + That(archetype.EntityCount, Is.EqualTo(0)); + That(archetype.EntityCapacity, Is.EqualTo(10240)); } /// @@ -262,7 +265,7 @@ public void TrimExcess() // Destroy all but one var counter = 0; var query = new QueryDescription().WithAll(); - world.Query(in query, (Entity entity) => + world.Query(in query, entity => { if (counter < amount - 1) { @@ -586,7 +589,7 @@ public void Remove() That(_world.GetArchetype(entity2), Is.EqualTo(_world.GetArchetype(entity))); That(_world.GetArchetype(entity).ChunkCount, Is.EqualTo(1)); - That(_world.GetArchetype(entity).Chunks[0].Size, Is.EqualTo(2)); + That(_world.GetArchetype(entity).Chunks[0].Count, Is.EqualTo(2)); } /// @@ -640,7 +643,7 @@ public void Remove_NonGeneric() That(_world.GetArchetype(entity2), Is.EqualTo(_world.GetArchetype(entity))); That(_world.GetArchetype(entity).ChunkCount, Is.EqualTo(1)); - That(_world.GetArchetype(entity).Chunks[0].Size, Is.EqualTo(2)); + That(_world.GetArchetype(entity).Chunks[0].Count, Is.EqualTo(2)); } /// @@ -710,7 +713,7 @@ public void GeneratedRemove() That(_world.GetArchetype(entity2), Is.EqualTo(_world.GetArchetype(entity))); That(_world.GetArchetype(entity).ChunkCount, Is.EqualTo(1)); - That(_world.GetArchetype(entity).Chunks[0].Size, Is.EqualTo(2)); + That(_world.GetArchetype(entity).Chunks[0].Count, Is.EqualTo(2)); } /// diff --git a/src/Arch/Arch.csproj b/src/Arch/Arch.csproj index 26865096..40235175 100644 --- a/src/Arch/Arch.csproj +++ b/src/Arch/Arch.csproj @@ -135,6 +135,10 @@ Merged arrays in EntityInfo for increased performance when creating and destroyi TextTemplatingFileGenerator World.EnsureCapacity.cs + + TextTemplatingFileGenerator + World.CreateBulk.cs + @@ -142,6 +146,11 @@ Merged arrays in EntityInfo for increased performance when creating and destroyi TextTemplatingFileGenerator World.CreateBulk.cs + + True + True + World.EnsureCapacity.tt + True True diff --git a/src/Arch/Core/Archetype.cs b/src/Arch/Core/Archetype.cs index a0c19a4b..fa3c1887 100644 --- a/src/Arch/Core/Archetype.cs +++ b/src/Arch/Core/Archetype.cs @@ -283,19 +283,16 @@ internal Archetype(Signature signature) Types = signature; // Calculations - ChunkSizeInBytes = MinimumRequiredChunkSize(MinimumAmountOfEntitiesPerChunk, signature); - EntitiesPerChunk = CalculateEntitiesPerChunk(ChunkSizeInBytes, signature); + ChunkSizeInBytes = GetByteCountFor(MinimumAmountOfEntitiesPerChunk, signature); + EntitiesPerChunk = GetEntityCountFor(ChunkSizeInBytes, signature); // The bitmask/set BitSet = signature; _componentIdToArrayIndex = signature.Components.ToLookupArray(); // Setup arrays and mappings - Chunks = ArrayPool.Shared.Rent(1); - Chunks[0] = new Chunk(EntitiesPerChunk, _componentIdToArrayIndex, signature); - - ChunkCount = 1; - ChunkCapacity = 1; + Chunks = new Chunks(1); + AddChunk(); _addEdges = new SparseJaggedArray(BucketSize); _removeEdges = new SparseJaggedArray(BucketSize); @@ -337,26 +334,41 @@ internal int[] LookupArray public int MinimumAmountOfEntitiesPerChunk { get; } = 100; /// - /// How many ' have been deposited within the array. - /// The total capacity. + /// An array which stores the 's. + /// May contain null references since its being pooled, therefore use the and for acessing it. /// - public int ChunkCapacity { get; internal set; } + public Chunks Chunks { get; internal set; } /// - /// The number of occupied/used 's within the array. + /// The number of 's within the array. /// - public int ChunkCount { get; internal set; } + public int ChunkCount { + get + { + return Chunks.Count; + } + } /// - /// An array which stores the 's. - /// May contain null references since its being pooled, therefore use the and for acessing it. + /// How many ' have been deposited within the array. + /// The total capacity. /// - public Array Chunks { get; internal set; } + public int ChunkCapacity { + get + { + return Chunks.Capacity; + } + } + + /// + /// The number of filled chunks within the array. + /// + public int Count { get; internal set; } /// /// Points to the last that is not yet full. /// - internal ref Chunk LastChunk { get => ref Chunks[ChunkCount - 1]; } + internal ref Chunk LastChunk { get => ref Chunks[Count]; } /// /// Points to the last . @@ -365,9 +377,9 @@ internal Slot LastSlot { get { - var lastRow = LastChunk.Size - 1; + var lastRow = LastChunk.Count - 1; //lastRow = lastRow > 0 ? lastRow : 0; // Make sure no negative slot is returned when chunk is empty. - return new(lastRow, ChunkCount - 1); + return new(lastRow, Count); } } @@ -388,6 +400,30 @@ public int EntityCapacity get => ChunkCapacity * EntitiesPerChunk; } + /// + /// Creates a new at the last . + /// + /// The new created . + public ref Chunk AddChunk() + { + Chunks.EnsureCapacity(Chunks.Count+1); + + // Insert chunk + var count = Chunks.Count; + Chunks.Add(new Chunk(EntitiesPerChunk, _componentIdToArrayIndex, Types)); + return ref Chunks[count]; + } + + /// + /// Returns a reference to a given using its index. + /// + /// + /// A reference to the at the given index. + public ref Chunk GetChunk(int index) + { + return ref Chunks[index]; + } + /// /// Adds an to the and offloads it to a . /// Uses the last that is not full, once it is full and the capacity is exhausted, a new is allocated. @@ -399,37 +435,37 @@ public int EntityCapacity internal bool Add(Entity entity, out Chunk chunk, out Slot slot) // TODO: Store chunk reference in slot? { // Storing stack variables to prevent multiple times accessing those fields. - ref var lastChunk = ref LastChunk; - var chunkCount = ChunkCount; + EntityCount++; + var count = Count; + ref var currentChunk = ref GetChunk(count); // Fill chunk - if (lastChunk.Size != lastChunk.Capacity) + if (currentChunk.Count < currentChunk.Capacity) { - slot = new Slot(lastChunk.Add(entity), chunkCount - 1); - chunk = lastChunk; - EntityCount++; + slot = new Slot(currentChunk.Add(entity), count); + chunk = currentChunk; return false; } // Chunk full? Use next allocated chunk - if (chunkCount < ChunkCapacity) + count++; + if (count < ChunkCapacity) { - ChunkCount++; - lastChunk = ref LastChunk; + currentChunk = ref GetChunk(count); - slot = new Slot(lastChunk.Add(entity), chunkCount); - chunk = lastChunk; - EntityCount++; + slot = new Slot(currentChunk.Add(entity), count); + chunk = currentChunk; + Count = count; return false; } // No more free allocated chunks? Create new chunk ref var newChunk = ref AddChunk(); - slot = new Slot(newChunk.Add(entity), chunkCount); + slot = new Slot(newChunk.Add(entity), count); chunk = newChunk; - EntityCount++; + Count = count; return true; } @@ -441,23 +477,22 @@ internal bool Add(Entity entity, out Chunk chunk, out Slot slot) // TODO: Store /// The amount. public void AddAll(Span entities, int amount) { - var filledChunks = 0; + EnsureEntityCapacity(EntityCount + amount); + var created = 0; - for (var chunkIndex = ChunkCount - 1; chunkIndex < ChunkCapacity && amount > 0; chunkIndex++) + for(var index = ChunkCount; index < ChunkCapacity && created < amount; index++) { - ref var chunk = ref GetChunk(chunkIndex); - var fillAmount = Math.Min(chunk.Buffer, amount); + ref var chunk = ref GetChunk(index); + var fillAmount = Math.Min(chunk.Buffer, amount - created); - Chunk.Copy(ref entities, created, ref chunk, chunk.Size, fillAmount); - chunk.Size += fillAmount; + Chunk.Copy(ref entities, created, ref chunk, chunk.Count, fillAmount); + chunk.Count += fillAmount; - filledChunks = chunk.IsFull ? filledChunks + 1 : filledChunks; - amount -= fillAmount; + Count = chunk.IsFull ? ChunkCount + 1 : ChunkCount; created += fillAmount; } - EntityCount += created; - ChunkCount += filledChunks; + EntityCount += amount; } /// @@ -476,12 +511,12 @@ internal void Remove(Slot slot, out int movedEntityId) EntityCount--; // Return to prevent that Size decreases when chunk IS not Empty and to prevent Size becoming 0 or -1. - if (lastChunk.Size != 0 || ChunkCount <= 1) + if (lastChunk.Count > 0 || Count <= 0) { return; } - ChunkCount--; + Count--; } /// @@ -545,32 +580,6 @@ internal ref Entity Entity(scoped ref Slot slot) return ref chunk.Entity(slot.Index); } - /// - /// Creates a new at the last . - /// - /// The new created . - public ref Chunk AddChunk() - { - // Resize chunks - var chunkCount = ChunkCount; - EnsureChunkCapacity(++ChunkCount); - - // Insert chunk - ref var chunk = ref GetChunk(chunkCount); - chunk = new Chunk(EntitiesPerChunk, _componentIdToArrayIndex, Types); - return ref chunk; - } - - /// - /// Returns a reference to a given using its index. - /// - /// - /// A reference to the at the given index. - public ref Chunk GetChunk(int index) - { - return ref Chunks[index]; - } - /// /// Sets a component value for all entities within an in a certain range of s /// @@ -588,7 +597,7 @@ internal void SetRange(in Slot from, in Slot to, in T? component = default) var isStart = chunkIndex == from.ChunkIndex; var isEnd = chunkIndex == to.ChunkIndex; - var upper = isStart ? from.Index+1 : chunk.Size; + var upper = isStart ? from.Index+1 : chunk.Count; var lower = isEnd ? to.Index : 0; Chunk.Fill(ref chunk, lower, upper-lower, component); @@ -632,10 +641,11 @@ internal ChunkRangeIterator GetRangeIterator(int to) public void Clear() { + Count = 0; EntityCount = 0; - ChunkCount = 1; - foreach (ref var chunk in this) + for (var index = 0; index < Chunks.Count; index++) { + ref var chunk = ref Chunks[index]; chunk.Clear(); } } @@ -704,17 +714,7 @@ public sealed partial class Archetype /// The amount of 's required, in total. private void EnsureChunkCapacity(int newCapacity) { - if (ChunkCapacity >= newCapacity) - { - return; - } - - // Increase chunk array size - var newChunks = ArrayPool.Shared.Rent(newCapacity); - Array.Copy(Chunks, newChunks, ChunkCapacity); - ArrayPool.Shared.Return(Chunks, true); - Chunks = newChunks; - ChunkCapacity = newCapacity; + Chunks.EnsureCapacity(newCapacity); } /// @@ -733,12 +733,11 @@ internal void EnsureEntityCapacity(int newCapacity) // Set capacity and insert new empty chunks. var previousCapacity = ChunkCapacity; - EnsureChunkCapacity(previousCapacity + neededChunks); + EnsureChunkCapacity(neededChunks); - for (var index = 0; index < neededChunks; index++) + for (var index = previousCapacity; index < neededChunks; index++) { - var newChunk = new Chunk(EntitiesPerChunk, _componentIdToArrayIndex, Types); - Chunks[previousCapacity + index] = newChunk; + Chunks.Add(new Chunk(EntitiesPerChunk, _componentIdToArrayIndex, Types)); } } @@ -748,43 +747,7 @@ internal void EnsureEntityCapacity(int newCapacity) /// internal void TrimExcess() { - // This always spares one single chunk. - var minimalSize = ChunkCount > 0 ? ChunkCount : 1; - - // Decrease chunk size - var newChunks = ArrayPool.Shared.Rent(minimalSize); - Array.Copy(Chunks, newChunks, minimalSize); - ArrayPool.Shared.Return(Chunks, true); - Chunks = newChunks; - ChunkCapacity = minimalSize; - } - - /// - /// Reserves space for a certain number of 's in addition to the already existing amount. - /// - /// The amount of new 's. - internal void Reserve(int amount) - { - // Calculate amount of required chunks. - ref var lastChunk = ref LastChunk; - var freeSpots = lastChunk.Capacity - lastChunk.Size; - var neededSpots = amount - freeSpots; - var neededChunks = (int)Math.Ceiling((float)neededSpots / EntitiesPerChunk); - - // Set capacity and insert new empty chunks. - var previousCapacity = ChunkCapacity; - EnsureChunkCapacity(previousCapacity + neededChunks); - for (var index = 0; index < neededChunks; index++) - { - var newChunk = new Chunk(EntitiesPerChunk, _componentIdToArrayIndex, Types); - Chunks[previousCapacity + index] = newChunk; - } - - // If last chunk was full, add. - if (freeSpots == 0) - { - ChunkCount++; - } + Chunks.TrimExcess(); } } @@ -792,41 +755,53 @@ public sealed partial class Archetype { /// - /// Calculates how many 's are needed to fulfill the . + /// Calculates how many bytes are needed to store the . /// - /// The minimum amount of entities per . + /// The amount of entities. /// The component structure of the 's. - /// The amount of 's required. - public unsafe static int MinimumRequiredChunkSize(int minimumAmountOfEntitiesPerChunk, Span types) + /// The amount of bytes required to store the s. + public unsafe static int GetByteCountFor(int entityAmount, Span types) { - var minimumEntities = (sizeof(Entity) + types.ToByteSize()) * minimumAmountOfEntitiesPerChunk; - return (int)Math.Ceiling((float)minimumEntities / BaseSize) * BaseSize; + var entityBytes = (sizeof(Entity) + types.ToByteSize()) * entityAmount; + return (int)Math.Ceiling((float)entityBytes / BaseSize) * BaseSize; } /// - /// Calculates how many 's fit into one . + /// Calculates how many 's fit into the desired . /// - /// The size in bytes. + /// The available bytes. /// The component structure of the 's. /// The amount of 's. - public unsafe static int CalculateEntitiesPerChunk(int chunkSizeInBytes, Span types) + public unsafe static int GetEntityCountFor(int byteAmount, Span types) { - return chunkSizeInBytes / (sizeof(Entity) + types.ToByteSize()); + return byteAmount / (sizeof(Entity) + types.ToByteSize()); } /// - /// Calculates the next s and writes them into a . + /// Calculates how many are required to store the desired . + /// + /// The amount of entities inside a . + /// The amount. + /// The amount of s required to store the entities. + public static int GetChunkCapacityFor(int entitiesPerChunk, int entityAmount) + { + return (int)Math.Ceiling((float)entityAmount / entitiesPerChunk); + } + + /// + /// Calculates the next s within the and its and writes them into a . /// /// The . /// The to fill the s into. - /// The amount of new 's. - internal static void GetNextSlots(Archetype archetype, Span slots, int amount) + /// The amount of 's we want to calculate. + /// The amount of s that fit into the + internal static int GetNextSlots(Archetype archetype, Span slots, int amount) { var next = 0; for (var chunkIndex = archetype.ChunkCount-1; chunkIndex < archetype.ChunkCapacity && amount > 0; chunkIndex++) { ref var chunk = ref archetype.GetChunk(chunkIndex); - var chunkSize = chunk.Size; + var chunkSize = chunk.Count; var fillLimit = Math.Min(chunk.Capacity - chunkSize, amount); for (var index = chunkSize; index < chunkSize+fillLimit; index++) @@ -836,6 +811,8 @@ internal static void GetNextSlots(Archetype archetype, Span slots, int amo amount -= fillLimit; } + + return next; } /// @@ -852,28 +829,28 @@ internal static void Copy(Archetype source, Archetype destination) // Copy chunks into destination chunks var sourceChunkIndex = 0; - var destinationChunkIndex = destination.ChunkCount - 1; + var destinationChunkIndex = destination.Count; while (sourceChunkIndex < source.ChunkCount) { - ref var sourceChunk = ref source.Chunks[sourceChunkIndex]; + ref var sourceChunk = ref source.GetChunk(sourceChunkIndex); var index = 0; - while (sourceChunk.Size > 0 && destinationChunkIndex < destination.ChunkCapacity) // Making sure that we dont go out of bounds + while (sourceChunk.Count > 0 && destinationChunkIndex < destination.ChunkCapacity) // Making sure that we dont go out of bounds { - ref var destinationChunk = ref destination.Chunks[destinationChunkIndex]; + ref var destinationChunk = ref destination.GetChunk(destinationChunkIndex); // Check how many entities fit into the destination chunk and choose the minimum as a copy length to prevent out of range exceptions. - var destinationRemainingCapacity = destinationChunk.Capacity - destinationChunk.Size; - var length = Math.Min(sourceChunk.Size, destinationRemainingCapacity); + var destinationRemainingCapacity = destinationChunk.Buffer; + var length = Math.Min(sourceChunk.Count, destinationRemainingCapacity); // Copy source array into destination chunk. - Chunk.Copy(ref sourceChunk, index, ref destinationChunk, destinationChunk.Size, length); + Chunk.Copy(ref sourceChunk, index, ref destinationChunk, destinationChunk.Count, length); - sourceChunk.Size -= length; - destinationChunk.Size += length; + sourceChunk.Count -= length; + destinationChunk.Count += length; index += length; // Current source chunk still has remaining capacity, destination is full, resume with next destination chunk. - if (destinationChunk.Size == destinationChunk.Capacity) + if (destinationChunk.Count == destinationChunk.Capacity) { destinationChunkIndex++; } @@ -883,10 +860,10 @@ internal static void Copy(Archetype source, Archetype destination) } // Set new chunk count and if the lastchunk was set to 0 by the copy algorithm, reduce it by one to point to a valid chunk - destination.ChunkCount = destinationChunkIndex+1; - if (destination.LastChunk.Size == 0) + destination.Count = destinationChunkIndex; + if (destination.LastChunk.Count == 0) { - destination.ChunkCount--; + destination.Count--; } // Increase entities by destination since those were copied, set source to zero since its now empty. diff --git a/src/Arch/Core/Chunk.cs b/src/Arch/Core/Chunk.cs index 22c3777e..78c56c31 100644 --- a/src/Arch/Core/Chunk.cs +++ b/src/Arch/Core/Chunk.cs @@ -1,13 +1,97 @@ +using System.Buffers; using System.Diagnostics.Contracts; using System.Drawing; using Arch.Core.Events; using Arch.Core.Extensions; using Arch.Core.Extensions.Internal; using Arch.Core.Utils; +using Arch.LowLevel; +using Collections.Pooled; using CommunityToolkit.HighPerformance; +using Array = System.Array; namespace Arch.Core; + +public class Chunks +{ + public Chunks(int capacity = 1) + { + Items = ArrayPool.Shared.Rent(capacity); + Count = 0; + Capacity = capacity; + } + + public Array Items { get; set; } + public int Count { get; set; } + public int Capacity { get; set; } + + public void Add(in Chunk chunk) + { + Debug.Assert(Count+1 <= Capacity, "Capacity exceeded."); + Items[Count++] = chunk; + } + + public void EnsureCapacity(int newCapacity) + { + if (newCapacity < Capacity) + { + return; + } + + var sourceArray = Items; + var destinationArray = (Array)ArrayPool.Shared.Rent(newCapacity); + Arch.LowLevel.Array.Copy(ref sourceArray, 0, ref destinationArray, 0, Capacity ); + ArrayPool.Shared.Return(sourceArray, true); + + Items = destinationArray; + Capacity = newCapacity; + } + + public void TrimExcess() + { + // This always spares one single chunk. + var minimalSize = Count > 0 ? Count : 1; + + // Decrease chunk size + var newChunks = ArrayPool.Shared.Rent(minimalSize); + Array.Copy(Items, newChunks, minimalSize); + ArrayPool.Shared.Return(Items, true); + + Items = newChunks; + Capacity = minimalSize; + } + + /// + /// Gets or sets the item at the given index. + /// + /// The index. + public ref Chunk this[int index] + { + get => ref Items[index]; + } + + public Span AsSpan() + { + return Items.AsSpan(); + } + + public void Clear() + { + Count = 0; + foreach (ref var chunk in Items) + { + chunk.Clear(); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Chunk[](Chunks instance) + { + return instance.Items; + } +} + /// /// The struct represents a contiguous block of memory in which various components are stored in Structure of Arrays. /// Chunks are internally allocated and filled by 's. @@ -23,8 +107,7 @@ public partial struct Chunk /// /// How many entities of the respective component structure fit into this . /// The respective component structure of all entities in this . - internal Chunk(int capacity, Span types) - : this(capacity, types.ToLookupArray(), types) { } + internal Chunk(int capacity, Span types) : this(capacity, types.ToLookupArray(), types) { } /// /// Initializes a new instance of the struct @@ -35,7 +118,7 @@ internal Chunk(int capacity, Span types) internal Chunk(int capacity, int[] componentIdToArrayIndex, Span types) { // Calculate capacity and init arrays. - Size = 0; + Count = 0; Capacity = capacity; Entities = new Entity[Capacity]; @@ -72,7 +155,7 @@ internal Chunk(int capacity, int[] componentIdToArrayIndex, Span /// /// The number of occupied slots in this . /// - public int Size { [Pure] get; internal set; } // 4 Byte + public int Count { [Pure] get; internal set; } // 4 Byte /// /// The number of possible 's in this . @@ -82,12 +165,12 @@ internal Chunk(int capacity, int[] componentIdToArrayIndex, Span /// /// The space that is left in this instance. /// - public readonly int Buffer { [Pure] get => Capacity - Size; } + public readonly int Buffer { [Pure] get => Capacity - Count; } /// /// Checks whether this instance is full or not. /// - public readonly bool IsFull { [Pure] get => Size >= Capacity; } + public readonly bool IsFull { [Pure] get => Count >= Capacity; } /// /// Inserts an entity into the . @@ -98,9 +181,9 @@ internal Chunk(int capacity, int[] componentIdToArrayIndex, Span internal int Add(Entity entity) { // Stack variable faster than accessing 3 times the Size field. - var size = Size; + var size = Count; Entity(size) = entity; - Size = size + 1; + Count = size + 1; return size; } @@ -190,7 +273,7 @@ public ref Entity Entity(int index) internal void Remove(int index) { // Last entity in archetype. - var lastIndex = Size - 1; + var lastIndex = Count - 1; // Copy last entity to replace the removed one. ref var entities = ref Entities.DangerousGetReference(); @@ -205,7 +288,7 @@ internal void Remove(int index) } // Update the mapping. - Size = lastIndex; + Count = lastIndex; } /// @@ -214,7 +297,7 @@ internal void Remove(int index) /// A new instance. public EntityEnumerator GetEnumerator() { - return new EntityEnumerator(Size); + return new EntityEnumerator(Count); } /// @@ -223,7 +306,7 @@ public EntityEnumerator GetEnumerator() /// public void Clear() { - Size = 0; + Count = 0; } /// @@ -232,7 +315,7 @@ public void Clear() /// A string. public override string ToString() { - return $"Chunk = {{ {nameof(Capacity)} = {Capacity}, {nameof(Size)} = {Size} }}"; + return $"Chunk = {{ {nameof(Capacity)} = {Capacity}, {nameof(Count)} = {Count} }}"; } } @@ -488,7 +571,7 @@ internal static void CopyComponents(ref Chunk source, int index, ref Chunk desti internal int Transfer(int index, ref Chunk chunk) { // Get last entity - var lastIndex = chunk.Size - 1; + var lastIndex = chunk.Count - 1; var lastEntity = chunk.Entity(lastIndex); // Replace index entity with the last entity from the other chunk @@ -500,7 +583,7 @@ internal int Transfer(int index, ref Chunk chunk) Array.Copy(sourceArray, lastIndex, desArray, index, 1); } - chunk.Size--; + chunk.Count--; return lastEntity.Id; } } diff --git a/src/Arch/Core/EntityInfo.cs b/src/Arch/Core/EntityInfo.cs index 12357000..781740df 100644 --- a/src/Arch/Core/EntityInfo.cs +++ b/src/Arch/Core/EntityInfo.cs @@ -198,7 +198,7 @@ public void Shift(Archetype archetype, Slot archetypeSlot, Archetype newArchetyp // Only move within the range, depening on which chunk we are at. var isStart = chunkIndex == archetypeSlot.ChunkIndex; - var upper = isStart ? archetypeSlot.Index : chunk.Size-1; + var upper = isStart ? archetypeSlot.Index : chunk.Count-1; for(var index = 0; index <= upper; index++) { diff --git a/src/Arch/Core/Enumerators.cs b/src/Arch/Core/Enumerators.cs index cd3d3c24..51a096c6 100644 --- a/src/Arch/Core/Enumerators.cs +++ b/src/Arch/Core/Enumerators.cs @@ -202,7 +202,7 @@ public bool MoveNext() unchecked { // Decrease chunk till its zero, skip empty chunks -> otherwhise entity query might fail since it tries to acess that chunk - if (--_index >= 0 && Current.Size > 0) + if (--_index >= 0 && Current.Count > 0) { return true; } diff --git a/src/Arch/Core/Extensions/Dangerous/DangerousArchetypeExtensions.cs b/src/Arch/Core/Extensions/Dangerous/DangerousArchetypeExtensions.cs index 65358798..fcdc94ec 100644 --- a/src/Arch/Core/Extensions/Dangerous/DangerousArchetypeExtensions.cs +++ b/src/Arch/Core/Extensions/Dangerous/DangerousArchetypeExtensions.cs @@ -27,7 +27,7 @@ public static Archetype CreateArchetype(ComponentType[] types) /// The size. public static void SetSize(this Archetype archetype, int size) { - archetype.ChunkCount = size; + //archetype.ChunkCount = size; } /// @@ -37,10 +37,11 @@ public static void SetSize(this Archetype archetype, int size) /// The list of s. public static void SetChunks(this Archetype archetype, List chunks) { + /* archetype.Chunks = ArrayPool.Shared.Rent(chunks.Count); chunks.CopyTo(archetype.Chunks); - archetype.ChunkCapacity = chunks.Count; + archetype.ChunkCapacity = chunks.Count;*/ } /// diff --git a/src/Arch/Core/Extensions/Dangerous/DangerousChunkExtensions.cs b/src/Arch/Core/Extensions/Dangerous/DangerousChunkExtensions.cs index f53539e1..444abbdf 100644 --- a/src/Arch/Core/Extensions/Dangerous/DangerousChunkExtensions.cs +++ b/src/Arch/Core/Extensions/Dangerous/DangerousChunkExtensions.cs @@ -27,6 +27,6 @@ public static Chunk CreateChunk(int capacity, int[] lookupArray, ComponentType[] /// Its new size. public static void SetSize(this ref Chunk chunk, int size) { - chunk.Size = size; + chunk.Count = size; } } diff --git a/src/Arch/Core/World.cs b/src/Arch/Core/World.cs index 6c1c873b..d4310b2c 100644 --- a/src/Arch/Core/World.cs +++ b/src/Arch/Core/World.cs @@ -231,25 +231,6 @@ private World(int id) /// internal PooledDictionary QueryCache { get; set; } - /// - /// Reserves space for a certain number of s of a given component structure/. - /// - /// - /// Causes a structural change. - /// - /// The component structure/. - /// The amount of s to reserve space for. - [StructuralChange] - public void Reserve(in Signature signature, int amount) - { - var archetype = GetOrCreate(signature); - archetype.Reserve(amount); - - var requiredCapacity = Capacity + amount; - EntityInfo.EnsureCapacity(requiredCapacity); - Capacity = requiredCapacity; - } - /// /// Creates a new using its given component structure/. /// Might resize its target and allocate new space if its full. @@ -951,10 +932,11 @@ public Archetype EnsureCapacity(in Signature signature, int amount) { // Ensure size of archetype var archetype = GetOrCreate(signature); + Capacity -= archetype.EntityCapacity; // Reduce capacity, in case the previous capacity was already included, ensures more and more till memory leak archetype.EnsureEntityCapacity(archetype.EntityCount + amount); // Ensure size of world - var requiredCapacity = Size + amount; + var requiredCapacity = Capacity + archetype.EntityCapacity; EntityInfo.EnsureCapacity(requiredCapacity); Capacity = requiredCapacity; diff --git a/src/Arch/Templates/World.CreateBulk.tt b/src/Arch/Templates/World.CreateBulk.tt index 30c429db..b162777d 100644 --- a/src/Arch/Templates/World.CreateBulk.tt +++ b/src/Arch/Templates/World.CreateBulk.tt @@ -26,7 +26,7 @@ for (var index = 2; index < Amount; index++) [StructuralChange] public void Create<<#= generics #>>(int amount, <#= parameters #>) -{ + { var archetype = EnsureCapacity<<#= generics #>>(amount); // Prepare entities, slots and data @@ -46,7 +46,7 @@ for (var index = 2; index < Amount; index++) // Add entities to entityinfo AddEntityData(entities, entityData, amount); -} + } <# } #>