diff --git a/build.zig b/build.zig index 9bfec28..b224710 100644 --- a/build.zig +++ b/build.zig @@ -1,23 +1,25 @@ const std = @import("std"); +const mach_core = @import("mach_core"); -pub fn build(b: *std.Build) void { +pub fn build(b: *std.Build) !void { const target = b.standardTargetOptions(.{}); const optimize = b.standardOptimizeOption(.{}); - const exe = b.addExecutable(.{ + const mach_core_dep = b.dependency("mach_core", .{ + .target = target, + .optimize = optimize, + }); + const app = try mach_core.App.init(b, mach_core_dep.builder, .{ .name = "zig-bloxel-game", - .root_source_file = .{ .path = "src/main.zig" }, + .src = "src/main.zig", .target = target, .optimize = optimize, + .deps = &.{}, }); - b.installArtifact(exe); - - const run_cmd = b.addRunArtifact(exe); - run_cmd.step.dependOn(b.getInstallStep()); - if (b.args) |args| run_cmd.addArgs(args); + if (b.args) |args| app.run.addArgs(args); const run_step = b.step("run", "Run the app"); - run_step.dependOn(&run_cmd.step); + run_step.dependOn(&app.run.step); const unit_tests = b.addTest(.{ .root_source_file = .{ .path = "src/main.zig" }, diff --git a/build.zig.zon b/build.zig.zon index 780f385..cac1b77 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -8,4 +8,11 @@ "build.zig.zon", "README.md", }, + + .dependencies = .{ + .mach_core = .{ + .url = "https://pkg.machengine.org/mach-core/6a62bcc90e0d072d632788a6575d77942bd09a19.tar.gz", + .hash = "12209d39954fcda0be158461c10f64d14d5c7d097bd6d26785b332d75ffefa7dd7a0", + }, + }, } diff --git a/src/main.zig b/src/main.zig index 9bafad8..a6f641a 100644 --- a/src/main.zig +++ b/src/main.zig @@ -1,17 +1,98 @@ const std = @import("std"); +const core = @import("mach-core"); +const gpu = core.gpu; -pub fn main() !void { - std.debug.print("All your {s} are belong to us.\n", .{"codebase"}); - const stdout_file = std.io.getStdOut().writer(); - var bw = std.io.bufferedWriter(stdout_file); - const stdout = bw.writer(); - try stdout.print("Run `zig build test` to run the tests.\n", .{}); - try bw.flush(); +pub const App = @This(); + +title_timer: core.Timer, +pipeline: *gpu.RenderPipeline, + +pub fn init(app: *App) !void { + try core.init(.{}); + + const shader_module = core.device.createShaderModuleWGSL("shader.wgsl", @embedFile("shader.wgsl")); + defer shader_module.release(); + + const blend = gpu.BlendState{}; + const color_target = gpu.ColorTargetState{ + .format = core.descriptor.format, + .blend = &blend, + .write_mask = gpu.ColorWriteMaskFlags.all, + }; + const fragment = gpu.FragmentState.init(.{ + .module = shader_module, + .entry_point = "frag_main", + .targets = &.{color_target}, + }); + const pipeline_descriptor = gpu.RenderPipeline.Descriptor{ + .vertex = gpu.VertexState{ + .module = shader_module, + .entry_point = "vertex_main", + }, + .fragment = &fragment, + }; + + app.title_timer = try core.Timer.start(); + app.pipeline = core.device.createRenderPipeline(&pipeline_descriptor); } -test "simple test" { - var list = std.ArrayList(i32).init(std.testing.allocator); - defer list.deinit(); - try list.append(42); - try std.testing.expectEqual(@as(i32, 42), list.pop()); +pub fn deinit(app: *App) void { + defer core.deinit(); + defer app.pipeline.release(); +} + +pub fn update(app: *App) !bool { + var iter = core.pollEvents(); + while (iter.next()) |event| { + switch (event) { + .close => return true, + else => {}, + } + } + + // Get back buffer texture to render to. + const back_buffer_view = core.swap_chain.getCurrentTextureView().?; + defer back_buffer_view.release(); + // Once rendering is done (hence `defer`), swap back buffer to the front to display. + defer core.swap_chain.present(); + + const color_attachment = gpu.RenderPassColorAttachment{ + .view = back_buffer_view, + .clear_value = std.mem.zeroes(gpu.Color), + .load_op = .clear, + .store_op = .store, + }; + const render_pass_info = gpu.RenderPassDescriptor.init(.{ + .color_attachments = &.{color_attachment}, + }); + + // Create a `WGPUCommandEncoder` which provides an interface for recording GPU commands. + const encoder = core.device.createCommandEncoder(null); + defer encoder.release(); + + { + const pass = encoder.beginRenderPass(&render_pass_info); + defer pass.release(); + defer pass.end(); + + pass.setPipeline(app.pipeline); + + // Draw a triangle with the help of a specialized shader. + pass.draw(3, 1, 0, 0); + } + + // Finish recording commands, creating a `WGPUCommandBuffer`. + var command = encoder.finish(null); + defer command.release(); + + // Submit the command(s) to the GPU. + core.queue.submit(&.{command}); + + // Update the window title to show FPS and input frequency. + if (app.title_timer.read() >= 1.0) { + app.title_timer.reset(); + try core.printTitle("Triangle [ {d}fps ] [ Input {d}hz ]", .{ core.frameRate(), core.inputRate() }); + } + + return false; } diff --git a/src/shader.wgsl b/src/shader.wgsl new file mode 100644 index 0000000..f34d2e2 --- /dev/null +++ b/src/shader.wgsl @@ -0,0 +1,14 @@ +@vertex fn vertex_main( + @builtin(vertex_index) index: u32 +) -> @builtin(position) vec4 { + let pos = array, 3>( + vec2( 0.0, 0.5), + vec2(-0.5, -0.5), + vec2( 0.5, -0.5) + ); + return vec4(pos[index], 0.0, 1.0); +} + +@fragment fn frag_main() -> @location(0) vec4 { + return vec4(1.0, 0.0, 0.0, 1.0); +}