diff --git a/fennecs.tests/Conceptual/SourceGenerator.cs b/fennecs.tests/Conceptual/SourceGenerator.cs new file mode 100644 index 0000000..72a5593 --- /dev/null +++ b/fennecs.tests/Conceptual/SourceGenerator.cs @@ -0,0 +1,156 @@ +using System.Text; + +namespace fennecs.tests.Conceptual; + +public class SourceGeneratorExperiment(ITestOutputHelper output) +{ + + private static string EntityUniform(bool entity, bool uniform) + { + return $"{(uniform ? "U uniform, " : "")}{(entity ? "Entity" : "")}"; + } + + private static string ActionParams(int width, bool entity, bool uniform, string pattern) + { + var typeParams = new StringBuilder(); + + //language=C# + if (entity) typeParams.Append($"EntityRef, "); + + //language=C# + if (uniform) typeParams.Append($"U, "); + + //language=C# + for (var i = 0; i < width; i++) + { + var rw = pattern[i] == 'W' ? "RW" : "R"; + typeParams.Append($"{rw}"); + if (i < width - 1) typeParams.Append(", "); + } + return typeParams.ToString(); + } + + private static string TypeParams(int width) + { + var typeParams = new StringBuilder(); + + //language=C# + for (var i = 0; i < width; i++) + { + typeParams.Append($"C{i}"); + if (i < width - 1) typeParams.Append(", "); + } + return typeParams.ToString(); + } + + private static string Select(int width) + { + var select = new StringBuilder(); + if (width > 1) select.Append("("); + //language=C# + for (var i = 0; i < width; i++) + { + select.Append($"s{i}"); + if (i < width - 1) select.Append(", "); + } + if (width > 1) select.Append(")"); + return select.ToString(); + } + + private static string Deconstruct(int width, string pattern) + { + var deconstruct = new StringBuilder(); + //language=C# + for (var i = 0; i < width; i++) + { + deconstruct.Append($"var span{i} = s{i}.Span; "); + if (pattern[i] == 'W') deconstruct.Append($"var type{i} = s{i}.Expression; "); + } + return deconstruct.ToString(); + } + + private static string LoadEntity(bool entity) + { + //language=C# + return entity ? "\n var entity = table[i];" : ""; + } + + private static string Parameters(bool entity, bool uniform, string pattern) + { + var parameters = new StringBuilder(); + + //language=C# + if (entity) parameters.Append("new(in entity), "); + + //language=C# + if (uniform) parameters.Append("uniform, "); + + var index = 0; + foreach (var p in pattern) + { + if (index != 0) parameters.Append(", "); + parameters.Append( + //language=C# + p switch + { + 'W' => $"new(ref span{index}[i], in entity, in type{index})", + 'R' => $"new(ref span{index}[i])", + _ => throw new NotImplementedException(), + } + ); + index++; + } + return parameters.ToString(); + } + + private string GenerateFor(bool entity, bool uniform, int width, int bits) + { + var pattern = $"{bits:b16}"[(16 - width)..16].Replace("0", "W").Replace("1", "R"); + + //output.WriteLine($"{bits:3}:{bits:b16}:{entityRW}"); + + //language=C# + var code = $$""" + /// + [OverloadResolutionPriority(0b_{{(entity ? 1 : 0)}}_{{bits:b8}})] + public void For{{(uniform ? "(U uniform, " : "(")}}Action<{{ActionParams(width, entity, uniform, pattern)}}> action) + { + using var worldLock = World.Lock(); + + foreach (var table in Filtered) + { + var count = table.Count; + using var join = table.CrossJoin<{{TypeParams(width)}}>(_streamTypes.AsSpan()); + if (join.Empty) continue; + do + { + var {{Select(width)}} = join.Select; + {{Deconstruct(width, pattern)}} + for (var i = 0; i < count; i++) + { {{LoadEntity(entity)}} + action({{Parameters(entity, uniform, pattern)}}); + } + } while (join.Iterate()); + } + } + + + """; + + return code; + } + + + [Theory] + [InlineData(3)] + private void TestFor(int width) + { + var top = (1 << width) - 1; + for (var bits = top; bits >= 0; bits--) + { + output.WriteLine(GenerateFor(true, true, width, bits)); + } + //return code; + } + +} diff --git a/fennecs/Delegates.3.cs b/fennecs/Delegates.3.cs index e236972..f81d307 100644 --- a/fennecs/Delegates.3.cs +++ b/fennecs/Delegates.3.cs @@ -8,32 +8,48 @@ namespace fennecs; #region For/Job: Component Actions -public delegate void ComponentActionWWW(RW comp0, RW comp1, RW comp2) where C0 : notnull where C1 : notnull where C2 : notnull; -public delegate void ComponentActionWWR(RW comp0, RW comp1, R comp2) where C0 : notnull where C1 : notnull where C2 : notnull; -public delegate void ComponentActionWRW(RW comp0, RW comp1, RW comp2) where C0 : notnull where C1 : notnull where C2 : notnull; -public delegate void ComponentActionWRR(RW comp0, RW comp1, R comp2) where C0 : notnull where C1 : notnull where C2 : notnull; -public delegate void ComponentActionRWW(R comp0, RW comp1, RW comp2) where C0 : notnull where C1 : notnull where C2 : notnull; -public delegate void ComponentActionRWR(R comp0, RW comp1, R comp2) where C0 : notnull where C1 : notnull where C2 : notnull; -public delegate void ComponentActionRRW(R comp0, R comp1, RW comp2) where C0 : notnull where C1 : notnull where C2 : notnull; -public delegate void ComponentActionRRR(R comp0, R comp1, R comp2) where C0 : notnull where C1 : notnull where C2 : notnull; +public delegate void ActionWWW(RW comp0, RW comp1, RW comp2) where C0 : notnull where C1 : notnull where C2 : notnull; +public delegate void ActionWWR(RW comp0, RW comp1, R comp2) where C0 : notnull where C1 : notnull where C2 : notnull; +public delegate void ActionWRW(RW comp0, RW comp1, RW comp2) where C0 : notnull where C1 : notnull where C2 : notnull; +public delegate void ActionWRR(RW comp0, RW comp1, R comp2) where C0 : notnull where C1 : notnull where C2 : notnull; +public delegate void ActionRWW(R comp0, RW comp1, RW comp2) where C0 : notnull where C1 : notnull where C2 : notnull; +public delegate void ActionRWR(R comp0, RW comp1, R comp2) where C0 : notnull where C1 : notnull where C2 : notnull; +public delegate void ActionRRW(R comp0, R comp1, RW comp2) where C0 : notnull where C1 : notnull where C2 : notnull; +public delegate void ActionRRR(R comp0, R comp1, R comp2) where C0 : notnull where C1 : notnull where C2 : notnull; #endregion #region For/Job: Entity Component Actions -public delegate void EntityComponentActionWWW(EntityRef entity, RW comp0, RW comp1, RW comp2) where C0 : notnull where C1 : notnull where C2 : notnull; -public delegate void EntityComponentActionWWR(EntityRef entity, RW comp0, RW comp1, R comp2) where C0 : notnull where C1 : notnull where C2 : notnull; -public delegate void EntityComponentActionWRW(EntityRef entity, RW comp0, RW comp1, RW comp2) where C0 : notnull where C1 : notnull where C2 : notnull; -public delegate void EntityComponentActionWRR(EntityRef entity, RW comp0, RW comp1, R comp2) where C0 : notnull where C1 : notnull where C2 : notnull; -public delegate void EntityComponentActionRWW(EntityRef entity, R comp0, RW comp1, RW comp2) where C0 : notnull where C1 : notnull where C2 : notnull; -public delegate void EntityComponentActionRWR(EntityRef entity, R comp0, RW comp1, R comp2) where C0 : notnull where C1 : notnull where C2 : notnull; -public delegate void EntityComponentActionRRW(EntityRef entity, R comp0, R comp1, RW comp2) where C0 : notnull where C1 : notnull where C2 : notnull; -public delegate void EntityComponentActionRRR(EntityRef entity, R comp0, R comp1, R comp2) where C0 : notnull where C1 : notnull where C2 : notnull; +public delegate void ActionEWWW(EntityRef entity, RW comp0, RW comp1, RW comp2) where C0 : notnull where C1 : notnull where C2 : notnull; +public delegate void ActionEWWR(EntityRef entity, RW comp0, RW comp1, R comp2) where C0 : notnull where C1 : notnull where C2 : notnull; +public delegate void ActionEWRW(EntityRef entity, RW comp0, R comp1, RW comp2) where C0 : notnull where C1 : notnull where C2 : notnull; +public delegate void ActionEWRR(EntityRef entity, RW comp0, R comp1, R comp2) where C0 : notnull where C1 : notnull where C2 : notnull; +public delegate void ActionERWW(EntityRef entity, R comp0, RW comp1, RW comp2) where C0 : notnull where C1 : notnull where C2 : notnull; +public delegate void ActionERWR(EntityRef entity, R comp0, RW comp1, R comp2) where C0 : notnull where C1 : notnull where C2 : notnull; +public delegate void ActionERRW(EntityRef entity, R comp0, R comp1, RW comp2) where C0 : notnull where C1 : notnull where C2 : notnull; +public delegate void ActionERRR(EntityRef entity, R comp0, R comp1, R comp2) where C0 : notnull where C1 : notnull where C2 : notnull; #endregion + +#region For/Job: Entity Component Actions + +public delegate void ActionEUWWW(EntityRef entity, RW comp0, RW comp1, RW comp2) where C0 : notnull where C1 : notnull where C2 : notnull; +public delegate void ActionEUWWR(EntityRef entity, RW comp0, RW comp1, R comp2) where C0 : notnull where C1 : notnull where C2 : notnull; +public delegate void ActionEUWRW(EntityRef entity, RW comp0, R comp1, RW comp2) where C0 : notnull where C1 : notnull where C2 : notnull; +public delegate void ActionEUWRR(EntityRef entity, RW comp0, R comp1, R comp2) where C0 : notnull where C1 : notnull where C2 : notnull; +public delegate void ActionEURWW(EntityRef entity, R comp0, RW comp1, RW comp2) where C0 : notnull where C1 : notnull where C2 : notnull; +public delegate void ActionEURWR(EntityRef entity, R comp0, RW comp1, R comp2) where C0 : notnull where C1 : notnull where C2 : notnull; +public delegate void ActionEURRW(EntityRef entity, R comp0, R comp1, RW comp2) where C0 : notnull where C1 : notnull where C2 : notnull; +public delegate void ActionEURRR(EntityRef entity, R comp0, R comp1, R comp2) where C0 : notnull where C1 : notnull where C2 : notnull; + +#endregion + + + #region Raw: Memory Actions public delegate void MemoryActionWWW(Memory comp0, Memory comp1, Memory comp2) where C0 : notnull where C1 : notnull where C2 : notnull; diff --git a/fennecs/Stream.3.cs b/fennecs/Stream.3.cs index faa9e69..6305c90 100644 --- a/fennecs/Stream.3.cs +++ b/fennecs/Stream.3.cs @@ -1,6 +1,8 @@ using System.Collections; using System.Collections.Immutable; +using System.Runtime.CompilerServices; using fennecs.pools; +using fennecs.storage; namespace fennecs; @@ -17,6 +19,207 @@ public record Stream(Query Query, Match Match0, Match Match1, Match { private readonly ImmutableArray _streamTypes = [TypeExpression.Of(Match0), TypeExpression.Of(Match1), TypeExpression.Of(Match2)]; + #region Component Stream.For + /// + [OverloadResolutionPriority(0b_1_00000111)] + public void For(U uniform, Action, R, R> action) + { + using var worldLock = World.Lock(); + + foreach (var table in Filtered) + { + var count = table.Count; + using var join = table.CrossJoin(_streamTypes.AsSpan()); + if (join.Empty) continue; + do + { + var (s0, s1, s2) = join.Select; + var span0 = s0.Span; var span1 = s1.Span; var span2 = s2.Span; + for (var i = 0; i < count; i++) + { + var entity = table[i]; + action(new(in entity), uniform, new(ref span0[i]), new(ref span1[i]), new(ref span2[i])); + } + } while (join.Iterate()); + } + } + + + /// + [OverloadResolutionPriority(0b_1_00000110)] + public void For(U uniform, Action, R, RW> action) + { + using var worldLock = World.Lock(); + + foreach (var table in Filtered) + { + var count = table.Count; + using var join = table.CrossJoin(_streamTypes.AsSpan()); + if (join.Empty) continue; + do + { + var (s0, s1, s2) = join.Select; + var span0 = s0.Span; var span1 = s1.Span; var span2 = s2.Span; var type2 = s2.Expression; + for (var i = 0; i < count; i++) + { + var entity = table[i]; + action(new(in entity), uniform, new(ref span0[i]), new(ref span1[i]), new(ref span2[i], in entity, in type2)); + } + } while (join.Iterate()); + } + } + + + /// + [OverloadResolutionPriority(0b_1_00000101)] + public void For(U uniform, Action, RW, R> action) + { + using var worldLock = World.Lock(); + + foreach (var table in Filtered) + { + var count = table.Count; + using var join = table.CrossJoin(_streamTypes.AsSpan()); + if (join.Empty) continue; + do + { + var (s0, s1, s2) = join.Select; + var span0 = s0.Span; var span1 = s1.Span; var type1 = s1.Expression; var span2 = s2.Span; + for (var i = 0; i < count; i++) + { + var entity = table[i]; + action(new(in entity), uniform, new(ref span0[i]), new(ref span1[i], in entity, in type1), new(ref span2[i])); + } + } while (join.Iterate()); + } + } + + + /// + [OverloadResolutionPriority(0b_1_00000100)] + public void For(U uniform, Action, RW, RW> action) + { + using var worldLock = World.Lock(); + + foreach (var table in Filtered) + { + var count = table.Count; + using var join = table.CrossJoin(_streamTypes.AsSpan()); + if (join.Empty) continue; + do + { + var (s0, s1, s2) = join.Select; + var span0 = s0.Span; var span1 = s1.Span; var type1 = s1.Expression; var span2 = s2.Span; var type2 = s2.Expression; + for (var i = 0; i < count; i++) + { + var entity = table[i]; + action(new(in entity), uniform, new(ref span0[i]), new(ref span1[i], in entity, in type1), new(ref span2[i], in entity, in type2)); + } + } while (join.Iterate()); + } + } + + + /// + [OverloadResolutionPriority(0b_1_00000011)] + public void For(U uniform, Action, R, R> action) + { + using var worldLock = World.Lock(); + + foreach (var table in Filtered) + { + var count = table.Count; + using var join = table.CrossJoin(_streamTypes.AsSpan()); + if (join.Empty) continue; + do + { + var (s0, s1, s2) = join.Select; + var span0 = s0.Span; var type0 = s0.Expression; var span1 = s1.Span; var span2 = s2.Span; + for (var i = 0; i < count; i++) + { + var entity = table[i]; + action(new(in entity), uniform, new(ref span0[i], in entity, in type0), new(ref span1[i]), new(ref span2[i])); + } + } while (join.Iterate()); + } + } + + + /// + [OverloadResolutionPriority(0b_1_00000010)] + public void For(U uniform, Action, R, RW> action) + { + using var worldLock = World.Lock(); + + foreach (var table in Filtered) + { + var count = table.Count; + using var join = table.CrossJoin(_streamTypes.AsSpan()); + if (join.Empty) continue; + do + { + var (s0, s1, s2) = join.Select; + var span0 = s0.Span; var type0 = s0.Expression; var span1 = s1.Span; var span2 = s2.Span; var type2 = s2.Expression; + for (var i = 0; i < count; i++) + { + var entity = table[i]; + action(new(in entity), uniform, new(ref span0[i], in entity, in type0), new(ref span1[i]), new(ref span2[i], in entity, in type2)); + } + } while (join.Iterate()); + } + } + + + /// + [OverloadResolutionPriority(0b_1_00000001)] + public void For(U uniform, Action, RW, R> action) + { + using var worldLock = World.Lock(); + + foreach (var table in Filtered) + { + var count = table.Count; + using var join = table.CrossJoin(_streamTypes.AsSpan()); + if (join.Empty) continue; + do + { + var (s0, s1, s2) = join.Select; + var span0 = s0.Span; var type0 = s0.Expression; var span1 = s1.Span; var type1 = s1.Expression; var span2 = s2.Span; + for (var i = 0; i < count; i++) + { + var entity = table[i]; + action(new(in entity), uniform, new(ref span0[i], in entity, in type0), new(ref span1[i], in entity, in type1), new(ref span2[i])); + } + } while (join.Iterate()); + } + } + + + /// + [OverloadResolutionPriority(0b_1_00000000)] + public void For(U uniform, Action, RW, RW> action) + { + using var worldLock = World.Lock(); + + foreach (var table in Filtered) + { + var count = table.Count; + using var join = table.CrossJoin(_streamTypes.AsSpan()); + if (join.Empty) continue; + do + { + var (s0, s1, s2) = join.Select; + var span0 = s0.Span; var type0 = s0.Expression; var span1 = s1.Span; var type1 = s1.Expression; var span2 = s2.Span; var type2 = s2.Expression; + for (var i = 0; i < count; i++) + { + var entity = table[i]; + action(new(in entity), uniform, new(ref span0[i], in entity, in type0), new(ref span1[i], in entity, in type1), new(ref span2[i], in entity, in type2)); + } + } while (join.Iterate()); + } + } + + #endregion #region Stream.For diff --git a/fennecs/fennecs.csproj b/fennecs/fennecs.csproj index 8bc122d..13ac1c5 100644 --- a/fennecs/fennecs.csproj +++ b/fennecs/fennecs.csproj @@ -63,4 +63,8 @@ + + + +