Pass varying data in KittyTextures rather than as function paramters

The function signatures will be set in stone post release so pass all
data via structs so that future additions to the structs will t break
third party shaders
This commit is contained in:
Kovid Goyal 2026-08-10 12:25:06 +05:30
parent 4a7b87a4f5
commit de36058da8
No known key found for this signature in database
GPG key ID: 06BC317B515ACE7C
20 changed files with 43 additions and 78 deletions

View file

@ -48,9 +48,7 @@ float lum(float4 c) {
public float4 fragment_main(
float4 color,
KittyTextures t,
KittyCustomShaderData d,
float4 viewport,
float animation_progress
KittyCustomShaderData d
) {
float2 uv = t.pos;

View file

@ -16,9 +16,7 @@ static const float4 TINT = float4(0, 0.8, 0.6, 0); // tint color set the fourth
public float4 fragment_main(
float4 color,
KittyTextures t,
KittyCustomShaderData d,
float4 viewport,
float animation_progress
KittyCustomShaderData d
) {
// UV within the current viewport (0..1)
float2 uv = t.pos;
@ -32,13 +30,13 @@ public float4 fragment_main(
uv.y -= 0.5f; uv.y *= 1.0f + (dc.x * (0.4f * WARP)); uv.y += 0.5f;
// Original Y pixel coordinate within the viewport (for scanline phase)
float fragCoordY = (t.pos.y - viewport.y) * float(d.viewport_size_pixels.y);
float fragCoordY = (t.pos.y - t.viewport.y) * float(d.viewport_size_pixels.y);
// Scanline darkness at this pixel row
float apply = abs(sin(fragCoordY) * 0.25f * SCAN);
// Sample the backbuffer at the warped viewport UV
float2 sample_uv = viewport.xy + uv * viewport.zw;
float2 sample_uv = t.viewport.xy + uv * t.viewport.zw;
float3 col = t.backbuffer.Sample(sample_uv).rgb;
// apply the tint
col = lerp(col, col * TINT.rgb, TINT.a);

View file

@ -72,9 +72,7 @@ static float movementVertexFactor(float2 a, float2 b) {
public float4 fragment_main(
float4 color,
KittyTextures t,
KittyCustomShaderData d,
float4 viewport,
float animation_progress
KittyCustomShaderData d
) {
if (d.cursor_trail_color.a <= 0.0) return color;
@ -85,7 +83,7 @@ public float4 fragment_main(
if (easedProgress <= 0.0) return color;
// Work in pixel space for correct aspect-ratio distance measurements.
float2 pixelDims = float2(d.viewport_size_pixels) * viewport.zw;
float2 pixelDims = float2(d.viewport_size_pixels) * t.viewport.zw;
float2 px = t.pos * pixelDims;
// Current cursor rect in pixel space (edge: .x=left .y=right .z=top .w=bottom, y-up).

View file

@ -32,9 +32,7 @@ bool inside_trail_quad(float2 uv, float2 c0, float2 c1, float2 c2, float2 c3) {
public float4 fragment_main(
float4 color,
KittyTextures t,
KittyCustomShaderData d,
float4 viewport,
float animation_progress
KittyCustomShaderData d
) {
float opacity = d.cursor_trail_color.a;
if (opacity <= 0.0) return color;

View file

@ -87,14 +87,12 @@ static float forkDist(float2 p, float2 a, float2 b, float seed) {
public float4 fragment_main(
float4 color,
KittyTextures t,
KittyCustomShaderData d,
float4 viewport,
float animation_progress
KittyCustomShaderData d
) {
float age = clamp((d.timestamp - d.cursor_trail_change_time) / DURATION, 0.0, 1.0);
if (age >= 1.0) return color;
float2 pixelDims = float2(d.viewport_size_pixels) * viewport.zw;
float2 pixelDims = float2(d.viewport_size_pixels) * t.viewport.zw;
// Cursor centres in pixel space (edge: .x=left .y=right .z=top .w=bottom, y-up).
float2 curCenter = float2(

View file

@ -100,11 +100,9 @@ float3 drawFireworks(float time, float2 uv, float3 particolor, float seed) {
public float4 fragment_main(
float4 color,
KittyTextures t,
KittyCustomShaderData d,
float4 viewport,
float animation_progress
KittyCustomShaderData d
) {
float2 pixelDims = float2(d.viewport_size_pixels) * viewport.zw;
float2 pixelDims = float2(d.viewport_size_pixels) * t.viewport.zw;
float aspect = pixelDims.x / pixelDims.y;
// Map to [-aspect, aspect] × [-1, 1] with y=1 at top (matching the original)

View file

@ -78,11 +78,9 @@ float3 rainbow(float3 c, float time) {
public float4 fragment_main(
float4 color,
KittyTextures t,
KittyCustomShaderData d,
float4 viewport,
float animation_progress
KittyCustomShaderData d
) {
float2 pixelDims = float2(d.viewport_size_pixels) * viewport.zw;
float2 pixelDims = float2(d.viewport_size_pixels) * t.viewport.zw;
float aspect = pixelDims.x / pixelDims.y;
float time = fmod(d.timestamp, 3600.0f) * TIME_SCALE;

View file

@ -22,9 +22,7 @@ static const float INACTIVE_DIM = 0.3;
public float4 fragment_main(
float4 color,
KittyTextures t,
KittyCustomShaderData d,
float4 viewport,
float animation_progress
KittyCustomShaderData d
) {
float4 g = d.active_window_geometry;
// Pass through unchanged if no active window geometry is known.
@ -35,7 +33,7 @@ public float4 fragment_main(
float2 winMax = g.xy + g.zw;
// Effect strength decays from 1 at animation start to 0 at the end.
float strength = 1.0 - animation_progress;
float strength = 1.0 - t.animation_progress;
bool inWindow = all(uv >= winMin) && all(uv <= winMax);

View file

@ -237,9 +237,7 @@ float smoothstep1(float x) { return smoothstep(0.0f, 1.0f, x); }
public float4 fragment_main(
float4 color,
KittyTextures t,
KittyCustomShaderData d,
float4 viewport,
float animation_progress
KittyCustomShaderData d
) {
float2 uv = t.pos;
float time = fmod(d.timestamp, 300.0f) * SPEED;

View file

@ -9,9 +9,7 @@ import kitty_custom_shader_types;
public float4 fragment_main(
float4 color,
KittyTextures t,
KittyCustomShaderData d,
float4 viewport,
float animation_progress
KittyCustomShaderData d
) {
return float4(1.0 - color.r, 1.0 - color.g, 1.0 - color.b, color.a);
}

View file

@ -64,7 +64,7 @@ uniform Sampler2D backbuffer, a, b, persist;
public float4 pipeline_fragment_main(float2 backbuffer_pos, int group, float4 viewport, float animation_progress, bool convert_to_srgb) {
float4 color = backbuffer.Sample(backbuffer_pos); // pre multiplied linear RGB
color = float4(color.rgb / color.a, color.a); // un pre multiplied color passed through pipeline
KittyTextures t = {backbuffer, a, b, persist, backbuffer_pos};
KittyTextures t = {backbuffer, a, b, persist, backbuffer_pos, viewport, animation_progress};
// PIPELINE
return vec4_premul(color.rgb, color.a);
}

View file

@ -28,11 +28,9 @@ static const float DIFFUSE_STRENGTH = 0.10;
public float4 fragment_main(
float4 color,
KittyTextures t,
KittyCustomShaderData d,
float4 viewport,
float animation_progress
KittyCustomShaderData d
) {
float2 pixelDims = float2(d.viewport_size_pixels) * viewport.zw;
float2 pixelDims = float2(d.viewport_size_pixels) * t.viewport.zw;
float aspect = pixelDims.x / pixelDims.y;
// d.mouse_pos.zw is the UV position of the last left-button press (y=0 at bottom).
@ -44,20 +42,20 @@ public float4 fragment_main(
float2 dir = delta / max(dist, 1e-5);
// Wavefront radius advances linearly with animation_progress.
float front = WAVE_SPEED * animation_progress;
float front = WAVE_SPEED * t.animation_progress;
// Signed distance from the wavefront (negative = wave has passed, positive = not yet reached).
float behind = dist - front;
// Gaussian spatial envelope centered at the wavefront; grows in width over time.
float sigma = WAVE_WIDTH * (1.0 + animation_progress);
float sigma = WAVE_WIDTH * (1.0 + t.animation_progress);
float env = exp(-behind * behind / (2.0 * sigma * sigma));
// Causality: suppress any energy ahead of the wavefront.
env *= 1.0 - smoothstep(-0.01, 0.02, behind);
// Temporal amplitude decay as the wave expands outward.
float decay = pow(1.0 - animation_progress, 0.7);
float decay = pow(1.0 - t.animation_progress, 0.7);
float k = WAVE_FREQUENCY * 6.28318530718;
float wave = sin(behind * k);

View file

@ -13,18 +13,7 @@ public float4 fragment_main(
KittyTextures t,
// See types.slang for details on the data in this structure
KittyCustomShaderData d,
// The viewport used for this group. In the form (x, y, width, height)
// where (x, y) is the bottom-left corner of the viewport, with (0, 0) at
// the bottom-left of the screen and y increasing upwards (standard UV
// coordinates). All numbers are unit floats. If no viewport is defined for
// the group defaults to (0, 0, 1, 1) that is, the full backbuffer.
float4 viewport,
// The value after applying the animation easing curve to time since
// animation start.
float animation_progress
KittyCustomShaderData d
)
{ // END_FUNCTION_SIGNATURE
return float4(color.r, min(max(0.1, color.g) * 2, 1), color.b, color.a);

View file

@ -24,15 +24,13 @@ static const int HIDE_WHEN_POINTER_HIDDEN = 1;
public float4 fragment_main(
float4 color,
KittyTextures t,
KittyCustomShaderData d,
float4 viewport,
float animation_progress
KittyCustomShaderData d
) {
if (HIDE_WHEN_POINTER_HIDDEN == 1 && d.mouse_pointer_hidden != 0.0) return color;
float2 uv = t.pos;
// Viewport pixel dimensions for aspect-ratio correction
float2 pixelDims = float2(d.viewport_size_pixels) * viewport.zw;
float2 pixelDims = float2(d.viewport_size_pixels) * t.viewport.zw;
float aspectRatio = pixelDims.x / pixelDims.y;
// Random oscillating center (different frequencies and phases on each axis)

View file

@ -54,14 +54,12 @@ float wipe_pixel_pos(float2 uv, float4 ca) {
public float4 fragment_main(
float4 color,
KittyTextures t,
KittyCustomShaderData d,
float4 viewport,
float animation_progress
KittyCustomShaderData d
) {
float4 ca = d.central_area;
if (ca.z <= 0.0 || ca.w <= 0.0) return color;
float p = animation_progress;
float p = t.animation_progress;
float brightness;
if (ANIMATION_TYPE == 0) {

View file

@ -21,9 +21,7 @@ void scanline(inout float3 color, float2 pixel_pos) {
public float4 fragment_main(
float4 color,
KittyTextures t,
KittyCustomShaderData d,
float4 viewport,
float animation_progress
KittyCustomShaderData d
) {
float2 pixel_pos = t.pos * float2(d.viewport_size_pixels);
float3 c = color.rgb;

View file

@ -52,8 +52,8 @@ public struct KittyCustomShaderData {
// the size in pixels of the full underlying viewport we are rendering too,
// before applying any viewport transforms from the current group.
// You can use the viewport vector passed to fragment_main to derive the
// actual size and position of the viewport currently being rendered too.
// You can use t.viewport to derive the actual size and position of the
// viewport currently being rendered too.
public uint2 viewport_size_pixels;
public float mouse_pointer_hidden; // 1.0 if the mouse pointer is currently hidden, 0.0 if visible
@ -80,5 +80,13 @@ public struct KittyTextures {
public Sampler2D persist;
public float2 pos; // the position of this pixel in the textures (UV coordinates)
// The viewport for this group in the form (x, y, width, height) where (x, y) is the
// bottom-left corner, with (0, 0) at the bottom-left of the screen and y increasing upwards
// (UV coordinates). Defaults to (0, 0, 1, 1) when no viewport is defined for the group.
public float4 viewport;
// The value after applying the animation easing curve to time since animation start.
// Zero when no animation is active.
public float animation_progress;
}
// END_TYPES_DEFINITION

View file

@ -65,11 +65,9 @@ float rayStrength(
public float4 fragment_main(
float4 color,
KittyTextures t,
KittyCustomShaderData d,
float4 viewport,
float animation_progress
KittyCustomShaderData d
) {
float2 iResolution = float2(d.viewport_size_pixels) * viewport.zw;
float2 iResolution = float2(d.viewport_size_pixels) * t.viewport.zw;
float iTime = d.timestamp;
// Convert kitty UV (y=0 at bottom) to a top-left-origin pixel coordinate,

View file

@ -15,9 +15,7 @@ static const float UV_DISPLACEMENT = 0.04; // UV warp magnitu
public float4 fragment_main(
float4 color,
KittyTextures t,
KittyCustomShaderData d,
float4 viewport,
float animation_progress
KittyCustomShaderData d
) {
float wtime = d.timestamp * 0.5 + 23.0;
float2 uv = t.pos;

View file

@ -1410,7 +1410,7 @@ def build_custom_shader_pipeline_ir(pipeline: Pipeline, cache_dir: str, invocati
# inactive animated groups do not cause an extra blit draw call.
pipeline_parts: list[str] = []
for g_idx, group in enumerate(pipeline['groups']):
calls = '\n'.join(f' color = {entry_point(g_idx, j)}(color, t, csd, viewport, animation_progress);' for j in range(len(group['shaders'])))
calls = '\n'.join(f' color = {entry_point(g_idx, j)}(color, t, csd);' for j in range(len(group['shaders'])))
calls += '\n if (convert_to_srgb) color = float4(linear2srgb(color.rgb), color.a);'
prefix = '} else ' if g_idx else ''
pipeline_parts.append(f' {prefix}if (group == {g_idx}) {{\n{calls}')