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_slowmotion = runloop_st->slowmotion;
video_info->fastforward_frameskip = settings->bools.fastforward_frameskip;
video_info->input_driver_nonblock_state = input_st ?
input_st->nonblocking_flag : false;
@ -3568,13 +3569,16 @@ void video_driver_frame(const void *data, unsigned width,
{
char status_text[128];
static char video_driver_msg[256];
static retro_time_t last_time;
static retro_time_t curr_time;
static retro_time_t fps_time;
static retro_time_t frame_time_accumulator;
static float last_fps, frame_time;
static uint64_t last_used_memory, last_total_memory;
/* Initialise 'last_frame_duped' to 'true'
* to ensure that the first frame is rendered */
static bool last_frame_duped = true;
bool render_frame = true;
retro_time_t new_time;
video_frame_info_t video_info;
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
video_driver_pix_fmt = video_st->pix_fmt;
bool runloop_idle = runloop_st->idle;
bool render_frame = !runloop_st->fastforward_frameskip_frames_current;
bool video_driver_active = video_st->active;
#if defined(HAVE_GFX_WIDGETS)
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);
/* Always render a frame if:
* - Menu is open
/* If fast forward is active and fast forward
* 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
* current frame is not (i.e. if core was
* previously sending duped frames, ensure
* that the next frame update is captured) */
render_frame |= video_info.menu_is_alive || (last_frame_duped && !!data);
last_frame_duped = !data;
if (video_info.input_driver_nonblock_state &&
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)
runloop_st->fastforward_frameskip_frames_current--;
/* Render frame if the accumulated time is
* 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. */
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)
{
video_st->active = video_st->current_video->frame(
video_st->data, data, width, height,
video_st->frame_count, (unsigned)pitch,
video_info.menu_screensaver_active || video_info.notifications_hidden ? "" : video_driver_msg,
&video_info);
runloop_st->fastforward_frameskip_frames_current = runloop_st->fastforward_frameskip_frames;
}
video_st->frame_count++;
/* Display the status text, with a higher priority. */

View File

@ -497,6 +497,7 @@ typedef struct video_frame_info
bool timedate_enable;
bool runloop_is_slowmotion;
bool runloop_is_paused;
bool fastforward_frameskip;
bool menu_is_alive;
bool menu_screensaver_active;
bool msg_bgcolor_enable;
@ -819,6 +820,7 @@ typedef struct
#endif
struct retro_system_av_info av_info; /* double alignment */
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_count;
uint8_t *record_gpu_buffer;

View File

@ -2628,6 +2628,10 @@ bool runloop_environment_cb(unsigned cmd, void *data)
(*info)->timing.sample_rate);
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);
if (no_video_reinit)
video_driver_set_aspect_ratio();
@ -4910,22 +4914,6 @@ static bool core_unload_game(void)
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)
{
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)
runloop_st->fastforward_after_frames = 1;
runloop_apply_fastmotion_frameskip(runloop_st, settings);
driver_set_nonblock_state();
/* Reset frame time counter when toggling
@ -5315,6 +5302,9 @@ static bool core_load(unsigned poll_type_behavior)
return false;
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;
}
@ -7080,7 +7070,6 @@ static enum runloop_state_enum runloop_check_state(
runloop_st->fastmotion = true;
}
runloop_apply_fastmotion_frameskip(runloop_st, settings);
driver_set_nonblock_state();
/* Reset frame time counter when toggling

View File

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