diff --git a/src/ECS/Archetype/Archetype.cs b/src/ECS/Archetype/Archetype.cs index d9357815..18187918 100644 --- a/src/ECS/Archetype/Archetype.cs +++ b/src/ECS/Archetype/Archetype.cs @@ -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 diff --git a/src/ECS/Archetype/EntityStore.Archetype.cs b/src/ECS/Archetype/EntityStore.Archetype.cs index 16b0f4c5..67fa6e4c 100644 --- a/src/ECS/Archetype/EntityStore.Archetype.cs +++ b/src/ECS/Archetype/EntityStore.Archetype.cs @@ -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) diff --git a/src/ECS/Archetype/TypeCache.cs b/src/ECS/Archetype/TypeCache.cs new file mode 100644 index 00000000..9f11c5d7 --- /dev/null +++ b/src/ECS/Archetype/TypeCache.cs @@ -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; + } +} \ No newline at end of file diff --git a/src/Tests-internal/ECS/Test_sizeof.cs b/src/Tests-internal/ECS/Test_sizeof.cs index 39e60767..50345c90 100644 --- a/src/Tests-internal/ECS/Test_sizeof.cs +++ b/src/Tests-internal/ECS/Test_sizeof.cs @@ -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); diff --git a/src/Tests/ECS/Entity/Test_StructComponent.cs b/src/Tests/ECS/Entity/Test_StructComponent.cs index b42eaf65..327901ed 100644 --- a/src/Tests/ECS/Entity/Test_StructComponent.cs +++ b/src/Tests/ECS/Entity/Test_StructComponent.cs @@ -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>()); + } + } } \ No newline at end of file