mirror of
https://github.com/libretro/RetroArch.git
synced 2026-11-09 11:41:35 -10:00
video: stop Scanline Sync latching itself off permanently
Scanline Sync stopped working ~121 frames into every session and never resumed. Instrumenting video_driver_scanline_before_frame() showed the spin in after_frame() running for the first ~31 frames and then never again - 2400 consecutive frames with zero polls, while the menu still reported the feature as on. Two faults, compounding. The disable tested "scanline_hold &&", so it fired only while a hold was already in progress, and re-armed scanline_hold to refresh_rate / 2 on every frame of the hold it had just set. The tail decrements by one, so hold sat permanently one below that value - the diagnostic reported hold 59 in every window, never anything else. It therefore never reached zero, the "Allow change" block is gated on !scanline_hold, and scanline_next stayed at 0. after_frame() computes scanline_target = 0 from that and takes its "scanline_target <= 0 -> wait = false" exit, so nothing ever waited again. The way in was frame_time_deviation, which compares frame_time_samples[frame_time_count - 1] against frame_time_target * 0.33f. That ring starts zeroed, and under video_frame_time_sample_gated a sample failing the clean predicate is not written and the count is not advanced, so the read can land on a slot that was never populated. Zero satisfies the comparison, so an unwritten sample reads as a permanent deviation. The diagnostic confirmed it: the disable fired on 120 of 120 frames while core_run_time exceeded its threshold on only 1. Decide while the sync is running (!scanline_hold) so the countdown completes uninterrupted, and gate the deviation test on frame_time > 0. The two severities the old code intended but could not reach are now distinguished: a core structurally over budget takes the long hold and surrenders the target, a transient deviation takes the short one and keeps it. Verified on a 4K120 display: hold now shows 0..59 rather than pinned at 59, tune fires, scanline_next reaches 2156, and the spin runs again. Two follow-ups this makes visible, neither addressed here. The short "rearm" path still pre-empts the recovery on the frame hold expires, so a trickle of deviations stalls tuning for long stretches. And with the spin actually running it makes 25-41 D3DKMTGetScanLine calls per frame at ~223 us each - 5.5 to 9.2 ms against an 8.31 ms frame period.
This commit is contained in:
1 parent
d958315502
commit
0966f2f32d
1 file changed
+30
-6
+30
-6
@@ -6280,15 +6280,39 @@ VIDEO_NOINLINE static void video_driver_scanline_before_frame(video_driver_state
|
||||
uint16_t frame_time_index = video_st->frame_time_count & (MEASURE_FRAME_TIME_SAMPLES_COUNT - 1);
|
||||
uint16_t sample_index = (uint16_t)((frame_time_index - 1) & (MEASURE_FRAME_TIME_SAMPLES_COUNT - 1));
|
||||
retro_time_t frame_time = video_st->frame_time_samples[sample_index];
|
||||
bool frame_time_deviation = frame_time >= frame_time_target * 1.66f || frame_time <= frame_time_target * 0.33f;
|
||||
/* frame_time > 0 gates the comparison: the ring starts zeroed, and
|
||||
* under video_frame_time_sample_gated a sample that fails the
|
||||
* clean predicate is not written and frame_time_count is not
|
||||
* advanced, so this can read an unwritten slot. Zero satisfies
|
||||
* "<= target * 0.33" and would register as a permanent deviation. */
|
||||
bool frame_time_deviation = frame_time > 0
|
||||
&& ( frame_time >= frame_time_target * 1.66f
|
||||
|| frame_time <= frame_time_target * 0.33f);
|
||||
|
||||
if (scanline_hold && (frame_time_deviation || core_run_time >= frame_time_target - 3000))
|
||||
/* Only decide while the sync is actually running (hold == 0).
|
||||
* This used to test "scanline_hold &&", so it re-fired on every
|
||||
* frame of the hold it had just armed - hold was reset to
|
||||
* refresh_rate / 2 and decremented by one at the tail, pinning it
|
||||
* one below that value forever. It could therefore never reach
|
||||
* zero, the "Allow change" block below is gated on !scanline_hold,
|
||||
* and scanline_next stayed at 0, which makes
|
||||
* video_driver_scanline_after_frame() take its
|
||||
* "scanline_target <= 0 -> wait = false" exit every frame. Once
|
||||
* the first deviation latched it, Scanline Sync did nothing at all
|
||||
* for the rest of the session. */
|
||||
if (!scanline_hold)
|
||||
{
|
||||
scanline_next = 0;
|
||||
scanline_hold = refresh_rate / 2;
|
||||
if (core_run_time >= frame_time_target - 3000)
|
||||
{
|
||||
/* Structural: the core cannot fit in the budget. Long hold,
|
||||
* and give up the target so nothing waits meanwhile. */
|
||||
scanline_next = 0;
|
||||
scanline_hold = (int16_t)(refresh_rate / 2);
|
||||
}
|
||||
else if (frame_time_deviation)
|
||||
/* Transient: ride it out briefly, keep the target. */
|
||||
scanline_hold += 3;
|
||||
}
|
||||
else if (!scanline_hold && frame_time_deviation)
|
||||
scanline_hold += 3;
|
||||
}
|
||||
|
||||
/* Shift overflow */
|
||||
|
||||
Reference in new issue
Block a user