mirror of
https://github.com/libretro/RetroArch.git
synced 2026-11-08 08:10:30 -10:00
retroarch: refresh the dummy core's advertised fps on a refresh rate change
Menu BGM (Menu Sounds -> Enable BGM Sound) garbles the moment the
refresh rate is changed from Settings -> Video -> Output -> Screen
Resolution, e.g. 1320x2868 (120 Hz) -> 1320x2868 (60 Hz), and stays
garbled.
With no content loaded the running core is the dummy core. Its
libretro_dummy_retro_get_system_av_info() reports
info->timing.fps = <GET_TARGET_REFRESH_RATE>
info->timing.sample_rate = 48000.0
so timing.fps is the display rate as it stood when the core was
initialised. Nothing re-queries the core when the display rate
changes afterwards, so timing.fps is stale from that point on.
audio_driver_menu_sample() emits
floor(timing.sample_rate / timing.fps) * 2
int16s per runloop iteration, and the runloop is driven at the real
display rate. The menu audio feed is therefore scaled by
(real rate / stale fps). At 120 Hz with fps 120 that is 400 frames
per iteration * 120 iterations = 48000 frames/s, which is correct.
After the switch the runloop drops to 60 iterations/s while fps is
still 120, giving 24000 frames/s - half. audio_driver_flush()
advances the mixer by the post-resample output frame count via
audio_mixer_mix(), so the mixer starves at half rate and the BGM
garbles.
This is not Apple-specific. Any platform whose display server
honours a refresh change from that dropdown hits it; it is simply
most audible at 2x, and iOS reaches it easily now that ProMotion
reports 120 Hz.
2089f43b8d routed the dropdown through
RARCH_DRIVER_CTL_SET_REFRESH_RATE so the resampler ratio and
driver_adjust_system_rates() would be re-run. That was necessary but
not sufficient: driver_adjust_system_rates() computes audio_st->input
from input_fps = av_info.timing.fps, so feeding it a stale fps just
re-derives the same wrong answer. The stale value has to be
corrected before it is consumed.
Update timing.fps to the new rate in the SET_REFRESH_RATE case, ahead
of driver_adjust_system_rates(). Doing it here rather than in
menu_cbs_ok.c covers every path that changes the refresh rate
(VIDEO_REFRESH_RATE, _AUTO, _POLLED and the resolution dropdown), all
of which already funnel through driver_ctl().
Gated on current_core_type == CORE_TYPE_DUMMY: a real core's fps
describes the emulated system, not the display, and must never be
overwritten. Also gated on a positive rate so a 0 Hz answer from a
display server cannot poison av_info. There is precedent for writing
timing.fps from this side - video_display_server_restore_refresh_rate()
already does exactly this when restoring the original mode.
Compile-tested on a Linux build (retroarch.c is platform-neutral);
the audible behaviour still wants an on-device check.
This commit is contained in:
1 parent
e3150f2fb8
commit
b17daaf0f6
1 file changed
+27
+27
@@ -2072,6 +2072,33 @@ bool driver_ctl(enum driver_ctl_state state, void *data)
|
||||
|
||||
video_monitor_set_refresh_rate(*hz);
|
||||
|
||||
/* With no content loaded it is the dummy core that is
|
||||
* running, and its retro_get_system_av_info() reports
|
||||
* timing.fps as whatever GET_TARGET_REFRESH_RATE answered
|
||||
* at core init - that is, the display rate. Nothing
|
||||
* re-queries it when the display rate changes afterwards,
|
||||
* so it goes stale the moment the user switches mode.
|
||||
*
|
||||
* That matters because audio_driver_menu_sample() emits
|
||||
* timing.sample_rate / timing.fps frames per runloop
|
||||
* iteration, while the runloop is driven at the *actual*
|
||||
* display rate. A stale fps scales the menu audio feed by
|
||||
* (real rate / stale fps): after a 120 Hz -> 60 Hz switch
|
||||
* it is halved, so the mixer - which audio_driver_flush()
|
||||
* advances by the post-resample output frame count -
|
||||
* starves and menu BGM garbles.
|
||||
*
|
||||
* driver_adjust_system_rates() below cannot repair this,
|
||||
* because it derives audio_st->input from this very
|
||||
* timing.fps and so re-derives the same wrong answer.
|
||||
*
|
||||
* Only the dummy core gets this treatment: a real core's
|
||||
* fps is a property of the emulated system, not of the
|
||||
* display, and must never be overwritten here. */
|
||||
if ( *hz > 0.0f
|
||||
&& runloop_st->current_core_type == CORE_TYPE_DUMMY)
|
||||
video_st->av_info.timing.fps = *hz;
|
||||
|
||||
/* Sets audio monitor rate to new value. */
|
||||
audio_st->src_ratio_orig =
|
||||
audio_st->src_ratio_curr =
|
||||
|
||||
Reference in new issue
Block a user