Skip to content

Keep create_text_sprite text through texture atlas rebuilds - #2918

Open
pvcraven wants to merge 1 commit into
developmentfrom
fix/text-sprite-atlas-rebuild
Open

pvcraven wants to merge 1 commit into
developmentfrom
fix/text-sprite-atlas-rebuild

Conversation

@pvcraven

@pvcraven pvcraven commented Oct 5, 2026

Copy link
Copy Markdown
Member

Problem

create_text_sprite sprites go blank when the texture atlas rebuilds itself.

create_text_sprite makes an empty texture (Texture.create_empty) and draws the text straight into the atlas with render_into. The texture's own image stays blank. When the atlas runs out of room and some images have been freed, DefaultTextureAtlas._add calls rebuild() instead of growing. rebuild() clears the atlas and redraws every texture from its image, so every text sprite becomes a blank image. Growing the atlas with resize() copies pixels on the GPU, so it isn't affected, which is why this only shows up sometimes.

Reproduced directly: a 20pt red "Hello World" sprite has 155,453 total alpha in the atlas before atlas.rebuild(), and 0 after.

This is why #2917's CI fails. test_same_text_different_style (added in #2915) fails on Python 3.10, 3.12 and 3.13 there: the red sprite reads back blank after the blue one is created. In that run, an earlier test left the shared atlas full enough that adding the blue sprite triggered a rebuild. #2917 changes which examples run and how often, which changes how full the atlas is by then. I reproduced exactly that sequence with a small atlas: a full atlas with some images freed, then the red sprite, then the blue one, which rebuilds the atlas. The red sprite is blank without this fix and unchanged with it.

Fix

After drawing, create_text_sprite calls texture_atlas.update_texture_image_from_atlas(texture), the atlas method meant for "render into the atlas and need to update the texture with the new pixel data". The texture's image now holds the text, so a rebuild redraws it correctly. As a bonus, sprite.texture.image now contains the text, for code that reads it.

Cost: the GPU read adds about 0.2–0.6 ms per create_text_sprite call, measured 2 × alternating:

Before After
"Score: 100", 16pt ~1.3 ms ~1.5–1.7 ms
a long 40pt title ~1.6 ms ~2.2 ms

create_text_sprite is called once per sprite, not every frame, so this seemed worth it for sprites that don't randomly vanish.

Tests

  • New test_survives_atlas_rebuild: calls atlas.rebuild() directly and checks the sprite's pixels are unchanged, and that texture.image matches the atlas. It fails without the fix, whatever state the shared atlas is in.
  • The existing flaky test: test_same_text_different_style now passes even when a rebuild happens during it.
  • Full suite on pyglet 3.0.dev11: 1485 passed. The 3 failures are the render tests that only fail on my machine.
  • Changelog: added as one more item in the unreleased create_text_sprite fixes entry.

Once this is merged, #2917 can be updated from development and its CI should pass.

🤖 Generated with Claude Code

create_text_sprite makes an empty texture and draws the text into the
atlas. When the atlas fills up after some textures were freed, it
rebuilds itself by clearing and redrawing every texture from its image,
and a text sprite's image was blank, so the sprite went blank.

Copy the drawn pixels back into the texture's image with
update_texture_image_from_atlas() after drawing. This also makes
sprite.texture.image contain the text. It adds about 0.2-0.6 ms per
create_text_sprite call for the GPU read.

This caused test_same_text_different_style to fail in CI whenever an
earlier test left the shared atlas full enough to rebuild. Add a test
that rebuilds the atlas directly.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant