Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions src/Arch.Tests/ArchetypeTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,6 @@ public void CopyTo([Values(1111,2222,3333)] int sourceAmount, [Values(1111,2222,

// Copy from one chunk into other.
Archetype.Copy(source, destination);
source.Clear();

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

// Copy from one chunk into other.
Archetype.Copy(source, destination);
source.Clear();

var requiredChunksForSource = Archetype.GetChunkCapacityFor(source.EntitiesPerChunk, sourceAmount);
var requiredChunksForDestination = Archetype.GetChunkCapacityFor(destination.EntitiesPerChunk, sourceAmount + destinationAmount);
Expand Down
65 changes: 65 additions & 0 deletions src/Arch.Tests/WorldTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -866,4 +866,69 @@ public void GeneratedAdd()
That(_world.GetArchetype(entity2), Is.EqualTo(_world.GetArchetype(entity)));
That(arch, Is.EqualTo(_world.GetArchetype(entity)));
}

/// <summary>
/// Checks if the <see cref="World"/> is copied correctly over a new instance.
/// </summary>
[Test]
public void Copy()
{
using var source = World.Create();
source.Create(new Transform(), new Rotation());
source.Create(new Transform(), new Rotation());
source.Create(new Transform());
source.Create(new Transform(), new Rotation());
source.Create(new Rotation());
source.Create(new Transform());

var copy = source.Copy();

That(copy.Size, Is.EqualTo(source.Size));
That(copy.Capacity, Is.EqualTo(source.Capacity));
That(copy.Archetypes.Count, Is.EqualTo(source.Archetypes.Count));
//same amount of entities per archetype
for (var index = 0; index < copy.Archetypes.Items.Count; index++)
{
var copyArchetype = copy.Archetypes.Items[index];
var sourceArchetypeExists = source.TryGetArchetype(copyArchetype.Signature, out var sourceArchetype);
That(sourceArchetypeExists, Is.True);
That(sourceArchetype, Is.Not.Null);
That(copyArchetype.Count, Is.EqualTo(sourceArchetype.Count));
That(copyArchetype.EntityCount, Is.EqualTo(sourceArchetype.EntityCount));
}
}

/// <summary>
/// Checks if the <see cref="World"/> is copied correctly over a new instance, skipping empty archetypes.
/// </summary>
[Test]
public void CopySkipping()
{
using var source = World.Create();
source.Create(new Transform(), new Rotation());
source.Create(new Transform(), new Rotation());
source.Create(new Transform());
source.Create(new Transform(), new Rotation());
source.Create(new Transform());
source.Create(new Transform());
//this will cause one archetype to be empty and ignored in the copy
source.Destroy(source.Create(new Rotation()));

var copy = source.Copy();

That(copy.Size, Is.EqualTo(source.Size));
//LessThan because one archetype got skipped
That(copy.Capacity, Is.LessThan(source.Capacity));
That(copy.Archetypes.Count, Is.LessThan(source.Archetypes.Count));
//same amount of entities per archetype
for (var index = 0; index < copy.Archetypes.Items.Count; index++)
{
var copyArchetype = copy.Archetypes.Items[index];
var sourceArchetypeExists = source.TryGetArchetype(copyArchetype.Signature, out var sourceArchetype);
That(sourceArchetypeExists, Is.True);
That(sourceArchetype, Is.Not.Null);
That(copyArchetype.Count, Is.EqualTo(sourceArchetype.Count));
That(copyArchetype.EntityCount, Is.EqualTo(sourceArchetype.EntityCount));
}
}
}
22 changes: 16 additions & 6 deletions src/Arch/Core/Archetype.cs
Original file line number Diff line number Diff line change
Expand Up @@ -867,7 +867,8 @@ internal static int GetNextSlots(Archetype archetype, Span<Slot> slots, int amou
/// </summary>
/// <param name="source">The source <see cref="Archetype"/>.</param>
/// <param name="destination">The destination <see cref="Archetype"/>.</param>
internal static void Copy(Archetype source, Archetype destination)
/// <param name="clearSource">Whether to clear the source Archetype and Chunk counts.</param>
internal static void Copy(Archetype source, Archetype destination, bool clearSource = true)
{
// Make sure other archetype can fit additional entities from this archetype.
destination.EnsureEntityCapacity(destination.EntityCount + source.EntityCount);
Expand All @@ -880,19 +881,25 @@ internal static void Copy(Archetype source, Archetype destination)

var amountCopied = 0;
var chunkIndex = 0;
var amountLeft = sourceChunk.Count;

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

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

// Apply copied amount to track the progress
sourceChunk.Count -= amountToCopy;
amountLeft -= amountToCopy;
if (clearSource)
{
sourceChunk.Count -= amountToCopy;
}

destinationChunk.Count += amountToCopy;
amountCopied += amountToCopy;
chunkIndex = destinationChunkIndex; // Track the last destination chunk we filled, important
Expand All @@ -903,8 +910,11 @@ internal static void Copy(Archetype source, Archetype destination)

// Update entity counts
destination.EntityCount += source.EntityCount;
source.EntityCount = 0;
source.Count = 0;
if (clearSource)
{
source.EntityCount = 0;
source.Count = 0;
}
}

/// <summary>
Expand Down
27 changes: 27 additions & 0 deletions src/Arch/Core/World.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1744,4 +1744,31 @@ public Signature GetSignature(Entity entity)
}
}

public partial class World
{
/// <summary>
/// Creates a deep copy of the world, copying archetypes and chunks.
/// </summary>
/// <returns>A newly created World with all data copied from the original.</returns>
public World Copy()
{
var copy = Create();
var archetypes = Archetypes.Items;
for (var index= 0; index < archetypes.Count; index++)
{
var archetype = archetypes[index];
if (archetype.EntityCount <= 0)
{
continue;
}

var newArchetype = copy.GetOrCreate(archetype.Signature);
Archetype.Copy(archetype, newArchetype, false);
copy.Size += newArchetype.EntityCount;
}

return copy;
}
}

#endregion
Loading