From df3bec7ce4b6299ca15e3cb2c76fb524d1a8e2d3 Mon Sep 17 00:00:00 2001 From: slimsag Date: Wed, 20 Dec 2023 21:00:39 +0000 Subject: [PATCH] deploy: eabf7eedde5f448e8144e677f01a8647ad96e9f7 --- about/faq/index.html | 2 +- about/goals/index.html | 2 +- about/index.html | 2 +- about/known-issues/index.html | 2 +- about/nixos-usage/index.html | 2 +- about/platforms/index.html | 2 +- about/stability/index.html | 2 +- about/style/index.html | 2 +- about/zig-version/index.html | 2 +- core/examples/index.html | 2 +- core/getting-started/index.html | 2 +- core/index.html | 2 +- core/migrations/2023-core-api/index.html | 2 +- core/migrations/index.html | 2 +- discord/index.html | 2 +- engine/gpu/errors/index.html | 2 +- engine/gpu/index.html | 2 +- engine/gpu/memory/index.html | 2 +- engine/index.html | 2 +- engine/math/coordinate-system/index.html | 4 ++-- engine/math/index.html | 2 +- engine/math/matrix-storage/index.html | 2 +- engine/modularity/index.html | 2 +- engine/roadmap/index.html | 2 +- engine/stdlib/index.html | 2 +- gpu/index.html | 2 +- ...caa9afec046a45d465b64074e1ec4f88737dd7b.css} | 2 +- ...9698b9d_62022_filter_4912858429100723217.png | Bin 0 -> 67306 bytes pkg/c/index.html | 2 +- pkg/fastfilter/index.html | 2 +- pkg/index.html | 2 +- pkg/mach-basisu/index.html | 2 +- pkg/mach-dusk/index.html | 2 +- pkg/mach-earcut/index.html | 2 +- pkg/mach-ecs/index.html | 2 +- pkg/mach-flac/index.html | 2 +- pkg/mach-freetype/index.html | 2 +- pkg/mach-gamemode/index.html | 2 +- pkg/mach-glfw/index.html | 2 +- pkg/mach-gpu-dawn/index.html | 2 +- pkg/mach-gpu/index.html | 2 +- pkg/mach-model3d/index.html | 2 +- pkg/mach-opus/index.html | 2 +- pkg/mach-sysaudio/index.html | 2 +- pkg/mach-sysgpu/index.html | 2 +- pkg/mach-sysjs/index.html | 2 +- 46 files changed, 46 insertions(+), 46 deletions(-) rename layouts/{docs.eba24e7b90282072fe2e748d2511c23c5000ad5f00b0ef3bf03f8ee20348f6e6.css => docs.1c549ac5e9eaa1dc4997cd957caa9afec046a45d465b64074e1ec4f88737dd7b.css} (94%) create mode 100644 opengraph/template_hu102e162f47cb7d8ae9eb284939698b9d_62022_filter_4912858429100723217.png diff --git a/about/faq/index.html b/about/faq/index.html index a2e0483..0cba343 100644 --- a/about/faq/index.html +++ b/about/faq/index.html @@ -5,4 +5,4 @@ GitHub Discord
Mach v0.2 has been released! For all the details check out the announcement

Frequently asked questions

When will the engine be released?

There won’t be one major event, rather it will be a bunch of small/incremental releases building up to it. You can look at the engine roadmap to get an idea of what we’re working on currently, and what will come later.

Can I use Mach to make a simple 2D game?

Using the engine? Definitely not today.

Using Mach core, maybe:

  • There is a sprite2d example which lets you load a JSON texture atlas and draws sprites on the screen you could use as a starting point. However, it is ~400 lines of code, a good amount of that is low-level graphics programming.
  • You will need to bring your own:
    • Math library
    • Physics library
    • GUI library
    • Text rendering (though we have freetype bindings you can use, text rendering is quite involved.)

If what you are looking for is a high-level simple 2D graphics API, we don’t have that today. I suggest you look elsewhere for right now; I’m a big fan of Raylib and there are people using it from Zig.

We are actively working on higher-level engine APIs that will make simple 2D games nice to build in Zig, but they’re not ready for use yet. See the engine roadmap for details.

Can I use Mach to make a simple 3D game?

Using the engine? Definitely not today.

Using Mach core, maybe:

  • There are examples for 3D model loading, however the examples for this are ~1000 lines of code and a good amount of the code is low-level graphics programming.
  • You will need to bring your own:
    • Math library
    • Physics library
    • GUI library
    • Text rendering (though we have freetype bindings you can use, text rendering is quite involved.)

It will be a while before we have good high-level 3D graphics support; a lot of other pieces need to land first. See the engine roadmap for details.

\ No newline at end of file + Donate
Mach v0.2 has been released! For all the details check out the announcement

Frequently asked questions

When will the engine be released?

There won’t be one major event, rather it will be a bunch of small/incremental releases building up to it. You can look at the engine roadmap to get an idea of what we’re working on currently, and what will come later.

Can I use Mach to make a simple 2D game?

Using the engine? Definitely not today.

Using Mach core, maybe:

  • There is a sprite2d example which lets you load a JSON texture atlas and draws sprites on the screen you could use as a starting point. However, it is ~400 lines of code, a good amount of that is low-level graphics programming.
  • You will need to bring your own:
    • Math library
    • Physics library
    • GUI library
    • Text rendering (though we have freetype bindings you can use, text rendering is quite involved.)

If what you are looking for is a high-level simple 2D graphics API, we don’t have that today. I suggest you look elsewhere for right now; I’m a big fan of Raylib and there are people using it from Zig.

We are actively working on higher-level engine APIs that will make simple 2D games nice to build in Zig, but they’re not ready for use yet. See the engine roadmap for details.

Can I use Mach to make a simple 3D game?

Using the engine? Definitely not today.

Using Mach core, maybe:

  • There are examples for 3D model loading, however the examples for this are ~1000 lines of code and a good amount of the code is low-level graphics programming.
  • You will need to bring your own:
    • Math library
    • Physics library
    • GUI library
    • Text rendering (though we have freetype bindings you can use, text rendering is quite involved.)

It will be a while before we have good high-level 3D graphics support; a lot of other pieces need to land first. See the engine roadmap for details.

\ No newline at end of file diff --git a/about/goals/index.html b/about/goals/index.html index f11aeb4..8ca45b0 100644 --- a/about/goals/index.html +++ b/about/goals/index.html @@ -5,7 +5,7 @@ GitHub Discord
Mach v0.2 has been released! For all the details check out the announcement

Mach project goals

This page covers more broad project goals that apply to all Mach projects, code, standalone libraries, etc.

Engine goals

See this page.

Zero-fuss installation

no more apt-get install

All too often we see people get a sudden burst of inspiration: "I'm going to finally start my game/app this weekend!" only to see them utlimately get bogged down by setting up their dev environment, ending their weekend with "well.. at least I got the hello world running" - there is no greater drain on human inspiration.

This is why Mach focuses so heavily on zero-fuss installation: you only need Zig, Git, and Curl to use Mach. Nothing else. No more `apt-get install` lists. No more cmake/ninja/dependency-hell.

Mach is able to achieve this by doing the heavy lifting of compiling C/C++ dependencies by source for you, using Zig as the C/C++ compiler. We build GLFW, Google Chrome’s WebGPU implementation, and much more using just Zig as a C/C++ compiler and avoid make/cmake/ninja/etc by taking on the hard work of using Zig as the build system too.

In some cases building from source can take a few minutes (like building Google Chrome’s WebGPU implementation) and we don’t want you to have to wait, so we provide binaries by default in that specific case which are used when you execute zig build. We understand the importance of 100% from-source builds, however, and so we make them possible at the flip of a switch DAWN_FROM_SOURCE=true zig build

Seamless cross-compilation

At the flip of a switch it is possible to cross-compile to nearly any target:

$ zig build -Dtarget=x86_64-windows
+ Donate
Mach v0.2 has been released! For all the details check out the announcement

Mach project goals

This page covers more broad project goals that apply to all Mach projects, code, standalone libraries, etc.

Engine goals

See this page.

Zero-fuss installation

no more apt-get install

All too often we see people get a sudden burst of inspiration: "I'm going to finally start my game/app this weekend!" only to see them utlimately get bogged down by setting up their dev environment, ending their weekend with "well.. at least I got the hello world running" - there is no greater drain on human inspiration.

This is why Mach focuses so heavily on zero-fuss installation: you only need Zig, Git, and Curl to use Mach. Nothing else. No more `apt-get install` lists. No more cmake/ninja/dependency-hell.

Mach is able to achieve this by doing the heavy lifting of compiling C/C++ dependencies by source for you, using Zig as the C/C++ compiler. We build GLFW, Google Chrome’s WebGPU implementation, and much more using just Zig as a C/C++ compiler and avoid make/cmake/ninja/etc by taking on the hard work of using Zig as the build system too.

In some cases building from source can take a few minutes (like building Google Chrome’s WebGPU implementation) and we don’t want you to have to wait, so we provide binaries by default in that specific case which are used when you execute zig build. We understand the importance of 100% from-source builds, however, and so we make them possible at the flip of a switch DAWN_FROM_SOURCE=true zig build

Seamless cross-compilation

At the flip of a switch it is possible to cross-compile to nearly any target:

$ zig build -Dtarget=x86_64-windows
 $ zig build -Dtarget=x86_64-linux-gnu
 $ zig build -Dtarget=x86_64-macos
 $ zig build -Dtarget=aarch64-macos
diff --git a/about/index.html b/about/index.html
index 304c783..0caacf8 100644
--- a/about/index.html
+++ b/about/index.html
@@ -5,5 +5,5 @@
 GitHub
 Discord
 
Mach v0.2 has been released! For all the details check out the announcement

About the project


To learn more about Mach, understand what it is, etc. please see our homepage, and consider joining our community:

Discord + Donate
Mach v0.2 has been released! For all the details check out the announcement

About the project


To learn more about Mach, understand what it is, etc. please see our homepage, and consider joining our community:

Discord GitHub

Early stages

Mach is still in its infancy - things are improving rapidly, not all APIs are stable, and we’re missing lots of basic things. We’re simple humans building towards a dream, so please try your best to help us towards that.

Contributing

We’d love your help building Mach! There’s lots to do, and little time - so whether you’re an experienced gamedev, or just someone interested in learning Zig, there are likely ways you could help us towards our dreams. There are both small bugs and docs to improve, as well as large architecture challenges to sort out.

The best way to contribute is to familiarize yourself with what’s missing today: there’s no better way to do this than to think about what you would need to build your own game/app using it, see what you would need first hand, and then help us by proposing/discussing those changes.

Be sure to join our Discord as that lets us help you faster so we can reach the finish line sooner. :)

Improve this site

We’re very appreciative of typo fixes, documentation improvements, etc. - simply file an issue or send us a pull-request!

Large design/opinionated changes can be a time sink for us and you, and can often devolve into rocket-shedding, so please don’t send PRs for such - instead we prefer you simply file issues with constructive feedback (what you dislike, why, and what you would prefer) in such cases so we can take your perspective into account.

\ No newline at end of file diff --git a/about/known-issues/index.html b/about/known-issues/index.html index 5f472c2..f7cb1d3 100644 --- a/about/known-issues/index.html +++ b/about/known-issues/index.html @@ -5,7 +5,7 @@ GitHub Discord
Mach v0.2 has been released! For all the details check out the announcement

Known issues

If you’re trying to run the Mach examples or similar, you may be running into one of these known issues.

nixOS

If you are using nixOS, we have tips on how to use Mach with it here.

Windows

File not found

If you encounter an error like this:

image

Windows does not have symlinks enabled, or Git is not configured to use them. This is very annoying and has been reported to Microsoft.

Two solutions exist:

  1. Enable symlinks in Windows:
  2. Build and run native Windows binaries from inside WSL:
    • cd mach-examples
    • zig build -Dtarget=x86_64-windows textured-cube
    • Run the native Windows exe in ./zig-out/bin/foo.exe

“SSL certificate problem: unable to get local issuer certificate”

This is a curl SSL CA issue, you may want to Google for proper solutions on your system. That said, you can set CURL_INSECURE=true and retry to disable SSL verification if you want to workaround the issue.

Unresponsive white window

You may be encountering this issue:

  • Symptoms: unresponsive white window, Windows 11
  • Suspected cause: Corsair iCue, other bad system drivers
  • Workaround: reboot Windows

Linux

How can I switch between Wayland/X11?

Mach builds with support for both Wayland and X11 in the same binary. By default, Mach will make use of X11 if it is available.

You can force the use of Wayland by setting DISPLAY to an empty string, e.g. DISPLAY= ./triangle.

“Error: vkCreateInstance failed with VK_ERROR_INCOMPATIBLE_DRIVER”

Some distros require packages to be installed to support the Vulkan graphics API.

For instance, Arch Linux has specific packages for Nvidia, Intel and AMD GPUs.

You may also try using OpenGL using the env var MACH_GPU_BACKEND=opengl.

“Error: Couldn’t load Vulkan. Searched /tmp/mach/gpu/zig-out/bin/libvulkan.so.1”

We’re aware of a bug failing to find libvulkan.so on some Linux distros such as Guix.

Error: Couldn't load Vulkan. Searched /tmp/mach/gpu/zig-out/bin/libvulkan.so.1, /tmp/mach/gpu/zig-out/bin/libvulkan.so.1, libvulkan.so.1.
+ Donate
Mach v0.2 has been released! For all the details check out the announcement

Known issues

If you’re trying to run the Mach examples or similar, you may be running into one of these known issues.

nixOS

If you are using nixOS, we have tips on how to use Mach with it here.

Windows

File not found

If you encounter an error like this:

image

Windows does not have symlinks enabled, or Git is not configured to use them. This is very annoying and has been reported to Microsoft.

Two solutions exist:

  1. Enable symlinks in Windows:
  2. Build and run native Windows binaries from inside WSL:
    • cd mach-examples
    • zig build -Dtarget=x86_64-windows textured-cube
    • Run the native Windows exe in ./zig-out/bin/foo.exe

“SSL certificate problem: unable to get local issuer certificate”

This is a curl SSL CA issue, you may want to Google for proper solutions on your system. That said, you can set CURL_INSECURE=true and retry to disable SSL verification if you want to workaround the issue.

Unresponsive white window

You may be encountering this issue:

  • Symptoms: unresponsive white window, Windows 11
  • Suspected cause: Corsair iCue, other bad system drivers
  • Workaround: reboot Windows

Linux

How can I switch between Wayland/X11?

Mach builds with support for both Wayland and X11 in the same binary. By default, Mach will make use of X11 if it is available.

You can force the use of Wayland by setting DISPLAY to an empty string, e.g. DISPLAY= ./triangle.

“Error: vkCreateInstance failed with VK_ERROR_INCOMPATIBLE_DRIVER”

Some distros require packages to be installed to support the Vulkan graphics API.

For instance, Arch Linux has specific packages for Nvidia, Intel and AMD GPUs.

You may also try using OpenGL using the env var MACH_GPU_BACKEND=opengl.

“Error: Couldn’t load Vulkan. Searched /tmp/mach/gpu/zig-out/bin/libvulkan.so.1”

We’re aware of a bug failing to find libvulkan.so on some Linux distros such as Guix.

Error: Couldn't load Vulkan. Searched /tmp/mach/gpu/zig-out/bin/libvulkan.so.1, /tmp/mach/gpu/zig-out/bin/libvulkan.so.1, libvulkan.so.1.
     at operator() (/home/runner/work/mach-gpu-dawn/mach-gpu-dawn/libs/dawn/src/dawn/native/vulkan/BackendVk.cpp:198)
     at Initialize (/home/runner/work/mach-gpu-dawn/mach-gpu-dawn/libs/dawn/src/dawn/native/vulkan/BackendVk.cpp:203)
     at Create (/home/runner/work/mach-gpu-dawn/mach-gpu-dawn/libs/dawn/src/dawn/native/vulkan/BackendVk.cpp:165)
diff --git a/about/nixos-usage/index.html b/about/nixos-usage/index.html
index 217825b..057135c 100644
--- a/about/nixos-usage/index.html
+++ b/about/nixos-usage/index.html
@@ -13,7 +13,7 @@
 GitHub
 Discord
 
Mach v0.2 has been released! For all the details check out the announcement

Using Mach on nixOS

If you use nixOS and want to use Mach, this document is for you.

usage via nix-ld (preferred, requires unstable)

Use nix-ld following e.g. this article.

For example this shell:

with import <nixpkgs> {};
+ Donate
Mach v0.2 has been released! For all the details check out the announcement

Using Mach on nixOS

If you use nixOS and want to use Mach, this document is for you.

usage via nix-ld (preferred, requires unstable)

Use nix-ld following e.g. this article.

For example this shell:

