diff options
Diffstat (limited to 'src/effect_sources.zig')
| -rw-r--r-- | src/effect_sources.zig | 273 |
1 files changed, 62 insertions, 211 deletions
diff --git a/src/effect_sources.zig b/src/effect_sources.zig index d4f0e38b..3ca15c3f 100644 --- a/src/effect_sources.zig +++ b/src/effect_sources.zig @@ -1,100 +1,18 @@ -//! 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 pardes_core = @embedFile("pardes.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 config = @import("config.zig"); +const filesystem = @import("fs.zig"); -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 ascii_diff = sourceSection( - pardes_core, - "// EFFECT_CODE_ASCII_DIFF_BEGIN\n", - "// EFFECT_CODE_ASCII_DIFF_END", -); -const ascii_compositor = sourceSection( - pardes_core, - " // EFFECT_CODE_ASCII_COMPOSITOR_BEGIN\n", - " // EFFECT_CODE_ASCII_COMPOSITOR_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" +const shader_dir = if (build_config.gui_shader_sources_prebuilt) + "shaders/prebuilt/" 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"; + "shaders/"; +const ui_vertex_path = shader_dir ++ "ui.vert.glsl"; +const ui_fragment_path = shader_dir ++ "ui.frag.glsl"; +const image_vertex_path = shader_dir ++ "image.vert.glsl"; +const image_fragment_path = shader_dir ++ "image.frag.glsl"; +const scene_vertex_path = shader_dir ++ "crt.vert.glsl"; +const scene_fragment_path = shader_dir ++ "crt.frag.glsl"; pub const Backend = @TypeOf(build_config.platform); pub const backend: Backend = build_config.platform; @@ -121,120 +39,72 @@ pub fn guiShaderSourceMode() ?GuiShaderSourceMode { .live; } -pub const Segment = struct { - path: []const u8, - source: []const u8, +pub const files = switch (backend) { + .tty => [_]filesystem.Source{ + .{ .path = "src/tty/panel_compositor.zig", .contents = @embedFile("tty/panel_compositor.zig") }, + }, + .gui => [_]filesystem.Source{ + .{ .path = "src/gui/gui.zig", .contents = @embedFile("gui/gui.zig") }, + .{ .path = "src/gui/crt.zig", .contents = @embedFile("gui/crt.zig") }, + .{ .path = ui_vertex_path, .contents = @embedFile("effect-source-ui.vert.glsl") }, + .{ .path = ui_fragment_path, .contents = @embedFile("effect-source-ui.frag.glsl") }, + .{ .path = image_vertex_path, .contents = @embedFile("effect-source-image.vert.glsl") }, + .{ .path = image_fragment_path, .contents = @embedFile("effect-source-image.frag.glsl") }, + .{ .path = scene_vertex_path, .contents = @embedFile("effect-source-crt.vert.glsl") }, + .{ .path = scene_fragment_path, .contents = @embedFile("effect-source-crt.frag.glsl") }, + }, + .macos => [_]filesystem.Source{ + .{ .path = "src/macos/Sources/PardesView.swift", .contents = @embedFile("macos/Sources/PardesView.swift") }, + .{ .path = "src/macos/Sources/ScenePostprocessor.swift", .contents = @embedFile("macos/Sources/ScenePostprocessor.swift") }, + .{ .path = "shaders/crt.ci.metal", .contents = @embedFile("effect-source-crt.ci.metal") }, + }, + .web, .esp32p4 => [_]filesystem.Source{}, }; -const tty_panel = [_]Segment{ - .{ .path = "src/panel_animation.zig", .source = panel_math }, - .{ .path = "src/pardes.zig#ascii-diff", .source = ascii_diff }, - .{ .path = "src/pardes.zig#ascii-compositor", .source = ascii_compositor }, - .{ .path = "src/tty/panel_compositor.zig", .source = tty_compositor }, -}; -const gui_panel = [_]Segment{ - .{ .path = "src/panel_animation.zig", .source = panel_math }, - .{ .path = "src/pardes.zig#ascii-diff", .source = ascii_diff }, - .{ .path = "src/pardes.zig#ascii-compositor", .source = ascii_compositor }, - .{ .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/pardes.zig#ascii-diff", .source = ascii_diff }, - .{ .path = "src/pardes.zig#ascii-compositor", .source = ascii_compositor }, - .{ .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 { +pub fn forSetting(setting: config.Runtime.Setting) ?[]const []const u8 { return switch (setting.action) { .transition => switch (backend) { - .tty => &tty_panel, - .gui => &gui_panel, - .macos => &mac_panel, + .tty => &.{ "src/layout.zig", "src/pardes.zig", "src/tty/panel_compositor.zig" }, + .gui => &.{ "src/layout.zig", "src/pardes.zig", "src/gui/gui.zig", ui_vertex_path, ui_fragment_path, image_vertex_path, image_fragment_path }, + .macos => &.{ "src/layout.zig", "src/pardes.zig", "src/macos/Sources/PardesView.swift", "src/macos/Sources/ScenePostprocessor.swift", "shaders/crt.ci.metal" }, .web, .esp32p4 => null, }, .scene => switch (backend) { - .gui => &gui_scene, - .macos => &mac_scene_segments, + .gui => &.{ "src/gui/crt.zig", "src/gui/gui.zig", scene_vertex_path, scene_fragment_path }, + .macos => &.{ "src/macos/Sources/PardesView.swift", "src/macos/Sources/ScenePostprocessor.swift", "shaders/crt.ci.metal" }, .tty, .web, .esp32p4 => 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 "EffectCode paths resolve uniquely to this backend's archived inputs" { + for (config.Runtime.settings) |setting| { + const available = switch (setting.action) { + .transition => backend == .tty or backend == .gui or backend == .macos, + .scene => backend == .gui or backend == .macos, + else => false, + }; + const paths = forSetting(setting); + try std.testing.expectEqual(available, paths != null); + if (paths) |listed| for (listed, 0..) |path, i| { + const bytes = filesystem.sourceBytes(path) orelse return error.MissingEffectSource; + try std.testing.expect(bytes.len > 0); + for (listed[0..i]) |previous| try std.testing.expect(!std.mem.eql(u8, path, previous)); + }; } -} - -test "GUI panel EffectCode includes the actual host paint and submission path" { - if (comptime backend == .gui) { - try std.testing.expect(std.mem.indexOf(u8, ascii_diff, "pub const PanelCellDiff") != null); - try std.testing.expect(std.mem.indexOf(u8, ascii_compositor, "fn composeAsciiTransitions") != null); - 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, panel_math, "frame_count") != null); - try std.testing.expect(std.mem.indexOf(u8, gui_frame_submission, "active.effect == .dissolve") != null); + for (files) |file| { + const archived = filesystem.sourceBytes(file.path) orelse return error.MissingEffectSource; + try std.testing.expectEqualStrings(file.contents, archived); + var matches: usize = 0; + for (filesystem.sources) |source| if (std.mem.eql(u8, source.path, file.path)) { + matches += 1; + }; + try std.testing.expectEqual(@as(usize, 1), matches); } + try std.testing.expectEqual(backend == .tty, filesystem.sourceBytes("src/tty/panel_compositor.zig") != null); + try std.testing.expectEqual(backend == .gui, filesystem.sourceBytes("src/gui/gui.zig") != null); + try std.testing.expectEqual(backend == .macos, filesystem.sourceBytes("src/macos/Sources/PardesView.swift") != null); } test "shader source provenance is GUI-only and follows the build input" { @@ -247,22 +117,3 @@ test "shader source provenance is GUI-only and follows the build input" { 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) { - // `extern "C" [[stitchable]]` is what the runtime compile requires: - // CIKernel.kernels(withMetalString:) looks for stitchable functions and - // rejects the whole source without them, so ScenePostprocessor.init? - // returns nil and every scene effect and panel transition silently - // degrades to the plain CoreText draw. The spelling is pinned on - // purpose; relaxing these three matches is how that comes back. - try std.testing.expect(std.mem.indexOf(u8, mac_scene, "extern \"C\" [[stitchable]] float4 pardesPanelClear") != null); - try std.testing.expect(std.mem.indexOf(u8, mac_scene, "extern \"C\" [[stitchable]] float4 pardesPanel") != null); - try std.testing.expect(std.mem.indexOf(u8, mac_scene, "extern \"C\" [[stitchable]] float4 pardesScene") != null); - try std.testing.expect(std.mem.indexOf(u8, mac_scene, "already composed in Pardes core") != 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); - } -} |
