make it clear how to use module system without mach.Core (remove mach.App)

Signed-off-by: Stephen Gutekanst <stephen@hexops.com>
This commit is contained in:
Stephen Gutekanst 2024-08-24 21:34:35 -07:00 committed by Stephen Gutekanst
parent 7ac5bef717
commit 642cc9b7f7
20 changed files with 567 additions and 334 deletions

View file

@ -5,8 +5,8 @@ pub const name = .app;
pub const Mod = mach.Mod(@This());
pub const systems = .{
.start = .{ .handler = start },
.init = .{ .handler = init },
.after_init = .{ .handler = afterInit },
.deinit = .{ .handler = deinit },
.tick = .{ .handler = tick },
};
@ -14,17 +14,20 @@ pub const systems = .{
title_timer: mach.Timer,
pipeline: *gpu.RenderPipeline,
pub fn deinit(core: *mach.Core.Mod, game: *Mod) void {
game.state().pipeline.release();
pub fn deinit(core: *mach.Core.Mod, app: *Mod) void {
app.state().pipeline.release();
core.schedule(.deinit);
}
fn init(game: *Mod, core: *mach.Core.Mod) !void {
fn start(app: *Mod, core: *mach.Core.Mod) !void {
core.schedule(.init);
game.schedule(.after_init);
app.schedule(.init);
}
fn afterInit(game: *Mod, core: *mach.Core.Mod) !void {
fn init(app: *Mod, core: *mach.Core.Mod) !void {
core.state().on_tick = app.system(.tick);
core.state().on_exit = app.system(.deinit);
// Create our shader module
const shader_module = core.state().device.createShaderModuleWGSL("shader.wgsl", @embedFile("shader.wgsl"));
defer shader_module.release();
@ -58,7 +61,7 @@ fn afterInit(game: *Mod, core: *mach.Core.Mod) !void {
const pipeline = core.state().device.createRenderPipeline(&pipeline_descriptor);
// Store our render pipeline in our module's state, so we can access it later on.
game.init(.{
app.init(.{
.title_timer = try mach.Timer.start(),
.pipeline = pipeline,
});
@ -67,7 +70,7 @@ fn afterInit(game: *Mod, core: *mach.Core.Mod) !void {
core.schedule(.start);
}
fn tick(core: *mach.Core.Mod, game: *Mod) !void {
fn tick(core: *mach.Core.Mod, app: *Mod) !void {
var iter = core.state().pollEvents();
while (iter.next()) |event| {
switch (event) {
@ -101,7 +104,7 @@ fn tick(core: *mach.Core.Mod, game: *Mod) !void {
defer render_pass.release();
// Draw
render_pass.setPipeline(game.state().pipeline);
render_pass.setPipeline(app.state().pipeline);
render_pass.draw(3, 1, 0, 0);
// Finish render pass
@ -116,8 +119,8 @@ fn tick(core: *mach.Core.Mod, game: *Mod) !void {
core.schedule(.present_frame);
// update the window title every second
if (game.state().title_timer.read() >= 1.0) {
game.state().title_timer.reset();
if (app.state().title_timer.read() >= 1.0) {
app.state().title_timer.reset();
try updateWindowTitle(core);
}
}

View file

@ -1,15 +1,41 @@
const std = @import("std");
const mach = @import("mach");
// The global list of Mach modules registered for use in our application.
// The global list of Mach modules our application may use.
pub const modules = .{
mach.Core,
@import("App.zig"),
};
pub fn main() !void {
// Initialize mach.Core
try mach.core.initModule();
const allocator = std.heap.c_allocator;
// Main loop
while (try mach.core.tick()) {}
// Initialize module system
try mach.mods.init(allocator);
// Schedule .app.start to run.
mach.mods.schedule(.app, .start);
// If desired, it is possible to observe when the app has finished starting by dispatching
// systems until the app has started:
const stack_space = try allocator.alloc(u8, 8 * 1024 * 1024);
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
// possible on all platforms.
if (mach.Core.supports_non_blocking) {
mach.Core.non_blocking = true;
while (mach.mods.mod.mach_core.state != .exited) {
// Execute systems until a frame has been finished.
try mach.mods.dispatchUntil(stack_space, .mach_core, .frame_finished);
}
} else {
// 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
// return (std.process.exit will occur first.)
//
// 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.
try mach.mods.dispatch(stack_space, .{});
}
}

View file

@ -5,8 +5,8 @@ pub const name = .app;
pub const Mod = mach.Mod(@This());
pub const systems = .{
.start = .{ .handler = start },
.init = .{ .handler = init },
.after_init = .{ .handler = afterInit },
.deinit = .{ .handler = deinit },
.tick = .{ .handler = tick },
};
@ -14,17 +14,20 @@ pub const systems = .{
title_timer: mach.Timer,
pipeline: *gpu.RenderPipeline,
pub fn deinit(core: *mach.Core.Mod, game: *Mod) void {
game.state().pipeline.release();
pub fn deinit(core: *mach.Core.Mod, app: *Mod) void {
app.state().pipeline.release();
core.schedule(.deinit);
}
fn init(game: *Mod, core: *mach.Core.Mod) !void {
fn start(app: *Mod, core: *mach.Core.Mod) !void {
core.schedule(.init);
game.schedule(.after_init);
app.schedule(.init);
}
fn afterInit(game: *Mod, core: *mach.Core.Mod) !void {
fn init(app: *Mod, core: *mach.Core.Mod) !void {
core.state().on_tick = app.system(.tick);
core.state().on_exit = app.system(.deinit);
// Create our shader module
const shader_module = core.state().device.createShaderModuleWGSL("shader.wgsl", @embedFile("shader.wgsl"));
defer shader_module.release();
@ -58,7 +61,7 @@ fn afterInit(game: *Mod, core: *mach.Core.Mod) !void {
const pipeline = core.state().device.createRenderPipeline(&pipeline_descriptor);
// Store our render pipeline in our module's state, so we can access it later on.
game.init(.{
app.init(.{
.title_timer = try mach.Timer.start(),
.pipeline = pipeline,
});
@ -67,7 +70,7 @@ fn afterInit(game: *Mod, core: *mach.Core.Mod) !void {
core.schedule(.start);
}
fn tick(core: *mach.Core.Mod, game: *Mod) !void {
fn tick(core: *mach.Core.Mod, app: *Mod) !void {
var iter = core.state().pollEvents();
while (iter.next()) |event| {
switch (event) {
@ -101,7 +104,7 @@ fn tick(core: *mach.Core.Mod, game: *Mod) !void {
defer render_pass.release();
// Draw
render_pass.setPipeline(game.state().pipeline);
render_pass.setPipeline(app.state().pipeline);
render_pass.draw(3, 1, 0, 0);
// Finish render pass
@ -116,8 +119,8 @@ fn tick(core: *mach.Core.Mod, game: *Mod) !void {
core.schedule(.present_frame);
// update the window title every second
if (game.state().title_timer.read() >= 1.0) {
game.state().title_timer.reset();
if (app.state().title_timer.read() >= 1.0) {
app.state().title_timer.reset();
try updateWindowTitle(core);
}
}

View file

@ -1,15 +1,24 @@
const std = @import("std");
const mach = @import("mach");
// The global list of Mach modules registered for use in our application.
// The global list of Mach modules our application may use.
pub const modules = .{
mach.Core,
@import("App.zig"),
};
// TODO: move this to a mach "entrypoint" zig module which handles nuances like WASM requires.
pub fn main() !void {
// Initialize mach.Core
try mach.core.initModule();
const allocator = std.heap.c_allocator;
// Main loop
while (try mach.core.tick()) {}
// Initialize module system
try mach.mods.init(allocator);
// Schedule .app.start to run.
mach.mods.schedule(.app, .start);
// 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.
const stack_space = try allocator.alloc(u8, 8 * 1024 * 1024);
try mach.mods.dispatch(stack_space, .{});
}