Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/facett

facett-map3d/src/present.wgsl

// ── THE PRESENT BLIT — a plain resample of whatever the AA lane resolved ─────
//
// This file is what `fxaa.wgsl` used to be, and the difference IS the ruling: **there
// is exactly one framebuffer AA scheme in facett and it is TAA** (plus the analytic
// feather in `facett-map/src/gpu/line.wgsl` for 2D line coverage, which is a different
// job and stays). FXAA lived here because the offscreen is `sample_count = 1` and
// something had to take the jaggies off the tonemapped image. TAA now does that
// properly — jittered sample positions accumulated over frames, reprojected by real
// motion vectors — and running FXAA after it would blur the very edges TAA is trying
// to converge, which the history then accumulates. So the present pass does the ONE
// thing a present pass should: sample, encode, blit.
//
// It keeps the sRGB gate verbatim from the pass it replaces, because that is a
// property of the SURFACE, not of the antialiasing: the browser's WebGPU canvas is
// `Bgra8Unorm` (nothing encodes on write) while a native window is `Bgra8UnormSrgb`
// (the hardware encodes). `gpu.rs::present_encode_srgb` decides, `U.texel.z` carries
// it, and dropping it here would bring the dark/muddy-brown browser render back.
//
// Binding layout is the post stack's shared `{uniform, tex, sampler}` — unchanged, so
// the same `sample_bgl` and the same `u_composite` feed it.

struct PresentUniforms {
    params: vec4<f32>, // unused here (shared post layout)
    texel: vec4<f32>,  // texel.xy = 1/width, 1/height; texel.z = sRGB-encode flag
};

@group(0) @binding(0) var<uniform> U: PresentUniforms;
@group(0) @binding(1) var src_tex: texture_2d<f32>;
@group(0) @binding(2) var src_smp: sampler;

struct VsOut {
    @builtin(position) clip: vec4<f32>,
    @location(0) uv: vec2<f32>,
};

@vertex
fn post_vs(@builtin(vertex_index) vi: u32) -> VsOut {
    var p = array<vec2<f32>, 3>(
        vec2<f32>(-1.0, -1.0),
        vec2<f32>( 3.0, -1.0),
        vec2<f32>(-1.0,  3.0),
    );
    var out: VsOut;
    let xy = p[vi];
    out.clip = vec4<f32>(xy, 0.0, 1.0);
    out.uv = vec2<f32>(xy.x * 0.5 + 0.5, 1.0 - (xy.y * 0.5 + 0.5));
    return out;
}

// Linear → sRGB (IEC 61966-2-1) encode, per channel.
fn linear_to_srgb(c: vec3<f32>) -> vec3<f32> {
    let lo = c * 12.92;
    let hi = 1.055 * pow(max(c, vec3<f32>(0.0)), vec3<f32>(1.0 / 2.4)) - 0.055;
    let cutoff = step(vec3<f32>(0.0031308), c);
    return clamp(mix(lo, hi, cutoff), vec3<f32>(0.0), vec3<f32>(1.0));
}

// Encode for the present target: sRGB-encode iff `U.texel.z > 0.5` (a non-sRGB —
// WebGPU browser — surface), else pass through and let the hardware encode.
fn present_encode(rgb: vec3<f32>) -> vec3<f32> {
    if (U.texel.z > 0.5) {
        return linear_to_srgb(rgb);
    }
    return rgb;
}

@fragment
fn present_fs(in: VsOut) -> @location(0) vec4<f32> {
    // Point-for-point: the source is the same size as the target, so a linear sampler
    // at the pixel centre reads exactly one texel. Nothing is filtered, blurred or
    // edge-detected here — the antialiasing already happened, upstream and in time.
    let c = textureSample(src_tex, src_smp, in.uv);
    return vec4<f32>(present_encode(c.rgb), c.a);
}