Skip to content

Commit

Permalink
Add QueryBuilder.GroupByCtx()
Browse files Browse the repository at this point in the history
  • Loading branch information
BeanCheeseBurrito committed Oct 18, 2024
1 parent 0f20d16 commit 905c364
Show file tree
Hide file tree
Showing 9 changed files with 251 additions and 70 deletions.
49 changes: 42 additions & 7 deletions src/Flecs.NET.Codegen/Generators/QueryBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1072,27 +1072,62 @@ public unsafe partial struct {{Generator.GetTypeName(type, i)}}
Ecs.GetQueryBuilder(ref this).GroupBy(component);
return ref this;
}

/// <inheritdoc cref="Core.QueryBuilder.GroupBy{T}()"/>
public ref {{Generator.GetTypeName(type, i)}} GroupBy<T>()
/// <inheritdoc cref="Core.QueryBuilder.GroupBy(ulong, Ecs.GroupByCallback)"/>
public ref {{Generator.GetTypeName(type, i)}} GroupBy(ulong component, Ecs.GroupByCallback callback)
{
Ecs.GetQueryBuilder(ref this).GroupBy<T>();
Ecs.GetQueryBuilder(ref this).GroupBy(component, callback);
return ref this;
}

/// <inheritdoc cref="Core.QueryBuilder.GroupBy(ulong, Ecs.GroupByCallback)"/>
public ref {{Generator.GetTypeName(type, i)}} GroupBy(ulong component, Ecs.GroupByCallback callback)
/// <inheritdoc cref="Core.QueryBuilder.GroupBy{TContext}(ulong, Ecs.GroupByCallback{TContext})"/>
public ref {{Generator.GetTypeName(type, i)}} GroupBy<TContext>(ulong component, Ecs.GroupByCallback<TContext> callback)
{
Ecs.GetQueryBuilder(ref this).GroupBy(component, callback);
return ref this;
}

/// <inheritdoc cref="Core.QueryBuilder.GroupBy{T}()"/>
public ref {{Generator.GetTypeName(type, i)}} GroupBy<T>()
{
Ecs.GetQueryBuilder(ref this).GroupBy<T>();
return ref this;
}

/// <inheritdoc cref="Core.QueryBuilder.GroupBy{T}(Ecs.GroupByCallback)"/>
public ref {{Generator.GetTypeName(type, i)}} GroupBy<T>(Ecs.GroupByCallback callback)
{
Ecs.GetQueryBuilder(ref this).GroupBy<T>(callback);
return ref this;
}

/// <inheritdoc cref="Core.QueryBuilder.GroupBy{T, TContext}(Ecs.GroupByCallback{TContext})"/>
public ref {{Generator.GetTypeName(type, i)}} GroupBy<T, TContext>(Ecs.GroupByCallback<TContext> callback)
{
Ecs.GetQueryBuilder(ref this).GroupBy<T, TContext>(callback);
return ref this;
}

/// <inheritdoc cref="Core.QueryBuilder.GroupByCtx{T}(T)"/>
public ref {{Generator.GetTypeName(type, i)}} GroupByCtx<T>(T value)
{
Ecs.GetQueryBuilder(ref this).GroupByCtx(value);
return ref this;
}

/// <inheritdoc cref="Core.QueryBuilder.GroupByCtx{T}(T, Ecs.UserContextFinish{T})"/>
public ref {{Generator.GetTypeName(type, i)}} GroupByCtx<T>(T value, Ecs.UserContextFinish<T> callback)
{
Ecs.GetQueryBuilder(ref this).GroupByCtx(value, callback);
return ref this;
}

/// <inheritdoc cref="Core.QueryBuilder.GroupByCtx{T}(T, Ecs.UserContextFinish{T})"/>
public ref {{Generator.GetTypeName(type, i)}} GroupByCtx<T>(T value, delegate*<ref T, void> callback)
{
Ecs.GetQueryBuilder(ref this).GroupByCtx(value, callback);
return ref this;
}

