diff --git a/audio/audio_driver.c b/audio/audio_driver.c index 9e083a54f4..410e97586d 100644 --- a/audio/audio_driver.c +++ b/audio/audio_driver.c @@ -889,7 +889,9 @@ void audio_driver_monitor_adjust_system_rates(void) timing_skew = fabs(1.0f - info->fps / video_refresh_rate); audio_driver_input = info->sample_rate; - if (timing_skew <= max_timing_skew && !settings->bools.vrr_runloop_enable) + if (timing_skew <= max_timing_skew && (!settings->bools.vrr_runloop_enable || + (settings->bools.vrr_runloop_enable && settings->bools.vrr_above_range_skew && + (info->fps > video_refresh_rate)))) audio_driver_input *= (video_refresh_rate / info->fps); RARCH_LOG("[Audio]: Set audio input rate to: %.2f Hz.\n", diff --git a/config.def.h b/config.def.h index 7aeaf11dd0..12a7ae410e 100644 --- a/config.def.h +++ b/config.def.h @@ -623,6 +623,9 @@ static const float fastforward_ratio = 0.0; /* Enable runloop for variable refresh rate screens. Force x1 speed while handling fast forward too. */ static const bool vrr_runloop_enable = false; +/* Allow deviation within max_timing_skew range when the content framerate is above max refresh rate. */ +static const bool vrr_above_range_skew = false; + /* Run core logic one or more frames ahead then load the state back to reduce perceived input lag. */ static const unsigned run_ahead_frames = 1; diff --git a/configuration.c b/configuration.c index fae1a05383..7a36b2d4be 100644 --- a/configuration.c +++ b/configuration.c @@ -1292,6 +1292,7 @@ static struct config_bool_setting *populate_settings_bool(settings_t *settings, SETTING_BOOL("suspend_screensaver_enable", &settings->bools.ui_suspend_screensaver_enable, true, true, false); SETTING_BOOL("rewind_enable", &settings->bools.rewind_enable, true, rewind_enable, false); SETTING_BOOL("vrr_runloop_enable", &settings->bools.vrr_runloop_enable, true, vrr_runloop_enable, false); + SETTING_BOOL("vrr_above_range_skew", &settings->bools.vrr_above_range_skew, true, vrr_above_range_skew, false); SETTING_BOOL("apply_cheats_after_toggle", &settings->bools.apply_cheats_after_toggle, true, apply_cheats_after_toggle, false); SETTING_BOOL("apply_cheats_after_load", &settings->bools.apply_cheats_after_load, true, apply_cheats_after_load, false); SETTING_BOOL("run_ahead_enabled", &settings->bools.run_ahead_enabled, true, false, false); diff --git a/configuration.h b/configuration.h index e9aeedf17d..1da194405c 100644 --- a/configuration.h +++ b/configuration.h @@ -251,6 +251,7 @@ typedef struct settings bool playlist_entry_rename; bool rewind_enable; bool vrr_runloop_enable; + bool vrr_above_range_skew; bool apply_cheats_after_toggle; bool apply_cheats_after_load; bool run_ahead_enabled; diff --git a/gfx/video_driver.c b/gfx/video_driver.c index 5a9b8c8e07..a92ea3b41e 100644 --- a/gfx/video_driver.c +++ b/gfx/video_driver.c @@ -1459,7 +1459,9 @@ void video_driver_monitor_adjust_system_rates(void) timing_skew = fabs( 1.0f - info->fps / timing_skew_hz); - if (!settings->bools.vrr_runloop_enable) + if (!settings->bools.vrr_runloop_enable || + (settings->bools.vrr_runloop_enable && settings->bools.vrr_above_range_skew && + (info->fps > video_refresh_rate))) { /* We don't want to adjust pitch too much. If we have extreme cases, * just don't readjust at all. */ diff --git a/intl/msg_hash_lbl.h b/intl/msg_hash_lbl.h index a66314de60..c46d8e63cb 100644 --- a/intl/msg_hash_lbl.h +++ b/intl/msg_hash_lbl.h @@ -965,6 +965,8 @@ MSG_HASH(MENU_ENUM_LABEL_REWIND_SETTINGS, "rewind_settings") MSG_HASH(MENU_ENUM_LABEL_VRR_RUNLOOP_ENABLE, "vrr_runloop_enable") +MSG_HASH(MENU_ENUM_LABEL_VRR_ABOVE_RANGE_SKEW, + "vrr_above_range_skew") MSG_HASH(MENU_ENUM_LABEL_CHEAT_SETTINGS, "cheat_settings") MSG_HASH(MENU_ENUM_LABEL_RGUI_BROWSER_DIRECTORY, diff --git a/intl/msg_hash_us.c b/intl/msg_hash_us.c index 833aee1915..ba77e2c362 100644 --- a/intl/msg_hash_us.c +++ b/intl/msg_hash_us.c @@ -1263,6 +1263,16 @@ int menu_hash_get_help_us_enum(enum msg_hash_enums msg, char *s, size_t len) "No deviation from the core requested refresh rate,\n" "no sound Dynamic Rate Control)."); break; + case MENU_ENUM_LABEL_VRR_ABOVE_RANGE_SKEW: + snprintf(s, len, + "Allow above range deviation for Sync to Exact Content Framerate.\n" + " \n" + "This option will revert to deviation\n" + "behaviour when the content framerate\n" + "goes slightly above the monitor refresh rate\n" + "(within the Audio Maximum Timing Skew range).\n" + "Recommended for VRR Monitors limited to 60hz."); + break; case MENU_ENUM_LABEL_VIDEO_MONITOR_INDEX: snprintf(s, len, "Which monitor to prefer.\n" diff --git a/intl/msg_hash_us.h b/intl/msg_hash_us.h index 9ed88725b2..f95e5096f1 100644 --- a/intl/msg_hash_us.h +++ b/intl/msg_hash_us.h @@ -825,6 +825,10 @@ MSG_HASH( MENU_ENUM_LABEL_VALUE_VRR_RUNLOOP_ENABLE, "Sync to Exact Content Framerate (G-Sync, FreeSync)" ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_VRR_ABOVE_RANGE_SKEW, + "Allow above range deviation for Sync to Exact Content Framerate" + ) MSG_HASH( MENU_ENUM_LABEL_VALUE_FRAME_THROTTLE_SETTINGS, "Frame Throttle" @@ -5480,6 +5484,10 @@ MSG_HASH( MENU_ENUM_SUBLABEL_VRR_RUNLOOP_ENABLE, "No deviation from core requested timing. Use for Variable Refresh Rate screens, G-Sync, FreeSync." ) +MSG_HASH( + MENU_ENUM_SUBLABEL_VRR_ABOVE_RANGE_SKEW, + "Make an exception and deviate when slightly above the monitor refresh rate for Sync to Exact Content Framerate." + ) MSG_HASH( MENU_ENUM_SUBLABEL_XMB_LAYOUT, "Select a different layout for the XMB interface." diff --git a/menu/cbs/menu_cbs_sublabel.c b/menu/cbs/menu_cbs_sublabel.c index 0d72663fef..616e481f91 100644 --- a/menu/cbs/menu_cbs_sublabel.c +++ b/menu/cbs/menu_cbs_sublabel.c @@ -209,6 +209,7 @@ default_sublabel_macro(action_bind_sublabel_savestate_auto_index, MENU_ default_sublabel_macro(action_bind_sublabel_block_sram_overwrite, MENU_ENUM_SUBLABEL_BLOCK_SRAM_OVERWRITE) default_sublabel_macro(action_bind_sublabel_fastforward_ratio, MENU_ENUM_SUBLABEL_FASTFORWARD_RATIO) default_sublabel_macro(action_bind_sublabel_vrr_runloop_enable, MENU_ENUM_SUBLABEL_VRR_RUNLOOP_ENABLE) +default_sublabel_macro(action_bind_sublabel_vrr_above_range_skew, MENU_ENUM_SUBLABEL_VRR_ABOVE_RANGE_SKEW) default_sublabel_macro(action_bind_sublabel_slowmotion_ratio, MENU_ENUM_SUBLABEL_SLOWMOTION_RATIO) default_sublabel_macro(action_bind_sublabel_run_ahead_enabled, MENU_ENUM_SUBLABEL_RUN_AHEAD_ENABLED) default_sublabel_macro(action_bind_sublabel_run_ahead_secondary_instance, MENU_ENUM_SUBLABEL_RUN_AHEAD_SECONDARY_INSTANCE) @@ -1519,6 +1520,9 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs, case MENU_ENUM_LABEL_VRR_RUNLOOP_ENABLE: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_vrr_runloop_enable); break; + case MENU_ENUM_LABEL_VRR_ABOVE_RANGE_SKEW: + BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_vrr_above_range_skew); + break; case MENU_ENUM_LABEL_BLOCK_SRAM_OVERWRITE: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_block_sram_overwrite); break; diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index 3b47914c46..c85740fa87 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -5171,6 +5171,9 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data) menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_VRR_RUNLOOP_ENABLE, PARSE_ONLY_BOOL, false); + menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_VRR_ABOVE_RANGE_SKEW, + PARSE_ONLY_BOOL, false); { settings_t *settings = config_get_ptr(); diff --git a/menu/menu_setting.c b/menu/menu_setting.c index 0418ba464a..7cf8327d45 100644 --- a/menu/menu_setting.c +++ b/menu/menu_setting.c @@ -6689,6 +6689,22 @@ static bool setting_append_list( SD_FLAG_NONE ); + CONFIG_BOOL( + list, list_info, + &settings->bools.vrr_above_range_skew, + MENU_ENUM_LABEL_VRR_ABOVE_RANGE_SKEW, + MENU_ENUM_LABEL_VALUE_VRR_ABOVE_RANGE_SKEW, + false, + MENU_ENUM_LABEL_VALUE_OFF, + MENU_ENUM_LABEL_VALUE_ON, + &group_info, + &subgroup_info, + parent_group, + general_write_handler, + general_read_handler, + SD_FLAG_NONE + ); + CONFIG_FLOAT( list, list_info, &settings->floats.slowmotion_ratio, diff --git a/msg_hash.h b/msg_hash.h index 3987382c32..00fd55fa2f 100644 --- a/msg_hash.h +++ b/msg_hash.h @@ -1347,6 +1347,7 @@ enum msg_hash_enums MENU_LABEL(FASTFORWARD_RATIO), MENU_LABEL(VRR_RUNLOOP_ENABLE), + MENU_LABEL(VRR_ABOVE_RANGE_SKEW), MENU_LABEL(REWIND_ENABLE), MENU_LABEL(CHEAT_APPLY_AFTER_TOGGLE), MENU_LABEL(CHEAT_APPLY_AFTER_LOAD), diff --git a/retroarch.c b/retroarch.c index d307b43c6b..17a0306993 100644 --- a/retroarch.c +++ b/retroarch.c @@ -3393,6 +3393,9 @@ int runloop_iterate(unsigned *sleep_ms) unsigned i; bool input_nonblock_state = input_driver_is_nonblock_state(); settings_t *settings = config_get_ptr(); + struct retro_system_av_info *av_info = video_viewport_get_system_av_info(); + bool vrr_runloop_above_refresh_deviation = + ((av_info->timing.fps > settings->floats.video_refresh_rate) && settings->bools.vrr_above_range_skew)? true : false; unsigned max_users = *(input_driver_get_uint(INPUT_ACTION_MAX_USERS)); #ifdef HAVE_DISCORD @@ -3526,16 +3529,13 @@ int runloop_iterate(unsigned *sleep_ms) autosave_unlock(); /* Condition for max speed x0.0 when vrr_runloop is off to skip that part */ - if (settings->floats.fastforward_ratio || settings->bools.vrr_runloop_enable) + if (settings->floats.fastforward_ratio || (settings->bools.vrr_runloop_enable && !vrr_runloop_above_refresh_deviation)) end: { retro_time_t to_sleep_ms; - if (settings->bools.vrr_runloop_enable) + if (settings->bools.vrr_runloop_enable && !vrr_runloop_above_refresh_deviation) { - struct retro_system_av_info *av_info = - video_viewport_get_system_av_info(); - /* Sync on video only, block audio later. */ if (fastforward_after_frames && settings->bools.audio_sync) {