summaryrefslogtreecommitdiff
path: root/src/effect_sources.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/effect_sources.zig')
-rw-r--r--src/effect_sources.zig242
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);
+ }
+}