Rework optional fast-forward frame skipping: Drop frames based on frame timing (#13578)

This commit is contained in:
jdgleaver 2022-01-31 15:32:17 +00:00 committed by GitHub
parent 6aa89b3723
commit a953b27614
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 57 additions and 31 deletions

View File

@ -2803,6 +2803,7 @@ void video_driver_build_info(video_frame_info_t *video_info)
video_info->runloop_is_paused = runloop_st->paused; video_info->runloop_is_paused = runloop_st->paused;
video_info->runloop_is_slowmotion = runloop_st->slowmotion; video_info->runloop_is_slowmotion = runloop_st->slowmotion;
video_info->fastforward_frameskip = settings->bools.fastforward_frameskip;
video_info->input_driver_nonblock_state = input_st ? video_info->input_driver_nonblock_state = input_st ?
input_st->nonblocking_flag : false; input_st->nonblocking_flag : false;
@ -3568,13 +3569,16 @@ void video_driver_frame(const void *data, unsigned width,
{ {
char status_text[128]; char status_text[128];
static char video_driver_msg[256]; static char video_driver_msg[256];
static retro_time_t last_time;
static retro_time_t curr_time; static retro_time_t curr_time;
static retro_time_t fps_time; static retro_time_t fps_time;
static retro_time_t frame_time_accumulator;
static float last_fps, frame_time; static float last_fps, frame_time;
static uint64_t last_used_memory, last_total_memory; static uint64_t last_used_memory, last_total_memory;
/* Initialise 'last_frame_duped' to 'true' /* Initialise 'last_frame_duped' to 'true'
* to ensure that the first frame is rendered */ * to ensure that the first frame is rendered */
static bool last_frame_duped = true; static bool last_frame_duped = true;
bool render_frame = true;
retro_time_t new_time; retro_time_t new_time;
video_frame_info_t video_info; video_frame_info_t video_info;
video_driver_state_t *video_st= &video_driver_st; video_driver_state_t *video_st= &video_driver_st;
@ -3582,7 +3586,6 @@ void video_driver_frame(const void *data, unsigned width,
const enum retro_pixel_format const enum retro_pixel_format
video_driver_pix_fmt = video_st->pix_fmt; video_driver_pix_fmt = video_st->pix_fmt;
bool runloop_idle = runloop_st->idle; bool runloop_idle = runloop_st->idle;
bool render_frame = !runloop_st->fastforward_frameskip_frames_current;
bool video_driver_active = video_st->active; bool video_driver_active = video_st->active;
#if defined(HAVE_GFX_WIDGETS) #if defined(HAVE_GFX_WIDGETS)
bool widgets_active = dispwidget_get_ptr()->active; bool widgets_active = dispwidget_get_ptr()->active;
@ -3621,17 +3624,55 @@ void video_driver_frame(const void *data, unsigned width,
video_driver_build_info(&video_info); video_driver_build_info(&video_info);
/* Always render a frame if: /* If fast forward is active and fast forward
* - Menu is open * frame skipping is enabled, drop any frames
* that occur at a rate higher than the core-set
* refresh rate. However: We must always render
* the current frame when:
* - The menu is open
* - The last frame was NULL and the * - The last frame was NULL and the
* current frame is not (i.e. if core was * current frame is not (i.e. if core was
* previously sending duped frames, ensure * previously sending duped frames, ensure
* that the next frame update is captured) */ * that the next frame update is captured) */
render_frame |= video_info.menu_is_alive || (last_frame_duped && !!data); if (video_info.input_driver_nonblock_state &&
last_frame_duped = !data; video_info.fastforward_frameskip &&
!(video_info.menu_is_alive ||
(last_frame_duped && !!data)))
{
/* Accumulate the elapsed time since the
* last frame */
frame_time_accumulator += new_time - last_time;
if (!render_frame) /* Render frame if the accumulated time is
runloop_st->fastforward_frameskip_frames_current--; * greater than or equal to the expected
* core frame time */
render_frame = frame_time_accumulator >=
video_st->core_frame_time;
/* If frame is to be rendered, subtract
* expected frame time from accumulator */
if (render_frame)
{
frame_time_accumulator -= video_st->core_frame_time;
/* If fast forward is working correctly,
* the actual frame time will always be
* less than the expected frame time.
* But if the host cannot run the core
* fast enough to achieve at least 1x
* speed then the frame time accumulator
* will never empty and may potentially
* overflow. If a 'runaway' accumulator
* is detected, we simply reset it */
if (frame_time_accumulator > video_st->core_frame_time)
frame_time_accumulator = 0;
}
}
else
frame_time_accumulator = 0;
last_time = new_time;
last_frame_duped = !data;
/* Get the amount of frames per seconds. */ /* Get the amount of frames per seconds. */
if (video_st->frame_count) if (video_st->frame_count)
@ -3933,16 +3974,12 @@ void video_driver_frame(const void *data, unsigned width,
} }
if (render_frame && video_st->current_video && video_st->current_video->frame) if (render_frame && video_st->current_video && video_st->current_video->frame)
{
video_st->active = video_st->current_video->frame( video_st->active = video_st->current_video->frame(
video_st->data, data, width, height, video_st->data, data, width, height,
video_st->frame_count, (unsigned)pitch, video_st->frame_count, (unsigned)pitch,
video_info.menu_screensaver_active || video_info.notifications_hidden ? "" : video_driver_msg, video_info.menu_screensaver_active || video_info.notifications_hidden ? "" : video_driver_msg,
&video_info); &video_info);
runloop_st->fastforward_frameskip_frames_current = runloop_st->fastforward_frameskip_frames;
}
video_st->frame_count++; video_st->frame_count++;
/* Display the status text, with a higher priority. */ /* Display the status text, with a higher priority. */

View File

@ -497,6 +497,7 @@ typedef struct video_frame_info
bool timedate_enable; bool timedate_enable;
bool runloop_is_slowmotion; bool runloop_is_slowmotion;
bool runloop_is_paused; bool runloop_is_paused;
bool fastforward_frameskip;
bool menu_is_alive; bool menu_is_alive;
bool menu_screensaver_active; bool menu_screensaver_active;
bool msg_bgcolor_enable; bool msg_bgcolor_enable;
@ -819,6 +820,7 @@ typedef struct
#endif #endif
struct retro_system_av_info av_info; /* double alignment */ struct retro_system_av_info av_info; /* double alignment */
retro_time_t frame_time_samples[MEASURE_FRAME_TIME_SAMPLES_COUNT]; retro_time_t frame_time_samples[MEASURE_FRAME_TIME_SAMPLES_COUNT];
retro_time_t core_frame_time;
uint64_t frame_time_count; uint64_t frame_time_count;
uint64_t frame_count; uint64_t frame_count;
uint8_t *record_gpu_buffer; uint8_t *record_gpu_buffer;

View File

@ -2628,6 +2628,10 @@ bool runloop_environment_cb(unsigned cmd, void *data)
(*info)->timing.sample_rate); (*info)->timing.sample_rate);
memcpy(av_info, *info, sizeof(*av_info)); memcpy(av_info, *info, sizeof(*av_info));
video_st->core_frame_time = 1000000 /
((video_st->av_info.timing.fps > 0.0) ?
video_st->av_info.timing.fps : 60.0);
command_event(CMD_EVENT_REINIT, &reinit_flags); command_event(CMD_EVENT_REINIT, &reinit_flags);
if (no_video_reinit) if (no_video_reinit)
video_driver_set_aspect_ratio(); video_driver_set_aspect_ratio();
@ -4910,22 +4914,6 @@ static bool core_unload_game(void)
return true; return true;
} }
static void runloop_apply_fastmotion_frameskip(runloop_state_t *runloop_st, settings_t *settings)
{
unsigned frames = 0;
if (runloop_st->fastmotion && settings->bools.fastforward_frameskip)
{
frames = (unsigned)settings->floats.fastforward_ratio;
/* Pick refresh rate as unlimited throttle rate */
frames = (!frames) ? (unsigned)roundf(settings->floats.video_refresh_rate) : frames;
/* Decrease one to represent skipped frames */
frames--;
}
runloop_st->fastforward_frameskip_frames_current = runloop_st->fastforward_frameskip_frames = frames;
}
static void runloop_apply_fastmotion_override(runloop_state_t *runloop_st, settings_t *settings) static void runloop_apply_fastmotion_override(runloop_state_t *runloop_st, settings_t *settings)
{ {
video_driver_state_t *video_st = video_state_get_ptr(); video_driver_state_t *video_st = video_state_get_ptr();
@ -4963,7 +4951,6 @@ static void runloop_apply_fastmotion_override(runloop_state_t *runloop_st, setti
if (!runloop_st->fastmotion) if (!runloop_st->fastmotion)
runloop_st->fastforward_after_frames = 1; runloop_st->fastforward_after_frames = 1;
runloop_apply_fastmotion_frameskip(runloop_st, settings);
driver_set_nonblock_state(); driver_set_nonblock_state();
/* Reset frame time counter when toggling /* Reset frame time counter when toggling
@ -5315,6 +5302,9 @@ static bool core_load(unsigned poll_type_behavior)
return false; return false;
runloop_st->current_core.retro_get_system_av_info(&video_st->av_info); runloop_st->current_core.retro_get_system_av_info(&video_st->av_info);
video_st->core_frame_time = 1000000 /
((video_st->av_info.timing.fps > 0.0) ?
video_st->av_info.timing.fps : 60.0);
return true; return true;
} }
@ -7080,7 +7070,6 @@ static enum runloop_state_enum runloop_check_state(
runloop_st->fastmotion = true; runloop_st->fastmotion = true;
} }
runloop_apply_fastmotion_frameskip(runloop_st, settings);
driver_set_nonblock_state(); driver_set_nonblock_state();
/* Reset frame time counter when toggling /* Reset frame time counter when toggling

View File

@ -216,8 +216,6 @@ struct runloop
unsigned max_frames; unsigned max_frames;
unsigned audio_latency; unsigned audio_latency;
unsigned fastforward_after_frames; unsigned fastforward_after_frames;
unsigned fastforward_frameskip_frames;
unsigned fastforward_frameskip_frames_current;
unsigned perf_ptr_libretro; unsigned perf_ptr_libretro;
unsigned subsystem_current_count; unsigned subsystem_current_count;
unsigned entry_state_slot; unsigned entry_state_slot;