diff --git a/src/Arch.SourceGen/Queries/AddWithQueryDescription.cs b/src/Arch.SourceGen/Queries/AddWithQueryDescription.cs
index f3cbbdc6..31f86d5d 100644
--- a/src/Arch.SourceGen/Queries/AddWithQueryDescription.cs
+++ b/src/Arch.SourceGen/Queries/AddWithQueryDescription.cs
@@ -79,12 +79,17 @@ public static void AppendAddWithQueryDescription(this StringBuilder sb, int amou
EntityInfo.Shift(archetype, archetypeSlot, newArchetype, newArchetypeLastSlot);
// Copy, set and clear
+ var oldCapacity = newArchetype.EntityCapacity;
Archetype.Copy(archetype, newArchetype);
var lastSlot = newArchetype.LastSlot;
newArchetype.SetRange(in lastSlot, in newArchetypeLastSlot, {{inParameters}});
- {{addEvents}}
archetype.Clear();
+
+ Capacity += newArchetype.EntityCapacity - oldCapacity;
+ {{addEvents}}
}
+
+ EntityInfo.EnsureCapacity(Capacity);
}
""";
diff --git a/src/Arch.SourceGen/Queries/RemoveWithQueryDescription.cs b/src/Arch.SourceGen/Queries/RemoveWithQueryDescription.cs
index 781d78ca..3d872b57 100644
--- a/src/Arch.SourceGen/Queries/RemoveWithQueryDescription.cs
+++ b/src/Arch.SourceGen/Queries/RemoveWithQueryDescription.cs
@@ -77,9 +77,14 @@ public static void AppendRemoveWithQueryDescription(this StringBuilder sb, int a
Slot.Shift(ref newArchetypeLastSlot, newArchetype.EntitiesPerChunk);
EntityInfo.Shift(archetype, archetypeSlot, newArchetype, newArchetypeLastSlot);
+ var oldCapacity = newArchetype.EntityCapacity;
Archetype.Copy(archetype, newArchetype);
archetype.Clear();
+
+ Capacity += newArchetype.EntityCapacity - oldCapacity;
}
+
+ EntityInfo.EnsureCapacity(Capacity);
}
""";
diff --git a/src/Arch.Tests/WorldTest.cs b/src/Arch.Tests/WorldTest.cs
index d0626369..a5b95201 100644
--- a/src/Arch.Tests/WorldTest.cs
+++ b/src/Arch.Tests/WorldTest.cs
@@ -515,6 +515,39 @@ public void RemoveByQueryDescription()
That(world.CountEntities(in withAIQueryDesc), Is.EqualTo(0));
That(world.CountEntities(in withoutAIQueryDesc), Is.EqualTo(1000));
}
+
+ ///
+ /// Checks if the world fills an empty archetype with left capacity correctly
+ ///
+ [Test]
+ public void FillEmptyArchetypeWithCapacityLeft()
+ {
+ // Create entities with a single archetype
+ for (var i = 0; i < 100; i++)
+ {
+ _world.Create(new Transform());
+ }
+
+ // Create entities with a duplex archetype
+ var archetype = _world.Archetypes[0];
+ var entityCapacity = archetype.EntityCapacity;
+ for (var i = 0; i <= entityCapacity; i++)
+ {
+ _world.Create(new Transform(), new Rotation());
+ }
+
+ // Try move entities from duplex archetype to single archetype
+ _world.Remove(new QueryDescription().WithAll());
+
+ // Just a big value to ensure it goes beyond the original capacity
+ // Capacity should grow properly
+ entityCapacity = archetype.EntityCapacity;
+ for (var i = 0; i <= entityCapacity; i++)
+ {
+ // Create entities with a duplex archetype
+ DoesNotThrow(() => _world.Create(new Transform(), new Rotation()), "Overflow at {0}", i);
+ }
+ }
}
// Get, Set, Has, Remove, Add
diff --git a/src/Arch/Arch.csproj b/src/Arch/Arch.csproj
index 031a6d02..087f85b3 100644
--- a/src/Arch/Arch.csproj
+++ b/src/Arch/Arch.csproj
@@ -91,7 +91,7 @@ Refactored adding of entities to be faster.
-
+
diff --git a/src/Arch/Buffer/CommandBuffer.cs b/src/Arch/Buffer/CommandBuffer.cs
index 30b6abfc..d9626083 100644
--- a/src/Arch/Buffer/CommandBuffer.cs
+++ b/src/Arch/Buffer/CommandBuffer.cs
@@ -1,3 +1,7 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Runtime.CompilerServices;
using Arch.Core;
using Arch.Core.Extensions;
using Arch.Core.Extensions.Internal;
diff --git a/src/Arch/Core/Archetype.cs b/src/Arch/Core/Archetype.cs
index d7036baa..51e356e6 100644
--- a/src/Arch/Core/Archetype.cs
+++ b/src/Arch/Core/Archetype.cs
@@ -560,24 +560,16 @@ private void EnsureChunkCapacity(int newCapacity)
ChunkCapacity = newCapacity;
}
- /// TODO : Currently this only ensures additional entity capacity, instead it should take the whole capacity in count.
///
- /// Ensures the capacity of the array.
- /// Increases the .
+ /// Ensures the capacity of the array for a certain amount of s.
+ /// Increases the to fit all entities within it.
///
- /// The amount of 's required, in total.
+ /// The amount of 's required, in total.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void EnsureEntityCapacity(int newCapacity)
{
- // TODO: LastChunk updated sich nicht wenn von einem archetype weniger entities in einen anderen kopier werden als vorher drin waren.
- // TODO: Dadurch bleibt z.B. ein Chunk am ende des Archetypes frei, wodurch beim entfernen eines entities wieder nen index -1 auftritt und ne exception
- // TODO: LastChunk MUSS sich irgendwie updaten bei so nem Kopier quatsch? Glaube in dieser Methode machts keinen Sinn? Oder vllt doch?
-
// Calculate amount of required chunks.
- //var freeSpots = EntityCapacity - EntityCount;
- //var neededSpots = newCapacity - freeSpots;
var neededChunks = (int)Math.Ceiling((float)newCapacity / EntitiesPerChunk);
-
if (ChunkCapacity-ChunkCount > neededChunks)
{
return;
@@ -592,12 +584,6 @@ internal void EnsureEntityCapacity(int newCapacity)
var newChunk = new Chunk(EntitiesPerChunk, _componentIdToArrayIndex, Types);
Chunks[previousCapacity + index] = newChunk;
}
-
- // If last chunk was full, add.
- /*if (freeSpots == 0)
- {
- ChunkCount++;
- }*/
}
///
diff --git a/src/Arch/Core/EntityInfo.cs b/src/Arch/Core/EntityInfo.cs
index 76b8adf7..2108a1a8 100644
--- a/src/Arch/Core/EntityInfo.cs
+++ b/src/Arch/Core/EntityInfo.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using Arch.Core;
@@ -229,7 +230,6 @@ public void Move(int id, Archetype archetype, Slot slot)
EntitySlots[id] = new EntitySlot(archetype,slot);
}
- /// TODO : Find a cleaner way to break? One that does NOT require a branching?
///
/// Updates the and all entities that moved/shifted between the archetypes.
/// Use and modify with caution, one small logical issue and the whole framework stops working.
@@ -242,7 +242,6 @@ public void Move(int id, Archetype archetype, Slot slot)
public void Shift(Archetype archetype, Slot archetypeSlot, Archetype newArchetype, Slot newArchetypeSlot)
{
// Update the entityInfo of all copied entities.
- //for (var chunkIndex = archetypeSlot.ChunkIndex; chunkIndex >= 0; --chunkIndex)
for (var chunkIndex = 0; chunkIndex <= archetypeSlot.ChunkIndex; chunkIndex++)
{
// Get data
@@ -253,7 +252,6 @@ public void Shift(Archetype archetype, Slot archetypeSlot, Archetype newArchetyp
var isStart = chunkIndex == archetypeSlot.ChunkIndex;
var upper = isStart ? archetypeSlot.Index : chunk.Size-1;
- //for (var index = upper; index >= 0; --index)
for(var index = 0; index <= upper; index++)
{
var entity = Unsafe.Add(ref entityFirstElement, index);
diff --git a/src/Arch/Core/Extensions/Internal/MathExtensions.cs b/src/Arch/Core/Extensions/Internal/MathExtensions.cs
index eb75a147..5560e779 100644
--- a/src/Arch/Core/Extensions/Internal/MathExtensions.cs
+++ b/src/Arch/Core/Extensions/Internal/MathExtensions.cs
@@ -7,25 +7,14 @@
internal static class MathExtensions
{
///
- /// This method will round down to the nearest power of 2 number. If the supplied number is a power of 2 it will return it.
+ /// Returns the max of two ints by bit operation without branching.
///
- ///
- ///
+ /// The first int.
+ /// The second int.
+ /// The highest of both ints.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- internal static int RoundToPowerOfTwo(int num)
+ public static int Max(int a, int b)
{
- // If num is a power of 2, return it
- if (num > 0 && (num & (num - 1)) == 0)
- {
- return num;
- }
-
- // Find the exponent of the nearest power of 2 (rounded down)
- int exponent = (int)Math.Floor(Math.Log(num) / Math.Log(2));
-
- // Calculate the nearest power of 2
- int result = (int)Math.Pow(2, exponent);
-
- return result;
+ return a - ((a - b) & ((a - b) >> 31));
}
}
diff --git a/src/Arch/Core/World.cs b/src/Arch/Core/World.cs
index b55ab099..75db9f7d 100644
--- a/src/Arch/Core/World.cs
+++ b/src/Arch/Core/World.cs
@@ -268,6 +268,7 @@ public Entity Create(params ComponentType[] types)
return Create(types.AsSpan());
}
+ // TODO: Find cleaner way to resize the EntityInfo? Let archetype.Create return an amount which is added to Capacity or whatever?
///
/// Creates a new using its given component structure/.
/// Might resize its target and allocate new space if its full.
@@ -299,7 +300,7 @@ public Entity Create(Span types)
EntityInfo.EnsureCapacity(Capacity);
}
- // Map
+ // Add entity to info storage
EntityInfo.Add(entity.Id, recycled.Version, archetype, slot);
Size++;
OnEntityCreated(entity);
@@ -895,14 +896,19 @@ public void Add(in QueryDescription queryDescription, in T? component = defau
Slot.Shift(ref newArchetypeLastSlot, newArchetype.EntitiesPerChunk);
EntityInfo.Shift(archetype, archetypeSlot, newArchetype, newArchetypeLastSlot);
- // Copy, set and clear
+ // Copy, Set and clear
+ var oldCapacity = newArchetype.EntityCapacity;
Archetype.Copy(archetype, newArchetype);
var lastSlot = newArchetype.LastSlot;
newArchetype.SetRange(in lastSlot, in newArchetypeLastSlot, in component);
archetype.Clear();
+ // Adjust capacity since the new archetype may have changed in size
+ Capacity += newArchetype.EntityCapacity - oldCapacity;
OnComponentAdded(newArchetype);
}
+
+ EntityInfo.EnsureCapacity(Capacity);
}
///
@@ -949,9 +955,16 @@ public void Remove(in QueryDescription queryDescription)
Slot.Shift(ref newArchetypeLastSlot, newArchetype.EntitiesPerChunk);
EntityInfo.Shift(archetype, archetypeSlot, newArchetype, newArchetypeLastSlot);
+ // Copy and track capacity difference
+ var oldCapacity = newArchetype.EntityCapacity;
Archetype.Copy(archetype, newArchetype);
archetype.Clear();
+
+ // Adjust capacity since the new archetype may have changed in size
+ Capacity += newArchetype.EntityCapacity - oldCapacity;
}
+
+ EntityInfo.EnsureCapacity(Capacity);
}
}