// **The GPU label draw** — instanced glyph quads whose instance count comes from the
// collision pass's `atomicAdd`, consumed by `draw_indirect` (GFX_V2 item 4).
//
// One instance per glyph-tap: `label_collide.wgsl` expands each glyph into 4 halo taps
// plus the ink pass, contiguously per label, so within a label the halo rasterises
// under the ink. Across labels order does not matter — not drawing two labels on the
// same pixels is the entire point of the pass upstream.
//
// ── Why this is not `msdf.wgsl` ─────────────────────────────────────────────────
//
// The atlas here is epaint's own font atlas — an 8-bit COVERAGE field premultiplied
// into RGBA, the same texture egui's text pass samples. Reusing egui's atlas is what
// makes a GPU-drawn street name identical to the CPU-painted one, so the fail-safe
// lane and the device lane letter the map the same way (and no font rasteriser,
// offline `msdf-atlas-gen` step or second copy of the glyph cache enters the tree).
//
// A coverage atlas has no signed distance to reconstruct, so `msdf_fs`'s median-of-
// three-channels and its `px_range` slope would be arithmetic over a field that is not
// there — `median3(a,a,a) - 0.5` on a coverage value is not a distance, and scaling it
// by a pixel range would harden the antialiasing egui already resolved. Sampling
// `.a` and letting the bilinear filter do the edge is the correct reconstruction for
// this texture. `MsdfText` remains the right pipeline for a real MSDF atlas.
struct LabelDrawU {
// x,y = viewport size in px (the space the collision pass emitted rects in)
vp: vec4<f32>,
};
@group(0) @binding(0) var<uniform> U: LabelDrawU;
@group(1) @binding(0) var atlas_tex: texture_2d<f32>;
@group(1) @binding(1) var atlas_smp: sampler;
struct GlyphIn {
@location(0) rect_min: vec2<f32>,
@location(1) rect_max: vec2<f32>,
@location(2) uv_min: vec2<f32>,
@location(3) uv_max: vec2<f32>,
@location(4) color: vec4<f32>,
};
struct VOut {
@builtin(position) clip: vec4<f32>,
@location(0) uv: vec2<f32>,
@location(1) color: vec4<f32>,
};
@vertex
fn label_vs(@builtin(vertex_index) vi: u32, g: GlyphIn) -> VOut {
var corners = array<vec2<f32>, 6>(
vec2<f32>(0.0, 0.0), vec2<f32>(1.0, 0.0), vec2<f32>(0.0, 1.0),
vec2<f32>(0.0, 1.0), vec2<f32>(1.0, 0.0), vec2<f32>(1.0, 1.0),
);
let c = corners[vi];
let px = mix(g.rect_min, g.rect_max, c);
let uv = mix(g.uv_min, g.uv_max, c);
// Pixel rect -> NDC, y down in pixel space.
let ndc = vec2<f32>(px.x / U.vp.x * 2.0 - 1.0, 1.0 - px.y / U.vp.y * 2.0);
var out: VOut;
out.clip = vec4<f32>(ndc, 0.0, 1.0);
out.uv = uv;
out.color = g.color;
return out;
}
@fragment
fn label_fs(in: VOut) -> @location(0) vec4<f32> {
// epaint writes each glyph as white premultiplied by its coverage, so alpha IS the
// coverage and the colour channels carry no glyph information of their own.
let cov = textureSample(atlas_tex, atlas_smp, in.uv).a;
let a = cov * in.color.a;
return vec4<f32>(in.color.rgb * a, a); // premultiplied source-over
}