Conversation
draw_text reuses arcade.Text labels from ctx.label_cache: - The cache key ignored multiline, so a multiline call and an otherwise equal single-line call shared a label and one drew wrong. The key is now a tuple (a concatenated string could also collide, e.g. font size 1 with font "2arial" and size 12 with "arial") and includes multiline. - rotation was in the key although it's updated on reused labels, so animating it added a label every frame, forever. It's no longer in the key. - The cache had no limit. It's now an OrderedDict of at most 256 labels, forgetting the least recently used. - Remove ctx.flush() after every call. Its comment said it stopped a reused label's buffers changing while an earlier draw used them, but glFlush doesn't wait for anything, and pyglet's default buffers are updated with glBufferSubData, which OpenGL keeps in order. Drawing one reused label six times a frame with different text still matches separate Text objects pixel for pixel. Unchanged calls: 76 -> 15 us. - Labels are now found by style and text, keeping up to 8 per style, so several lines in the same style each keep their own label instead of one label being re-laid out for every call. When a style has 8, the least recently used is reused, so a line whose text keeps changing reuses its own old label. 5 static HUD lines: 1.24 -> 0.08 ms/frame. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This branch has not been deployed
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
PR 2 of the text improvements: fixes and speeds up the label cache behind
arcade.draw_text(), which reusesarcade.Textobjects fromctx.label_cache.Bugs
multiline. A multiline call and an otherwise equal single-line call shared a label, so one drew wrong(10,477,258,511)instead of(10,497,325,511))rotationwas in the key, even though it's updated on reused labels anywayfont_sizegrew the cache foreverThe key is now a tuple. The old concatenated string could also collide, for example font size
1with font"2arial"and size12with"arial".Speed
1. Remove
ctx.flush()after every call. Its comment said it stops a reused label's vertex buffers from changing while an earlier draw of that label is still using them. But:glFlushonly submits commands; it doesn't wait for anything, so it can't prevent that.GLAttributeBufferObject) are updated withglBufferSubData, which OpenGL applies in command order.pyglet.options.opengl_persistent_buffers) are off by default, and wouldn't be protected byglFlusheither.I tested exactly the case the comment describes: one cached label drawn six times in a frame with different text and positions. It matches six separate
Textobjects pixel for pixel, with and without the flush. There's also a test for this.2. One label per line, per style. Labels are now found by style and text, with up to 8 per style. So several
draw_textcalls in the same style each keep their own label, instead of one label being re-laid out for every call, every frame. When a style already has 8 labels, the least recently used one is reused. Lines that don't change are used every frame and stay recent, so a line whose text keeps changing (a timer, say) reuses its own old label, not another line's.µs per call or per frame, pyglet
debug_apioff:draw_textcallThe single changing line is about the same: it still re-lays out every frame, and finding the label to reuse is cheap.
Trade-offs
Tests
New
tests/unit/text/test_draw_text_cache.py:test_multiline_has_its_own_labeltest_rotation_reuses_label: 100 rotations, 1 label, with the latest angle.test_cache_size_is_limited: with the limit set to 5 for speed, since each new font size costs about 30 ms to load in pyglet. The oldest labels are forgotten, and using a label keeps it.test_same_style_lines_keep_their_labels: the static HUD lines keep the same label objects over 30 frames, and a changing line doesn't add a label every frame.test_reused_label_draws_correctly: ten texts drawn withdraw_textmatch tenTextobjects pixel for pixel.On
development, the first four fail and the pixel test passes; the old code drew correctly, it was just slow.Full suite on pyglet 3.0.dev11: 1489 passed, with the same 3 render tests failing on my machine only. Ruff is clean. mypy reports one error at
text.py:461, theLinearGradientcolor getter, which is the same ondevelopmentand will be fixed in the next text PR.The changelog has entries under Unreleased: the bugs in Fixes, the speed-ups in Misc Changes.
🤖 Generated with Claude Code