perf(display): skip preview work nobody reads (lazy checksum, 1 Hz snapshot writer) (#717)

Mid-scroll, update_display() no longer checksums every frame: it asks is_currently_scrolling() once per frame and reuses the answer, hashes only when dirty tracking can skip a static frame, and the preview snapshot asks its policy first and hashes only when a write or touch could follow (decide() is monotone, pinned by a property test). With the preview open the snapshot is written at most once a second (VIEWER_INTERVAL 1.0 s, was 0.2 s; the SSE stream re-read it once a second, so four encodes in five went unread); the stream now polls its mtime every 0.25 s (VIEWER_POLL_INTERVAL), so the preview stays about as fresh. The PNG is written at compress_level=1. --preview soaks are not comparable across this change.

Merged with #716: _scan_segments takes the frame's one scrolling answer and #716's static-handover pass-through, as soaked on ledpi (A B B A, 20 min each: main 0.118% / 0.113% late, with #716 and this 0.107% / 0.104%).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Chuck
2026-10-01 20:27:20 -04:00
committed by GitHub
co-authored by Claude Opus 5.5
parent 1ecf3aba03
commit f72d69c2b0
13 changed files with 804 additions and 47 deletions
+57 -6
View File
@@ -151,12 +151,58 @@ class TestTheFirstStaticFrame:
[(_blit, _wait, hold, scrolling, _at)] = records
assert (hold, scrolling) == (3, True)
def test_it_is_decided_on_the_frames_one_answer_and_not_hashed(
self, dm, monkeypatch, tmp_path):
# The handover frame goes through update_display like any frame while
# the scroll state is set: it asks is_currently_scrolling() once, takes
# no digest, and that answer and its hold reach the swap and the
# record -- while the frame itself goes out as drawn.
import time
import zlib
import src.display_manager as display_manager_module
dm._scan_lag_bands = [(24, 48, 1)]
# No preview and no snapshot due: only the frame itself asks or hashes.
dm._viewer_marker_path = str(tmp_path / "no-viewer")
dm._viewer_check_ts = 0.0
dm.set_scrolling_state(True, 2)
_push(dm, (255, 0, 0)) # the ticker's last frame
dm._last_snapshot_ts = dm._last_snapshot_touch_ts = time.time()
asked, hashed, records = [], [], []
real_asked = dm.is_currently_scrolling
def asking():
asked.append(1)
return real_asked()
def hashing(data, *args):
hashed.append(len(data))
return zlib.adler32(data, *args)
real_record = dm.frame_timing.record
def record(*args, **kwargs):
records.append(args[2:4])
return real_record(*args, **kwargs)
monkeypatch.setattr(dm, "is_currently_scrolling", asking)
monkeypatch.setattr(display_manager_module, "zlib",
types.SimpleNamespace(adler32=hashing))
monkeypatch.setattr(dm.frame_timing, "record", record)
holds = _watch_swaps(dm)
before = len(dm._presented)
dm._last_blit_seconds = 0.0 # fast enough to split
dm.end_scroll_for_static_screen()
_push(dm, (0, 0, 0))
assert len(asked) == 1
assert hashed == []
assert holds == [2]
assert records == [(2, True)]
assert len(dm._presented) - before == 1
assert dm._presented[-1].getpixel((10, 30)) == (0, 0, 0)
def test_nor_its_own_first_frame_under_its_second(self, dm):
# A first display() that pushes two frames: a clear, then the screen.
# Composed, the second would show the first's rows -- and once the
# scroll is over, dirty tracking (which compares frames as drawn, not
# as composed) skips every identical 1 Hz redraw, so that half-black
# frame would stay up for the whole turn.
# Composed, the second would show the first's rows: a half-black frame
# up for the whole second until the 1 Hz loop's next redraw.
dm._scan_lag_bands = [(24, 48, 1)]
dm.set_scrolling_state(True, 1)
_push(dm, (255, 0, 0)) # the ticker's last frame
@@ -166,8 +212,13 @@ class TestTheFirstStaticFrame:
assert shown.getpixel((10, 30)) == (0, 0, 255)
dm.set_scrolling_state(False) # the controller, after the dispatch
pushed = len(dm._presented)
_push(dm, (0, 0, 255)) # the 1 Hz redraw: skipped
assert len(dm._presented) == pushed
# The 1 Hz redraw is pushed once, as drawn: a frame pushed while the
# scroll state was set leaves dirty tracking no digest to match. The
# next identical one is skipped.
assert _push(dm, (0, 0, 255)).getpixel((10, 30)) == (0, 0, 255)
assert len(dm._presented) == pushed + 1
_push(dm, (0, 0, 255))
assert len(dm._presented) == pushed + 1
def test_the_next_scroll_is_compensated_again(self, dm):
dm._scan_lag_bands = [(24, 48, 1)]