Skip to content

Commit

Permalink
ECS - optimize: add TypeCache for Entity.AddComponent<>() / RemoveCom…
Browse files Browse the repository at this point in the history
…ponent<>()
  • Loading branch information
friflo committed Aug 17, 2024
1 parent b23d6cc commit 0883053
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 4 deletions.
2 changes: 2 additions & 0 deletions src/ECS/Archetype/Archetype.cs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ public Span<TComponent> Components<TComponent>() where TComponen
[Browse(Never)] internal readonly int archIndex; // 4 - archetype index in EntityStore.archs[]
[Browse(Never)] internal readonly StandardComponents std; // 32 - heap references to std types: Position, Rotation, ...
[Browse(Never)] private ArchetypeQuery query; // 8 - return the entities of this archetype
[Browse(Never)] internal TypeCache componentAdd; // 24
[Browse(Never)] internal TypeCache componentRemove;// 24
#endregion

#region public methods
Expand Down
16 changes: 12 additions & 4 deletions src/ECS/Archetype/EntityStore.Archetype.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,32 +76,40 @@ internal bool TryGetValue(ArchetypeKey searchKey, out ArchetypeKey archetypeKey)

internal static Archetype GetArchetypeWith(EntityStoreBase store, Archetype current, int structIndex)
{
var typeIndex = current.componentAdd.FindType(structIndex);
if (typeIndex >= 0) {
return current.store.archs[typeIndex];
}
var key = store.searchKey;
key.SetWith(current, structIndex);
if (store.archSet.TryGetValue(key, out var archetypeKey)) {
return archetypeKey.archetype;
return current.componentAdd.Cache((byte)structIndex, archetypeKey.archetype);
}
var config = GetArchetypeConfig(store);
var componentTypes = current.componentTypes;
componentTypes.bitSet.SetBit(structIndex);
var archetype = Archetype.CreateWithComponentTypes(config, componentTypes, current.tags);
AddArchetype(store, archetype);
return archetype;
return current.componentAdd.Cache((byte)structIndex, archetype);
}

internal static Archetype GetArchetypeWithout(EntityStoreBase store, Archetype archetype, int structIndex)
{
var typeIndex = archetype.componentRemove.FindType(structIndex);
if (typeIndex >= 0) {
return archetype.store.archs[typeIndex];
}
var key = store.searchKey;
key.SetWithout(archetype, structIndex);
if (store.archSet.TryGetValue(key, out var archetypeKey)) {
return archetypeKey.archetype;
return archetype.componentRemove.Cache((byte)structIndex, archetypeKey.archetype);
}
var config = GetArchetypeConfig(store);
var componentTypes = archetype.componentTypes;
componentTypes.bitSet.ClearBit(structIndex);
var result = Archetype.CreateWithComponentTypes(config, componentTypes, archetype.tags);
AddArchetype(store, result);
return result;
return archetype.componentRemove.Cache((byte)structIndex, result);
}

private static Archetype GetArchetypeWithTags(EntityStoreBase store, Archetype archetype, in Tags tags)
Expand Down
42 changes: 42 additions & 0 deletions src/ECS/Archetype/TypeCache.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Copyright (c) Ullrich Praetz - https://github.com/friflo. All rights reserved.
// See LICENSE file in the project root for full license information.

// ReSharper disable once CheckNamespace
namespace Friflo.Engine.ECS;

internal struct TypeCache
{
private byte type0; // 1
private byte type1; // 1
private byte type2; // 1
private byte type3; // 1

private int index0; // 4
private int index1; // 4
private int index2; // 4
private int index3; // 4

private int next; // 4

internal int FindType(int type)
{
if (type == type0) return index0;
if (type == type1) return index1;
if (type == type2) return index2;
if (type == type3) return index3;
return -1;
}

internal Archetype Cache(byte type, Archetype archetype)
{
var index = archetype.archIndex;
var at = next++ % 4;
switch (at) {
case 0: type0 = type; index0 = index; break;
case 1: type1 = type; index1 = index; break;
case 2: type2 = type; index2 = index; break;
case 3: type3 = type; index3 = index; break;
}
return archetype;
}
}
7 changes: 7 additions & 0 deletions src/Tests-internal/ECS/Test_sizeof.cs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,13 @@ public static void Test_sizeof_StructIndexes() {
AreEqual(16, size);
}

[Test]
public static void Test_sizeof_TypeCache() {
var type = typeof(TypeCache);
var size = Marshal.SizeOf(type!);
AreEqual(24, size);
}

[Test]
public static unsafe void Test_Math_sizeof() {
var size = sizeof(Position);
Expand Down
37 changes: 37 additions & 0 deletions src/Tests/ECS/Entity/Test_StructComponent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -516,6 +516,43 @@ public static void Test_StructComponent_Archetype_CreateEntity_default_component
AreEqual(0, entity.MyComponent1().a);
}

[Test]
public static void Test_StructComponent_Cache()
{
var store = new EntityStore();
var entity = store.CreateEntity();

IsTrue (entity.AddComponent(new MyComponent1()));
IsTrue (entity.RemoveComponent<MyComponent1>());
IsTrue (entity.AddComponent(new MyComponent1 { a = 10 }));
AreEqual(10, entity.GetComponent<MyComponent1>().a);
IsTrue (entity.RemoveComponent<MyComponent1>());

IsTrue (entity.AddComponent(new MyComponent2()));
IsTrue (entity.RemoveComponent<MyComponent2>());
IsTrue (entity.AddComponent(new MyComponent2 { b = 11 }));
AreEqual(11, entity.GetComponent<MyComponent2>().b);
IsTrue (entity.RemoveComponent<MyComponent2>());

IsTrue (entity.AddComponent(new MyComponent3()));
IsTrue (entity.RemoveComponent<MyComponent3>());
IsTrue (entity.AddComponent(new MyComponent3 { b = 12 }));
AreEqual(12, entity.GetComponent<MyComponent3>().b);
IsTrue (entity.RemoveComponent<MyComponent3>());

IsTrue (entity.AddComponent(new MyComponent4()));
IsTrue (entity.RemoveComponent<MyComponent4>());
IsTrue (entity.AddComponent(new MyComponent4 { b = 13 }));
AreEqual(13, entity.GetComponent<MyComponent4>().b);
IsTrue (entity.RemoveComponent<MyComponent4>());

IsTrue (entity.AddComponent(new MyComponent5()));
IsTrue (entity.RemoveComponent<MyComponent5>());
IsTrue (entity.AddComponent(new MyComponent5 { b = 14 }));
AreEqual(14, entity.GetComponent<MyComponent5>().b);
IsTrue (entity.RemoveComponent<MyComponent5>());
}

}

}

0 comments on commit 0883053

Please sign in to comment.