Skip to content

Commit

Permalink
Add 'Frame Rest' power saving option
Browse files Browse the repository at this point in the history
  • Loading branch information
sonninnos committed Oct 25, 2023
1 parent 06d5cd3 commit f7ef6af
Show file tree
Hide file tree
Showing 14 changed files with 224 additions and 15 deletions.
3 changes: 3 additions & 0 deletions config.def.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions configuration.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions configuration.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
171 changes: 160 additions & 11 deletions gfx/video_driver.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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
{
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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 */
Expand All @@ -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,
Expand All @@ -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 */
Expand Down Expand Up @@ -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);
}
}
10 changes: 9 additions & 1 deletion gfx/video_driver.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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:
Expand Down
4 changes: 4 additions & 0 deletions intl/msg_hash_lbl.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
8 changes: 8 additions & 0 deletions intl/msg_hash_us.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
4 changes: 4 additions & 0 deletions menu/cbs/menu_cbs_sublabel.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down
3 changes: 2 additions & 1 deletion menu/drivers/ozone.c
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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:
Expand Down Expand Up @@ -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:
Expand Down
Loading

0 comments on commit f7ef6af

Please sign in to comment.