From b8188baeafbbc364d01705064d31e2136793c42f Mon Sep 17 00:00:00 2001 From: sonninnos Date: Sat, 14 Aug 2021 03:34:34 +0300 Subject: [PATCH] Automatic refresh rate switch where available --- gfx/common/win32_common.c | 5 +- gfx/display_servers/dispserv_win32.c | 2 +- gfx/video_display_server.h | 6 ++ retroarch.c | 121 ++++++++++++++++++++++++++- retroarch_data.h | 1 + 5 files changed, 130 insertions(+), 5 deletions(-) diff --git a/gfx/common/win32_common.c b/gfx/common/win32_common.c index 4e180fb1fa..3e99750e55 100644 --- a/gfx/common/win32_common.c +++ b/gfx/common/win32_common.c @@ -1905,8 +1905,7 @@ void win32_set_style(MONITORINFOEX *current_mon, HMONITOR *hm_to_use, * for black frame insertion using video_refresh_rate set to a portion * of the display refresh rate, as well as higher vsync swap intervals. */ float refresh_mod = bfi + 1.0f; - unsigned refresh = roundf(video_refresh * refresh_mod - * swap_interval); + float refresh_rate = (video_refresh * refresh_mod * swap_interval); if (windowed_full) { @@ -1919,7 +1918,7 @@ void win32_set_style(MONITORINFOEX *current_mon, HMONITOR *hm_to_use, *style = WS_POPUP | WS_VISIBLE; if (!win32_monitor_set_fullscreen(*width, *height, - refresh, current_mon->szDevice)) { } + (int)refresh_rate, current_mon->szDevice)) { } /* Display settings might have changed, get new coordinates. */ GetMonitorInfo(*hm_to_use, (LPMONITORINFO)current_mon); diff --git a/gfx/display_servers/dispserv_win32.c b/gfx/display_servers/dispserv_win32.c index 9a6a24c643..896a1fd89e 100644 --- a/gfx/display_servers/dispserv_win32.c +++ b/gfx/display_servers/dispserv_win32.c @@ -239,7 +239,7 @@ static bool win32_display_server_set_resolution(void *data, if (serv->orig_height == 0) serv->orig_height = GetSystemMetrics(SM_CYSCREEN); if (serv->orig_refresh == 0) - serv->orig_refresh = dm.dmDisplayFrequency; + serv->orig_refresh = video_driver_get_refresh_rate(); /* Used to stop super resolution bug */ if (width == dm.dmPelsWidth) diff --git a/gfx/video_display_server.h b/gfx/video_display_server.h index 8d623782d6..53f403abe0 100644 --- a/gfx/video_display_server.h +++ b/gfx/video_display_server.h @@ -87,6 +87,12 @@ bool video_display_server_can_set_screen_orientation(void); bool video_display_server_has_resolution_list(void); +bool video_display_server_set_refresh_rate(float hz); + +bool video_display_server_has_refresh_rate(float hz); + +void video_display_server_restore_refresh_rate(void); + enum rotation video_display_server_get_screen_orientation(void); extern const video_display_server_t dispserv_win32; diff --git a/retroarch.c b/retroarch.c index ab09ee63e3..da7e88c367 100644 --- a/retroarch.c +++ b/retroarch.c @@ -12227,6 +12227,11 @@ static void command_event_deinit_core( uninit_libretro_symbols(p_rarch, &p_rarch->current_core); p_rarch->current_core.symbols_inited = false; + /* Restore original refresh rate, if it has been changed + * automatically in SET_SYSTEM_AV_INFO */ + if (p_rarch->video_refresh_rate_original) + video_display_server_restore_refresh_rate(); + if (reinit) driver_uninit(p_rarch, DRIVERS_CMD_ALL); @@ -17464,13 +17469,67 @@ static bool rarch_environment_cb(unsigned cmd, void *data) if (data) { settings_t *settings = p_rarch->configuration_settings; + float refresh_rate = (*info)->timing.fps; unsigned crt_switch_resolution = settings->uints.crt_switch_resolution; bool video_fullscreen = settings->bools.video_fullscreen; - const bool no_video_reinit = ( + bool video_has_resolution_list = video_display_server_has_resolution_list(); + bool video_switch_refresh_rate = false; + bool no_video_reinit = true; + + /* Refresh rate switch for regular displays */ + if (video_has_resolution_list) + { + float refresh_mod = 0.0f; + float video_refresh_rate = settings->floats.video_refresh_rate; + unsigned video_swap_interval = settings->uints.video_swap_interval; + unsigned video_bfi = settings->uints.video_black_frame_insertion; + bool video_windowed_full = settings->bools.video_windowed_fullscreen; + bool vrr_runloop_enable = settings->bools.vrr_runloop_enable; + + /* Roundings to PAL & NTSC standards */ + refresh_rate = (refresh_rate > 54 && refresh_rate < 60) ? 59.94f : refresh_rate; + refresh_rate = (refresh_rate > 49 && refresh_rate < 55) ? 50.00f : refresh_rate; + + /* Black frame insertion + swap interval multiplier */ + refresh_mod = video_bfi + 1.0f; + refresh_rate = (refresh_rate * refresh_mod * video_swap_interval); + + /* Fallback when target refresh rate is not exposed */ + if (!video_display_server_has_refresh_rate(refresh_rate)) + refresh_rate = (60.0f * refresh_mod * video_swap_interval); + + /* Store original refresh rate on automatic change, and + * restore it in deinit_core and main_quit, because not all + * cores announce refresh rate via SET_SYSTEM_AV_INFO */ + if (!p_rarch->video_refresh_rate_original) + p_rarch->video_refresh_rate_original = video_refresh_rate; + + /* Try to switch display rate when: + * - Not already at correct rate + * - In exclusive fullscreen + * - 'CRT SwitchRes' OFF & 'Sync to Exact Content Framerate' OFF + */ + video_switch_refresh_rate = ( + refresh_rate != video_refresh_rate && + !crt_switch_resolution && !vrr_runloop_enable && + video_fullscreen && !video_windowed_full); + } + + no_video_reinit = ( crt_switch_resolution == 0 + && video_switch_refresh_rate == false && data && ((*info)->geometry.max_width == av_info->geometry.max_width) && ((*info)->geometry.max_height == av_info->geometry.max_height)); + + /* First set new refresh rate and display rate, then after REINIT do + * another display rate change to make sure the change stays */ + if (video_switch_refresh_rate) + { + video_monitor_set_refresh_rate(refresh_rate); + video_display_server_set_refresh_rate(refresh_rate); + } + /* When not doing video reinit, we also must not do input and menu * reinit, otherwise the input driver crashes and the menu gets * corrupted. */ @@ -17489,6 +17548,9 @@ static bool rarch_environment_cb(unsigned cmd, void *data) if (no_video_reinit) video_driver_set_aspect_ratio(); + if (video_switch_refresh_rate) + video_display_server_set_refresh_rate(refresh_rate); + /* Cannot continue recording with different parameters. * Take the easiest route out and just restart the recording. */ if (p_rarch->recording_data) @@ -29454,6 +29516,58 @@ void *video_display_server_get_resolution_list(unsigned *size) return NULL; } +bool video_display_server_has_refresh_rate(float hz) +{ + unsigned i, size = 0; + bool rate_exists = false; + + struct video_display_config *video_list = (struct video_display_config*) + video_display_server_get_resolution_list(&size); + + if (video_list) + { + struct rarch_state *p_rarch = &rarch_st; + unsigned video_driver_width = p_rarch->video_driver_width; + unsigned video_driver_height = p_rarch->video_driver_height; + + for (i = 0; i < size && !rate_exists; i++) + { + if (video_list[i].width == video_driver_width && + video_list[i].height == video_driver_height && + video_list[i].refreshrate == floor(hz)) + rate_exists = true; + } + + free(video_list); + } + + return rate_exists; +} + +bool video_display_server_set_refresh_rate(float hz) +{ + struct rarch_state *p_rarch = &rarch_st; + if (current_display_server && current_display_server->set_resolution) + return current_display_server->set_resolution( + p_rarch->current_display_server_data, 0, 0, (int)hz, + hz, 0, 0, 0, 0); + return false; +} + +void video_display_server_restore_refresh_rate(void) +{ + struct rarch_state *p_rarch = &rarch_st; + settings_t *settings = p_rarch->configuration_settings; + float refresh_rate_original = p_rarch->video_refresh_rate_original; + float refresh_rate_current = settings->floats.video_refresh_rate; + + if (!refresh_rate_original || refresh_rate_current == refresh_rate_original) + return; + + video_monitor_set_refresh_rate(refresh_rate_original); + video_display_server_set_refresh_rate(refresh_rate_original); +} + const char *video_display_server_get_output_options(void) { struct rarch_state *p_rarch = &rarch_st; @@ -36790,6 +36904,11 @@ bool retroarch_main_quit(void) discord_is_inited = false; #endif + /* Restore original refresh rate, if it has been changed + * automatically in SET_SYSTEM_AV_INFO */ + if (p_rarch->video_refresh_rate_original) + video_display_server_restore_refresh_rate(); + if (!runloop_state.shutdown_initiated) { command_event_save_auto_state( diff --git a/retroarch_data.h b/retroarch_data.h index 893ba26bc5..e29c988a1d 100644 --- a/retroarch_data.h +++ b/retroarch_data.h @@ -2069,6 +2069,7 @@ struct rarch_state float *audio_driver_input_data; float video_driver_core_hz; float video_driver_aspect_ratio; + float video_refresh_rate_original; #ifdef HAVE_AUDIOMIXER float audio_driver_mixer_volume_gain;