summaryrefslogtreecommitdiff
path: root/build.zig
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-08-12 16:33:14 -0300
committerGabriel Schneider <[email protected]>2026-08-12 20:22:18 -0300
commit9280c597b000eed661fd98793e182fdcb640f6cd (patch)
tree1b8e928c052c46ceb9a58c83256d5fb4e11e361a /build.zig
parentbc89f57cb576e23a58572ec35f96db068367f1b4 (diff)
downloadpardes-9280c597b000eed661fd98793e182fdcb640f6cd.tar.gz
pardes-9280c597b000eed661fd98793e182fdcb640f6cd.zip
shaders: -Dprebuilt-shaders, so a gui build needs no Vulkan SDK
glslc is the one build input that wants a tool a stock machine does not have, and it is also the input that changes least often: eight GLSL files that have outlived several rewrites of everything around them. Asking every machine that wants to run the SDL shell for shaderc is the wrong trade. The SPIR-V is now COMMITTED, under shaders/prebuilt/, and -Dprebuilt-shaders embeds that copy instead of shelling out. The default stays the honest one -- compile the shaders that are actually in the tree -- because the flag trades a dependency for a freshness problem: with it on, the .glsl sources are not build inputs at all, so editing one changes nothing. `zig build shaders` is the other half, and it is deliberately independent of -Dplatform: it recompiles every shader and writes the result back into the tracked directory, so whoever changes a shader refreshes the cache on a machine that has the compiler and commits the diff. `jj diff shaders/prebuilt` after it is the freshness check -- empty means the cache was already current. The shader list is also spelled once now (gui_shaders): the eight embeds, the eight glslc runs and the refresh step all read it, so adding a shader is a name there plus the @embedFile in gui.zig, not three edits in two places. Verified: -Dplatform=gui -Dprebuilt-shaders builds with glslc absent from PATH, and image-harness passes on that binary -- real SDL GPU pipelines built from the committed SPIR-V, 512 source pixels read back. The default gui build still runs the eight glslc steps; tty runs none. The committed bytes are identical to a fresh glslc run, and `zig build shaders` is idempotent.
Diffstat (limited to 'build.zig')
-rw-r--r--build.zig79
1 files changed, 51 insertions, 28 deletions
diff --git a/build.zig b/build.zig
index 8fe2b1e3..6e311072 100644
--- a/build.zig
+++ b/build.zig
@@ -21,6 +21,19 @@ pub const macos_min_version: std.SemanticVersion = .{ .major = 13, .minor = 0, .
const zls_version = "0.16.1-dev+3e0d0820";
pub const TreeSitterGrammars = enum { disabled, zig, minimal, full };
+/// The GUI shell's shaders, spelled ONCE. Three things read this list: the
+/// eight SPIR-V imports the gui module embeds, the glslc runs that produce
+/// them, and `zig build shaders`, which writes the same set into the tracked
+/// shaders/prebuilt/. Each name is `shaders/<name>.glsl`, and the `.vert`/
+/// `.frag` in it is also the glslc shader stage — adding a shader is adding a
+/// name here plus the @embedFile in src/gui/gui.zig.
+const gui_shaders = [_][]const u8{
+ "ui.vert", "ui.frag",
+ "overlay.vert", "overlay.frag",
+ "image.vert", "image.frag",
+ "crt.vert", "crt.frag",
+};
+
pub fn build(b: *std.Build) void {
const platform = b.option(Platform, "platform", "which shell to build (tty, gui, web, macos)") orelse .tty;
// Default target is the Steam Deck (deckcap's trick): x86_64 linux-gnu
@@ -88,6 +101,20 @@ pub fn build(b: *std.Build) void {
// the machine that built it and needs no keychain; a Developer ID here is
// what makes one launchable on someone else's. See the macos branch below.
const macos_identity = b.option([]const u8, "macos-identity", "codesigning identity for pardes.app (default: ad-hoc)") orelse "-";
+ // Shader compilation is the one build input that needs a tool nothing else
+ // here needs: glslc, which ships with the Vulkan SDK / shaderc and is not
+ // on a stock machine. It is also the input that changes least often, so
+ // -Dprebuilt-shaders decouples the two: the SPIR-V compiled from
+ // shaders/*.glsl is committed under shaders/prebuilt/, and this flag
+ // embeds that copy instead of shelling out. `-Dplatform=gui` then builds
+ // with nothing but a C toolchain.
+ //
+ // Off by default because it trades a dependency for a freshness problem:
+ // with the flag on, the .glsl sources are NOT build inputs, so editing one
+ // changes nothing until someone runs `zig build shaders` (see below). The
+ // default therefore stays the honest one — compile the shaders that are
+ // actually in the tree.
+ const prebuilt_shaders = b.option(bool, "prebuilt-shaders", "embed the committed shaders/prebuilt/*.spv instead of running glslc (-Dplatform=gui without a Vulkan SDK)") orelse false;
// The browser shell is a freestanding wasm core plus ordinary web files.
// JavaScript owns the loop and IO; HTML/CSS own rendering.
@@ -102,6 +129,19 @@ pub fn build(b: *std.Build) void {
if (!enable_mupdf)
pdf_sections_bench_step.dependOn(&b.addFail("pdf-sections-bench is unavailable with -Dmupdf=false").step);
+ // The other half of -Dprebuilt-shaders: recompile every shader and write
+ // the SPIR-V back into shaders/prebuilt/, which is TRACKED rather than
+ // generated-and-gitignored, because a machine without glslc cannot
+ // regenerate it. Deliberately independent of -Dplatform: whoever changes a
+ // shader runs `zig build shaders` on a machine that has the compiler and
+ // commits the diff (`jj diff shaders/prebuilt` says whether the cache had
+ // drifted), and no gui build is involved either way.
+ const shaders_step = b.step("shaders", "recompile shaders/*.glsl into the tracked shaders/prebuilt/*.spv (glslc; refreshes what -Dprebuilt-shaders embeds)");
+ const update_shaders = b.addUpdateSourceFiles();
+ for (gui_shaders) |name|
+ update_shaders.addCopyFileToSource(compileGlsl(b, name), b.fmt("shaders/prebuilt/{s}.spv", .{name}));
+ shaders_step.dependOn(&update_shaders.step);
+
// Web LOOK has a tiny read-only source filesystem. Generate one Zig module
// on the host from every tracked .zig path + its current working-tree
// contents. The generator prefers Git as the tracked-file filter and has a
@@ -610,29 +650,11 @@ pub fn build(b: *std.Build) void {
root_mod.addAnonymousImport("AdwaitaMono-Regular.ttf", .{
.root_source_file = b.path("assets/AdwaitaMono-Regular.ttf"),
});
- root_mod.addAnonymousImport("ui.vert.spv", .{
- .root_source_file = compileGlsl(b, "shaders/ui.vert.glsl", "vertex", "ui.vert.spv"),
- });
- root_mod.addAnonymousImport("ui.frag.spv", .{
- .root_source_file = compileGlsl(b, "shaders/ui.frag.glsl", "fragment", "ui.frag.spv"),
- });
- root_mod.addAnonymousImport("overlay.vert.spv", .{
- .root_source_file = compileGlsl(b, "shaders/overlay.vert.glsl", "vertex", "overlay.vert.spv"),
- });
- root_mod.addAnonymousImport("overlay.frag.spv", .{
- .root_source_file = compileGlsl(b, "shaders/overlay.frag.glsl", "fragment", "overlay.frag.spv"),
- });
- root_mod.addAnonymousImport("image.vert.spv", .{
- .root_source_file = compileGlsl(b, "shaders/image.vert.glsl", "vertex", "image.vert.spv"),
- });
- root_mod.addAnonymousImport("image.frag.spv", .{
- .root_source_file = compileGlsl(b, "shaders/image.frag.glsl", "fragment", "image.frag.spv"),
- });
- root_mod.addAnonymousImport("crt.vert.spv", .{
- .root_source_file = compileGlsl(b, "shaders/crt.vert.glsl", "vertex", "crt.vert.spv"),
- });
- root_mod.addAnonymousImport("crt.frag.spv", .{
- .root_source_file = compileGlsl(b, "shaders/crt.frag.glsl", "fragment", "crt.frag.spv"),
+ for (gui_shaders) |name| root_mod.addAnonymousImport(b.fmt("{s}.spv", .{name}), .{
+ .root_source_file = if (prebuilt_shaders)
+ b.path(b.fmt("shaders/prebuilt/{s}.spv", .{name}))
+ else
+ compileGlsl(b, name),
});
}
@@ -1180,13 +1202,14 @@ fn reindexed(b: *std.Build, archive: std.Build.LazyPath, index: usize) std.Build
return run.addOutputFileArg(b.fmt("{d}.a", .{index}));
}
-// glslc <src> -fshader-stage=<stage> -o <out> -> .spv, returned as a LazyPath
-// for @embedFile. Only used by the SDL3 GPU shell (-Dplatform=gui).
-fn compileGlsl(b: *std.Build, src: []const u8, stage: []const u8, out: []const u8) std.Build.LazyPath {
+// glslc shaders/<name>.glsl -fshader-stage=<vertex|fragment> -o <name>.spv,
+// returned as a LazyPath for @embedFile. The stage is the name's own suffix.
+fn compileGlsl(b: *std.Build, name: []const u8) std.Build.LazyPath {
+ const stage = if (std.mem.endsWith(u8, name, ".vert")) "vertex" else "fragment";
const cmd = b.addSystemCommand(&.{ "glslc", b.fmt("-fshader-stage={s}", .{stage}) });
- cmd.addFileArg(b.path(src));
+ cmd.addFileArg(b.path(b.fmt("shaders/{s}.glsl", .{name})));
cmd.addArg("-o");
- return cmd.addOutputFileArg(out);
+ return cmd.addOutputFileArg(b.fmt("{s}.spv", .{name}));
}
/// The theme sources, sorted. SORTED because the run step is cached by its