Core: use an explicit .start event sent by app to begin .tick events

Signed-off-by: Stephen Gutekanst <stephen@hexops.com>
This commit is contained in:
Stephen Gutekanst 2024-04-29 18:25:57 -07:00 committed by Stephen Gutekanst
parent 8089d3356e
commit 15fd2c3a64
8 changed files with 67 additions and 25 deletions

View file

@ -11,6 +11,10 @@ pub const name = .mach_core;
pub const Mod = mach.Mod(@This());
pub const events = .{
.start = .{ .handler = start, .description =
\\ Send this once your app is initialized and ready for .app.tick events.
},
.update = .{ .handler = update, .description =
\\ Send this when window entities have been updated and you want the new values respected.
},
@ -19,15 +23,24 @@ pub const events = .{
\\ Send this when rendering has finished and the swapchain should be presented.
},
.init = .{ .handler = init },
.init_done = .{ .handler = fn () void },
.exit = .{ .handler = exit, .description =
\\ Send this when you would like to exit the application.
\\
\\ When the next .present_frame occurs, then .app.deinit will be sent giving your app a chance
\\ to deinitialize itself and .app.tick will no longer be sent. Once your app is done with
\\ deinitialization, you should send the final .mach_core.deinit event which will cause the
\\ application to finish.
},
.deinit = .{ .handler = deinit, .description =
\\ Send this once your app is fully deinitialized and ready to exit for good.
},
// TODO(important): need some way to tie event execution to a specific thread once we have a
// multithreaded dispatch implementation
.init = .{ .handler = init },
.main_thread_tick = .{ .handler = mainThreadTick },
.main_thread_tick_done = .{ .handler = fn () void },
.deinit = .{ .handler = deinit },
.exit = .{ .handler = exit },
};
pub const components = .{
@ -81,14 +94,25 @@ allocator: std.mem.Allocator,
device: *mach.gpu.Device,
queue: *mach.gpu.Queue,
main_window: mach.EntityID,
should_exit: bool = false,
run_state: enum {
initialized,
running,
exiting,
deinitializing,
exited,
} = .initialized,
fn start(core: *Mod) !void {
core.state().run_state = .running;
}
fn init(core: *Mod) !void {
mach.core.allocator = gpa.allocator(); // TODO: banish this global allocator
// Initialize GPU implementation
if (comptime !mach.use_sysgpu) try mach.wgpu.Impl.init(mach.core.allocator, .{});
if (comptime mach.use_sysgpu) try mach.sysgpu.Impl.init(mach.core.allocator, .{});
mach.core.allocator = gpa.allocator(); // TODO: banish this global allocator
try mach.core.init(.{});
// TODO(important): update this information upon framebuffer resize events
@ -105,7 +129,6 @@ fn init(core: *Mod) !void {
});
mach.core.mods.send(.app, .init, .{});
core.send(.init_done, .{});
}
fn update(core: *Mod) !void {
@ -130,16 +153,24 @@ fn update(core: *Mod) !void {
}
fn presentFrame(core: *Mod) !void {
mach.core.swap_chain.present();
switch (core.state().run_state) {
.running => {
mach.core.swap_chain.present();
// Signal that mainThreadTick is done
core.send(.main_thread_tick_done, .{});
// Signal that mainThreadTick is done
core.send(.main_thread_tick_done, .{});
},
.exiting => {
// Exit opportunity is here, deinitialize now
core.state().run_state = .deinitializing;
mach.core.mods.send(.app, .deinit, .{});
},
else => return,
}
}
fn deinit(core: *Mod) void {
core.state().queue.release();
// TODO: this triggers a device loss error, which we should handle correctly
// core.state().device.release();
mach.core.deinit();
var archetypes_iter = core.entities.query(.{ .all = &.{
@ -152,14 +183,18 @@ fn deinit(core: *Mod) void {
}
_ = gpa.deinit();
core.state().should_exit = true;
core.state().run_state = .exited;
// Signal that mainThreadTick is done
core.send(.main_thread_tick_done, .{});
}
fn mainThreadTick() !void {
fn mainThreadTick(core: *Mod) !void {
if (core.state().run_state != .running) return;
_ = try mach.core.update(null);
mach.core.mods.send(.app, .tick, .{});
}
fn exit() void {
mach.core.mods.send(.app, .deinit, .{});
fn exit(core: *Mod) void {
core.state().run_state = .exiting;
}

View file

@ -15,13 +15,8 @@ var stack_space: [8 * 1024 * 1024]u8 = undefined;
pub fn initModule() !void {
// Initialize the global set of Mach modules used in the program.
try mods.init(std.heap.c_allocator);
mods.send(.mach_core, .init, .{});
// Dispatch events until this .mach_core.init_done is sent
try mods.dispatch(&stack_space, .{ .until = .{
.module_name = mods.moduleNameToID(.mach_core),
.local_event = mods.localEventToID(.mach_core, .init_done),
} });
mods.send(.mach_core, .init, .{});
}
/// Tick runs a single step of the main loop on the main OS thread.
@ -36,7 +31,7 @@ pub fn tick() !bool {
.local_event = mods.localEventToID(.mach_core, .main_thread_tick_done),
} });
return !mods.mod.mach_core.state().should_exit;
return mods.mod.mach_core.state().run_state != .exited;
}
/// Returns the error set that the function F returns.