mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-10-13 18:46:37 +00:00
fix(scroll): restart the stats window when an idle gap is dropped by size
#582 dropped an idle gap from the frame stats two ways: the reset_scroll() sentinel, which also restarts the 5s window timer, and a size guard for scrollers that never call reset_scroll(), which did not. On that path the first real frame after the gap found the boundary overdue and logged a stats line for a one-frame window. Both paths now share one seeding helper. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -985,11 +985,7 @@ class ScrollHelper:
|
|||||||
# the real stall rates being measured, so the number could not be
|
# the real stall rates being measured, so the number could not be
|
||||||
# trusted at all. Seed the clock and take no sample.
|
# trusted at all. Seed the clock and take no sample.
|
||||||
if self.last_frame_time is None:
|
if self.last_frame_time is None:
|
||||||
self.last_frame_time = current_time
|
self._restart_stats_window(current_time)
|
||||||
# Restart the window with the scroll. Otherwise the boundary is
|
|
||||||
# already long overdue when the second frame arrives, and the new
|
|
||||||
# scroll opens by reporting a window of exactly one frame.
|
|
||||||
self.last_fps_log_time = current_time
|
|
||||||
return
|
return
|
||||||
|
|
||||||
# Calculate instantaneous frame time
|
# Calculate instantaneous frame time
|
||||||
@@ -998,9 +994,10 @@ class ScrollHelper:
|
|||||||
# A caller that scrolls without ever calling reset_scroll() never arms
|
# A caller that scrolls without ever calling reset_scroll() never arms
|
||||||
# the sentinel above, so catch the same gap by its size. Nothing that
|
# the sentinel above, so catch the same gap by its size. Nothing that
|
||||||
# renders a scroll produces a frame longer than the log interval; a
|
# renders a scroll produces a frame longer than the log interval; a
|
||||||
# sample that large is an idle period, not a frame.
|
# sample that large is an idle period, not a frame. It starts a new
|
||||||
|
# scroll exactly as the sentinel does, window timer included.
|
||||||
if frame_time >= FPS_LOG_INTERVAL:
|
if frame_time >= FPS_LOG_INTERVAL:
|
||||||
self.last_frame_time = current_time
|
self._restart_stats_window(current_time)
|
||||||
return
|
return
|
||||||
|
|
||||||
self.frame_times.append(frame_time)
|
self.frame_times.append(frame_time)
|
||||||
@@ -1035,6 +1032,16 @@ class ScrollHelper:
|
|||||||
self.last_frame_time = current_time
|
self.last_frame_time = current_time
|
||||||
self.frame_count += 1
|
self.frame_count += 1
|
||||||
|
|
||||||
|
def _restart_stats_window(self, current_time: float) -> None:
|
||||||
|
"""Seed the frame clock at the start of a scroll, taking no sample.
|
||||||
|
|
||||||
|
The window timer restarts with it. Otherwise the 5s boundary is
|
||||||
|
already long overdue when the next frame arrives, and the new scroll
|
||||||
|
opens by reporting a window of exactly one frame.
|
||||||
|
"""
|
||||||
|
self.last_frame_time = current_time
|
||||||
|
self.last_fps_log_time = current_time
|
||||||
|
|
||||||
def clear_cache(self) -> None:
|
def clear_cache(self) -> None:
|
||||||
"""
|
"""
|
||||||
Clear the cached scrolling image.
|
Clear the cached scrolling image.
|
||||||
|
|||||||
@@ -460,6 +460,19 @@ class TestIdleGapIsNotAFrame:
|
|||||||
assert not info.called
|
assert not info.called
|
||||||
assert len(helper._window) == 1
|
assert len(helper._window) == 1
|
||||||
|
|
||||||
|
def test_a_dropped_gap_restarts_the_window_timer_too(self, helper):
|
||||||
|
"""The size-guard path is the one scrollers that never call
|
||||||
|
reset_scroll() take, so it must restart the window like the sentinel
|
||||||
|
does, or their next scroll opens with a one-frame stats line."""
|
||||||
|
helper.log_frame_rate() # seeds
|
||||||
|
helper.last_frame_time = time.time() - 137.0
|
||||||
|
helper.last_fps_log_time = 0.0 # boundary long overdue
|
||||||
|
with patch.object(helper.logger, "info") as info:
|
||||||
|
helper.log_frame_rate() # the gap, dropped
|
||||||
|
helper.log_frame_rate() # first real frame
|
||||||
|
assert not info.called, "a one-frame window was reported"
|
||||||
|
assert len(helper._window) == 1
|
||||||
|
|
||||||
def test_a_normal_frame_still_counts(self, helper):
|
def test_a_normal_frame_still_counts(self, helper):
|
||||||
helper.last_frame_time = time.time() - 0.010
|
helper.last_frame_time = time.time() - 0.010
|
||||||
helper.log_frame_rate()
|
helper.log_frame_rate()
|
||||||
|
|||||||
Reference in New Issue
Block a user