diff options
| author | Gabriel Schneider <[email protected]> | 2026-08-16 15:49:12 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-08-18 23:44:42 -0300 |
| commit | 1551e409c31992437cb2fa864f576d45c8433801 (patch) | |
| tree | e2fae8451f87b735a1360c7c2e383fdc40165789 /src/effect_sources.zig | |
| parent | be2a9957708cbf0c478ca861c4a1f0f227bbfe10 (diff) | |
| download | pardes-1551e409c31992437cb2fa864f576d45c8433801.tar.gz pardes-1551e409c31992437cb2fa864f576d45c8433801.zip | |
big slow change: prebuilt shaders (SPIR-V/Metal), core gui reflow, docs, web + snapshot refresh
Diffstat (limited to 'src/effect_sources.zig')
| -rw-r--r-- | src/effect_sources.zig | 242 |
1 files changed, 242 insertions, 0 deletions
diff --git a/src/effect_sources.zig b/src/effect_sources.zig new file mode 100644 index 00000000..87026278 --- /dev/null +++ b/src/effect_sources.zig @@ -0,0 +1,242 @@ +//! Build-embedded source behind EffectCode. +//! +//! Several builtins intentionally share one shader pass. Returning segments +//! makes that sharing visible instead of copying or manufacturing a pretend +//! per-effect program. + +const std = @import("std"); +const build_config = @import("pardes_config"); +const runtime_config = @import("runtime_config.zig"); + +const panel_math = @embedFile("panel_animation.zig"); +const tty_compositor = @embedFile("tty/panel_compositor.zig"); +const panel_vertex = @embedFile("effect-source-ui.vert.glsl"); +const panel_fragment = @embedFile("effect-source-ui.frag.glsl"); +const image_vertex = @embedFile("effect-source-image.vert.glsl"); +const image_fragment = @embedFile("effect-source-image.frag.glsl"); +const scene_vertex = @embedFile("effect-source-crt.vert.glsl"); +const scene_fragment = @embedFile("effect-source-crt.frag.glsl"); +const gui_scene_mapping = @embedFile("gui/crt.zig"); +const gui_host = @embedFile("gui/gui.zig"); +const mac_scene = @embedFile("effect-source-crt.ci.metal"); +const mac_postprocessor = @embedFile("macos/Sources/ScenePostprocessor.swift"); +const mac_view = @embedFile("macos/Sources/PardesView.swift"); + +fn sourceSection( + comptime source: []const u8, + comptime begin_marker: []const u8, + comptime end_marker: []const u8, +) []const u8 { + // gui.zig is intentionally embedded as the source actually compiled, then + // narrowed to its marked host paths. The comptime byte scan is larger than + // Zig's small default quota but contributes no runtime work. + @setEvalBranchQuota(1_000_000); + const begin = std.mem.indexOf(u8, source, begin_marker) orelse + @compileError("EffectCode source begin marker is missing"); + const body = begin + begin_marker.len; + const end = std.mem.indexOfPos(u8, source, body, end_marker) orelse + @compileError("EffectCode source end marker is missing"); + return source[body..end]; +} + +const gui_panel_host = sourceSection( + gui_host, + "// EFFECT_CODE_PANEL_HOST_BEGIN\n", + "// EFFECT_CODE_PANEL_HOST_END", +); +const gui_native_panel = sourceSection( + gui_host, + "// EFFECT_CODE_NATIVE_PANEL_BEGIN\n", + "// EFFECT_CODE_NATIVE_PANEL_END", +); +const gui_frame_submission = sourceSection( + gui_host, + "// EFFECT_CODE_FRAME_SUBMISSION_BEGIN\n", + "// EFFECT_CODE_FRAME_SUBMISSION_END", +); +const gui_cell_instance = sourceSection( + gui_host, + "// EFFECT_CODE_CELL_INSTANCE_BEGIN\n", + "// EFFECT_CODE_CELL_INSTANCE_END", +); + +const ui_vertex_path = if (build_config.gui_shader_sources_prebuilt) + "shaders/prebuilt/ui.vert.glsl" +else + "shaders/ui.vert.glsl"; +const ui_fragment_path = if (build_config.gui_shader_sources_prebuilt) + "shaders/prebuilt/ui.frag.glsl" +else + "shaders/ui.frag.glsl"; +const image_vertex_path = if (build_config.gui_shader_sources_prebuilt) + "shaders/prebuilt/image.vert.glsl" +else + "shaders/image.vert.glsl"; +const image_fragment_path = if (build_config.gui_shader_sources_prebuilt) + "shaders/prebuilt/image.frag.glsl" +else + "shaders/image.frag.glsl"; +const scene_vertex_path = if (build_config.gui_shader_sources_prebuilt) + "shaders/prebuilt/crt.vert.glsl" +else + "shaders/crt.vert.glsl"; +const scene_fragment_path = if (build_config.gui_shader_sources_prebuilt) + "shaders/prebuilt/crt.frag.glsl" +else + "shaders/crt.frag.glsl"; + +pub const Backend = @TypeOf(build_config.platform); +pub const backend: Backend = build_config.platform; + +pub const GuiShaderSourceMode = enum { + live, + prebuilt_snapshot, + + pub fn label(mode: GuiShaderSourceMode) []const u8 { + return switch (mode) { + .live => "live GLSL compiled during this build", + .prebuilt_snapshot => "paired GLSL snapshot for committed SPIR-V", + }; + } +}; + +/// Only the SDL backend consumes GLSL/SPIR-V. Other backends deliberately have +/// no shader-source mode rather than being mislabeled "live" or "prebuilt". +pub fn guiShaderSourceMode() ?GuiShaderSourceMode { + if (backend != .gui) return null; + return if (build_config.gui_shader_sources_prebuilt) + .prebuilt_snapshot + else + .live; +} + +pub const Segment = struct { + path: []const u8, + source: []const u8, +}; + +const tty_panel = [_]Segment{ + .{ .path = "src/panel_animation.zig", .source = panel_math }, + .{ .path = "src/tty/panel_compositor.zig", .source = tty_compositor }, +}; +const gui_panel = [_]Segment{ + .{ .path = "src/panel_animation.zig", .source = panel_math }, + .{ .path = "src/gui/gui.zig#panel-host", .source = gui_panel_host }, + .{ .path = "src/gui/gui.zig#native-panel", .source = gui_native_panel }, + .{ .path = "src/gui/gui.zig#frame-submission", .source = gui_frame_submission }, + .{ .path = "src/gui/gui.zig#cell-instance", .source = gui_cell_instance }, + .{ .path = ui_vertex_path, .source = panel_vertex }, + .{ .path = ui_fragment_path, .source = panel_fragment }, + .{ .path = image_vertex_path, .source = image_vertex }, + .{ .path = image_fragment_path, .source = image_fragment }, +}; +const gui_scene = [_]Segment{ + .{ .path = "src/gui/crt.zig", .source = gui_scene_mapping }, + .{ .path = "src/gui/gui.zig#frame-submission", .source = gui_frame_submission }, + .{ .path = scene_vertex_path, .source = scene_vertex }, + .{ .path = scene_fragment_path, .source = scene_fragment }, +}; +const mac_panel = [_]Segment{ + .{ .path = "src/panel_animation.zig", .source = panel_math }, + .{ .path = "src/macos/Sources/PardesView.swift", .source = mac_view }, + .{ .path = "src/macos/Sources/ScenePostprocessor.swift", .source = mac_postprocessor }, + .{ .path = "shaders/crt.ci.metal", .source = mac_scene }, +}; +const mac_scene_segments = [_]Segment{ + .{ .path = "src/macos/Sources/PardesView.swift", .source = mac_view }, + .{ .path = "src/macos/Sources/ScenePostprocessor.swift", .source = mac_postprocessor }, + .{ .path = "shaders/crt.ci.metal", .source = mac_scene }, +}; + +/// Sources for the backend this executable was built for. Keeping the backend +/// compile-time and out of the call signature is important: a TTY binary must +/// not carry the macOS host and Metal source merely because another switch arm +/// could have returned them. +pub fn forSetting(setting: runtime_config.Setting) ?[]const Segment { + return switch (setting.action) { + .transition => switch (backend) { + .tty => &tty_panel, + .gui => &gui_panel, + .macos => &mac_panel, + .web => null, + }, + .scene => switch (backend) { + .gui => &gui_scene, + .macos => &mac_scene_segments, + .tty, .web => null, + }, + else => null, + }; +} + +test "every current-backend effect exposes embedded implementation code" { + for (runtime_config.settings) |setting| switch (setting.action) { + .transition => if (backend != .web) { + const segments = forSetting(setting) orelse return error.MissingTransitionSource; + for (segments) |segment| { + try std.testing.expect(segment.path.len > 0); + try std.testing.expect(segment.source.len > 0); + } + } else try std.testing.expect(forSetting(setting) == null), + .scene => if (backend == .gui or backend == .macos) { + const segments = forSetting(setting) orelse return error.MissingSceneSource; + for (segments) |segment| { + try std.testing.expect(segment.path.len > 0); + try std.testing.expect(segment.source.len > 0); + } + } else try std.testing.expect(forSetting(setting) == null), + else => {}, + }; +} + +test "GUI scene EffectCode includes both runtime shader stages" { + if (comptime backend == .gui) { + try std.testing.expectEqual(@as(usize, 4), gui_scene.len); + try std.testing.expectEqualStrings("src/gui/crt.zig", gui_scene[0].path); + try std.testing.expectEqualStrings("src/gui/gui.zig#frame-submission", gui_scene[1].path); + try std.testing.expectEqualStrings(scene_vertex_path, gui_scene[2].path); + try std.testing.expectEqualStrings(scene_fragment_path, gui_scene[3].path); + try std.testing.expect(std.mem.indexOf(u8, gui_scene[0].source, "pub fn mapScene") != null); + try std.testing.expect(std.mem.indexOf(u8, gui_scene[1].source, "SDL_PushGPUFragmentUniformData") != null); + try std.testing.expect(std.mem.indexOf(u8, gui_scene[2].source, "gl_VertexIndex") != null); + try std.testing.expect(std.mem.indexOf(u8, gui_scene[3].source, "u_crt.effects") != null); + } +} + +test "GUI panel EffectCode includes the actual host paint and submission path" { + if (comptime backend == .gui) { + try std.testing.expect(std.mem.indexOf(u8, gui_panel_host, "fn makePaintPlan") != null); + try std.testing.expect(std.mem.indexOf(u8, gui_panel_host, "fn setTransitionFields") != null); + try std.testing.expect(std.mem.indexOf(u8, gui_native_panel, "fn prepareNativeImages") != null); + try std.testing.expect(std.mem.indexOf(u8, gui_native_panel, "fn drawNativeImagesGpu") != null); + try std.testing.expect(std.mem.indexOf(u8, gui_native_panel, "active.visualBox()") != null); + try std.testing.expect(std.mem.indexOf(u8, gui_frame_submission, "for (paint_plan.batches") != null); + try std.testing.expect(std.mem.indexOf(u8, gui_cell_instance, "fn emitInstance") != null); + try std.testing.expect(std.mem.indexOf(u8, gui_cell_instance, "setTransitionFields") != null); + try std.testing.expect(std.mem.indexOf(u8, image_fragment, "vec3 contrastInk") != null); + } +} + +test "shader source provenance is GUI-only and follows the build input" { + if (comptime backend == .gui) { + const expected: GuiShaderSourceMode = if (build_config.gui_shader_sources_prebuilt) + .prebuilt_snapshot + else + .live; + try std.testing.expectEqual(expected, guiShaderSourceMode().?); + try std.testing.expect(expected.label().len > 0); + } else try std.testing.expect(guiShaderSourceMode() == null); +} + +test "mac EffectCode includes the runtime panel and scene wiring" { + if (comptime backend == .macos) { + try std.testing.expect(std.mem.indexOf(u8, mac_scene, "extern \"C\" float4 pardesPanelClear") != null); + try std.testing.expect(std.mem.indexOf(u8, mac_scene, "extern \"C\" float4 pardesPanel") != null); + try std.testing.expect(std.mem.indexOf(u8, mac_scene, "extern \"C\" float4 pardesScene") != null); + try std.testing.expect(std.mem.indexOf(u8, mac_scene, "float3 contrastInk") != null); + try std.testing.expect(std.mem.indexOf(u8, mac_view, "hideCursor: !tracks.isEmpty") != null); + try std.testing.expect(std.mem.indexOf(u8, mac_postprocessor, "panelClearKernel.apply") != null); + try std.testing.expect(std.mem.indexOf(u8, mac_postprocessor, "panelKernel.apply") != null); + try std.testing.expect(std.mem.indexOf(u8, mac_postprocessor, "sceneKernel.apply") != null); + } +} |
