mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-10-05 23:05:10 +00:00
perf(scroll): build the strip's PIL image only when something reads it (#695)
* perf(timing): say which render-thread work a late frame followed The soak already says how often a moving frame reached the panel late, but not what the render thread was doing just before it. Vegas does two kinds of work there between frames -- building its strip (compose, extend) and, with live elements, patching changed pixels into it -- and deciding whether either is affordable needs their own numbers. - FrameTimingRecorder.note_op(kind, nbytes) tags the next presented frame. Totals gain op_frames, late_op_frames, op_freezes and op_bytes per kind; aggregate() still takes frames without ops. The file schema is unchanged. - Vegas tags compose and every strip extension (with the bytes it copied). - frame_soak prints an "after work" table: frames, late %, freezes and MB moved per kind, only when something tagged its work. - render_bench gains --strip-screens (Vegas-sized strips), --patch-bytes / --patch-every / --patch-where (in-place column writes, as a live element update does) and --extend-every-screens / --extend-width (append + trim on a fixed cadence that holds the strip's width). No runtime behaviour changes: this is the measurement gate for live Vegas elements. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(changelog): note the frame-op attribution and bench modes Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * perf(scroll): build the strip's PIL image only when something reads it Every Vegas strip extension rebuilt ScrollHelper.cached_image from cached_array in full, twice (append, then trim), on the render thread: Image.fromarray is 1.7ms for an 8,000px strip and 3.8ms for 20,000px on a Pi 4 (measured on ledpi), about two thirds of an extension's render-thread cost. Nothing on the frame path reads the image's pixels; every frame is cut from the array. cached_image is now a property. append_content and drop_scrolled_prefix defer it; the first read builds it from the array it started with and keeps it only if the strip has not changed meanwhile, so a sync push racing an extension cannot leave a stale image cached. Assigning cached_image stores exactly what was assigned, as before. has_strip() says whether there is a strip without building its image; the helper's frame path, Vegas and the adapter's scroll-cache invalidation use it. The strip is also no longer held in memory twice. In Vegas the image is now built only by a multi-display sync push. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -332,6 +332,20 @@ read any of them:
|
||||
worker, which applies the latest one as soon as the lock frees, and before
|
||||
the plugin's next update() at the latest. The plugin API is unchanged.
|
||||
|
||||
### Scrolling
|
||||
|
||||
- A Vegas strip extension costs the render thread about a third of what it
|
||||
did. Appending the next group and trimming what has scrolled past each
|
||||
rebuilt the strip's PIL image from its numpy array in full
|
||||
(`Image.fromarray`: 1.7ms for an 8,000px strip, 3.8ms for 20,000px, on a
|
||||
Pi 4 -- twice per extension), though every frame is cut from the array and
|
||||
nothing on the frame path reads the image's pixels. `ScrollHelper` now
|
||||
builds `cached_image` only when something reads it, which in Vegas means
|
||||
only a multi-display sync push, and the strip is no longer held in memory
|
||||
twice. Assigning `cached_image` still stores exactly what was assigned.
|
||||
New `ScrollHelper.has_strip()` says whether there is a strip without
|
||||
building its image; the frame path and Vegas use it.
|
||||
|
||||
### Tooling
|
||||
|
||||
- The frame-timing recorder says which render-thread work a late frame
|
||||
|
||||
Reference in New Issue
Block a user