Mach v0.3 has been released! For all the details check out the announcement

Migration notes

To learn more about Mach’s library stability guarantees, check out the libraries overview page. This page provides migration guides for Mach libraries-walking you through how to update your code to the latest version.

mach.core: package URL change (2024-03-06)

As part of #1165 mach-core is now part of the Mach standard library. The code now lives in https://github.com/hexops/mach and can be imported as @import("mach").core.

Thanks to lazy code evaluation, and lazy dependencies recently added to Zig, we can have this code live in the same repository which makes testing and contributing easier - while still enabling someone to ‘just use mach [core,sysaudio,sysgpu] without the entire engine’.

To update your mach-core project, make these changes.

See the Mach standard library for details on how the stdlib works, lazy dependencies, etc.

mach-core: updated to Zig 2024.1.0-mach (2024-01-14)

Mach core 967d9b7 and above now uses Zig 2024.1.0-mach. This is the first mach-core version to follow nominated Zig versions.

To update your mach-core project, make these changes.

To update your Zig code in general, see: tips on upgrading your Zig code.

mach-core: Zig version update (2023-10-17)

Mach core 331ce61 and above now uses Zig version 0.12.0-dev.978+78855bd21.

To update your mach-core project, follow this diff.

mach-core: build API improvements (2023-09-24)

Imports now have a mach- prefix:

-const core = @import("core");
+const core = @import("mach-core");

The module() helper (you likely do not use this) has been replaced with a proper Zig module accessible via b.dependency("mach_core", .{...}).module("mach-core")

mach-core: build API improvements (2023-09-17)

Zig 0.12.0-dev.389+61b70778b is now in use (previously 0.12.0-dev.21+ac95cfe44); and your build.zig.zon file no longer needs to specify transitive dependencies, and the build.zig API has changed slightly:

mach-core: API design changes (2023-08-07)

printTitle

Instead of writing e.g. this before:

const title = try std.fmt.bufPrintZ(&core.title, "Sprite2D [ FPS: {d} ]", .{@floor(1 / delta_time)});
core.setTitle(title);

It is now possible to write just:

try core.printTitle("Sprite2D [ FPS: {d} ]", .{@floor(1 / delta_time)});

Delta time, frame rate

Internal timers now provide reasonable default methods of measuring frame rate, input rate, and frame delta time.

  • core.frameRate()
  • core.inputRate()
  • core.delta_time (f32 seconds)
  • core.delta_time_ns (u64 nanoseconds)

mach.Core is now a global

  • Your App struct no longer needs to have a field core: mach.Core, (before it was required, and the name MUST be core)
  • APIs that were accessible through a *Core instance before, e.g. app.core.frameRate(), are now accessible as global methods. e.g. @import("core").frameRate()
  • core.init no longer needs an allocator, instead core.allocator is used - which you can change at any point before calling core.init()

Before

pub const App = @This();

var gpa = std.heap.GeneralPurposeAllocator(.{}){};

core: mach.Core,

pub fn init(app: *App) !void {
    try app.core.init(gpa.allocator(), .{});
    app.* = .{
        // ...
    };
}

pub fn deinit(app: *App) void {
    defer _ = gpa.deinit();
    defer app.core.deinit();
    // ...
}

pub fn update(app: *App) !bool {
    // ...
    return false;
}

After

pub const App = @This();

pub fn init(app: *App) !void {
    try core.init(.{});
    app.* = .{
        // ...
    };
}

pub fn deinit(app: *App) void {
    defer core.deinit();
    // ...
}

pub fn update(app: *App) !bool {
    // ...
    return false;
}

Custom entrypoints

It’s now easier than ever to write a custom entrypoint for your application (e.g. pub fn main) if you really want/need to:

Fields instead of getters

A few getters have been turned into fields instead:

  • core.device() -> core.device
  • core.device().getQueue() -> core.queue
  • core.descriptor() -> core.descriptor
  • core.adapter() -> core.adapter
  • core.swapChain() -> core.swap_chain

mach-core: build system changes

The following fields/functions from mach.App in build.zig have been removed:

  • .step
  • .install()
  • .addRunArtifact()
  • .getInstallStep()
  • .link(.{ .gpu_dawn_options = .{} })

The following have been added:

  • .compile.step
  • .install.step
  • .run.step
  • App.init(.{ .gpu_dawn_options = .{} })

Suggested usage is now e.g.:

const app = try core.App.init(...);

const install_step = b.step("install", "Install " ++ example.name);
install_step.dependOn(&app.install.step);
b.getInstallStep().dependOn(install_step);

const run_step = b.step("run", "Run " ++ example.name);
run_step.dependOn(&app.run.step);

It is also possible to add a ‘compile only’ step now, for e.g. checking code merely builds should you want to:

const compile_step = b.step("compile", "Install " ++ example.name);
compile_step.dependOn(&app.compile.step);

mach-core: multithreaded rendering & standalone usage

mach-core is now available as a 100% standalone repository / Zig package. The getting started documentation has been updated to reflect this.

Additionally, we have landed multi-threaded rendering support which allows native applications to run at e.g. 60FPS while handling input events at 240hz. It also enables butter-smooth window resizing.

API changes:

  • core.framebufferSize() has been removed in favor of core.descriptor().width and core.descriptor().height
  • App.init and App.deinit are executed on the main thread, App.update is executed in a separate thread.
  • Input events are enqueued/bufferred, and you can poll them whenever you like (e.g. you may handle input mid-frame if you like.)
  • setWaitTimeout / “waiting” APIs are being redesigned and are not yet functional. e.g. for the case of a desktop app that you rightfully want to be low-power, in this scenario you need a way to slow event polling of both the main thread and rendering thread - which is not yet possible.

mach-glfw: package manager usage

If you are a user of mach-glfw, note that we have adopted the experimental Zig package manager. It is not perfect yet and there are many papercuts; for details on how to update your codebase please see this

mach-core: v0.2 API redesign

2023-01-27 - affects all mach-core users

Mach v0.2 brings a complete redesign of the mach-core API. To upgrade your application see the upgrade guide

mach-glfw: error handling improvements

2023-01-10 - affects all mach-glfw users

We’ve made another error handling improvement to the mach-glfw API:

  • glfw.getError has been renamed to glfw.getErrorCode (returns a Zig error type still)
  • glfw.getError instead now returns a struct with both the error message and Zig error type.
  • glfw.clearError has been added, a smaller helper to make writing defer glfw.clearError() nicer than before (defer glfw.getErrorCode() catch {};).

glfw: error handling changes

2023-01-08 - affects all mach-glfw users

We have completely overhauled the mach-glfw error handling approach to help users better avoid footguns and ultimately improve the ability of Zig GLFW applications to run on more obscure X11 window managers and Wayland in general.