perf(ui): per-shelf dirty spans for tight font-atlas uploads (#129) #147

Merged
catbot merged 1 commit from claude/issue-129 into master 2026-06-18 15:36:33 +02:00
Member

Problem

FontAtlas::Update accumulated all glyph dirt into a single union bounding box. When new glyphs land on different shelves the same frame, that union becomes a tall box spanning the untouched rows between the shelves, so the upload copies mostly-unchanged texels. Warmup-only (glyphs are write-once; steady state bails on !dirty).

Fix

Track a dirty span per shelf (Shelf::dirty) and issue one tight UpdateRegion / wgpuWriteAtlasRegion copy per armed span. A shelf packs glyphs left-to-right at a fixed top, so each span is naturally a contiguous X run capped by the shelf height — no wasted rows between shelves.

  • ShelfPlace now reports the placed shelf index so the caller marks the right span.
  • The one-shot whole-atlas zero-clear in Initialize keeps using the existing dirtyRect span.
  • dirty is now the OR of every span; Update clears them all together.

DirtyRect, MarkDirty, and the public dirty flag keep their existing semantics, so the FontAtlasDirtyRect math test is unchanged.

Testing

  • crafter-build test — 23 passed (incl. FontAtlasDirtyRect, ShapeTextCache, ImageStagingRelease).
  • Ran HelloUI on the WebGPU backend; text renders correctly through the new multi-span upload path.

Screenshots

HelloUI text rendering

Resolves #129

## Problem `FontAtlas::Update` accumulated all glyph dirt into a single union bounding box. When new glyphs land on different shelves the same frame, that union becomes a tall box spanning the untouched rows *between* the shelves, so the upload copies mostly-unchanged texels. Warmup-only (glyphs are write-once; steady state bails on `!dirty`). ## Fix Track a dirty span **per shelf** (`Shelf::dirty`) and issue one tight `UpdateRegion` / `wgpuWriteAtlasRegion` copy per armed span. A shelf packs glyphs left-to-right at a fixed top, so each span is naturally a contiguous X run capped by the shelf height — no wasted rows between shelves. - `ShelfPlace` now reports the placed shelf index so the caller marks the right span. - The one-shot whole-atlas zero-clear in `Initialize` keeps using the existing `dirtyRect` span. - `dirty` is now the OR of every span; `Update` clears them all together. `DirtyRect`, `MarkDirty`, and the public `dirty` flag keep their existing semantics, so the `FontAtlasDirtyRect` math test is unchanged. ## Testing - `crafter-build test` — 23 passed (incl. `FontAtlasDirtyRect`, `ShapeTextCache`, `ImageStagingRelease`). - Ran HelloUI on the WebGPU backend; text renders correctly through the new multi-span upload path. ## Screenshots ![HelloUI text rendering](https://forgejo.catcrafts.net/attachments/3eedb757-117e-4526-bf3d-cde0d9c18fd6) Resolves #129
FontAtlas::Update used a single union bounding box for the dirty rect.
Scattered new glyphs landing on different shelves produced a tall union
box that re-uploaded mostly-unchanged texels between the shelves.

Track a dirty span per shelf instead and issue one tight UpdateRegion /
wgpuWriteAtlasRegion copy per armed span. A shelf packs glyphs
left-to-right at a fixed top, so each span stays a contiguous X run
capped by the shelf height. The whole-atlas zero-clear in Initialize
keeps using the existing dirtyRect span. `dirty` is now the OR of every
span and is cleared together with them in Update.

Verified: full test suite green (23 passed); HelloUI text renders
crisply on the WebGPU backend through the new multi-span upload path.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
catbot merged commit a4fdbf7d30 into master 2026-06-18 15:36:33 +02:00
catbot deleted branch claude/issue-129 2026-06-18 15:36:34 +02:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Catcrafts/Crafter.Graphics!147
No description provided.