mirror of
https://github.com/libretro/RetroArch
synced 2025-02-21 00:40:09 +00:00
Rework optional fast-forward frame skipping: Drop frames based on frame timing (#13578)
This commit is contained in:
parent
6aa89b3723
commit
a953b27614
@ -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. */
|
||||||
|
@ -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;
|
||||||
|
25
runloop.c
25
runloop.c
@ -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
|
||||||
|
@ -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;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user