Skip to content

Commit

Permalink
Fixes #220
Browse files Browse the repository at this point in the history
  • Loading branch information
genaray committed Jun 20, 2024
1 parent d208564 commit 87f2105
Show file tree
Hide file tree
Showing 9 changed files with 74 additions and 41 deletions.
7 changes: 6 additions & 1 deletion src/Arch.SourceGen/Queries/AddWithQueryDescription.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
""";

Expand Down
5 changes: 5 additions & 0 deletions src/Arch.SourceGen/Queries/RemoveWithQueryDescription.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
""";

Expand Down
33 changes: 33 additions & 0 deletions src/Arch.Tests/WorldTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -515,6 +515,39 @@ public void RemoveByQueryDescription()
That(world.CountEntities(in withAIQueryDesc), Is.EqualTo(0));
That(world.CountEntities(in withoutAIQueryDesc), Is.EqualTo(1000));
}

/// <summary>
/// Checks if the world fills an empty archetype with left capacity correctly
/// </summary>
[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<Rotation>(new QueryDescription().WithAll<Transform, Rotation>());

// 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
Expand Down
2 changes: 1 addition & 1 deletion src/Arch/Arch.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ Refactored adding of entities to be faster. </PackageReleaseNotes>
</ItemGroup>

<ItemGroup>
<PackageReference Include="Arch.LowLevel" Version="1.1.0" />
<PackageReference Include="Arch.LowLevel" Version="1.1.1" />
<PackageReference Include="Collections.Pooled" Version="2.0.0-preview.27" />
<PackageReference Include="CommunityToolkit.HighPerformance" Version="8.2.2" />
<PackageReference Include="Microsoft.Extensions.ObjectPool" Version="7.0.0" />
Expand Down
4 changes: 4 additions & 0 deletions src/Arch/Buffer/CommandBuffer.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
20 changes: 3 additions & 17 deletions src/Arch/Core/Archetype.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
/// <summary>
/// Ensures the capacity of the <see cref="Chunks"/> array.
/// Increases the <see cref="ChunkCapacity"/>.
/// Ensures the capacity of the <see cref="Chunks"/> array for a certain amount of <see cref="Entity"/>s.
/// Increases the <see cref="ChunkCapacity"/> to fit all entities within it.
/// </summary>
/// <param name="newCapacity">The amount of <see cref="Chunk"/>'s required, in total.</param>
/// <param name="newCapacity">The amount of <see cref="Entity"/>'s required, in total.</param>
[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;
Expand All @@ -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++;
}*/
}

/// <summary>
Expand Down
4 changes: 1 addition & 3 deletions src/Arch/Core/EntityInfo.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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?
/// <summary>
/// Updates the <see cref="EntityInfo"/> and all entities that moved/shifted between the archetypes.
/// <remarks>Use and modify with caution, one small logical issue and the whole framework stops working.</remarks>
Expand All @@ -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
Expand All @@ -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);
Expand Down
23 changes: 6 additions & 17 deletions src/Arch/Core/Extensions/Internal/MathExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,25 +7,14 @@
internal static class MathExtensions
{
/// <summary>
/// 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.
/// </summary>
/// <param name="num"></param>
/// <returns></returns>
/// <param name="a">The first int.</param>
/// <param name="b">The second int.</param>
/// <returns>The highest of both ints.</returns>
[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));
}
}
17 changes: 15 additions & 2 deletions src/Arch/Core/World.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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?
/// <summary>
/// Creates a new <see cref="Entity"/> using its given component structure/<see cref="Archetype"/>.
/// Might resize its target <see cref="Archetype"/> and allocate new space if its full.
Expand Down Expand Up @@ -299,7 +300,7 @@ public Entity Create(Span<ComponentType> types)
EntityInfo.EnsureCapacity(Capacity);
}

// Map
// Add entity to info storage
EntityInfo.Add(entity.Id, recycled.Version, archetype, slot);
Size++;
OnEntityCreated(entity);
Expand Down Expand Up @@ -895,14 +896,19 @@ public void Add<T>(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<T>(newArchetype);
}

EntityInfo.EnsureCapacity(Capacity);
}

/// <summary>
Expand Down Expand Up @@ -949,9 +955,16 @@ public void Remove<T>(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);
}
}

Expand Down

0 comments on commit 87f2105

Please sign in to comment.