perf(core): skip transparent glyph pixels - #411
Draft
lfkdsk wants to merge 1 commit into
Draft
Conversation
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.
Glyph cells are mostly empty. Rasterizing hero's text scans 10.7M glyph-cell
pixels of which 1.66M carry coverage — 84.6% of the work touches transparent
pixels. On a text-denser app (
note) it is 86.8%.This builds a sparse per-row span index when an atlas is parsed, and walks only
the covered samples during full-render glyph runs. Coverage values still live in
the bitmap; the index stores coordinates.
Measured on
mainAt the Vita configuration — density-2 baked atlases, scale-2 output — 180-frame
medians over 15 alternating independent-process pairs:
The cost is parse-time storage: hero's d2 span indexes are 144,628 B across 10
allocations, note's 84,890 B across 8. The 180 render calls after that allocate
nothing. The feature adds 6,292 B of wasm over the dense opt-out build.
Parse happens once per guest boot — every
loadFontAtlascall site is a mountloop, a PSP/Vita pak feed, or a fresh sim/tape/web world. There is no runtime
on-demand font-slot loading, so the parse cost is never paid at frame cadence.
Where it applies
Sparse spans compile in on web/wasm, the Vita and Switch software rasterizers,
and normal hosts; they compile out on PSP, Nintendo 3DS and ESP-IDF. Real
PSP and 3DS target outputs plus a forced ESP-IDF configuration each contain zero
sparse helper symbols.
Verification
Four
cargomatrices compare sparse against the dense oracle on buffersprefilled with nonzero data, covering scales 1..4, density-2 sampling and
downsampling, four-edge and disjoint clips, negative cell origins, translucent
composition with destination reads, alpha 0, a full DrawList through the real
GLYPH_RUNdispatch, and RGBA/ARGB/RGB565 targets. Both 180-frame app replaysassert per-frame hashes.
Mutation checks: treating coverage below 64 as transparent fails 6 tests and
diverges at d2/s2 frame 0 (the threshold is chosen because baked atlases contain
no bytes 1..8, so a
cov <= 1mutant would be invisible end to end); droppingthe last source row or column at density ≠ 1 fails 2 tests each.
One documented gap: a floor-instead-of-ceil mutant in the destination-run
boundary math fails its unit test but still passes both 180-frame d2/s3 replays.
The non-integer downsample branch has no frame-level coverage today — shipped
tapes are d1/s1 and Vita is d2/s2 — so it rests on the density 1..255 × scale
1..4 unit matrix.
Suite behaviour matches clean
mainexactly: 574 pass, 1 skip, the same fiveunit failures, and the same frame-0 divergence of the committed
hero-maintapehashes.