Turing USB: coalesce widget updates into rate-limited full-frame sends (~340% -> ~5% CPU) - #1049
Open
davidfdr wants to merge 1 commit into
Open
Turing USB: coalesce widget updates into rate-limited full-frame sends (~340% -> ~5% CPU)#1049davidfdr wants to merge 1 commit into
davidfdr wants to merge 1 commit into
Conversation
The Turing USB protocol only supports full-screen image uploads, so every widget refresh (each text value, bar, ...) encoded and sent the whole framebuffer. With a typical theme refreshing ~20 widgets per second, and PNG compress_level=9 costing ~540 ms CPU per 1920x480 photographic frame, the monitor pinned 3-4 CPU cores. Instead of sending from every DisplayPILImage call, only paste the widget into current_state (under a lock) and mark the frame dirty. A dedicated daemon thread sends the composed frame at most every 0.5 s. A device lock serializes USB access, which was previously unsynchronized across the stat refresh threads. Also encode streamed frames with PNG compress_level=1 (~25 ms, still lossless). Frames must stay RGBA: the device firmware does not render RGB PNGs correctly. On an 8-core Ryzen with the 9.2" model this drops CPU usage from ~340% to ~5% of one core with no visible change on the display. Co-Authored-By: Claude Fable 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.
Problem
The Turing USB protocol (
REVISION: TUR_USB, HW rev 1.x models incl. 8.8"/9.2") only supports full-screen image uploads — there is no partial-update command.LcdCommTuringUSB.DisplayPILImage()therefore encoded and sent the entire framebuffer once per widget refresh. With a typical theme refreshing ~20 widgets per second, and_encode_png()usingcompress_level=9(~540 ms CPU per 1920x480 photographic frame), the monitor pinned 3-4 CPU cores on an 8-core Ryzen (9.2" model, theme with photo background).Profiled with py-spy: virtually all samples were in
ImageFile._encode_tileunderDisplayPILImage, spread across the stat refresh threads.Fix
DisplayPILImage()now only pastes the widget intocurrent_state(under a lock) and marks the frame dirty — no encode/send per widget.TuringUSB_Sender) sends the composed frame at most every 0.5 s, so any number of widget updates coalesce into <= 2 uploads/sec. Visually nothing changes (stat intervals are >= 1 s).compress_level=1(~25 ms, still lossless, still well under the 1 MiB payload limit).upload_file()storage uploads keep level 9.Measurements (9.2" model, 1920x480 theme with photographic background)
compress_level=1Encode benchmark for one full RGBA frame: level 9 = 538 ms, level 1 = 25 ms (625 KiB).
🤖 Generated with Claude Code