/// <inheritdoc cref="Core.QueryBuilder.OnGroupCreate(Ecs.GroupCreateCallback)"/>
public ref {{Generator.GetTypeName(type, i)}} OnGroupCreate(Ecs.GroupCreateCallback callback)
Expand Down
28 changes: 28 additions & 0 deletions src/Flecs.NET.Tests/CSharp/Core/QueryTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -985,4 +985,32 @@ private void EachSparseSharedManaged()
Assert.Equal(30, c3.Value);
});
}

[Fact]
private void GroupByCtx()
{
using World world = World.Create();

Entity rel = world.Entity();

world.Entity()
.Add<Position>()
.Add(rel, world.Entity());

using Query query = world.QueryBuilder()
.With<Position>()
.GroupByCtx(10, static (ref int groupByCtx) =>
{
Assert.Equal(20, groupByCtx);
})
.GroupBy(rel, static (World _, Table _, ulong _, ref int groupByCtx) =>
{
Assert.Equal(10, groupByCtx);
groupByCtx = 20;
return 0;
})
.Build();

query.Each((Iter _, int _) => { });
}
}
34 changes: 26 additions & 8 deletions src/Flecs.NET/Core/BindingContext/Functions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,16 @@ internal static void TypeHooksContextFree(TypeHooksContext* context)
Memory.Free(context);
}

internal static void UserContextFinishDelegate<T>(ref UserContext context)
{
((Ecs.UserContextFinish<T>)context.Callback.Delegate.Target!)(ref context.Get<T>());
}

internal static void UserContextFinishPointer<T>(ref UserContext context)
{
((delegate*<ref T, void>)context.Callback.Pointer)(ref context.Get<T>());
}

#endregion

