unify mach.Call and mach.Runner into one type

Signed-off-by: Stephen Gutekanst <stephen@hexops.com>
This commit is contained in:
Stephen Gutekanst 2024-09-23 11:25:33 -07:00 committed by Emi Gutekanst
parent 14ccd5a93c
commit 8054d03b4d
19 changed files with 125 additions and 97 deletions

View file

@ -23,11 +23,10 @@ pub fn deinit(app: *App) void {
pub fn init( pub fn init(
app: *App, app: *App,
core: *mach.Core, core: *mach.Core,
app_tick: mach.Call(App, .tick), app_mod: mach.Functions(App),
app_deinit: mach.Call(App, .deinit),
) !void { ) !void {
core.on_tick = app_tick.id; core.on_tick = app_mod.id.tick;
core.on_exit = app_deinit.id; core.on_exit = app_mod.id.deinit;
// Create our shader module // Create our shader module
const shader_module = core.device.createShaderModuleWGSL("shader.wgsl", @embedFile("shader.wgsl")); const shader_module = core.device.createShaderModuleWGSL("shader.wgsl", @embedFile("shader.wgsl"));

View file

@ -19,14 +19,10 @@ pipeline: *gpu.RenderPipeline,
pub fn init( pub fn init(
core: *mach.Core, core: *mach.Core,
app: *App, app: *App,
app_tick: mach.Call(App, .tick), app_mod: mach.Functions(App),
app_deinit: mach.Call(App, .deinit),
// app_caller: mach.Caller(App),
) !void { ) !void {
core.on_tick = app_tick.id; core.on_tick = app_mod.id.tick;
core.on_exit = app_deinit.id; core.on_exit = app_mod.id.deinit;
// core.on_tick = app_caller.tick;
// core.on_exit = app_caller.exit;
// Create our shader module // Create our shader module
const shader_module = core.device.createShaderModuleWGSL("shader.wgsl", @embedFile("shader.wgsl")); const shader_module = core.device.createShaderModuleWGSL("shader.wgsl", @embedFile("shader.wgsl"));

View file

@ -20,7 +20,7 @@ direction: Vec2 = vec2(0, 0),
spawning: bool = false, spawning: bool = false,
spawn_timer: mach.time.Timer, spawn_timer: mach.time.Timer,
// Components our game module defines. // TODO(object)
pub const components = .{ pub const components = .{
// Whether an entity is a "follower" of our player entity or not. The type is void because we // Whether an entity is a "follower" of our player entity or not. The type is void because we
// don't need any information, this is just a tag we assign to an entity with no data. // don't need any information, this is just a tag we assign to an entity with no data.
@ -49,11 +49,10 @@ fn init(
core: *mach.Core, core: *mach.Core,
renderer: *Renderer, renderer: *Renderer,
app: *App, app: *App,
app_tick: mach.Call(App, .tick), app_mod: mach.Functions(App),
app_deinit: mach.Call(App, .deinit),
) !void { ) !void {
core.on_tick = app_tick.id; core.on_tick = app_mod.id.tick;
core.on_exit = app_deinit.id; core.on_exit = app_mod.id.deinit;
// Create our player entity. // Create our player entity.
const player = try entities.new(); const player = try entities.new();

View file

@ -15,6 +15,7 @@ uniform_buffer: *gpu.Buffer,
pub const mach_module = .renderer; pub const mach_module = .renderer;
// TODO(object)
pub const components = .{ pub const components = .{
.position = .{ .type = Vec3 }, .position = .{ .type = Vec3 },
.rotation = .{ .type = Vec3 }, .rotation = .{ .type = Vec3 },

View file

@ -56,11 +56,10 @@ fn init(
glyphs: *Glyphs.Mod, glyphs: *Glyphs.Mod,
app: *App, app: *App,
core: *mach.Core, core: *mach.Core,
app_tick: mach.Call(App, .tick), app_mod: mach.Functions(App),
app_deinit: mach.Call(App, .deinit),
) !void { ) !void {
core.on_tick = app_tick.id; core.on_tick = app_mod.id.tick;
core.on_exit = app_deinit.id; core.on_exit = app_mod.id.deinit;
// Create a sprite rendering pipeline // Create a sprite rendering pipeline
const texture = glyphs.state().texture; const texture = glyphs.state().texture;

View file

@ -80,12 +80,10 @@ fn init(
text: *gfx.Text.Mod, text: *gfx.Text.Mod,
sprite_pipeline: *gfx.SpritePipeline.Mod, sprite_pipeline: *gfx.SpritePipeline.Mod,
app: *App, app: *App,
app_tick: mach.Call(App, .tick), app_mod: mach.Functions(App),
app_deinit: mach.Call(App, .deinit),
app_audio_state_change: mach.Call(App, .audio_state_change),
) !void { ) !void {
core.on_tick = app_tick.id; core.on_tick = app_mod.id.tick;
core.on_exit = app_deinit.id; core.on_exit = app_mod.id.deinit;
// Configure the audio module to run our audio_state_change system when entities audio finishes // Configure the audio module to run our audio_state_change system when entities audio finishes
// playing // playing

View file

@ -25,6 +25,7 @@ pub const mach_systems = .{ .start, .init, .deinit, .tick, .audio_state_change }
// TODO: banish global allocator // TODO: banish global allocator
var gpa = std.heap.GeneralPurposeAllocator(.{}){}; var gpa = std.heap.GeneralPurposeAllocator(.{}){};
// TODO(object)
pub const components = .{ pub const components = .{
.play_after = .{ .type = f32 }, .play_after = .{ .type = f32 },
}; };
@ -41,12 +42,10 @@ fn init(
core: *mach.Core, core: *mach.Core,
audio: *mach.Audio, audio: *mach.Audio,
app: *App, app: *App,
app_tick: mach.Call(App, .tick), app_mod: mach.Functions(App),
app_deinit: mach.Call(App, .deinit),
app_audio_state_change: mach.Call(App, .audio_state_change),
) !void { ) !void {
core.on_tick = app_tick.id; core.on_tick = app_mod.id.tick;
core.on_exit = app_deinit.id; core.on_exit = app_mod.id.deinit;
// Configure the audio module to send our app's .audio_state_change event when an entity's sound // Configure the audio module to send our app's .audio_state_change event when an entity's sound
// finishes playing. // finishes playing.

View file

@ -19,6 +19,7 @@ pub const mach_module = .app;
pub const mach_systems = .{ .start, .init, .deinit, .tick, .audio_state_change }; pub const mach_systems = .{ .start, .init, .deinit, .tick, .audio_state_change };
// TODO(object)
pub const components = .{ pub const components = .{
.is_bgm = .{ .type = void }, .is_bgm = .{ .type = void },
}; };
@ -40,12 +41,10 @@ fn init(
core: *mach.Core, core: *mach.Core,
audio: *mach.Audio, audio: *mach.Audio,
app: *App, app: *App,
app_tick: mach.Call(App, .tick), app_mod: mach.Functions(App),
app_deinit: mach.Call(App, .deinit),
app_audio_state_change: mach.Call(App, .audio_state_change),
) !void { ) !void {
core.on_tick = app_tick.id; core.on_tick = app_mod.id.tick;
core.on_exit = app_deinit.id; core.on_exit = app_mod.id.deinit;
// Configure the audio module to send our app's .audio_state_change event when an entity's sound // Configure the audio module to send our app's .audio_state_change event when an entity's sound
// finishes playing. // finishes playing.

View file

@ -58,11 +58,10 @@ fn init(
sprite: *gfx.Sprite.Mod, sprite: *gfx.Sprite.Mod,
sprite_pipeline: *gfx.SpritePipeline.Mod, sprite_pipeline: *gfx.SpritePipeline.Mod,
app: *App, app: *App,
app_tick: mach.Call(App, .tick), app_mod: mach.Functions(App),
app_deinit: mach.Call(App, .deinit),
) !void { ) !void {
core.on_tick = app_tick.id; core.on_tick = app_mod.id.tick;
core.on_exit = app_deinit.id; core.on_exit = app_mod.id.deinit;
// We can create entities, and set components on them. Note that components live in a module // We can create entities, and set components on them. Note that components live in a module
// namespace, e.g. the `.mach_gfx_sprite` module could have a 3D `.location` component with a different // namespace, e.g. the `.mach_gfx_sprite` module could have a 3D `.location` component with a different

View file

@ -67,11 +67,10 @@ fn init(
text_pipeline: *gfx.TextPipeline.Mod, text_pipeline: *gfx.TextPipeline.Mod,
text_style: *gfx.TextStyle.Mod, text_style: *gfx.TextStyle.Mod,
app: *App, app: *App,
app_tick: mach.Call(App, .tick), app_mod: mach.Functions(App),
app_deinit: mach.Call(App, .deinit),
) !void { ) !void {
core.on_tick = app_tick.id; core.on_tick = app_mod.id.tick;
core.on_exit = app_deinit.id; core.on_exit = app_mod.id.deinit;
// TODO: a better way to initialize entities with default values // TODO: a better way to initialize entities with default values
// TODO(text): ability to specify other style options (custom font name, font color, italic/bold, etc.) // TODO(text): ability to specify other style options (custom font name, font color, italic/bold, etc.)

View file

@ -5,9 +5,9 @@ const sysaudio = mach.sysaudio;
pub const Opus = @import("mach-opus"); pub const Opus = @import("mach-opus");
pub const name = .mach_audio; pub const mach_module = .mach_audio;
pub const Mod = mach.Mod(@This());
// TODO(object)
pub const components = .{ pub const components = .{
.samples = .{ .type = []const f32 }, .samples = .{ .type = []const f32 },
.channels = .{ .type = u8 }, .channels = .{ .type = u8 },
@ -22,7 +22,7 @@ pub const systems = .{
.audio_tick = .{ .handler = audioTick }, .audio_tick = .{ .handler = audioTick },
}; };
const log = std.log.scoped(name); const log = std.log.scoped(mach_module);
// The number of milliseconds worth of audio to render ahead of time. The lower this number is, the // The number of milliseconds worth of audio to render ahead of time. The lower this number is, the
// less latency there is in playing new audio. The higher this number is, the less chance there is // less latency there is in playing new audio. The higher this number is, the less chance there is

View file

@ -233,17 +233,17 @@ pub fn init(core: *Core) !void {
try core.input.start(); try core.input.start();
} }
pub fn tick(core: *Core, present_frame: mach.Call(Core, .present_frame), runner: mach.Runner) void { pub fn tick(core: *Core, core_mod: mach.Functions(Core)) void {
runner.run(core.on_tick.?); core_mod.run(core.on_tick.?);
runner.run(present_frame.id); core_mod.call(.presentFrame);
} }
pub fn main(core: *Core, present_frame: mach.Call(Core, .presentFrame), runner: mach.Runner) !void { pub fn main(core: *Core, core_mod: mach.Functions(Core)) !void {
if (core.on_tick == null) @panic("core.on_tick callback must be set"); if (core.on_tick == null) @panic("core.on_tick callback must be set");
if (core.on_exit == null) @panic("core.on_exit callback must be set"); if (core.on_exit == null) @panic("core.on_exit callback must be set");
runner.run(core.on_tick.?); core_mod.run(core.on_tick.?);
runner.run(present_frame.id); core_mod.call(.presentFrame);
// If the user doesn't want mach.Core to take control of the main loop, we bail out - the next // If the user doesn't want mach.Core to take control of the main loop, we bail out - the next
// app tick is already scheduled to run in the future and they'll .present_frame to return // app tick is already scheduled to run in the future and they'll .present_frame to return
@ -259,8 +259,8 @@ pub fn main(core: *Core, present_frame: mach.Call(Core, .presentFrame), runner:
// The user wants mach.Core to take control of the main loop. // The user wants mach.Core to take control of the main loop.
if (supports_non_blocking) { if (supports_non_blocking) {
while (core.state().state != .exited) { while (core.state().state != .exited) {
runner.run(core.on_tick.?); core_mod.run(core.on_tick.?);
runner.run(present_frame.id); core_mod.call(.presentFrame);
} }
// 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
@ -268,7 +268,7 @@ pub fn main(core: *Core, present_frame: mach.Call(Core, .presentFrame), runner:
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, .{ core, present_frame.id, runner }); Platform.run(platform_update_callback, .{ core, core_mod });
// 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
@ -277,9 +277,9 @@ pub fn main(core: *Core, present_frame: mach.Call(Core, .presentFrame), runner:
} }
} }
fn platform_update_callback(core: *Core, present_frame: mach.FunctionID, runner: mach.Runner) !bool { fn platform_update_callback(core: *Core, core_mod: mach.Functions(Core)) !bool {
runner.run(core.on_tick.?); core_mod.run(core.on_tick.?);
runner.run(present_frame); core_mod.call(.presentFrame);
return core.state != .exited; return core.state != .exited;
} }
@ -563,7 +563,7 @@ pub fn mousePosition(core: *@This()) Position {
// return core.platform.nativeWindowWin32(); // return core.platform.nativeWindowWin32();
// } // }
pub fn presentFrame(core: *Core, core_deinit: mach.Call(Core, .deinit), runner: mach.Runner) !void { pub fn presentFrame(core: *Core, core_mod: mach.Functions(Core)) !void {
// TODO(object)(window-title) // TODO(object)(window-title)
// // Update windows title // // Update windows title
// var num_windows: usize = 0; // var num_windows: usize = 0;
@ -622,8 +622,8 @@ pub fn presentFrame(core: *Core, core_deinit: mach.Call(Core, .deinit), runner:
.running => {}, .running => {},
.exiting => { .exiting => {
core.state = .deinitializing; core.state = .deinitializing;
runner.run(core.on_exit.?); core_mod.run(core.on_exit.?);
runner.run(core_deinit.id); core_mod.call(.deinit);
}, },
.deinitializing => {}, .deinitializing => {},
.exited => @panic("application not running"), .exited => @panic("application not running"),

View file

@ -13,9 +13,9 @@ const Vec3 = math.Vec3;
const Mat3x3 = math.Mat3x3; const Mat3x3 = math.Mat3x3;
const Mat4x4 = math.Mat4x4; const Mat4x4 = math.Mat4x4;
pub const name = .mach_gfx_sprite; pub const mach_module = .mach_gfx_sprite;
pub const Mod = mach.Mod(@This());
// TODO(object)
pub const components = .{ pub const components = .{
.transform = .{ .type = Mat4x4, .description = .transform = .{ .type = Mat4x4, .description =
\\ The sprite model transformation matrix. A sprite is measured in pixel units, starting from \\ The sprite model transformation matrix. A sprite is measured in pixel units, starting from

View file

@ -4,9 +4,9 @@ const mach = @import("../main.zig");
const gpu = mach.gpu; const gpu = mach.gpu;
const math = mach.math; const math = mach.math;
pub const name = .mach_gfx_sprite_pipeline; pub const mach_module = .mach_gfx_sprite_pipeline;
pub const Mod = mach.Mod(@This());
// TODO(object)
pub const components = .{ pub const components = .{
.texture = .{ .type = *gpu.Texture, .description = .texture = .{ .type = *gpu.Texture, .description =
\\ Texture to use when rendering. The default shader can handle only one texture input. \\ Texture to use when rendering. The default shader can handle only one texture input.

View file

@ -14,9 +14,9 @@ const Mat4x4 = math.Mat4x4;
var gpa = std.heap.GeneralPurposeAllocator(.{}){}; var gpa = std.heap.GeneralPurposeAllocator(.{}){};
pub const name = .mach_gfx_text; pub const mach_module = .mach_gfx_text;
pub const Mod = mach.Mod(@This());
// TODO(object)
pub const components = .{ pub const components = .{
.transform = .{ .type = Mat4x4, .description = .transform = .{ .type = Mat4x4, .description =
\\ The text model transformation matrix. Text is measured in pixel units, starting from \\ The text model transformation matrix. Text is measured in pixel units, starting from

View file

@ -5,9 +5,9 @@ const gfx = mach.gfx;
const gpu = mach.gpu; const gpu = mach.gpu;
const math = mach.math; const math = mach.math;
pub const name = .mach_gfx_text_pipeline; pub const mach_module = .mach_gfx_text_pipeline;
pub const Mod = mach.Mod(@This());
// TODO(object)
pub const components = .{ pub const components = .{
.is_pipeline = .{ .type = void, .description = .is_pipeline = .{ .type = void, .description =
\\ Tag to indicate an entity represents a text pipeline. \\ Tag to indicate an entity represents a text pipeline.

View file

@ -1,9 +1,9 @@
const mach = @import("../main.zig"); const mach = @import("../main.zig");
const math = mach.math; const math = mach.math;
pub const name = .mach_gfx_text_style; pub const mach_module = .mach_gfx_text_style;
pub const Mod = mach.Mod(@This());
// TODO(object)
pub const components = .{ pub const components = .{
// // TODO: ship a default font // // TODO: ship a default font
// .font_name = .{ .type = []const u8, .description = // .font_name = .{ .type = []const u8, .description =

View file

@ -26,8 +26,7 @@ pub const Modules = @import("module.zig").Modules;
pub const ModuleID = @import("module.zig").ModuleID; pub const ModuleID = @import("module.zig").ModuleID;
pub const ModuleFunctionID = @import("module.zig").ModuleFunctionID; pub const ModuleFunctionID = @import("module.zig").ModuleFunctionID;
pub const FunctionID = @import("module.zig").FunctionID; pub const FunctionID = @import("module.zig").FunctionID;
pub const Call = @import("module.zig").Call; pub const Functions = @import("module.zig").Functions;
pub const Runner = @import("module.zig").Runner;
pub const ObjectID = u32; pub const ObjectID = u32;

View file

@ -9,27 +9,69 @@ pub const ModuleFunctionID = u16;
/// Unique identifier for a function within a module, including those only known at runtime. /// Unique identifier for a function within a module, including those only known at runtime.
pub const FunctionID = struct { module_id: ModuleID, fn_id: ModuleFunctionID }; pub const FunctionID = struct { module_id: ModuleID, fn_id: ModuleFunctionID };
pub fn Call(module_tag_or_type: anytype, fn_name_tag: anytype) type { pub fn Mod(comptime M: type) type {
return struct { return struct {
pub const IsMachCall = void; pub const IsMachMod = void;
pub const module_name = module_tag_or_type; pub const module_name = M.mach_module;
pub const fn_name = fn_name_tag; pub const Module = M;
id: FunctionID, id: ModFunctionIDs(M),
}; _ctx: *anyopaque,
}
pub const Runner = struct {
pub const IsMachRunner = void;
ctx: *anyopaque,
_run: *const fn (ctx: *anyopaque, fn_id: FunctionID) void, _run: *const fn (ctx: *anyopaque, fn_id: FunctionID) void,
pub fn run(r: *const @This(), fn_id: FunctionID) void { pub fn run(r: *const @This(), fn_id: FunctionID) void {
r._run(r.ctx, fn_id); r._run(r._ctx, fn_id);
} }
};
pub fn call(r: *const @This(), comptime f: ModuleFunctionName2(M)) void {
const fn_id = @field(r.id, @tagName(f));
r.run(fn_id);
}
};
}
pub fn ModFunctionIDs(comptime Module: type) type {
var fields: []const std.builtin.Type.StructField = &[0]std.builtin.Type.StructField{};
for (Module.mach_systems) |fn_name| {
fields = fields ++ [_]std.builtin.Type.StructField{.{
.name = @tagName(fn_name),
.type = FunctionID,
.default_value = null,
.is_comptime = false,
.alignment = @alignOf(FunctionID),
}};
}
return @Type(.{
.Struct = .{
.layout = .auto,
.is_tuple = false,
.fields = fields,
.decls = &[_]std.builtin.Type.Declaration{},
},
});
}
/// Enum describing all declarations for a given comptime-known module.
// TODO: unify with ModuleFunctionName
fn ModuleFunctionName2(comptime M: type) type {
validate(M);
var enum_fields: []const std.builtin.Type.EnumField = &[0]std.builtin.Type.EnumField{};
var i: u32 = 0;
inline for (M.mach_systems) |fn_tag| {
// TODO: verify decls are Fn or mach.schedule() decl
enum_fields = enum_fields ++ [_]std.builtin.Type.EnumField{.{ .name = @tagName(fn_tag), .value = i }};
i += 1;
}
return @Type(.{
.Enum = .{
.tag_type = if (enum_fields.len > 0) std.math.IntFittingRange(0, enum_fields.len - 1) else u0,
.fields = enum_fields,
.decls = &[_]std.builtin.Type.Declaration{},
.is_exhaustive = true,
},
});
}
pub fn Modules(module_lists: anytype) type { pub fn Modules(module_lists: anytype) type {
inline for (moduleTuple(module_lists)) |module| { inline for (moduleTuple(module_lists)) |module| {
@ -147,16 +189,11 @@ pub fn Modules(module_lists: anytype) type {
@field(args, arg.name) = &@field(m.mods, @tagName(std.meta.Child(arg.type).mach_module)); @field(args, arg.name) = &@field(m.mods, @tagName(std.meta.Child(arg.type).mach_module));
continue :outer; continue :outer;
} }
if (@typeInfo(arg.type) == .Struct and @hasDecl(arg.type, "IsMachCall")) { if (@typeInfo(arg.type) == .Struct and @hasDecl(arg.type, "IsMachMod")) {
const machCall = arg.type; const M = arg.type.Module;
@field(args, arg.name) = .{ var mv: Mod(M) = .{
.id = Module(@field(machCall, "module_name")).getFunction(@field(machCall, "fn_name")), .id = undefined,
}; ._ctx = m.modules,
continue :outer;
}
if (@typeInfo(arg.type) == .Struct and @hasDecl(arg.type, "IsMachRunner")) {
@field(args, arg.name) = Runner{
.ctx = m.modules,
._run = (struct { ._run = (struct {
pub fn run(ctx: *anyopaque, fn_id: FunctionID) void { pub fn run(ctx: *anyopaque, fn_id: FunctionID) void {
const modules2: *Modules(module_lists) = @ptrCast(@alignCast(ctx)); const modules2: *Modules(module_lists) = @ptrCast(@alignCast(ctx));
@ -164,6 +201,10 @@ pub fn Modules(module_lists: anytype) type {
} }
}).run, }).run,
}; };
inline for (M.mach_systems) |m_fn_name| {
@field(mv.id, @tagName(m_fn_name)) = Module(M).getFunction(m_fn_name);
}
@field(args, arg.name) = mv;
continue :outer; continue :outer;
} }
@compileError("mach: function " ++ debug_name ++ " has an invalid argument(" ++ arg.name ++ ") type: " ++ @typeName(arg.type)); @compileError("mach: function " ++ debug_name ++ " has an invalid argument(" ++ arg.name ++ ") type: " ++ @typeName(arg.type));