module: remove the ability to send "standard" arguments to systems
Signed-off-by: Stephen Gutekanst <stephen@hexops.com>
This commit is contained in:
parent
f220494649
commit
2a13c07d9e
12 changed files with 39 additions and 144 deletions
|
|
@ -18,8 +18,7 @@ pub fn main() !void {
|
||||||
|
|
||||||
// If desired, it is possible to observe when the app has finished starting by dispatching
|
// If desired, it is possible to observe when the app has finished starting by dispatching
|
||||||
// systems until the app has started:
|
// systems until the app has started:
|
||||||
const stack_space = try allocator.alloc(u8, 8 * 1024 * 1024);
|
try mach.mods.dispatchUntil(.mach_core, .started);
|
||||||
try mach.mods.dispatchUntil(stack_space, .mach_core, .started);
|
|
||||||
|
|
||||||
// On some platforms, you can drive the mach.Core main loop yourself - but this isn't
|
// On some platforms, you can drive the mach.Core main loop yourself - but this isn't
|
||||||
// possible on all platforms.
|
// possible on all platforms.
|
||||||
|
|
@ -27,9 +26,8 @@ pub fn main() !void {
|
||||||
mach.Core.non_blocking = true;
|
mach.Core.non_blocking = true;
|
||||||
while (mach.mods.mod.mach_core.state().state != .exited) {
|
while (mach.mods.mod.mach_core.state().state != .exited) {
|
||||||
// Execute systems until a frame has been finished.
|
// Execute systems until a frame has been finished.
|
||||||
try mach.mods.dispatchUntil(stack_space, .mach_core, .frame_finished);
|
try mach.mods.dispatchUntil(.mach_core, .frame_finished);
|
||||||
}
|
}
|
||||||
allocator.free(stack_space);
|
|
||||||
} else {
|
} else {
|
||||||
// On platforms where you cannot control the mach.Core main loop, the .mach_core.start
|
// On platforms where you cannot control the mach.Core main loop, the .mach_core.start
|
||||||
// system your app schedules will block forever and the function call below will NEVER
|
// system your app schedules will block forever and the function call below will NEVER
|
||||||
|
|
@ -37,6 +35,6 @@ pub fn main() !void {
|
||||||
//
|
//
|
||||||
// In this case we can just dispatch systems until there are no more left to execute, which
|
// In this case we can just dispatch systems until there are no more left to execute, which
|
||||||
// conviently works even if you aren't using mach.Core in your program.
|
// conviently works even if you aren't using mach.Core in your program.
|
||||||
try mach.mods.dispatch(stack_space, .{});
|
try mach.mods.dispatch(.{});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,5 @@ pub fn main() !void {
|
||||||
|
|
||||||
// Dispatch systems forever or until there are none left to dispatch. If your app uses mach.Core
|
// Dispatch systems forever or until there are none left to dispatch. If your app uses mach.Core
|
||||||
// then this will block forever and never return.
|
// then this will block forever and never return.
|
||||||
const stack_space = try allocator.alloc(u8, 8 * 1024 * 1024);
|
try mach.mods.dispatch(.{});
|
||||||
try mach.mods.dispatch(stack_space, .{});
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,5 @@ pub fn main() !void {
|
||||||
|
|
||||||
// Dispatch systems forever or until there are none left to dispatch. If your app uses mach.Core
|
// Dispatch systems forever or until there are none left to dispatch. If your app uses mach.Core
|
||||||
// then this will block forever and never return.
|
// then this will block forever and never return.
|
||||||
const stack_space = try allocator.alloc(u8, 8 * 1024 * 1024);
|
try mach.mods.dispatch(.{});
|
||||||
try mach.mods.dispatch(stack_space, .{});
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,5 @@ pub fn main() !void {
|
||||||
|
|
||||||
// Dispatch systems forever or until there are none left to dispatch. If your app uses mach.Core
|
// Dispatch systems forever or until there are none left to dispatch. If your app uses mach.Core
|
||||||
// then this will block forever and never return.
|
// then this will block forever and never return.
|
||||||
const stack_space = try allocator.alloc(u8, 8 * 1024 * 1024);
|
try mach.mods.dispatch(.{});
|
||||||
try mach.mods.dispatch(stack_space, .{});
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,5 @@ pub fn main() !void {
|
||||||
|
|
||||||
// Dispatch systems forever or until there are none left to dispatch. If your app uses mach.Core
|
// Dispatch systems forever or until there are none left to dispatch. If your app uses mach.Core
|
||||||
// then this will block forever and never return.
|
// then this will block forever and never return.
|
||||||
const stack_space = try allocator.alloc(u8, 8 * 1024 * 1024);
|
try mach.mods.dispatch(.{});
|
||||||
try mach.mods.dispatch(stack_space, .{});
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,5 @@ pub fn main() !void {
|
||||||
|
|
||||||
// Dispatch systems forever or until there are none left to dispatch. If your app uses mach.Core
|
// Dispatch systems forever or until there are none left to dispatch. If your app uses mach.Core
|
||||||
// then this will block forever and never return.
|
// then this will block forever and never return.
|
||||||
const stack_space = try allocator.alloc(u8, 8 * 1024 * 1024);
|
try mach.mods.dispatch(.{});
|
||||||
try mach.mods.dispatch(stack_space, .{});
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,5 @@ pub fn main() !void {
|
||||||
|
|
||||||
// Dispatch systems forever or until there are none left to dispatch. If your app uses mach.Core
|
// Dispatch systems forever or until there are none left to dispatch. If your app uses mach.Core
|
||||||
// then this will block forever and never return.
|
// then this will block forever and never return.
|
||||||
const stack_space = try allocator.alloc(u8, 8 * 1024 * 1024);
|
try mach.mods.dispatch(.{});
|
||||||
try mach.mods.dispatch(stack_space, .{});
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,5 @@ pub fn main() !void {
|
||||||
|
|
||||||
// Dispatch systems forever or until there are none left to dispatch. If your app uses mach.Core
|
// Dispatch systems forever or until there are none left to dispatch. If your app uses mach.Core
|
||||||
// then this will block forever and never return.
|
// then this will block forever and never return.
|
||||||
const stack_space = try allocator.alloc(u8, 8 * 1024 * 1024);
|
try mach.mods.dispatch(.{});
|
||||||
try mach.mods.dispatch(stack_space, .{});
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,5 @@ pub fn main() !void {
|
||||||
|
|
||||||
// Dispatch systems forever or until there are none left to dispatch. If your app uses mach.Core
|
// Dispatch systems forever or until there are none left to dispatch. If your app uses mach.Core
|
||||||
// then this will block forever and never return.
|
// then this will block forever and never return.
|
||||||
const stack_space = try allocator.alloc(u8, 8 * 1024 * 1024);
|
try mach.mods.dispatch(.{});
|
||||||
try mach.mods.dispatch(stack_space, .{});
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
18
src/Core.zig
18
src/Core.zig
|
|
@ -308,22 +308,16 @@ pub fn start(core: *Mod) !void {
|
||||||
}
|
}
|
||||||
|
|
||||||
// The user wants mach.Core to take control of the main loop.
|
// The user wants mach.Core to take control of the main loop.
|
||||||
|
|
||||||
// TODO: we already have stack space since we are an executing system, so in theory we could
|
|
||||||
// deduplicate this allocation and just use 'our current stack space' - but accessing it from
|
|
||||||
// the dispatcher is tricky.
|
|
||||||
const stack_space = try core.state().allocator.alloc(u8, 8 * 1024 * 1024);
|
|
||||||
|
|
||||||
if (supports_non_blocking) {
|
if (supports_non_blocking) {
|
||||||
while (core.state().state != .exited) {
|
while (core.state().state != .exited) {
|
||||||
dispatch(stack_space);
|
dispatch();
|
||||||
}
|
}
|
||||||
// Don't return, because Platform.run wouldn't either (marked noreturn due to underlying
|
// Don't return, because Platform.run wouldn't either (marked noreturn due to underlying
|
||||||
// platform APIs never returning.)
|
// platform APIs never returning.)
|
||||||
std.process.exit(0);
|
std.process.exit(0);
|
||||||
} else {
|
} else {
|
||||||
// Platform drives the main loop.
|
// Platform drives the main loop.
|
||||||
Platform.run(platform_update_callback, .{ &mach.mods.mod.mach_core, stack_space });
|
Platform.run(platform_update_callback, .{&mach.mods.mod.mach_core});
|
||||||
|
|
||||||
// Platform.run should be marked noreturn, so this shouldn't ever run. But just in case we
|
// Platform.run should be marked noreturn, so this shouldn't ever run. But just in case we
|
||||||
// accidentally introduce a different Platform.run in the future, we put an exit here for
|
// accidentally introduce a different Platform.run in the future, we put an exit here for
|
||||||
|
|
@ -332,16 +326,16 @@ pub fn start(core: *Mod) !void {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dispatch(stack_space: []u8) void {
|
fn dispatch() void {
|
||||||
mach.mods.dispatchUntil(stack_space, .mach_core, .frame_finished) catch {
|
mach.mods.dispatchUntil(.mach_core, .frame_finished) catch {
|
||||||
@panic("Dispatch in Core failed");
|
@panic("Dispatch in Core failed");
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn platform_update_callback(core: *Mod, stack_space: []u8) !bool {
|
fn platform_update_callback(core: *Mod) !bool {
|
||||||
// Execute systems until .mach_core.frame_finished is dispatched, signalling a frame was
|
// Execute systems until .mach_core.frame_finished is dispatched, signalling a frame was
|
||||||
// finished.
|
// finished.
|
||||||
try mach.mods.dispatchUntil(stack_space, .mach_core, .frame_finished);
|
try mach.mods.dispatchUntil(.mach_core, .frame_finished);
|
||||||
|
|
||||||
return core.state().state != .exited;
|
return core.state().state != .exited;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -102,6 +102,5 @@ test "entities DB" {
|
||||||
world.mod.renderer.schedule(.tick);
|
world.mod.renderer.schedule(.tick);
|
||||||
|
|
||||||
// Dispatch systems
|
// Dispatch systems
|
||||||
var stack_space: [8 * 1024 * 1024]u8 = undefined;
|
try world.dispatch(.{});
|
||||||
try world.dispatch(&stack_space, .{});
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,13 +26,6 @@ fn validateModule(comptime M: type, comptime systems: bool) void {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// TODO: implement serialization constraints
|
|
||||||
/// For now this exists just to indicate things that we expect will be required to be serializable in
|
|
||||||
/// the future.
|
|
||||||
fn Serializable(comptime T: type) type {
|
|
||||||
return T;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: add runtime module support
|
// TODO: add runtime module support
|
||||||
pub const ModuleID = u32;
|
pub const ModuleID = u32;
|
||||||
pub const SystemID = u32;
|
pub const SystemID = u32;
|
||||||
|
|
@ -129,13 +122,10 @@ pub fn Modules(comptime modules: anytype) type {
|
||||||
const Dispatch = struct {
|
const Dispatch = struct {
|
||||||
module_name: ModuleID,
|
module_name: ModuleID,
|
||||||
system_name: SystemID,
|
system_name: SystemID,
|
||||||
args_slice: []u8,
|
|
||||||
args_alignment: u32,
|
|
||||||
};
|
};
|
||||||
const DispatchQueue = std.fifo.LinearFifo(Dispatch, .Dynamic);
|
const DispatchQueue = std.fifo.LinearFifo(Dispatch, .Dynamic);
|
||||||
|
|
||||||
dispatch_queue_mu: std.Thread.RwLock = .{},
|
dispatch_queue_mu: std.Thread.RwLock = .{},
|
||||||
dispatch_args_queue: std.ArrayListUnmanaged(u8) = .{},
|
|
||||||
dispatch_queue: DispatchQueue,
|
dispatch_queue: DispatchQueue,
|
||||||
mod: ModsByName(modules),
|
mod: ModsByName(modules),
|
||||||
// TODO: pass mods directly instead of ComponentTypesByName?
|
// TODO: pass mods directly instead of ComponentTypesByName?
|
||||||
|
|
@ -161,13 +151,10 @@ pub fn Modules(comptime modules: anytype) type {
|
||||||
|
|
||||||
m.* = .{
|
m.* = .{
|
||||||
.entities = entities,
|
.entities = entities,
|
||||||
// TODO(module): better default allocations
|
|
||||||
.dispatch_args_queue = try std.ArrayListUnmanaged(u8).initCapacity(allocator, 8 * 1024 * 1024),
|
|
||||||
.dispatch_queue = DispatchQueue.init(allocator),
|
.dispatch_queue = DispatchQueue.init(allocator),
|
||||||
.mod = undefined,
|
.mod = undefined,
|
||||||
.debug_trace = debug_trace,
|
.debug_trace = debug_trace,
|
||||||
};
|
};
|
||||||
errdefer m.dispatch_args_queue.deinit(allocator);
|
|
||||||
errdefer m.dispatch_queue.deinit();
|
errdefer m.dispatch_queue.deinit();
|
||||||
try m.dispatch_queue.ensureTotalCapacity(1024); // TODO(module): better default allocations
|
try m.dispatch_queue.ensureTotalCapacity(1024); // TODO(module): better default allocations
|
||||||
|
|
||||||
|
|
@ -183,7 +170,7 @@ pub fn Modules(comptime modules: anytype) type {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(m: *@This(), allocator: std.mem.Allocator) void {
|
pub fn deinit(m: *@This(), allocator: std.mem.Allocator) void {
|
||||||
m.dispatch_args_queue.deinit(allocator);
|
_ = allocator; // autofix
|
||||||
m.dispatch_queue.deinit();
|
m.dispatch_queue.deinit();
|
||||||
m.entities.deinit();
|
m.entities.deinit();
|
||||||
}
|
}
|
||||||
|
|
@ -215,27 +202,6 @@ pub fn Modules(comptime modules: anytype) type {
|
||||||
comptime module_name: ModuleName(modules),
|
comptime module_name: ModuleName(modules),
|
||||||
// TODO(important): cleanup comptime
|
// TODO(important): cleanup comptime
|
||||||
comptime system_name: SystemEnumM(@TypeOf(@field(m.mod, @tagName(module_name)).__state)),
|
comptime system_name: SystemEnumM(@TypeOf(@field(m.mod, @tagName(module_name)).__state)),
|
||||||
) void {
|
|
||||||
m.scheduleWithArgs(module_name, system_name, .{});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Schedule the specified system to run later, passing some additional arguments.
|
|
||||||
///
|
|
||||||
/// Today, any arguments are allowed, but in the future these will be restricted to simple
|
|
||||||
/// data types
|
|
||||||
/// , non-pointers, and you will want to ensure they are not stateful in order for
|
|
||||||
/// your program to work with future debugging tools.
|
|
||||||
///
|
|
||||||
/// In general, scheduleWithArgs should really only be used for cross-language, cross-process,
|
|
||||||
/// or cross-network behavior. If you otherwise need to get data from one system to another
|
|
||||||
/// you should be using entities and components.
|
|
||||||
pub fn scheduleWithArgs(
|
|
||||||
m: *@This(),
|
|
||||||
// TODO: is a variant of this function where module_name/system_name is not comptime known, but asserted to be a valid enum, useful?
|
|
||||||
comptime module_name: ModuleName(modules),
|
|
||||||
// TODO(important): cleanup comptime
|
|
||||||
comptime system_name: SystemEnumM(@TypeOf(@field(m.mod, @tagName(module_name)).__state)),
|
|
||||||
args: SystemArgsM(@TypeOf(@field(m.mod, @tagName(module_name)).__state), system_name),
|
|
||||||
) void {
|
) void {
|
||||||
// TODO: comptime safety/debugging
|
// TODO: comptime safety/debugging
|
||||||
const system_name_g: System = comptime moduleToGlobalSystemName(
|
const system_name_g: System = comptime moduleToGlobalSystemName(
|
||||||
|
|
@ -245,39 +211,24 @@ pub fn Modules(comptime modules: anytype) type {
|
||||||
SystemEnum,
|
SystemEnum,
|
||||||
system_name,
|
system_name,
|
||||||
);
|
);
|
||||||
m.sendInternal(@intFromEnum(module_name), @intFromEnum(system_name_g), args);
|
m.sendInternal(@intFromEnum(module_name), @intFromEnum(system_name_g));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Schedule the specified system to run later, using fully dynamic parameters (i.e. to run
|
/// Schedule the specified system to run later, using fully dynamic parameters (i.e. to run
|
||||||
/// a system not known to the program at compile time.)
|
/// a system not known to the program at compile time.)
|
||||||
pub fn scheduleDynamic(m: *@This(), module_name: ModuleID, system_name: SystemID) void {
|
pub fn scheduleDynamic(m: *@This(), module_name: ModuleID, system_name: SystemID) void {
|
||||||
m.scheduleDynamicWithArgs(module_name, system_name, .{});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Schedule the specified system to run later, using fully dynamic parameters (i.e. to run
|
|
||||||
/// a system not known to the program at compile time.)
|
|
||||||
pub fn scheduleDynamicWithArgs(m: *@This(), module_name: ModuleID, system_name: SystemID, args: anytype) void {
|
|
||||||
// TODO: runtime safety/debugging
|
// TODO: runtime safety/debugging
|
||||||
// TODO: check args do not have obviously wrong things, like comptime values
|
// TODO: check args do not have obviously wrong things, like comptime values
|
||||||
// TODO: if module_name and system_name are valid enums, can we type-check args at runtime?
|
m.sendInternal(module_name, system_name);
|
||||||
m.sendInternal(module_name, system_name, args);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sendInternal(m: *@This(), module_name: ModuleID, system_name: SystemID, args: anytype) void {
|
fn sendInternal(m: *@This(), module_name: ModuleID, system_name: SystemID) void {
|
||||||
// TODO: verify arguments are valid, e.g. not comptime types
|
|
||||||
_ = Serializable(@TypeOf(args));
|
|
||||||
|
|
||||||
// TODO: debugging
|
// TODO: debugging
|
||||||
m.dispatch_queue_mu.lock();
|
m.dispatch_queue_mu.lock();
|
||||||
defer m.dispatch_queue_mu.unlock();
|
defer m.dispatch_queue_mu.unlock();
|
||||||
|
|
||||||
const args_bytes = std.mem.asBytes(&args);
|
|
||||||
m.dispatch_args_queue.appendSliceAssumeCapacity(args_bytes);
|
|
||||||
m.dispatch_queue.writeItemAssumeCapacity(.{
|
m.dispatch_queue.writeItemAssumeCapacity(.{
|
||||||
.module_name = module_name,
|
.module_name = module_name,
|
||||||
.system_name = system_name,
|
.system_name = system_name,
|
||||||
.args_slice = m.dispatch_args_queue.items[m.dispatch_args_queue.items.len - args_bytes.len .. m.dispatch_args_queue.items.len],
|
|
||||||
.args_alignment = @alignOf(@TypeOf(args)),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -317,12 +268,11 @@ pub fn Modules(comptime modules: anytype) type {
|
||||||
/// Use .dispatch() with a .until argument if you need to specify a runtime-known system.
|
/// Use .dispatch() with a .until argument if you need to specify a runtime-known system.
|
||||||
pub fn dispatchUntil(
|
pub fn dispatchUntil(
|
||||||
m: *@This(),
|
m: *@This(),
|
||||||
stack_space: []u8,
|
|
||||||
comptime module_name: ModuleName(modules),
|
comptime module_name: ModuleName(modules),
|
||||||
// TODO(important): cleanup comptime
|
// TODO(important): cleanup comptime
|
||||||
system: SystemEnumM(@TypeOf(@field(m.mod, @tagName(module_name)).__state)),
|
system: SystemEnumM(@TypeOf(@field(m.mod, @tagName(module_name)).__state)),
|
||||||
) !void {
|
) !void {
|
||||||
try m.dispatch(stack_space, .{
|
try m.dispatch(.{
|
||||||
.until = .{
|
.until = .{
|
||||||
.module_name = m.moduleNameToID(module_name),
|
.module_name = m.moduleNameToID(module_name),
|
||||||
.system = m.systemToID(module_name, system),
|
.system = m.systemToID(module_name, system),
|
||||||
|
|
@ -336,7 +286,6 @@ pub fn Modules(comptime modules: anytype) type {
|
||||||
/// which may be invoked, e.g. 8MB. It may be heap-allocated.
|
/// which may be invoked, e.g. 8MB. It may be heap-allocated.
|
||||||
pub fn dispatch(
|
pub fn dispatch(
|
||||||
m: *@This(),
|
m: *@This(),
|
||||||
stack_space: []u8,
|
|
||||||
options: DispatchOptions,
|
options: DispatchOptions,
|
||||||
) !void {
|
) !void {
|
||||||
const Injectable = comptime blk: {
|
const Injectable = comptime blk: {
|
||||||
|
|
@ -357,12 +306,11 @@ pub fn Modules(comptime modules: anytype) type {
|
||||||
}
|
}
|
||||||
@compileError("failed to initialize Injectable field (this is a bug): " ++ field.name ++ " " ++ @typeName(field.type));
|
@compileError("failed to initialize Injectable field (this is a bug): " ++ field.name ++ " " ++ @typeName(field.type));
|
||||||
}
|
}
|
||||||
return m.dispatchInternal(stack_space, options, injectable);
|
return m.dispatchInternal(options, injectable);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dispatchInternal(
|
pub fn dispatchInternal(
|
||||||
m: *@This(),
|
m: *@This(),
|
||||||
stack_space: []u8,
|
|
||||||
options: DispatchOptions,
|
options: DispatchOptions,
|
||||||
injectable: anytype,
|
injectable: anytype,
|
||||||
) !void {
|
) !void {
|
||||||
|
|
@ -373,27 +321,14 @@ pub fn Modules(comptime modules: anytype) type {
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
// Dequeue the next system
|
// Dequeue the next system
|
||||||
m.dispatch_queue_mu.lock();
|
const d = blk: {
|
||||||
var d = m.dispatch_queue.readItem() orelse {
|
m.dispatch_queue_mu.lock();
|
||||||
m.dispatch_queue_mu.unlock();
|
defer m.dispatch_queue_mu.unlock();
|
||||||
return;
|
break :blk m.dispatch_queue.readItem() orelse return;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Pop the arguments off the d.args_slice stack, so we can release args_slice space.
|
|
||||||
// Otherwise when we release m.dispatch_queue_mu someone may add more system' arguments
|
|
||||||
// to the buffer which would make it tricky to find a good point-in-time to release
|
|
||||||
// argument buffer space.
|
|
||||||
const aligned_addr = std.mem.alignForward(usize, @intFromPtr(stack_space.ptr), d.args_alignment);
|
|
||||||
const align_offset = aligned_addr - @intFromPtr(stack_space.ptr);
|
|
||||||
|
|
||||||
@memcpy(stack_space[align_offset .. align_offset + d.args_slice.len], d.args_slice);
|
|
||||||
d.args_slice = stack_space[align_offset .. align_offset + d.args_slice.len];
|
|
||||||
|
|
||||||
m.dispatch_args_queue.shrinkRetainingCapacity(m.dispatch_args_queue.items.len - d.args_slice.len);
|
|
||||||
m.dispatch_queue_mu.unlock();
|
|
||||||
|
|
||||||
// Dispatch the system
|
// Dispatch the system
|
||||||
try m.callSystem(@enumFromInt(d.module_name), @enumFromInt(d.system_name), d.args_slice, injectable);
|
try m.callSystem(@enumFromInt(d.module_name), @enumFromInt(d.system_name), &.{}, injectable);
|
||||||
|
|
||||||
// If we only wanted to dispatch until this system, then return.
|
// If we only wanted to dispatch until this system, then return.
|
||||||
if (options.until) |until| {
|
if (options.until) |until| {
|
||||||
|
|
@ -437,6 +372,7 @@ pub fn Modules(comptime modules: anytype) type {
|
||||||
inline fn callHandler(handler: anytype, args_data: []u8, injectable: anytype, comptime debug_name: anytype) !void {
|
inline fn callHandler(handler: anytype, args_data: []u8, injectable: anytype, comptime debug_name: anytype) !void {
|
||||||
const Handler = @TypeOf(handler);
|
const Handler = @TypeOf(handler);
|
||||||
const StdArgs = UninjectedArgsTuple(Handler);
|
const StdArgs = UninjectedArgsTuple(Handler);
|
||||||
|
if (@typeInfo(StdArgs).Struct.fields.len > 0) @compileError("mach: system may not take any arguments except injected ones: " ++ debug_name);
|
||||||
const std_args: *StdArgs = @alignCast(@ptrCast(args_data.ptr));
|
const std_args: *StdArgs = @alignCast(@ptrCast(args_data.ptr));
|
||||||
const args = injectArgs(Handler, @TypeOf(injectable), injectable, std_args.*, debug_name);
|
const args = injectArgs(Handler, @TypeOf(injectable), injectable, std_args.*, debug_name);
|
||||||
const Ret = @typeInfo(Handler).@"fn".return_type orelse void;
|
const Ret = @typeInfo(Handler).@"fn".return_type orelse void;
|
||||||
|
|
@ -636,15 +572,11 @@ pub fn ModSet(comptime modules: anytype) type {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub inline fn schedule(m: *@This(), comptime system_name: SystemEnumM(M)) void {
|
pub inline fn schedule(m: *@This(), comptime system_name: SystemEnumM(M)) void {
|
||||||
m.scheduleWithArgs(system_name, .{});
|
|
||||||
}
|
|
||||||
|
|
||||||
pub inline fn scheduleWithArgs(m: *@This(), comptime system_name: SystemEnumM(M), args: SystemArgsM(M, system_name)) void {
|
|
||||||
const ModulesT = Modules(modules);
|
const ModulesT = Modules(modules);
|
||||||
const MByName = ModsByName(modules);
|
const MByName = ModsByName(modules);
|
||||||
const mod_ptr: *MByName = @alignCast(@fieldParentPtr(@tagName(module_tag), m));
|
const mod_ptr: *MByName = @alignCast(@fieldParentPtr(@tagName(module_tag), m));
|
||||||
const mods: *ModulesT = @fieldParentPtr("mod", mod_ptr);
|
const mods: *ModulesT = @fieldParentPtr("mod", mod_ptr);
|
||||||
mods.scheduleWithArgs(module_tag, system_name, args);
|
mods.schedule(module_tag, system_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub inline fn system(_: *@This(), comptime system_name: SystemEnumM(M)) AnySystem {
|
pub inline fn system(_: *@This(), comptime system_name: SystemEnumM(M)) AnySystem {
|
||||||
|
|
@ -1440,7 +1372,6 @@ test "dispatch" {
|
||||||
var physics_updates: usize = 0;
|
var physics_updates: usize = 0;
|
||||||
var physics_calc: usize = 0;
|
var physics_calc: usize = 0;
|
||||||
var renderer_updates: usize = 0;
|
var renderer_updates: usize = 0;
|
||||||
var basic_args_sum: usize = 0;
|
|
||||||
};
|
};
|
||||||
var foo = struct {
|
var foo = struct {
|
||||||
injected_args_sum: usize = 0,
|
injected_args_sum: usize = 0,
|
||||||
|
|
@ -1455,7 +1386,6 @@ test "dispatch" {
|
||||||
pub const systems = .{
|
pub const systems = .{
|
||||||
.tick = .{ .handler = tick },
|
.tick = .{ .handler = tick },
|
||||||
.update = .{ .handler = update },
|
.update = .{ .handler = update },
|
||||||
.update_with_struct_arg = .{ .handler = updateWithStructArg },
|
|
||||||
.calc = .{ .handler = calc },
|
.calc = .{ .handler = calc },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -1467,15 +1397,6 @@ test "dispatch" {
|
||||||
global.physics_updates += 1;
|
global.physics_updates += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
const MyStruct = extern struct {
|
|
||||||
x: [4]extern struct { x: @Vector(4, f32) } = undefined,
|
|
||||||
y: [4]extern struct { x: @Vector(4, f32) } = undefined,
|
|
||||||
};
|
|
||||||
fn updateWithStructArg(arg: MyStruct) void {
|
|
||||||
_ = arg;
|
|
||||||
global.physics_updates += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn calc() void {
|
fn calc() void {
|
||||||
global.physics_calc += 1;
|
global.physics_calc += 1;
|
||||||
}
|
}
|
||||||
|
|
@ -1486,7 +1407,6 @@ test "dispatch" {
|
||||||
.tick = .{ .handler = tick },
|
.tick = .{ .handler = tick },
|
||||||
.frame_done = .{ .handler = fn (i32) void },
|
.frame_done = .{ .handler = fn (i32) void },
|
||||||
.update = .{ .handler = update },
|
.update = .{ .handler = update },
|
||||||
.basic_args = .{ .handler = basicArgs },
|
|
||||||
.injected_args = .{ .handler = injectedArgs },
|
.injected_args = .{ .handler = injectedArgs },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -1500,12 +1420,8 @@ test "dispatch" {
|
||||||
global.renderer_updates += 1;
|
global.renderer_updates += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn basicArgs(a: u32, b: u32) void {
|
fn injectedArgs(foo_ptr: *@TypeOf(foo)) void {
|
||||||
global.basic_args_sum = a + b;
|
foo_ptr.*.injected_args_sum = 1337;
|
||||||
}
|
|
||||||
|
|
||||||
fn injectedArgs(foo_ptr: *@TypeOf(foo), a: u32, b: u32) void {
|
|
||||||
foo_ptr.*.injected_args_sum = a + b;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -1523,24 +1439,20 @@ test "dispatch" {
|
||||||
const M = ModuleName(modules2);
|
const M = ModuleName(modules2);
|
||||||
|
|
||||||
// Systems
|
// Systems
|
||||||
var stack_space: [8 * 1024 * 1024]u8 = undefined;
|
|
||||||
modules.schedule(.engine_renderer, .update);
|
modules.schedule(.engine_renderer, .update);
|
||||||
try modules.dispatchInternal(&stack_space, .{}, .{&foo});
|
try modules.dispatchInternal(.{}, .{&foo});
|
||||||
try testing.expect(usize, 1).eql(global.renderer_updates);
|
try testing.expect(usize, 1).eql(global.renderer_updates);
|
||||||
modules.schedule(.engine_physics, .update);
|
modules.schedule(.engine_physics, .update);
|
||||||
modules.scheduleWithArgs(.engine_physics, .update_with_struct_arg, .{.{}});
|
|
||||||
modules.scheduleDynamic(
|
modules.scheduleDynamic(
|
||||||
@intFromEnum(@as(M, .engine_physics)),
|
@intFromEnum(@as(M, .engine_physics)),
|
||||||
@intFromEnum(@as(LE, .calc)),
|
@intFromEnum(@as(LE, .calc)),
|
||||||
);
|
);
|
||||||
try modules.dispatchInternal(&stack_space, .{}, .{&foo});
|
try modules.dispatchInternal(.{}, .{&foo});
|
||||||
try testing.expect(usize, 2).eql(global.physics_updates);
|
try testing.expect(usize, 1).eql(global.physics_updates);
|
||||||
try testing.expect(usize, 1).eql(global.physics_calc);
|
try testing.expect(usize, 1).eql(global.physics_calc);
|
||||||
|
|
||||||
// Systems
|
// Systems
|
||||||
modules.scheduleWithArgs(.engine_renderer, .basic_args, .{ @as(u32, 1), @as(u32, 2) }); // TODO: match arguments against fn ArgsTuple, for correctness and type inference
|
modules.schedule(.engine_renderer, .injected_args);
|
||||||
modules.scheduleWithArgs(.engine_renderer, .injected_args, .{ @as(u32, 1), @as(u32, 2) });
|
try modules.dispatchInternal(.{}, .{&foo});
|
||||||
try modules.dispatchInternal(&stack_space, .{}, .{&foo});
|
try testing.expect(usize, 1337).eql(foo.injected_args_sum);
|
||||||
try testing.expect(usize, 3).eql(global.basic_args_sum);
|
|
||||||
try testing.expect(usize, 3).eql(foo.injected_args_sum);
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue