diff --git a/gfx/video_driver.c b/gfx/video_driver.c index d977c1b1b41..9a6070e9aa6 100644 --- a/gfx/video_driver.c +++ b/gfx/video_driver.c @@ -4026,6 +4026,94 @@ void video_driver_reinit(int flags) video_st->window_title_prev[0] = '\0'; } +void video_frame_delay(video_driver_state_t *video_st, + settings_t *settings, + bool core_paused) +{ + runloop_state_t *runloop_st = runloop_state_get_ptr(); + unsigned video_frame_delay = settings->uints.video_frame_delay; + unsigned video_frame_delay_effective = video_st->frame_delay_effective; + bool skip_delay = core_paused + || (runloop_st->flags & RUNLOOP_FLAG_SLOWMOTION) + || (runloop_st->flags & RUNLOOP_FLAG_FASTMOTION); + + if (settings->bools.video_frame_delay_auto) + { + float refresh_rate = settings->floats.video_refresh_rate; + uint8_t video_swap_interval = runloop_get_video_swap_interval( + settings->uints.video_swap_interval); + uint8_t video_bfi = settings->uints.video_black_frame_insertion; + uint8_t frame_time_interval = 8; + static uint8_t skip_update = 0; + static bool skip_delay_prev = false; + bool frame_time_update = + /* Skip some initial frames for stabilization */ + video_st->frame_count > frame_time_interval + /* Only update when there are enough frames for averaging */ + && video_st->frame_count % frame_time_interval == 0; + + /* A few frames must be ignored after slow+fastmotion/pause + * is disabled or geometry change is triggered */ + if ( (!skip_delay && skip_delay_prev) + || video_st->frame_delay_pause) + { + skip_update = frame_time_interval * 4; + video_st->frame_delay_pause = false; + } + + if (skip_update) + skip_update--; + + skip_delay_prev = skip_delay; + + /* Always skip when slow+fastmotion/pause is active */ + if (skip_delay_prev) + skip_update = 1; + + if (skip_update) + frame_time_update = false; + + /* Black frame insertion + swap interval multiplier */ + refresh_rate = (refresh_rate / (video_bfi + 1.0f) / video_swap_interval); + + /* Set target moderately as half frame time with 0 (Auto) delay */ + if (video_frame_delay == 0) + video_frame_delay = 1 / refresh_rate * 1000 / 2; + + /* Reset new desired delay target */ + if (video_st->frame_delay_target != video_frame_delay) + { + frame_time_update = false; + video_st->frame_delay_target = video_frame_delay_effective = video_frame_delay; + RARCH_LOG("[Video]: Frame delay reset to %d ms.\n", video_frame_delay); + } + + /* Decide what should happen to effective delay */ + if (video_frame_delay_effective > 0 && frame_time_update) + { + video_frame_delay_auto_t vfda = {0}; + vfda.frame_time_interval = frame_time_interval; + vfda.refresh_rate = refresh_rate; + + video_frame_delay_auto(video_st, &vfda); + if (vfda.delay_decrease > 0) + { + video_frame_delay_effective -= vfda.delay_decrease; + RARCH_LOG("[Video]: Frame delay decrease by %d ms to %d ms due to frame time average: %d > %d.\n", + vfda.delay_decrease, video_frame_delay_effective, vfda.frame_time_avg, vfda.frame_time_target); + } + } + } + else + video_st->frame_delay_target = video_frame_delay_effective = video_frame_delay; + + video_st->frame_delay_effective = video_frame_delay_effective; + + /* Never apply frame delay when slow+fastmotion/pause is active */ + if (video_frame_delay_effective > 0 && !skip_delay) + retro_sleep(video_frame_delay_effective); +} + void video_frame_delay_auto(video_driver_state_t *video_st, video_frame_delay_auto_t *vfda) { int i; diff --git a/gfx/video_driver.h b/gfx/video_driver.h index 06b5d4db69e..52346b3bec6 100644 --- a/gfx/video_driver.h +++ b/gfx/video_driver.h @@ -1090,6 +1090,10 @@ bool *video_driver_get_threaded(void); void video_driver_set_threaded(bool val); +void video_frame_delay(video_driver_state_t *video_st, + settings_t *settings, + bool core_paused); + void video_frame_delay_auto(video_driver_state_t *video_st, video_frame_delay_auto_t *vfda); diff --git a/runloop.c b/runloop.c index 7a620ac2e5f..1b9603665fe 100644 --- a/runloop.c +++ b/runloop.c @@ -6807,8 +6807,6 @@ int runloop_iterate(void) #endif settings_t *settings = config_get_ptr(); runloop_state_t *runloop_st = &runloop_state; - unsigned video_frame_delay = settings->uints.video_frame_delay; - unsigned video_frame_delay_effective = video_st->frame_delay_effective; bool vrr_runloop_enable = settings->bools.vrr_runloop_enable; unsigned max_users = settings->uints.input_max_users; retro_time_t current_time = cpu_features_get_time_usec(); @@ -7057,91 +7055,6 @@ int runloop_iterate(void) } } - /* Frame delay */ - if ( !(input_st->flags & INP_FLAG_NONBLOCKING) - || (runloop_st->flags & RUNLOOP_FLAG_FASTMOTION)) - { - bool skip_delay = core_paused - || (runloop_st->flags & RUNLOOP_FLAG_SLOWMOTION) - || (runloop_st->flags & RUNLOOP_FLAG_FASTMOTION); - - if (settings->bools.video_frame_delay_auto) - { - float refresh_rate = settings->floats.video_refresh_rate; - uint8_t video_swap_interval = runloop_get_video_swap_interval( - settings->uints.video_swap_interval); - uint8_t video_bfi = settings->uints.video_black_frame_insertion; - uint8_t frame_time_interval = 8; - static uint8_t skip_update = 0; - static bool skip_delay_prev = false; - bool frame_time_update = - /* Skip some initial frames for stabilization */ - video_st->frame_count > frame_time_interval - /* Only update when there are enough frames for averaging */ - && video_st->frame_count % frame_time_interval == 0; - - /* A few frames must be ignored after slow+fastmotion/pause - * is disabled or geometry change is triggered */ - if ( (!skip_delay && skip_delay_prev) - || video_st->frame_delay_pause) - { - skip_update = frame_time_interval * 4; - video_st->frame_delay_pause = false; - } - - if (skip_update) - skip_update--; - - skip_delay_prev = skip_delay; - - /* Always skip when slow+fastmotion/pause is active */ - if (skip_delay_prev) - skip_update = 1; - - if (skip_update) - frame_time_update = false; - - /* Black frame insertion + swap interval multiplier */ - refresh_rate = (refresh_rate / (video_bfi + 1.0f) / video_swap_interval); - - /* Set target moderately as half frame time with 0 (Auto) delay */ - if (video_frame_delay == 0) - video_frame_delay = 1 / refresh_rate * 1000 / 2; - - /* Reset new desired delay target */ - if (video_st->frame_delay_target != video_frame_delay) - { - frame_time_update = false; - video_st->frame_delay_target = video_frame_delay_effective = video_frame_delay; - RARCH_LOG("[Video]: Frame delay reset to %d ms.\n", video_frame_delay); - } - - /* Decide what should happen to effective delay */ - if (video_frame_delay_effective > 0 && frame_time_update) - { - video_frame_delay_auto_t vfda = {0}; - vfda.frame_time_interval = frame_time_interval; - vfda.refresh_rate = refresh_rate; - - video_frame_delay_auto(video_st, &vfda); - if (vfda.delay_decrease > 0) - { - video_frame_delay_effective -= vfda.delay_decrease; - RARCH_LOG("[Video]: Frame delay decrease by %d ms to %d ms due to frame time average: %d > %d.\n", - vfda.delay_decrease, video_frame_delay_effective, vfda.frame_time_avg, vfda.frame_time_target); - } - } - } - else - video_st->frame_delay_target = video_frame_delay_effective = video_frame_delay; - - video_st->frame_delay_effective = video_frame_delay_effective; - - /* Never apply frame delay when slow+fastmotion/pause is active */ - if (video_frame_delay_effective > 0 && !skip_delay) - retro_sleep(video_frame_delay_effective); - } - { #ifdef HAVE_RUNAHEAD bool run_ahead_enabled = settings->bools.run_ahead_enabled; @@ -7225,6 +7138,11 @@ int runloop_iterate(void) autosave_unlock(); #endif + /* Frame delay */ + if ( !(input_st->flags & INP_FLAG_NONBLOCKING) + || (runloop_st->flags & RUNLOOP_FLAG_FASTMOTION)) + video_frame_delay(video_st, settings, core_paused); + end: if (vrr_runloop_enable) {