perf(ui): per-shelf dirty spans for tight atlas uploads (#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>
This commit is contained in:
parent
e1222c0ab7
commit
16d291b0ad
2 changed files with 51 additions and 28 deletions
|
|
@ -102,9 +102,12 @@ export namespace Crafter {
|
|||
std::vector<std::uint8_t> staging;
|
||||
#endif
|
||||
// `dirty` stays the cheap "is there anything to flush?" flag the
|
||||
// renderer polls each frame; `dirtyRect` carries the bounds Update
|
||||
// copies. The two are always set and cleared together (dirty ==
|
||||
// !dirtyRect.Empty()).
|
||||
// renderer polls each frame; it is the OR of every dirty span Update
|
||||
// copies. `dirtyRect` is the *whole-atlas* span — used only for the
|
||||
// one-shot zero-clear in Initialize; per-glyph dirt is tracked tight
|
||||
// per shelf (Shelf::dirty) instead of inflating one tall union box
|
||||
// across scattered shelves (#129). `dirty` and the spans are always
|
||||
// armed and cleared together.
|
||||
bool dirty = false;
|
||||
DirtyRect dirtyRect;
|
||||
|
||||
|
|
@ -131,7 +134,11 @@ export namespace Crafter {
|
|||
}
|
||||
|
||||
private:
|
||||
struct Shelf { int y = 0; int height = 0; int cursorX = 0; };
|
||||
// A shelf packs glyphs left-to-right at a fixed top (`y`). Its `dirty`
|
||||
// span therefore stays naturally tight: a contiguous X run capped by
|
||||
// the shelf height — far smaller than a union box spanning every
|
||||
// shelf a frame happened to touch.
|
||||
struct Shelf { int y = 0; int height = 0; int cursorX = 0; DirtyRect dirty; };
|
||||
std::vector<Shelf> shelves_;
|
||||
int nextShelfY_ = 0;
|
||||
|
||||
|
|
@ -149,6 +156,8 @@ export namespace Crafter {
|
|||
};
|
||||
std::unordered_map<Key, Glyph, KeyHash> cache_;
|
||||
|
||||
bool ShelfPlace(int w, int h, int& outX, int& outY);
|
||||
// On success, outShelf is the index into shelves_ of the placed
|
||||
// glyph, so the caller can mark that shelf's dirty span.
|
||||
bool ShelfPlace(int w, int h, int& outX, int& outY, int& outShelf);
|
||||
};
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue