summaryrefslogtreecommitdiff
path: root/src/gui/gui.zig
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-09-29 09:43:05 -0300
committerGabriel Schneider <[email protected]>2026-10-01 00:12:16 -0300
commit826ec1fac3c012e71ab34207328faaac5b5827b7 (patch)
treee0e6b1f5a38c963875aa91c5817c27245a291faa /src/gui/gui.zig
parent7f8b9d2e8666c2e2104abcd462d79cf6f949c718 (diff)
downloadpardes-826ec1fac3c012e71ab34207328faaac5b5827b7.tar.gz
pardes-826ec1fac3c012e71ab34207328faaac5b5827b7.zip
Shader files compile again on save; an attached GUI runs the session's post chain
shader_build.zig takes the Shadertoy compile out of the GUI's Post: the process that holds the core compiles each chain file behind the prefix on a thread, watches its directory (file_watch's new shader slots), and on a change reads and hashes each file, compiling only one whose bytes moved. A failed compile keeps the last good SPIR-V and is said once; the same bytes are never compiled or reported twice. A file is read with std, not fs.zig's readFile, whose turn hand-off a worker thread does not hold. A detached session compiles for its frontends and sends them the chain (wire post: each pass's scene and level or the file's SPIR-V, and ShaderAnimation) on attach and on every change, so an attached GUI runs the same passes, levels and animation as a local one while still reading no disk and running no program. The attached GUI describes its frames to the chain from the session's chrome and redraws an animating chain on its own.
Diffstat (limited to 'src/gui/gui.zig')
-rw-r--r--src/gui/gui.zig58
1 files changed, 41 insertions, 17 deletions
diff --git a/src/gui/gui.zig b/src/gui/gui.zig
index 127f9b5d..2d31416b 100644
--- a/src/gui/gui.zig
+++ b/src/gui/gui.zig
@@ -11,6 +11,7 @@ const config = @import("../config.zig");
const look = @import("../look.zig");
const message = pardes.Messages.Message;
const file_watch = @import("../file_watch.zig");
+const shader_build = @import("../shader_build.zig");
const deck = @import("deck.zig");
const Post = @import("Post.zig");
const pet = @import("pet.zig");
@@ -2693,6 +2694,7 @@ fn localSession(
.test_hz = if (std.c.getenv("PARDES_TEST_HZ")) |hz| @max(1, std.fmt.parseInt(u64, std.mem.span(hz), 10) catch 60) else 60,
};
defer shell.shutdownPtys();
+ defer shell.shaders.deinit(gpa, inotify_fd, &watches);
// A virtual clock never blinks: goldens and feel series hold still.
g.no_blink = shell.test_clock != null;
const host = shell.host();
@@ -2862,6 +2864,7 @@ fn attachedLoop(gpa: std.mem.Allocator, g: *Gui, client: *detached_client.Client
in.post(.{ .paste = text });
},
.open_link => |url| look.openLink(url),
+ .post => dirty = true,
}
}
try link;
@@ -2877,7 +2880,15 @@ fn attachedLoop(gpa: std.mem.Allocator, g: *Gui, client: *detached_client.Client
geom = now;
try attachedResize(client, g, now.cols, now.rows);
}
- if (!dirty) continue;
+ // The session's chain, its files compiled there.
+ g.post.sync(gpa, g.device, g.swapchain_format, client.post.list(), client.post_key, null);
+ if (!dirty) {
+ // As a local window does: a chain that moves on its own redraws
+ // alone, once a refresh.
+ if (g.post.animating(client.post.animation) and g.scene_tex != null and
+ c.SDL_GetTicksNS() -| g.post.last_ns + std.time.ns_per_ms >= refreshNs(g)) redrawPost(g, gpa);
+ continue;
+ }
dirty = false;
paintAttached(g, gpa, client);
}
@@ -3993,6 +4004,8 @@ const Shell = struct {
pipe_tasks: *PipeTasks,
inotify_fd: c_int,
watches: *file_watch.Table,
+ /// The post chain's files as SPIR-V.
+ shaders: shader_build = .{},
threads_ok: bool = false,
test_mode: bool = false,
feed: StdinFeed = .{},
@@ -4209,11 +4222,19 @@ const Shell = struct {
.files_changed => check_files = true,
.fs_ready => {},
};
+ if (check_files) s.shaders.recheck();
if (check_files and file_watch.reloadChanged(s.core, s.io, s.gpa, s.watches))
s.queue.push(.files_changed);
}
};
+/// A post chain compile is done (shader_build.zig): the loop's SDL wait ends.
+fn wakeSdl(_: ?*anyopaque) void {
+ var event = std.mem.zeroes(c.SDL_Event);
+ event.type = c.SDL_EVENT_USER;
+ _ = c.SDL_PushEvent(&event);
+}
+
fn wakeFs(ctx: ?*anyopaque) void {
const q: *Queue = @ptrCast(@alignCast(ctx.?));
q.push(.fs_ready);
@@ -4356,7 +4377,11 @@ fn pollFrame(ctx: ?*anyopaque) void {
const geom = windowCells(g);
if (updateCoreResize(core, geom.cols, geom.rows, g.cell_w, g.cell_h, g.tagline_width, g.tagline_height)) resetScroll(g);
stepScroll(g, core, s.gpa);
- g.post.sync(s.gpa, s.io, g.device, g.swapchain_format, core);
+ _ = s.shaders.sync(s.gpa, s.io, core, s.inotify_fd, s.watches, .{ .call = wakeSdl });
+ {
+ var passes: [Post.max]shader_build.Pass = undefined;
+ g.post.sync(s.gpa, g.device, g.swapchain_format, s.shaders.view(&core.settings.post, &passes), s.shaders.revision, core);
+ }
if (s.test_mode) writeTestStatus(g, core, s.presented);
// Between the core's frames, a chain that moves on its own redraws
// alone, once a refresh of the display the window is on (a millisecond
@@ -5805,8 +5830,9 @@ fn renderFrame(
const win_w: f32 = @floatFromInt(sw);
const win_h: f32 = @floatFromInt(sh);
- // The post chain runs over a local frame, from the scene texture.
- var scene_on = core != null and g.post.ready();
+ // The post chain runs from the scene texture, over a local frame or an
+ // attached session's alike.
+ var scene_on = g.post.ready();
if (scene_on) ensureSceneTexture(g, sw, sh) catch {
g.scene_target_failed = true;
scene_on = false;
@@ -6179,19 +6205,17 @@ fn renderFrame(
c.SDL_EndGPURenderPass(rp);
if (scene_on) {
- if (core) |p| {
- var boxes: [max_cursors]CursorBox = undefined;
- const cursors = if (cursors_shown) cursorBoxes(g, surface, layout, win_h, &boxes) else 0;
- var spare: [Post.max_spare][4]f32 = undefined;
- g.post.describe(.{
- .cursor = if (cursors > 0) boxes[0] else null,
- .spare = spare[0..postSpare(surface, layout, &spare)],
- .background = chrome.page orelse bg_default,
- .foreground = chrome.fg orelse fg_default,
- .selection_foreground = p.theme().sel_fg,
- .selection_background = p.theme().sel_bg,
- });
- }
+ var boxes: [max_cursors]CursorBox = undefined;
+ const cursors = if (cursors_shown) cursorBoxes(g, surface, layout, win_h, &boxes) else 0;
+ var spare: [Post.max_spare][4]f32 = undefined;
+ g.post.describe(.{
+ .cursor = if (cursors > 0) boxes[0] else null,
+ .spare = spare[0..postSpare(surface, layout, &spare)],
+ .background = chrome.page orelse bg_default,
+ .foreground = chrome.fg orelse fg_default,
+ .selection_foreground = if (core) |p| p.theme().sel_fg else chrome.sel_fg,
+ .selection_background = if (core) |p| p.theme().sel_bg else chrome.sel_bg,
+ });
const chain_zone = tracy.zone(@src(), "post chain");
defer chain_zone.end();
g.post.scale = display_scale;