Repository navigation
Track every texture that shares an atlas name - #2955
Merged
Merged
Conversation
Textures with the same image and vertex order share an atlas name and slot, such as every SpriteSolidColor size. The atlas kept only the first of them in _unique_textures[name], so its records could disagree with the reference count: - When the first texture died while another was still used, the set was empty but the name stayed. unique_textures then raised "Empty set in unique textures", so resize(), which runs when the atlas fills up, failed and left the atlas half-resized. - The finalizer only removed the name if the set was already empty. If it ran before the set's own weak reference callback, the name stayed after its slot was freed, and the next texture with that name raised "Texture '...' not found in UVData", as in an occasional CI failure. Every texture sharing a name is now added to the set, the name is removed whenever the reference count reaches 0, and unique_textures skips a set whose last texture is dying. Co-Authored-By: Claude Opus 5.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.
Summary
Fixes two
DefaultTextureAtlasbugs with textures that share an image. One of them is likely behind an occasional CI failure: PR #2952's first run failed on Python 3.12, and the rerun passed.Background
Separate textures with the same image and vertex order share an atlas name and slot. For example, every
SpriteSolidColorsize shares one white image. The atlas counts the textures using each name, and frees the slot from a finalizer when the count reaches 0.It kept only the first texture for each name in
_unique_textures[name], though, so that set could disagree with the count. That causes two bugs.Bug 1:
resize()raises (reproduced every time)unique_texturesraisesRuntimeError: Empty set in unique textures, soresize()fails. Resizing happens whenever the atlas fills up. It also fails partway through, after the atlas's size and texture have already changed.SpriteSolidColorto an atlas, free the first, then resize. On development that raises every time.Bug 2: a freed name is left behind (the CI failure)
Texture '...' not found in UVData. That's the CI error.addraises exactly that message. I couldn't recreate the real sequence that led to it on CI, even with Python 3.12 and pymunk, many runs, very frequent garbage collection, and a randomized stress test of creating, freeing, rebuilding and resizing. This fix doesn't depend on that sequence.The fix
unique_texturesskips a set whose last texture is dying, instead of raising.Textures sharing a name share one slot, so
resize()updating one live texture per name updates them all.Tests
New
tests/unit/atlas/test_shared_textures.py:Empty set in unique textures.Full suite: 1814 passed on Python 3.13, and 1814 passed on 3.12 with pymunk. The stress test finds no stale entries. ruff, mypy and pyright all pass.
The changelog has an entry under Unreleased → Fixes.
🤖 Generated with Claude Code