Warm the deck's card images at match start - #11731
Open
liamiak wants to merge 2 commits into
Open
Conversation
Opening a large zone view - most visibly the library during a tutor search - froze the UI for seconds, and froze it again on every sort, close and reopen. The cache is capped at 400 entries while a single large zone view needs two per card: the decoded original and the scaled copy the panel paints. A 500-card library therefore evicted everything it had just loaded and rebuilt the whole view on every refresh. 400 was never a considered choice for this workload, it is just what this preference has always defaulted to, so treat that exact value as unset and use 1500; any other value is one somebody chose and is left alone. Also switch to soft values so memory pressure rather than entry count is what ultimately evicts, which is what this class already documented itself as doing. Measured on a 500-card library with no images downloaded: reopening the view cost ~1.8s and re-requesting at a different size ~4.5s, both now ~1ms. Costs about 40MB of heap on that measurement. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The first view that shows a large library decodes every image at the moment it opens. Decode them one per EDT event once the match views are up instead, so the work happens while nobody is waiting. Originals only: they carry the decode cost and do not depend on the size the view eventually asks for. Cards with no local image cost a failed file lookup and are skipped rather than rendered or downloaded. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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.
Opening a zone view that shows a whole library - a tutor search, most visibly - decodes every card
image at the moment it opens. On a 525-card highlander deck with the art already downloaded that is
5.5 seconds of JPEG decode on the EDT, and the player waits for all of it.
Decode them at match start instead, one card per EDT event, once the match views are up.
The warm itself is ~10 ms per event. Measured in a real match rather than a harness: 550 cards
(both decks) warmed in 5,356 ms of spread-out EDT work, during which the game played normally with
no perceptible stutter. A 60-card deck is the same work scaled down, so no cap seemed worth adding.
Two scope cuts keep it small. It warms originals only - they carry the decode cost and do not
depend on the size the view eventually asks for, so this needs no advance knowledge of that size.
And it passes
useDefaultIfNotFound=false, so a card with no local image costs a failed file lookupand is skipped: nothing is rendered, and the online fetcher is not involved. That avoids
FCardImageRenderer's non-thread-safe statics without needing a worker thread, which is why this is~50 lines and touches no threading.
It starts after the match views open, because the desktop clears the image cache when switching
screens and would otherwise discard the work, and it cancels whenever the cache is cleared. Exposed
as a default no-op on
IGuiBase; only the desktop GUI opts in.Depends on #11295. Without the larger image cache this does almost nothing: on plain master a
500-card warm went 8,451 ms -> 8,068 ms, because a 400-entry cache evicts the warmed originals
faster than they are stored. The two are complementary - #11295 makes a large view stay cached,
this makes its first open cheap.
Memory. Warming allocates the decoded originals early rather than additionally. Opening a
525-card library costs +589 MB of heap on master; warming first and then opening costs +593 MB - the
same peak, paid at match start instead of mid-tutor. What is genuinely new is the player who never
opens a large zone view: they hold ~506 MB (525-card deck), or proportionally less (~60 MB for 60
cards), that they would not otherwise have allocated. The cache holds soft values, so it is
reclaimed under memory pressure, and a screen switch clears it entirely.
No test. Every test in this repo drives
PlayerControllerAi, so none of them reach this codepath; a test here would assert nothing. It was verified by instrumenting both ends of the hook and
playing a real match, and by the measurements above.
Full desktop suite: 360 tests, 0 failures, 6 skipped. Checkstyle clean.
Written with Claude Opus 5 (also recorded in the commit co-authors).