Skip to content
2 changes: 1 addition & 1 deletion build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ pub fn build(b: *std.Build) !void {
build_options.addOption(SysgpuBackend, "sysgpu_backend", sysgpu_backend);

var examples = [_]Example{
.{ .name = "core-custom-entrypoint", .deps = &.{} },
//.{ .name = "core-custom-entrypoint", .deps = &.{} },
.{ .name = "core-triangle", .deps = &.{} },
.{ .name = "core-transparent-window", .deps = &.{} },
.{ .name = "custom-renderer", .deps = &.{} },
Expand Down
6 changes: 4 additions & 2 deletions build.zig.zon
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
.{
.name = "mach",
.version = "0.4.0",
.mach_zig_version = "2024.11.0-mach",
.paths = .{
"src",
"build.zig",
Expand All @@ -22,8 +23,9 @@
.lazy = true,
},
.mach_objc = .{
.url = "https://pkg.machengine.org/mach-objc/79b6f80c32b14948554958afe72dace261b14afc.tar.gz",
.hash = "12203675829014e69be2ea7c126ecf25d403009d336b7ca5f6e7c4ccede826c8e597",
//.path = "../mach-objc-dev",
.url = "https://github.com/foxnne/mach-objc/archive/59b75ebc1253a2517a08940086e66e574e63f2e1.tar.gz",
.hash = "1220097d6c23dc1f4ec7c46a8f94171b4a09f954ee09156951db81edb20405a7ebf8",
.lazy = true,
},
.xcode_frameworks = .{
Expand Down
11 changes: 8 additions & 3 deletions examples/core-transparent-window/App.zig
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ const gpu = mach.gpu;

const App = @This();

const title = "core-transparent-window [ {d}fps ] [ Input {d}hz ]";

// The set of Mach modules our application may use.
pub const Modules = mach.Modules(.{
mach.Core,
Expand Down Expand Up @@ -36,8 +38,7 @@ pub fn init(
core.on_exit = app_mod.id.deinit;

const window = try core.windows.new(.{
.title = "core-transparent-window",
.vsync_mode = .double,
.title = try std.fmt.allocPrintZ(core.allocator, title, .{ 0, 0 }),
.transparent = true,
});

Expand Down Expand Up @@ -156,11 +157,15 @@ pub fn tick(app: *App, core: *mach.Core) void {
defer command.release();
window.queue.submit(&[_]*gpu.CommandBuffer{command});

mach.sysgpu.Impl.deviceTick(window.device);

window.swap_chain.present();

if (app.title_timer.read() >= 1.0) {
app.title_timer.reset();
// TODO(object): window-title

core.windows.set(app.window, .title, std.fmt.allocPrintZ(core.allocator, "core-transparent-window [ {d}fps ] [ Input {d}hz ]", .{ core.frame.rate, core.input.rate }) catch unreachable);
core.windows.set(app.window, .title, std.fmt.allocPrintZ(core.allocator, title, .{ window.frame.rate, core.frame.rate }) catch unreachable);
}

if (app.color_time >= 4.0 or app.color_time <= 0.0) {
Expand Down
53 changes: 28 additions & 25 deletions examples/core-triangle/App.zig
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ pub const mach_module = .app;

pub const mach_systems = .{ .main, .init, .tick, .deinit };

pub const window_title = "core_triangle [ {d}fps ] [ Input {d}hz ]";

pub const main = mach.schedule(.{
.{ mach.Core, .init },
.{ App, .init },
Expand All @@ -23,6 +25,7 @@ pub const main = mach.schedule(.{
window: mach.ObjectID,
title_timer: mach.time.Timer,
pipeline: *gpu.RenderPipeline,
frames: usize = 0,

pub fn init(
core: *mach.Core,
Expand All @@ -33,7 +36,11 @@ pub fn init(
core.on_exit = app_mod.id.deinit;

const window = try core.windows.new(.{
.title = "core-triangle",
.title = try std.fmt.allocPrintZ(
core.allocator,
window_title,
.{ 0, 0 },
),
});

// Store our render pipeline in our module's state, so we can access it later on.
Expand Down Expand Up @@ -81,10 +88,7 @@ fn setupPipeline(core: *mach.Core, app: *App, window_id: mach.ObjectID) !void {
app.pipeline = window.device.createRenderPipeline(&pipeline_descriptor);
}

// TODO(object): window-title
// try updateWindowTitle(core);

pub fn tick(app: *App, core: *mach.Core) void {
pub fn tick(app: *App, core: *mach.Core) !void {
while (core.nextEvent()) |event| {
switch (event) {
.window_open => |ev| {
Expand Down Expand Up @@ -134,28 +138,27 @@ pub fn tick(app: *App, core: *mach.Core) void {
defer command.release();
window.queue.submit(&[_]*gpu.CommandBuffer{command});

// update the window title every second
// if (app.title_timer.read() >= 1.0) {
// app.title_timer.reset();
// // TODO(object): window-title
// // try updateWindowTitle(core);
// }
mach.sysgpu.Impl.deviceTick(window.device);

window.swap_chain.present();

app.frames += 1;

//update the window title every second
if (app.title_timer.read() >= 1.0) {
app.title_timer.reset();

core.allocator.free(window.title);
core.windows.set(app.window, .title, try std.fmt.allocPrintZ(
core.allocator,
window_title,
.{ app.frames, core.frame.rate },
));

app.frames = 0;
}
}

pub fn deinit(app: *App) void {
app.pipeline.release();
}

// TODO(object): window-title
// fn updateWindowTitle(core: *mach.Core) !void {
// try core.printTitle(
// core.main_window,
// "core-custom-entrypoint [ {d}fps ] [ Input {d}hz ]",
// .{
// // TODO(Core)
// core.frameRate(),
// core.inputRate(),
// },
// );
// core.schedule(.update);
// }
4 changes: 4 additions & 0 deletions examples/custom-renderer/Renderer.zig
Original file line number Diff line number Diff line change
Expand Up @@ -171,4 +171,8 @@ pub fn renderFrame(
var command = encoder.finish(&.{ .label = label });
defer command.release();
window.queue.submit(&[_]*gpu.CommandBuffer{command});

mach.sysgpu.Impl.deviceTick(window.device);

window.swap_chain.present();
}
4 changes: 4 additions & 0 deletions examples/glyphs/App.zig
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,10 @@ pub fn tick(
command.release();
render_pass.release();

mach.sysgpu.Impl.deviceTick(window.device);

window.swap_chain.present();

app.frame_count += 1;
app.time += delta_time;

Expand Down
4 changes: 4 additions & 0 deletions examples/hardware-check/App.zig
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,10 @@ pub fn tick(
command.release();
render_pass.release();

mach.sysgpu.Impl.deviceTick(window.device);

window.swap_chain.present();

app.frame_count += 1;
app.time += delta_time;

Expand Down
4 changes: 4 additions & 0 deletions examples/piano/App.zig
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,10 @@ pub fn tick(
var command = encoder.finish(&.{ .label = label });
defer command.release();
window.queue.submit(&[_]*gpu.CommandBuffer{command});

mach.sysgpu.Impl.deviceTick(window.device);

window.swap_chain.present();
}

fn fillTone(app: *App, audio: *mach.Audio, frequency: f32) ![]align(mach.Audio.alignment) const f32 {
Expand Down
4 changes: 4 additions & 0 deletions examples/play-opus/App.zig
Original file line number Diff line number Diff line change
Expand Up @@ -190,4 +190,8 @@ pub fn tick(
var command = encoder.finish(&.{ .label = label });
defer command.release();
window.queue.submit(&[_]*gpu.CommandBuffer{command});

mach.sysgpu.Impl.deviceTick(window.device);

window.swap_chain.present();
}
4 changes: 4 additions & 0 deletions examples/sprite/App.zig
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,10 @@ pub fn tick(
command.release();
render_pass.release();

mach.sysgpu.Impl.deviceTick(window.device);

window.swap_chain.present();

app.frame_count += 1;
app.time += delta_time;

Expand Down
4 changes: 4 additions & 0 deletions examples/text/App.zig
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,10 @@ pub fn tick(
command.release();
render_pass.release();

mach.sysgpu.Impl.deviceTick(window.device);

window.swap_chain.present();

app.frame_count += 1;
app.time += delta_time;

Expand Down
74 changes: 39 additions & 35 deletions src/Core.zig
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const EventQueue = std.fifo.LinearFifo(Event, .Dynamic);

pub const mach_module = .mach_core;

pub const mach_systems = .{ .main, .init, .tick, .presentFrame, .deinit };
pub const mach_systems = .{ .main, .init, .tick, .deinit };

// Set track_fields to true so that when these field values change, we know about it
// and can update the platform windows.
Expand All @@ -24,6 +24,13 @@ windows: mach.Objects(
// TODO: allocation/free strategy
title: [:0]const u8 = "Mach Window",

/// Callback called directly from backend when a frame is needed to be rendered
/// Will match the monitors refresh rate
on_tick: ?mach.FunctionID = null,

/// Frequency to get ticks per second
frame: mach.time.Frequency = .{ .target = 0 },

/// Texture format of the framebuffer (read-only)
framebuffer_format: gpu.Texture.Format = .bgra8_unorm,

Expand All @@ -35,9 +42,6 @@ windows: mach.Objects(
/// Will be updated to reflect the actual framebuffer dimensions after window creation.
framebuffer_height: u32 = 1080 / 2,

/// Vertical sync mode, prevents screen tearing.
vsync_mode: VSyncMode = .none,

/// Window display mode: fullscreen, windowed or borderless fullscreen
display_mode: DisplayMode = .windowed,

Expand All @@ -51,9 +55,6 @@ windows: mach.Objects(
/// Height of the window in virtual pixels
height: u32 = 1080 / 2,

/// Target frames per second
refresh_rate: u32 = 0,

/// Whether window decorations (titlebar, borders, etc.) should be shown.
///
/// Has no effect on windows who DisplayMode is .fullscreen or .fullscreen_borderless
Expand Down Expand Up @@ -111,7 +112,6 @@ state: enum {
} = .running,

frame: mach.time.Frequency,
input: mach.time.Frequency,

// Internal module state
allocator: std.mem.Allocator,
Expand All @@ -136,12 +136,10 @@ pub fn init(core: *Core) !void {
.events = events,
.input_state = .{},

.input = .{ .target = 0 },
.frame = .{ .target = 1 },
};

try core.frame.start();
try core.input.start();
}

pub fn initWindow(core: *Core, window_id: mach.ObjectID) !void {
Expand Down Expand Up @@ -204,13 +202,10 @@ pub fn initWindow(core: *Core, window_id: mach.ObjectID) !void {
.format = .bgra8_unorm,
.width = core_window.framebuffer_width,
.height = core_window.framebuffer_height,
.present_mode = switch (core_window.vsync_mode) {
.none => .immediate,
.double => .fifo,
.triple => .mailbox,
},
.present_mode = .fifo,
};
core_window.swap_chain = core_window.device.createSwapChain(core_window.surface, &core_window.swap_chain_descriptor);
try core_window.frame.start();
core.pushEvent(.{ .window_open = .{ .window_id = window_id } });
}

Expand All @@ -219,25 +214,11 @@ pub fn tick(core: *Core, core_mod: mach.Mod(Core)) !void {
// during application execution, rendering to multiple windows, etc.) and how
// that relates to Platform.tick being responsible for both handling window updates
// (like title/size changes) and window creation, plus multi-threaded rendering.
try Platform.tick(core);
try Platform.tick(core, core_mod);

core_mod.run(core.on_tick.?);
core_mod.call(.presentFrame);
}

pub fn presentFrame(core: *Core, core_mod: mach.Mod(Core)) !void {
var windows = core.windows.slice();
while (windows.next()) |window_id| {
var core_window = core.windows.getValue(window_id);
defer core.windows.setValueRaw(window_id, core_window);

mach.sysgpu.Impl.deviceTick(core_window.device);

core_window.swap_chain.present();
}

// Record to frame rate frequency monitor that a frame was finished.
core.frame.tick();
//core_mod.call(.presentFrame);

switch (core.state) {
.running => {},
Expand All @@ -255,9 +236,20 @@ pub fn main(core: *Core, core_mod: mach.Mod(Core)) !void {
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");

try Platform.tick(core);
try Platform.tick(core, core_mod);
core_mod.run(core.on_tick.?);
core_mod.call(.presentFrame);
core.frame.tick();

switch (core.state) {
.running => {},
.exiting => {
core.state = .deinitializing;
core_mod.run(core.on_exit.?);
core_mod.call(.deinit);
},
.deinitializing => {},
.exited => @panic("application not running"),
}

// Platform drives the main loop.
Platform.run(platform_update_callback, .{ core, core_mod });
Expand All @@ -272,10 +264,22 @@ fn platform_update_callback(core: *Core, core_mod: mach.Mod(Core)) !bool {
// during application execution, rendering to multiple windows, etc.) and how
// that relates to Platform.tick being responsible for both handling window updates
// (like title/size changes) and window creation, plus multi-threaded rendering.
try Platform.tick(core);

try Platform.tick(core, core_mod);

core_mod.run(core.on_tick.?);
core_mod.call(.presentFrame);
core.frame.tick();

switch (core.state) {
.running => {},
.exiting => {
core.state = .deinitializing;
core_mod.run(core.on_exit.?);
core_mod.call(.deinit);
},
.deinitializing => {},
.exited => @panic("application not running"),
}

return core.state != .exited;
}
Expand Down
Loading
Loading