fix(element): stabilize MSDF text anti-aliasing at small sizes#8990
Merged
Conversation
…e field gradient #8935 switched the coverage anti-aliasing to fwidth(median(sample)). On minified (small) text the atlas is undersampled, so that gradient is noisy and breathes as the text translates - small text shimmers and thin strokes (l, i, 1) flicker on and off (#8984). Derive the transition width from the uv magnification (both axes) and the atlas spread instead - the msdfgen screenPxRange approach - which depends only on geometry, so it is stable under motion and minification, floored at 1px. Keep the true-edge threshold (edge = 0.5 - 0.5*intensity) from #8935 so the #2948 crossbar fix and the rendered weight are unchanged. Re-adds the font_pxrange uniform removed by #8935 (atlas size comes from textureSize/textureDimensions); bumps the msdfPS chunk version to 2.21. Fixes #8984 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Build size reportThis PR changes the size of the minified bundles.
|
willeastcott
added a commit
that referenced
this pull request
Jun 30, 2026
…e field gradient (#8990) #8935 switched the coverage anti-aliasing to fwidth(median(sample)). On minified (small) text the atlas is undersampled, so that gradient is noisy and breathes as the text translates - small text shimmers and thin strokes (l, i, 1) flicker on and off (#8984). Derive the transition width from the uv magnification (both axes) and the atlas spread instead - the msdfgen screenPxRange approach - which depends only on geometry, so it is stable under motion and minification, floored at 1px. Keep the true-edge threshold (edge = 0.5 - 0.5*intensity) from #8935 so the #2948 crossbar fix and the rendered weight are unchanged. Re-adds the font_pxrange uniform removed by #8935 (atlas size comes from textureSize/textureDimensions). The msdfPS chunk version is left at 2.20 on this release line. Fixes #8984 Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
willeastcott
added a commit
that referenced
this pull request
Jun 30, 2026
#8990 re-added the font_pxrange uniform and bumped msdfPS to 2.21, but that change is additive: a 2.20-era override that doesn't read font_pxrange just ignores the extra uniform and keeps working. The only overrides that break are pre-2.20 ones (they read the removed font_textureWidth), and those are already flagged by the existing 2.20 marker. 2.20 stays the last breaking change to the chunk, and this keeps main and release-2.20 consistent. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This was referenced Jul 1, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Small MSDF text rendered via an
ElementComponentshimmers and looks rough since 2.20: as the text translates, thin 1px strokes (l,i,1) flicker on and off and edges crawl. Regression from #8935. (#8984)aa = fwidth(median(sample))— content-derived, noisy under minificationscreenPxRange, stableCause
#8935 made two independent changes. The coverage threshold (
edge = 0.5 - 0.5 * font_sdfIntensity, which fixed the #2948 crossbar erosion) is correct and kept. The regression is the other change — deriving the AA width from the distance-field gradient,aa = fwidth(median(sample)).MSDF atlases can't be mipmapped (averaging the channels breaks the median), so under minification the field is undersampled and the sampled median is noisy. Its screen-space gradient therefore changes frame-to-frame as the glyph moves sub-pixel, so the AA width breathes (→ shimmer), and where the gradient momentarily collapses the edge goes razor-sharp (→ thin strokes snap on/off). Pre-2.20 the AA width came from the uv magnification only, which is constant across a glyph and independent of sub-pixel position — stable.
Fix
Compute the transition width with the msdfgen-standard
screenPxRange(): derived fromfwidth(vUv0)(both axes) and the atlas spread, floored at 1px. It depends only on geometry, so it is stable under motion and minification, and using both uv axes also covers the diagonal/horizontal edges that motivated the gradient approach in #8935. The true-edge threshold is unchanged, so the #2948 fix and the rendered weight are preserved — this PR only changes AA stability.Re-adds the
font_pxrangeuniform removed by #8935 (atlas size now comes fromtextureSize/textureDimensions, sofont_textureWidthstays gone). GLSL and WGSL kept in parity.msdfPSbumped to chunk version2.21.Testing
fontSize6, DPR 1.75 ≈ 3 atlas-texels/screen-px): total text coverage over a sub-pixel translation cycle fluctuates ±1.4% with the fix vs ±5.7% on 2.20 (≈4× less). Coverage is conserved under pure translation, so its fluctuation is the shimmer. No shader-compile errors; text renders correctly.lstem: the 2.20 AA flickered ≈7× more than pre-2.20, the stem peak snapping between ~0.5 and a hard 1.0; the fix is flat.npm test(incl.text-element, 90 passing) and lint clean.Fixes #8984