ee1a63bd
build(renderer): wire vulkan-zig + glslc + embed SPIR-V
a73x 2026-04-08 08:52
Commit message
build.zig
| Old | New | ||
|---|---|---|---|
| @@ -144,4 +144,50 @@ pub fn build(b: *std.Build) void { | |||
| 144 | .root_module = font_test_mod, | 144 | .root_module = font_test_mod, |
| 145 | }); | 145 | }); |
| 146 | test_step.dependOn(&b.addRunArtifact(font_tests).step); | 146 | test_step.dependOn(&b.addRunArtifact(font_tests).step); |
| 147 | |||
| 148 | // vulkan-zig — generate Vulkan bindings from vk.xml | ||
| 149 | const vulkan_headers_dep = b.dependency("vulkan_headers", .{}); | ||
| 150 | const vulkan_zig_dep = b.dependency("vulkan", .{ | ||
| 151 | .registry = vulkan_headers_dep.path("registry/vk.xml"), | ||
| 152 | }); | ||
| 153 | const vulkan_module = vulkan_zig_dep.module("vulkan-zig"); | ||
| 154 | |||
| 155 | // Compile cell shaders to SPIR-V via glslc | ||
| 156 | const glslc_vert = b.addSystemCommand(&.{ "glslc", "--target-env=vulkan1.2" }); | ||
| 157 | glslc_vert.addFileArg(b.path("shaders/cell.vert")); | ||
| 158 | glslc_vert.addArg("-o"); | ||
| 159 | const cell_vert_spv = glslc_vert.addOutputFileArg("cell.vert.spv"); | ||
| 160 | |||
| 161 | const glslc_frag = b.addSystemCommand(&.{ "glslc", "--target-env=vulkan1.2" }); | ||
| 162 | glslc_frag.addFileArg(b.path("shaders/cell.frag")); | ||
| 163 | glslc_frag.addArg("-o"); | ||
| 164 | const cell_frag_spv = glslc_frag.addOutputFileArg("cell.frag.spv"); | ||
| 165 | |||
| 166 | // Collect renderer.zig + both SPV blobs into one WriteFiles directory so | ||
| 167 | // that @embedFile("cell.vert.spv") resolves correctly relative to renderer.zig. | ||
| 168 | const renderer_dir = b.addWriteFiles(); | ||
| 169 | const renderer_zig_path = renderer_dir.addCopyFile(b.path("src/renderer.zig"), "renderer.zig"); | ||
| 170 | _ = renderer_dir.addCopyFile(cell_vert_spv, "cell.vert.spv"); | ||
| 171 | _ = renderer_dir.addCopyFile(cell_frag_spv, "cell.frag.spv"); | ||
| 172 | |||
| 173 | // renderer module | ||
| 174 | const renderer_mod = b.createModule(.{ | ||
| 175 | .root_source_file = renderer_zig_path, | ||
| 176 | .target = target, | ||
| 177 | .optimize = optimize, | ||
| 178 | }); | ||
| 179 | renderer_mod.addImport("vulkan", vulkan_module); | ||
| 180 | exe_mod.addImport("renderer", renderer_mod); | ||
| 181 | |||
| 182 | // Test renderer.zig | ||
| 183 | const renderer_test_mod = b.createModule(.{ | ||
| 184 | .root_source_file = renderer_zig_path, | ||
| 185 | .target = target, | ||
| 186 | .optimize = optimize, | ||
| 187 | }); | ||
| 188 | renderer_test_mod.addImport("vulkan", vulkan_module); | ||
| 189 | const renderer_tests = b.addTest(.{ | ||
| 190 | .root_module = renderer_test_mod, | ||
| 191 | }); | ||
| 192 | test_step.dependOn(&b.addRunArtifact(renderer_tests).step); | ||
| 147 | } | 193 | } |
build.zig.zon
| Old | New | ||
|---|---|---|---|
| @@ -15,8 +15,8 @@ | |||
| 15 | .hash = "wayland-0.6.0-dev-lQa1koD8AQDA1Ez_XdLOqA8QPvwKyxsAanpCIsZEb3OS", | 15 | .hash = "wayland-0.6.0-dev-lQa1koD8AQDA1Ez_XdLOqA8QPvwKyxsAanpCIsZEb3OS", |
| 16 | }, | 16 | }, |
| 17 | .vulkan = .{ | 17 | .vulkan = .{ |
| 18 | .url = "git+https://github.com/Snektron/vulkan-zig#b3086a867a47bb10e66a987a38115a43056b60f7", | 18 | .url = "git+https://github.com/Snektron/vulkan-zig?ref=zig-0.15-compat#3ada9e2989bab70090a55f0f6fac19ea90d06357", |
| 19 | .hash = "vulkan-0.0.0-r7Ytx99oAwDmJu0XtvukW_bSYnAv1PpbPO9ZFwsjcPPO", | 19 | .hash = "vulkan-0.0.0-r7Ytx6FIAwD3QyrrJhvObO0YSZ6WEkMGSCPKbhM__HFd", |
| 20 | }, | 20 | }, |
| 21 | .vulkan_headers = .{ | 21 | .vulkan_headers = .{ |
| 22 | .url = "git+https://github.com/KhronosGroup/Vulkan-Headers#afe9eb980aa928a66d1c9c06f38c55dd59868720", | 22 | .url = "git+https://github.com/KhronosGroup/Vulkan-Headers#afe9eb980aa928a66d1c9c06f38c55dd59868720", |
shaders/cell.frag
| Old | New | ||
|---|---|---|---|
| @@ -0,0 +1,14 @@ | |||
| 1 | #version 450 | ||
| 2 | |||
| 3 | layout(binding = 0) uniform sampler2D glyph_atlas; | ||
| 4 | |||
| 5 | layout(location = 0) in vec2 in_uv; | ||
| 6 | layout(location = 1) in vec4 in_fg; | ||
| 7 | layout(location = 2) in vec4 in_bg; | ||
| 8 | |||
| 9 | layout(location = 0) out vec4 out_color; | ||
| 10 | |||
| 11 | void main() { | ||
| 12 | float alpha = texture(glyph_atlas, in_uv).r; | ||
| 13 | out_color = mix(in_bg, in_fg, alpha); | ||
| 14 | } | ||
shaders/cell.vert
| Old | New | ||
|---|---|---|---|
| @@ -0,0 +1,27 @@ | |||
| 1 | #version 450 | ||
| 2 | |||
| 3 | layout(push_constant) uniform PushConstants { | ||
| 4 | vec2 viewport_size; | ||
| 5 | vec2 cell_size; | ||
| 6 | } pc; | ||
| 7 | |||
| 8 | layout(location = 0) in vec2 in_unit_pos; | ||
| 9 | |||
| 10 | layout(location = 1) in vec2 in_cell_pos; | ||
| 11 | layout(location = 2) in vec4 in_uv_rect; | ||
| 12 | layout(location = 3) in vec4 in_fg_color; | ||
| 13 | layout(location = 4) in vec4 in_bg_color; | ||
| 14 | |||
| 15 | layout(location = 0) out vec2 out_uv; | ||
| 16 | layout(location = 1) out vec4 out_fg; | ||
| 17 | layout(location = 2) out vec4 out_bg; | ||
| 18 | |||
| 19 | void main() { | ||
| 20 | vec2 pixel_pos = (in_cell_pos + in_unit_pos) * pc.cell_size; | ||
| 21 | vec2 ndc = (pixel_pos / pc.viewport_size) * 2.0 - 1.0; | ||
| 22 | gl_Position = vec4(ndc, 0.0, 1.0); | ||
| 23 | |||
| 24 | out_uv = mix(in_uv_rect.xy, in_uv_rect.zw, in_unit_pos); | ||
| 25 | out_fg = in_fg_color; | ||
| 26 | out_bg = in_bg_color; | ||
| 27 | } | ||
src/renderer.zig
| Old | New | ||
|---|---|---|---|
| @@ -0,0 +1,21 @@ | |||
| 1 | const std = @import("std"); | ||
| 2 | const vk = @import("vulkan"); | ||
| 3 | |||
| 4 | pub const cell_vert_spv: []const u8 = @embedFile("cell.vert.spv"); | ||
| 5 | pub const cell_frag_spv: []const u8 = @embedFile("cell.frag.spv"); | ||
| 6 | |||
| 7 | test "vulkan module imports" { | ||
| 8 | _ = vk; | ||
| 9 | } | ||
| 10 | |||
| 11 | test "shaders are embedded with SPIR-V magic" { | ||
| 12 | try std.testing.expect(cell_vert_spv.len > 0); | ||
| 13 | try std.testing.expect(cell_frag_spv.len > 0); | ||
| 14 | |||
| 15 | // SPIR-V magic number (0x07230203), little-endian first 4 bytes | ||
| 16 | const magic: u32 = std.mem.readInt(u32, cell_vert_spv[0..4], .little); | ||
| 17 | try std.testing.expectEqual(@as(u32, 0x07230203), magic); | ||
| 18 | |||
| 19 | const magic2: u32 = std.mem.readInt(u32, cell_frag_spv[0..4], .little); | ||
| 20 | try std.testing.expectEqual(@as(u32, 0x07230203), magic2); | ||
| 21 | } | ||