#region Iterator Callbacks
Expand Down Expand Up @@ -234,14 +244,22 @@ internal static ulong GroupByCallback(ecs_world_t* world, ecs_table_t* table, ul

internal static ulong GroupByCallbackDelegate(ecs_world_t* world, ecs_table_t* table, ulong id, GroupByContext* context)
{
Ecs.GroupByCallback callback = (Ecs.GroupByCallback)context->GroupBy.Delegate.Target!;
return callback(new World(world), new Table(world, table), id);
return ((Ecs.GroupByCallback)context->GroupBy.Delegate.Target!)(world, new Table(world, table), id);
}

internal static ulong GroupByCallbackPointer(ecs_world_t* world, ecs_table_t* table, ulong id, GroupByContext* context)
{
delegate*<World, Table, ulong, ulong> callback = (delegate*<World, Table, ulong, ulong>)context->GroupBy.Pointer;
return callback(new World(world), new Table(world, table), id);
return ((delegate*<World, Table, ulong, ulong>)context->GroupBy.Pointer)(world, new Table(world, table), id);
}

internal static ulong GroupByCallbackDelegate<T>(ecs_world_t* world, ecs_table_t* table, ulong id, GroupByContext* context)
{
return ((Ecs.GroupByCallback<T>)context->GroupBy.Delegate.Target!)(world, new Table(world, table), id, ref context->GroupByUserContext.Get<T>());
}

internal static ulong GroupByCallbackPointer<T>(ecs_world_t* world, ecs_table_t* table, ulong id, GroupByContext* context)
{
return ((delegate*<World, Table, ulong, ref T, ulong>)context->GroupBy.Pointer)(world, new Table(world, table), id, ref context->GroupByUserContext.Get<T>());
}

#endregion
Expand All @@ -268,14 +286,14 @@ internal static ulong GroupByCallbackPointer(ecs_world_t* world, ecs_table_t* ta

internal static void* GroupCreateCallbackDelegate<T>(ecs_world_t* world, ulong id, GroupByContext* context)
{
((Ecs.GroupCreateCallback<T>)context->GroupCreate.Delegate.Target!)(world, id, out T userContext);
return UserContext.Alloc(ref userContext);
((Ecs.GroupCreateCallback<T>)context->GroupCreate.Delegate.Target!)(world, id, out T groupContext);
return UserContext.Alloc(ref groupContext);
}

internal static void* GroupCreateCallbackPointer<T>(ecs_world_t* world, ulong id, GroupByContext* context)
{
((delegate*<World, ulong, out T, void>)context->GroupCreate.Pointer)(world, id, out T userContext);
return UserContext.Alloc(ref userContext);
((delegate*<World, ulong, out T, void>)context->GroupCreate.Pointer)(world, id, out T groupContext);
return UserContext.Alloc(ref groupContext);
}

#endregion
Expand Down
2 changes: 2 additions & 0 deletions src/Flecs.NET/Core/BindingContext/GroupByContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@ internal struct GroupByContext : IDisposable
public Callback GroupBy;
public Callback GroupCreate;
public Callback GroupDelete;
public UserContext GroupByUserContext;

public void Dispose()
{
GroupBy.Dispose();
GroupCreate.Dispose();
GroupDelete.Dispose();
GroupByUserContext.Dispose();
}
}
10 changes: 10 additions & 0 deletions src/Flecs.NET/Core/BindingContext/Pointers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -133,8 +133,18 @@ static Pointers()
[SuppressMessage("ReSharper", "StaticMemberInGenericType")]
internal static unsafe partial class Pointers<T0>
{
#region Context Free Callbacks

internal static readonly nint UserContextFinishDelegate = (nint)(delegate*<ref UserContext, void>)&Functions.UserContextFinishDelegate<T0>;
internal static readonly nint UserContextFinishPointer = (nint)(delegate*<ref UserContext, void>)&Functions.UserContextFinishPointer<T0>;

#endregion

#region Group Callbacks

internal static readonly nint GroupByCallbackDelegate = (nint)(delegate*<ecs_world_t*, ecs_table_t*, ulong, GroupByContext*, ulong>)&Functions.GroupByCallbackDelegate<T0>;
internal static readonly nint GroupByCallbackPointer = (nint)(delegate*<ecs_world_t*, ecs_table_t*, ulong, GroupByContext*, ulong>)&Functions.GroupByCallbackPointer<T0>;

internal static readonly nint GroupCreateCallbackDelegate = (nint)(delegate*<ecs_world_t*, ulong, GroupByContext*, void*>)&Functions.GroupCreateCallbackDelegate<T0>;
internal static readonly nint GroupCreateCallbackPointer = (nint)(delegate*<ecs_world_t*, ulong, GroupByContext*, void*>)&Functions.GroupCreateCallbackPointer<T0>;

Expand Down
48 changes: 40 additions & 8 deletions src/Flecs.NET/Core/BindingContext/UserContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,34 +7,66 @@ namespace Flecs.NET.Core.BindingContext;

internal unsafe struct UserContext : IDisposable, IEquatable<UserContext>
{
public GCHandle Object;
public GCHandle Object; // User context object
public Callback Callback; // User context finish callback

public void Dispose()
{
if (Callback != default)
((delegate*<ref UserContext, void>)Callback.Invoker)(ref this);

Managed.FreeGcHandle(Object);
Callback.Dispose();

Object = default;
Callback = default;
}

public ref T Get<T>()
{
Ecs.Assert(Object.IsAllocated, "User context object is empty.");
Ecs.Assert(Object.Target is StrongBox<T>, "User context type does not match the given type argument 'T'.");
return ref Managed.GetTypeRef<T>(Object);
}

public static void Set<T>(ref UserContext dest, ref T value)
public void Set<T>(ref T value)
{
if (dest != default)
dest.Dispose();
Dispose();
Object = GCHandle.Alloc(new StrongBox<T>(value));
}

dest.Object = GCHandle.Alloc(new StrongBox<T>(value));
public void Set<T>(ref T value, Ecs.UserContextFinish<T> callback)
{
Dispose();
Object = GCHandle.Alloc(new StrongBox<T>(value));
Callback.Set(callback, Pointers<T>.UserContextFinishDelegate);
}

public void Set<T>(ref T value, delegate*<ref T, void> callback)
{
Dispose();
Object = GCHandle.Alloc(new StrongBox<T>(value));
Callback.Set((nint)callback, Pointers<T>.UserContextFinishPointer);
}

public void Set<T>(T value)
{
Set(ref value);
}

public void Set<T>(T value, Ecs.UserContextFinish<T> callback)
{
Set(ref value, callback);
}

public static void Set<T>(ref UserContext dest, T value)
public void Set<T>(T value, delegate*<ref T, void> callback)
{
Set(ref dest, ref value);
Set(ref value, callback);
}

public static UserContext* Alloc<T>(ref T value)
{
return Memory.Alloc(new UserContext { Object = GCHandle.Alloc(new StrongBox<T>(value)) });
return Memory.AllocZeroed(new UserContext { Object = GCHandle.Alloc(new StrongBox<T>(value)) });
}

public static UserContext* Alloc<T>(T value)
Expand Down
44 changes: 10 additions & 34 deletions src/Flecs.NET/Core/Ecs/Delegates.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,48 +11,29 @@ public static unsafe partial class Ecs
public delegate int AppInitAction(ecs_world_t* world);

/// <summary>
/// Context free.
/// Callback to be run before a user context object is released by flecs.
/// </summary>
public delegate void ContextFree(void* ctx);

/// <summary>
/// Ctor type hook callback.
/// </summary>
public delegate void CtorCallback(void* data, int count, ecs_type_info_t* typeInfo);
/// <typeparam name="T">The user context type.</typeparam>
public delegate void UserContextFinish<T>(ref T value);

/// <summary>
/// Ctor type hook callback.
/// </summary>
/// <typeparam name="T"></typeparam>
public delegate void CtorCallback<T>(ref T data, TypeInfo typeInfo);

/// <summary>
/// Dtor type hook callback.
/// </summary>
public delegate void DtorCallback(void* data, int count, ecs_type_info_t* typeInfo);

/// <summary>
/// Dtor type hook callback.
/// </summary>
/// <typeparam name="T"></typeparam>
public delegate void DtorCallback<T>(ref T data, TypeInfo typeInfo);

/// <summary>
/// Move type hook callback.
/// </summary>
public delegate void MoveCallback(void* dst, void* src, int count, ecs_type_info_t* typeInfo);

/// <summary>
/// Move type hook callback.
/// </summary>
/// <typeparam name="T"></typeparam>
public delegate void MoveCallback<T>(ref T dst, ref T src, TypeInfo typeInfo);

/// <summary>
/// Copy type hook callback.
/// </summary>
public delegate void CopyCallback(void* dst, void* src, int count, ecs_type_info_t* typeInfo);

/// <summary>
/// Copy type hook callback.
/// </summary>
Expand Down Expand Up @@ -114,10 +95,15 @@ public static unsafe partial class Ecs
public delegate void PostFrameCallback(World world);

/// <summary>
/// GroupBy action.
/// GroupBy Callback.
/// </summary>
public delegate ulong GroupByCallback(World world, Table table, ulong group);

/// <summary>
/// GroupBy Callback.
/// </summary>
public delegate ulong GroupByCallback<T>(World world, Table table, ulong group, ref T groupByContext);

/// <summary>
/// Group create callback.
/// </summary>
Expand All @@ -126,7 +112,7 @@ public static unsafe partial class Ecs
/// <summary>
/// Group create callback.
/// </summary>
public delegate void GroupCreateCallback<T>(World world, ulong group, out T context);
public delegate void GroupCreateCallback<T>(World world, ulong group, out T groupContext);

/// <summary>
/// Group delete action.
Expand All @@ -138,21 +124,11 @@ public static unsafe partial class Ecs
/// </summary>
public delegate void GroupDeleteCallback<T>(World world, ulong group, ref T context);

/// <summary>
/// Iter action.
/// </summary>
public delegate void IterAction(ecs_iter_t* it);

/// <summary>
/// Iter callback.
/// </summary>
public delegate void IterCallback(Iter it);

/// <summary>
/// Iter next action.
/// </summary>
public delegate byte IterNextAction(ecs_iter_t* it);

/// <summary>
/// OrderBy action.
/// </summary>
Expand Down
Loading

0 comments on commit 905c364

Please sign in to comment.