diff --git a/config.def.h b/config.def.h index 1ebbb626f1e..a59e56441b1 100644 --- a/config.def.h +++ b/config.def.h @@ -391,6 +391,9 @@ #define MAXIMUM_FRAME_DELAY 19 #define DEFAULT_FRAME_DELAY_AUTO false +/* Try to sleep the spare time after frame is presented in order to reduce vsync CPU usage. */ +#define DEFAULT_FRAME_REST false + /* Inserts black frame(s) inbetween frames. * Useful for Higher Hz monitors (set to multiples of 60 Hz) who want to play 60 Hz * material with eliminated ghosting. video_refresh_rate should still be configured diff --git a/configuration.c b/configuration.c index 6448f23087e..89d32b5bb10 100644 --- a/configuration.c +++ b/configuration.c @@ -1810,6 +1810,7 @@ static struct config_bool_setting *populate_settings_bool( 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); + SETTING_BOOL("video_frame_rest", &settings->bools.video_frame_rest, true, DEFAULT_FRAME_REST, 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 diff --git a/configuration.h b/configuration.h index a44ae3fbccf..efbdb2ac5f5 100644 --- a/configuration.h +++ b/configuration.h @@ -578,6 +578,7 @@ typedef struct settings bool video_ctx_scaling; bool video_force_aspect; bool video_frame_delay_auto; + bool video_frame_rest; bool video_crop_overscan; bool video_aspect_ratio_auto; bool video_dingux_ipu_keep_aspect; diff --git a/gfx/video_driver.c b/gfx/video_driver.c index 5f124955cd4..d977c1b1b41 100644 --- a/gfx/video_driver.c +++ b/gfx/video_driver.c @@ -20,6 +20,7 @@ #include #include #include +#include #ifdef HAVE_CONFIG_H #include "../config.h" @@ -67,6 +68,7 @@ #define TIME_TO_FPS(last_time, new_time, frames) ((1000000.0f * (frames)) / ((new_time) - (last_time))) #define FRAME_DELAY_AUTO_DEBUG 0 +#define FRAME_REST_DEBUG 0 typedef struct { @@ -527,27 +529,27 @@ video_driver_t *hw_render_context_driver( #endif case RETRO_HW_CONTEXT_D3D10: #if defined(HAVE_D3D10) - return &video_d3d10; + return &video_d3d10; #else break; #endif case RETRO_HW_CONTEXT_D3D11: #if defined(HAVE_D3D11) - return &video_d3d11; + return &video_d3d11; #else break; #endif case RETRO_HW_CONTEXT_D3D12: #if defined(HAVE_D3D12) - return &video_d3d12; + return &video_d3d12; #else break; #endif case RETRO_HW_CONTEXT_D3D9: #if defined(HAVE_D3D9) && defined(HAVE_HLSL) - return &video_d3d9_hlsl; + return &video_d3d9_hlsl; #else - break; + break; #endif case RETRO_HW_CONTEXT_VULKAN: #if defined(HAVE_VULKAN) @@ -2641,6 +2643,7 @@ void video_driver_build_info(video_frame_info_t *video_info) video_info->runloop_is_paused = (runloop_st->flags & RUNLOOP_FLAG_PAUSED) ? true : false; video_info->runloop_is_slowmotion = (runloop_st->flags & RUNLOOP_FLAG_SLOWMOTION) ? true : false; video_info->fastforward_frameskip = settings->bools.fastforward_frameskip; + video_info->frame_rest = settings->bools.video_frame_rest; #ifdef _WIN32 #ifdef HAVE_VULKAN @@ -3749,6 +3752,7 @@ void video_driver_frame(const void *data, unsigned width, if (render_frame && video_info.statistics_show) { audio_statistics_t audio_stats; + char throttle_stats[128]; char latency_stats[128]; char tmp[128]; size_t len; @@ -3796,9 +3800,27 @@ void video_driver_frame(const void *data, unsigned width, audio_compute_buffer_statistics(&audio_stats); - latency_stats[0] = '\0'; - tmp[0] = '\0'; - len = 0; + throttle_stats[0] = '\0'; + latency_stats[0] = '\0'; + tmp[0] = '\0'; + len = 0; + + if (video_info.frame_rest) + len = snprintf(tmp + len, sizeof(throttle_stats), + " Frame Rest: %2u.00 ms\n" + " - Rested: %5.2f %%\n", + video_st->frame_rest, + (float)video_st->frame_rest_time_count / runloop_st->core_runtime_usec * 100); + + if (len) + { + /* TODO/FIXME - localize */ + size_t _len = strlcpy(throttle_stats, "THROTTLE\n", sizeof(throttle_stats)); + strlcpy(throttle_stats + _len, tmp, sizeof(throttle_stats) - _len); + } + + tmp[0] = '\0'; + len = 0; /* TODO/FIXME - localize */ if (video_st->frame_delay_target > 0) @@ -3826,9 +3848,9 @@ void video_driver_frame(const void *data, unsigned width, if (len) { - /* TODO/FIXME - localize */ - size_t _len = strlcpy(latency_stats, "LATENCY\n", sizeof(latency_stats)); - strlcpy(latency_stats + _len, tmp, sizeof(latency_stats) - _len); + /* TODO/FIXME - localize */ + size_t _len = strlcpy(latency_stats, "LATENCY\n", sizeof(latency_stats)); + strlcpy(latency_stats + _len, tmp, sizeof(latency_stats) - _len); } /* TODO/FIXME - localize */ @@ -3853,6 +3875,7 @@ void video_driver_frame(const void *data, unsigned width, " Underrun: %5.2f %%\n" " Blocking: %5.2f %%\n" " Samples: %5d\n" + "%s" "%s", av_info->geometry.base_width, av_info->geometry.base_height, @@ -3875,6 +3898,7 @@ void video_driver_frame(const void *data, unsigned width, audio_stats.close_to_underrun, audio_stats.close_to_blocking, audio_stats.samples, + throttle_stats, latency_stats); /* TODO/FIXME - add OSD chat text here */ @@ -4173,3 +4197,128 @@ void video_frame_delay_auto(video_driver_state_t *video_st, video_frame_delay_au ); #endif } + +void video_frame_rest(video_driver_state_t *video_st, + settings_t *settings, + retro_time_t current_time) +{ +#ifdef HAVE_MENU + bool menu_is_pausing = settings->bools.menu_pause_libretro && (menu_state_get_ptr()->flags & MENU_ST_FLAG_ALIVE); +#else + bool menu_is_pausing = false; +#endif + runloop_state_t *runloop_st = runloop_state_get_ptr(); + retro_time_t latest_time = cpu_features_get_time_usec(); + retro_time_t frame_time_delta = latest_time - current_time; + retro_time_t frame_time_target = 1000000.0f / settings->floats.video_refresh_rate; + retro_time_t frame_time = 0; + static retro_time_t after_present = 0; + int sleep_max = frame_time_target / 1000 / 2; + int sleep = 0; + int frame_time_near_req_count = ceil(settings->floats.video_refresh_rate / 2); + static int frame_time_over_count = 0; + static int frame_time_near_count = 0; + static int frame_time_try_count = 0; + double video_stddev = 0; + audio_statistics_t audio_stats; + + /* Must require video and audio deviation standards */ + video_monitor_fps_statistics(NULL, &video_stddev, NULL); + audio_compute_buffer_statistics(&audio_stats); + + /* Don't care about deviations when core is not running */ + if ( (runloop_st->flags & RUNLOOP_FLAG_PAUSED) + || !(runloop_st->flags & RUNLOOP_FLAG_CORE_RUNNING) + || menu_is_pausing) + video_stddev = audio_stats.std_deviation_percentage = 0; + + /* Compare to previous timestamp */ + frame_time = latest_time - after_present; + + /* Count running timers */ + if (frame_time > frame_time_target) + frame_time_over_count++; + else if (frame_time < frame_time_target) + frame_time_over_count--; + + if (labs(frame_time - frame_time_target) < frame_time_target * 1.002f - frame_time_target) + frame_time_near_count++; + else + frame_time_near_count--; + + /* Take new timestamp */ + after_present = latest_time; + + /* Ignore unreasonable frame times */ + if ( frame_time < frame_time_target / 2 + || frame_time > frame_time_target * 2) + return; + + /* Carry the extra */ + frame_time_delta -= frame_time_target - frame_time; + sleep = (frame_time_delta > 0) ? frame_time_delta : 0; + + /* No rest with bogus values */ + if ( sleep < 0 + || ( frame_time_target < frame_time_delta + && frame_time_target < frame_time)) + sleep = 0; + + /* Reset over the target counter */ + if (!sleep) + frame_time_over_count = 0; + + frame_time_try_count++; + if ( frame_time_try_count > frame_time_near_req_count * 2 + || frame_time_try_count < frame_time_near_count) + frame_time_over_count = frame_time_near_count = frame_time_try_count = 0; + + /* Increase */ + if (sleep + && (frame_time_over_count < 2) + && (video_stddev * 100.0f < 25.00f) + && (audio_stats.std_deviation_percentage < 25.00f) + && (frame_time_near_count > frame_time_try_count / 2) + && (frame_time_near_count > frame_time_near_req_count) + ) + { +#if FRAME_REST_DEBUG + RARCH_LOG("+ frame=%5d delta=%5d sleep=%2d over=%3d near=%3d try=%3d\n", frame_time, frame_time_delta, video_st->frame_rest, frame_time_over_count, frame_time_near_count, frame_time_try_count); +#endif + video_st->frame_rest++; + frame_time_over_count = frame_time_near_count = frame_time_try_count = 0; + } + /* Decrease */ + else if ( sleep + && frame_time_over_count != 0 + && frame_time_try_count > 10 + && ( (frame_time_near_count < -2 && -frame_time_near_count > frame_time_try_count) + || (frame_time_over_count > frame_time_near_req_count / 2) + || (frame_time_over_count < -(frame_time_near_req_count / 2)) + ) + ) + { +#if FRAME_REST_DEBUG + RARCH_LOG("- frame=%5d delta=%5d sleep=%2d over=%3d near=%3d try=%3d\n", frame_time, frame_time_delta, video_st->frame_rest, frame_time_over_count, frame_time_near_count, frame_time_try_count); +#endif + if (video_st->frame_rest) + video_st->frame_rest--; + frame_time_over_count = frame_time_near_count = frame_time_try_count = 0; + } + + /* Limit to maximum sleep */ + if (video_st->frame_rest > sleep_max) + video_st->frame_rest = sleep_max; + +#if FRAME_REST_DEBUG + RARCH_LOG(" frame=%5d delta=%5d sleep=%2d over=%3d near=%3d try=%3d %f %f\n", frame_time, frame_time_delta, video_st->frame_rest, frame_time_over_count, frame_time_near_count, frame_time_try_count, video_stddev, audio_stats.std_deviation_percentage); +#endif + + /* Do what is promised and add to statistics */ + if (video_st->frame_rest > 0) + { + if (!menu_is_pausing) + video_st->frame_rest_time_count += video_st->frame_rest * 1000; + retro_sleep(video_st->frame_rest); + } +} diff --git a/gfx/video_driver.h b/gfx/video_driver.h index 7755ffc7b21..06b5d4db69e 100644 --- a/gfx/video_driver.h +++ b/gfx/video_driver.h @@ -482,6 +482,7 @@ typedef struct video_frame_info bool runloop_is_slowmotion; bool runloop_is_paused; bool fastforward_frameskip; + bool frame_rest; bool msg_bgcolor_enable; bool crt_switch_hires_menu; bool hdr_enable; @@ -774,6 +775,7 @@ typedef struct retro_time_t frame_time_samples[MEASURE_FRAME_TIME_SAMPLES_COUNT]; uint64_t frame_time_count; uint64_t frame_count; + uint64_t frame_rest_time_count; uint8_t *record_gpu_buffer; #ifdef HAVE_VIDEO_FILTER rarch_softfilter_t *state_filter; @@ -859,6 +861,7 @@ typedef struct char title_buf[64]; char cached_driver_id[32]; + uint8_t frame_rest; uint8_t frame_delay_target; uint8_t frame_delay_effective; bool frame_delay_pause; @@ -1087,7 +1090,12 @@ bool *video_driver_get_threaded(void); void video_driver_set_threaded(bool val); -void video_frame_delay_auto(video_driver_state_t *video_st, video_frame_delay_auto_t *vfda); +void video_frame_delay_auto(video_driver_state_t *video_st, + video_frame_delay_auto_t *vfda); + +void video_frame_rest(video_driver_state_t *video_st, + settings_t *settings, + retro_time_t current_time); /** * video_context_driver_init: diff --git a/intl/msg_hash_lbl.h b/intl/msg_hash_lbl.h index 8f1dd793e12..7dec64a687f 100644 --- a/intl/msg_hash_lbl.h +++ b/intl/msg_hash_lbl.h @@ -4216,6 +4216,10 @@ MSG_HASH( MENU_ENUM_LABEL_VIDEO_FRAME_DELAY_AUTO, "video_frame_delay_auto" ) +MSG_HASH( + MENU_ENUM_LABEL_VIDEO_FRAME_REST, + "video_frame_rest" + ) MSG_HASH( MENU_ENUM_LABEL_VIDEO_SHADER_DELAY, "video_shader_delay" diff --git a/intl/msg_hash_us.h b/intl/msg_hash_us.h index 833568778dc..b781135e47f 100644 --- a/intl/msg_hash_us.h +++ b/intl/msg_hash_us.h @@ -15430,6 +15430,14 @@ MSG_HASH( MENU_ENUM_LABEL_HELP_GAMEMODE_ENABLE, "Enabling Linux GameMode can improve latency, fix audio crackling issues and maximize overall performance by automatically configuring your CPU and GPU for best performance.\nThe GameMode software needs to be installed for this to work. See https://github.com/FeralInteractive/gamemode for information on how to install GameMode." ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_VIDEO_FRAME_REST, + "Frame Rest" + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_VIDEO_FRAME_REST, + "Try to reduce vsync CPU usage by sleeping as much as possible after frame presentation. Designed primarily for third party scanline sync." + ) MSG_HASH( MENU_ENUM_LABEL_VALUE_PAL60_ENABLE, "Use PAL60 Mode" diff --git a/menu/cbs/menu_cbs_sublabel.c b/menu/cbs/menu_cbs_sublabel.c index 380c11c41fb..3340d169814 100644 --- a/menu/cbs/menu_cbs_sublabel.c +++ b/menu/cbs/menu_cbs_sublabel.c @@ -1232,6 +1232,7 @@ DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_gamemode_enable, MENU #endif #endif /*HAVE_LAKKA*/ +DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_video_frame_rest, MENU_ENUM_SUBLABEL_VIDEO_FRAME_REST) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_brightness_control, MENU_ENUM_SUBLABEL_BRIGHTNESS_CONTROL) #ifdef _3DS @@ -5225,6 +5226,9 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs, BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_gamemode_enable); break; #endif /*HAVE_LAKKA*/ + case MENU_ENUM_LABEL_VIDEO_FRAME_REST: + BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_video_frame_rest); + break; case MENU_ENUM_LABEL_BRIGHTNESS_CONTROL: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_brightness_control); break; diff --git a/menu/drivers/ozone.c b/menu/drivers/ozone.c index 8efd224a976..a56e666a36d 100644 --- a/menu/drivers/ozone.c +++ b/menu/drivers/ozone.c @@ -1981,6 +1981,7 @@ static uintptr_t ozone_entries_icon_get_texture( case MENU_ENUM_LABEL_CONTENT_SHOW_LATENCY: case MENU_ENUM_LABEL_SETTINGS_SHOW_LATENCY: case MENU_ENUM_LABEL_MENU_THROTTLE_FRAMERATE: + case MENU_ENUM_LABEL_VIDEO_FRAME_REST: return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_LATENCY]; case MENU_ENUM_LABEL_SAVING_SETTINGS: case MENU_ENUM_LABEL_SETTINGS_SHOW_SAVING: @@ -2003,6 +2004,7 @@ static uintptr_t ozone_entries_icon_get_texture( case MENU_ENUM_LABEL_FASTFORWARD_RATIO: case MENU_ENUM_LABEL_FRAME_THROTTLE_SETTINGS: case MENU_ENUM_LABEL_SETTINGS_SHOW_FRAME_THROTTLE: + case MENU_ENUM_LABEL_FASTFORWARD_FRAMESKIP: return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_FRAMESKIP]; case MENU_ENUM_LABEL_QUICK_MENU_START_RECORDING: case MENU_ENUM_LABEL_QUICK_MENU_SHOW_START_RECORDING: @@ -2059,7 +2061,6 @@ static uintptr_t ozone_entries_icon_get_texture( #endif case MENU_ENUM_LABEL_POWER_MANAGEMENT_SETTINGS: case MENU_ENUM_LABEL_SETTINGS_SHOW_POWER_MANAGEMENT: - case MENU_ENUM_LABEL_FASTFORWARD_FRAMESKIP: return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_POWER]; case MENU_ENUM_LABEL_RETRO_ACHIEVEMENTS_SETTINGS: case MENU_ENUM_LABEL_SETTINGS_SHOW_ACHIEVEMENTS: diff --git a/menu/drivers/xmb.c b/menu/drivers/xmb.c index 3f642d05da1..e6a9ee532f1 100644 --- a/menu/drivers/xmb.c +++ b/menu/drivers/xmb.c @@ -3183,6 +3183,7 @@ static uintptr_t xmb_icon_get_id(xmb_handle_t *xmb, case MENU_ENUM_LABEL_CONTENT_SHOW_LATENCY: case MENU_ENUM_LABEL_SETTINGS_SHOW_LATENCY: case MENU_ENUM_LABEL_MENU_THROTTLE_FRAMERATE: + case MENU_ENUM_LABEL_VIDEO_FRAME_REST: return xmb->textures.list[XMB_TEXTURE_LATENCY]; case MENU_ENUM_LABEL_SAVING_SETTINGS: case MENU_ENUM_LABEL_SETTINGS_SHOW_SAVING: @@ -3205,6 +3206,7 @@ static uintptr_t xmb_icon_get_id(xmb_handle_t *xmb, case MENU_ENUM_LABEL_FASTFORWARD_RATIO: case MENU_ENUM_LABEL_FRAME_THROTTLE_SETTINGS: case MENU_ENUM_LABEL_SETTINGS_SHOW_FRAME_THROTTLE: + case MENU_ENUM_LABEL_FASTFORWARD_FRAMESKIP: return xmb->textures.list[XMB_TEXTURE_FRAMESKIP]; case MENU_ENUM_LABEL_QUICK_MENU_START_RECORDING: case MENU_ENUM_LABEL_QUICK_MENU_SHOW_START_RECORDING: @@ -3261,7 +3263,6 @@ static uintptr_t xmb_icon_get_id(xmb_handle_t *xmb, #endif case MENU_ENUM_LABEL_POWER_MANAGEMENT_SETTINGS: case MENU_ENUM_LABEL_SETTINGS_SHOW_POWER_MANAGEMENT: - case MENU_ENUM_LABEL_FASTFORWARD_FRAMESKIP: return xmb->textures.list[XMB_TEXTURE_POWER]; case MENU_ENUM_LABEL_RETRO_ACHIEVEMENTS_SETTINGS: case MENU_ENUM_LABEL_SETTINGS_SHOW_ACHIEVEMENTS: diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index 5667760e510..db56e5f0eea 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -10434,6 +10434,8 @@ unsigned menu_displaylist_build_list( case DISPLAYLIST_POWER_MANAGEMENT_SETTINGS_LIST: { menu_displaylist_build_info_t build_list[] = { + {MENU_ENUM_LABEL_FASTFORWARD_FRAMESKIP, PARSE_ONLY_BOOL}, + {MENU_ENUM_LABEL_VIDEO_FRAME_REST, PARSE_ONLY_BOOL}, {MENU_ENUM_LABEL_SUSTAINED_PERFORMANCE_MODE, PARSE_ONLY_BOOL}, {MENU_ENUM_LABEL_CPU_PERFPOWER, PARSE_ACTION}, #ifdef HAVE_LAKKA @@ -10915,7 +10917,8 @@ unsigned menu_displaylist_build_list( {MENU_ENUM_LABEL_FASTFORWARD_FRAMESKIP, PARSE_ONLY_BOOL, true}, {MENU_ENUM_LABEL_SLOWMOTION_RATIO, PARSE_ONLY_FLOAT, true}, {MENU_ENUM_LABEL_VRR_RUNLOOP_ENABLE, PARSE_ONLY_BOOL, true}, - {MENU_ENUM_LABEL_MENU_THROTTLE_FRAMERATE, PARSE_ONLY_BOOL , true}, + {MENU_ENUM_LABEL_MENU_THROTTLE_FRAMERATE, PARSE_ONLY_BOOL, true}, + {MENU_ENUM_LABEL_VIDEO_FRAME_REST, PARSE_ONLY_BOOL, true}, }; #ifdef HAVE_REWIND diff --git a/menu/menu_setting.c b/menu/menu_setting.c index e23a3d25863..6175c2f068c 100644 --- a/menu/menu_setting.c +++ b/menu/menu_setting.c @@ -19154,6 +19154,21 @@ static bool setting_append_list( general_read_handler, SD_FLAG_NONE); + CONFIG_BOOL( + list, list_info, + &settings->bools.video_frame_rest, + MENU_ENUM_LABEL_VIDEO_FRAME_REST, + MENU_ENUM_LABEL_VALUE_VIDEO_FRAME_REST, + DEFAULT_FRAME_REST, + 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); + END_SUB_GROUP(list, list_info, parent_group); END_GROUP(list, list_info, parent_group); break; diff --git a/msg_hash.h b/msg_hash.h index 8fc17474a95..314cf16835b 100644 --- a/msg_hash.h +++ b/msg_hash.h @@ -3818,6 +3818,7 @@ enum msg_hash_enums MENU_LABEL(CPU_MANAGED_MAX_FREQ), MENU_LBL_H(GAMEMODE_ENABLE), MENU_ENUM_SUBLABEL_GAMEMODE_ENABLE_LINUX, + MENU_LABEL(VIDEO_FRAME_REST), MENU_ENUM_LABEL_VALUE_CPU_PERF_MODE_MANAGED_PERF, MENU_ENUM_LABEL_VALUE_CPU_PERF_MODE_MANAGED_PER_CONTEXT, diff --git a/runloop.c b/runloop.c index a4c3fec55c7..98976d2e0be 100644 --- a/runloop.c +++ b/runloop.c @@ -4083,10 +4083,15 @@ void runloop_event_deinit_core(void) av_info->timing.fps = 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; + /* Reset frame rest counter */ + if (settings->bools.video_frame_rest) + video_st->frame_rest_time_count = video_st->frame_rest = 0; + driver_uninit(DRIVERS_CMD_ALL, 0); #ifdef HAVE_CONFIGFILE @@ -7289,6 +7294,11 @@ int runloop_iterate(void) runloop_st->frame_limit_last_time = end_frame_time; } + /* Post-frame power saving sleep resting */ + if ( settings->bools.video_frame_rest + && !(input_st->flags & INP_FLAG_NONBLOCKING)) + video_frame_rest(video_st, settings, current_time); + /* Set paused state after x frames */ if (runloop_st->run_frames_and_pause > 0) {