A `Primitive` now carries its own WGSL, and `PrimitiveRegistry` keys ids by `TypeId`, so `Painter::primitive` takes only the value and the `RECT`, `GLYPH` and `TEXTURE` constants are gone. Registering is what a first draw does; the built-ins are seeded up front so first-draw order cannot decide anything about them. What a primitive samples is no longer something every registration states. The glyph atlas and the one sampler moved into the shared group, which is where a mask texture would go too, so a rect's pipeline has no texture in its layout at all. Only a primitive whose type sets `TEXTURE` gets an image group, and that is also what records the slot at write time -- so nothing reads a `u32` back out of the instance payload. Verified on the headless rig: the tabs example, two images added at runtime, an image alone in a layer, and text spanning a four-layer atlas after the array grew twice. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
28 lines
675 B
WebGPU Shading Language
28 lines
675 B
WebGPU Shading Language
// Matches `GlyphEntry::IS_COLORED`.
|
|
const COLORED: u32 = 1u;
|
|
|
|
struct GlyphInfo {
|
|
uv_min: vec2<f32>,
|
|
uv_max: vec2<f32>,
|
|
// Which layer of the atlas array this glyph's page is.
|
|
layer: u32,
|
|
color: u32,
|
|
flags: u32,
|
|
}
|
|
|
|
@group(1) @binding(0)
|
|
var<storage> glyphs: array<GlyphInfo>;
|
|
|
|
@fragment
|
|
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
|
|
let g = glyphs[in.idx];
|
|
let uv = mix(g.uv_min, g.uv_max, in.uv);
|
|
let texel = textureSample(atlas, samp, uv, i32(g.layer));
|
|
if (g.flags & COLORED) != 0u {
|
|
return masked(in, texel);
|
|
}
|
|
var color = unpack4x8unorm(g.color);
|
|
color.a *= texel.a;
|
|
return masked(in, color);
|
|
}
|