render: shader contract v2 (chained stages, history, params, event animation) #239
No reviewers
Labels
No labels
Area/ci
Area/cli
Area/client
Area/client-core
Area/daemon
Area/deps
Area/docs
Area/grid
Area/manual
Area/nix
Area/protocol
Area/pty
Area/render
Area/shaping
Area/skills
Area/transport
Area/vt
Compat/Breaking
Kind/Bug
Kind/Documentation
Kind/Enhancement
Kind/Feature
Kind/Performance
Kind/Security
Kind/Testing
perf-regression-accepted
Reviewed
Confirmed
Reviewed
Duplicate
Reviewed
Invalid
Reviewed
Won't Fix
Status
Blocked
Status
Need More Info
Status
Upstream
No milestone
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
felis-terminal/felis!239
Loading…
Reference in a new issue
No description provided.
Delete branch "render/shader-contract-ext"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Extends the WGSL post-process shader contract to v2. Chained stages, a previous-frame texture, params and event-triggered animation now exist in kitty 0.49 and ghostty, and felis had none of them. The idle-zero invariant is unchanged: no contract field can request frames, and an animation's end is a time the host knows.
What changes
shader.posttakes the existing single table or an array of up to 16 stages. Each stage samples the previous stage's output, and a stage that fails to load is dropped while the rest still run.@group(0) @binding(3)gets its own previous frame as input. The texture is allocated only for a stage that declares it. A final stage with history renders offscreen and is copied to the swapchain by one extra blit, so the surface staysRENDER_ATTACHMENT-only.paramsset WGSLoverrideconstants, so the shader source is never rewritten. Each value is type-checked against the declared override,@idkeys are mapped from identifiers, and naga'sprocess_overridesruns before pipeline creation.animation = { on = [...], duration_ms }. The events arebell,focus_in,focus_out,left_press, anduser:<name>from a newshader_eventkeymap action.duration_msis required and must be in 1..=10000; values outside that range are rejected, not clamped. The client redraws at 60 Hz until the run ends, then stops. Easing is left to the shader, which getsanimation_progressandanimation_start_s.palette, with a 4352-byte struct:time_whole_s/time_fract_s(exact over long-lived windows, where the f32time_sdrifts),focus_change_time_s,stage_index, the animation fields, and an explicit reserved tail. All time fields come from one renderer-lifetime clock that survives stage rebuilds.fs_postinterface,enable f16;) drops that stage instead of panicking. The binding check also fixes a v1 gap where a mistyped binding panicked at pipeline creation.The docs cover
shaders.md,config.md,keybindings.md,pipeline.md(the "Revisit if … multi-pass composition, or a persistent texture" line is now a recorded decision),spec.md(REQ-701/713/714 updated, new REQ-716), and CHANGELOG.Not in scope
GLSL/Slang import, kitty's
pointer_map, semantic masks, IPC-triggered shader events, host-side easing curves, and vector params. Source compatibility with ghostty/kitty shaders remains a non-goal.Verification
just checkandjust check-windowspass on every commit (clippy and the Windows cross-lint per commit, the full suite on the tip).clear, the previous screen and the cursor trail remain as a decaying ghost.focus_in/focus_out/left_press/shader_eventtriggers (they share the bell's clock path, which unit tests cover).