@@ -13,6 +13,278 @@ rss_ignore: true
1313
1414# Getting Started using Mach
1515
16- _ Coming soon. _
16+ ## Initialize your Zig project
1717
18- See also: [ the Mach standard library, and 'Using mach.math as a separate library'] ( ../stdlib ) .
18+ ``` sh
19+ zig init
20+ ```
21+
22+ Delete the ` build.zig ` file and ` src/ ` files it created, we won't need them.
23+
24+ ## Add Mach dependency to ` build.zig.zon `
25+
26+ <pre ><code id =" zig-fetch-main " >zig fetch --save https://pkg.machengine.org/mach/$LATEST_COMMIT.tar.gz
27+ </code ></pre >
28+ <script >
29+ fetch (' https://api.github.com/repos/hexops/mach/branches/main' , {
30+ method: ' GET' ,
31+ headers: {' Accept' : ' application/json' }},
32+ )
33+ .then (resp => resp .json ())
34+ .then (resp => {
35+ let elem = document .querySelector (' #zig-fetch-main' );
36+ elem .innerHTML = elem .innerHTML .replace (' $LATEST_COMMIT' , resp .commit .sha );
37+ });
38+ </script >
39+
40+ ## Create your ` build.zig ` file
41+
42+ A basic ` build.zig ` file for use with Mach looks like this:
43+
44+ ``` zig
45+ const std = @import("std");
46+
47+ pub fn build(b: *std.Build) void {
48+ const target = b.standardTargetOptions(.{});
49+ const optimize = b.standardOptimizeOption(.{});
50+
51+ // Create our Mach app module, where all our code lives.
52+ const app_mod = b.createModule(.{
53+ .root_source_file = b.path("src/App.zig"),
54+ .optimize = optimize,
55+ .target = target,
56+ });
57+
58+ // Add Mach import to our app.
59+ const mach_dep = b.dependency("mach", .{
60+ .target = target,
61+ .optimize = optimize,
62+ });
63+ app_mod.addImport("mach", mach_dep.module("mach"));
64+
65+ // Have Mach create the executable for us
66+ const exe = @import("mach").addExecutable(mach_dep.builder, .{
67+ .name = "hello-world",
68+ .app = app_mod,
69+ .target = target,
70+ .optimize = optimize,
71+ });
72+ b.installArtifact(exe);
73+
74+ // Run the app when `zig build run` is invoked
75+ const run_cmd = b.addRunArtifact(exe);
76+ run_cmd.step.dependOn(b.getInstallStep());
77+ if (b.args) |args| {
78+ run_cmd.addArgs(args);
79+ }
80+ const run_step = b.step("run", "Run the app");
81+ run_step.dependOn(&run_cmd.step);
82+
83+ // Run tests when `zig build test` is run
84+ const app_unit_tests = b.addTest(.{
85+ .root_module = app_mod,
86+ });
87+ const run_app_unit_tests = b.addRunArtifact(app_unit_tests);
88+ const test_step = b.step("test", "Run unit tests");
89+ test_step.dependOn(&run_app_unit_tests.step);
90+ }
91+ ```
92+
93+ ## Create your ` src/App.zig ` file
94+
95+ This is where all our program code lives, it will render a triangle:
96+
97+ ``` zig
98+ const std = @import("std");
99+ const mach = @import("mach");
100+ const gpu = mach.gpu;
101+
102+ const App = @This();
103+
104+ // The set of Mach modules our application may use.
105+ pub const Modules = mach.Modules(.{
106+ mach.Core,
107+ App,
108+ });
109+
110+ pub const mach_module = .app;
111+
112+ pub const mach_systems = .{ .main, .init, .tick, .deinit };
113+
114+ pub const main = mach.schedule(.{
115+ .{ mach.Core, .init },
116+ .{ App, .init },
117+ .{ mach.Core, .main },
118+ });
119+
120+ window: mach.ObjectID,
121+ title_timer: mach.time.Timer,
122+ pipeline: *gpu.RenderPipeline,
123+
124+ pub fn init(
125+ core: *mach.Core,
126+ app: *App,
127+ app_mod: mach.Mod(App),
128+ ) !void {
129+ core.on_tick = app_mod.id.tick;
130+ core.on_exit = app_mod.id.deinit;
131+
132+ const window = try core.windows.new(.{
133+ .title = "core-triangle",
134+ });
135+
136+ // Store our render pipeline in our module's state, so we can access it later on.
137+ app.* = .{
138+ .window = window,
139+ .title_timer = try mach.time.Timer.start(),
140+ .pipeline = undefined,
141+ };
142+ }
143+
144+ fn setupPipeline(core: *mach.Core, app: *App, window_id: mach.ObjectID) !void {
145+ var window = core.windows.getValue(window_id);
146+ defer core.windows.setValueRaw(window_id, window);
147+
148+ // Create our shader module
149+ const shader_module = window.device.createShaderModuleWGSL("shader.wgsl", @embedFile("shader.wgsl"));
150+ defer shader_module.release();
151+
152+ // Blend state describes how rendered colors get blended
153+ const blend = gpu.BlendState{};
154+
155+ // Color target describes e.g. the pixel format of the window we are rendering to.
156+ const color_target = gpu.ColorTargetState{
157+ .format = window.framebuffer_format,
158+ .blend = &blend,
159+ };
160+
161+ // Fragment state describes which shader and entrypoint to use for rendering fragments.
162+ const fragment = gpu.FragmentState.init(.{
163+ .module = shader_module,
164+ .entry_point = "frag_main",
165+ .targets = &.{color_target},
166+ });
167+
168+ // Create our render pipeline that will ultimately get pixels onto the screen.
169+ const label = @tagName(mach_module) ++ ".init";
170+ const pipeline_descriptor = gpu.RenderPipeline.Descriptor{
171+ .label = label,
172+ .fragment = &fragment,
173+ .vertex = gpu.VertexState{
174+ .module = shader_module,
175+ .entry_point = "vertex_main",
176+ },
177+ };
178+ app.pipeline = window.device.createRenderPipeline(&pipeline_descriptor);
179+ }
180+
181+ pub fn tick(app: *App, core: *mach.Core) void {
182+ while (core.nextEvent()) |event| {
183+ switch (event) {
184+ .window_open => |ev| {
185+ try setupPipeline(core, app, ev.window_id);
186+ },
187+ .close => core.exit(),
188+ else => {},
189+ }
190+ }
191+
192+ const window = core.windows.getValue(app.window);
193+
194+ // Grab the back buffer of the swapchain
195+ const back_buffer_view = window.swap_chain.getCurrentTextureView().?;
196+ defer back_buffer_view.release();
197+
198+ // Create a command encoder
199+ const label = @tagName(mach_module) ++ ".tick";
200+
201+ const encoder = window.device.createCommandEncoder(&.{ .label = label });
202+ defer encoder.release();
203+
204+ // Begin render pass
205+ const sky_blue_background = gpu.Color{ .r = 0.776, .g = 0.988, .b = 1, .a = 1 };
206+ const color_attachments = [_]gpu.RenderPassColorAttachment{.{
207+ .view = back_buffer_view,
208+ .clear_value = sky_blue_background,
209+ .load_op = .clear,
210+ .store_op = .store,
211+ }};
212+ const render_pass = encoder.beginRenderPass(&gpu.RenderPassDescriptor.init(.{
213+ .label = label,
214+ .color_attachments = &color_attachments,
215+ }));
216+ defer render_pass.release();
217+
218+ // Draw
219+ render_pass.setPipeline(app.pipeline);
220+ render_pass.draw(3, 1, 0, 0);
221+
222+ // Finish render pass
223+ render_pass.end();
224+
225+ // Submit our commands to the queue
226+ var command = encoder.finish(&.{ .label = label });
227+ defer command.release();
228+ window.queue.submit(&[_]*gpu.CommandBuffer{command});
229+ }
230+
231+ pub fn deinit(app: *App) void {
232+ app.pipeline.release();
233+ }
234+ ```
235+
236+ ## Create your ` src/shader.wgsl ` program
237+
238+ This is the fragment and vertex GPU shader used to render the triangle:
239+
240+ ``` wgsl
241+ @vertex fn vertex_main(
242+ @builtin(vertex_index) VertexIndex : u32
243+ ) -> @builtin(position) vec4<f32> {
244+ var pos = array<vec2<f32>, 3>(
245+ vec2<f32>( 0.0, 0.5),
246+ vec2<f32>(-0.5, -0.5),
247+ vec2<f32>( 0.5, -0.5)
248+ );
249+ return vec4<f32>(pos[VertexIndex], 0.0, 1.0);
250+ }
251+
252+ @fragment fn frag_main() -> @location(0) vec4<f32> {
253+ return vec4<f32>(1.0, 0.0, 0.0, 1.0);
254+ }
255+ ```
256+
257+ ## Build and run
258+
259+ ``` sh
260+ zig build run
261+ ```
262+
263+ This will open a window and display a triangle:
264+
265+ ![ ] ( /example/core-triangle.png )
266+
267+ ## Run tests
268+
269+ If you later decide to add any Zig ` test ` blocks to your ` src/App.zig ` , you can run them using ` zig build test ` .
270+
271+ ## Cross-compile
272+
273+ You can cross-compile this program to any major desktop OS using the following:
274+
275+ ``` sh
276+ # Linux
277+ zig build -Dtarget=x86_64-linux-gnu
278+
279+ # macOS (Apple Silicon)
280+ zig build -Dtarget=aarch64-macos
281+
282+ # Windows
283+ zig build -Dtarget=x86_64-windows-gnu
284+ ```
285+
286+ (the output will be in ` zig-out/bin/ ` )
287+
288+ ## Continue learning
289+
290+ From here, you can [ explore more examples] ( https://github.com/hexops/mach/tree/main/examples ) or read about [ the object systems] ( /docs/object/ ) which is the foundation of every Mach API.
0 commit comments