render: recycle the glyph atlas at a placed-slot count #459

Merged
natsukium merged 2 commits from render/glyph-slot-cap-451 into main 2026-09-25 18:53:34 +09:00
Owner

The glyph slot map was emptied only when an atlas sheet filled, so its size tracked glyph area rather than entry count. A producer printing text at many tiny OSC 66 fractional sizings rasterizes each key to a pixel or two, and a 2048² sheet holds millions of those before it recycles, so PTY output alone could grow the map that far.

The sheets now also count as full at 65 536 placed slots and recycle through the same path as a filled shelf, so the frame's second walk and the thrash logging apply unchanged and the hit path is untouched. Per-entry eviction is not possible without leaking sheet space, since the shelf packer frees no single slot. The count is twice what a sheet holds of 8×16 cells, so text at a readable size still fills the sheet first; the map's table stays under 8 MiB.

A test drives more than 65 536 distinct tiny placed glyphs through a 2048² index and checks that the cap, not the sheet, triggers the one recycle, that the placed count never exceeds it, and that the newest glyph survives. Docs: pipeline.md "Atlases", security-model.md "Resource exhaustion", REQ-704, CHANGELOG.

Closes #451

The glyph slot map was emptied only when an atlas sheet filled, so its size tracked glyph area rather than entry count. A producer printing text at many tiny OSC 66 fractional sizings rasterizes each key to a pixel or two, and a 2048² sheet holds millions of those before it recycles, so PTY output alone could grow the map that far. The sheets now also count as full at 65 536 placed slots and recycle through the same path as a filled shelf, so the frame's second walk and the thrash logging apply unchanged and the hit path is untouched. Per-entry eviction is not possible without leaking sheet space, since the shelf packer frees no single slot. The count is twice what a sheet holds of 8×16 cells, so text at a readable size still fills the sheet first; the map's table stays under 8 MiB. A test drives more than 65 536 distinct tiny placed glyphs through a 2048² index and checks that the cap, not the sheet, triggers the one recycle, that the placed count never exceeds it, and that the newest glyph survives. Docs: pipeline.md "Atlases", security-model.md "Resource exhaustion", REQ-704, CHANGELOG. Closes #451
The glyph slot map was emptied only when an atlas sheet filled, so its
size tracked glyph area rather than entry count. A producer printing
text at many tiny OSC 66 fractional sizings rasterizes each key to a
pixel or two, and a 2048² sheet holds millions of those before it
recycles; the map grew with it from PTY output alone.

The sheets now also count as full at 65 536 placed slots and recycle
through the same path as a filled shelf, so the frame's second walk and
the thrash logging apply unchanged. A per-entry eviction would leave
the evicted glyph's pixels allocated, since the shelf packer frees no
single slot. The count sits at twice what a sheet holds of 8×16 cells,
so text at a readable size still fills the sheet first, and it keeps
the map near the sheet's own 4 MiB.

Assisted-by: Claude Code
render: state the slot map's size with its spare hash capacity
Some checks failed
windows / detect relevant changes (pull_request) Failing after 4m59s
windows / cargo nextest (Windows) (pull_request) Has been skipped
windows / windows docs-only no-op (pull_request) Has been skipped
windows / frontend smoke (Windows) (pull_request) Has been skipped
windows / package felis (x86_64-pc-windows-msvc) (pull_request) Has been skipped
darwin / detect relevant changes (pull_request) Failing after 5m1s
darwin / build felis (aarch64-darwin) (pull_request) Has been skipped
darwin / darwin docs-only no-op (pull_request) Has been skipped
darwin / publish felis (aarch64-darwin) (pull_request) Has been skipped
pr / nix flake check (pull_request) Has been cancelled
pr / cargo build / clippy / test / deny (pull_request) Has been cancelled
pr / wire schema is compatible with the base (pull_request) Has been cancelled
pr / frontend smoke (x86_64-linux) (pull_request) Has been cancelled
pr / publish felis (x86_64-linux) (pull_request) Has been cancelled
windows / cargo clippy (Windows cross) (pull_request) Successful in 50s
68e3d4db07
At 65 536 entries the hash table has room for 114 688, so its buckets and control bytes come to about 6.6 MiB, not the 4 MiB the per-entry estimate gave.

Assisted-by: Claude Code
natsukium deleted branch render/glyph-slot-cap-451 2026-09-25 18:53:35 +09:00
Commenting is not possible because the repository is archived.
No description provided.