Skip to content

Commit 1378f54

Browse files
committed
implement world copy leveraging archetype copy
1 parent 04d52e7 commit 1378f54

File tree

4 files changed

+108
-8
lines changed

4 files changed

+108
-8
lines changed

src/Arch.Tests/ArchetypeTest.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,6 @@ public void CopyTo([Values(1111,2222,3333)] int sourceAmount, [Values(1111,2222,
245245

246246
// Copy from one chunk into other.
247247
Archetype.Copy(source, destination);
248-
source.Clear();
249248

250249
var sourceCounter = sourceAmount;
251250
var destinationCounter = destinationAmount;
@@ -358,7 +357,6 @@ public void CopyToShift([Values(1111,2222,3333)] int sourceAmount, [Values(1111,
358357

359358
// Copy from one chunk into other.
360359
Archetype.Copy(source, destination);
361-
source.Clear();
362360

363361
var requiredChunksForSource = Archetype.GetChunkCapacityFor(source.EntitiesPerChunk, sourceAmount);
364362
var requiredChunksForDestination = Archetype.GetChunkCapacityFor(destination.EntitiesPerChunk, sourceAmount + destinationAmount);

src/Arch.Tests/WorldTest.cs

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -866,4 +866,69 @@ public void GeneratedAdd()
866866
That(_world.GetArchetype(entity2), Is.EqualTo(_world.GetArchetype(entity)));
867867
That(arch, Is.EqualTo(_world.GetArchetype(entity)));
868868
}
869+
870+
/// <summary>
871+
/// Checks if the <see cref="World"/> is copied correctly over a new instance.
872+
/// </summary>
873+
[Test]
874+
public void Copy()
875+
{
876+
using var source = World.Create();
877+
source.Create(new Transform(), new Rotation());
878+
source.Create(new Transform(), new Rotation());
879+
source.Create(new Transform());
880+
source.Create(new Transform(), new Rotation());
881+
source.Create(new Rotation());
882+
source.Create(new Transform());
883+
884+
var copy = source.Copy();
885+
886+
That(copy.Size, Is.EqualTo(source.Size));
887+
That(copy.Capacity, Is.EqualTo(source.Capacity));
888+
That(copy.Archetypes.Count, Is.EqualTo(source.Archetypes.Count));
889+
//same amount of entities per archetype
890+
for (var index = 0; index < copy.Archetypes.Items.Count; index++)
891+
{
892+
var copyArchetype = copy.Archetypes.Items[index];
893+
var sourceArchetypeExists = source.TryGetArchetype(copyArchetype.Signature, out var sourceArchetype);
894+
That(sourceArchetypeExists, Is.True);
895+
That(sourceArchetype, Is.Not.Null);
896+
That(copyArchetype.Count, Is.EqualTo(sourceArchetype.Count));
897+
That(copyArchetype.EntityCount, Is.EqualTo(sourceArchetype.EntityCount));
898+
}
899+
}
900+
901+
/// <summary>
902+
/// Checks if the <see cref="World"/> is copied correctly over a new instance, skipping empty archetypes.
903+
/// </summary>
904+
[Test]
905+
public void CopySkipping()
906+
{
907+
using var source = World.Create();
908+
source.Create(new Transform(), new Rotation());
909+
source.Create(new Transform(), new Rotation());
910+
source.Create(new Transform());
911+
source.Create(new Transform(), new Rotation());
912+
source.Create(new Transform());
913+
source.Create(new Transform());
914+
//this will cause one archetype to be empty and ignored in the copy
915+
source.Destroy(source.Create(new Rotation()));
916+
917+
var copy = source.Copy();
918+
919+
That(copy.Size, Is.EqualTo(source.Size));
920+
//LessThan because one archetype got skipped
921+
That(copy.Capacity, Is.LessThan(source.Capacity));
922+
That(copy.Archetypes.Count, Is.LessThan(source.Archetypes.Count));
923+
//same amount of entities per archetype
924+
for (var index = 0; index < copy.Archetypes.Items.Count; index++)
925+
{
926+
var copyArchetype = copy.Archetypes.Items[index];
927+
var sourceArchetypeExists = source.TryGetArchetype(copyArchetype.Signature, out var sourceArchetype);
928+
That(sourceArchetypeExists, Is.True);
929+
That(sourceArchetype, Is.Not.Null);
930+
That(copyArchetype.Count, Is.EqualTo(sourceArchetype.Count));
931+
That(copyArchetype.EntityCount, Is.EqualTo(sourceArchetype.EntityCount));
932+
}
933+
}
869934
}

src/Arch/Core/Archetype.cs

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -867,7 +867,8 @@ internal static int GetNextSlots(Archetype archetype, Span<Slot> slots, int amou
867867
/// </summary>
868868
/// <param name="source">The source <see cref="Archetype"/>.</param>
869869
/// <param name="destination">The destination <see cref="Archetype"/>.</param>
870-
internal static void Copy(Archetype source, Archetype destination)
870+
/// <param name="clearSource">Whether to clear the source Archetype and Chunk counts.</param>
871+
internal static void Copy(Archetype source, Archetype destination, bool clearSource = true)
871872
{
872873
// Make sure other archetype can fit additional entities from this archetype.
873874
destination.EnsureEntityCapacity(destination.EntityCount + source.EntityCount);
@@ -880,19 +881,25 @@ internal static void Copy(Archetype source, Archetype destination)
880881

881882
var amountCopied = 0;
882883
var chunkIndex = 0;
884+
var amountLeft = sourceChunk.Count;
883885

884886
// Loop over destination chunk and fill them with the source chunk till either the source chunk is empty or theres no more capacity
885-
for (int destinationChunkIndex = destination.Count; destinationChunkIndex < destination.ChunkCapacity && sourceChunk.Count > 0; destinationChunkIndex++)
887+
for (int destinationChunkIndex = destination.Count; destinationChunkIndex < destination.ChunkCapacity && amountLeft > 0; destinationChunkIndex++)
886888
{
887889
// Determine amount that can be copied into destination
888890
ref var destinationChunk = ref destination.GetChunk(destinationChunkIndex);
889891
var remainingCapacity = destinationChunk.Buffer;
890-
var amountToCopy = Math.Min(sourceChunk.Count, remainingCapacity);
892+
var amountToCopy = Math.Min(amountLeft, remainingCapacity);
891893

892894
Chunk.Copy(ref sourceChunk, amountCopied, ref sourceSignature, ref destinationChunk, destinationChunk.Count, amountToCopy);
893895

894896
// Apply copied amount to track the progress
895-
sourceChunk.Count -= amountToCopy;
897+
amountLeft -= amountToCopy;
898+
if (clearSource)
899+
{
900+
sourceChunk.Count -= amountToCopy;
901+
}
902+
896903
destinationChunk.Count += amountToCopy;
897904
amountCopied += amountToCopy;
898905
chunkIndex = destinationChunkIndex; // Track the last destination chunk we filled, important
@@ -903,8 +910,11 @@ internal static void Copy(Archetype source, Archetype destination)
903910

904911
// Update entity counts
905912
destination.EntityCount += source.EntityCount;
906-
source.EntityCount = 0;
907-
source.Count = 0;
913+
if (clearSource)
914+
{
915+
source.EntityCount = 0;
916+
source.Count = 0;
917+
}
908918
}
909919

910920
/// <summary>

src/Arch/Core/World.cs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1744,4 +1744,31 @@ public Signature GetSignature(Entity entity)
17441744
}
17451745
}
17461746

1747+
public partial class World
1748+
{
1749+
/// <summary>
1750+
/// Creates a deep copy of the world, copying archetypes and chunks.
1751+
/// </summary>
1752+
/// <returns>A newly created World with all data copied from the original.</returns>
1753+
public World Copy()
1754+
{
1755+
var copy = Create();
1756+
var archetypes = Archetypes.Items;
1757+
for (var index= 0; index < archetypes.Count; index++)
1758+
{
1759+
var archetype = archetypes[index];
1760+
if (archetype.EntityCount <= 0)
1761+
{
1762+
continue;
1763+
}
1764+
1765+
var newArchetype = copy.GetOrCreate(archetype.Signature);
1766+
Archetype.Copy(archetype, newArchetype, false);
1767+
copy.Size += newArchetype.EntityCount;
1768+
}
1769+
1770+
return copy;
1771+
}
1772+
}
1773+
17471774
#endregion

0 commit comments

Comments
 (0)