diff --git a/Arch.System.SourceGenerator/Query.cs b/Arch.System.SourceGenerator/Query.cs index 85bb5b2..b612513 100644 --- a/Arch.System.SourceGenerator/Query.cs +++ b/Arch.System.SourceGenerator/Query.cs @@ -118,17 +118,22 @@ public static StringBuilder JobParameters(this StringBuilder sb, IEnumerable /// Appends a set of if they are marked by the data attribute. /// ref gameTime, out somePassedList,... /// /// The instance. /// The of s which will be appended if they are marked with data. + /// Whether the method is static and should not add the self parameter /// - public static StringBuilder JobParametersAssigment(this StringBuilder sb, IEnumerable parameterSymbols) + public static StringBuilder JobParametersAssigment(this StringBuilder sb, IEnumerable parameterSymbols, bool isStatic) { bool found = false; + if (!isStatic) + { + sb.Append($"self = this,"); + } foreach (var parameter in parameterSymbols) { if (parameter.GetAttributes().Any(attributeData => attributeData.AttributeClass.Name.Contains("Data"))) @@ -276,7 +281,9 @@ public static StringBuilder AppendQueryMethod(this StringBuilder sb, ref QueryMe var getFirstElements = new StringBuilder().GetFirstElements(queryMethod.Components); var getComponents = new StringBuilder().GetComponents(queryMethod.Components); var insertParams = new StringBuilder().InsertParams(queryMethod.Parameters); - + var jobParameters = new StringBuilder().JobParameters(queryMethod.Parameters); + var jobParametersAssigment = new StringBuilder().JobParametersAssigment(queryMethod.Parameters, queryMethod.IsStatic); + var allTypeArray = new StringBuilder().GetTypeArray(queryMethod.AllFilteredTypes); var anyTypeArray = new StringBuilder().GetTypeArray(queryMethod.AnyFilteredTypes); var noneTypeArray = new StringBuilder().GetTypeArray(queryMethod.NoneFilteredTypes); @@ -284,6 +291,7 @@ public static StringBuilder AppendQueryMethod(this StringBuilder sb, ref QueryMe var template = $$""" + // #nullable enable using System; using System.Runtime.CompilerServices; @@ -291,6 +299,7 @@ public static StringBuilder AppendQueryMethod(this StringBuilder sb, ref QueryMe using Arch.Core; using Arch.Core.Extensions; using Arch.Core.Utils; + using Schedulers; using ArrayExtensions = CommunityToolkit.HighPerformance.ArrayExtensions; using Component = Arch.Core.Component; {{(!queryMethod.IsGlobalNamespace ? $"namespace {queryMethod.Namespace} {{" : "")}} @@ -305,27 +314,49 @@ partial class {{queryMethod.ClassName}}{ private {{staticModifier}} World? _{{queryMethod.MethodName}}_Initialized; private {{staticModifier}} Query? _{{queryMethod.MethodName}}_Query; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public {{staticModifier}} void {{queryMethod.MethodName}}Query(World world {{data}}){ - - if(!ReferenceEquals(_{{queryMethod.MethodName}}_Initialized, world)) { - _{{queryMethod.MethodName}}_Query = world.Query(in {{queryMethod.MethodName}}_QueryDescription); - _{{queryMethod.MethodName}}_Initialized = world; - } - - foreach(ref var chunk in _{{queryMethod.MethodName}}_Query){ - + private struct {{queryMethod.MethodName}}QueryJobChunk : IJob + { + {{jobParameters}} + public {{queryMethod.ClassName}} self; + public void Execute() { + foreach(ref var chunk in {{(queryMethod.IsStatic?$"_{queryMethod.MethodName}_Query" : $"self._{queryMethod.MethodName}_Query")}}){ {{(queryMethod.IsEntityQuery ? "ref var entityFirstElement = ref chunk.Entity(0);" : "")}} {{getFirstElements}} - + foreach(var entityIndex in chunk) { {{(queryMethod.IsEntityQuery ? $"ref readonly var {queryMethod.EntityParameter.Name.ToLower()} = ref Unsafe.Add(ref entityFirstElement, entityIndex);" : "")}} {{getComponents}} - {{queryMethod.MethodName}}({{insertParams}}); + {{(queryMethod.IsStatic ? "" : "self.")}}{{queryMethod.MethodName}}({{insertParams}}); } } + } + } + + public {{staticModifier}} void {{queryMethod.MethodName}}Query(World world {{data}}){ + + if(!ReferenceEquals(_{{queryMethod.MethodName}}_Initialized, world)) { + _{{queryMethod.MethodName}}_Query = world.Query(in {{queryMethod.MethodName}}_QueryDescription); + _{{queryMethod.MethodName}}_Initialized = world; + } + var job = new {{queryMethod.MethodName}}QueryJobChunk() { {{jobParametersAssigment}} }; + job.Execute(); + } + + public {{staticModifier}} JobHandle {{queryMethod.MethodName}}QueryAsync(World world, JobHandle parent, JobHandle source {{data}}){ + + if(!ReferenceEquals(_{{queryMethod.MethodName}}_Initialized, world)) { + _{{queryMethod.MethodName}}_Query = world.Query(in {{queryMethod.MethodName}}_QueryDescription); + _{{queryMethod.MethodName}}_Initialized = world; + } + var job = new {{queryMethod.MethodName}}QueryJobChunk() { {{jobParametersAssigment}} }; + var handle = World.SharedJobScheduler.Schedule(job, parent); + if(!source.IsNull) + { + handle.SetDependsOn(source); + } + World.SharedJobScheduler.Flush(handle); + return handle; } } {{(!queryMethod.IsGlobalNamespace ? "}" : "")}} @@ -347,7 +378,7 @@ public static StringBuilder AppendParallelQueryMethod(this StringBuilder sb, ref // Generate code var jobParameters = new StringBuilder().JobParameters(queryMethod.Parameters); - var jobParametersAssigment = new StringBuilder().JobParametersAssigment(queryMethod.Parameters); + var jobParametersAssigment = new StringBuilder().JobParametersAssigment(queryMethod.Parameters, queryMethod.IsStatic); var data = new StringBuilder().DataParameters(queryMethod.Parameters); var getFirstElements = new StringBuilder().GetFirstElements(queryMethod.Components); var getComponents = new StringBuilder().GetComponents(queryMethod.Components); @@ -360,6 +391,7 @@ public static StringBuilder AppendParallelQueryMethod(this StringBuilder sb, ref var template = $$""" + // #nullable enable using System; using System.Runtime.CompilerServices; @@ -368,6 +400,7 @@ public static StringBuilder AppendParallelQueryMethod(this StringBuilder sb, ref using Arch.Core.Extensions; using Arch.Core.Utils; using ArrayExtensions = CommunityToolkit.HighPerformance.ArrayExtensions; + using Schedulers; using Component = Arch.Core.Component; {{(!queryMethod.IsGlobalNamespace ? $"namespace {queryMethod.Namespace} {{" : "")}} partial class {{queryMethod.ClassName}}{ @@ -382,34 +415,72 @@ partial class {{queryMethod.ClassName}}{ private {{staticModifier}} World? _{{queryMethod.MethodName}}_Initialized; private {{staticModifier}} Query? _{{queryMethod.MethodName}}_Query; - private struct {{queryMethod.MethodName}}QueryJobChunk : IChunkJob + private struct {{queryMethod.MethodName}}QueryJobChunk : IParallelChunkJobProducer { {{jobParameters}} - - public void Execute(ref Chunk chunk) { + public {{queryMethod.ClassName}} self; + private Chunk chunk; + public void RunSingle(int entityIndex) { {{(queryMethod.IsEntityQuery ? "ref var entityFirstElement = ref chunk.Entity(0);" : "")}} {{getFirstElements}} - foreach(var entityIndex in chunk) + {{(queryMethod.IsEntityQuery ? $"ref readonly var {queryMethod.EntityParameter.Name.ToLower()} = ref Unsafe.Add(ref entityFirstElement, entityIndex);" : "")}} + {{getComponents}} + {{(queryMethod.IsStatic ? "" : "self.")}}{{queryMethod.MethodName}}({{insertParams}}); + } + + public void RunVectorized(int fromEntityIndex, int toEntityIndex) { + {{(queryMethod.IsEntityQuery ? "ref var entityFirstElement = ref chunk.Entity(0);" : "")}} + {{getFirstElements}} + for (var entityIndex = fromEntityIndex; entityIndex < toEntityIndex; entityIndex++) { {{(queryMethod.IsEntityQuery ? $"ref readonly var {queryMethod.EntityParameter.Name.ToLower()} = ref Unsafe.Add(ref entityFirstElement, entityIndex);" : "")}} {{getComponents}} - {{queryMethod.MethodName}}({{insertParams}}); + {{(queryMethod.IsStatic ? "" : "self.")}}{{queryMethod.MethodName}}({{insertParams}}); } } + + public void SetChunk(Chunk chunk) + { + this.chunk = chunk; + } } + /// + /// Runs the query asynchronously. + /// Meaning you must wait for the handle returned by this method somewhere with />. + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public {{staticModifier}} void {{queryMethod.MethodName}}Query(World world {{data}}){ - + public {{staticModifier}} JobHandle {{queryMethod.MethodName}}QueryAsync(World world, JobHandle parent, JobHandle source {{data}}){ + if(!ReferenceEquals(_{{queryMethod.MethodName}}_Initialized, world)) { + _{{queryMethod.MethodName}}_Query = world.Query(in {{queryMethod.MethodName}}_QueryDescription); + _{{queryMethod.MethodName}}_Initialized = world; + } + + var job = new {{queryMethod.MethodName}}QueryJobChunk() { {{jobParametersAssigment}} }; + return world.AdvancedInlineParallelChunkQuery(in {{queryMethod.MethodName}}_QueryDescription, job, parent, source); + } + + /// + /// Runs the query synchronously. + /// This is not recommended, as it will block the main thread until the query is finished. + /// This is how all queries worked in previous arch versions. + /// Use instead. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public {{staticModifier}} JobHandle {{queryMethod.MethodName}}Query(World world {{data}}){ if(!ReferenceEquals(_{{queryMethod.MethodName}}_Initialized, world)) { _{{queryMethod.MethodName}}_Query = world.Query(in {{queryMethod.MethodName}}_QueryDescription); _{{queryMethod.MethodName}}_Initialized = world; } var job = new {{queryMethod.MethodName}}QueryJobChunk() { {{jobParametersAssigment}} }; - world.InlineParallelChunkQuery(in {{queryMethod.MethodName}}_QueryDescription, job); + var parentHandle = World.SharedJobScheduler!.Schedule(); + var handle = world.AdvancedInlineParallelChunkQuery(in {{queryMethod.MethodName}}_QueryDescription, job, parentHandle, default); + World.SharedJobScheduler.Flush(parentHandle); + handle.Wait(); + return handle; } } {{(!queryMethod.IsGlobalNamespace ? "}" : "")}}