diff --git a/src/ECS.Boost/Chunk/Extensions.6.cs b/src/ECS.Boost/Chunk/Extensions.6.cs new file mode 100644 index 000000000..1626bd13f --- /dev/null +++ b/src/ECS.Boost/Chunk/Extensions.6.cs @@ -0,0 +1,86 @@ +// Copyright (c) Ullrich Praetz - https://github.com/friflo. All rights reserved. +// See LICENSE file in the project root for full license information. + +using System; + +// ReSharper disable once CheckNamespace +namespace Friflo.Engine.ECS; + +public static partial class ChunkExtensions +{ + public static void Each(this Chunks chunks, ref TEach each) + where TEach : IEach + where T1 : struct + where T2 : struct + where T3 : struct + where T4 : struct + where T5 : struct + where T6 : struct + { + var entities = chunks.Entities; + var start = entities.Start; + var length = entities.Length; + var span1 = new Span(chunks.Chunk1.ArchetypeComponents, start, length); + var span2 = new Span(chunks.Chunk2.ArchetypeComponents, start, length); + var span3 = new Span(chunks.Chunk3.ArchetypeComponents, start, length); + var span4 = new Span(chunks.Chunk4.ArchetypeComponents, start, length); + var span5 = new Span(chunks.Chunk5.ArchetypeComponents, start, length); + var span6 = new Span(chunks.Chunk6.ArchetypeComponents, start, length); + + unsafe { +#pragma warning disable CS8500 + fixed (T1* c1 = span1) + fixed (T2* c2 = span2) + fixed (T3* c3 = span3) + fixed (T4* c4 = span4) + fixed (T5* c5 = span5) + fixed (T6* c6 = span6) +#pragma warning restore CS8500 + { + for (int i = 0; i < length; i++) { + // all spanX.Length == length. spanX[i] is always in bounds. + each.Execute(ref c1[i], ref c2[i], ref c3[i], ref c4[i], ref c5[i], ref c6[i]); + } + } + } + } + + public static void EachEntity(this Chunks chunks, ref TEachEntity each) + where TEachEntity : IEachEntity + where T1 : struct + where T2 : struct + where T3 : struct + where T4 : struct + where T5 : struct + where T6 : struct + { + var entities = chunks.Entities; + var start = entities.Start; + var length = entities.Length; + var spanIds = entities.Archetype.EntityIds.Slice (start, length); + var span1 = new Span(chunks.Chunk1.ArchetypeComponents, start, length); + var span2 = new Span(chunks.Chunk2.ArchetypeComponents, start, length); + var span3 = new Span(chunks.Chunk3.ArchetypeComponents, start, length); + var span4 = new Span(chunks.Chunk4.ArchetypeComponents, start, length); + var span5 = new Span(chunks.Chunk5.ArchetypeComponents, start, length); + var span6 = new Span(chunks.Chunk6.ArchetypeComponents, start, length); + + unsafe { +#pragma warning disable CS8500 + fixed (T1* c1 = span1) + fixed (T2* c2 = span2) + fixed (T3* c3 = span3) + fixed (T4* c4 = span4) + fixed (T5* c5 = span5) + fixed (T6* c6 = span6) +#pragma warning restore CS8500 + fixed (int* ids = spanIds) + { + for (int i = 0; i < length; i++) { + // all spanX.Length == length. spanX[i] is always in bounds. + each.Execute(ref c1[i], ref c2[i], ref c3[i], ref c4[i], ref c5[i], ref c6[i], ids[i]); + } + } + } + } +} \ No newline at end of file diff --git a/src/ECS.Boost/Chunk/Extensions.7.cs b/src/ECS.Boost/Chunk/Extensions.7.cs new file mode 100644 index 000000000..1bbd67262 --- /dev/null +++ b/src/ECS.Boost/Chunk/Extensions.7.cs @@ -0,0 +1,92 @@ +// Copyright (c) Ullrich Praetz - https://github.com/friflo. All rights reserved. +// See LICENSE file in the project root for full license information. + +using System; + +// ReSharper disable once CheckNamespace +namespace Friflo.Engine.ECS; + +public static partial class ChunkExtensions +{ + public static void Each(this Chunks chunks, ref TEach each) + where TEach : IEach + where T1 : struct + where T2 : struct + where T3 : struct + where T4 : struct + where T5 : struct + where T6 : struct + where T7 : struct + { + var entities = chunks.Entities; + var start = entities.Start; + var length = entities.Length; + var span1 = new Span(chunks.Chunk1.ArchetypeComponents, start, length); + var span2 = new Span(chunks.Chunk2.ArchetypeComponents, start, length); + var span3 = new Span(chunks.Chunk3.ArchetypeComponents, start, length); + var span4 = new Span(chunks.Chunk4.ArchetypeComponents, start, length); + var span5 = new Span(chunks.Chunk5.ArchetypeComponents, start, length); + var span6 = new Span(chunks.Chunk6.ArchetypeComponents, start, length); + var span7 = new Span(chunks.Chunk7.ArchetypeComponents, start, length); + + unsafe { +#pragma warning disable CS8500 + fixed (T1* c1 = span1) + fixed (T2* c2 = span2) + fixed (T3* c3 = span3) + fixed (T4* c4 = span4) + fixed (T5* c5 = span5) + fixed (T6* c6 = span6) + fixed (T7* c7 = span7) +#pragma warning restore CS8500 + { + for (int i = 0; i < length; i++) { + // all spanX.Length == length. spanX[i] is always in bounds. + each.Execute(ref c1[i], ref c2[i], ref c3[i], ref c4[i], ref c5[i], ref c6[i], ref c7[i]); + } + } + } + } + + public static void EachEntity(this Chunks chunks, ref TEachEntity each) + where TEachEntity : IEachEntity + where T1 : struct + where T2 : struct + where T3 : struct + where T4 : struct + where T5 : struct + where T6 : struct + where T7 : struct + { + var entities = chunks.Entities; + var start = entities.Start; + var length = entities.Length; + var spanIds = entities.Archetype.EntityIds.Slice (start, length); + var span1 = new Span(chunks.Chunk1.ArchetypeComponents, start, length); + var span2 = new Span(chunks.Chunk2.ArchetypeComponents, start, length); + var span3 = new Span(chunks.Chunk3.ArchetypeComponents, start, length); + var span4 = new Span(chunks.Chunk4.ArchetypeComponents, start, length); + var span5 = new Span(chunks.Chunk5.ArchetypeComponents, start, length); + var span6 = new Span(chunks.Chunk6.ArchetypeComponents, start, length); + var span7 = new Span(chunks.Chunk7.ArchetypeComponents, start, length); + + unsafe { +#pragma warning disable CS8500 + fixed (T1* c1 = span1) + fixed (T2* c2 = span2) + fixed (T3* c3 = span3) + fixed (T4* c4 = span4) + fixed (T5* c5 = span5) + fixed (T6* c6 = span6) + fixed (T7* c7 = span7) +#pragma warning restore CS8500 + fixed (int* ids = spanIds) + { + for (int i = 0; i < length; i++) { + // all spanX.Length == length. spanX[i] is always in bounds. + each.Execute(ref c1[i], ref c2[i], ref c3[i], ref c4[i], ref c5[i], ref c6[i], ref c7[i], ids[i]); + } + } + } + } +} \ No newline at end of file diff --git a/src/ECS.Boost/Query/Extensions.6.cs b/src/ECS.Boost/Query/Extensions.6.cs new file mode 100644 index 000000000..06a53af51 --- /dev/null +++ b/src/ECS.Boost/Query/Extensions.6.cs @@ -0,0 +1,38 @@ +// 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; + +public static partial class QueryExtensions +{ + public static TEach Each(this ArchetypeQuery query, TEach each) + where TEach : IEach + where T1 : struct + where T2 : struct + where T3 : struct + where T4 : struct + where T5 : struct + where T6 : struct + { + foreach (var chunk in query.Chunks) { + chunk.Each(ref each); + } + return each; + } + + public static TEachEntity EachEntity(this ArchetypeQuery query, TEachEntity each) + where TEachEntity : IEachEntity + where T1 : struct + where T2 : struct + where T3 : struct + where T4 : struct + where T5 : struct + where T6 : struct + { + foreach (var chunk in query.Chunks) { + chunk.EachEntity(ref each); + } + return each; + } +} \ No newline at end of file diff --git a/src/ECS.Boost/Query/Extensions.7.cs b/src/ECS.Boost/Query/Extensions.7.cs new file mode 100644 index 000000000..6e224212a --- /dev/null +++ b/src/ECS.Boost/Query/Extensions.7.cs @@ -0,0 +1,40 @@ +// 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; + +public static partial class QueryExtensions +{ + public static TEach Each(this ArchetypeQuery query, TEach each) + where TEach : IEach + where T1 : struct + where T2 : struct + where T3 : struct + where T4 : struct + where T5 : struct + where T6 : struct + where T7 : struct + { + foreach (var chunk in query.Chunks) { + chunk.Each(ref each); + } + return each; + } + + public static TEachEntity EachEntity(this ArchetypeQuery query, TEachEntity each) + where TEachEntity : IEachEntity + where T1 : struct + where T2 : struct + where T3 : struct + where T4 : struct + where T5 : struct + where T6 : struct + where T7 : struct + { + foreach (var chunk in query.Chunks) { + chunk.EachEntity(ref each); + } + return each; + } +} \ No newline at end of file diff --git a/src/ECS/Archetype/ComponentTypes.cs b/src/ECS/Archetype/ComponentTypes.cs index 00f1b344e..4d0837e99 100644 --- a/src/ECS/Archetype/ComponentTypes.cs +++ b/src/ECS/Archetype/ComponentTypes.cs @@ -68,8 +68,12 @@ internal ComponentTypes(in SignatureIndexes indexes) case 3: goto Type3; case 4: goto Type4; case 5: goto Type5; + case 6: goto Type6; + case 7: goto Type7; default: throw new IndexOutOfRangeException(); // unreachable - already ensured at SignatureIndexes } + Type7: bitSet.SetBit(indexes.T7); + Type6: bitSet.SetBit(indexes.T6); Type5: bitSet.SetBit(indexes.T5); Type4: bitSet.SetBit(indexes.T4); Type3: bitSet.SetBit(indexes.T3); @@ -315,7 +319,7 @@ public static ComponentTypes Get() /// /// Create an instance containing the passed types , - /// , , and . + /// , , and . /// public static ComponentTypes Get() where T1 : struct, IComponent @@ -326,52 +330,19 @@ public static ComponentTypes Get() { return Generic.ComponentTypes; } - /* - internal static ComponentTypes Get() - where T1 : struct, IComponent - where T2 : struct, IComponent - where T3 : struct, IComponent - where T4 : struct, IComponent - where T5 : struct, IComponent - where T6 : struct, IComponent - { - return new ComponentTypes( - StructInfo.Index, - StructInfo.Index, - StructInfo.Index, - StructInfo.Index, - StructInfo.Index, - StructInfo.Index); - } - - internal static ComponentTypes Get() - where T1 : struct, IComponent - where T2 : struct, IComponent - where T3 : struct, IComponent - where T4 : struct, IComponent - where T5 : struct, IComponent - where T6 : struct, IComponent - where T7 : struct, IComponent - { - return new ComponentTypes( - StructInfo.Index, - StructInfo.Index, - StructInfo.Index, - StructInfo.Index, - StructInfo.Index, - StructInfo.Index, - StructInfo.Index); - } - internal static ComponentTypes Get() + /// + /// Create an instance containing the passed types , + /// , , , + /// and . + /// + public static ComponentTypes Get() where T1 : struct, IComponent where T2 : struct, IComponent where T3 : struct, IComponent where T4 : struct, IComponent where T5 : struct, IComponent where T6 : struct, IComponent - where T7 : struct, IComponent - where T8 : struct, IComponent { return new ComponentTypes( StructInfo.Index, @@ -379,58 +350,102 @@ internal static ComponentTypes Get() StructInfo.Index, StructInfo.Index, StructInfo.Index, - StructInfo.Index, - StructInfo.Index, - StructInfo.Index); + StructInfo.Index); } - internal static ComponentTypes Get() - where T1 : struct, IComponent - where T2 : struct, IComponent - where T3 : struct, IComponent - where T4 : struct, IComponent - where T5 : struct, IComponent - where T6 : struct, IComponent - where T7 : struct, IComponent - where T8 : struct, IComponent - where T9 : struct, IComponent - { - return new ComponentTypes( - StructInfo.Index, - StructInfo.Index, - StructInfo.Index, - StructInfo.Index, - StructInfo.Index, - StructInfo.Index, - StructInfo.Index, - StructInfo.Index, - StructInfo.Index); - } + /// + /// Create an instance containing the passed types , + /// , , , , + /// and . + /// + public static ComponentTypes Get() + where T1 : struct, IComponent + where T2 : struct, IComponent + where T3 : struct, IComponent + where T4 : struct, IComponent + where T5 : struct, IComponent + where T6 : struct, IComponent + where T7 : struct, IComponent + { + return new ComponentTypes( + StructInfo.Index, + StructInfo.Index, + StructInfo.Index, + StructInfo.Index, + StructInfo.Index, + StructInfo.Index, + StructInfo.Index); + } - internal static ComponentTypes Get() - where T1 : struct, IComponent - where T2 : struct, IComponent - where T3 : struct, IComponent - where T4 : struct, IComponent - where T5 : struct, IComponent - where T6 : struct, IComponent - where T7 : struct, IComponent - where T8 : struct, IComponent - where T9 : struct, IComponent - where T10: struct, IComponent - { - return new ComponentTypes( - StructInfo.Index, - StructInfo.Index, - StructInfo.Index, - StructInfo.Index, - StructInfo.Index, - StructInfo.Index, - StructInfo.Index, - StructInfo.Index, - StructInfo.Index, - StructInfo.Index); - }*/ + /* + internal static ComponentTypes Get() + where T1 : struct, IComponent + where T2 : struct, IComponent + where T3 : struct, IComponent + where T4 : struct, IComponent + where T5 : struct, IComponent + where T6 : struct, IComponent + where T7 : struct, IComponent + where T8 : struct, IComponent + { + return new ComponentTypes( + StructInfo.Index, + StructInfo.Index, + StructInfo.Index, + StructInfo.Index, + StructInfo.Index, + StructInfo.Index, + StructInfo.Index, + StructInfo.Index); + } + + internal static ComponentTypes Get() + where T1 : struct, IComponent + where T2 : struct, IComponent + where T3 : struct, IComponent + where T4 : struct, IComponent + where T5 : struct, IComponent + where T6 : struct, IComponent + where T7 : struct, IComponent + where T8 : struct, IComponent + where T9 : struct, IComponent + { + return new ComponentTypes( + StructInfo.Index, + StructInfo.Index, + StructInfo.Index, + StructInfo.Index, + StructInfo.Index, + StructInfo.Index, + StructInfo.Index, + StructInfo.Index, + StructInfo.Index); + } + + internal static ComponentTypes Get() + where T1 : struct, IComponent + where T2 : struct, IComponent + where T3 : struct, IComponent + where T4 : struct, IComponent + where T5 : struct, IComponent + where T6 : struct, IComponent + where T7 : struct, IComponent + where T8 : struct, IComponent + where T9 : struct, IComponent + where T10: struct, IComponent + { + return new ComponentTypes( + StructInfo.Index, + StructInfo.Index, + StructInfo.Index, + StructInfo.Index, + StructInfo.Index, + StructInfo.Index, + StructInfo.Index, + StructInfo.Index, + StructInfo.Index, + StructInfo.Index); + }*/ #endregion #region internal methods diff --git a/src/ECS/Query/Arg.6/IEach.cs b/src/ECS/Query/Arg.6/IEach.cs new file mode 100644 index 000000000..b5b64c720 --- /dev/null +++ b/src/ECS/Query/Arg.6/IEach.cs @@ -0,0 +1,16 @@ +// 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; + +public interface IEach +{ + void Execute(ref T1 c1, ref T2 c2, ref T3 c3, ref T4 c4, ref T5 c5, ref T6 c6); +} + +public interface IEachEntity +{ + void Execute(ref T1 c1, ref T2 c2, ref T3 c3, ref T4 c4, ref T5 c5, ref T6 c6, int id); +} \ No newline at end of file diff --git a/src/ECS/Query/Arg.6/Query.Chunks.cs b/src/ECS/Query/Arg.6/Query.Chunks.cs new file mode 100644 index 000000000..9eb63bbb1 --- /dev/null +++ b/src/ECS/Query/Arg.6/Query.Chunks.cs @@ -0,0 +1,209 @@ +// Copyright (c) Ullrich Praetz - https://github.com/friflo. All rights reserved. +// See LICENSE file in the project root for full license information. + + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; + +// ReSharper disable InconsistentNaming +// ReSharper disable once CheckNamespace +namespace Friflo.Engine.ECS; + +/// +/// Contains the components returned by a component query. +/// See Example. +/// +public readonly struct Chunks + where T1 : struct + where T2 : struct + where T3 : struct + where T4 : struct + where T5 : struct + where T6 : struct +{ + public int Length => Chunk1.Length; + public readonly Chunk Chunk1; // 16 + public readonly Chunk Chunk2; // 16 + public readonly Chunk Chunk3; // 16 + public readonly Chunk Chunk4; // 16 + public readonly Chunk Chunk5; // 16 + public readonly Chunk Chunk6; // 16 + public readonly ChunkEntities Entities; // 32 + + public override string ToString() => Entities.GetChunksString(); + + internal Chunks(Chunk chunk1, Chunk chunk2, Chunk chunk3, Chunk chunk4, Chunk chunk5, Chunk chunk6, in ChunkEntities entities) { + Chunk1 = chunk1; + Chunk2 = chunk2; + Chunk3 = chunk3; + Chunk4 = chunk4; + Chunk5 = chunk5; + Chunk6 = chunk6; + Entities = entities; + } + + internal Chunks(in Chunks chunks, int start, int length, int taskIndex) { + Chunk1 = new Chunk (chunks.Chunk1, start, length); + Chunk2 = new Chunk (chunks.Chunk2, start, length); + Chunk3 = new Chunk (chunks.Chunk3, start, length); + Chunk4 = new Chunk (chunks.Chunk4, start, length); + Chunk5 = new Chunk (chunks.Chunk5, start, length); + Chunk6 = new Chunk (chunks.Chunk6, start, length); + Entities = new ChunkEntities(chunks.Entities, start, length, taskIndex); + } + + internal Chunks(in ChunkEntities entities, int taskIndex) { + Entities = new ChunkEntities(entities, taskIndex); + } + + public void Deconstruct(out Chunk chunk1, out Chunk chunk2, out Chunk chunk3, out Chunk chunk4, out Chunk chunk5, out Chunk chunk6, out ChunkEntities entities) { + chunk1 = Chunk1; + chunk2 = Chunk2; + chunk3 = Chunk3; + chunk4 = Chunk4; + chunk5 = Chunk5; + chunk6 = Chunk6; + entities = Entities; + } +} + +/// +/// Contains the component chunks returned by a component query. +/// See Example. +/// +public readonly struct QueryChunks : IEnumerable > + where T1 : struct + where T2 : struct + where T3 : struct + where T4 : struct + where T5 : struct + where T6 : struct +{ + private readonly ArchetypeQuery query; + + public int Count => query.Count; + + /// Obsolete. Renamed to . + [Obsolete($"Renamed to {nameof(Count)}")] [DebuggerBrowsable(DebuggerBrowsableState.Never)] + public int EntityCount => query.Count; + + public override string ToString() => query.GetQueryChunksString(); + + internal QueryChunks(ArchetypeQuery query) { + this.query = query; + } + + // --- IEnumerable<> + [ExcludeFromCodeCoverage] + IEnumerator> + IEnumerable>.GetEnumerator() => new ChunkEnumerator (query); + + // --- IEnumerable + [ExcludeFromCodeCoverage] + IEnumerator IEnumerable.GetEnumerator() => new ChunkEnumerator (query); + + public ChunkEnumerator GetEnumerator() => new (query); +} + +public struct ChunkEnumerator : IEnumerator> + where T1 : struct + where T2 : struct + where T3 : struct + where T4 : struct + where T5 : struct + where T6 : struct +{ + private readonly int structIndex1; // 4 + private readonly int structIndex2; // 4 + private readonly int structIndex3; // 4 + private readonly int structIndex4; // 4 + private readonly int structIndex5; // 4 + private readonly int structIndex6; // 4 + // + private readonly Archetypes archetypes; // 16 + // + private int archetypePos; // 4 + private Chunks chunks; // 104 + + + internal ChunkEnumerator(ArchetypeQuery query) + { + structIndex1 = query.signatureIndexes.T1; + structIndex2 = query.signatureIndexes.T2; + structIndex3 = query.signatureIndexes.T3; + structIndex4 = query.signatureIndexes.T4; + structIndex5 = query.signatureIndexes.T5; + structIndex6 = query.signatureIndexes.T6; + archetypes = query.GetArchetypes(); + archetypePos = -1; + } + + /// return Current by reference to avoid struct copy and enable mutation in library + public readonly Chunks Current => chunks; + + // --- IEnumerator + [ExcludeFromCodeCoverage] + public void Reset() { + archetypePos = -1; + chunks = default; + } + + [ExcludeFromCodeCoverage] + object IEnumerator.Current => chunks; + + // --- IEnumerator + public bool MoveNext() + { + Archetype archetype; + int count; + int start = 0; + var types = archetypes; + var pos = archetypePos; + if (types.chunkPositions != null) { + goto SingleEntity; + } + do { + if (pos >= types.last) { // last = length - 1 + archetypePos = pos; + return false; + } + archetype = types.array[++pos]; + count = archetype.entityCount; + } + while (count == 0); // skip archetypes without entities + SetChunks: + archetypePos = pos; + var heapMap = archetype.heapMap; + var chunks1 = (StructHeap)heapMap[structIndex1]; + var chunks2 = (StructHeap)heapMap[structIndex2]; + var chunks3 = (StructHeap)heapMap[structIndex3]; + var chunks4 = (StructHeap)heapMap[structIndex4]; + var chunks5 = (StructHeap)heapMap[structIndex5]; + var chunks6 = (StructHeap)heapMap[structIndex6]; + + var chunk1 = new Chunk(chunks1.components, count, start); + var chunk2 = new Chunk(chunks2.components, count, start); + var chunk3 = new Chunk(chunks3.components, count, start); + var chunk4 = new Chunk(chunks4.components, count, start); + var chunk5 = new Chunk(chunks5.components, count, start); + var chunk6 = new Chunk(chunks6.components, count, start); + var entities = new ChunkEntities(archetype, count, start); + chunks = new Chunks(chunk1, chunk2, chunk3, chunk4, chunk5, chunk6, entities); + return true; + SingleEntity: + if (pos >= types.last) { + return false; + } + pos++; + start = types.chunkPositions[pos]; + archetype = types.array [pos]; + count = 1; + goto SetChunks; + } + + // --- IDisposable + public void Dispose() { } +} diff --git a/src/ECS/Query/Arg.6/Query.cs b/src/ECS/Query/Arg.6/Query.cs new file mode 100644 index 000000000..344b42674 --- /dev/null +++ b/src/ECS/Query/Arg.6/Query.cs @@ -0,0 +1,99 @@ +// Copyright (c) Ullrich Praetz - https://github.com/friflo. All rights reserved. +// See LICENSE file in the project root for full license information. + +using System; + +// ReSharper disable once CheckNamespace +namespace Friflo.Engine.ECS; + +/// +/// Provide the state of an within . +/// +public delegate void ForEachEntity(ref T1 component1, ref T2 component2, ref T3 component3, ref T4 component4, ref T5 component5, ref T6 component, Entity entity) + where T1 : struct + where T2 : struct + where T3 : struct + where T4 : struct + where T5 : struct + where T6 : struct; + + +/// +/// A query instance use to retrieve the given component types. +/// See Example. +/// +public sealed class ArchetypeQuery : ArchetypeQuery + where T1 : struct + where T2 : struct + where T3 : struct + where T4 : struct + where T5 : struct + where T6 : struct +{ + /// + public new ArchetypeQuery AllTags (in Tags tags) { SetHasAllTags(tags); return this; } + /// + public new ArchetypeQuery AnyTags (in Tags tags) { SetHasAnyTags(tags); return this; } + /// + public new ArchetypeQuery WithDisabled () { SetWithDisabled(); return this; } + /// + public new ArchetypeQuery WithoutAllTags(in Tags tags) { SetWithoutAllTags(tags); return this; } + /// + public new ArchetypeQuery WithoutAnyTags(in Tags tags) { SetWithoutAnyTags(tags); return this; } + + /// + public new ArchetypeQuery AllComponents (in ComponentTypes componentTypes) { SetHasAllComponents(componentTypes); return this; } + /// + public new ArchetypeQuery AnyComponents (in ComponentTypes componentTypes) { SetHasAnyComponents(componentTypes); return this; } + /// + public new ArchetypeQuery WithoutAllComponents(in ComponentTypes componentTypes) { SetWithoutAllComponents(componentTypes); return this; } + /// + public new ArchetypeQuery WithoutAnyComponents(in ComponentTypes componentTypes) { SetWithoutAnyComponents(componentTypes); return this; } + + /// + public new ArchetypeQuery HasValue (TValue value) where TComponent : struct, IIndexedComponent + { base.HasValue (value); return this; } + + /// + public new ArchetypeQuery ValueInRange(TValue min, TValue max) where TComponent : struct, IIndexedComponent where TValue : IComparable + { base.ValueInRange(min, max); return this; } + + /// + public new ArchetypeQuery FreezeFilter() { SetFreezeFilter(); return this; } + + internal ArchetypeQuery(EntityStoreBase store, in Signature signature, QueryFilter filter) + : base(store, signature.signatureIndexes, filter, null) { + } + + /// + /// Return the 's storing the components and entities of an .
+ /// See Example. + ///
+ public QueryChunks Chunks => new (this); + + /// + /// Returns a that enables query execution. + /// + public QueryJob ForEach(Action, Chunk, Chunk, Chunk, Chunk, Chunk, ChunkEntities> action) => new (this, action); + + /// + /// Executes the given for each entity in the query result. + /// + public void ForEachEntity(ForEachEntity lambda) + { + var store = Store; + foreach (var (chunk1, chunk2, chunk3, chunk4, chunk5, chunk6, entities) in Chunks) + { + var span1 = chunk1.Span; + var span2 = chunk2.Span; + var span3 = chunk3.Span; + var span4 = chunk4.Span; + var span5 = chunk5.Span; + var span6 = chunk6.Span; + var ids = entities.Ids; + for (int n = 0; n < chunk1.Length; n++) { + lambda(ref span1[n], ref span2[n], ref span3[n], ref span4[n], ref span5[n], ref span6[n], new Entity(store, ids[n])); + } + } + } +} diff --git a/src/ECS/Query/Arg.6/QueryJob.cs b/src/ECS/Query/Arg.6/QueryJob.cs new file mode 100644 index 000000000..812a75759 --- /dev/null +++ b/src/ECS/Query/Arg.6/QueryJob.cs @@ -0,0 +1,120 @@ +// Copyright (c) Ullrich Praetz - https://github.com/friflo. All rights reserved. +// See LICENSE file in the project root for full license information. + +using System; +using static System.Diagnostics.DebuggerBrowsableState; +using Browse = System.Diagnostics.DebuggerBrowsableAttribute; + +// ReSharper disable StaticMemberInGenericType +// ReSharper disable CoVariantArrayConversion +// ReSharper disable once CheckNamespace +namespace Friflo.Engine.ECS; + +/// +/// Enables query execution returning the specified components. +/// See Example. +/// +public sealed class QueryJob : QueryJob + where T1 : struct + where T2 : struct + where T3 : struct + where T4 : struct + where T5 : struct + where T6 : struct +{ + internal QueryChunks Chunks => new (query); // only for debugger + internal QueryEntities Entities => query.Entities; // only for debugger + public override string ToString() => query.GetQueryJobString(); + + [Browse(Never)] + private readonly ArchetypeQuery query; // 8 + private readonly Action, Chunk, Chunk, Chunk, Chunk, Chunk, ChunkEntities> action; // 8 + [Browse(Never)] + private QueryJobTask[] jobTasks; // 8 + + + private class QueryJobTask : JobTask { + internal Action, Chunk, Chunk, Chunk, Chunk, Chunk, ChunkEntities> action; + internal Chunks chunks; + + internal override void ExecuteTask() => action(chunks.Chunk1, chunks.Chunk2, chunks.Chunk3, chunks.Chunk4, chunks.Chunk5, chunks.Chunk6, chunks.Entities); + } + + internal QueryJob( + ArchetypeQuery query, + Action, Chunk, Chunk, Chunk, Chunk, Chunk, ChunkEntities> action) + { + this.query = query; + this.action = action; + jobRunner = query.Store.JobRunner; + } + + public override void Run() + { + foreach (Chunks chunk in query.Chunks) { + action(chunk.Chunk1, chunk.Chunk2, chunk.Chunk3, chunk.Chunk4, chunk.Chunk5, chunk.Chunk6, chunk.Entities); + } + } + + /// Execute the query. + /// See Example..
+ /// All chunks having at least * + /// components are executed . + ///
+ public override void RunParallel() + { + if (jobRunner == null) throw JobRunnerIsNullException(); + var taskCount = jobRunner.workerCount + 1; + + foreach (Chunks chunks in query.Chunks) + { + var chunkLength = chunks.Length; + if (ExecuteSequential(taskCount, chunkLength)) { + action(chunks.Chunk1, chunks.Chunk2, chunks.Chunk3, chunks.Chunk4, chunks.Chunk5, chunks.Chunk6, chunks.Entities); + continue; + } + var tasks = jobTasks; + if (tasks == null || tasks.Length < taskCount) { + tasks = jobTasks = new QueryJobTask[taskCount]; + for (int n = 0; n < taskCount; n++) { + tasks[n] = new QueryJobTask { action = action }; + } + } + var sectionSize = GetSectionSize(chunkLength, taskCount, Multiple); + var start = 0; + for (int taskIndex = 0; taskIndex < taskCount; taskIndex++) + { + var length = GetSectionLength (chunkLength, start, sectionSize); + if (length > 0) { + tasks[taskIndex].chunks = new Chunks(chunks, start, length, taskIndex); + start += sectionSize; + continue; + } + for (; taskIndex < taskCount; taskIndex++) { + tasks[taskIndex].chunks = new Chunks(chunks.Entities, taskIndex); + } + break; + } + jobRunner.ExecuteJob(this, tasks); + } + } + + public override int ParallelComponentMultiple => Multiple; + private static readonly int Multiple = GetMultiple(); + + private static int GetMultiple() + { + int lcm1 = StructPadding.ComponentMultiple; + int lcm2 = StructPadding.ComponentMultiple; + int lcm3 = StructPadding.ComponentMultiple; + int lcm4 = StructPadding.ComponentMultiple; + int lcm5 = StructPadding.ComponentMultiple; + int lcm6 = StructPadding.ComponentMultiple; + + int lcm12 = LeastComponentMultiple(lcm1, lcm2); + int lcm34 = LeastComponentMultiple(lcm3, lcm4); + int lcm1234 = LeastComponentMultiple(lcm12, lcm34); + int lcm12345 = LeastComponentMultiple(lcm1234, lcm5); + return LeastComponentMultiple(lcm12345, lcm6); + } +} diff --git a/src/ECS/Query/Arg.7/IEach.cs b/src/ECS/Query/Arg.7/IEach.cs new file mode 100644 index 000000000..7e4c53cfb --- /dev/null +++ b/src/ECS/Query/Arg.7/IEach.cs @@ -0,0 +1,16 @@ +// 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; + +public interface IEach +{ + void Execute(ref T1 c1, ref T2 c2, ref T3 c3, ref T4 c4, ref T5 c5, ref T6 c6, ref T7 c7); +} + +public interface IEachEntity +{ + void Execute(ref T1 c1, ref T2 c2, ref T3 c3, ref T4 c4, ref T5 c5, ref T6 c6, ref T7 c7, int id); +} \ No newline at end of file diff --git a/src/ECS/Query/Arg.7/Query.Chunks.cs b/src/ECS/Query/Arg.7/Query.Chunks.cs new file mode 100644 index 000000000..0c0a0195c --- /dev/null +++ b/src/ECS/Query/Arg.7/Query.Chunks.cs @@ -0,0 +1,222 @@ +// Copyright (c) Ullrich Praetz - https://github.com/friflo. All rights reserved. +// See LICENSE file in the project root for full license information. + + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; + +// ReSharper disable InconsistentNaming +// ReSharper disable once CheckNamespace +namespace Friflo.Engine.ECS; + +/// +/// Contains the components returned by a component query. +/// See Example. +/// +public readonly struct Chunks + where T1 : struct + where T2 : struct + where T3 : struct + where T4 : struct + where T5 : struct + where T6 : struct + where T7 : struct +{ + public int Length => Chunk1.Length; + public readonly Chunk Chunk1; // 16 + public readonly Chunk Chunk2; // 16 + public readonly Chunk Chunk3; // 16 + public readonly Chunk Chunk4; // 16 + public readonly Chunk Chunk5; // 16 + public readonly Chunk Chunk6; // 16 + public readonly Chunk Chunk7; // 16 + public readonly ChunkEntities Entities; // 32 + + public override string ToString() => Entities.GetChunksString(); + + internal Chunks(Chunk chunk1, Chunk chunk2, Chunk chunk3, Chunk chunk4, Chunk chunk5, Chunk chunk6, Chunk chunk7, in ChunkEntities entities) { + Chunk1 = chunk1; + Chunk2 = chunk2; + Chunk3 = chunk3; + Chunk4 = chunk4; + Chunk5 = chunk5; + Chunk6 = chunk6; + Chunk7 = chunk7; + Entities = entities; + } + + internal Chunks(in Chunks chunks, int start, int length, int taskIndex) { + Chunk1 = new Chunk (chunks.Chunk1, start, length); + Chunk2 = new Chunk (chunks.Chunk2, start, length); + Chunk3 = new Chunk (chunks.Chunk3, start, length); + Chunk4 = new Chunk (chunks.Chunk4, start, length); + Chunk5 = new Chunk (chunks.Chunk5, start, length); + Chunk6 = new Chunk (chunks.Chunk6, start, length); + Chunk7 = new Chunk (chunks.Chunk7, start, length); + Entities = new ChunkEntities(chunks.Entities, start, length, taskIndex); + } + + internal Chunks(in ChunkEntities entities, int taskIndex) { + Entities = new ChunkEntities(entities, taskIndex); + } + + public void Deconstruct(out Chunk chunk1, out Chunk chunk2, out Chunk chunk3, out Chunk chunk4, out Chunk chunk5, out Chunk chunk6, out Chunk chunk7, out ChunkEntities entities) { + chunk1 = Chunk1; + chunk2 = Chunk2; + chunk3 = Chunk3; + chunk4 = Chunk4; + chunk5 = Chunk5; + chunk6 = Chunk6; + chunk7 = Chunk7; + entities = Entities; + } +} + +/// +/// Contains the component chunks returned by a component query. +/// See Example. +/// +public readonly struct QueryChunks : IEnumerable > + where T1 : struct + where T2 : struct + where T3 : struct + where T4 : struct + where T5 : struct + where T6 : struct + where T7 : struct +{ + private readonly ArchetypeQuery query; + + public int Count => query.Count; + + /// Obsolete. Renamed to . + [Obsolete($"Renamed to {nameof(Count)}")] [DebuggerBrowsable(DebuggerBrowsableState.Never)] + public int EntityCount => query.Count; + + public override string ToString() => query.GetQueryChunksString(); + + internal QueryChunks(ArchetypeQuery query) { + this.query = query; + } + + // --- IEnumerable<> + [ExcludeFromCodeCoverage] + IEnumerator> + IEnumerable>.GetEnumerator() => new ChunkEnumerator (query); + + // --- IEnumerable + [ExcludeFromCodeCoverage] + IEnumerator IEnumerable.GetEnumerator() => new ChunkEnumerator (query); + + public ChunkEnumerator GetEnumerator() => new (query); +} + + +public struct ChunkEnumerator : IEnumerator> + where T1 : struct + where T2 : struct + where T3 : struct + where T4 : struct + where T5 : struct + where T6 : struct + where T7 : struct +{ + private readonly int structIndex1; // 4 + private readonly int structIndex2; // 4 + private readonly int structIndex3; // 4 + private readonly int structIndex4; // 4 + private readonly int structIndex5; // 4 + private readonly int structIndex6; // 4 + private readonly int structIndex7; // 4 + // + private readonly Archetypes archetypes; // 16 + // + private int archetypePos; // 4 + private Chunks chunks; // 104 + + + internal ChunkEnumerator(ArchetypeQuery query) + { + structIndex1 = query.signatureIndexes.T1; + structIndex2 = query.signatureIndexes.T2; + structIndex3 = query.signatureIndexes.T3; + structIndex4 = query.signatureIndexes.T4; + structIndex5 = query.signatureIndexes.T5; + structIndex6 = query.signatureIndexes.T6; + structIndex7 = query.signatureIndexes.T7; + + archetypes = query.GetArchetypes(); + archetypePos = -1; + } + + /// return Current by reference to avoid struct copy and enable mutation in library + public readonly Chunks Current => chunks; + + // --- IEnumerator + [ExcludeFromCodeCoverage] + public void Reset() { + archetypePos = -1; + chunks = default; + } + + [ExcludeFromCodeCoverage] + object IEnumerator.Current => chunks; + + // --- IEnumerator + public bool MoveNext() + { + Archetype archetype; + int count; + int start = 0; + var types = archetypes; + var pos = archetypePos; + if (types.chunkPositions != null) { + goto SingleEntity; + } + do { + if (pos >= types.last) { // last = length - 1 + archetypePos = pos; + return false; + } + archetype = types.array[++pos]; + count = archetype.entityCount; + } + while (count == 0); // skip archetypes without entities + SetChunks: + archetypePos = pos; + var heapMap = archetype.heapMap; + var chunks1 = (StructHeap)heapMap[structIndex1]; + var chunks2 = (StructHeap)heapMap[structIndex2]; + var chunks3 = (StructHeap)heapMap[structIndex3]; + var chunks4 = (StructHeap)heapMap[structIndex4]; + var chunks5 = (StructHeap)heapMap[structIndex5]; + var chunks6 = (StructHeap)heapMap[structIndex6]; + var chunks7 = (StructHeap)heapMap[structIndex7]; + + var chunk1 = new Chunk(chunks1.components, count, start); + var chunk2 = new Chunk(chunks2.components, count, start); + var chunk3 = new Chunk(chunks3.components, count, start); + var chunk4 = new Chunk(chunks4.components, count, start); + var chunk5 = new Chunk(chunks5.components, count, start); + var chunk6 = new Chunk(chunks6.components, count, start); + var chunk7 = new Chunk(chunks7.components, count, start); + var entities = new ChunkEntities(archetype, count, start); + chunks = new Chunks(chunk1, chunk2, chunk3, chunk4, chunk5, chunk6, chunk7, entities); + return true; + SingleEntity: + if (pos >= types.last) { + return false; + } + pos++; + start = types.chunkPositions[pos]; + archetype = types.array [pos]; + count = 1; + goto SetChunks; + } + + // --- IDisposable + public void Dispose() { } +} diff --git a/src/ECS/Query/Arg.7/Query.cs b/src/ECS/Query/Arg.7/Query.cs new file mode 100644 index 000000000..c22ee70bc --- /dev/null +++ b/src/ECS/Query/Arg.7/Query.cs @@ -0,0 +1,103 @@ +// Copyright (c) Ullrich Praetz - https://github.com/friflo. All rights reserved. +// See LICENSE file in the project root for full license information. + +using System; + +// ReSharper disable once CheckNamespace +namespace Friflo.Engine.ECS; + +/// +/// Provide the state of an within . +/// +public delegate void ForEachEntity(ref T1 component1, ref T2 component2, ref T3 component3, ref T4 component4, ref T5 component5, ref T6 component6, ref T7 component7, Entity entity) + where T1 : struct + where T2 : struct + where T3 : struct + where T4 : struct + where T5 : struct + where T6 : struct + where T7 : struct; + + +/// +/// A query instance use to retrieve the given component types. +/// See Example. +/// +public sealed class ArchetypeQuery : ArchetypeQuery + where T1 : struct + where T2 : struct + where T3 : struct + where T4 : struct + where T5 : struct + where T6 : struct + where T7 : struct +{ + /// + public new ArchetypeQuery AllTags (in Tags tags) { SetHasAllTags(tags); return this; } + /// + public new ArchetypeQuery AnyTags (in Tags tags) { SetHasAnyTags(tags); return this; } + /// + public new ArchetypeQuery WithDisabled () { SetWithDisabled(); return this; } + /// + public new ArchetypeQuery WithoutAllTags(in Tags tags) { SetWithoutAllTags(tags); return this; } + /// + public new ArchetypeQuery WithoutAnyTags(in Tags tags) { SetWithoutAnyTags(tags); return this; } + + /// + public new ArchetypeQuery AllComponents (in ComponentTypes componentTypes) { SetHasAllComponents(componentTypes); return this; } + /// + public new ArchetypeQuery AnyComponents (in ComponentTypes componentTypes) { SetHasAnyComponents(componentTypes); return this; } + /// + public new ArchetypeQuery WithoutAllComponents(in ComponentTypes componentTypes) { SetWithoutAllComponents(componentTypes); return this; } + /// + public new ArchetypeQuery WithoutAnyComponents(in ComponentTypes componentTypes) { SetWithoutAnyComponents(componentTypes); return this; } + + /// + public new ArchetypeQuery HasValue (TValue value) where TComponent : struct, IIndexedComponent + { base.HasValue (value); return this; } + + /// + public new ArchetypeQuery ValueInRange(TValue min, TValue max) where TComponent : struct, IIndexedComponent where TValue : IComparable + { base.ValueInRange(min, max); return this; } + + /// + public new ArchetypeQuery FreezeFilter() { SetFreezeFilter(); return this; } + + internal ArchetypeQuery(EntityStoreBase store, in Signature signature, QueryFilter filter) + : base(store, signature.signatureIndexes, filter, null) { + } + + /// + /// Return the 's storing the components and entities of an .
+ /// See Example. + ///
+ public QueryChunks Chunks => new (this); + + /// + /// Returns a that enables query execution. + /// + public QueryJob ForEach(Action, Chunk, Chunk, Chunk, Chunk, Chunk, Chunk, ChunkEntities> action) => new (this, action); + + /// + /// Executes the given for each entity in the query result. + /// + public void ForEachEntity(ForEachEntity lambda) + { + var store = Store; + foreach (var (chunk1, chunk2, chunk3, chunk4, chunk5, chunk6, chunk7, entities) in Chunks) + { + var span1 = chunk1.Span; + var span2 = chunk2.Span; + var span3 = chunk3.Span; + var span4 = chunk4.Span; + var span5 = chunk5.Span; + var span6 = chunk6.Span; + var span7 = chunk7.Span; + + var ids = entities.Ids; + for (int n = 0; n < chunk1.Length; n++) { + lambda(ref span1[n], ref span2[n], ref span3[n], ref span4[n], ref span5[n], ref span6[n], ref span7[n], new Entity(store, ids[n])); + } + } + } +} diff --git a/src/ECS/Query/Arg.7/QueryJob.cs b/src/ECS/Query/Arg.7/QueryJob.cs new file mode 100644 index 000000000..f7dbe5366 --- /dev/null +++ b/src/ECS/Query/Arg.7/QueryJob.cs @@ -0,0 +1,123 @@ +// Copyright (c) Ullrich Praetz - https://github.com/friflo. All rights reserved. +// See LICENSE file in the project root for full license information. + +using System; +using static System.Diagnostics.DebuggerBrowsableState; +using Browse = System.Diagnostics.DebuggerBrowsableAttribute; + +// ReSharper disable StaticMemberInGenericType +// ReSharper disable CoVariantArrayConversion +// ReSharper disable once CheckNamespace +namespace Friflo.Engine.ECS; + +/// +/// Enables query execution returning the specified components. +/// See Example. +/// +public sealed class QueryJob : QueryJob + where T1 : struct + where T2 : struct + where T3 : struct + where T4 : struct + where T5 : struct + where T6 : struct + where T7 : struct +{ + internal QueryChunks Chunks => new (query); // only for debugger + internal QueryEntities Entities => query.Entities; // only for debugger + public override string ToString() => query.GetQueryJobString(); + + [Browse(Never)] + private readonly ArchetypeQuery query; // 8 + private readonly Action, Chunk, Chunk, Chunk, Chunk, Chunk, Chunk, ChunkEntities> action; // 8 + [Browse(Never)] + private QueryJobTask[] jobTasks; // 8 + + + private class QueryJobTask : JobTask { + internal Action, Chunk, Chunk, Chunk, Chunk, Chunk, Chunk, ChunkEntities> action; + internal Chunks chunks; + + internal override void ExecuteTask() => action(chunks.Chunk1, chunks.Chunk2, chunks.Chunk3, chunks.Chunk4, chunks.Chunk5, chunks.Chunk6, chunks.Chunk7, chunks.Entities); + } + + internal QueryJob( + ArchetypeQuery query, + Action, Chunk, Chunk, Chunk, Chunk, Chunk, Chunk, ChunkEntities> action) + { + this.query = query; + this.action = action; + jobRunner = query.Store.JobRunner; + } + + public override void Run() + { + foreach (Chunks chunk in query.Chunks) { + action(chunk.Chunk1, chunk.Chunk2, chunk.Chunk3, chunk.Chunk4, chunk.Chunk5, chunk.Chunk6, chunk.Chunk7, chunk.Entities); + } + } + + /// Execute the query. + /// See Example..
+ /// All chunks having at least * + /// components are executed . + ///
+ public override void RunParallel() + { + if (jobRunner == null) throw JobRunnerIsNullException(); + var taskCount = jobRunner.workerCount + 1; + + foreach (Chunks chunks in query.Chunks) + { + var chunkLength = chunks.Length; + if (ExecuteSequential(taskCount, chunkLength)) { + action(chunks.Chunk1, chunks.Chunk2, chunks.Chunk3, chunks.Chunk4, chunks.Chunk5, chunks.Chunk6, chunks.Chunk7, chunks.Entities); + continue; + } + var tasks = jobTasks; + if (tasks == null || tasks.Length < taskCount) { + tasks = jobTasks = new QueryJobTask[taskCount]; + for (int n = 0; n < taskCount; n++) { + tasks[n] = new QueryJobTask { action = action }; + } + } + var sectionSize = GetSectionSize(chunkLength, taskCount, Multiple); + var start = 0; + for (int taskIndex = 0; taskIndex < taskCount; taskIndex++) + { + var length = GetSectionLength (chunkLength, start, sectionSize); + if (length > 0) { + tasks[taskIndex].chunks = new Chunks(chunks, start, length, taskIndex); + start += sectionSize; + continue; + } + for (; taskIndex < taskCount; taskIndex++) { + tasks[taskIndex].chunks = new Chunks(chunks.Entities, taskIndex); + } + break; + } + jobRunner.ExecuteJob(this, tasks); + } + } + + public override int ParallelComponentMultiple => Multiple; + private static readonly int Multiple = GetMultiple(); + + private static int GetMultiple() + { + int lcm1 = StructPadding.ComponentMultiple; + int lcm2 = StructPadding.ComponentMultiple; + int lcm3 = StructPadding.ComponentMultiple; + int lcm4 = StructPadding.ComponentMultiple; + int lcm5 = StructPadding.ComponentMultiple; + int lcm6 = StructPadding.ComponentMultiple; + int lcm7 = StructPadding.ComponentMultiple; + + int lcm12 = LeastComponentMultiple(lcm1, lcm2); + int lcm34 = LeastComponentMultiple(lcm3, lcm4); + int lcm1234 = LeastComponentMultiple(lcm12, lcm34); + int lcm12345 = LeastComponentMultiple(lcm1234, lcm5); + int lcm123456 = LeastComponentMultiple(lcm12345, lcm6); + return LeastComponentMultiple(lcm123456, lcm7); + } +} diff --git a/src/ECS/Query/EntityStore.Query.cs b/src/ECS/Query/EntityStore.Query.cs index dd875f906..354733f55 100644 --- a/src/ECS/Query/EntityStore.Query.cs +++ b/src/ECS/Query/EntityStore.Query.cs @@ -255,4 +255,105 @@ public ArchetypeQuery Query (QueryFilter return result; } #endregion + +#region args - 6 + /// + /// Create a reusable for given component . + /// + public ArchetypeQuery Query (Signature signature) + where T1: struct, IComponent + where T2: struct, IComponent + where T3: struct, IComponent + where T4: struct, IComponent + where T5: struct, IComponent + where T6: struct, IComponent + { + var result = new ArchetypeQuery(this, signature, null); + return result; + } + + /// + /// Create a reusable for the given component types.
+ /// See Example. + ///
+ public ArchetypeQuery Query () + where T1: struct, IComponent + where T2: struct, IComponent + where T3: struct, IComponent + where T4: struct, IComponent + where T5: struct, IComponent + where T6: struct, IComponent + { + var result = new ArchetypeQuery(this, Signature.Get(), null); + return result; + } + + /// + /// Create a reusable with given query .
+ /// The filter attached to the query can be modified subsequently. + ///
+ public ArchetypeQuery Query (QueryFilter filter) + where T1 : struct, IComponent + where T2 : struct, IComponent + where T3 : struct, IComponent + where T4 : struct, IComponent + where T5 : struct, IComponent + where T6 : struct, IComponent + { + var result = new ArchetypeQuery(this, Signature.Get(), filter); + return result; + } +#endregion + +#region args - 7 + /// + /// Create a reusable for given component . + /// + public ArchetypeQuery Query (Signature signature) + where T1: struct, IComponent + where T2: struct, IComponent + where T3: struct, IComponent + where T4: struct, IComponent + where T5: struct, IComponent + where T6: struct, IComponent + where T7: struct, IComponent + { + var result = new ArchetypeQuery(this, signature, null); + return result; + } + + /// + /// Create a reusable for the given component types.
+ /// See Example. + ///
+ public ArchetypeQuery Query () + where T1: struct, IComponent + where T2: struct, IComponent + where T3: struct, IComponent + where T4: struct, IComponent + where T5: struct, IComponent + where T6: struct, IComponent + where T7: struct, IComponent + { + var result = new ArchetypeQuery(this, Signature.Get(), null); + return result; + } + + /// + /// Create a reusable with given query .
+ /// The filter attached to the query can be modified subsequently. + ///
+ public ArchetypeQuery Query (QueryFilter filter) + where T1 : struct, IComponent + where T2 : struct, IComponent + where T3 : struct, IComponent + where T4 : struct, IComponent + where T5 : struct, IComponent + where T6 : struct, IComponent + where T7 : struct, IComponent + { + var result = new ArchetypeQuery(this, Signature.Get(), filter); + return result; + } +#endregion } diff --git a/src/ECS/Query/Signature.Indexes.cs b/src/ECS/Query/Signature.Indexes.cs index 6c3f95605..b993b3168 100644 --- a/src/ECS/Query/Signature.Indexes.cs +++ b/src/ECS/Query/Signature.Indexes.cs @@ -23,8 +23,8 @@ internal readonly struct SignatureIndexes internal readonly byte T3; // 1 internal readonly byte T4; // 1 internal readonly byte T5; // 1 - private readonly byte T6; // 1 - private readonly byte T7; // 1 + internal readonly byte T6; // 1 + internal readonly byte T7; // 1 private readonly byte T8; // 1 private readonly byte T9; // 1 private readonly byte T10; // 1 diff --git a/src/ECS/Query/Signature.cs b/src/ECS/Query/Signature.cs index 6d86a5669..a7264286d 100644 --- a/src/ECS/Query/Signature.cs +++ b/src/ECS/Query/Signature.cs @@ -135,6 +135,60 @@ public static Signature Get() return new Signature(indexes); } + /// + /// Returns a query containing the specified component types.
+ ///
+ /// + /// It can be used to query entities with the specified component types with .Query() methods. + /// + public static Signature Get() + where T1 : struct, IComponent + where T2 : struct, IComponent + where T3 : struct, IComponent + where T4 : struct, IComponent + where T5 : struct, IComponent + where T6 : struct, IComponent + { + var schema = EntityStoreBase.Static.EntitySchema; + var indexes = new SignatureIndexes( + T1: schema.CheckStructIndex(typeof(T1), StructInfo.Index), + T2: schema.CheckStructIndex(typeof(T2), StructInfo.Index), + T3: schema.CheckStructIndex(typeof(T3), StructInfo.Index), + T4: schema.CheckStructIndex(typeof(T4), StructInfo.Index), + T5: schema.CheckStructIndex(typeof(T5), StructInfo.Index), + T6: schema.CheckStructIndex(typeof(T6), StructInfo.Index) + ); + return new Signature(indexes); + } + + /// + /// Returns a query containing the specified component types.
+ ///
+ /// + /// It can be used to query entities with the specified component types with .Query() methods. + /// + public static Signature Get() + where T1 : struct, IComponent + where T2 : struct, IComponent + where T3 : struct, IComponent + where T4 : struct, IComponent + where T5 : struct, IComponent + where T6 : struct, IComponent + where T7 : struct, IComponent + { + var schema = EntityStoreBase.Static.EntitySchema; + var indexes = new SignatureIndexes( + T1: schema.CheckStructIndex(typeof(T1), StructInfo.Index), + T2: schema.CheckStructIndex(typeof(T2), StructInfo.Index), + T3: schema.CheckStructIndex(typeof(T3), StructInfo.Index), + T4: schema.CheckStructIndex(typeof(T4), StructInfo.Index), + T5: schema.CheckStructIndex(typeof(T5), StructInfo.Index), + T6: schema.CheckStructIndex(typeof(T6), StructInfo.Index), + T7: schema.CheckStructIndex(typeof(T7), StructInfo.Index) + ); + return new Signature(indexes); + } + internal static string GetSignatureString(in SignatureIndexes indexes) { return indexes.GetString("Signature: "); } @@ -259,4 +313,145 @@ internal Signature(in SignatureIndexes signatureIndexes) { } } +/// +/// A Signature used to create a query using with five components. +/// +public readonly struct Signature + where T1 : struct + where T2 : struct + where T3 : struct + where T4 : struct + where T5 : struct + where T6 : struct +{ + /// Return the component types of the query signature. + public ComponentTypes ComponentTypes => new ComponentTypes(signatureIndexes); + + /// Gets the number component types of the query signature. + [Browse(Never)] public int ComponentCount => signatureIndexes.length; + + [Browse(Never)] internal readonly SignatureIndexes signatureIndexes; // 16 + + public override string ToString() => Signature.GetSignatureString(signatureIndexes); + + internal Signature(in SignatureIndexes signatureIndexes) { + this.signatureIndexes = signatureIndexes; + } +} + +/// +/// A Signature used to create a query using with five components. +/// +public readonly struct Signature + where T1 : struct + where T2 : struct + where T3 : struct + where T4 : struct + where T5 : struct + where T6 : struct + where T7 : struct +{ + /// Return the component types of the query signature. + public ComponentTypes ComponentTypes => new ComponentTypes(signatureIndexes); + + /// Gets the number component types of the query signature. + [Browse(Never)] public int ComponentCount => signatureIndexes.length; + + [Browse(Never)] internal readonly SignatureIndexes signatureIndexes; // 16 + + public override string ToString() => Signature.GetSignatureString(signatureIndexes); + + internal Signature(in SignatureIndexes signatureIndexes) { + this.signatureIndexes = signatureIndexes; + } +} + +/// +/// A Signature used to create a query using with five components. +/// +public readonly struct Signature + where T1 : struct + where T2 : struct + where T3 : struct + where T4 : struct + where T5 : struct + where T6 : struct + where T7 : struct + where T8 : struct +{ + /// Return the component types of the query signature. + public ComponentTypes ComponentTypes => new ComponentTypes(signatureIndexes); + + /// Gets the number component types of the query signature. + [Browse(Never)] public int ComponentCount => signatureIndexes.length; + + [Browse(Never)] internal readonly SignatureIndexes signatureIndexes; // 16 + + public override string ToString() => Signature.GetSignatureString(signatureIndexes); + + internal Signature(in SignatureIndexes signatureIndexes) { + this.signatureIndexes = signatureIndexes; + } +} + + +/// +/// A Signature used to create a query using with five components. +/// +public readonly struct Signature + where T1 : struct + where T2 : struct + where T3 : struct + where T4 : struct + where T5 : struct + where T6 : struct + where T7 : struct + where T8 : struct + where T9 : struct +{ + /// Return the component types of the query signature. + public ComponentTypes ComponentTypes => new ComponentTypes(signatureIndexes); + + /// Gets the number component types of the query signature. + [Browse(Never)] public int ComponentCount => signatureIndexes.length; + + [Browse(Never)] internal readonly SignatureIndexes signatureIndexes; // 16 + + public override string ToString() => Signature.GetSignatureString(signatureIndexes); + + internal Signature(in SignatureIndexes signatureIndexes) { + this.signatureIndexes = signatureIndexes; + } +} + +/// +/// A Signature used to create a query using with five components. +/// +public readonly struct Signature + where T1 : struct + where T2 : struct + where T3 : struct + where T4 : struct + where T5 : struct + where T6 : struct + where T7 : struct + where T8 : struct + where T9 : struct + where T10 : struct +{ + /// Return the component types of the query signature. + public ComponentTypes ComponentTypes => new ComponentTypes(signatureIndexes); + + /// Gets the number component types of the query signature. + [Browse(Never)] public int ComponentCount => signatureIndexes.length; + + [Browse(Never)] internal readonly SignatureIndexes signatureIndexes; // 16 + + public override string ToString() => Signature.GetSignatureString(signatureIndexes); + + internal Signature(in SignatureIndexes signatureIndexes) { + this.signatureIndexes = signatureIndexes; + } +} + #endregion diff --git a/src/ECS/Systems/Query/Arg.6.cs b/src/ECS/Systems/Query/Arg.6.cs new file mode 100644 index 000000000..0f1907e61 --- /dev/null +++ b/src/ECS/Systems/Query/Arg.6.cs @@ -0,0 +1,41 @@ +// Copyright (c) Ullrich Praetz - https://github.com/friflo. All rights reserved. +// See LICENSE file in the project root for full license information. + +using static System.Diagnostics.DebuggerBrowsableState; +using Browse = System.Diagnostics.DebuggerBrowsableAttribute; +// Hard Rule! file must not have any dependency a to a specific game engine. E.g. Unity, Godot, Monogame, ... + +// ReSharper disable ConvertToPrimaryConstructor +// ReSharper disable ConvertToAutoPropertyWithPrivateSetter +// ReSharper disable once CheckNamespace +namespace Friflo.Engine.ECS.Systems; + +/// +/// A query system returning entities with the specified component types via its property. +/// +public abstract class QuerySystem : QuerySystemBase + where T1 : struct, IComponent + where T2 : struct, IComponent + where T3 : struct, IComponent + where T4 : struct, IComponent + where T5 : struct, IComponent + where T6 : struct, IComponent +{ + /// Return all entities matching the . + protected ArchetypeQuery Query => query; + + public override string ToString() => GetString(Signature.Get().signatureIndexes); + +#region fields + [Browse(Never)] private ArchetypeQuery query; + #endregion + + protected QuerySystem() : base (Generic.ComponentTypes) { } + + internal override void SetQuery(ArchetypeQuery query) { this.query = (ArchetypeQuery)query; } + + internal override ArchetypeQuery CreateQuery(EntityStore store) { + return store.Query(Filter); + } + +} diff --git a/src/ECS/Systems/Query/Arg.7.cs b/src/ECS/Systems/Query/Arg.7.cs new file mode 100644 index 000000000..facce2eac --- /dev/null +++ b/src/ECS/Systems/Query/Arg.7.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. + +using static System.Diagnostics.DebuggerBrowsableState; +using Browse = System.Diagnostics.DebuggerBrowsableAttribute; +// Hard Rule! file must not have any dependency a to a specific game engine. E.g. Unity, Godot, Monogame, ... + +// ReSharper disable ConvertToPrimaryConstructor +// ReSharper disable ConvertToAutoPropertyWithPrivateSetter +// ReSharper disable once CheckNamespace +namespace Friflo.Engine.ECS.Systems; + +/// +/// A query system returning entities with the specified component types via its property. +/// +public abstract class QuerySystem : QuerySystemBase + where T1 : struct, IComponent + where T2 : struct, IComponent + where T3 : struct, IComponent + where T4 : struct, IComponent + where T5 : struct, IComponent + where T6 : struct, IComponent + where T7 : struct, IComponent +{ + /// Return all entities matching the . + protected ArchetypeQuery Query => query; + + public override string ToString() => GetString(Signature.Get().signatureIndexes); + +#region fields + [Browse(Never)] private ArchetypeQuery query; + #endregion + + protected QuerySystem() : base (Generic.ComponentTypes) { } + + internal override void SetQuery(ArchetypeQuery query) { this.query = (ArchetypeQuery)query; } + + internal override ArchetypeQuery CreateQuery(EntityStore store) { + return store.Query(Filter); + } + +} diff --git a/src/Tests-internal/ECS/Test_QueryJob.cs b/src/Tests-internal/ECS/Test_QueryJob.cs index 3556f58cf..8cd14a2fe 100644 --- a/src/Tests-internal/ECS/Test_QueryJob.cs +++ b/src/Tests-internal/ECS/Test_QueryJob.cs @@ -24,7 +24,7 @@ private static void AssertMissingRunner(QueryJob job) public static void Test_QueryJob_ToString() { var store = new EntityStore(PidType.UsePidAsId); - var types = ComponentTypes.Get(); + var types = ComponentTypes.Get(); var archetype = store.GetArchetype(types); for (int n = 0; n < 32; n++) { archetype.CreateEntity(); @@ -59,7 +59,20 @@ public static void Test_QueryJob_ToString() AreEqual(32, job.Entities.Count); AreEqual("QueryJob [MyComponent1, MyComponent2, Position, Rotation, Scale3]", job.ToString()); AssertMissingRunner(job); + } { + var job = store.Query().ForEach((_, _, _, _, _, _, _) => { }); + AreEqual(32, job.Chunks.Count); + AreEqual(32, job.Entities.Count); + AreEqual("QueryJob [MyComponent1, MyComponent2, MyComponent3, Position, Rotation, Scale3]", job.ToString()); + AssertMissingRunner(job); } + // { + // var job = store.Query().ForEach((_, _, _, _, _, _, _, _) => { }); + // AreEqual(32, job.Chunks.Count); + // AreEqual(32, job.Entities.Count); + // AreEqual("QueryJob [MyComponent1, MyComponent2, MyComponent3, MyComponent4, Position, Rotation, Scale3]", job.ToString()); + // AssertMissingRunner(job); + // } } [Test] diff --git a/src/Tests/Boost/Test_Query_Each.cs b/src/Tests/Boost/Test_Query_Each.cs index c21db81af..73144fda6 100644 --- a/src/Tests/Boost/Test_Query_Each.cs +++ b/src/Tests/Boost/Test_Query_Each.cs @@ -138,6 +138,58 @@ public static void Test_Query_Chunks_Each5() } } + [Test] + public static void Test_Query_Each6() + { + var store = CreateStore(); + var query = store.Query(); + var each = query.Each(new Each6()); + foreach (var entity in store.Entities) { + Mem.AreEqual(31, entity.GetComponent().a); + } + Mem.AreEqual(100, each.count); + } + + [Test] + public static void Test_Query_Chunks_Each6() + { + var store = CreateStore(); + var query = store.Query(); + var each = new Each6(); + foreach (var chunk in query.Chunks) { + chunk.Each(ref each); + } + foreach (var entity in store.Entities) { + Mem.AreEqual(31, entity.GetComponent().a); + } + } + + [Test] + public static void Test_Query_Each7() + { + var store = CreateStore(); + var query = store.Query(); + var each = query.Each(new Each7()); + foreach (var entity in store.Entities) { + Mem.AreEqual(63, entity.GetComponent().a); + } + Mem.AreEqual(100, each.count); + } + + [Test] + public static void Test_Query_Chunks_Each7() + { + var store = CreateStore(); + var query = store.Query(); + var each = new Each7(); + foreach (var chunk in query.Chunks) { + chunk.Each(ref each); + } + foreach (var entity in store.Entities) { + Mem.AreEqual(63, entity.GetComponent().a); + } + } + private static EntityStore CreateStore() { @@ -148,7 +200,9 @@ private static EntityStore CreateStore() new MyComponent2{ b = 1 }, new MyComponent3{ b = 2 }, new MyComponent4{ b = 4 }, - new MyComponent5{ b = 8 }); + new MyComponent5{ b = 8 }, + new MyComponent6{ b = 16 }, + new MyComponent7{ b = 32 }); } return store; } @@ -197,5 +251,24 @@ public void Execute(ref MyComponent1 c1, ref MyComponent2 c2, ref MyComponent3 c count++; } } + + private struct Each6 : IEach + { + internal int count; + public void Execute(ref MyComponent1 c1, ref MyComponent2 c2, ref MyComponent3 c3, ref MyComponent4 c4, ref MyComponent5 c5, ref MyComponent6 c6) { + c1.a = c2.b + c3.b + c4.b + c5.b + c6.b; + count++; + } + } + + private struct Each7 : IEach + { + internal int count; + public void Execute(ref MyComponent1 c1, ref MyComponent2 c2, ref MyComponent3 c3, ref MyComponent4 c4, ref MyComponent5 c5, ref MyComponent6 c6, ref MyComponent7 c7) + { + c1.a = c2.b + c3.b + c4.b + c5.b + c6.b + c7.b; + count++; + } + } } } \ No newline at end of file diff --git a/src/Tests/Boost/Test_Query_EachEntity.cs b/src/Tests/Boost/Test_Query_EachEntity.cs index 311cc773f..b88c896be 100644 --- a/src/Tests/Boost/Test_Query_EachEntity.cs +++ b/src/Tests/Boost/Test_Query_EachEntity.cs @@ -117,7 +117,7 @@ public static void Test_Query_Chunks_EachEntity4() public static void Test_Query_EachEntity5() { var store = CreateStore(); - var query = store.Query(); + var query = store.Query(); var each = query.EachEntity(new Each5()); foreach (var entity in store.Entities) { Mem.AreEqual(15, entity.GetComponent().a); @@ -139,6 +139,58 @@ public static void Test_Query_Chunks_EachEntity5() } } + [Test] + public static void Test_Query_EachEntity6() + { + var store = CreateStore(); + var query = store.Query(); + var each = query.EachEntity(new Each6()); + foreach (var entity in store.Entities) { + Mem.AreEqual(31, entity.GetComponent().a); + } + Mem.AreEqual(100, each.count); + } + + [Test] + public static void Test_Query_Chunks_EachEntity6() + { + var store = CreateStore(); + var query = store.Query(); + var each = new Each6(); + foreach (var chunk in query.Chunks) { + chunk.EachEntity(ref each); + } + foreach (var entity in store.Entities) { + Mem.AreEqual(31, entity.GetComponent().a); + } + } + + [Test] + public static void Test_Query_EachEntity7() + { + var store = CreateStore(); + var query = store.Query(); + var each = query.EachEntity(new Each7()); + foreach (var entity in store.Entities) { + Mem.AreEqual(63, entity.GetComponent().a); + } + Mem.AreEqual(100, each.count); + } + + [Test] + public static void Test_Query_Chunks_EachEntity7() + { + var store = CreateStore(); + var query = store.Query(); + var each = new Each7(); + foreach (var chunk in query.Chunks) { + chunk.EachEntity(ref each); + } + foreach (var entity in store.Entities) { + Mem.AreEqual(63, entity.GetComponent().a); + } + } + private static EntityStore CreateStore() { @@ -149,7 +201,9 @@ private static EntityStore CreateStore() new MyComponent2{ b = 1 }, new MyComponent3{ b = 2 }, new MyComponent4{ b = 4 }, - new MyComponent5{ b = 8 }); + new MyComponent5{ b = 8 }, + new MyComponent6{ b = 16 }, + new MyComponent7{ b = 32 }); } return store; } @@ -198,5 +252,23 @@ public void Execute(ref MyComponent1 c1, ref MyComponent2 c2, ref MyComponent3 c Mem.AreEqual(++count, id); } } + + private struct Each6 : IEachEntity + { + internal int count; + public void Execute(ref MyComponent1 c1, ref MyComponent2 c2, ref MyComponent3 c3, ref MyComponent4 c4, ref MyComponent5 c5, ref MyComponent6 c6, int id) { + c1.a = c2.b + c3.b + c4.b + c5.b + c6.b; + Mem.AreEqual(++count, id); + } + } + + private struct Each7 : IEachEntity + { + internal int count; + public void Execute(ref MyComponent1 c1, ref MyComponent2 c2, ref MyComponent3 c3, ref MyComponent4 c4, ref MyComponent5 c5, ref MyComponent6 c6, ref MyComponent7 c7, int id) { + c1.a = c2.b + c3.b + c4.b + c5.b + c6.b + c7.b; + Mem.AreEqual(++count, id); + } + } } } \ No newline at end of file diff --git a/src/Tests/ECS/Arch/Test_Query.cs b/src/Tests/ECS/Arch/Test_Query.cs index 410e3ba2e..8cec059e5 100644 --- a/src/Tests/ECS/Arch/Test_Query.cs +++ b/src/Tests/ECS/Arch/Test_Query.cs @@ -236,18 +236,21 @@ public static void Test_Signature_Query() var sig3 = Signature.Get(); var sig4 = Signature.Get(); var sig5 = Signature.Get(); + var sig6 = Signature.Get(); // var query1 = store.Query(sig1); var query2 = store.Query(sig2); var query3 = store.Query(sig3); var query4 = store.Query(sig4); var query5 = store.Query(sig5); + var query6 = store.Query(sig6); AreEqual("Query: [Position] Count: 0", query1.ToString()); AreEqual("Query: [Position, Rotation] Count: 0", query2.ToString()); AreEqual("Query: [Position, Rotation, Scale3] Count: 0", query3.ToString()); AreEqual("Query: [Position, Rotation, Scale3, MyComponent1] Count: 0", query4.ToString()); AreEqual("Query: [Position, Rotation, Scale3, MyComponent1, MyComponent2] Count: 0", query5.ToString()); + AreEqual("Query: [Position, Rotation, Scale3, MyComponent1, MyComponent2, MyComponent3] Count: 0", query6.ToString()); AreEqual(0, query1.Archetypes.Length); AreEqual(0, query2.Archetypes.Length); @@ -289,6 +292,14 @@ public static void Test_Signature_Query() AreEqual(3, query3.Archetypes.Length); AreEqual(2, query4.Archetypes.Length); AreEqual(1, query5.Archetypes.Length); + + entity.AddComponent(); + AreEqual(6, query1.Archetypes.Length); + AreEqual(5, query2.Archetypes.Length); + AreEqual(4, query3.Archetypes.Length); + AreEqual(3, query4.Archetypes.Length); + AreEqual(2, query5.Archetypes.Length); + AreEqual(1, query6.Archetypes.Length); } [Test] diff --git a/src/Tests/ECS/Arch/Test_QueryComponents.cs b/src/Tests/ECS/Arch/Test_QueryComponents.cs index e8fa47abe..c12f037d2 100644 --- a/src/Tests/ECS/Arch/Test_QueryComponents.cs +++ b/src/Tests/ECS/Arch/Test_QueryComponents.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using Friflo.Engine.ECS; using NUnit.Framework; using static NUnit.Framework.Assert; @@ -14,6 +15,8 @@ private struct Comp2 : IComponent { } private struct Comp3 : IComponent { } private struct Comp4 : IComponent { } private struct Comp5 : IComponent { } + private struct Comp6 : IComponent { } + private struct Comp7 : IComponent { } [Test] public static void Test_QueryComponents_AllComponents() @@ -23,19 +26,25 @@ public static void Test_QueryComponents_AllComponents() var query = store.Query(sig); query = query.AllComponents(ComponentTypes.Get()); - AssertResult("2, 7, 8, 9, 10", query); + AssertResult("11, 102, 103, 104, 105, 106, 107", query); query = query.AllComponents(ComponentTypes.Get()); - AssertResult("7, 8, 9, 10", query); + AssertResult("102, 103, 104, 105, 106, 107", query); query = query.AllComponents(ComponentTypes.Get()); - AssertResult("8, 9, 10", query); + AssertResult("103, 104, 105, 106, 107", query); query = query.AllComponents(ComponentTypes.Get()); - AssertResult("9, 10", query); + AssertResult("104, 105, 106, 107", query); query = query.AllComponents(ComponentTypes.Get()); - AssertResult("10", query); + AssertResult("105, 106, 107", query); + + query = query.AllComponents(ComponentTypes.Get()); + AssertResult("106, 107", query); + + query = query.AllComponents(ComponentTypes.Get()); + AssertResult("107", query); } [Test] @@ -46,19 +55,25 @@ public static void Test_QueryComponents_AnyComponents() var query = store.Query(sig); query = query.AnyComponents(ComponentTypes.Get()); - AssertResult("2, 7, 8, 9, 10", query); + AssertResult("11, 102, 103, 104, 105, 106, 107", query); query = query.AnyComponents(ComponentTypes.Get()); - AssertResult("3, 7, 8, 9, 10", query); + AssertResult("12, 102, 103, 104, 105, 106, 107", query); query = query.AnyComponents(ComponentTypes.Get()); - AssertResult("4, 8, 9, 10", query); + AssertResult("13, 103, 104, 105, 106, 107", query); query = query.AnyComponents(ComponentTypes.Get()); - AssertResult("5, 9, 10", query); + AssertResult("14, 104, 105, 106, 107", query); query = query.AnyComponents(ComponentTypes.Get()); - AssertResult("6, 10", query); + AssertResult("15, 105, 106, 107", query); + + query = query.AnyComponents(ComponentTypes.Get()); + AssertResult("16, 106, 107", query); + + query = query.AnyComponents(ComponentTypes.Get()); + AssertResult("17, 107", query); } [Test] @@ -71,19 +86,25 @@ public static void Test_QueryComponents_AnyComponents_AllComponents() var all = ComponentTypes.Get(); // entities: 8, 9, 10 query = query.AnyComponents(ComponentTypes.Get()).AllComponents(all); - AssertResult("2, 7, 8, 9, 10", query); + AssertResult("11, 102, 103, 104, 105, 106, 107", query); query = query.AnyComponents(ComponentTypes.Get()).AllComponents(all); - AssertResult("3, 7, 8, 9, 10", query); + AssertResult("12, 102, 103, 104, 105, 106, 107", query); query = query.AnyComponents(ComponentTypes.Get()).AllComponents(all); - AssertResult("4, 8, 9, 10", query); + AssertResult("13, 103, 104, 105, 106, 107", query); query = query.AnyComponents(ComponentTypes.Get()).AllComponents(all); - AssertResult("5, 8, 9, 10", query); + AssertResult("14, 103, 104, 105, 106, 107", query); query = query.AnyComponents(ComponentTypes.Get()).AllComponents(all); - AssertResult("6, 8, 9, 10", query); + AssertResult("15, 103, 104, 105, 106, 107", query); + + query = query.AnyComponents(ComponentTypes.Get()).AllComponents(all); + AssertResult("16, 103, 104, 105, 106, 107", query); + + query = query.AnyComponents(ComponentTypes.Get()).AllComponents(all); + AssertResult("17, 103, 104, 105, 106, 107", query); } [Test] @@ -94,19 +115,25 @@ public static void Test_QueryComponents_WithoutAllComponents() var query = store.Query(sig); query = query.WithoutAllComponents(ComponentTypes.Get()); - AssertResult("1, 3, 4, 5, 6", query); + AssertResult("1, 12, 13, 14, 15, 16, 17", query); query = query.WithoutAllComponents(ComponentTypes.Get()); - AssertResult("1, 2, 3, 4, 5, 6", query); + AssertResult("1, 11, 12, 13, 14, 15, 16, 17", query); query = query.WithoutAllComponents(ComponentTypes.Get()); - AssertResult("1, 2, 3, 4, 5, 6, 7", query); + AssertResult("1, 11, 12, 13, 14, 15, 16, 17, 102", query); query = query.WithoutAllComponents(ComponentTypes.Get()); - AssertResult("1, 2, 3, 4, 5, 6, 7, 8", query); + AssertResult("1, 11, 12, 13, 14, 15, 16, 17, 102, 103", query); query = query.WithoutAllComponents(ComponentTypes.Get()); - AssertResult("1, 2, 3, 4, 5, 6, 7, 8, 9", query); + AssertResult("1, 11, 12, 13, 14, 15, 16, 17, 102, 103, 104", query); + + query = query.WithoutAllComponents(ComponentTypes.Get()); + AssertResult("1, 11, 12, 13, 14, 15, 16, 17, 102, 103, 104, 105", query); + + query = query.WithoutAllComponents(ComponentTypes.Get()); + AssertResult("1, 11, 12, 13, 14, 15, 16, 17, 102, 103, 104, 105, 106", query); } [Test] @@ -117,19 +144,25 @@ public static void Test_QueryComponents_WithoutAnyComponents() var query = store.Query(sig); query = query.WithoutAnyComponents(ComponentTypes.Get()); - AssertResult("1, 3, 4, 5, 6", query); + AssertResult("1, 12, 13, 14, 15, 16, 17", query); query = query.WithoutAnyComponents(ComponentTypes.Get()); - AssertResult("1, 2, 4, 5, 6", query); + AssertResult("1, 11, 13, 14, 15, 16, 17", query); query = query.WithoutAnyComponents(ComponentTypes.Get()); - AssertResult("1, 2, 3, 5, 6, 7", query); + AssertResult("1, 11, 12, 14, 15, 16, 17, 102", query); query = query.WithoutAnyComponents(ComponentTypes.Get()); - AssertResult("1, 2, 3, 4, 6, 7, 8", query); + AssertResult("1, 11, 12, 13, 15, 16, 17, 102, 103", query); query = query.WithoutAnyComponents(ComponentTypes.Get()); - AssertResult("1, 2, 3, 4, 5, 7, 8, 9", query); + AssertResult("1, 11, 12, 13, 14, 16, 17, 102, 103, 104", query); + + query = query.WithoutAnyComponents(ComponentTypes.Get()); + AssertResult("1, 11, 12, 13, 14, 15, 17, 102, 103, 104, 105", query); + + query = query.WithoutAnyComponents(ComponentTypes.Get()); + AssertResult("1, 11, 12, 13, 14, 15, 16, 102, 103, 104, 105, 106", query); } [Test] @@ -139,27 +172,28 @@ public static void Test_QueryComponents_WithoutAnyComponents_WithoutAllComponent var sig = Signature.Get(); var query = store.Query(sig); - var all = ComponentTypes.Get(); // entities: 8, 9, 10 + var all = ComponentTypes.Get(); - // without: 1, 3, 4, 5, 6 query = query.WithoutAnyComponents(ComponentTypes.Get()).WithoutAllComponents(all); - AssertResult("1, 3, 4, 5, 6", query); + AssertResult("1, 12, 13, 14, 15, 16, 17", query); - // without: 1, 2, 4, 5, 6 query = query.WithoutAnyComponents(ComponentTypes.Get()).WithoutAllComponents(all); - AssertResult("1, 2, 4, 5, 6", query); + AssertResult("1, 11, 13, 14, 15, 16, 17", query); - // without: 1, 2, 3, 5, 6, 7 query = query.WithoutAnyComponents(ComponentTypes.Get()).WithoutAllComponents(all); - AssertResult("1, 2, 3, 5, 6, 7", query); + AssertResult("1, 11, 12, 14, 15, 16, 17, 102", query); - // without: 1, 2, 3, 4, 6, 7, 8 query = query.WithoutAnyComponents(ComponentTypes.Get()).WithoutAllComponents(all); - AssertResult("1, 2, 3, 4, 6, 7", query); + AssertResult("1, 11, 12, 13, 15, 16, 17, 102", query); - // without: 1, 2, 3, 4, 5, 7, 8, 9 query = query.WithoutAnyComponents(ComponentTypes.Get()).WithoutAllComponents(all); - AssertResult("1, 2, 3, 4, 5, 7", query); + AssertResult("1, 11, 12, 13, 14, 16, 17, 102", query); + + query = query.WithoutAnyComponents(ComponentTypes.Get()).WithoutAllComponents(all); + AssertResult("1, 11, 12, 13, 14, 15, 17, 102", query); + + query = query.WithoutAnyComponents(ComponentTypes.Get()).WithoutAllComponents(all); + AssertResult("1, 11, 12, 13, 14, 15, 16, 102", query); } private static EntityStore CreateTestStore() @@ -167,16 +201,20 @@ private static EntityStore CreateTestStore() var store = new EntityStore(PidType.UsePidAsId); CreateEntity(store, 1, new ComponentTypes()); - CreateEntity(store, 2, ComponentTypes.Get()); - CreateEntity(store, 3, ComponentTypes.Get()); - CreateEntity(store, 4, ComponentTypes.Get()); - CreateEntity(store, 5, ComponentTypes.Get()); - CreateEntity(store, 6, ComponentTypes.Get()); - - CreateEntity(store, 7, ComponentTypes.Get()); - CreateEntity(store, 8, ComponentTypes.Get()); - CreateEntity(store, 9, ComponentTypes.Get()); - CreateEntity(store, 10, ComponentTypes.Get()); + CreateEntity(store, 11, ComponentTypes.Get()); + CreateEntity(store, 12, ComponentTypes.Get()); + CreateEntity(store, 13, ComponentTypes.Get()); + CreateEntity(store, 14, ComponentTypes.Get()); + CreateEntity(store, 15, ComponentTypes.Get()); + CreateEntity(store, 16, ComponentTypes.Get()); + CreateEntity(store, 17, ComponentTypes.Get()); + + CreateEntity(store, 102, ComponentTypes.Get()); + CreateEntity(store, 103, ComponentTypes.Get()); + CreateEntity(store, 104, ComponentTypes.Get()); + CreateEntity(store, 105, ComponentTypes.Get()); + CreateEntity(store, 106, ComponentTypes.Get()); + CreateEntity(store, 107, ComponentTypes.Get()); return store; } @@ -221,19 +259,19 @@ public static void Test_QueryComponents_overloads_AllComponents_AnyComponents() var any = ComponentTypes.Get(); var query = store.Query().AllComponents(all).AnyComponents(any); - AssertResult("2, 7, 8, 9, 10", query); + AssertResult("11, 102, 103, 104, 105, 106, 107", query); query = store.Query().AllComponents(all).AnyComponents(any); - AssertResult("2, 7, 8, 9, 10", query); + AssertResult("11, 102, 103, 104, 105, 106, 107", query); query = store.Query().AllComponents(all).AnyComponents(any); - AssertResult("2, 7, 8, 9, 10", query); + AssertResult("11, 102, 103, 104, 105, 106, 107", query); query = store.Query().AllComponents(all).AnyComponents(any); - AssertResult("2, 7, 8, 9, 10", query); + AssertResult("11, 102, 103, 104, 105, 106, 107", query); query = store.Query().AllComponents(all).AnyComponents(any); - AssertResult("2, 7, 8, 9, 10", query); + AssertResult("11, 102, 103, 104, 105, 106, 107", query); } [Test] @@ -245,19 +283,19 @@ public static void Test_QueryComponents_overloads_WithoutAllComponents_WithoutAn var any = ComponentTypes.Get(); var query = store.Query().WithoutAllComponents(all).WithoutAnyComponents(any); - AssertResult("1, 3, 4, 5, 6", query); + AssertResult("1, 12, 13, 14, 15, 16, 17", query); query = store.Query().WithoutAllComponents(all).WithoutAnyComponents(any); - AssertResult("1, 3, 4, 5, 6", query); + AssertResult("1, 12, 13, 14, 15, 16, 17", query); query = store.Query().WithoutAllComponents(all).WithoutAnyComponents(any); - AssertResult("1, 3, 4, 5, 6", query); + AssertResult("1, 12, 13, 14, 15, 16, 17", query); query = store.Query().WithoutAllComponents(all).WithoutAnyComponents(any); - AssertResult("1, 3, 4, 5, 6", query); + AssertResult("1, 12, 13, 14, 15, 16, 17", query); query = store.Query().WithoutAllComponents(all).WithoutAnyComponents(any); - AssertResult("1, 3, 4, 5, 6", query); + AssertResult("1, 12, 13, 14, 15, 16, 17", query); } [Test] diff --git a/src/Tests/ECS/Arch/Test_QueryJobArg.cs b/src/Tests/ECS/Arch/Test_QueryJobArg.cs index 8575cdce2..25727e705 100644 --- a/src/Tests/ECS/Arch/Test_QueryJobArg.cs +++ b/src/Tests/ECS/Arch/Test_QueryJobArg.cs @@ -313,6 +313,145 @@ public static void Test_QueryJobArg_RunParallel_Arg_5() entities[n] = archetype.CreateEntity(); } } + + [Test] + public static void Test_QueryJobArg_RunParallel_Arg_6() + { + var store = CreateTestStore(); + var archetype = store.GetArchetype(ComponentTypes.Get()); + var entities = new Entity[EntityCount]; + + var query = store.Query(); + var count = 0; + var forEach = query.ForEach((myComponent1, myComponent2, myComponent3, position, rotation, scale3, chunkEntities) => { + AssertExecution(chunkEntities); + var span1 = myComponent1.Span; + var span2 = myComponent2.Span; + var span3 = myComponent3.Span; + var span4 = position.Span; + var span5 = rotation.Span; + var span6 = scale3.Span; + var ids = chunkEntities.Ids; + Interlocked.Add(ref count, span1.Length); + for (int n = 0; n < span1.Length; n++) { + Mem.AreEqual(span1[n].a + 1, ids[n]); + ++span1[n].a; + ++span2[n].b; + ++span3[n].b; + ++span4[n].x; + ++span5[n].x; + ++span6[n].x; + } + }); + Assert.AreEqual(16, forEach.ParallelComponentMultiple); + forEach.MinParallelChunkLength = 1; + + for (int n = 0; n < EntityCount; n++) + { + for (int i = 0; i < n; i++) { + var entity = entities[i]; + entity.GetComponent().a = i; + entity.GetComponent().b = i; + entity.GetComponent().b = i; + entity.GetComponent().x = i; + entity.GetComponent().x = i; + entity.GetComponent().x = i; + } + // method under test + count = 0; + Run(forEach, n); + + Mem.AreEqual(n, count); + for (int i = 0; i < n; i++) { + var entity = entities[i]; + var comp1 = entity.GetComponent(); + Mem.AreEqual(i + 1, comp1.a); + var comp2 = entity.GetComponent(); + Mem.AreEqual(i + 1, comp2.b); + var comp3 = entity.GetComponent(); + Mem.AreEqual(i + 1, comp3.b); + var comp4 = entity.GetComponent(); + Mem.AreEqual(i + 1, comp4.x); + var comp5 = entity.GetComponent(); + Mem.AreEqual(i + 1, comp5.x); + var comp6 = entity.GetComponent(); + Mem.AreEqual(i + 1, comp6.x); + } + entities[n] = archetype.CreateEntity(); + } + } + + [Test] + public static void Test_QueryJobArg_RunParallel_Arg_7() + { + var store = CreateTestStore(); + var archetype = store.GetArchetype(ComponentTypes.Get()); + var entities = new Entity[EntityCount]; + + var query = store.Query(); + var count = 0; + var forEach = query.ForEach((myComponent1, myComponent2, myComponent3, myComponent4, position, rotation, scale3, chunkEntities) => { + AssertExecution(chunkEntities); + var span1 = myComponent1.Span; + var span2 = myComponent2.Span; + var span3 = myComponent3.Span; + var span4 = myComponent4.Span; + var span5 = position.Span; + var span6 = rotation.Span; + var span7 = scale3.Span; + var ids = chunkEntities.Ids; + Interlocked.Add(ref count, span1.Length); + for (int n = 0; n < span1.Length; n++) { + Mem.AreEqual(span1[n].a + 1, ids[n]); + ++span1[n].a; + ++span2[n].b; + ++span3[n].b; + ++span4[n].b; + ++span5[n].x; + ++span6[n].x; + ++span7[n].x; + } + }); + Assert.AreEqual(16, forEach.ParallelComponentMultiple); + forEach.MinParallelChunkLength = 1; + + for (int n = 0; n < EntityCount; n++) + { + for (int i = 0; i < n; i++) { + var entity = entities[i]; + entity.GetComponent().a = i; + entity.GetComponent().b = i; + entity.GetComponent().b = i; + entity.GetComponent().b = i; + entity.GetComponent().x = i; + entity.GetComponent().x = i; + entity.GetComponent().x = i; + } + // method under test + count = 0; + Run(forEach, n); + + Mem.AreEqual(n, count); + for (int i = 0; i < n; i++) { + var entity = entities[i]; + var comp1 = entity.GetComponent(); + Mem.AreEqual(i + 1, comp1.a); + var comp2 = entity.GetComponent(); + Mem.AreEqual(i + 1, comp2.b); + var comp3 = entity.GetComponent(); + Mem.AreEqual(i + 1, comp3.b); + var comp4 = entity.GetComponent(); + Mem.AreEqual(i + 1, comp4.b); + var comp5 = entity.GetComponent(); + Mem.AreEqual(i + 1, comp5.x); + var comp6 = entity.GetComponent(); + Mem.AreEqual(i + 1, comp6.x); + var comp7 = entity.GetComponent(); + Mem.AreEqual(i + 1, comp7.x); + } + entities[n] = archetype.CreateEntity(); + } + } } diff --git a/src/Tests/ECS/Arch/Test_Signature.cs b/src/Tests/ECS/Arch/Test_Signature.cs index 81d9c7a2b..2aa5e4588 100644 --- a/src/Tests/ECS/Arch/Test_Signature.cs +++ b/src/Tests/ECS/Arch/Test_Signature.cs @@ -42,6 +42,12 @@ public static void Test_Signature_Get() var sig5_ = Signature.Get(); AreEqual("Signature: [Rotation, Position, Scale3, EntityName, MyComponent1]", sig5_.ToString()); + + var sig6_ = Signature.Get(); + AreEqual("Signature: [Rotation, Position, Scale3, EntityName, MyComponent1, MyComponent2]", sig6_.ToString()); + + var sig7_ = Signature.Get(); + AreEqual("Signature: [Rotation, Position, Scale3, EntityName, MyComponent1, MyComponent2, MyComponent3]", sig7_.ToString()); } [Test] @@ -51,18 +57,24 @@ public static void Test_Signature_GetComponentTypes() { var ct3 = ComponentTypes.Get() .ToString(); var ct4 = ComponentTypes.Get() .ToString(); var ct5 = ComponentTypes.Get().ToString(); + var ct6 = ComponentTypes.Get().ToString(); + var ct7 = ComponentTypes.Get().ToString(); var s1 = Signature.Get(); var s2 = Signature.Get(); var s3 = Signature.Get(); var s4 = Signature.Get(); var s5 = Signature.Get(); + var s6 = Signature.Get(); + var s7 = Signature.Get(); AreEqual(s1.ComponentTypes.ToString(), ct1); AreEqual(s2.ComponentTypes.ToString(), ct2); AreEqual(s3.ComponentTypes.ToString(), ct3); AreEqual(s4.ComponentTypes.ToString(), ct4); AreEqual(s5.ComponentTypes.ToString(), ct5); + AreEqual(s6.ComponentTypes.ToString(), ct6); + AreEqual(s7.ComponentTypes.ToString(), ct7); } } diff --git a/src/Tests/ECS/Base/Test_ComponentSchema.cs b/src/Tests/ECS/Base/Test_ComponentSchema.cs index c69e511ae..3be6ce64c 100644 --- a/src/Tests/ECS/Base/Test_ComponentSchema.cs +++ b/src/Tests/ECS/Base/Test_ComponentSchema.cs @@ -49,12 +49,12 @@ public static void Test_ComponentTypes() var components = schema.Components; var scripts = schema.Scripts; - AreEqual("components: 74 scripts: 10 entity tags: 16", schema.ToString()); - AreEqual(75, components.Length); + AreEqual("components: 76 scripts: 10 entity tags: 16", schema.ToString()); + AreEqual(77, components.Length); AreEqual(11, scripts.Length); - AreEqual(80, schema.SchemaTypeByKey.Count); - AreEqual(74, schema.ComponentTypeByType.Count); + AreEqual(82, schema.SchemaTypeByKey.Count); + AreEqual(76, schema.ComponentTypeByType.Count); AreEqual(10, schema.ScriptTypeByType.Count); IsNull(components[0]); diff --git a/src/Tests/ECS/Entity/Test_StructComponent.cs b/src/Tests/ECS/Entity/Test_StructComponent.cs index 327901edc..b9958e33f 100644 --- a/src/Tests/ECS/Entity/Test_StructComponent.cs +++ b/src/Tests/ECS/Entity/Test_StructComponent.cs @@ -133,20 +133,28 @@ public static void Test_4_GetArchetype() { var type1 = store.GetArchetype(ComponentTypes.Get()); var type2 = store.GetArchetype(ComponentTypes.Get()); AreSame(type1, type2); + AreEqual(2, store.Archetypes.Length); type1 = store.GetArchetype(ComponentTypes.Get()); type2 = store.GetArchetype(ComponentTypes.Get()); AreSame(type1, type2); + AreEqual(3, store.Archetypes.Length); type1 = store.GetArchetype(ComponentTypes.Get()); type2 = store.GetArchetype(ComponentTypes.Get()); AreSame(type1, type2); + AreEqual(4, store.Archetypes.Length); type1 = store.GetArchetype(ComponentTypes.Get()); type2 = store.GetArchetype(ComponentTypes.Get()); AreSame(type1, type2); - AreEqual(5, store.Archetypes.Length); + + type1 = store.GetArchetype(ComponentTypes.Get()); + type2 = store.GetArchetype(ComponentTypes.Get()); + AreSame(type1, type2); + + AreEqual(6, store.Archetypes.Length); AreEqual(0, type1.Count); AreEqual(0, type2.Count); } diff --git a/src/Tests/ECS/Index/Query/Arg5.cs b/src/Tests/ECS/Index/Query/Arg5.cs index 4596689f9..007e99640 100644 --- a/src/Tests/ECS/Index/Query/Arg5.cs +++ b/src/Tests/ECS/Index/Query/Arg5.cs @@ -15,11 +15,15 @@ private static void QueryArg5 (IndexContext cx) HasValue (123); var query4 = store.Query(). HasValue(cx.target); var query5 = store.Query(). ValueInRange(100, 1000); + + + cx.query1 = query1; cx.query2 = query2; cx.query3 = query3; cx.query4 = query4; cx.query5 = query5; + { int count = 0; query1.ForEachEntity((ref Position pos, ref Rotation _, ref MyComponent1 _, ref MyComponent2 _, ref MyComponent3 _, Entity entity) => { @@ -82,7 +86,7 @@ private static void QueryArg5 (IndexContext cx) } } AreEqual(3, count); - } + } } } diff --git a/src/Tests/ECS/Index/Query/Arg6.cs b/src/Tests/ECS/Index/Query/Arg6.cs new file mode 100644 index 000000000..5b491423b --- /dev/null +++ b/src/Tests/ECS/Index/Query/Arg6.cs @@ -0,0 +1,93 @@ +using Friflo.Engine.ECS; +using static NUnit.Framework.Assert; + +// ReSharper disable InconsistentNaming +namespace Tests.ECS.Index.Query { + +public static partial class Test_Index_Query +{ + private static void QueryArg6 (IndexContext cx) + { + var store = cx.store; + var query1 = store.Query(). HasValue("find-me"); + var query2 = store.Query(). HasValue (123); + var query3 = store.Query(). HasValue("find-me"). + HasValue (123); + var query4 = store.Query(). HasValue(cx.target); + var query5 = store.Query(). ValueInRange(100, 1000); + + + + cx.query1 = query1; + cx.query2 = query2; + cx.query3 = query3; + cx.query4 = query4; + cx.query5 = query5; + + { + int count = 0; + query1.ForEachEntity((ref Position pos, ref Rotation _, ref MyComponent1 _, ref MyComponent2 _, ref MyComponent3 _, ref MyComponent4 _, Entity entity) => { + switch (count++) { + case 0: AreEqual(11, entity.Id); AreEqual(11f, pos.x); break; + case 1: AreEqual(13, entity.Id); AreEqual(13f, pos.x); break; + } + AreEqual("find-me", entity.GetComponent().name); + }); + AreEqual(2, count); + } { + int count = 0; + query2.ForEachEntity((ref Position pos, ref Rotation _, ref MyComponent1 _, ref MyComponent2 _, ref MyComponent3 _, ref MyComponent4 _, Entity entity) => { + switch (count++) { + case 0: AreEqual(12, entity.Id); AreEqual(12f, pos.x); break; + case 1: AreEqual(13, entity.Id); AreEqual(13f, pos.x); break; + } + AreEqual(123, entity.GetComponent().value); + }); + AreEqual(2, count); + } { + var count = 0; + query3.ForEachEntity((ref Position pos, ref Rotation _, ref MyComponent1 _, ref MyComponent2 _, ref MyComponent3 _, ref MyComponent4 _, Entity entity) => { + switch (count++) { + case 0: AreEqual(11, entity.Id); AreEqual(11f, pos.x); break; + case 1: AreEqual(13, entity.Id); AreEqual(13f, pos.x); break; + case 2: AreEqual(12, entity.Id); AreEqual(12f, pos.x); break; + } + }); + AreEqual(3, count); + } { + var count = 0; + query4.ForEachEntity((ref Position pos, ref Rotation _, ref MyComponent1 _, ref MyComponent2 _, ref MyComponent3 _, ref MyComponent4 _, Entity entity) => { + count++; + AreEqual(13, entity.Id); AreEqual(13f, pos.x); + AreEqual(cx.target, entity.GetComponent().target); + }); + AreEqual(1, count); + } { + var count = 0; + query5.ForEachEntity((ref Position pos, ref Rotation _, ref MyComponent1 _, ref MyComponent2 _, ref MyComponent3 _, ref MyComponent4 _, Entity entity) => { + switch (count++) { + case 0: AreEqual(12, entity.Id); AreEqual(12f, pos.x); break; + case 1: AreEqual(13, entity.Id); AreEqual(13f, pos.x); break; + case 2: AreEqual(14, entity.Id); AreEqual(14f, pos.x); break; + } + }); + AreEqual(3, count); + } { + var count = 0; + foreach (var (positions, _, _, _, _, _, entities) in query5.Chunks) { + AreEqual(1, positions.Length); + AreEqual(1, entities.Length); + var pos = positions[0]; // positions.start can be > 0 + var id = entities [0]; // entities.start can be > 0 + switch (count++) { + case 0: AreEqual(12, id); AreEqual(12f, pos.x); break; + case 1: AreEqual(13, id); AreEqual(13f, pos.x); break; + case 2: AreEqual(14, id); AreEqual(14f, pos.x); break; + } + } + AreEqual(3, count); + } + } +} + +} diff --git a/src/Tests/ECS/Index/Query/Arg7.cs b/src/Tests/ECS/Index/Query/Arg7.cs new file mode 100644 index 000000000..fb71fb4cc --- /dev/null +++ b/src/Tests/ECS/Index/Query/Arg7.cs @@ -0,0 +1,93 @@ +using Friflo.Engine.ECS; +using static NUnit.Framework.Assert; + +// ReSharper disable InconsistentNaming +namespace Tests.ECS.Index.Query { + +public static partial class Test_Index_Query +{ + private static void QueryArg7 (IndexContext cx) + { + var store = cx.store; + var query1 = store.Query(). HasValue("find-me"); + var query2 = store.Query(). HasValue (123); + var query3 = store.Query(). HasValue("find-me"). + HasValue (123); + var query4 = store.Query(). HasValue(cx.target); + var query5 = store.Query(). ValueInRange(100, 1000); + + + + cx.query1 = query1; + cx.query2 = query2; + cx.query3 = query3; + cx.query4 = query4; + cx.query5 = query5; + + { + int count = 0; + query1.ForEachEntity((ref Position pos, ref Rotation _, ref MyComponent1 _, ref MyComponent2 _, ref MyComponent3 _, ref MyComponent4 _, ref MyComponent5 _, Entity entity) => { + switch (count++) { + case 0: AreEqual(11, entity.Id); AreEqual(11f, pos.x); break; + case 1: AreEqual(13, entity.Id); AreEqual(13f, pos.x); break; + } + AreEqual("find-me", entity.GetComponent().name); + }); + AreEqual(2, count); + } { + int count = 0; + query2.ForEachEntity((ref Position pos, ref Rotation _, ref MyComponent1 _, ref MyComponent2 _, ref MyComponent3 _, ref MyComponent4 _, ref MyComponent5 _, Entity entity) => { + switch (count++) { + case 0: AreEqual(12, entity.Id); AreEqual(12f, pos.x); break; + case 1: AreEqual(13, entity.Id); AreEqual(13f, pos.x); break; + } + AreEqual(123, entity.GetComponent().value); + }); + AreEqual(2, count); + } { + var count = 0; + query3.ForEachEntity((ref Position pos, ref Rotation _, ref MyComponent1 _, ref MyComponent2 _, ref MyComponent3 _, ref MyComponent4 _, ref MyComponent5 _, Entity entity) => { + switch (count++) { + case 0: AreEqual(11, entity.Id); AreEqual(11f, pos.x); break; + case 1: AreEqual(13, entity.Id); AreEqual(13f, pos.x); break; + case 2: AreEqual(12, entity.Id); AreEqual(12f, pos.x); break; + } + }); + AreEqual(3, count); + } { + var count = 0; + query4.ForEachEntity((ref Position pos, ref Rotation _, ref MyComponent1 _, ref MyComponent2 _, ref MyComponent3 _, ref MyComponent4 _, ref MyComponent5 _, Entity entity) => { + count++; + AreEqual(13, entity.Id); AreEqual(13f, pos.x); + AreEqual(cx.target, entity.GetComponent().target); + }); + AreEqual(1, count); + } { + var count = 0; + query5.ForEachEntity((ref Position pos, ref Rotation _, ref MyComponent1 _, ref MyComponent2 _, ref MyComponent3 _, ref MyComponent4 _, ref MyComponent5 _, Entity entity) => { + switch (count++) { + case 0: AreEqual(12, entity.Id); AreEqual(12f, pos.x); break; + case 1: AreEqual(13, entity.Id); AreEqual(13f, pos.x); break; + case 2: AreEqual(14, entity.Id); AreEqual(14f, pos.x); break; + } + }); + AreEqual(3, count); + } { + var count = 0; + foreach (var (positions, _, _, _, _, _, _, entities) in query5.Chunks) { + AreEqual(1, positions.Length); + AreEqual(1, entities.Length); + var pos = positions[0]; // positions.start can be > 0 + var id = entities [0]; // entities.start can be > 0 + switch (count++) { + case 0: AreEqual(12, id); AreEqual(12f, pos.x); break; + case 1: AreEqual(13, id); AreEqual(13f, pos.x); break; + case 2: AreEqual(14, id); AreEqual(14f, pos.x); break; + } + } + AreEqual(3, count); + } + } +} + +} diff --git a/src/Tests/ECS/Index/Query/Test_Index_Query.cs b/src/Tests/ECS/Index/Query/Test_Index_Query.cs index 4562dc042..4e2d974c0 100644 --- a/src/Tests/ECS/Index/Query/Test_Index_Query.cs +++ b/src/Tests/ECS/Index/Query/Test_Index_Query.cs @@ -44,7 +44,9 @@ private static IndexContext Query_Setup() new Rotation(), new MyComponent1 { a = n }, new MyComponent2 { b = n }, - new MyComponent3 { b = n }); + new MyComponent3 { b = n }, + new MyComponent4 { b = n }, + new MyComponent5 { b = n }); entities.Add(entity); } cx.target = targets[0]; @@ -149,6 +151,22 @@ public static void Test_Index_Component_Add_Remove_Arg5() QueryArg5(cx); Query_Assertions(cx); } + + [Test] + public static void Test_Index_Component_Add_Remove_Arg6() + { + var cx = Query_Setup(); + QueryArg6(cx); + Query_Assertions(cx); + } + + [Test] + public static void Test_Index_Component_Add_Remove_Arg7() + { + var cx = Query_Setup(); + QueryArg7(cx); + Query_Assertions(cx); + } } } diff --git a/src/Tests/ECS/Systems/Test_Query.cs b/src/Tests/ECS/Systems/Test_Query.cs index 2584a3b77..30942d55a 100644 --- a/src/Tests/ECS/Systems/Test_Query.cs +++ b/src/Tests/ECS/Systems/Test_Query.cs @@ -29,7 +29,7 @@ public static void Test_Query_arg_count_1() foreach (var chunk in query.Chunks) { if (chunkCount++ == 1) { Mem.AreEqual(1, chunk.Length); - Mem.AreEqual("Chunks[1] Archetype: [EntityName, Position, Rotation, Scale3, Transform, TreeNode, MyComponent1] entities: 1", chunk.ToString()); + Mem.AreEqual("Chunks[1] Archetype: [EntityName, Position, Rotation, Scale3, Transform, TreeNode, MyComponent1, MyComponent2] entities: 1", chunk.ToString()); var positions = chunk.Chunk1; Mem.AreEqual("Position[1]", positions.ToString()); Mem.AreEqual(1, positions[0].x); @@ -71,7 +71,7 @@ public static void Test_Query_arg_count_2() foreach (var chunk in query.Chunks) { if (chunkCount++ == 1) { Mem.AreEqual(1, chunk.Length); - Mem.AreEqual("Chunks[1] Archetype: [EntityName, Position, Rotation, Scale3, Transform, TreeNode, MyComponent1] entities: 1", chunk.ToString()); + Mem.AreEqual("Chunks[1] Archetype: [EntityName, Position, Rotation, Scale3, Transform, TreeNode, MyComponent1, MyComponent2] entities: 1", chunk.ToString()); } } Mem.AreEqual(2, chunkCount); @@ -141,7 +141,7 @@ public static void Test_Query_arg_count_4() foreach (var chunk in query.Chunks) { if (chunkCount++ == 1) { Mem.AreEqual(1, chunk.Length); - Mem.AreEqual("Chunks[1] Archetype: [EntityName, Position, Rotation, Scale3, Transform, TreeNode, MyComponent1] entities: 1", chunk.ToString()); + Mem.AreEqual("Chunks[1] Archetype: [EntityName, Position, Rotation, Scale3, Transform, TreeNode, MyComponent1, MyComponent2] entities: 1", chunk.ToString()); } } Mem.AreEqual(2, chunkCount); @@ -177,11 +177,10 @@ public static void Test_Query_arg_count_5() foreach (var chunk in query.Chunks) { if (chunkCount++ == 0) { Mem.AreEqual(1, chunk.Length); - Mem.AreEqual("Chunks[1] Archetype: [EntityName, Position, Rotation, Scale3, Transform, TreeNode, MyComponent1] entities: 1", chunk.ToString()); + Mem.AreEqual("Chunks[1] Archetype: [EntityName, Position, Rotation, Scale3, Transform, TreeNode, MyComponent1, MyComponent2] entities: 1", chunk.ToString()); } } Mem.AreEqual(1, chunkCount); - AssertChunkExtensions(query); // --- run perf var start = Mem.GetAllocatedBytes(); @@ -192,7 +191,80 @@ public static void Test_Query_arg_count_5() Mem.AssertNoAlloc(start); } - private static void AssertChunkExtensions(ArchetypeQuery query) { + [Test] + public static void Test_Query_arg_count_6() + { + var store = SetupTestStore(); + var root = store.StoreRoot; + root.AddScript(new SystemSet { argCount = 6 }); + + var archetype = store.GetArchetype(ComponentTypes.Get()); + for (int n = 2; n <= 1000; n++) { + var child = archetype.CreateEntity(); + child.Position = new Position(n, 0, 0); + child.Rotation = new Rotation(n, 0, 0, 0); + child.Scale3 = new Scale3 (n, 0, 0); + child.Name.value = "child"; + root.AddChild(child); + } + // --- force one time allocations + var query = store.Query(); + int chunkCount = 0; + foreach (var chunk in query.Chunks) { + if (chunkCount++ == 0) { + Mem.AreEqual(1, chunk.Length); + Mem.AreEqual("Chunks[1] Archetype: [EntityName, Position, Rotation, Scale3, Transform, TreeNode, MyComponent1, MyComponent2] entities: 1", chunk.ToString()); + } + } + Mem.AreEqual(1, chunkCount); + + // --- run perf + var start = Mem.GetAllocatedBytes(); + // 10_000_000 ~ #PC: 1078 ms + for (long n = 0; n < Count; n++) { + foreach (var (_, _, _, _, _, _, _) in query.Chunks) { } + } + Mem.AssertNoAlloc(start); + } + + [Test] + public static void Test_Query_arg_count_7() + { + var store = SetupTestStore(); + var root = store.StoreRoot; + root.AddScript(new SystemSet { argCount = 6 }); + + var archetype = store.GetArchetype(ComponentTypes.Get()); + for (int n = 2; n <= 1000; n++) { + var child = archetype.CreateEntity(); + child.Position = new Position(n, 0, 0); + child.Rotation = new Rotation(n, 0, 0, 0); + child.Scale3 = new Scale3 (n, 0, 0); + child.Name.value = "child"; + root.AddChild(child); + } + // --- force one time allocations + var query = store.Query(); + int chunkCount = 0; + foreach (var chunk in query.Chunks) { + if (chunkCount++ == 0) { + Mem.AreEqual(1, chunk.Length); + Mem.AreEqual("Chunks[1] Archetype: [EntityName, Position, Rotation, Scale3, Transform, TreeNode, MyComponent1, MyComponent2] entities: 1", chunk.ToString()); + } + } + Mem.AreEqual(1, chunkCount); + AssertChunkExtensions(query); + + // --- run perf + var start = Mem.GetAllocatedBytes(); + // 10_000_000 ~ #PC: 1078 ms + for (long n = 0; n < Count; n++) { + foreach (var (_, _, _, _, _, _, _, _) in query.Chunks) { } + } + Mem.AssertNoAlloc(start); + } + + private static void AssertChunkExtensions(ArchetypeQuery query) { foreach (var chunk in query.Chunks) { var length = chunk.Entities.Length; @@ -221,6 +293,7 @@ private static EntityStore SetupTestStore() { root.AddComponent(); root.AddComponent(); root.AddComponent(); + root.AddComponent(); store.SetStoreRoot(root); return store; } diff --git a/src/Tests/ECS/Systems/Test_ScriptSystems.cs b/src/Tests/ECS/Systems/Test_ScriptSystems.cs index e7bb5dae0..5a309245b 100644 --- a/src/Tests/ECS/Systems/Test_ScriptSystems.cs +++ b/src/Tests/ECS/Systems/Test_ScriptSystems.cs @@ -22,6 +22,8 @@ public override void Start() { 3 => new MySystem_Arg3(), 4 => new MySystem_Arg4(), 5 => new MySystem_Arg5(), + 6 => new MySystem_Arg6(), + 7 => new MySystem_Arg7(), _ => throw new ArgumentException($"value: {argCount}", nameof(argCount)) }; Systems.Add(system); @@ -181,6 +183,67 @@ protected override void OnUpdate() } } +public class MySystem_Arg6 : QuerySystem +{ + /// Cover + protected override void OnUpdate() + { + var store = Query.Store; + var childArch = store.GetArchetype(ComponentTypes.Get()); + int chunkCount = 0; + foreach (var (positions, rotation, name, scale, transform, myComponent1, entities) in Query.Chunks) { + var matrix4X4 = transform.AsSpanMatrix4x4(); + var length = entities.Length; + Mem.AreEqual(length, matrix4X4.Length); + Mem.AreEqual(length, positions.Length); + Mem.AreEqual(length, rotation.Length); + Mem.AreEqual(length, name.Length); + Mem.AreEqual(length, scale.Length); + Mem.AreEqual(length, transform.Length); + Mem.AreEqual(length, myComponent1.Length); + Mem.AreEqual(positions[0].x, entities.Ids[0]); + Mem.AreEqual(positions[length - 1].x, entities.Ids[length - 1]); + switch(chunkCount++) { + case 0: Mem.AreEqual(999, length); Mem.AreSame(childArch, entities.Archetype); break; + case 1: Mem.AreEqual(1, length); Mem.AreSame(store.StoreRoot.Archetype, entities.Archetype); break; + default: throw new InvalidOperationException("unexpected"); + } + } + Mem.AreEqual(2, chunkCount); + } +} + +public class MySystem_Arg7 : QuerySystem +{ + /// Cover + protected override void OnUpdate() + { + var store = Query.Store; + var childArch = store.GetArchetype(ComponentTypes.Get()); + int chunkCount = 0; + foreach (var (positions, rotation, name, scale, transform, myComponent1,myComponent2, entities) in Query.Chunks) { + var matrix4X4 = transform.AsSpanMatrix4x4(); + var length = entities.Length; + Mem.AreEqual(length, matrix4X4.Length); + Mem.AreEqual(length, positions.Length); + Mem.AreEqual(length, rotation.Length); + Mem.AreEqual(length, name.Length); + Mem.AreEqual(length, scale.Length); + Mem.AreEqual(length, transform.Length); + Mem.AreEqual(length, myComponent1.Length); + Mem.AreEqual(length, myComponent2.Length); + Mem.AreEqual(positions[0].x, entities.Ids[0]); + Mem.AreEqual(positions[length - 1].x, entities.Ids[length - 1]); + switch(chunkCount++) { + case 0: Mem.AreEqual(999, length); Mem.AreSame(childArch, entities.Archetype); break; + case 1: Mem.AreEqual(1, length); Mem.AreSame(store.StoreRoot.Archetype, entities.Archetype); break; + default: throw new InvalidOperationException("unexpected"); + } + } + Mem.AreEqual(2, chunkCount); + } +} + public static class Test_ScriptSystems { [Test] @@ -320,6 +383,66 @@ public static void Test_ScriptSystems_query_arg_count_5() ExecuteSystems(store, count); } + [Test] + public static void Test_ScriptSystems_query_arg_count_6() + { + var store = SetupTestStore(); + var root = store.StoreRoot; + root.AddScript(new SystemSet { argCount = 6 }); + + var child = store.CreateEntity(); + root.AddChild(child); + child.AddComponent(new Position(2, 0, 0)); + child.AddComponent(new Rotation(2, 0, 0, 0)); + child.AddComponent(new Scale3 (2, 0, 0)); + child.AddComponent(); + child.AddComponent(new EntityName("child")); + child.AddComponent(new MyComponent1()); + for (int n = 3; n <= 1000; n++) { + child = child.Archetype.CreateEntity(); + child.Position = new Position(n, 0, 0); + child.Rotation = new Rotation(n, 0, 0, 0); + child.Scale3 = new Scale3 (n, 0, 0); + child.AddComponent(new MyComponent1()); + child.Name.value = "child"; + root.AddChild(child); + } + CreateSystems(store); + int count = 10; // 10_000_000 ~ #PC: 1847 ms + ExecuteSystems(store, count); + } + + [Test] + public static void Test_ScriptSystems_query_arg_count_7() + { + var store = SetupTestStore(); + var root = store.StoreRoot; + root.AddScript(new SystemSet { argCount = 7 }); + + var child = store.CreateEntity(); + root.AddChild(child); + child.AddComponent(new Position(2, 0, 0)); + child.AddComponent(new Rotation(2, 0, 0, 0)); + child.AddComponent(new Scale3 (2, 0, 0)); + child.AddComponent(); + child.AddComponent(new EntityName("child")); + child.AddComponent(new MyComponent1()); + child.AddComponent(new MyComponent2()); + for (int n = 3; n <= 1000; n++) { + child = child.Archetype.CreateEntity(); + child.Position = new Position(n, 0, 0); + child.Rotation = new Rotation(n, 0, 0, 0); + child.Scale3 = new Scale3 (n, 0, 0); + child.AddComponent(new MyComponent1()); + child.AddComponent(new MyComponent2()); + child.Name.value = "child"; + root.AddChild(child); + } + CreateSystems(store); + int count = 10; // 10_000_000 ~ #PC: 1847 ms + ExecuteSystems(store, count); + } + internal static EntityStore SetupTestStore() { var store = new EntityStore(); @@ -330,6 +453,7 @@ internal static EntityStore SetupTestStore() { root.AddComponent(); root.AddComponent(); root.AddComponent(); + root.AddComponent(); store.SetStoreRoot(root); return store; }