repose-render-wgpu 0.30.5

Repose wgpu handler
Documentation
// Projective layer composite: samples an offscreen graphics-layer texture
// through a general 2D projective map (perspective flattening, CSS style).
//
// The subtree renders flat into the layer; the four layer-rect corners are
// projected on the CPU to NDC (`c0..c3`, counter-clockwise from top-left)
// with their homogeneous `w`. The `w` rides in CLIP SPACE
// (`out.pos = vec4(corner * w, w)`), so the GPU's perspective-correct
// interpolation reconstructs the projective map exactly; the fragment
// shader samples the plain interpolated uv (a separate `w` varying would
// interpolate linearly in screen space, which is wrong). Input and output
// are premultiplied alpha, like the sharp `CompositeLayer` path.
struct Globals {
    ndc_to_px: vec2<f32>,
    _pad: vec2<f32>,
};
@group(0) @binding(0) var<uniform> G: Globals;

struct VSOut {
    @builtin(position) pos: vec4<f32>,
    @location(0) uv: vec2<f32>,
    @location(1) alpha: f32,
};

@vertex
fn vs_main(
    @location(0) c0: vec2<f32>,
    @location(1) c1: vec2<f32>,
    @location(2) c2: vec2<f32>,
    @location(3) c3: vec2<f32>,
    @location(4) uv_bounds: vec4<f32>,
    @location(5) w_row: vec4<f32>,
    @location(6) alpha: f32,
    @builtin(vertex_index) v: u32,
) -> VSOut {
    // 6 vertices -> 4 corners: 0,1,2, 0,2,3.
    var corner = c0;
    var uv = uv_bounds.xy;
    var w = w_row.x;
    if (v == 1u) {
        corner = c1;
        uv = vec2(uv_bounds.z, uv_bounds.y);
        w = w_row.y;
    } else if (v == 2u || v == 4u) {
        corner = c2;
        uv = uv_bounds.zw;
        w = w_row.z;
    } else if (v == 5u) {
        corner = c3;
        uv = vec2(uv_bounds.x, uv_bounds.w);
        w = w_row.w;
    }

    var out: VSOut;
    out.pos = vec4(corner * w, 0.0, w);
    out.uv = uv;
    out.alpha = alpha;
    return out;
}

@group(1) @binding(0) var layer_tex: texture_2d<f32>;
@group(1) @binding(1) var layer_samp: sampler;

@fragment
fn fs_main(in: VSOut) -> @location(0) vec4<f32> {
    // `in.uv` is already perspective-correct (divided by clip-space `w` in
    // hardware). The layer sampler clamps, like the sharp composite path.
    let c = textureSample(layer_tex, layer_samp, in.uv);
    let a = c.a * in.alpha;
    return vec4(c.rgb * in.alpha, a);
}