with import <nixpkgs> {};
 mkShell {
   NIX_LD_LIBRARY_PATH = lib.makeLibraryPath [
     pkgs.xorg.libX11
diff --git a/about/platforms/index.html b/about/platforms/index.html
index bf0f887..08556d5 100644
--- a/about/platforms/index.html
+++ b/about/platforms/index.html
@@ -5,4 +5,4 @@
 GitHub
 Discord
 
Mach v0.2 has been released! For all the details check out the announcement

Platform support

We aim to support a broad array of operating systems and architectures, as long as they are reasonably used by desktop/mobile consumers, or someone would like to contribute and maintain support for them.

OSx86_64aarch64WebAssemblynotes
macOSlast 3 major versions supported;
Windows🏃windows 7+ supported; ARM support is WIP
Linux
SteamOS (deck)truly native (Vulkan); no controller/OS integration yet; demo video
Browser🏃WebGPU not working; audio, input, recompile-on-reload, etc. is working
iOS💭contributions welcome; planned in future
Android💭contributions welcome; planned in future

Console support

Console support is a goal in future years, but we’re realistic this will not happen anytime soon.

Zig has a work-in-progress C backend, which enables Zig projects to compile themselves to C code which can then be compiled using a standard C compiler. We will leverage this in order to compile Mach games using console’s official SDKs, when the time comes.

Our intention is for Hexops (game company) to offer private access to a set of plugins that will enable console support/features, under all neccessary NDAs/legal agreements which console manufacturers require of partners and publishers. We are optimistic about offering this as an at-cost service, with no intention of making profits from it.

\ No newline at end of file + Donate
Mach v0.2 has been released! For all the details check out the announcement

Platform support

We aim to support a broad array of operating systems and architectures, as long as they are reasonably used by desktop/mobile consumers, or someone would like to contribute and maintain support for them.

OSx86_64aarch64WebAssemblynotes
macOSlast 3 major versions supported;
Windows🏃windows 7+ supported; ARM support is WIP
Linux
SteamOS (deck)truly native (Vulkan); no controller/OS integration yet; demo video
Browser🏃WebGPU not working; audio, input, recompile-on-reload, etc. is working
iOS💭contributions welcome; planned in future
Android💭contributions welcome; planned in future

Console support

Console support is a goal in future years, but we’re realistic this will not happen anytime soon.

Zig has a work-in-progress C backend, which enables Zig projects to compile themselves to C code which can then be compiled using a standard C compiler. We will leverage this in order to compile Mach games using console’s official SDKs, when the time comes.

Our intention is for Hexops (game company) to offer private access to a set of plugins that will enable console support/features, under all neccessary NDAs/legal agreements which console manufacturers require of partners and publishers. We are optimistic about offering this as an at-cost service, with no intention of making profits from it.

\ No newline at end of file diff --git a/about/stability/index.html b/about/stability/index.html index 5903d87..560e58a 100644 --- a/about/stability/index.html +++ b/about/stability/index.html @@ -5,4 +5,4 @@ GitHub Discord
Mach v0.2 has been released! For all the details check out the announcement

What stability means to us

We haven’t reached Mach 1 yet, but Mach encompasses many projects and some are more stable than others.

AreaStabilityNotes
EngineExperimentalWe expect many things to change before Mach 1.
CoreStableWe expect some API breaking changes as we add support for more platforms, add small features, etc.
Packages-Depends on the package, experimental packages will have a clear warning in the README.

Mach is romantically versioned

When there are new features, bug fixes, etc. that are major, we want to be able to communicate those to people in a meaningful way. For example, if you see a “Mach v1 released” blog post, we want you to know that is a major event - the delta between major versions doesn’t just mean an API-breaking change was made, it means something really big/important for our users - the project might look totally different between v1 and v2.

Similarly, If you see a “Mach v1.1 released” blog post, we want you to know that we continued on the same general direction of the project and made great headway. That might be the result of several months, or even a year of work, features, polish, and bug-fixes.

As a result, Mach is romantically versioned, not semantically versioned.

Communicating stability

Although Mach is romantically versioned, we still have a responsibility and duty to clearly communicate to our users what is stable and what isn’t. Additionally, there are systems that may arise in the future (such as the Zig package manager adopting minimum-version-selection) which may impact us.

We communicate stability and instability through two mechanisms today:

What “stable” means

When we say a project is stable, we mean:

  1. We believe the scope of the project has been generally solidified, and its API/design choices are generally set in stone.
  2. We may still make API breaking changes, such as to add new features, improve the API design, or account for Zig changes. However it is our responsibility to communicate those changes clearly so that it is obvious how you can upgrade your own code to the latest API.

We believe this gives us the flexibility to improve projects, as we’re not locked into specific API design choices, and can balance/judge the impact of making such changes on a case-by-case basis rather than using a strict semantic definition of API compatibility. In other words, when we get something wrong, we’ll be able to actually fix it.

With stable projects, we maintain migration notes where we clearly document API changes and how to upgrade your code. For example, see mach-core’s migration notes.

What “experimental” means

When a project has an experimental warning, it means all bets are off. You should carefully read the warning to understand why the project is experimental, and assume the worst.

It could be the case that the project is nearing ‘stable’ status, and only a few minor issues remain. Or it could mean that we’re not even sure that project should be a part of what we’re building, and is subject to being removed completely in the future.

\ No newline at end of file + Donate
Mach v0.2 has been released! For all the details check out the announcement

What stability means to us

We haven’t reached Mach 1 yet, but Mach encompasses many projects and some are more stable than others.

AreaStabilityNotes
EngineExperimentalWe expect many things to change before Mach 1.
CoreStableWe expect some API breaking changes as we add support for more platforms, add small features, etc.
Packages-Depends on the package, experimental packages will have a clear warning in the README.

Mach is romantically versioned

When there are new features, bug fixes, etc. that are major, we want to be able to communicate those to people in a meaningful way. For example, if you see a “Mach v1 released” blog post, we want you to know that is a major event - the delta between major versions doesn’t just mean an API-breaking change was made, it means something really big/important for our users - the project might look totally different between v1 and v2.

Similarly, If you see a “Mach v1.1 released” blog post, we want you to know that we continued on the same general direction of the project and made great headway. That might be the result of several months, or even a year of work, features, polish, and bug-fixes.

As a result, Mach is romantically versioned, not semantically versioned.

Communicating stability

Although Mach is romantically versioned, we still have a responsibility and duty to clearly communicate to our users what is stable and what isn’t. Additionally, there are systems that may arise in the future (such as the Zig package manager adopting minimum-version-selection) which may impact us.

We communicate stability and instability through two mechanisms today:

What “stable” means

When we say a project is stable, we mean:

  1. We believe the scope of the project has been generally solidified, and its API/design choices are generally set in stone.
  2. We may still make API breaking changes, such as to add new features, improve the API design, or account for Zig changes. However it is our responsibility to communicate those changes clearly so that it is obvious how you can upgrade your own code to the latest API.

We believe this gives us the flexibility to improve projects, as we’re not locked into specific API design choices, and can balance/judge the impact of making such changes on a case-by-case basis rather than using a strict semantic definition of API compatibility. In other words, when we get something wrong, we’ll be able to actually fix it.

With stable projects, we maintain migration notes where we clearly document API changes and how to upgrade your code. For example, see mach-core’s migration notes.

What “experimental” means

When a project has an experimental warning, it means all bets are off. You should carefully read the warning to understand why the project is experimental, and assume the worst.

It could be the case that the project is nearing ‘stable’ status, and only a few minor issues remain. Or it could mean that we’re not even sure that project should be a part of what we’re building, and is subject to being removed completely in the future.

\ No newline at end of file diff --git a/about/style/index.html b/about/style/index.html index 6447343..1171cfa 100644 --- a/about/style/index.html +++ b/about/style/index.html @@ -5,7 +5,7 @@ GitHub Discord
Mach v0.2 has been released! For all the details check out the announcement

Mach code style guide

Use mach.testing where applicable

In the main Mach repository we have the mach.testing module, which provides alternatives to std.testing equality methods with:

You should use mach.testing wherever it is accessible (without pulling in an additional dependency.)

usingnamespace is banned

usingnamespace in general is banned from use in Mach code, because it allows one to ‘mixin’ namespaces and tends to produce increasingly worse code quality over time. See e.g. ziglang/zig#9618, hexops/mach#396

We’ve successfully banished usingnamespace in general, and have yet to encounter a single instance where removing it did not improve code quality & lead to better / more legible layout of code.

// This is prohibited!
+ Donate
Mach v0.2 has been released! For all the details check out the announcement

Mach code style guide

Use mach.testing where applicable

In the main Mach repository we have the mach.testing module, which provides alternatives to std.testing equality methods with:

You should use mach.testing wherever it is accessible (without pulling in an additional dependency.)

usingnamespace is banned

usingnamespace in general is banned from use in Mach code, because it allows one to ‘mixin’ namespaces and tends to produce increasingly worse code quality over time. See e.g. ziglang/zig#9618, hexops/mach#396

We’ve successfully banished usingnamespace in general, and have yet to encounter a single instance where removing it did not improve code quality & lead to better / more legible layout of code.

// This is prohibited!
 pub usingnamespace @import("foo.zig");
 
 // This is allowed
diff --git a/about/zig-version/index.html b/about/zig-version/index.html
index 9980313..34481b8 100644
--- a/about/zig-version/index.html
+++ b/about/zig-version/index.html
@@ -5,4 +5,4 @@
 GitHub
 Discord
 
Mach v0.2 has been released! For all the details check out the announcement

Supported Zig version

Mach aims to follow the latest Zig nightly version, but is sometimes a week or so behind.

We update every Mach project every few weeks and in unison with one another, so that everything works and is being tested with a specific nightly version. That is the version you are expected to use.

Download the latest supported Zig version from our pkg.machengine.org mirror:

OS/ArchKindDownload mirror
Linux x86_64Binaryzig-linux-x86_64-0.12.0-dev.1092+68ed78775.tar.xz
Linux aarch64 (ARM)Binaryzig-linux-aarch64-0.12.0-dev.1092+68ed78775.tar.xz
Windows x86_64Binaryzig-windows-x86_64-0.12.0-dev.1092+68ed78775.zip
Windows aarch64 (ARM)Binaryzig-windows-aarch64-0.12.0-dev.1092+68ed78775.zip
macOS x86_64 (Intel)Binaryzig-macos-x86_64-0.12.0-dev.1092+68ed78775.tar.xz
macOS aarch64 (Apple Silicon)Binaryzig-macos-aarch64-0.12.0-dev.1092+68ed78775.tar.xz
Source codeSourcezig-0.12.0-dev.1092+68ed78775.tar.xz

Signatures: .minisig Download signatures can be retrieved from ziglang.org for all downloads.

Installation

Download, extract, and place on your system $PATH. Ensure zig version reports the right version.

Version history

  • Mach v0.2 is compatible with Zig v0.11
    • Note: This was coincidental, and future releases will be compatible with specific Zig nightly versions instead.
\ No newline at end of file + Donate
Mach v0.2 has been released! For all the details check out the announcement

Supported Zig version

Mach aims to follow the latest Zig nightly version, but is sometimes a week or so behind.

We update every Mach project every few weeks and in unison with one another, so that everything works and is being tested with a specific nightly version. That is the version you are expected to use.

Download the latest supported Zig version from our pkg.machengine.org mirror:

OS/ArchKindDownload mirror
Linux x86_64Binaryzig-linux-x86_64-0.12.0-dev.1092+68ed78775.tar.xz
Linux aarch64 (ARM)Binaryzig-linux-aarch64-0.12.0-dev.1092+68ed78775.tar.xz
Windows x86_64Binaryzig-windows-x86_64-0.12.0-dev.1092+68ed78775.zip
Windows aarch64 (ARM)Binaryzig-windows-aarch64-0.12.0-dev.1092+68ed78775.zip
macOS x86_64 (Intel)Binaryzig-macos-x86_64-0.12.0-dev.1092+68ed78775.tar.xz
macOS aarch64 (Apple Silicon)Binaryzig-macos-aarch64-0.12.0-dev.1092+68ed78775.tar.xz
Source codeSourcezig-0.12.0-dev.1092+68ed78775.tar.xz

Signatures: .minisig Download signatures can be retrieved from ziglang.org for all downloads.

Installation

Download, extract, and place on your system $PATH. Ensure zig version reports the right version.

Version history

  • Mach v0.2 is compatible with Zig v0.11
    • Note: This was coincidental, and future releases will be compatible with specific Zig nightly versions instead.
\ No newline at end of file diff --git a/core/examples/index.html b/core/examples/index.html index 2e14091..10836b3 100644 --- a/core/examples/index.html +++ b/core/examples/index.html @@ -5,7 +5,7 @@ GitHub Discord
Mach v0.2 has been released! For all the details check out the announcement

Mach core examples

All examples require this Zig nightly version | known issues
Mach v0.2 has been released! For all the details check out the announcement

Mach core examples

All examples require this Zig nightly version | known issues

deferred-rendering

Dynamic lighting using deferred rendering techniques.

 git clone https://github.com/hexops/mach-core
 cd mach-core/
diff --git a/core/getting-started/index.html b/core/getting-started/index.html
index 5c70d42..68e2756 100644
--- a/core/getting-started/index.html
+++ b/core/getting-started/index.html
@@ -5,7 +5,7 @@
 GitHub
 Discord
 
Mach v0.2 has been released! For all the details check out the announcement

Getting Started with Mach core

Here you will learn how to use mach-core in your own project/repository. If you haven’t already, check out the examples as those describe how to actually use Mach core’s APIs, this page just describes project setup.

Option 1: Copying the starter project

If you like, you can simply start by copying this starter project - where we ran through all the steps below for you.

Option 2: Create a project from scratch

Create Zig project

mkdir myproject/
+ Donate
Mach v0.2 has been released! For all the details check out the announcement

Getting Started with Mach core

Here you will learn how to use mach-core in your own project/repository. If you haven’t already, check out the examples as those describe how to actually use Mach core’s APIs, this page just describes project setup.

Option 1: Copying the starter project

If you like, you can simply start by copying this starter project - where we ran through all the steps below for you.

Option 2: Create a project from scratch

Create Zig project

mkdir myproject/
 cd myproject/
 zig init-exe
 

Add dependencies

mach-core uses the Zig package manager. Create a build.zig.zon file like this in your project next to your build.zig file. Replace LATEST_COMMIT with the latest commit hash:

.{
diff --git a/core/index.html b/core/index.html
index aa7cdbc..3ed7828 100644
--- a/core/index.html
+++ b/core/index.html
@@ -5,7 +5,7 @@
 GitHub
 Discord
 
Mach v0.2 has been released! For all the details check out the announcement
Mach v0.2 has been released! For all the details check out the announcement

Window+Input+GPU, truly cross-platform

Provides the power of Vulkan, DirectX, Metal, and modern OpenGL in a single concise graphics API and shader language - by compiling Google Chrome's WebGPU implementation natively using Zig.

No more cmake/ninja/gn/apt-get/etc - just Zig, Git, and `curl`; and at the flip of a switch you can seamlessly cross-compile for Windows/Linux/macOS, with browser support (in development), and mobile support (coming in the future) - all under a single unified API.

Zero-fuss installation, cross-compilation at the flip of a switch, and broad platform support is our goal.

Under the hood

We use Google Chrome’s WebGPU implementation, Dawn, but we compile it using Zig as the C/C++ compiler, and use Zig’s build system and package manager instead of cmake/gn/depot_tools/etc.

On desktop platforms, we use GLFW to create windows, set up Vulkan/Direct3D/etc. and provide an abstraction layer on top that will enable browser and mobile support in the future under the same API.

By default, we use prebuilt binaries of Dawn (the WebGPU implementation) so that you don’t have to wait for compilation, and also so that you can use a release build in your debug application for nice performance while developing your games/apps. But 100% from-source builds are always available at the flip of a switch DAWN_FROM_SOURCE=true zig build. We use Zig’s package manager to fetch the few libraries/headers we need to build from source, so you don’t need to deal with with any external build systems or dependencies: just zig, git, and curl.

Example showcase

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

mach-core: v0.2 API redesign

Mach v0.2 brought a complete redesign of the mach-core API. To upgrade your application, see the notes below.

See also the migration notes page.

Updating your application

Previously, a complete Mach Core application looked something like this:

pub const App = @This();
+ Donate
Mach v0.2 has been released! For all the details check out the announcement

mach-core: v0.2 API redesign

Mach v0.2 brought a complete redesign of the mach-core API. To upgrade your application, see the notes below.

See also the migration notes page.

Updating your application

Previously, a complete Mach Core application looked something like this:

pub const App = @This();
 
 pub fn init(app: *App, core: *mach.Core) !void {
     // ...
diff --git a/core/migrations/index.html b/core/migrations/index.html
index 116a6d5..5b92db1 100644
--- a/core/migrations/index.html
+++ b/core/migrations/index.html
@@ -5,7 +5,7 @@
 GitHub
 Discord
 
Mach v0.2 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: Zig version update (2023-10-17)

Mach core 967d9b7 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");
+ Donate
Mach v0.2 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: Zig version update (2023-10-17)

Mach core 967d9b7 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);
diff --git a/discord/index.html b/discord/index.html
index fcd527e..b3959db 100644
--- a/discord/index.html
+++ b/discord/index.html
@@ -5,4 +5,4 @@
 GitHub
 Discord
 
Mach v0.2 has been released! For all the details check out the announcement
\ No newline at end of file + Donate
Mach v0.2 has been released! For all the details check out the announcement
\ No newline at end of file diff --git a/engine/gpu/errors/index.html b/engine/gpu/errors/index.html index 2989cae..9f30aee 100644 --- a/engine/gpu/errors/index.html +++ b/engine/gpu/errors/index.html @@ -5,7 +5,7 @@ GitHub Discord
Mach v0.2 has been released! For all the details check out the announcement

GPU error handling

Asynchronous nature

GPUs have largely asynchronous APIs: you build up a command buffer which encodes a number of commands instructing the GPU to do something, and then you ask the GPU to execute that buffer of commands. As a result, errors must be handled asynchronously as well. try isn’t going to work here.

Error scopes

A concept of error scopes is used, where you can push an error scope onto the stack and pop an error scope. Commands that produce errors while the error scope was on the stack will result in that error scope’s callback being invoked. Let’s look at a concrete example:

Catching a shader compilation error

// Push a validation error scope, so that if compiling the shader fails we'll
+ Donate
Mach v0.2 has been released! For all the details check out the announcement

GPU error handling

Asynchronous nature

GPUs have largely asynchronous APIs: you build up a command buffer which encodes a number of commands instructing the GPU to do something, and then you ask the GPU to execute that buffer of commands. As a result, errors must be handled asynchronously as well. try isn’t going to work here.

Error scopes

A concept of error scopes is used, where you can push an error scope onto the stack and pop an error scope. Commands that produce errors while the error scope was on the stack will result in that error scope’s callback being invoked. Let’s look at a concrete example:

Catching a shader compilation error

// Push a validation error scope, so that if compiling the shader fails we'll
 // be able to handle that error.
 core.device().pushErrorScope(.validation);
 
diff --git a/engine/gpu/index.html b/engine/gpu/index.html
index 9ef68bd..e3739ce 100644
--- a/engine/gpu/index.html
+++ b/engine/gpu/index.html
@@ -5,4 +5,4 @@
 GitHub
 Discord
 
Mach v0.2 has been released! For all the details check out the announcement

GPU overview

Mach uses WebGPU as its graphics API, this is an overview of that, links to learning resources and more information about how to use it.

Learning resources

Things to reference as you go

Video tutorials

@GetIntoGameDev has an excellent series of around 30 “WebGPU for Beginners” tutorials, they are using JavaScript but the APIs and concepts are the same. They go into an interesting raytracing-using-compute-shaders technique towards the end.

Code examples

Articles

\ No newline at end of file + Donate
Mach v0.2 has been released! For all the details check out the announcement

GPU overview

Mach uses WebGPU as its graphics API, this is an overview of that, links to learning resources and more information about how to use it.

Learning resources

Things to reference as you go

Video tutorials

@GetIntoGameDev has an excellent series of around 30 “WebGPU for Beginners” tutorials, they are using JavaScript but the APIs and concepts are the same. They go into an interesting raytracing-using-compute-shaders technique towards the end.

Code examples

Articles

\ No newline at end of file diff --git a/engine/gpu/memory/index.html b/engine/gpu/memory/index.html index 80fd050..42e31a9 100644 --- a/engine/gpu/memory/index.html +++ b/engine/gpu/memory/index.html @@ -5,4 +5,4 @@ GitHub Discord
Mach v0.2 has been released! For all the details check out the announcement

GPU memory management

Reference counting

GPU objects like gpu.Texture, gpu.Buffer, or any other object which has a .reference, .release and .destroy method - use reference counting.

As Zig developers, we have no qualms with explicit memory management, but GPU memory is perhaps one of the best use-cases for reference counting. GPU objects are reference-counted handles CPU-side, much like a file handle, but often hold references to eachother - while the actual memory lives on the GPU.

Guidance

Use .reference() and .release() to release memory of objects. When the reference count reaches zero, .destroy() will be called automatically for you.

Use-after-free is possible (e.g. passing a texture whose reference count has reached 0 into an API.)

In the browser, .reference() and .release() are managed by the JavaScript garbage collector - and as a result, an additional .destroy() option is provided which lets you explicitly request an object be freed, so you can get GPU memory back without being at the mercy of the JS garbage collector. Whenever the reference count reaches zero, .destroy() is automatically called for you.

Memory alignment

GPUs and underlying graphics APIs have unique memory alignment requirements:

  • Uniforms: 16-byte alignment
  • Buffer uploads: any alignment
  • Buffer uploads with an offset: 256-byte alignment
  • WGSL accesses of e.g. buffer struct fields: any alignment
  • Buffer downloads (calls to getMappedRange(T, ...)) return pointers from the underlying GPU API, generally it is required that @alignOf(T) == 16. See also hexops/mach#847 and webgpu-headers#180.
\ No newline at end of file + Donate
Mach v0.2 has been released! For all the details check out the announcement

GPU memory management

Reference counting

GPU objects like gpu.Texture, gpu.Buffer, or any other object which has a .reference, .release and .destroy method - use reference counting.

As Zig developers, we have no qualms with explicit memory management, but GPU memory is perhaps one of the best use-cases for reference counting. GPU objects are reference-counted handles CPU-side, much like a file handle, but often hold references to eachother - while the actual memory lives on the GPU.

Guidance

Use .reference() and .release() to release memory of objects. When the reference count reaches zero, .destroy() will be called automatically for you.

Use-after-free is possible (e.g. passing a texture whose reference count has reached 0 into an API.)

In the browser, .reference() and .release() are managed by the JavaScript garbage collector - and as a result, an additional .destroy() option is provided which lets you explicitly request an object be freed, so you can get GPU memory back without being at the mercy of the JS garbage collector. Whenever the reference count reaches zero, .destroy() is automatically called for you.

Memory alignment

GPUs and underlying graphics APIs have unique memory alignment requirements:

  • Uniforms: 16-byte alignment
  • Buffer uploads: any alignment
  • Buffer uploads with an offset: 256-byte alignment
  • WGSL accesses of e.g. buffer struct fields: any alignment
  • Buffer downloads (calls to getMappedRange(T, ...)) return pointers from the underlying GPU API, generally it is required that @alignOf(T) == 16. See also hexops/mach#847 and webgpu-headers#180.
\ No newline at end of file diff --git a/engine/index.html b/engine/index.html index b8cae48..be14acd 100644 --- a/engine/index.html +++ b/engine/index.html @@ -5,5 +5,5 @@ GitHub Discord
Mach v0.2 has been released! For all the details check out the announcement

Mach engine is not ready for use yet and is in very early-stages of development.
FAQ: "Can I use Mach to make a simple 2D game?"

Mach engine: game engine & graphics toolkit

+ Donate

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

Mach engine is not ready for use yet and is in very early-stages of development.
FAQ: "Can I use Mach to make a simple 2D game?"

Mach engine: game engine & graphics toolkit

Competitive in spirit with other engines

We’re planning a fully-fledged set of deeply integrated tools, a proper editor, the whole deal-we’re not kidding around here.

Not ready for use yet

Mach engine is not ready for use yet and is in the early-stages of development. We're realistic in that we are a fair amount of time away from this being reality - so please wait to hear our voice and see the roadmap for a general idea of what we're up to today.

Deeply rooted in modularity

Mach engine is designed to be modular, the only part you must adopt is our Entity Component System (as all modules are built using this to integrate with one another.)

All modules will be optional, so that you can easily plug-and-play different ones for making GUIs, rendering 2D sprites, 3D meshes, physics & more.

Data & tooling-driven

We envision a new set of tools for gamedevs that involve a deep integration with our ECS, which should allow for extensive debugging facilities and more. We’re optimistic about exploring other tooling which effectively produce data in simple & intuitive ways (think: why is developing games not as fun as playing them?)

Roadmap

There is a long road ahead, it’s going to require a lot of work to get to where we are going.

We’ve been working on Mach for ~2 years now, primarily building out the Zig gamedev ecosystem and building foundational packages that we needed for Mach core, as a result we’ve really just broken ground on the engine side of things.

See the roadmap for a general idea of what we’re up to today.

Join the effort

There is a ton of stuff to do, and we’d love your help in making this a reality. :)

Join us in Discord to participate and keep up with the latest developments.

\ No newline at end of file diff --git a/engine/math/coordinate-system/index.html b/engine/math/coordinate-system/index.html index 9160deb..f7f7363 100644 --- a/engine/math/coordinate-system/index.html +++ b/engine/math/coordinate-system/index.html @@ -5,5 +5,5 @@ GitHub Discord
Mach v0.2 has been released! For all the details check out the announcement

Coordinate system

This page briefly covers Mach’s coordinate systems; then it delves into traversing coordinate systems explaining at a high-level (and with very minimal maths) how e.g. a polygon in a 3D model in the scene goes through a bunch of coordinate system transforms before finally ending up as a pixel on the screen.

You don’t have to read this whole page, but it might be worth skimming and taking a look at the diagrams!

World space

3D models and 2D sprites are said to exist in world space, Mach uses a ‘+Y up, left-handed’ coordinate system to represent objects in space. To visualize it, imagine your own eyes are the camera. Hold your left hand out in front of you in a thumbs-up pose, as if you were holding a stick:

  • X goes through the back side of your palm, towards your fingernails
  • Y goes through the bottom of your hand towards the tip of your thumb
  • Z goes through your eyes and towards your thumb
  • Positive rotation values follow the direction of your fingers curled around the stick (any axis)

Quick reference

WhatCoordinate system
World space+Y up, left-handed
Texture coordinates+Y down; (0, 0) is at the top-left corner and the first texel in memory address order. (1, 1) is the last texel in memory address order.
Framebuffer coordinates+Y down; (0, 0) is at the top-left corner
Normalized device coordinates (NDC)+Y up; (-1, -1) is at the bottom-left corner

Mach vs. other software

If you are familiar with other software / APIs, the following comparison tables may be helpful:

-

Traversing coordinate systems

Suppose that you have a 3D model placed in a scene, and a virtual camera is viewing it. How do the vertices of that model ultimately end up on the 2D screen? We’ll walk through this below, one coordinate system transformation at a time!

Cartesian coordinate system transformations

First we need to know what a coordinate system transformation is. Imagine a typical 3D grid like you’d find in a 3D model editor, such as Blender. That’s a 3D cartesian coordinate system - the linear axis in X, Y, and Z dimensions.

Pick any point on that grid. Then imagine a second grid / coordinate system, overlayed exactly with the other one. But this one you can move, rotate, scale, etc.! As you do, the point you chose on the first grid remains in the same location.. but in the 2nd grid its location changes depending on how you move/rotate/scale the grid!

The fact that we can take a point anywhere in the first grid, and determine where it would be in the 2nd grid which is moved/rotated/scaled, is a coordinate system transformation and is what a matrix transformation does. Keep this in mind as you think about the transformations we describe later.

Model -> World space

Now that we understand what a coordinate system transformation is, we can begin talking about the first one we need to perform: Model -> World space. Imagine a vertex on a 3D model: the point of a diamond floating in front of a monkey

This vertex is said to be in the local space of the model, or just ‘model space’ for short. We get to decide what unit of measurement is used, as long as we’re consistent everywhere - so let’s say our unit is meters. The position of this vertex at the tip of the monkey’s hand is (0, 0, 1) in model space: ‘one meter in front’ of the monkey, from the perspective of the monkey. It doesn’t matter where the monkey is located in the world, that vertex is always at (0, 0, 1) in model space because it is from the perspective of the monkey.

The Model matrix describes how to transform a point in model space into world space, so that we can say ’the monkey’s finger tip’ is at a specific point in the world, rather than being relative to the monkey’s location/rotation/scale/etc.

World -> View space

Next up, we need to convert from world space into view space. Just like how model space is relative to the model, view space is relative to the viewer (i.e. the virtual camera of the scene) - this lets us say “this vertex in the 3D world is at (x, y, z) relative to the camera/viewer.

This is like the opposite of what we just did above: going from Wrench’s local model space to world space. Instead, we’re going from world space to the camera’s local space.

View -> Clip space

Now that we know where the point is in view space / relative to the camera, we need to transform the vertex into clip space. A projection matrix is used for this, taking 3D points relative to the viewer, and transforming them into a normalized clip space bounding box.

Notice how the view frustum above represents a sort of virtual camera lens, with the far plane being much larger than the near plane. The shape of the view frustum is what makes our geometry look like it is in 3D space, objects further away from will appear smaller and objects closer to the camera will appear larger. For 2D games, an orthographic projection matrix is used instead of the ‘warped’ perspective projection matrix shown above.

Also note that the view frustum is where we take into account the aspect ratio and field-of-view. For 2D games, the orthographic projection matrix often e.g. determine how many world-space units map to a single fragment/pixel on screen.

The clip space volume / bounding box on the right is a concept defined by the underlying graphics APIs and hardware. When a vertex shader runs on every vertex of a 2D/3D model, its goal is to output the position of the vertex in clip space. Unlike our other 3D coordinate spaces so far, clip space is a 4D homegenous space - also known as a projective space.

Projective geometry

In all our other 3D coordinate systems so far, we’ve been thinking in terms of Euclidean geometry with [x, y, z] points. But in clip space, we need to think in terms of Projective geometry instead with [x, y, z, w], where 𝑊 acts basically as a scaling transformation for the 3D coordinate - which will be used in our next transformation.

The above diagram shows 2D projective space, with [x, y, 𝑊] - but clip space on GPUs is in 3D projective space, with [x, y, z, 𝑊] components. It’s hard to visualize adding another dimension to the diagram above (a projector which exists in 4D, and projects onto 3D space) - so you’ll have to use your imagination - but how 𝑊 works remains: as it increases, the coordinate [x, y, z] expands (scales up) and when 𝑊 decreases, the coordinate [x, y, z] shrinks (scales down). Each vertex can have its own 𝑊 value, and so each vertex effectively lives in its own unique projective clip space. The 𝑊 is basically a scaling transformation for the 3D coordinate.

Clip space continued

The vertex shader (i.e., you) are responsible for producing that four-dimensional vertex position in clip space. Vertices form primitive shapes (like triangles), and when they go beyond the bounds of that clip space volume / bounding box in the diagram shown earlier, or if they intersect it, the GPU will clip them. You can literally think of a piece of paper in the shape of a triangle, then imagine using scissors to clip a portion of the triangle off!

Anything that is outside the clip space volume / bounding box, any points that do not pass the following tests, will be clipped:

  • −p.𝑊 ≤ p.x ≤ p.𝑊
  • −p.𝑊 ≤ p.y ≤ p.𝑊
  • 0 ≤ p.z ≤ p.𝑊 (depth clipping, optional)

GPUs use the clip volume to determine what actually needs to be rendered. Fragments/pixels/vertices outside of this clip volume, anything off-screen or behind the viewer/camera, do not need to be rendered.

Clip space -> NDC

Finally it is time to perform the perspective divide, and get our clip-space coordinates into normalized device coordinates (NDC).

Much like clip space, NDC is a bounding box:

  • -1.0 ≤ x ≤ 1.0
  • -1.0 ≤ y ≤ 1.0
  • 0.0 ≤ z ≤ 1.0
  • The bottom-left corner is at (-1.0, -1.0, z).

To convert from clip space -> NDC, we perform the perspective divide, take the (x, y, z) components and dividing each by the 𝑊 component, this converts a 4D clip-space coordinate into 3D space once again.

Rasterization

Multiple vertices in normalized device coordinates make up primitive shapes (like triangles), which the GPU performs rasterization on, rendering to fragments in the framebuffer, depth buffer, etc. This involves many other aspects we won’t go into here: multisampling, depth testing, front/back-face culling, stenciling, scissor operations, and more! It also involves running your fragment shader for each fragment that would end up in the framebuffer and, ultimately, as a pixel on the screen.

What we will explain here is how normalized device coordinates end up mapping to framebuffer space, though!

Normalized device coordinates

Here you can see how normalized device coordinates would map to e.g. a fullscreen window.

(x=0, y=0) is always the center of the framebuffer, whether it’s a window, a fullscreen application, running at any resolution - it’s always the center! (-1, -1) is always the bottom-left, and (1, 1) the top-right. The Z axis extends from z=0 (the surface of your screen) into it at z=1.

Framebuffer coordinates

It’s worth noting that framebuffer coordinates are not always the same thing as pixels. Framebuffers hold fragments, the smallest possible little dot of light, pixel is also a fine name! But as displays became ever higher and higher in their resolutions, OS developers decided that physical pixels (fragments) and virtual pixels used to position things on screen, would be different concepts. Blegh!

For example.. you may create a window on your screen which is 720x480px. On a machine with an older display/monitor/OS version, that may mean you get a 720x480px framebuffer, everything matches, great! But on a newer system with a HDPI display, you may find that your 720x480px window has a framebuffer resolution of twice that, at 1440x960px(!) because one virtual pixel maps to four (2x2) physical pixels on the display!

macOS, Windows, Linux, and cellphones all have this distinction today between physical and virtual pixels. Some platforms (e.g. Linux) allow for fractional scaling, where e.g. a virtual pixel may be made up of say 2.1 physical pixels, so your 720px wide window might end up being 1512px wide, funky!

The key thing here to keep in mind is just that the window resolution != framebuffer resolution, you’re always rendering pixels into the framebuffer, and that resolution may not be the same as the window size itself - so you may need to convert between the two.

You are now a multidimensional wizard!

You’re now capable of traversing the various coordinate systems, to 4D space and back again - or, at least, hopefully you have a clearer picture of the parts involved in getting a vertex in the 3D world to end up on the screen at the end of the day.

\ No newline at end of file + Donate
Mach v0.2 has been released! For all the details check out the announcement

Coordinate system

This page briefly covers Mach’s coordinate systems; then it delves into traversing coordinate systems explaining at a high-level (and with very minimal maths) how e.g. a polygon in a 3D model in the scene goes through a bunch of coordinate system transforms before finally ending up as a pixel on the screen.

You don’t have to read this whole page, but it might be worth skimming and taking a look at the diagrams!

World space

3D models and 2D sprites are said to exist in world space, Mach uses a ‘+Y up, left-handed’ coordinate system to represent objects in space. To visualize it, imagine your own eyes are the camera. Hold your left hand out in front of you in a thumbs-up pose, as if you were holding a stick:

  • X goes through the back side of your palm, towards your fingernails
  • Y goes through the bottom of your hand towards the tip of your thumb
  • Z goes through your eyes and towards your thumb
  • Positive rotation values follow the direction of your fingers curled around the stick (any axis)

Quick reference

WhatCoordinate system
World space+Y up, left-handed
Texture coordinates+Y down; (0, 0) is at the top-left corner and the first texel in memory address order. (1, 1) is the last texel in memory address order.
Framebuffer coordinates+Y down; (0, 0) is at the top-left corner
Normalized device coordinates (NDC)+Y up; (-1, -1) is at the bottom-left corner

Mach vs. other software

If you are familiar with other software / APIs, the following comparison tables may be helpful:

+

Traversing coordinate systems

Suppose that you have a 3D model placed in a scene, and a virtual camera is viewing it. How do the vertices of that model ultimately end up on the 2D screen? We’ll walk through this below, one coordinate system transformation at a time!

Cartesian coordinate system transformations

First we need to know what a coordinate system transformation is. Imagine a typical 3D grid like you’d find in a 3D model editor, such as Blender. That’s a 3D cartesian coordinate system - the linear axis in X, Y, and Z dimensions.

Pick any point on that grid. Then imagine a second grid / coordinate system, overlayed exactly with the other one. But this one you can move, rotate, scale, etc.! As you do, the point you chose on the first grid remains in the same location.. but in the 2nd grid its location changes depending on how you move/rotate/scale the grid!

The fact that we can take a point anywhere in the first grid, and determine where it would be in the 2nd grid which is moved/rotated/scaled, is a coordinate system transformation and is what a matrix transformation does. Keep this in mind as you think about the transformations we describe later.

Model -> World space

Now that we understand what a coordinate system transformation is, we can begin talking about the first one we need to perform: Model -> World space. Imagine a vertex on a 3D model: the point of a diamond floating in front of a monkey

This vertex is said to be in the local space of the model, or just ‘model space’ for short. We get to decide what unit of measurement is used, as long as we’re consistent everywhere - so let’s say our unit is meters. The position of this vertex at the tip of the monkey’s hand is (0, 0, 1) in model space: ‘one meter in front’ of the monkey, from the perspective of the monkey. It doesn’t matter where the monkey is located in the world, that vertex is always at (0, 0, 1) in model space because it is from the perspective of the monkey.

The Model matrix describes how to transform a point in model space into world space, so that we can say ’the monkey’s finger tip’ is at a specific point in the world, rather than being relative to the monkey’s location/rotation/scale/etc.

World -> View space

Next up, we need to convert from world space into view space. Just like how model space is relative to the model, view space is relative to the viewer (i.e. the virtual camera of the scene) - this lets us say “this vertex in the 3D world is at (x, y, z) relative to the camera/viewer.

This is like the opposite of what we just did above: going from Wrench’s local model space to world space. Instead, we’re going from world space to the camera’s local space.

View -> Clip space

Now that we know where the point is in view space / relative to the camera, we need to transform the vertex into clip space. A projection matrix is used for this, taking 3D points relative to the viewer, and transforming them into a normalized clip space bounding box.

Notice how the view frustum above represents a sort of virtual camera lens, with the far plane being much larger than the near plane. The shape of the view frustum is what makes our geometry look like it is in 3D space, objects further away from will appear smaller and objects closer to the camera will appear larger. For 2D games, an orthographic projection matrix is used instead of the ‘warped’ perspective projection matrix shown above.

Also note that the view frustum is where we take into account the aspect ratio and field-of-view. For 2D games, the orthographic projection matrix often e.g. determine how many world-space units map to a single fragment/pixel on screen.

The clip space volume / bounding box on the right is a concept defined by the underlying graphics APIs and hardware. When a vertex shader runs on every vertex of a 2D/3D model, its goal is to output the position of the vertex in clip space. Unlike our other 3D coordinate spaces so far, clip space is a 4D homegenous space - also known as a projective space.

Projective geometry

In all our other 3D coordinate systems so far, we’ve been thinking in terms of Euclidean geometry with [x, y, z] points. But in clip space, we need to think in terms of Projective geometry instead with [x, y, z, w], where 𝑊 acts basically as a scaling transformation for the 3D coordinate - which will be used in our next transformation.

The above diagram shows 2D projective space, with [x, y, 𝑊] - but clip space on GPUs is in 3D projective space, with [x, y, z, 𝑊] components. It’s hard to visualize adding another dimension to the diagram above (a projector which exists in 4D, and projects onto 3D space) - so you’ll have to use your imagination - but how 𝑊 works remains: as it increases, the coordinate [x, y, z] expands (scales up) and when 𝑊 decreases, the coordinate [x, y, z] shrinks (scales down). Each vertex can have its own 𝑊 value, and so each vertex effectively lives in its own unique projective clip space. The 𝑊 is basically a scaling transformation for the 3D coordinate.

Clip space continued

The vertex shader (i.e., you) are responsible for producing that four-dimensional vertex position in clip space. Vertices form primitive shapes (like triangles), and when they go beyond the bounds of that clip space volume / bounding box in the diagram shown earlier, or if they intersect it, the GPU will clip them. You can literally think of a piece of paper in the shape of a triangle, then imagine using scissors to clip a portion of the triangle off!

Anything that is outside the clip space volume / bounding box, any points that do not pass the following tests, will be clipped:

  • −p.𝑊 ≤ p.x ≤ p.𝑊
  • −p.𝑊 ≤ p.y ≤ p.𝑊
  • 0 ≤ p.z ≤ p.𝑊 (depth clipping, optional)

GPUs use the clip volume to determine what actually needs to be rendered. Fragments/pixels/vertices outside of this clip volume, anything off-screen or behind the viewer/camera, do not need to be rendered.

Clip space -> NDC

Finally it is time to perform the perspective divide, and get our clip-space coordinates into normalized device coordinates (NDC).

Much like clip space, NDC is a bounding box:

  • -1.0 ≤ x ≤ 1.0
  • -1.0 ≤ y ≤ 1.0
  • 0.0 ≤ z ≤ 1.0
  • The bottom-left corner is at (-1.0, -1.0, z).

To convert from clip space -> NDC, we perform the perspective divide, take the (x, y, z) components and dividing each by the 𝑊 component, this converts a 4D clip-space coordinate into 3D space once again.

Rasterization

Multiple vertices in normalized device coordinates make up primitive shapes (like triangles), which the GPU performs rasterization on, rendering to texels in the framebuffer, depth buffer, etc. This involves many other aspects we won’t go into here: multisampling, depth testing, front/back-face culling, stenciling, scissor operations, and more! It also involves running your fragment shader for each fragment that would end up in the framebuffer and, ultimately, as a pixel on the screen.

What we will explain here is how normalized device coordinates end up mapping to framebuffer space, though!

Normalized device coordinates

Here you can see how normalized device coordinates would map to e.g. a fullscreen window.

(x=0, y=0) is always the center of the framebuffer, whether it’s a window, a fullscreen application, running at any resolution - it’s always the center! (-1, -1) is always the bottom-left, and (1, 1) the top-right. The Z axis extends from z=0 (the surface of your screen) into it at z=1.

Framebuffer coordinates

It’s worth noting that framebuffer coordinates are not always the same thing as pixels. Framebuffers hold texels. As displays became higher and higher in their resolutions, OS developers decided that physical pixels on your screen and virtual pixels used to position/place things on screen, would be different concepts. Blegh!

For example.. you may create a window on your screen which is 720x480px. On a machine with an older display/monitor/OS version, that may mean you get a 720x480 resolution framebuffer, everything matches, great! But on a newer system with a HDPI display, you may find that your 720x480px window has a framebuffer resolution of twice that, at 1440x960! In this case, one virtual pixel maps to four (2x2) physical pixels on the display!

macOS, Windows, Linux, and cellphones all have this distinction today between physical and virtual pixels, with various options that affect the mapping of virtual->physical. Some platforms (e.g. Linux) even allow for fractional scaling, where e.g. a virtual pixel may be made up of say 2.1 physical pixels, so your 720px wide window might end up being 1512px wide!

The key thing here to keep in mind is just that the window resolution != framebuffer resolution, you’re always rendering pixels/texels into the framebuffer, but there’s no guarantee a pixel/texel in the framebuffer will be displayed as 1 physical pixel on the screen. You may need to convert between the two frequently.

You are now a multidimensional wizard!

You’re now capable of traversing the various coordinate systems, to 4D space and back again - or, at least, hopefully you have a clearer picture of the parts involved in getting a vertex in the 3D world to end up on the screen at the end of the day.

\ No newline at end of file diff --git a/engine/math/index.html b/engine/math/index.html index dec0b09..b4e58b7 100644 --- a/engine/math/index.html +++ b/engine/math/index.html @@ -5,4 +5,4 @@ GitHub Discord
Mach v0.2 has been released! For all the details check out the announcement

Mach’s math library is opinionated

Math is hard enough as-is, without you having to question ground truths while problem solving. As a result, Mach’s math library takes a more opinionated approach than some other math libraries: we try to encourage you through API design to use what we believe to be the best choices. For example, other math libraries provide both LH and RH (left-handed and right-handed) variants for each operation, and they sit on equal footing for you to choose from; mach.math may also provide both variants as needed for conversions, but unlike other libraries will bless one choice with e.g. a shorter function name to nudge you in the right direction and towards consistency.

Matrices

  • Column-major matrix storage
  • Column-vectors (i.e. right-associative multiplication, matrix * vector = vector)

The benefit of using this “OpenGL-style” matrix is that it matches the conventions accepted by the scientific community, it’s what you’ll find in linear algebra textbooks. It also matches WebGPU, Vulkan, Unity3D, etc. It does NOT match DirectX-style which e.g. Unreal Engine uses.

Note: many people will say “row major” or “column major” and implicitly mean three or more different concepts; consider reading the matrix storage docs.

Coordinate system (+Y up, left-handed)

  • Normalized Device coordinates: +Y up; (-1, -1) is at the bottom-left corner.
  • Framebuffer coordinates: +Y down; (0, 0) is at the top-left corner.
  • Texture coordinates: +Y down; (0, 0) is at the top-left corner.

This coordinate system is consistent with WebGPU, Metal, DirectX, and Unity (NDC only.)

\ No newline at end of file + Donate
Mach v0.2 has been released! For all the details check out the announcement

Mach’s math library is opinionated

Math is hard enough as-is, without you having to question ground truths while problem solving. As a result, Mach’s math library takes a more opinionated approach than some other math libraries: we try to encourage you through API design to use what we believe to be the best choices. For example, other math libraries provide both LH and RH (left-handed and right-handed) variants for each operation, and they sit on equal footing for you to choose from; mach.math may also provide both variants as needed for conversions, but unlike other libraries will bless one choice with e.g. a shorter function name to nudge you in the right direction and towards consistency.

Matrices

  • Column-major matrix storage
  • Column-vectors (i.e. right-associative multiplication, matrix * vector = vector)

The benefit of using this “OpenGL-style” matrix is that it matches the conventions accepted by the scientific community, it’s what you’ll find in linear algebra textbooks. It also matches WebGPU, Vulkan, Unity3D, etc. It does NOT match DirectX-style which e.g. Unreal Engine uses.

Note: many people will say “row major” or “column major” and implicitly mean three or more different concepts; consider reading the matrix storage docs.

Coordinate system (+Y up, left-handed)

  • Normalized Device coordinates: +Y up; (-1, -1) is at the bottom-left corner.
  • Framebuffer coordinates: +Y down; (0, 0) is at the top-left corner.
  • Texture coordinates: +Y down; (0, 0) is at the top-left corner.

This coordinate system is consistent with WebGPU, Metal, DirectX, and Unity (NDC only.)

\ No newline at end of file diff --git a/engine/math/matrix-storage/index.html b/engine/math/matrix-storage/index.html index 838ece1..11241dc 100644 --- a/engine/math/matrix-storage/index.html +++ b/engine/math/matrix-storage/index.html @@ -5,7 +5,7 @@ GitHub Discord
Mach v0.2 has been released! For all the details check out the announcement

Matrix storage

Matrices in Mach use:

  • Column-major matrix storage
  • Column-vectors (i.e. right-associative multiplication, matrix * vector = vector)

The benefit of using this “OpenGL-style” matrix is that it matches the conventions accepted by the scientific community, it’s what you’ll find in linear algebra textbooks. It also matches WebGPU, Vulkan, Unity3D, etc. It does NOT match DirectX-style which e.g. Unreal Engine uses.

Note: many people will say “row major” or “column major” and implicitly mean three or more different concepts; to avoid confusion we’ll go over this in more depth below.

Mathematical matrix order: row-major, column-vectors

Mathematically, a matrix is defined as m x n, where m is the number of rows (horizontals) and n is the number of columns (verticals.) For example, this 4x4 matrix where a10 (zero-indexed) represents the element at the second row and first column of the matrix:

|a00 a01 a02 a03|
+ Donate
Mach v0.2 has been released! For all the details check out the announcement

Matrix storage

Matrices in Mach use:

  • Column-major matrix storage
  • Column-vectors (i.e. right-associative multiplication, matrix * vector = vector)

The benefit of using this “OpenGL-style” matrix is that it matches the conventions accepted by the scientific community, it’s what you’ll find in linear algebra textbooks. It also matches WebGPU, Vulkan, Unity3D, etc. It does NOT match DirectX-style which e.g. Unreal Engine uses.

Note: many people will say “row major” or “column major” and implicitly mean three or more different concepts; to avoid confusion we’ll go over this in more depth below.

Mathematical matrix order: row-major, column-vectors

Mathematically, a matrix is defined as m x n, where m is the number of rows (horizontals) and n is the number of columns (verticals.) For example, this 4x4 matrix where a10 (zero-indexed) represents the element at the second row and first column of the matrix:

|a00 a01 a02 a03|
 |a10 a11 a12 a13|
 |a20 a21 a22 a23|
 |a30 a31 a32 a33|
diff --git a/engine/modularity/index.html b/engine/modularity/index.html
index 0435f3e..3584653 100644
--- a/engine/modularity/index.html
+++ b/engine/modularity/index.html
@@ -5,4 +5,4 @@
 GitHub
 Discord
 
Mach v0.2 has been released! For all the details check out the announcement

Modularity

Historically, we believe there are three types of approaches to game development:

DescriptionOpinionated?Modularity?Note
LibrariesNot at allExtremelyNo natural interoperability, lots of glue code required
FrameworksSomewhatVeryOften unable to provide high-level experiences due to modularity
EnginesExtremelyNot at allProvides high-level experiences but makes rigid choices on your behalf

The Mach project provides some Libraries, but mostly aims to sit between Frameworks and Engines. Opinionated where it matters to provide high-level experiences like engines do, but modular enough to let anyone break out of the ‘blessed’ paths we have chosen to do their own thing.

Where is ‘Mach engine’?

‘Mach engine’ is the combined overall experience of two things:

  1. The Mach standard library - the engine APIs and ‘blessed’ paths for doing things, but still modular.
  2. The Mach editor and CLI tooling - which provide standard tools and high-level experiences you’d expect from a game engine

Whether you use both, or how much of either you decide to use, is up to you!

Can I make my own game engine using Mach?

Yes! For example instead of using GLFW/SDL+OpenGL+glm as the base for your own engine, you might choose to use mach.core and mach.math from our standard library.

\ No newline at end of file + Donate
Mach v0.2 has been released! For all the details check out the announcement

Modularity

Historically, we believe there are three types of approaches to game development:

DescriptionOpinionated?Modularity?Note
LibrariesNot at allExtremelyNo natural interoperability, lots of glue code required
FrameworksSomewhatVeryOften unable to provide high-level experiences due to modularity
EnginesExtremelyNot at allProvides high-level experiences but makes rigid choices on your behalf

The Mach project provides some Libraries, but mostly aims to sit between Frameworks and Engines. Opinionated where it matters to provide high-level experiences like engines do, but modular enough to let anyone break out of the ‘blessed’ paths we have chosen to do their own thing.

Where is ‘Mach engine’?

‘Mach engine’ is the combined overall experience of two things:

  1. The Mach standard library - the engine APIs and ‘blessed’ paths for doing things, but still modular.
  2. The Mach editor and CLI tooling - which provide standard tools and high-level experiences you’d expect from a game engine

Whether you use both, or how much of either you decide to use, is up to you!

Can I make my own game engine using Mach?

Yes! For example instead of using GLFW/SDL+OpenGL+glm as the base for your own engine, you might choose to use mach.core and mach.math from our standard library.

\ No newline at end of file diff --git a/engine/roadmap/index.html b/engine/roadmap/index.html index c4a401c..e00dcfb 100644 --- a/engine/roadmap/index.html +++ b/engine/roadmap/index.html @@ -5,4 +5,4 @@ GitHub Discord
Mach v0.2 has been released! For all the details check out the announcement

Engine roadmap

There is a long road ahead: it’s going to require a lot of work and time to get to where we are going.

We’ve been working on Mach for ~2 years now, primarily building out the Zig gamedev ecosystem, building foundational packages that we needed for Mach core, and as a result we’ve really just broken ground on the engine side of things.

Development is fairly linear, but developments that you see aren’t linear, we were focused on more foundational things before - but today we’re actively focused on higher-level APIs which will have more profound impacts for end-users.

We intend to accelerate our release schedule to about once every 6 months.

v0.2 (finished)

  • Mach core is an actual alternative to the traditional GLFW+OpenGL or SDL combo
    • Codebase is strictly separated and usable separate from Mach engine.
    • Examples are decent, and a basic 2D sprite rendering example exists.
    • We feel confident about the internals, feel good about the API design overall, etc.
    • Multi-threaded rendering & input handling is resolved.
  • Build out the packages/bindings we need for Mach engine
    • mach-sysaudio: pure-Zig low-level cross-platform audio input/output
    • mach-flac: lossless audio decoding
    • mach-opus: lossy audio decoding support
    • mach-gamemode: Linux gamemode optimization client (rewrite in Zig)
    • mach-gpu: add infrastructure for automated updates & make it the best WebGPU interface for Zig
  • Adopt the Zig self-hosted compiler
  • Fully migrate to the experimental Zig package manager (eliminate all submodules)
  • Begin prototyping engine design
    • mach-ecs: prototyping & direction set for the future
    • Prototype gfx2d sprite rendering ECS module / example
    • Prototype gfx2d text rendering ECS module / example
  • New website that can hold documentation for the project going forward
  • Wrench automation infrastructure for managing Zig updates and other tedious time-consuming issues

v0.3 (in progress)

  • Mach engine becomes viable for basic 2D games
    • Basic text rendering ECS module
    • Basic sprite rendering ECS module
    • Basic audio rendering ECS module
    • ECS implementation begins to mature (extremely immature today)
  • Basic 2D game demo
  • Basic custom GUI ECS module
  • Browser support
    • mach-sysjs: rewritten for code generation approach (‘bindgen-like’)

Future

  • Mach engine becomes viable for basic 3D games
    • Basic 3D model loading
  • Custom GUI begins to mature
  • Initial editor development support
  • Mobile support
  • sysgpu becomes our successor to WebGPU/Dawn and becomes the default.
    • Mature Vulkan backend
    • Mature Direct3D backend
    • Mature Metal backend
    • Mature shading language solution
  • Physics
\ No newline at end of file + Donate
Mach v0.2 has been released! For all the details check out the announcement

Engine roadmap

There is a long road ahead: it’s going to require a lot of work and time to get to where we are going.

We’ve been working on Mach for ~2 years now, primarily building out the Zig gamedev ecosystem, building foundational packages that we needed for Mach core, and as a result we’ve really just broken ground on the engine side of things.

Development is fairly linear, but developments that you see aren’t linear, we were focused on more foundational things before - but today we’re actively focused on higher-level APIs which will have more profound impacts for end-users.

We intend to accelerate our release schedule to about once every 6 months.

v0.2 (finished)

  • Mach core is an actual alternative to the traditional GLFW+OpenGL or SDL combo
    • Codebase is strictly separated and usable separate from Mach engine.
    • Examples are decent, and a basic 2D sprite rendering example exists.
    • We feel confident about the internals, feel good about the API design overall, etc.
    • Multi-threaded rendering & input handling is resolved.
  • Build out the packages/bindings we need for Mach engine
    • mach-sysaudio: pure-Zig low-level cross-platform audio input/output
    • mach-flac: lossless audio decoding
    • mach-opus: lossy audio decoding support
    • mach-gamemode: Linux gamemode optimization client (rewrite in Zig)
    • mach-gpu: add infrastructure for automated updates & make it the best WebGPU interface for Zig
  • Adopt the Zig self-hosted compiler
  • Fully migrate to the experimental Zig package manager (eliminate all submodules)
  • Begin prototyping engine design
    • mach-ecs: prototyping & direction set for the future
    • Prototype gfx2d sprite rendering ECS module / example
    • Prototype gfx2d text rendering ECS module / example
  • New website that can hold documentation for the project going forward
  • Wrench automation infrastructure for managing Zig updates and other tedious time-consuming issues

v0.3 (in progress)

  • Mach engine becomes viable for basic 2D games
    • Basic text rendering ECS module
    • Basic sprite rendering ECS module
    • Basic audio rendering ECS module
    • ECS implementation begins to mature (extremely immature today)
  • Basic 2D game demo
  • Basic custom GUI ECS module
  • Browser support
    • mach-sysjs: rewritten for code generation approach (‘bindgen-like’)

Future

  • Mach engine becomes viable for basic 3D games
    • Basic 3D model loading
  • Custom GUI begins to mature
  • Initial editor development support
  • Mobile support
  • sysgpu becomes our successor to WebGPU/Dawn and becomes the default.
    • Mature Vulkan backend
    • Mature Direct3D backend
    • Mature Metal backend
    • Mature shading language solution
  • Physics
\ No newline at end of file diff --git a/engine/stdlib/index.html b/engine/stdlib/index.html index f8472a5..b6616c9 100644 --- a/engine/stdlib/index.html +++ b/engine/stdlib/index.html @@ -5,7 +5,7 @@ GitHub Discord
Mach v0.2 has been released! For all the details check out the announcement

Standard library

The Mach standard library can be found at: github.com/hexops/mach - it is modular by design and you may choose to use depend on all of it, or just some of it.

Using mach.math as a separate library

Let’s take for example mach.math, our math library. Unlike other gamedev math libraries which may try to support a bunch of different coordinate systems in order to support various graphics APIs, ours is specifically for Mach’s design goals. For example:

  • It only exposes projection matrices that map into a clip space of [0, 1], and doesn’t expose options for e.g. OpenGL which uses [-1, 1]
  • It only speaks of a +Y up, left-handed coordinate system because that’s what Mach uses.

If you want to use mach.math as a standalone library, without the rest of Mach, that’s perfect doable!

Getting started

Create a build.zig.zon in your project (replace LATEST_COMMIT with the latest commit hash):

.{
+ Donate
Mach v0.2 has been released! For all the details check out the announcement

Standard library

The Mach standard library can be found at: github.com/hexops/mach - it is modular by design and you may choose to use depend on all of it, or just some of it.

Using mach.math as a separate library

Let’s take for example mach.math, our math library. Unlike other gamedev math libraries which may try to support a bunch of different coordinate systems in order to support various graphics APIs, ours is specifically for Mach’s design goals. For example:

  • It only exposes projection matrices that map into a clip space of [0, 1], and doesn’t expose options for e.g. OpenGL which uses [-1, 1]
  • It only speaks of a +Y up, left-handed coordinate system because that’s what Mach uses.

If you want to use mach.math as a standalone library, without the rest of Mach, that’s perfect doable!

Getting started

Create a build.zig.zon in your project (replace LATEST_COMMIT with the latest commit hash):

.{
     .name = "mypkg",
     .version = "0.1.0",
     .dependencies = .{
diff --git a/gpu/index.html b/gpu/index.html
index d895a5e..92f021d 100644
--- a/gpu/index.html
+++ b/gpu/index.html
@@ -5,4 +5,4 @@
 GitHub
 Discord
 
Mach v0.2 has been released! For all the details check out the announcement
\ No newline at end of file + Donate
Mach v0.2 has been released! For all the details check out the announcement
\ No newline at end of file diff --git a/layouts/docs.eba24e7b90282072fe2e748d2511c23c5000ad5f00b0ef3bf03f8ee20348f6e6.css b/layouts/docs.1c549ac5e9eaa1dc4997cd957caa9afec046a45d465b64074e1ec4f88737dd7b.css similarity index 94% rename from layouts/docs.eba24e7b90282072fe2e748d2511c23c5000ad5f00b0ef3bf03f8ee20348f6e6.css rename to layouts/docs.1c549ac5e9eaa1dc4997cd957caa9afec046a45d465b64074e1ec4f88737dd7b.css index 7148799..ffa0a5e 100644 --- a/layouts/docs.eba24e7b90282072fe2e748d2511c23c5000ad5f00b0ef3bf03f8ee20348f6e6.css +++ b/layouts/docs.1c549ac5e9eaa1dc4997cd957caa9afec046a45d465b64074e1ec4f88737dd7b.css @@ -1 +1 @@ -.color-border{border-color:#bfbfbf !important}.color-bg{background-color:#f8f9fb}.color-fg{color:#000}.color-fg1{color:gray}.color-bg1{background-color:#f8f9fb}@media (prefers-color-scheme: dark){.color-border{border-color:#2f5151 !important}.color-bg{background-color:#0a1619}.color-fg{color:#fff}.color-fg1{color:#5da2a2}.color-bg1{background-color:#183a42}}*,html{scroll-behavior:smooth !important}html,body{height:100%;width:100%;margin:0;padding:0;line-height:1.25;font-family:'Inter', sans-serif;background:#f8f9fb;color:#000}@media (prefers-color-scheme: dark){html,body{background:#0a1619;color:#fff}}.plain-font{font-family:'Inter', sans-serif}h1,h2,h3,h4,h5,h6,.big-button{font-family:Orbitron}main p,main li{font-size:16px}.with-alert{padding-top:2rem}a{color:#000}@media (prefers-color-scheme: dark){a{color:#fff}}blockquote{color:grey;margin-bottom:0}h1,h2,h3,h4,h5,h6{margin-bottom:0;margin-top:3rem}.subtext{font-size:80%;padding-left:0.5rem}p{margin-bottom:0}@media (prefers-color-scheme: dark){.auto-color{filter:invert(100%)}}@media (prefers-color-scheme: light){.auto-color-inverted{filter:invert(100%)}}.invert-color{filter:invert(100%)}.svg-icon{height:1.25rem;display:flex;padding-top:0;color:#000;filter:none}@media (prefers-color-scheme: dark){.svg-icon{filter:invert(100%) sepia(100%) saturate(0%) hue-rotate(143deg) brightness(105%) contrast(104%)}}.navbar{position:relative;z-index:2;background:#f8f9fb}@media (prefers-color-scheme: dark){.navbar{background:#0a1619}}.navbar .content{display:flex;align-items:center;justify-content:space-between;margin:auto;margin-left:6rem;margin-right:6rem;width:calc(100% - 6rem - 6rem)}.navbar .content .logo{display:inline-block;padding-left:0;padding-right:0;flex:10;padding-top:0.5rem;padding-bottom:0.5rem}.navbar .content .logo .img{width:9.5rem;height:2.25rem;display:flex;background-repeat:no-repeat;background-size:contain;background-image:url("../assets/media/mach/logo_light.svg")}@media (prefers-color-scheme: dark){.navbar .content .logo .img{background-image:url("../assets/media/mach/logo_dark.svg")}}.navbar .content .divider{width:0;height:3.3rem;border-left:1px solid}.navbar .content .subsection{display:flex;align-items:center;height:100%}.navbar .content .subsection a{display:inline-flex;align-items:center;text-decoration:none;line-height:3rem;font-weight:600;height:100%;padding-right:1rem;padding-left:1rem;color:#000}@media (prefers-color-scheme: dark){.navbar .content .subsection a{color:#fff;text-shadow:0 0 0.2rem black}}.navbar .content .subsection a:not(.donate-button):hover,.navbar .content .subsection a:not(.donate-button):active{color:gray}@media (prefers-color-scheme: dark){.navbar .content .subsection a:not(.donate-button):hover,.navbar .content .subsection a:not(.donate-button):active{color:#5da2a2}}.navbar .content .subsection a:not(.donate-button):hover.svg-item,.navbar .content .subsection a:not(.donate-button):active.svg-item{filter:invert(50%)}.navbar .content .subsection .donate-button{color:black;text-shadow:none;text-shadow:0.5px 0.5px 1px rgba(255,255,255,0.6);border-radius:0.75rem;border:2px solid #9be3ff;height:2.25rem;background-color:#3cc1f5;background-image:linear-gradient(350deg, #33a3ce, #8adbfb);box-shadow:rgba(0,82,255,0.1) 2px 5px 3px 0px,rgba(0,82,255,0.1) 1px 5px 12px 0px,rgba(0,82,255,0.3) 2px 2px 15px 0px;margin-right:1rem}.navbar .content .subsection .donate-button img{filter:drop-shadow(0.5px 0.5px 1px rgba(0,0,0,0.4));height:1rem}@media (prefers-color-scheme: light){.navbar .content .subsection .donate-button img{filter:drop-shadow(0.5px 0.5px 1px rgba(0,0,0,0.4))}}@media (prefers-color-scheme: dark){.navbar .content .subsection .donate-button{border:1px solid #6cb7d4;background-color:#1490c2;background-image:linear-gradient(350deg, #13769d, #4ca1c3)}}.navbar .content .subsection .donate-button:hover,.navbar .content .subsection .donate-button:active{filter:brightness(110%)}.glass-link:hover,.glass-link:active{background:rgba(0,0,0,0.1);backdrop-filter:blur(0.5rem);-webkit-backdrop-filter:blur(0.5rem)}@media (prefers-color-scheme: dark){.glass-link:hover,.glass-link:active{background-color:rgba(255,255,255,0.1)}}@media (min-width: 835px) and (max-width: 1028px){.navbar .content{margin:auto;margin-left:1rem;width:calc(100% - 1rem);flex-wrap:wrap;justify-content:center}.navbar .content .divider{display:none}.home .choose-your-journey>div>div:last-child video{margin-left:1rem}.home .code{white-space:pre-line}.home .p-section{padding-left:1rem;padding-right:1rem}}@media (max-width: 835px){.navbar .content{margin:auto;margin-left:1rem;width:calc(100% - 1rem);flex-wrap:wrap;justify-content:center}.navbar .content .divider{display:none}.navbar .content .logo{display:flex;justify-content:center;flex:auto;padding-top:0.75rem;padding-bottom:0;width:100%}.alert>span{height:2.5rem !important}.home .choose-your-journey{min-height:60rem}.home .choose-your-journey>div{padding-left:1rem;padding-right:1rem;flex-direction:column !important;margin-left:0rem !important;margin-right:0rem !important}.home .choose-your-journey>div h1,.home .choose-your-journey>div h2{text-align:center}.home .choose-your-journey>div>div:last-child{margin-top:1rem}.home .choose-your-journey>div>div:last-child video{width:100%;height:auto}.home .always-open-source>.card{display:flex;flex-direction:column;padding:1rem}.home .code{white-space:pre-line}.home .p-section{padding:1rem}.home .p-section h1,.home .p-section h2{margin-top:1rem !important;text-align:center !important}.home .p-img-right,.home .p-img-left{margin:auto !important}}.alert{height:0;z-index:2;justify-content:center;display:flex}.alert.warning>span{background:red;color:#fff}.alert>span{background:rgba(138,171,176,0.6);backdrop-filter:blur(1rem);-webkit-backdrop-filter:blur(1rem);border-top:1px solid #00000026;height:1.25rem;padding:0.5rem;border-bottom-left-radius:2px;border-bottom-right-radius:2px;width:100%;box-shadow:inset 0 1rem 1rem -1rem rgba(0,0,0,0.6);text-align:center;z-index:2}@media (prefers-color-scheme: dark){.alert>span{background:rgba(26,54,59,0.6)}}#content{display:flex;flex:1;flex-direction:column;margin-left:auto;margin-right:auto;width:100%;overflow:hidden}#content main{display:flex;z-index:1;min-height:calc(100vh - 10rem)}.footer{z-index:1;margin:auto;height:3rem;display:flex;flex-direction:row;justify-content:center;padding-bottom:3rem}main{width:100%;display:flex}.home,.unlimited{display:flex;flex-direction:column;padding-bottom:3rem}.home,.unlimited{width:100%;text-align:center}.home p,.unlimited p{max-width:40rem;text-align:left;margin-left:auto;margin-right:auto}.main-single{max-width:50rem;margin:auto}.footer{border-top:1px solid grey;padding-top:1rem;max-width:50rem;display:flex;flex-direction:row;align-self:center;align-items:baseline;font-size:12px;justify-content:space-between;width:80%}.big-button img{height:5rem;position:relative;top:-0.25rem;margin-right:1rem}.big-button{background:rgba(0,0,0,0.1);backdrop-filter:blur(0.5rem);-webkit-backdrop-filter:blur(0.5rem);margin-top:1rem;display:inline-flex;align-items:center;text-decoration:none;line-height:3rem;padding:2rem;padding-right:3rem;padding-left:3rem;filter:brightness(100%);font-size:200%;height:3rem;animation:flashyLoad;background-repeat:no-repeat;background-position:-2400px -240px, 0 0;background-size:250% 250%, 100% 100%;transition:background-position 0s ease;background-image:-webkit-linear-gradient(top left, rgba(255,255,255,0) 0%, rgba(255,255,255,0.2) 37%, rgba(255,255,255,0.8) 45%, rgba(255,255,255,0) 100%);background-image:linear-gradient(0 0, rgba(255,255,255,0) 0%, rgba(255,255,255,0.2) 37%, rgba(255,255,255,0.8) 45%, rgba(255,255,255,0) 100%)}@media (prefers-color-scheme: dark){.big-button{background-color:rgba(255,255,255,0.1)}}@media (prefers-color-scheme: dark){.big-button{text-shadow:2px 2px #000}}.big-button::before,.big-button-false-before::before{background:rgba(0,0,0,0.1);backdrop-filter:blur(0.5rem);-webkit-backdrop-filter:blur(0.5rem);position:absolute;z-index:-1;width:35rem;margin-left:-3rem;height:7rem;clip-path:polygon(0 0, calc(100% - 1rem) 0, 100% 1rem, 100% 100%, 1rem 100%, 0 calc(100% - 1rem));content:''}@media (prefers-color-scheme: dark){.big-button::before,.big-button-false-before::before{background-color:rgba(255,255,255,0.1)}}.big-button:hover,.big-button:active{background-position:0 0, 0 0;transition-duration:0.5s}@media (prefers-reduced-motion: reduce){.big-button:hover,.big-button:active{transition-duration:0.1s}}@keyframes flashyLoad{0%{background-position:-2400px -240px, 0 0}100%{background-position:800px 0, 0 0}}.big-button.glass-hover{background:none}.big-button.glass-hover:hover{background:rgba(0,0,0,0.1);backdrop-filter:blur(0.5rem);-webkit-backdrop-filter:blur(0.5rem)}@media (prefers-color-scheme: dark){.big-button.glass-hover:hover{background-color:rgba(255,255,255,0.1)}}.glass{background:rgba(0,0,0,0.1);backdrop-filter:blur(0.5rem);-webkit-backdrop-filter:blur(0.5rem)}@media (prefers-color-scheme: dark){.glass{background-color:rgba(255,255,255,0.1)}}.card{background:#183a42;text-align:left;display:flex;flex-direction:row;align-items:center;border-radius:0.5rem;padding:1rem;padding-top:2rem;padding-bottom:2rem}.card,.card a,.card a:active{color:#fff}h2{text-align:left;margin-top:3rem}.img-link{text-decoration:none;outline:none}pre{padding:1rem;background-color:#272822;color:#f8f9fb;tab-size:4;font-size:16px;overflow:scroll}.code{text-align:left;background:#ffeadf;color:black;padding:0.5rem}.code::-moz-selection{color:white;background:black}.code::selection{color:white;background:black}table{margin-top:1rem;border-collapse:collapse;width:calc(100%)}thead{border-bottom:1px solid gray}td,th{padding:0.5rem}tr:nth-child(even){background:white}@media (prefers-color-scheme: dark){tr:nth-child(even){background:#3b3c38}}th{padding-top:1rem;padding-bottom:1rem;text-align:left}.chroma{color:#e5e5e5;background-color:#000000}.chroma .err{color:#ff0000}.chroma .lntd{vertical-align:top;padding:0;margin:0;border:0}.chroma .lntable{border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block}.chroma .hl{display:block;width:100%;background-color:#ffffcc}.chroma .lnt{margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#727272}.chroma .ln{margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#727272}.chroma .k{color:#0f7501;font-weight:bold}.chroma .kc{color:#067fff;font-weight:bold}.chroma .kd{color:#067fff;font-weight:bold}.chroma .kn{color:#067fff;font-weight:bold}.chroma .kp{color:#067fff;font-weight:bold}.chroma .kr{color:#ff7800;font-weight:bold}.chroma .kt{color:#067fff;font-weight:bold}.chroma .na{color:#007f7f}.chroma .nb{color:#067fff;font-weight:bold}.chroma .nt{font-weight:bold}.chroma .ld{color:#ffff00;font-weight:bold}.chroma .s{color:#72bb29;font-weight:bold;font-style:italic}.chroma .sa{color:#00ffff;font-weight:bold}.chroma .sb{color:#00ffff;font-weight:bold}.chroma .sc{color:#00ffff;font-weight:bold}.chroma .dl{color:#00ffff;font-weight:bold}.chroma .sd{color:#00ffff;font-weight:bold}.chroma .s2{color:#00ffff;font-weight:bold}.chroma .se{color:#00ffff;font-weight:bold}.chroma .sh{color:#00ffff;font-weight:bold}.chroma .si{color:#00ffff;font-weight:bold}.chroma .sx{color:#00ffff;font-weight:bold}.chroma .sr{color:#00ffff;font-weight:bold}.chroma .s1{color:#00ffff;font-weight:bold}.chroma .ss{color:#00ffff;font-weight:bold}.chroma .m{color:#ffff00;font-weight:bold}.chroma .mb{color:#ffff00;font-weight:bold}.chroma .mf{color:#ffff00;font-weight:bold}.chroma .mh{color:#ffff00;font-weight:bold}.chroma .mi{color:#ffff00;font-weight:bold}.chroma .il{color:#ffff00;font-weight:bold}.chroma .mo{color:#ffff00;font-weight:bold}.chroma .o{color:#ff9076}.chroma .p{color:#c18004}.chroma .c{color:#007f7f}.chroma .ch{color:#007f7f}.chroma .cm{color:#007f7f}.chroma .c1{color:#007f7f}.chroma .cs{color:#007f7f}.chroma .cp{color:#00ff00;font-weight:bold}.chroma .cpf{color:#00ff00;font-weight:bold}.chroma .gd{background:#b51a1a}.chroma .gh{font-weight:bold}.chroma .gi{background:#017701}.chroma .gs{font-weight:bold}.chroma .gu{font-weight:bold}.chroma .gl{text-decoration:underline}.docs{display:flex;flex-direction:column;padding-bottom:3rem;width:100%}.main-docs{align-self:center;justify-content:center;flex-direction:row}.main-docs .docs{padding:3rem;padding-top:0.5rem;max-width:70rem;text-align:left;overflow:hidden}.main-docs .docs img{max-width:100%}.main-docs .docs h1:first-of-type{margin-top:1rem}.main-docs .docs h3,.main-docs .docs h4,.main-docs .docs h5,.main-docs .docs h6{margin-top:1rem}.main-docs aside,.main-docs .toc{display:flex;flex-direction:column;padding:0;margin:0}.main-docs aside{width:15rem}.main-docs .toc{width:25rem}.main-docs aside{flex:0 0 auto}.main-docs aside>ul{padding-top:1rem}.main-docs .toc{padding-top:4rem}.main-docs .toc h3{margin:0;padding:0}.toc,.toc a{font-size:small}aside>ul{padding:0;margin:0;width:100%;min-height:100vh;font-weight:bold;background:#ebebeb}@media (prefers-color-scheme: dark){aside>ul{background:#0e1e21}}aside>ul li{width:100%;display:flex;margin:0;padding:0}aside>ul a,aside>ul h3{margin:0;padding:1rem;padding-top:0.75rem;padding-bottom:0.75rem;width:100%}aside>ul h3{margin-top:1rem;border-bottom:1px solid black}aside>ul a:hover{background:rgba(0,0,0,0.1);backdrop-filter:blur(0.5rem);-webkit-backdrop-filter:blur(0.5rem)}@media (prefers-color-scheme: dark){aside>ul a:hover{background-color:rgba(255,255,255,0.1)}}aside>ul a:link,aside>ul a:visited,aside>ul a:hover,aside>ul a:active{text-decoration:none}@media (min-width: 835px) and (max-width: 1200px){.main-docs .toc{display:none !important}.main-docs .docs,.main-docs aside{max-width:calc(100% - 15rem) !important}}@media (max-width: 835px){#content>.main-docs>aside{margin-top:1rem;max-width:unset !important;width:100%}#content>.main-docs>aside ul{min-height:auto}.main-docs{flex-direction:column}.main-docs .toc{display:none !important}.main-docs .docs,.main-docs aside{max-width:calc(100% - 6rem) !important}.main-docs .docs{overflow:scroll}} +.color-border{border-color:#bfbfbf !important}.color-bg{background-color:#f8f9fb}.color-fg{color:#000}.color-fg1{color:gray}.color-bg1{background-color:#f8f9fb}@media (prefers-color-scheme: dark){.color-border{border-color:#2f5151 !important}.color-bg{background-color:#0a1619}.color-fg{color:#fff}.color-fg1{color:#5da2a2}.color-bg1{background-color:#183a42}}*,html{scroll-behavior:smooth !important}html,body{height:100%;width:100%;margin:0;padding:0;line-height:1.25;font-family:'Inter', sans-serif;background:#f8f9fb;color:#000}@media (prefers-color-scheme: dark){html,body{background:#0a1619;color:#fff}}.plain-font{font-family:'Inter', sans-serif}h1,h2,h3,h4,h5,h6,.big-button{font-family:Orbitron}main p,main li{font-size:16px}.with-alert{padding-top:2rem}a{color:#000}@media (prefers-color-scheme: dark){a{color:#fff}}blockquote{color:grey;margin-bottom:0}h1,h2,h3,h4,h5,h6{margin-bottom:0;margin-top:3rem}.subtext{font-size:80%;padding-left:0.5rem}p{margin-bottom:0}@media (prefers-color-scheme: dark){.auto-color{filter:invert(100%)}}@media (prefers-color-scheme: light){.auto-color-inverted{filter:invert(100%)}}.invert-color{filter:invert(100%)}.svg-icon{height:1.25rem;display:flex;padding-top:0;color:#000;filter:none}@media (prefers-color-scheme: dark){.svg-icon{filter:invert(100%) sepia(100%) saturate(0%) hue-rotate(143deg) brightness(105%) contrast(104%)}}.navbar{position:relative;z-index:2;background:#f8f9fb}@media (prefers-color-scheme: dark){.navbar{background:#0a1619}}.navbar .content{display:flex;align-items:center;justify-content:space-between;margin:auto;margin-left:6rem;margin-right:6rem;width:calc(100% - 6rem - 6rem)}.navbar .content .logo{display:inline-block;padding-left:0;padding-right:0;flex:10;padding-top:0.5rem;padding-bottom:0.5rem}.navbar .content .logo .img{width:9.5rem;height:2.25rem;display:flex;background-repeat:no-repeat;background-size:contain;background-image:url("../assets/media/mach/logo_light.svg")}@media (prefers-color-scheme: dark){.navbar .content .logo .img{background-image:url("../assets/media/mach/logo_dark.svg")}}.navbar .content .divider{width:0;height:3.3rem;border-left:1px solid}.navbar .content .subsection{display:flex;align-items:center;height:100%}.navbar .content .subsection a{display:inline-flex;align-items:center;text-decoration:none;line-height:3rem;font-weight:600;height:100%;padding-right:1rem;padding-left:1rem;color:#000}@media (prefers-color-scheme: dark){.navbar .content .subsection a{color:#fff;text-shadow:0 0 0.2rem black}}.navbar .content .subsection a:not(.donate-button):hover,.navbar .content .subsection a:not(.donate-button):active{color:gray}@media (prefers-color-scheme: dark){.navbar .content .subsection a:not(.donate-button):hover,.navbar .content .subsection a:not(.donate-button):active{color:#5da2a2}}.navbar .content .subsection a:not(.donate-button):hover.svg-item,.navbar .content .subsection a:not(.donate-button):active.svg-item{filter:invert(50%)}.navbar .content .subsection .donate-button{color:black;text-shadow:none;text-shadow:0.5px 0.5px 1px rgba(255,255,255,0.6);border-radius:0.75rem;border:2px solid #9be3ff;height:2.25rem;background-color:#3cc1f5;background-image:linear-gradient(350deg, #33a3ce, #8adbfb);box-shadow:rgba(0,82,255,0.1) 2px 5px 3px 0px,rgba(0,82,255,0.1) 1px 5px 12px 0px,rgba(0,82,255,0.3) 2px 2px 15px 0px;margin-right:1rem}.navbar .content .subsection .donate-button img{filter:drop-shadow(0.5px 0.5px 1px rgba(0,0,0,0.4));height:1rem}@media (prefers-color-scheme: light){.navbar .content .subsection .donate-button img{filter:drop-shadow(0.5px 0.5px 1px rgba(0,0,0,0.4))}}@media (prefers-color-scheme: dark){.navbar .content .subsection .donate-button{border:1px solid #6cb7d4;background-color:#1490c2;background-image:linear-gradient(350deg, #13769d, #4ca1c3)}}.navbar .content .subsection .donate-button:hover,.navbar .content .subsection .donate-button:active{filter:brightness(110%)}.glass-link:hover,.glass-link:active{background:rgba(0,0,0,0.1);backdrop-filter:blur(0.5rem);-webkit-backdrop-filter:blur(0.5rem)}@media (prefers-color-scheme: dark){.glass-link:hover,.glass-link:active{background-color:rgba(255,255,255,0.1)}}@media (min-width: 835px) and (max-width: 1028px){.navbar .content{margin:auto;margin-left:1rem;width:calc(100% - 1rem);flex-wrap:wrap;justify-content:center}.navbar .content .divider{display:none}.home .choose-your-journey>div>div:last-child video{margin-left:1rem}.home .code{white-space:pre-line}.home .p-section{padding-left:1rem;padding-right:1rem}}@media (max-width: 835px){.navbar .content{margin:auto;margin-left:1rem;width:calc(100% - 1rem);flex-wrap:wrap;justify-content:center}.navbar .content .divider{display:none}.navbar .content .logo{display:flex;justify-content:center;flex:auto;padding-top:0.75rem;padding-bottom:0;width:100%}.alert>span{height:2.5rem !important}.home .choose-your-journey{min-height:60rem}.home .choose-your-journey>div{padding-left:1rem;padding-right:1rem;flex-direction:column !important;margin-left:0rem !important;margin-right:0rem !important}.home .choose-your-journey>div h1,.home .choose-your-journey>div h2{text-align:center}.home .choose-your-journey>div>div:last-child{margin-top:1rem}.home .choose-your-journey>div>div:last-child video{width:100%;height:auto}.home .always-open-source>.card{display:flex;flex-direction:column;padding:1rem}.home .code{white-space:pre-line}.home .p-section{padding:1rem}.home .p-section h1,.home .p-section h2{margin-top:1rem !important;text-align:center !important}.home .p-img-right,.home .p-img-left{margin:auto !important}}.alert{height:0;z-index:2;justify-content:center;display:flex}.alert.warning>span{background:red;color:#fff}.alert>span{background:rgba(138,171,176,0.6);backdrop-filter:blur(1rem);-webkit-backdrop-filter:blur(1rem);border-top:1px solid #00000026;height:1.25rem;padding:0.5rem;border-bottom-left-radius:2px;border-bottom-right-radius:2px;width:100%;box-shadow:inset 0 1rem 1rem -1rem rgba(0,0,0,0.6);text-align:center;z-index:2}@media (prefers-color-scheme: dark){.alert>span{background:rgba(26,54,59,0.6)}}#content{display:flex;flex:1;flex-direction:column;margin-left:auto;margin-right:auto;width:100%;overflow:hidden}#content main{display:flex;z-index:1;min-height:calc(100vh - 10rem)}.footer{z-index:1;margin:auto;height:3rem;display:flex;flex-direction:row;justify-content:center;padding-bottom:3rem}main{width:100%;display:flex}.home,.unlimited{display:flex;flex-direction:column;padding-bottom:3rem}.home,.unlimited{width:100%;text-align:center}.home p,.unlimited p{max-width:40rem;text-align:left;margin-left:auto;margin-right:auto}.main-single{max-width:50rem;margin:auto}.footer{border-top:1px solid grey;padding-top:1rem;max-width:50rem;display:flex;flex-direction:row;align-self:center;align-items:baseline;font-size:12px;justify-content:space-between;width:80%}.big-button img{height:5rem;position:relative;top:-0.25rem;margin-right:1rem}.big-button{background:rgba(0,0,0,0.1);backdrop-filter:blur(0.5rem);-webkit-backdrop-filter:blur(0.5rem);margin-top:1rem;display:inline-flex;align-items:center;text-decoration:none;line-height:3rem;padding:2rem;padding-right:3rem;padding-left:3rem;filter:brightness(100%);font-size:200%;height:3rem;animation:flashyLoad;background-repeat:no-repeat;background-position:-2400px -240px, 0 0;background-size:250% 250%, 100% 100%;transition:background-position 0s ease;background-image:-webkit-linear-gradient(top left, rgba(255,255,255,0) 0%, rgba(255,255,255,0.2) 37%, rgba(255,255,255,0.8) 45%, rgba(255,255,255,0) 100%);background-image:linear-gradient(0 0, rgba(255,255,255,0) 0%, rgba(255,255,255,0.2) 37%, rgba(255,255,255,0.8) 45%, rgba(255,255,255,0) 100%)}@media (prefers-color-scheme: dark){.big-button{background-color:rgba(255,255,255,0.1)}}@media (prefers-color-scheme: dark){.big-button{text-shadow:2px 2px #000}}.big-button::before,.big-button-false-before::before{background:rgba(0,0,0,0.1);backdrop-filter:blur(0.5rem);-webkit-backdrop-filter:blur(0.5rem);position:absolute;z-index:-1;width:35rem;margin-left:-3rem;height:7rem;clip-path:polygon(0 0, calc(100% - 1rem) 0, 100% 1rem, 100% 100%, 1rem 100%, 0 calc(100% - 1rem));content:''}@media (prefers-color-scheme: dark){.big-button::before,.big-button-false-before::before{background-color:rgba(255,255,255,0.1)}}.big-button:hover,.big-button:active{background-position:0 0, 0 0;transition-duration:0.5s}@media (prefers-reduced-motion: reduce){.big-button:hover,.big-button:active{transition-duration:0.1s}}@keyframes flashyLoad{0%{background-position:-2400px -240px, 0 0}100%{background-position:800px 0, 0 0}}.big-button.glass-hover{background:none}.big-button.glass-hover:hover{background:rgba(0,0,0,0.1);backdrop-filter:blur(0.5rem);-webkit-backdrop-filter:blur(0.5rem)}@media (prefers-color-scheme: dark){.big-button.glass-hover:hover{background-color:rgba(255,255,255,0.1)}}.glass{background:rgba(0,0,0,0.1);backdrop-filter:blur(0.5rem);-webkit-backdrop-filter:blur(0.5rem)}@media (prefers-color-scheme: dark){.glass{background-color:rgba(255,255,255,0.1)}}.card{background:#183a42;text-align:left;display:flex;flex-direction:row;align-items:center;border-radius:0.5rem;padding:1rem;padding-top:2rem;padding-bottom:2rem}.card,.card a,.card a:active{color:#fff}h2{text-align:left;margin-top:3rem}.img-link{text-decoration:none;outline:none}pre{padding:1rem;background-color:#272822;color:#f8f9fb;tab-size:4;font-size:16px;overflow:scroll}.code{text-align:left;background:#ffeadf;color:black;padding:0.5rem}.code::-moz-selection{color:white;background:black}.code::selection{color:white;background:black}table{margin-top:1rem;border-collapse:collapse;width:calc(100%)}thead{border-bottom:1px solid gray}td,th{padding:0.5rem}tr:nth-child(even){background:white}@media (prefers-color-scheme: dark){tr:nth-child(even){background:#3b3c38}}th{padding-top:1rem;padding-bottom:1rem;text-align:left}.chroma{color:#e5e5e5;background-color:#000000}.chroma .err{color:#ff0000}.chroma .lntd{vertical-align:top;padding:0;margin:0;border:0}.chroma .lntable{border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block}.chroma .hl{display:block;width:100%;background-color:#ffffcc}.chroma .lnt{margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#727272}.chroma .ln{margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#727272}.chroma .k{color:#0f7501;font-weight:bold}.chroma .kc{color:#067fff;font-weight:bold}.chroma .kd{color:#067fff;font-weight:bold}.chroma .kn{color:#067fff;font-weight:bold}.chroma .kp{color:#067fff;font-weight:bold}.chroma .kr{color:#ff7800;font-weight:bold}.chroma .kt{color:#067fff;font-weight:bold}.chroma .na{color:#007f7f}.chroma .nb{color:#067fff;font-weight:bold}.chroma .nt{font-weight:bold}.chroma .ld{color:#ffff00;font-weight:bold}.chroma .s{color:#72bb29;font-weight:bold;font-style:italic}.chroma .sa{color:#00ffff;font-weight:bold}.chroma .sb{color:#00ffff;font-weight:bold}.chroma .sc{color:#00ffff;font-weight:bold}.chroma .dl{color:#00ffff;font-weight:bold}.chroma .sd{color:#00ffff;font-weight:bold}.chroma .s2{color:#00ffff;font-weight:bold}.chroma .se{color:#00ffff;font-weight:bold}.chroma .sh{color:#00ffff;font-weight:bold}.chroma .si{color:#00ffff;font-weight:bold}.chroma .sx{color:#00ffff;font-weight:bold}.chroma .sr{color:#00ffff;font-weight:bold}.chroma .s1{color:#00ffff;font-weight:bold}.chroma .ss{color:#00ffff;font-weight:bold}.chroma .m{color:#ffff00;font-weight:bold}.chroma .mb{color:#ffff00;font-weight:bold}.chroma .mf{color:#ffff00;font-weight:bold}.chroma .mh{color:#ffff00;font-weight:bold}.chroma .mi{color:#ffff00;font-weight:bold}.chroma .il{color:#ffff00;font-weight:bold}.chroma .mo{color:#ffff00;font-weight:bold}.chroma .o{color:#ff9076}.chroma .p{color:#c18004}.chroma .c{color:#007f7f}.chroma .ch{color:#007f7f}.chroma .cm{color:#007f7f}.chroma .c1{color:#007f7f}.chroma .cs{color:#007f7f}.chroma .cp{color:#00ff00;font-weight:bold}.chroma .cpf{color:#00ff00;font-weight:bold}.chroma .gd{background:#b51a1a}.chroma .gh{font-weight:bold}.chroma .gi{background:#017701}.chroma .gs{font-weight:bold}.chroma .gu{font-weight:bold}.chroma .gl{text-decoration:underline}.docs{display:flex;flex-direction:column;padding-bottom:3rem;width:100%}.main-docs{align-self:center;justify-content:center;flex-direction:row}.main-docs .docs{padding:3rem;padding-top:0.5rem;max-width:70rem;text-align:left;overflow:hidden}.main-docs .docs img{max-width:100%}.main-docs .docs h1:first-of-type{margin-top:1rem}.main-docs .docs h3,.main-docs .docs h4,.main-docs .docs h5,.main-docs .docs h6{margin-top:1rem}.main-docs aside,.main-docs .toc{display:flex;flex-direction:column;padding:0;margin:0}.main-docs aside{width:15rem}.main-docs .toc{width:25rem}.main-docs aside{flex:0 0 auto}.main-docs aside>ul{padding-top:1rem}.main-docs .toc{padding-top:4rem}.main-docs .toc h3{margin:0;padding:0}.toc,.toc a{font-size:small}aside>ul{padding:0;margin:0;width:100%;min-height:100vh;font-weight:bold;background:#ebebeb}@media (prefers-color-scheme: dark){aside>ul{background:#0e1e21}}aside>ul li{width:100%;display:flex;margin:0;padding:0}aside>ul a,aside>ul h3{margin:0;padding:1rem;padding-top:0.75rem;padding-bottom:0.75rem;width:100%}aside>ul h3{margin-top:1rem;border-bottom:1px solid black}aside>ul a:hover{background:rgba(0,0,0,0.1);backdrop-filter:blur(0.5rem);-webkit-backdrop-filter:blur(0.5rem)}@media (prefers-color-scheme: dark){aside>ul a:hover{background-color:rgba(255,255,255,0.1)}}aside>ul a:link,aside>ul a:visited,aside>ul a:hover,aside>ul a:active{text-decoration:none}.centered{display:flex;flex-direction:column;justify-content:space-between;align-items:center;margin-bottom:1rem}.img-link{text-decoration:none;outline:none}.img-link.centered img{max-width:80% !important}@media (min-width: 835px) and (max-width: 1200px){.main-docs .toc{display:none !important}.main-docs .docs,.main-docs aside{max-width:calc(100% - 15rem) !important}}@media (max-width: 835px){#content>.main-docs>aside{margin-top:1rem;max-width:unset !important;width:100%}#content>.main-docs>aside ul{min-height:auto}.main-docs{flex-direction:column}.main-docs .toc{display:none !important}.main-docs .docs,.main-docs aside{max-width:calc(100% - 6rem) !important}.main-docs .docs{overflow:scroll}} diff --git a/opengraph/template_hu102e162f47cb7d8ae9eb284939698b9d_62022_filter_4912858429100723217.png b/opengraph/template_hu102e162f47cb7d8ae9eb284939698b9d_62022_filter_4912858429100723217.png new file mode 100644 index 0000000000000000000000000000000000000000..22bb17676ece7d300814c5e3f2f5bac4d13a5591 GIT binary patch literal 67306 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

Xr(<4O0Oy0Wq|0I+qt(OY=;emw_1ZW;g;1uZT9 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 Discord

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

C libraries & headers packaged via the Zig build system

Mach provides an ecosystem of C libraries via the Zig package manager.

These aren’t Zig bindings to these libraries (which we have separately), but instead are just forks of the actual project with their build system replaced by build.zig so you can depend on them and build them using the Zig package manager.

Pure forks with build.zig:

Header packages:

Special cases:

\ No newline at end of file + Donate
Mach v0.2 has been released! For all the details check out the announcement

C libraries & headers packaged via the Zig build system

Mach provides an ecosystem of C libraries via the Zig package manager.

These aren’t Zig bindings to these libraries (which we have separately), but instead are just forks of the actual project with their build system replaced by build.zig so you can depend on them and build them using the Zig package manager.

Pure forks with build.zig:

Header packages:

Special cases:

\ No newline at end of file diff --git a/pkg/fastfilter/index.html b/pkg/fastfilter/index.html index f628423..2a33a8b 100644 --- a/pkg/fastfilter/index.html +++ b/pkg/fastfilter/index.html @@ -5,7 +5,7 @@ GitHub Discord
Mach v0.2 has been released! For all the details check out the announcement

fastfilter

Binary fuse & xor filters for Zig (faster and smaller than bloom filters)

+ Donate
Mach v0.2 has been released! For all the details check out the announcement

fastfilter

Binary fuse & xor filters for Zig (faster and smaller than bloom filters)

Getting started

Create a build.zig.zon in your project (replace LATEST_COMMIT with the latest commit hash):

.{
     .name = "mypkg",
     .version = "0.1.0",
diff --git a/pkg/index.html b/pkg/index.html
index b32d870..1cc9d8d 100644
--- a/pkg/index.html
+++ b/pkg/index.html
@@ -5,7 +5,7 @@
 GitHub
 Discord
 
Mach v0.2 has been released! For all the details check out the announcement

Standalone Zig gamedev packages

Mach provides an ecosystem of 100% standalone, high-quality Zig gamedev packages - and they all feature zero-fuss installation, cross-compilation at the flip of a switch, and broad platform support.

The ecosystem

mach-gpu + Donate
Mach v0.2 has been released! For all the details check out the announcement

Standalone Zig gamedev packages

Mach provides an ecosystem of 100% standalone, high-quality Zig gamedev packages - and they all feature zero-fuss installation, cross-compilation at the flip of a switch, and broad platform support.

The ecosystem

mach-gpu mach-ecs mach-sysaudio mach-sysjs diff --git a/pkg/mach-basisu/index.html b/pkg/mach-basisu/index.html index 3c5d589..565e731 100644 --- a/pkg/mach-basisu/index.html +++ b/pkg/mach-basisu/index.html @@ -5,7 +5,7 @@ GitHub Discord
Mach v0.2 has been released! For all the details check out the announcement
Mach v0.2 has been released! For all the details check out the announcement

basis universal (supercompressed textures) for Zig

Experimental

This is an experimental project according to our stability guarantees:

When a project has an experimental warning, it means all bets are off. You should carefully read the warning to understand why the project is experimental, and assume the worst.

Tracking issue: https://github.com/hexops/mach/issues/965

Getting started

Create a build.zig.zon in your project (replace LATEST_COMMIT with the latest commit hash):

.{
     .name = "mypkg",
     .version = "0.1.0",
diff --git a/pkg/mach-dusk/index.html b/pkg/mach-dusk/index.html
index b474902..a048c2d 100644
--- a/pkg/mach-dusk/index.html
+++ b/pkg/mach-dusk/index.html
@@ -5,4 +5,4 @@
 GitHub
 Discord
 
Mach v0.2 has been released! For all the details check out the announcement
\ No newline at end of file + Donate
Mach v0.2 has been released! For all the details check out the announcement
\ No newline at end of file diff --git a/pkg/mach-earcut/index.html b/pkg/mach-earcut/index.html index 706e107..a73cd9e 100644 --- a/pkg/mach-earcut/index.html +++ b/pkg/mach-earcut/index.html @@ -5,4 +5,4 @@ GitHub Discord
Mach v0.2 has been released! For all the details check out the announcement

No longer supported as an official Mach project. The code has moved to github.com/slimsag/mach-earcut and may not be maintained to the same standard as official Mach projects.

Additional details: https://github.com/hexops/mach/issues/967

View the older v0.2 documentation: https://machengine.org/v0.2/pkg/mach-earcut

\ No newline at end of file + Donate
Mach v0.2 has been released! For all the details check out the announcement

No longer supported as an official Mach project. The code has moved to github.com/slimsag/mach-earcut and may not be maintained to the same standard as official Mach projects.

Additional details: https://github.com/hexops/mach/issues/967

View the older v0.2 documentation: https://machengine.org/v0.2/pkg/mach-earcut

\ No newline at end of file diff --git a/pkg/mach-ecs/index.html b/pkg/mach-ecs/index.html index 986a5bd..5207367 100644 --- a/pkg/mach-ecs/index.html +++ b/pkg/mach-ecs/index.html @@ -5,7 +5,7 @@ GitHub Discord
Mach v0.2 has been released! For all the details check out the announcement
Mach v0.2 has been released! For all the details check out the announcement

The Mach entity component system, written from first-principles and designed for deep tooling capabilities.

  • Initially a 100% clean-room implementation, working from first-principles; later informed by research into how other open-source ECS work.
  • Enable deep tooling to provide tracing, editors, visualizers, profilers, etc.
  • Fast, optimal for CPU caches, multi-threaded, leverage comptime for type safety.
  • Dynamic, allow for very flexible runtime capabilities.

We’re publishing a blog series “Let’s build an Entity Component System from scatch” as we go.

Experimental

This is an experimental project according to our stability guarantees:

When a project has an experimental warning, it means all bets are off. You should carefully read the warning to understand why the project is experimental, and assume the worst.

Tracking issue: https://github.com/hexops/mach/issues/968

Getting started

Create a build.zig.zon in your project (replace LATEST_COMMIT with the latest commit hash):

.{
     .name = "mypkg",
     .version = "0.1.0",
diff --git a/pkg/mach-flac/index.html b/pkg/mach-flac/index.html
index 309b9f3..1a6b5d6 100644
--- a/pkg/mach-flac/index.html
+++ b/pkg/mach-flac/index.html
@@ -5,7 +5,7 @@
 GitHub
 Discord
 
Mach v0.2 has been released! For all the details check out the announcement
Mach v0.2 has been released! For all the details check out the announcement

FLAC audio decoding and encoding for Zig via the battle-hardened xiph.org libflac

Experimental

This is an experimental project according to our stability guarantees:

When a project has an experimental warning, it means all bets are off. You should carefully read the warning to understand why the project is experimental, and assume the worst.

Tracking issue: https://github.com/hexops/mach/issues/959

Getting started

Create a build.zig.zon in your project (replace LATEST_COMMIT with the latest commit hash):

.{
     .name = "mypkg",
     .version = "0.1.0",
diff --git a/pkg/mach-freetype/index.html b/pkg/mach-freetype/index.html
index 3e1197c..96dc8a6 100644
--- a/pkg/mach-freetype/index.html
+++ b/pkg/mach-freetype/index.html
@@ -5,7 +5,7 @@
 GitHub
 Discord
 
Mach v0.2 has been released! For all the details check out the announcement
Mach v0.2 has been released! For all the details check out the announcement

Ziggified Freetype 2 bindings with zero-fuss installation, cross compilation, and more.

Getting started

Create a build.zig.zon in your project (replace LATEST_COMMIT with the latest commit hash):

.{
     .name = "mypkg",
     .version = "0.1.0",
diff --git a/pkg/mach-gamemode/index.html b/pkg/mach-gamemode/index.html
index 4b52c0d..0f6742d 100644
--- a/pkg/mach-gamemode/index.html
+++ b/pkg/mach-gamemode/index.html
@@ -5,7 +5,7 @@
 GitHub
 Discord
 
Mach v0.2 has been released! For all the details check out the announcement
Mach v0.2 has been released! For all the details check out the announcement

Make your Linux games go brrr

Enables Linux games written in Zig to request gamemode be enabled, opting in to various CPU, GPU, and kernel optimizations. If the user’s machine doesn’t have gamemode, it simply does nothing. There are no dependencies and your game will still run without it normally.

This is preferred as it means your game will automatically invoke gamemode for the user when running, rather than them having to manually enable it.

What is Linux GameMode?

Used by titles such as DiRT 4, many Tomb Raider and Total War games, GameMode is a daemon/lib combo for Linux that allows games to request a set of optimisations be temporarily applied to the host OS and/or a game process, including:

CPU governor
 I/O priority
 Process niceness
diff --git a/pkg/mach-glfw/index.html b/pkg/mach-glfw/index.html
index 9f13fcd..bebe51e 100644
--- a/pkg/mach-glfw/index.html
+++ b/pkg/mach-glfw/index.html
@@ -5,7 +5,7 @@
 GitHub
 Discord
 
Mach v0.2 has been released! For all the details check out the announcement
Mach v0.2 has been released! For all the details check out the announcement

Perfected GLFW bindings for Zig, with 100% API coverage, zero-fuss installation, cross compilation, and more.

What does a ziggified GLFW API offer?

  • Enums, always know what value a GLFW function can accept as everything is strictly typed. And use the nice Zig syntax to access enums, like window.getKey(.escape) instead of c.glfwGetKey(window, c.GLFW_KEY_ESCAPE)
  • Slices instead of C pointers and lengths.
  • Generics, so you can just use window.hint instead of glfwWindowHint, glfwWindowHintString, etc.
  • packed structs represent bit masks, so you can use if (joystick.down and joystick.right) instead of if (joystick & c.GLFW_HAT_DOWN and joystick & c.GLFW_HAT_RIGHT), etc.
  • Methods, e.g. my_window.hint(...) instead of glfwWindowHint(my_window, ...)
  • true and false instead of c.GLFW_TRUE and c.GLFW_FALSE constants.

How do I use OpenGL, Vulkan, WebGPU, etc. with this?

You’ll need to bring your own library, e.g.:

Getting started

First zig init-exe to create a Zig project. Then you will need to create a new build.zig.zon file, and update your build.zig and src/main.zig files:

build.zig.zon

mach-glfw uses the Zig package manager. Create a build.zig.zon in your project (replace LATEST_COMMIT with the latest commit hash):

.mach_glfw = .{
     .url = "https://pkg.machengine.org/mach-glfw/LATEST_COMMIT.tar.gz",
 },
diff --git a/pkg/mach-gpu-dawn/index.html b/pkg/mach-gpu-dawn/index.html
index f13814d..b1ed60f 100644
--- a/pkg/mach-gpu-dawn/index.html
+++ b/pkg/mach-gpu-dawn/index.html
@@ -5,6 +5,6 @@
 GitHub
 Discord
 
Mach v0.2 has been released! For all the details check out the announcement
Mach v0.2 has been released! For all the details check out the announcement

Google’s Dawn WebGPU implementation, cross-compiled with Zig into a single static library, requiring nothing more than zig and git to build and cross-compile a static Dawn library for every OS:

  • No cmake
  • No ninja
  • No gn
  • No system dependencies (xcode, etc.)
  • Automagic cross compilation out of the box with nothing more than zig and git!
  • Builds a single static libdawn.a

Building from source

This will take ~10 minutes to finish, so by default when using this package a prebuilt binary version of Dawn (see the ‘binary releases’ section below) but you can always build Dawn from source using:

DAWN_FROM_SOURCE=true zig build dawn
 

Binary releases

Dawn (specifically all the shader compilers, and the DirectXShaderCompiler) is a large C++ codebase and takes 5-10 minutes to build on a modern laptop. Since waiting is no fun, we also have binary releases produced by our GitHub actions:

View binary releases

Here’s how to read the downloads provided:

  • _debug.tar.gz and _release-fast.tar.gz are tarballs of the static library + headers for each OS and debug/release mode, respectively.
  • headers.json.gz is a JSON archive of all the Dawn/WebGPU headers.
  • Files ending in .a.gz and .lib.gz are the individual static libdawn.a and dawn.lib (Windows) gzippped and distributed. These are provided as individual downloads so there is no need to extract a tarball.

Important: Building WebGPU API symbols

Dawn and other WebGPU implementations (like the Rust one) do not agree on a standard webgpu.h API. Aspirationally, they aim to target the same https://github.com/webgpu-native/webgpu-headers header, but in practice they expose different APIs which are not ABI compatible.

When you call a Dawn webgpu.h function, Dawn internally diverts this call through a vtable which must be initialized using a call to dawnProcSetProcs.

mach/gpu-dawn builds since Oct 17th 2022 no longer include the webgpu.h symbols by default. If you intend to actually call the WebGPU API, you should build these two source files as part of your application:

  1. dawn_proc.c
  2. webgpu_dawn_native_proc.cpp

And call dawnProcSetProcs to set up the proc table.

A warning about API stability

You should be aware:

  • WebGPU’s API is not formalized yet.
  • Dawn’s API is still changing.
  • The webgpu.h API is still changing
  • Dawn and gfx-rs/wgpu, although both try to implement webgpu.h, do not exactly implement the same interface. There are subtle differences in device discovery & creation for example.

Generated code

Dawn itself relies on a fairly large amount of dependencies, generated code, etc. To avoid having any dependency on Google build tools, code generation, etc. we maintain a minor fork of Dawn which has generated code and third-party dependencies comitted in “generated” branches. We are usually up-to-date with the upstream within a few weeks on average.

It also provides a few small patches to enable building Dawn with the Zig compiler which we plan to upstream soon, as well as some patches to build the DirectXShaderCompiler with Zig.

\ No newline at end of file diff --git a/pkg/mach-gpu/index.html b/pkg/mach-gpu/index.html index 697430a..bc177bd 100644 --- a/pkg/mach-gpu/index.html +++ b/pkg/mach-gpu/index.html @@ -5,7 +5,7 @@ GitHub Discord
Mach v0.2 has been released! For all the details check out the announcement
Mach v0.2 has been released! For all the details check out the announcement

The WebGPU interface for Zig, featuring:

  • Zero-fuss installation, cross-compilation at the flip of a switch, and broad platform support.
  • 100% API coverage. Every function, type, constant, etc. has been exposed in a ziggified API.
  • Desktop, Steam Deck, (soon) web, and (future) mobile support.
  • A modern graphics API similar to Metal, Vulkan, and DirectX 12.
  • Cross-platform shading language
  • Compute shaders
  • Advanced GPU features where hardware support is available.

Benefits of mach/gpu and WebGPU

mach/gpu is a zero-cost idiomatic Zig interface to the next-generation WebGPU API, which supersedes WebGL and exposes the common denominator between the latest low-level graphics APIs (Vulkan, Metal, D3D12) in the web.

Despite its name, WebGPU was built with native support in mind and has substantial investment from Mozilla, Google, Microsoft, Intel, and Apple.

When targeting WebAssembly, mach/gpu merely calls into the browser’s native WebGPU implementation.

When targeting native platforms, we build Google Chrome’s WebGPU implementation, Dawn using Zig as the C/C++ compiler toolchain. We bypass the client-server sandboxing model, and use zig build (plus a lot of hand-holding) to support zero-fuss cross compilation & installation without any third-party Google tools, libraries, etc. Just zig and git needed, nothing else.

Perfecting WebGPU for Zig

There is a detailed write-up of how we’ve been perfecting WebGPU for Zig.

Usage

On your own, this involves creating a window (using GLFW, and other APIs if you want Web, Mobile, or other platform support), using Dawn’s API to create a device and bind it to that window, using OS-specific APIs to get the window handle to bind, etc. examples/main.zig demonstrates how to do this. There’s a fair amount of setup code involved.

You may also want to consider using Mach core.

Goals

  • Allow comptime-defined interception of WebGPU API requests (comptime interfaces.)
  • Expose a standard Dawn webgpu.h-compliant C ABI, which routes through Zig comptime interfaces.
  • Support Dawn and Browser (via WASM/JS) implementations of WebGPU.
  • Broad platform support: desktop, mobile, web, consoles.
  • First-class Linux support (Wayland, OpenGL and OpenGL ES fallbacks, etc.)

Non-goals

  • Support non-Dawn (e.g. Rust WebGPU) implementations if they don’t match the same webgpu.h as Dawn.
  • Maintain backwards compatibility with deprecated webgpu.h methods.

Getting started

Create a build.zig.zon in your project (replace LATEST_COMMIT with the latest commit hash):

.{
     .name = "mypkg",
     .version = "0.1.0",
diff --git a/pkg/mach-model3d/index.html b/pkg/mach-model3d/index.html
index 0e3c30e..d6079e1 100644
--- a/pkg/mach-model3d/index.html
+++ b/pkg/mach-model3d/index.html
@@ -5,7 +5,7 @@
 GitHub
 Discord
 
Mach v0.2 has been released! For all the details check out the announcement
Mach v0.2 has been released! For all the details check out the announcement

Compact, featureful model format & alternative to glTF.

Model3D is an up-and-coming compact, featureful, universal model format that tries to address the shortcomings of existing formats (yes, including glTF - see their rationale.)

Experimental

This is an experimental project according to our stability guarantees:

When a project has an experimental warning, it means all bets are off. You should carefully read the warning to understand why the project is experimental, and assume the worst.

Tracking issue: https://github.com/hexops/mach/issues/969

Getting started

Create a build.zig.zon in your project (replace LATEST_COMMIT with the latest commit hash):

.{
     .name = "mypkg",
     .version = "0.1.0",
diff --git a/pkg/mach-opus/index.html b/pkg/mach-opus/index.html
index 59560d3..cbe14e2 100644
--- a/pkg/mach-opus/index.html
+++ b/pkg/mach-opus/index.html
@@ -5,7 +5,7 @@
 GitHub
 Discord
 
Mach v0.2 has been released! For all the details check out the announcement
Mach v0.2 has been released! For all the details check out the announcement

Opus audio decoding and encoding for Zig via the battle-hardened xiph.org libopus and libopusfile

Experimental

This is an experimental project according to our stability guarantees:

When a project has an experimental warning, it means all bets are off. You should carefully read the warning to understand why the project is experimental, and assume the worst.

Tracking issue: https://github.com/hexops/mach/issues/961

Getting started

Create a build.zig.zon in your project (replace LATEST_COMMIT with the latest commit hash):

.{
     .name = "mypkg",
     .version = "0.1.0",
diff --git a/pkg/mach-sysaudio/index.html b/pkg/mach-sysaudio/index.html
index 63c90f5..e854b30 100644
--- a/pkg/mach-sysaudio/index.html
+++ b/pkg/mach-sysaudio/index.html
@@ -5,7 +5,7 @@
 GitHub
 Discord
 
Mach v0.2 has been released! For all the details check out the announcement
Mach v0.2 has been released! For all the details check out the announcement

Truly cross-platform, low-level, audio IO in Zig - playback and recording with backends for:

  • Linux
    • PulseAudio
    • PipeWire
    • Jack
    • ALSA
  • Windows: WASAPI
  • macOS/iOS: CoreAudio
  • WebAssembly: WebAudio

Getting started

Create a build.zig.zon in your project (replace LATEST_COMMIT with the latest commit hash):

.{
     .name = "mypkg",
     .version = "0.1.0",
diff --git a/pkg/mach-sysgpu/index.html b/pkg/mach-sysgpu/index.html
index 72e4d54..9310089 100644
--- a/pkg/mach-sysgpu/index.html
+++ b/pkg/mach-sysgpu/index.html
@@ -5,5 +5,5 @@
 GitHub
 Discord
 
Mach v0.2 has been released! For all the details check out the announcement
Mach v0.2 has been released! For all the details check out the announcement

Highly experimental, blazingly fast, lean & mean descendant of WebGPU written in Zig.

Experimental

This is an experimental project according to our stability guarantees:

When a project has an experimental warning, it means all bets are off. You should carefully read the warning to understand why the project is experimental, and assume the worst.

Tracking issue: https://github.com/hexops/mach/issues/966

History

We announced Dusk, a WebGPU implementation in Zig, as part of the Mach v0.2 release.

Since then, the project grew to have the goal of superseding WebGPU for native applications. Although the API is still greatly inspired by (and familiar to developers who use) WebGPU, it now stands on its own as a direct competitor to WebGPU.

Goals

Modern graphics API

Competitive with other graphics abstraction APIs like WebGPU, SDL3, sokol_gfx, etc.

Feels very cozy to devs familiar with WebGPU, ‘a better WebGPU’.

  • Windows: Direct3D 12
  • Linux: Vulkan
  • macOS: Metal
  • iOS: Metal
  • Android: Vulkan
  • Browser: WebGPU

Alleviate pain points of WebGPU

  • Better approach to pipeline creation / descriptors, with an API that supports push constants / optimization when available.
  • More integrated approach to binding resources to shaders, type-correctness, etc.

More modern, performant, and featureful

  • WebGPU must support all hardware released in the last 8-12 years, sysgpu only targets hardware in the last 5 years, making our ‘baseline’ API much more modern.
  • WebGPU/Browsers cannot add new features or make breaking changes without committee quorum, implementations in multiple browsers, etc. We can move faster because sysgpu is an implementation, not a specification.
  • Support modern functionality as optional extensions, e.g. bindless resources, ray tracing, push constants, multiple queues/async, etc. and graduate them to non-extensions when hardware support is wide enough.

Offline shader compilation

  • Compile shaders fully offline for better runtime performance, and also so you do not have to ship large shader compilation stacks with your binaries.

Not just for games

Viable for plain desktop/mobile apps, too. We will provide e.g. an OpenGL fallback on platforms like Linux where functional drivers may not exist so that it is viable for plain desktop/mobile apps.

Other goals

  • C API support
  • Improved shading language (think: #include support, better integration with Zig for type safety, etc.)

Non-goals

  • Support of hardware released more than 5+ years ago (browsers/WebGPU must support hardware released 8-12 years ago), e.g. we will have no “WebGPU compatibility mode” equivalent
  • Patching and/or working around bad/insecure graphics drivers (browsers/WebGPU must do this and maintains e.g. denylists of drivers.)
  • Pure software rendering, e.g. WebGPU/Dawn falls back to a Vulkan software renderer in some cases.
  • Bringing your own shading language (via SPIRV or otherwise), we’ll bless one shading language and it will be integrated nicely.
  • Supporting more than 1 (max 2) backend APIs per platform; in general we will have one backend using the platform’s modern-and-widely-supported API, and sometimes one backend as a fallback for systems that have great divergence. We won’t support a myriad of backends per platform.
  • Support of extremely underpowered hardware (e.g. embedded devices)

Experimental, not ready for general use

sysgpu is coming along very nicely, with functional backends capable of running most of the mach-core examples already for Direct3D 12, Metal, and Vulkan today. An OpenGL fallback backend is in active development.

We have a functional WGSL parser/compiler/transpiler which in many cases is more correct than Naga (wgpu-native/wgpu-rs WGSL implementation), but shader compilation is an open area of exploration and we are considering replacing WGSL with a Zig(-like?) shading language instead.

Currently it is a nearly fully-functional implementation of webgpu.h, but numerous improvements to the actual API are planned.

Join the Mach Discord community for discussion, keep tabs on open issues and wait for an announcement that it is generally ready for use.

\ No newline at end of file diff --git a/pkg/mach-sysjs/index.html b/pkg/mach-sysjs/index.html index 6a5624e..ab1596b 100644 --- a/pkg/mach-sysjs/index.html +++ b/pkg/mach-sysjs/index.html @@ -5,5 +5,5 @@ GitHub Discord
Mach v0.2 has been released! For all the details check out the announcement
Mach v0.2 has been released! For all the details check out the announcement

Enables Zig <-> JS interoperability via code generation, with complex type support.

Experimental

This is an experimental project according to our stability guarantees:

When a project has an experimental warning, it means all bets are off. You should carefully read the warning to understand why the project is experimental, and assume the worst.

Tracking issue: https://github.com/hexops/mach/issues/970

Table of contents

\ No newline at end of file