mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-10-04 06:15:09 +00:00
fix(display): end the scroll state at scroller-to-static handovers (#716)
A static plugin screen that follows a scroller no longer starts with the ticker's lagging rows on scan-compensated panels, and the 1 Hz loop's second frame is no longer recorded as a ~1 s mid-scroll freeze / Render stall. The display controller calls DisplayManager.end_scroll_for_static_screen() before a static screen's first display() (clears the scan history; _scan_segments passes its frames through in one swap) and set_scrolling_state(False) after it; the scroller's hold stays until then, so late-frame counts are unchanged. A screen's first frame is tagged 'handover': gaps of 250 ms or more before it go to the additive handover_freezes (frame_soak prints 'Handover gaps'), not freezes. The display thread is named display-<plugin id>. The WiFi notice and the schedule-off blank are not covered yet (docs list them as a follow-up). ledpi A B B A soak (20 min each, --preview): main 0.118% / 0.113% late with 6 / 3 freezes; with this and #717 0.107% / 0.104% late, 0 freezes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+14
-1
@@ -27,9 +27,16 @@ What the numbers mean
|
||||
late frames frames that reached the panel one or more refreshes after they
|
||||
were due -- the panel showed the previous frame again, which on
|
||||
a moving strip is a visible hitch. This is the pass/fail number.
|
||||
freezes gaps of 250ms+ inside a scroll: recomposes, plugin handovers,
|
||||
freezes gaps of 250ms+ inside a scroll: recomposes, plugin handovers
|
||||
the display controller does not tag (see handover gaps),
|
||||
blocking calls on the render thread. Reported, not failed on,
|
||||
since some are handovers between plugins rather than faults.
|
||||
handover gaps the same length of gap where the display controller had just
|
||||
started a screen's turn (also the same mode's again): its
|
||||
first display() drawing. Counted here instead of under
|
||||
freezes. Stats from a service older than this count have no
|
||||
such line, and their freezes include these, so do not
|
||||
compare freeze counts across that change.
|
||||
blit copying the frame into the matrix canvas (rgbmatrix SetImage).
|
||||
Grows with width x height x pwm_bits.
|
||||
wait blocked in SwapOnVSync, i.e. slack before the refresh.
|
||||
@@ -185,6 +192,9 @@ def build_report(before, after, preview: bool) -> Dict[str, Any]:
|
||||
"freezes": totals["freezes"],
|
||||
"freezes_per_hour": round(totals["freezes"] / hours, 1) if hours else None,
|
||||
"freeze_seconds": round(totals["freeze_seconds"], 2),
|
||||
# None from a service that predates the count: its handovers are
|
||||
# among the freezes above.
|
||||
"handover_freezes": totals.get("handover_freezes"),
|
||||
"worst_interval_ms": (round(totals["worst_interval_ms"], 1)
|
||||
if totals["worst_interval_ms"] else None),
|
||||
"timing_ms": {name: percentiles(h, bucket_ms)
|
||||
@@ -240,6 +250,9 @@ def print_report(report: Dict[str, Any], limit: float) -> None:
|
||||
if report["freezes"]:
|
||||
print(" by length: " + ", ".join(
|
||||
f"{k}: {v}" for k, v in report["freeze_by"].items()))
|
||||
if report.get("handover_freezes") is not None:
|
||||
print(f"Handover gaps {report['handover_freezes']}"
|
||||
" >=250ms before a new screen's first frame; not in the freezes")
|
||||
print()
|
||||
print(f"{'ms':<18}{'p50':>8}{'p95':>8}{'p99':>8}{'max':>8}")
|
||||
for name in ("blit", "wait", "work", "interval_per_hold"):
|
||||
|
||||
Reference in New Issue
Block a user