Add playback performance HUD - #25
Closed
emlcpfx wants to merge 1 commit into
Closed
Conversation
View > Performance HUD (Ctrl+Alt+H) overlays measured FPS against target, frame, resolution, average decode cost and cache depth. It answers the question a reviewer actually asks when playback feels wrong: is it me, or is it the machine? A supervisor calling a note on timing needs to know they are watching 24 fps and not 17, or they are grading the playback and not the shot. playback/stats.py is pure and takes an injected clock, so the rolling averages are asserted exactly instead of being slept for. - Frame rate is measured where frames reach the viewer, which is the only honest definition of a displayed frame -- not at the playback timer, which keeps firing whether or not a frame made it. - Decode cost is timed around the decode itself. For sequences that is inside the decode thread, so the queue wait in front of it is not blamed on the decoder; for movies it covers the proxy scale, RGB conversion and OCIO, which is what stands between a packet and the screen. - A decode slower than its share of the frame budget is flagged: it cannot sustain real time however fast the rest of the pipeline is. A stall and a pause both stop frames arriving and both empty the rolling window, so measured FPS falls to zero, which is the same reading as playback never having started. Without telling those apart, a total freeze renders as a calm placeholder dash, and that is the exact failure this HUD exists to catch. stalled() tracks whether frames were ever flowing, and the window passes in whether the player is actually running, so a freeze reads STALLED in red while a pause stays quiet. Only a genuinely bad row is coloured, so the eye goes to the thing that is actually wrong rather than a wall of red. 26 tests. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TexnzYbmCjjTDB8zzZuUPb
Owner
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.
View > Performance HUD(Ctrl+Alt+H) overlays measured FPS against target, frame, resolution, average decode cost and cache depth.It answers the question a reviewer actually asks when playback feels wrong: is it me, or is it the machine? A supervisor calling a note on timing needs to know they're watching 24 fps and not 17 — otherwise they're grading the playback, not the shot.
playback/stats.pyis pure and takes an injected clock, so the rolling averages are asserted exactly rather than slept for.Measuring the right thing
The subtle one: a stall looks exactly like a pause
Both stop frames arriving. Both empty the rolling window. So measured FPS falls to zero — which is the same reading as "nothing has played yet."
Handled naively, a total freeze renders as a calm
--, which is precisely the failure this HUD exists to catch.stalled()tracks whether frames were ever flowing, and the window passes in whether the player is actually running. A freeze reads STALLED in red; a pause stays quiet.test_a_stall_is_visible_immediately_not_averaged_awayandtest_a_pause_is_not_a_stallpin both halves.I only found this because a test I'd written to assert the obvious thing failed — the first cut genuinely did report a frozen player as healthy.
Only a genuinely bad row is coloured, so the eye goes to the thing that's actually wrong rather than a wall of red.
Verification
26 new tests, 264 on the branch, green three runs in a row, compile clean.
Driven end to end through a real
MainWindowwith a real decoded clip — the HUD reports a real measured decode cost (1.3 ms for a 1080p frame), the row set is correct, measurements reset between sources, and the refresh timer starts and stops with the toggle.One behavioural note:
SequenceDecodeThread.decodedgains a fifth argument (decode milliseconds). Timing has to be taken on the decode thread; carrying it back with the frame is the only way to keep the queue wait out of the number.Branches off current
main.