summaryrefslogtreecommitdiff
path: root/src/gui/Post.zig
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-09-29 13:19:08 -0300
committerGabriel Schneider <[email protected]>2026-10-01 00:12:16 -0300
commit4e224b7c7e67fafcbe7359fcf534048e254b701d (patch)
tree33539e34e8cee525792767facebf36356bc28b08 /src/gui/Post.zig
parent7ec97214a7a6ee905167b7f94a800a8c1d70b100 (diff)
downloadpardes-4e224b7c7e67fafcbe7359fcf534048e254b701d.tar.gz
pardes-4e224b7c7e67fafcbe7359fcf534048e254b701d.zip
The theme's chrome is worked out once, not per grapheme; still post passes let the GUI rest; a perf gate
somtrmsz's contrast floors made ChromeTheme.fromTheme run its focus-tint and separator searches (pow calls each), and Output's RowDecoration.styleAt called it for every grapheme of every highlighted row: the 50k-line file's render went from 0.11 ms to 13 ms. Pardes.bodyChrome now keeps the theme's chrome, worked out again only when the theme differs; recolorSyntax asks for it once a decorated row, and a plain row (nearly every row of a file) never asks. ReleaseFast, medium fixture, median us, before -> after (main): render 13150 -> 76 (111), key-down 14658 -> 79 (110), wheel 13589 -> 76 (109), open 14891 -> 2095 (2055), edit-char 16703 -> 3006 (1251; the rest of that gap is editing and tree-sitter, not this). Post.animating asked for frames whenever the window had focus and any pass was ready, so a still pass kept the GUI drawing at the display's rate (Bloom: 49% of a core idle, in a hidden test window). A pass now says whether it moves on its own: the CRT (its hum and dither) and a Shadertoy file whose source reads iTime, iFrame or iDate (shader_build.readsTime; the flag rides the wire's post message); Bloom, Vignette and Grain are still. Bloom idle: 49% -> 1.2%, as with no pass. zig build perf-gate: the 50k-line file's gestures, each's fastest sample within 3x of the recorded baseline's (test/perf-baseline-<platform>- <optimize>.json, recorded from this build), run with every unit-test. On the regressed code it fails at 180x for render.
Diffstat (limited to 'src/gui/Post.zig')
-rw-r--r--src/gui/Post.zig27
1 files changed, 25 insertions, 2 deletions
diff --git a/src/gui/Post.zig b/src/gui/Post.zig
index 06310b7f..602b2790 100644
--- a/src/gui/Post.zig
+++ b/src/gui/Post.zig
@@ -94,6 +94,8 @@ pub const Pass = struct {
pipeline: ?*Pipeline = null,
/// The SPIR-V revision `pipeline` was made from (shader_build.Pass).
revision: u32 = 0,
+ /// It moves on its own (shader_build.Pass).
+ animated: bool = false,
};
passes: [max]Pass = @splat(.{}),
@@ -140,7 +142,7 @@ pub fn sync(post: *Post, gpa: std.mem.Allocator, device: *c.SDL_GPUDevice, forma
post.bloom_down = makePipeline(device, bloom_format, bloom_down_spv, 1, 1) catch null;
post.bloom_up = makePipeline(device, bloom_format, bloom_up_spv, 1, 1) catch null;
}
- next[i] = .{ .scene = scene, .level = entry.level, .pipeline = post.bundled.get(scene) };
+ next[i] = .{ .scene = scene, .level = entry.level, .pipeline = post.bundled.get(scene), .animated = entry.animated };
continue;
}
// A file already in the chain keeps its pipeline and its path.
@@ -151,6 +153,7 @@ pub fn sync(post: *Post, gpa: std.mem.Allocator, device: *c.SDL_GPUDevice, forma
break;
} else next[i] = .{ .path = gpa.dupe(u8, entry.path) catch &.{} };
const pass = &next[i];
+ pass.animated = entry.animated;
if (pass.revision == entry.revision or entry.spirv.len == 0) continue;
pass.revision = entry.revision;
const made = makePipeline(device, format, entry.spirv, 1, 4) catch {
@@ -200,8 +203,14 @@ pub fn ready(post: *const Post) bool {
}
/// The chain redraws on its own (level A) as ShaderAnimation says.
+/// Only a pass that moves on its own (shader_build.Pass.animated) asks for
+/// frames while idle: a still one (Bloom, Vignette, Grain, a file that reads
+/// no clock) is drawn again only with a frame of the core's.
pub fn animating(post: *const Post, mode: pardes.config.Runtime.ShaderAnimation) bool {
- return post.ready() and switch (mode) {
+ const moving = for (post.passes[0..post.len]) |pass| {
+ if (pass.pipeline != null and pass.animated) break true;
+ } else false;
+ return moving and switch (mode) {
.off => false,
.on => post.uniforms.focus != 0,
.always => true,
@@ -664,3 +673,17 @@ test "a moved cursor becomes the previous one, stamped with the time of the move
try std.testing.expectEqual([4]f32{ 1.0 / 255.0, 2.0 / 255.0, 3.0 / 255.0, 1 }, post.uniforms.selection_foreground_color);
}
+
+test "a still pass asks for no idle frames; one that reads the clock does" {
+ var post: Post = .{};
+ const fake: *Pipeline = @ptrFromInt(0x1000);
+ post.passes[0] = .{ .scene = .bloom, .level = 2, .pipeline = fake };
+ post.len = 1;
+ try std.testing.expect(!post.animating(.on) and !post.animating(.always));
+ post.passes[1] = .{ .scene = .crt, .level = 2, .pipeline = fake, .animated = true };
+ post.len = 2;
+ try std.testing.expect(post.animating(.on) and post.animating(.always) and !post.animating(.off));
+ // Unfocused, `on` rests.
+ post.uniforms.focus = 0;
+ try std.testing.expect(!post.animating(.on) and post.animating(.always));
+}