summaryrefslogtreecommitdiff
path: root/src/effect_sources.zig
blob: 6ce11160e99986fff03f18dbfb5de20a1bab069b (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
const std = @import("std");
const build_config = @import("pardes_config");
const config = @import("config.zig");
const filesystem = @import("fs.zig");

const shader_dir = if (build_config.gui_shader_sources_prebuilt)
    "shaders/prebuilt/"
else
    "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 post_vertex_path = shader_dir ++ "post.vert.glsl";
// The bundled post passes as compiled: the prefix and the pass together.
const post_crt_path = shader_dir ++ "post-crt.frag.glsl";
const post_ripple_path = shader_dir ++ "post-ripple.frag.glsl";
const post_glitch_path = shader_dir ++ "post-glitch.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 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/Post.zig", .contents = @embedFile("gui/Post.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 = post_vertex_path, .contents = @embedFile("effect-source-post.vert.glsl") },
        .{ .path = post_crt_path, .contents = @embedFile("effect-source-post-crt.frag.glsl") },
        .{ .path = post_ripple_path, .contents = @embedFile("effect-source-post-ripple.frag.glsl") },
        .{ .path = post_glitch_path, .contents = @embedFile("effect-source-post-glitch.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{},
};

pub fn forSetting(setting: config.Runtime.Setting) ?[]const []const u8 {
    return switch (setting.action) {
        .transition => switch (backend) {
            .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 => |effect| switch (backend) {
            .gui => switch (effect) {
                .crt => &.{ "src/gui/Post.zig", "src/gui/gui.zig", post_vertex_path, post_crt_path },
                .ripple => &.{ "src/gui/Post.zig", "src/gui/gui.zig", post_vertex_path, post_ripple_path },
                .glitch => &.{ "src/gui/Post.zig", "src/gui/gui.zig", post_vertex_path, post_glitch_path },
            },
            .macos => &.{ "src/macos/Sources/PardesView.swift", "src/macos/Sources/ScenePostprocessor.swift", "shaders/crt.ci.metal" },
            .tty, .web, .esp32p4 => null,
        },
        else => null,
    };
}

test "EffectCode paths resolve uniquely to this backend's archived inputs" {
    if (!@import("memory.zig").limits.embedded_sources) return error.SkipZigTest;
    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));
        };
    }
    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" {
    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);
}