Skip to content

MSDF font atlases are mipmapped, corrupting the median on minified text #8997

Description

@willeastcott

Summary

MSDF font atlas textures are loaded with mipmaps enabled (mipmaps: true, minFilter: FILTER_LINEAR_MIPMAP_LINEAR). Mipmapping is wrong for MSDF: each mip level box-averages the R/G/B channels, but median(average) != average(median), so under minification the shader samples a corrupted median. This shows as faint ghost/flicker artifacts on small text — e.g. a thin horizontal line that flickers on and off just below closely-spaced stems (the ll in "Hello") as the text moves. Most visible at small fontSize and DPR 1 / non-HiDPI.

Why it's visible now

Latent for a long time (the atlas has always been mipmapped), but surfaced by #8990, which renders MSDF text at the true glyph edge (threshold 0.5). The previous inward erosion (~0.525 plus the (m-0.05)/0.95 remap) had kept the faint corrupted-median values below the render threshold. Related to #8984.

Repro

  • MSDF font (e.g. store Roboto), ElementComponent text, small fontSize (~6), useDevicePixelRatio: false (DPR 1 makes it much easier to see), text translating sub-pixel.
  • A faint horizontal line flickers below the ll. Forcing the atlas to sample the base level only (no mipmaps) eliminates it, without making small text look worse.

Fix direction / constraints

Load MSDF atlases at the base level only — mipmaps: false + minFilter: FILTER_LINEAR (bitmap fonts should keep their mipmaps). Two things make this non-trivial:

  1. Must be set at texture creation, not mutated after load. On WebGPU Texture#mipmaps can't be changed after creation (the setter warns and keeps the mip chain), and the WebGPU sampler sets no lodMaxClamp, so even a non-mip minFilter still minifies into the chain — the artifact persists unless the mip chain doesn't exist.
  2. No clean creation-options channel. Font atlas textures load via loader.load(url, 'texture', cb) with no asset, so there's currently no tidy way to pass creation options (mipmaps/minFilter) through to the texture handler. fix(font): disable mipmaps on MSDF font atlases #8996 attempted to add one by threading an options arg through ResourceHandler.open but that was rejected (too broad / public-API surface). The clean channel is the open design question.

Closed attempt: #8996.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions