mirror of
https://github.com/libretro/RetroArch
synced 2025-03-02 19:13:34 +00:00
Add 'Automatic Frame Delay' option (#13190)
This commit is contained in:
parent
65a421d90f
commit
3137f8470b
@ -344,6 +344,7 @@
|
||||
*/
|
||||
#define DEFAULT_FRAME_DELAY 0
|
||||
#define MAXIMUM_FRAME_DELAY 19
|
||||
#define DEFAULT_FRAME_DELAY_AUTO false
|
||||
|
||||
/* Inserts black frame(s) inbetween frames.
|
||||
* Useful for Higher Hz monitors (set to multiples of 60 Hz) who want to play 60 Hz
|
||||
|
@ -1667,6 +1667,7 @@ static struct config_bool_setting *populate_settings_bool(
|
||||
SETTING_BOOL("video_smooth", &settings->bools.video_smooth, true, DEFAULT_VIDEO_SMOOTH, false);
|
||||
SETTING_BOOL("video_ctx_scaling", &settings->bools.video_ctx_scaling, true, DEFAULT_VIDEO_CTX_SCALING, false);
|
||||
SETTING_BOOL("video_force_aspect", &settings->bools.video_force_aspect, true, DEFAULT_FORCE_ASPECT, false);
|
||||
SETTING_BOOL("video_frame_delay_auto", &settings->bools.video_frame_delay_auto, true, DEFAULT_FRAME_DELAY_AUTO, false);
|
||||
#if defined(DINGUX)
|
||||
SETTING_BOOL("video_dingux_ipu_keep_aspect", &settings->bools.video_dingux_ipu_keep_aspect, true, DEFAULT_DINGUX_IPU_KEEP_ASPECT, false);
|
||||
#endif
|
||||
|
@ -534,6 +534,7 @@ typedef struct settings
|
||||
bool video_smooth;
|
||||
bool video_ctx_scaling;
|
||||
bool video_force_aspect;
|
||||
bool video_frame_delay_auto;
|
||||
bool video_crop_overscan;
|
||||
bool video_aspect_ratio_auto;
|
||||
bool video_dingux_ipu_keep_aspect;
|
||||
|
@ -3838,12 +3838,14 @@ void video_driver_frame(const void *data, unsigned width,
|
||||
snprintf(video_info.stat_text,
|
||||
sizeof(video_info.stat_text),
|
||||
"Video Statistics:\n -Frame rate: %6.2f fps\n -Frame time: %6.2f ms\n -Frame time deviation: %.3f %%\n"
|
||||
" -Frame count: %" PRIu64"\n -Viewport: %d x %d x %3.2f\n"
|
||||
" -Frame delay (target/effective): %u/%u ms\n -Frame count: %" PRIu64"\n -Viewport: %d x %d x %3.2f\n"
|
||||
"Audio Statistics:\n -Average buffer saturation: %.2f %%\n -Standard deviation: %.2f %%\n -Time spent close to underrun: %.2f %%\n -Time spent close to blocking: %.2f %%\n -Sample count: %d\n"
|
||||
"Core Geometry:\n -Size: %u x %u\n -Max Size: %u x %u\n -Aspect: %3.2f\nCore Timing:\n -FPS: %3.2f\n -Sample Rate: %6.2f\n",
|
||||
last_fps,
|
||||
frame_time / 1000.0f,
|
||||
100.0f * stddev,
|
||||
video_st->frame_delay_target,
|
||||
video_st->frame_delay_effective,
|
||||
video_st->frame_count,
|
||||
video_info.width,
|
||||
video_info.height,
|
||||
|
@ -869,6 +869,8 @@ typedef struct
|
||||
unsigned state_scale;
|
||||
unsigned state_out_bpp;
|
||||
#endif
|
||||
unsigned frame_delay_target;
|
||||
unsigned frame_delay_effective;
|
||||
unsigned frame_cache_width;
|
||||
unsigned frame_cache_height;
|
||||
unsigned width;
|
||||
|
@ -3280,6 +3280,10 @@ MSG_HASH(
|
||||
MENU_ENUM_LABEL_VIDEO_FRAME_DELAY,
|
||||
"video_frame_delay"
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VIDEO_FRAME_DELAY_AUTO,
|
||||
"video_frame_delay_auto"
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VIDEO_SHADER_DELAY,
|
||||
"video_shader_delay"
|
||||
|
@ -26,6 +26,7 @@
|
||||
|
||||
#ifdef RARCH_INTERNAL
|
||||
#include "../configuration.h"
|
||||
#include "../config.def.h"
|
||||
|
||||
int msg_hash_get_help_us_enum(enum msg_hash_enums msg, char *s, size_t len)
|
||||
{
|
||||
@ -1543,7 +1544,17 @@ int msg_hash_get_help_us_enum(enum msg_hash_enums msg, char *s, size_t len)
|
||||
"Can reduce latency at the cost of\n"
|
||||
"higher risk of stuttering.\n"
|
||||
" \n"
|
||||
"Maximum is 15.");
|
||||
"Maximum is %d.", MAXIMUM_FRAME_DELAY);
|
||||
break;
|
||||
case MENU_ENUM_LABEL_VIDEO_FRAME_DELAY_AUTO:
|
||||
snprintf(s, len,
|
||||
"Temporarily decreases effective 'Frame Delay'\n"
|
||||
"until target refresh rate is stable.\n"
|
||||
" \n"
|
||||
"Measuring starts from half frame time when\n"
|
||||
"'Frame Delay' is 0.\n"
|
||||
" \n"
|
||||
"E.g. 8 for NTSC and 10 for PAL.");
|
||||
break;
|
||||
case MENU_ENUM_LABEL_VIDEO_SHADER_DELAY:
|
||||
snprintf(s, len,
|
||||
|
@ -1849,6 +1849,14 @@ MSG_HASH(
|
||||
MENU_ENUM_SUBLABEL_VIDEO_FRAME_DELAY,
|
||||
"Reduces latency at the cost of a higher risk of video stuttering. Adds a delay after VSync (in ms)."
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_VIDEO_FRAME_DELAY_AUTO,
|
||||
"Automatic Frame Delay"
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_SUBLABEL_VIDEO_FRAME_DELAY_AUTO,
|
||||
"Decrease effective 'Frame Delay' temporarily to prevent future frame drops. Starting point is half frame time when 'Frame Delay' is 0."
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_VIDEO_HARD_SYNC,
|
||||
"Hard GPU Sync"
|
||||
|
@ -312,6 +312,7 @@ DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_materialui_thumbnail_background_enab
|
||||
#endif
|
||||
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_add_content_list, MENU_ENUM_SUBLABEL_ADD_CONTENT_LIST)
|
||||
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_video_frame_delay, MENU_ENUM_SUBLABEL_VIDEO_FRAME_DELAY)
|
||||
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_video_frame_delay_auto, MENU_ENUM_SUBLABEL_VIDEO_FRAME_DELAY_AUTO)
|
||||
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_video_shader_delay, MENU_ENUM_SUBLABEL_VIDEO_SHADER_DELAY)
|
||||
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_video_black_frame_insertion, MENU_ENUM_SUBLABEL_VIDEO_BLACK_FRAME_INSERTION)
|
||||
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_systeminfo_cpu_cores, MENU_ENUM_SUBLABEL_CPU_CORES)
|
||||
@ -3822,6 +3823,9 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs,
|
||||
case MENU_ENUM_LABEL_VIDEO_FRAME_DELAY:
|
||||
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_video_frame_delay);
|
||||
break;
|
||||
case MENU_ENUM_LABEL_VIDEO_FRAME_DELAY_AUTO:
|
||||
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_video_frame_delay_auto);
|
||||
break;
|
||||
case MENU_ENUM_LABEL_VIDEO_SHADER_DELAY:
|
||||
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_video_shader_delay);
|
||||
break;
|
||||
|
@ -8006,6 +8006,10 @@ unsigned menu_displaylist_build_list(
|
||||
MENU_ENUM_LABEL_VIDEO_FRAME_DELAY,
|
||||
PARSE_ONLY_UINT, false) == 0)
|
||||
count++;
|
||||
if (MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(list,
|
||||
MENU_ENUM_LABEL_VIDEO_FRAME_DELAY_AUTO,
|
||||
PARSE_ONLY_BOOL, false) == 0)
|
||||
count++;
|
||||
}
|
||||
|
||||
if (video_driver_test_all_flags(GFX_CTX_FLAGS_HARD_SYNC))
|
||||
@ -8425,6 +8429,7 @@ unsigned menu_displaylist_build_list(
|
||||
bool video_hard_sync = settings->bools.video_hard_sync;
|
||||
menu_displaylist_build_info_selective_t build_list[] = {
|
||||
{MENU_ENUM_LABEL_VIDEO_FRAME_DELAY, PARSE_ONLY_UINT, true },
|
||||
{MENU_ENUM_LABEL_VIDEO_FRAME_DELAY_AUTO, PARSE_ONLY_BOOL, true },
|
||||
{MENU_ENUM_LABEL_AUDIO_LATENCY, PARSE_ONLY_UINT, true },
|
||||
{MENU_ENUM_LABEL_INPUT_POLL_TYPE_BEHAVIOR, PARSE_ONLY_UINT, true },
|
||||
{MENU_ENUM_LABEL_INPUT_BLOCK_TIMEOUT, PARSE_ONLY_UINT, true },
|
||||
|
@ -12178,6 +12178,23 @@ static bool setting_append_list(
|
||||
menu_settings_list_current_add_range(list, list_info, 0, MAXIMUM_FRAME_DELAY, 1, true, true);
|
||||
SETTINGS_DATA_LIST_CURRENT_ADD_FLAGS(list, list_info, SD_FLAG_LAKKA_ADVANCED);
|
||||
|
||||
CONFIG_BOOL(
|
||||
list, list_info,
|
||||
&settings->bools.video_frame_delay_auto,
|
||||
MENU_ENUM_LABEL_VIDEO_FRAME_DELAY_AUTO,
|
||||
MENU_ENUM_LABEL_VALUE_VIDEO_FRAME_DELAY_AUTO,
|
||||
DEFAULT_FRAME_DELAY_AUTO,
|
||||
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
|
||||
);
|
||||
SETTINGS_DATA_LIST_CURRENT_ADD_FLAGS(list, list_info, SD_FLAG_LAKKA_ADVANCED);
|
||||
|
||||
/* Unlike all other shader-related menu entries
|
||||
* (which appear in the shaders quick menu, and
|
||||
* are thus hidden automatically on platforms
|
||||
|
@ -1049,6 +1049,7 @@ enum msg_hash_enums
|
||||
MENU_LABEL(VIDEO_GPU_SCREENSHOT),
|
||||
MENU_LABEL(VIDEO_BLACK_FRAME_INSERTION),
|
||||
MENU_LABEL(VIDEO_FRAME_DELAY),
|
||||
MENU_LABEL(VIDEO_FRAME_DELAY_AUTO),
|
||||
MENU_LABEL(VIDEO_SHADER_DELAY),
|
||||
MENU_LABEL(VIDEO_VSYNC),
|
||||
MENU_LABEL(VIDEO_ADAPTIVE_VSYNC),
|
||||
|
89
retroarch.c
89
retroarch.c
@ -4035,6 +4035,10 @@ static void command_event_deinit_core(
|
||||
if (video_st->video_refresh_rate_original)
|
||||
video_display_server_restore_refresh_rate();
|
||||
|
||||
/* Recalibrate frame delay target */
|
||||
if (settings->bools.video_frame_delay_auto)
|
||||
video_st->frame_delay_target = 0;
|
||||
|
||||
if (reinit)
|
||||
driver_uninit(p_rarch, DRIVERS_CMD_ALL);
|
||||
|
||||
@ -9132,6 +9136,10 @@ bool runloop_environment_cb(unsigned cmd, void *data)
|
||||
if (video_fullscreen)
|
||||
video_driver_hide_mouse();
|
||||
|
||||
/* Recalibrate frame delay target */
|
||||
if (settings->bools.video_frame_delay_auto)
|
||||
video_st->frame_delay_target = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@ -16708,6 +16716,7 @@ int runloop_iterate(void)
|
||||
settings_t *settings = config_get_ptr();
|
||||
runloop_state_t *runloop_st = &runloop_state;
|
||||
unsigned video_frame_delay = settings->uints.video_frame_delay;
|
||||
unsigned video_frame_delay_effective = video_st->frame_delay_effective;
|
||||
bool vrr_runloop_enable = settings->bools.vrr_runloop_enable;
|
||||
unsigned max_users = settings->uints.input_max_users;
|
||||
retro_time_t current_time = cpu_features_get_time_usec();
|
||||
@ -16931,8 +16940,84 @@ int runloop_iterate(void)
|
||||
}
|
||||
}
|
||||
|
||||
if ((video_frame_delay > 0) && input_st && !input_st->nonblocking_flag)
|
||||
retro_sleep(video_frame_delay);
|
||||
if (input_st && !input_st->nonblocking_flag)
|
||||
{
|
||||
if (settings->bools.video_frame_delay_auto)
|
||||
{
|
||||
float refresh_rate = settings->floats.video_refresh_rate;
|
||||
unsigned frame_time_interval = 8;
|
||||
bool frame_time_update =
|
||||
/* Skip some starting frames for stabilization */
|
||||
video_st->frame_count > 10 &&
|
||||
video_st->frame_count % frame_time_interval == 0;
|
||||
|
||||
/* Set target moderately as half frame time with 0 delay */
|
||||
if (video_frame_delay == 0)
|
||||
video_frame_delay = 1 / refresh_rate * 1000 / 2;
|
||||
|
||||
if (video_st->frame_delay_target != video_frame_delay)
|
||||
{
|
||||
video_st->frame_delay_target = video_frame_delay_effective = video_frame_delay;
|
||||
RARCH_LOG("[Video]: Frame delay reset to %d.\n", video_frame_delay);
|
||||
}
|
||||
|
||||
if (video_frame_delay_effective > 0 && frame_time_update)
|
||||
{
|
||||
unsigned i = 0;
|
||||
unsigned frame_time = 0;
|
||||
unsigned frame_time_frames = frame_time_interval - 1;
|
||||
unsigned frame_time_target = 1000000.0f / refresh_rate;
|
||||
unsigned frame_time_limit_min = frame_time_target * 1.25;
|
||||
unsigned frame_time_limit_med = frame_time_target * 1.50;
|
||||
unsigned frame_time_limit_max = frame_time_target * 1.90;
|
||||
unsigned frame_time_limit_cap = frame_time_target * 2.25;
|
||||
unsigned frame_time_limit_ign = frame_time_target * 2.50;
|
||||
unsigned frame_time_index =
|
||||
(video_st->frame_time_count &
|
||||
(MEASURE_FRAME_TIME_SAMPLES_COUNT - 1));
|
||||
|
||||
/* Calculate average frame time to balance spikes */
|
||||
for (i = 1; i < frame_time_frames + 1; i++)
|
||||
{
|
||||
unsigned frame_time_i = video_st->frame_time_samples[frame_time_index - i];
|
||||
|
||||
/* Ignore values when core is doing internal frame skipping */
|
||||
if (frame_time_i > frame_time_limit_ign)
|
||||
frame_time_i = 0;
|
||||
/* Limit maximum to prevent false positives */
|
||||
else if (frame_time_i > frame_time_limit_cap)
|
||||
frame_time_i = frame_time_limit_cap;
|
||||
|
||||
frame_time += frame_time_i;
|
||||
}
|
||||
frame_time /= frame_time_frames;
|
||||
|
||||
if (frame_time > frame_time_limit_min)
|
||||
{
|
||||
unsigned delay_decrease = 1;
|
||||
|
||||
/* Increase decrease the more frame time is off target */
|
||||
if (frame_time > frame_time_limit_med && video_frame_delay_effective > delay_decrease)
|
||||
{
|
||||
delay_decrease++;
|
||||
if (frame_time > frame_time_limit_max && video_frame_delay_effective > delay_decrease)
|
||||
delay_decrease++;
|
||||
}
|
||||
|
||||
video_frame_delay_effective -= delay_decrease;
|
||||
RARCH_LOG("[Video]: Frame delay decrease by %d to %d due to frame time: %d > %d.\n",
|
||||
delay_decrease, video_frame_delay_effective, frame_time, frame_time_target);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
video_st->frame_delay_target = video_frame_delay_effective = video_frame_delay;
|
||||
|
||||
video_st->frame_delay_effective = video_frame_delay_effective;
|
||||
|
||||
if (video_frame_delay_effective > 0)
|
||||
retro_sleep(video_frame_delay_effective);
|
||||
}
|
||||
|
||||
{
|
||||
#ifdef HAVE_RUNAHEAD
|
||||
|
Loading…
x
Reference in New Issue
Block a user