Add 'Frame Rest' power saving option (#15834)

This commit is contained in:
sonninnos 2023-10-26 14:20:48 +03:00 committed by GitHub
parent 4f8e3fabad
commit da59b8c9ac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 224 additions and 15 deletions

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -20,6 +20,7 @@
#include <retro_inline.h>
#include <string/stdstring.h>
#include <retro_math.h>
#include <retro_timers.h>
#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);
}
}

View File

@ -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:

View File

@ -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"

View File

@ -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"

View File

@ -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;

View File

@ -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:

View File

@ -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:

View File

@ -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

View File

@ -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;

View File

@ -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,

View File

@ -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 @@ end:
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)
{