The map's decoded-tile dict was never evicted from, so it grew for the
life of the screen with every tile the view had ever touched. Measured on
the Lyra, a decoded tile is ~0.9 MB resident (62 bytes a point) against a
device with ~100 MB in total and, in practice, under 20 MB free -- so a
long enough pan was an out-of-memory kill waiting to happen. It predates
the caching work, but making tiles cheap to load makes it easier to reach.
The budget is relative to the view rather than a fixed number, so it
scales itself: twice what the current viewport needs, floored at 8. That
lands around 8-12 tiles on the PicoCalc's 53x26 and grows on a desktop
terminal, without either one needing to know about the other.
Two details worth keeping. Tiles on screen are moved to the end of the
order before the trim runs, so eviction starts at the far end and can
never take something being drawn. And only entries holding geometry are
counted or dropped: a None is the memory of having *asked* -- the tile was
absent, or the source never answered -- which costs a dict slot rather
than a megabyte, and dropping it would buy nothing back while costing a
re-request on the next repaint.
Getting this wrong is now cheap, which is what makes a tight budget the
right call: an evicted tile comes back from the decoded cache in ~25-50 ms
having already been parsed once, so the cache is trading a little memory
for instant back-panning, not for correctness.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>