repose-render-wgpu 0.31.17

Repose wgpu handler
Documentation
struct Globals {
    ndc_to_px: vec2f,
    _pad: vec2f,
};
@group(0) @binding(0) var<uniform> G: Globals;

// Field order and member widths mirror `MeshUniform` in src/lib.rs; every
// member is a `vec4` because the uniform address space forces 16-byte
// alignment. `paint.z` is the live stop count (0..=8).
struct MeshUniform {
    m0: vec4f,
    m1: vec4f,
    paint: vec4u,
    color0: vec4f,
    color1: vec4f,
    grad_start: vec4f,
    grad_end: vec4f,
    stop_colors: array<vec4f, 8>,
    stop_offsets: array<vec4f, 2>,
};
@group(1) @binding(0) var<uniform> U: MeshUniform;

struct VSOut {
    @builtin(position) pos: vec4f,
    @location(0) @interpolate(flat) paint_type: u32,
    @location(1) @interpolate(flat) paint_kind: u32,
    @location(2) color: vec4f,
    @location(3) local: vec2f,
    @location(4) color0: vec4f,
    @location(5) color1: vec4f,
    @location(6) grad: vec4f,
};

@vertex
fn vs_main(
    @location(0) pos: vec2f,
    @location(1) color: vec4f,
    @location(2) uv: vec2f,
) -> VSOut {
    // world_px = affine(local), rows in U.m0 / U.m1.
    let world = vec2f(
        U.m0.x * pos.x + U.m0.y * pos.y + U.m0.z,
        U.m1.x * pos.x + U.m1.y * pos.y + U.m1.z,
    );
    var out: VSOut;
    out.pos = vec4f(
        world.x / G.ndc_to_px.x - 1.0,
        1.0 - world.y / G.ndc_to_px.y,
        0.0,
        1.0,
    );
    out.paint_type = U.paint.x;
    out.paint_kind = U.paint.y;
    out.color = color;
    out.local = pos;
    out.color0 = U.color0;
    out.color1 = U.color1;
    out.grad = vec4f(U.grad_start.xy, U.grad_end.xy);
    return out;
}

const MAX_STOPS: u32 = 8u;

fn stop_color(i: u32) -> vec4f {
    return U.stop_colors[min(i, MAX_STOPS - 1u)];
}

// Four offsets share one `vec4`; the lane is a branch on a uniform value, not a
// dynamic vector index, so no backend has to lower a component read from
// storage.
fn stop_offset(i: u32) -> f32 {
    let lane = i % 4u;
    let packed = U.stop_offsets[min(i / 4u, MAX_STOPS / 4u - 1u)];
    if (lane == 0u) {
        return packed.x;
    }
    if (lane == 1u) {
        return packed.y;
    }
    if (lane == 2u) {
        return packed.z;
    }
    return packed.w;
}

// Multi-stop ramp at parametric position `p`. Offsets arrive clamped to
// 0..=1 and non-decreasing (Rust forces that when packing), so one forward
// scan finds the bracketing pair; `p` outside the first/last offset clamps to
// that stop's colour (TileMode::Clamp), and repeated offsets are a hard stop
// at the shared position (the later stop wins). `count == 0` paints nothing,
// `count == 1` paints the single stop.
fn eval_stops(p: f32, count: u32) -> vec4f {
    if (count == 0u) {
        return vec4f(0.0);
    }
    let last = min(count, MAX_STOPS) - 1u;
    if (last == 0u) {
        return stop_color(0u);
    }
    if (p <= stop_offset(0u)) {
        return stop_color(0u);
    }
    if (p >= stop_offset(last)) {
        return stop_color(last);
    }
    var hi = last;
    for (var i = 1u; i < last; i++) {
        if (p < stop_offset(i)) {
            hi = i;
            break;
        }
    }
    let lo = hi - 1u;
    let span = stop_offset(hi) - stop_offset(lo);
    var f = 1.0;
    if (span > 0.0) {
        f = clamp((p - stop_offset(lo)) / span, 0.0, 1.0);
    }
    return mix(stop_color(lo), stop_color(hi), f);
}

fn radial_pos(local: vec2f, center: vec2f, radius: f32) -> f32 {
    return clamp(distance(local, center) / max(radius, 1e-3), 0.0, 1.0);
}

fn linear_pos(local: vec2f, p0: vec2f, p1: vec2f) -> f32 {
    let dir = p1 - p0;
    let len2 = max(dot(dir, dir), 1e-6);
    return clamp(dot(local - p0, dir) / len2, 0.0, 1.0);
}

fn eval_paint(in: VSOut) -> vec4f {
    if (in.paint_type == 0u) {
        return vec4f(in.color.rgb * in.color.a, in.color.a);
    }
    if (in.paint_kind == 1u) {
        let c = mix(in.color0, in.color1, radial_pos(in.local, in.grad.xy, in.grad.z));
        return vec4f(c.rgb * c.a, c.a);
    }
    if (in.paint_kind == 2u) {
        let rel = in.local - in.grad.xy;
        var frac = atan2(rel.y, rel.x) / 6.2831853;
        if (frac < 0.0) {
            frac += 1.0;
        }
        let c = mix(in.color0, in.color1, clamp(frac, 0.0, 1.0));
        return vec4f(c.rgb * c.a, c.a);
    }
    // Same positions as the two-stop ramps above, so a two-stop table
    // reproduces `Linear`/`Radial` exactly.
    if (in.paint_kind == 3u) {
        let c = eval_stops(linear_pos(in.local, in.grad.xy, in.grad.zw), U.paint.z);
        return vec4f(c.rgb * c.a, c.a);
    }
    if (in.paint_kind == 4u) {
        let c = eval_stops(radial_pos(in.local, in.grad.xy, in.grad.z), U.paint.z);
        return vec4f(c.rgb * c.a, c.a);
    }
    let c = mix(in.color0, in.color1, linear_pos(in.local, in.grad.xy, in.grad.zw));
    return vec4f(c.rgb * c.a, c.a);
}

@fragment
fn fs_main(in: VSOut) -> @location(0) vec4f {
    return eval_paint(in);
}