From 15fd2c3a6443dd6dec6c3ff143bf66db9db16e54 Mon Sep 17 00:00:00 2001 From: Stephen Gutekanst Date: Mon, 29 Apr 2024 18:25:57 -0700 Subject: [PATCH] Core: use an explicit .start event sent by app to begin .tick events Signed-off-by: Stephen Gutekanst --- examples/core-custom-entrypoint/App.zig | 2 + examples/custom-renderer/App.zig | 2 + examples/glyphs/App.zig | 4 +- examples/piano/App.zig | 4 +- examples/sprite/App.zig | 2 + examples/text/App.zig | 2 + src/Core.zig | 67 +++++++++++++++++++------ src/core/main.zig | 9 +--- 8 files changed, 67 insertions(+), 25 deletions(-) diff --git a/examples/core-custom-entrypoint/App.zig b/examples/core-custom-entrypoint/App.zig index c2c006e1..ee768714 100644 --- a/examples/core-custom-entrypoint/App.zig +++ b/examples/core-custom-entrypoint/App.zig @@ -58,6 +58,8 @@ fn init(game: *Mod, core: *mach.Core.Mod) !void { .pipeline = pipeline, }); try updateWindowTitle(core); + + core.send(.start, .{}); } // TODO(important): remove need for returning an error here diff --git a/examples/custom-renderer/App.zig b/examples/custom-renderer/App.zig index 62f9ab02..de5804c5 100644 --- a/examples/custom-renderer/App.zig +++ b/examples/custom-renderer/App.zig @@ -74,6 +74,8 @@ fn init( .spawn_timer = try mach.Timer.start(), .player = player, }); + + core.send(.start, .{}); } // TODO(important): remove need for returning an error here diff --git a/examples/glyphs/App.zig b/examples/glyphs/App.zig index 7f9749fe..cded7847 100644 --- a/examples/glyphs/App.zig +++ b/examples/glyphs/App.zig @@ -46,7 +46,7 @@ fn deinit(core: *mach.Core.Mod, sprite_pipeline: *gfx.SpritePipeline.Mod, glyphs core.send(.deinit, .{}); } -fn init(sprite_pipeline: *gfx.SpritePipeline.Mod, glyphs: *Glyphs.Mod, game: *Mod) !void { +fn init(core: *mach.Core.Mod, sprite_pipeline: *gfx.SpritePipeline.Mod, glyphs: *Glyphs.Mod, game: *Mod) !void { sprite_pipeline.send(.init, .{}); glyphs.send(.init, .{}); @@ -55,6 +55,8 @@ fn init(sprite_pipeline: *gfx.SpritePipeline.Mod, glyphs: *Glyphs.Mod, game: *Mo // Run our init code after glyphs module is initialized. game.send(.after_init, .{}); + + core.send(.start, .{}); } fn afterInit( diff --git a/examples/piano/App.zig b/examples/piano/App.zig index feaa9993..27edfb09 100644 --- a/examples/piano/App.zig +++ b/examples/piano/App.zig @@ -36,7 +36,7 @@ pub const components = .{ ghost_key_mode: bool = false, -fn init(audio: *mach.Audio.Mod, app: *Mod) void { +fn init(core: *mach.Core.Mod, audio: *mach.Audio.Mod, app: *Mod) void { // Initialize audio module, telling it to send our module's .audio_state_change event when an // entity's sound stops playing audio.send(.init, .{app.event(.audio_state_change)}); @@ -49,6 +49,8 @@ fn init(audio: *mach.Audio.Mod, app: *Mod) void { std.debug.print("[spacebar] enable ghost-key mode (demonstrate seamless back-to-back sound playback)\n", .{}); std.debug.print("[arrow up] increase volume 10%\n", .{}); std.debug.print("[arrow down] decrease volume 10%\n", .{}); + + core.send(.start, .{}); } fn deinit(core: *mach.Core.Mod, audio: *mach.Audio.Mod) void { diff --git a/examples/sprite/App.zig b/examples/sprite/App.zig index e8757fc5..3f200506 100644 --- a/examples/sprite/App.zig +++ b/examples/sprite/App.zig @@ -89,6 +89,8 @@ fn init( .allocator = allocator, .pipeline = pipeline, }); + + core.send(.start, .{}); } fn tick( diff --git a/examples/text/App.zig b/examples/text/App.zig index c69cf485..b04bfcd2 100644 --- a/examples/text/App.zig +++ b/examples/text/App.zig @@ -127,6 +127,8 @@ fn init( .allocator = allocator, .pipeline = pipeline, }); + + core.send(.start, .{}); } fn tick( diff --git a/src/Core.zig b/src/Core.zig index f1e3b42d..4b3bc6d1 100644 --- a/src/Core.zig +++ b/src/Core.zig @@ -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; } diff --git a/src/core/main.zig b/src/core/main.zig index 407eda0c..2c6de101 100644 --- a/src/core/main.zig +++ b/src/core/main.zig @@ -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.