Merge PR #1023: preserve minified text strokes

Keep adaptive sampling in both text shaders with compact sampling loops. Validate shader derivatives and existing text rendering, including GPU pipeline creation.

Closes #964
This commit is contained in:
Hakan Seven committed 2026-09-06 21:04:14 +03:00
commit bf4614856c
3 files changed
+64 -10

No files matched your search

+23 -3
View File
@@ -49,9 +49,29 @@ fn vs_main(v: VertIn) -> VertOut {
@fragment
fn fs_main(in: VertOut) -> @location(0) vec4<f32> {
let sd = textureSample(atlas_tex, atlas_samp, in.uv).r;
let aa = max(fwidth(sd), 1e-4);
let alpha = smoothstep(0.5 - aa, 0.5 + aa, sd);
let uv_dx = dpdx(in.uv);
let uv_dy = dpdy(in.uv);
let dims = vec2<f32>(textureDimensions(atlas_tex));
let footprint = max(length(uv_dx * dims), length(uv_dy * dims));
let sd_center = textureSampleLevel(atlas_tex, atlas_samp, in.uv, 0.0).r;
let aa = max(fwidth(sd_center), 1e-4);
var alpha = smoothstep(0.5 - aa, 0.5 + aa, sd_center);
if footprint > 1.0 {
// Average coverage over the pixel footprint so thin strokes survive minification.
let samples = select(4, 2, footprint <= 4.0);
var sum = 0.0;
for (var y = 0; y < samples; y += 1) {
for (var x = 0; x < samples; x += 1) {
let offset = (vec2<f32>(f32(x), f32(y)) + 0.5) / f32(samples) - 0.5;
let sd = textureSampleLevel(
atlas_tex, atlas_samp, in.uv + uv_dx * offset.x + uv_dy * offset.y, 0.0
).r;
sum += smoothstep(0.5 - aa, 0.5 + aa, sd);
}
}
alpha = sum / f32(samples * samples);
}
if alpha <= 0.0 {
discard;
}
+25 -7
View File
@@ -59,13 +59,31 @@ fn vs_main(in: VertIn) -> VertOut {
// ── Fragment stage ────────────────────────────────────────────────────────────
@fragment
fn fs_main(in: VertOut) -> @location(0) vec4<f32> {
// SDF value: 0.5 at the glyph edge, higher inside the ink.
let sd = textureSample(atlas_tex, atlas_samp, in.uv).r;
// Antialias over one screen-space texel of field change.
let aa = max(fwidth(sd), 1e-4);
let a = smoothstep(0.5 - aa, 0.5 + aa, sd);
if (a <= 0.0) {
let uv_dx = dpdx(in.uv);
let uv_dy = dpdy(in.uv);
let dims = vec2<f32>(textureDimensions(atlas_tex));
let footprint = max(length(uv_dx * dims), length(uv_dy * dims));
let sd_center = textureSampleLevel(atlas_tex, atlas_samp, in.uv, 0.0).r;
let aa = max(fwidth(sd_center), 1e-4);
var alpha = smoothstep(0.5 - aa, 0.5 + aa, sd_center);
if footprint > 1.0 {
// Average coverage over the pixel footprint so thin strokes survive minification.
let samples = select(4, 2, footprint <= 4.0);
var sum = 0.0;
for (var y = 0; y < samples; y += 1) {
for (var x = 0; x < samples; x += 1) {
let offset = (vec2<f32>(f32(x), f32(y)) + 0.5) / f32(samples) - 0.5;
let sd = textureSampleLevel(
atlas_tex, atlas_samp, in.uv + uv_dx * offset.x + uv_dy * offset.y, 0.0
).r;
sum += smoothstep(0.5 - aa, 0.5 + aa, sd);
}
}
alpha = sum / f32(samples * samples);
}
if alpha <= 0.0 {
discard;
}
return vec4<f32>(in.color.rgb, in.color.a * a);
return vec4<f32>(in.color.rgb, in.color.a * alpha);
}
+16
View File
@@ -0,0 +1,16 @@
#[test]
fn adaptive_text_sampling_passes_derivative_uniformity_validation() {
for (name, source) in [
("text", include_str!("../src/shaders/text.wgsl")),
("block text", include_str!("../src/shaders/block_text.wgsl")),
] {
let module = naga::front::wgsl::parse_str(source)
.unwrap_or_else(|error| panic!("{name}: {}", error.emit_to_string(source)));
naga::valid::Validator::new(
naga::valid::ValidationFlags::all(),
naga::valid::Capabilities::empty(),
)
.validate(&module)
.unwrap_or_else(|error| panic!("{name}: {error:?}"));
}
}