Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 96 additions & 25 deletions Arch.System.SourceGenerator/Query.cs
Original file line number Diff line number Diff line change
Expand Up @@ -118,17 +118,22 @@ public static StringBuilder JobParameters(this StringBuilder sb, IEnumerable<IPa
}
return sb;
}

/// <summary>
/// Appends a set of <see cref="parameterSymbols"/> if they are marked by the data attribute.
/// <example>ref gameTime, out somePassedList,...</example>
/// </summary>
/// <param name="sb">The <see cref="StringBuilder"/> instance.</param>
/// <param name="parameterSymbols">The <see cref="IEnumerable{T}"/> of <see cref="IParameterSymbol"/>s which will be appended if they are marked with data.</param>
/// <param name="isStatic">Whether the method is static and should not add the self parameter</param>
/// <returns></returns>
public static StringBuilder JobParametersAssigment(this StringBuilder sb, IEnumerable<IParameterSymbol> parameterSymbols)
public static StringBuilder JobParametersAssigment(this StringBuilder sb, IEnumerable<IParameterSymbol> 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")))
Expand Down Expand Up @@ -276,21 +281,25 @@ 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);
var exclusiveTypeArray = new StringBuilder().GetTypeArray(queryMethod.ExclusiveFilteredTypes);

var template =
$$"""
// <auto-generated />
#nullable enable
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
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} {{" : "")}}
Expand All @@ -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 ? "}" : "")}}
Expand All @@ -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);
Expand All @@ -360,6 +391,7 @@ public static StringBuilder AppendParallelQueryMethod(this StringBuilder sb, ref

var template =
$$"""
// <auto-generated />
#nullable enable
using System;
using System.Runtime.CompilerServices;
Expand All @@ -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}}{
Expand All @@ -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;
}
}

/// <summary>
/// Runs the query asynchronously.
/// Meaning you must wait for the handle returned by this method somewhere with <see cref="JobHandle.Wait"/>/>.
/// </summary>
[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);
}

/// <summary>
/// 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 <see cref="{{queryMethod.MethodName}}QueryAsync(World, ...)"/> instead.
/// </summary>
[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 ? "}" : "")}}
Expand Down