diff --git a/config.def.h b/config.def.h index 71d392d4be..069472afc7 100644 --- a/config.def.h +++ b/config.def.h @@ -1253,6 +1253,9 @@ static const bool savestate_thumbnail_enable = false; /* Maximum fast forward ratio. */ #define DEFAULT_FASTFORWARD_RATIO 0.0 +/* Skip frames when fast forwarding. */ +#define DEFAULT_FASTFORWARD_FRAMESKIP true + /* Enable runloop for variable refresh rate screens. Force x1 speed while handling fast forward too. */ #define DEFAULT_VRR_RUNLOOP_ENABLE false diff --git a/configuration.c b/configuration.c index df6b5288f8..2c1bab86d8 100644 --- a/configuration.c +++ b/configuration.c @@ -1654,6 +1654,7 @@ static struct config_bool_setting *populate_settings_bool( SETTING_BOOL("ui_menubar_enable", &settings->bools.ui_menubar_enable, true, DEFAULT_UI_MENUBAR_ENABLE, false); SETTING_BOOL("suspend_screensaver_enable", &settings->bools.ui_suspend_screensaver_enable, true, true, false); SETTING_BOOL("rewind_enable", &settings->bools.rewind_enable, true, DEFAULT_REWIND_ENABLE, false); + SETTING_BOOL("fastforward_frameskip", &settings->bools.fastforward_frameskip, true, DEFAULT_FASTFORWARD_FRAMESKIP, false); SETTING_BOOL("vrr_runloop_enable", &settings->bools.vrr_runloop_enable, true, DEFAULT_VRR_RUNLOOP_ENABLE, false); SETTING_BOOL("apply_cheats_after_toggle", &settings->bools.apply_cheats_after_toggle, true, DEFAULT_APPLY_CHEATS_AFTER_TOGGLE, false); SETTING_BOOL("apply_cheats_after_load", &settings->bools.apply_cheats_after_load, true, DEFAULT_APPLY_CHEATS_AFTER_LOAD, false); diff --git a/configuration.h b/configuration.h index dea12077b5..a9712d441f 100644 --- a/configuration.h +++ b/configuration.h @@ -834,6 +834,7 @@ typedef struct settings bool history_list_enable; bool playlist_entry_rename; bool rewind_enable; + bool fastforward_frameskip; bool vrr_runloop_enable; bool apply_cheats_after_toggle; bool apply_cheats_after_load; diff --git a/gfx/video_driver.c b/gfx/video_driver.c index 7ede7b1778..74efd3a9d5 100644 --- a/gfx/video_driver.c +++ b/gfx/video_driver.c @@ -3579,6 +3579,7 @@ 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; @@ -3617,6 +3618,11 @@ void video_driver_frame(const void *data, unsigned width, video_driver_build_info(&video_info); + render_frame |= video_info.menu_is_alive; + + if (!render_frame) + runloop_st->fastforward_frameskip_frames_current--; + /* Get the amount of frames per seconds. */ if (video_st->frame_count) { @@ -3772,7 +3778,7 @@ void video_driver_frame(const void *data, unsigned width, pitch, runloop_idle); #ifdef HAVE_VIDEO_FILTER - if (data && video_st->state_filter) + if (render_frame && data && video_st->state_filter) { unsigned output_width = 0; unsigned output_height = 0; @@ -3834,11 +3840,7 @@ void video_driver_frame(const void *data, unsigned width, msg_entry.category, msg_entry.prio, false, -#ifdef HAVE_MENU - menu_state_get_ptr()->alive -#else - false -#endif + video_info.menu_is_alive ); } /* ...otherwise, just output message via @@ -3858,7 +3860,7 @@ void video_driver_frame(const void *data, unsigned width, } } - if (video_info.statistics_show) + if (render_frame && video_info.statistics_show) { audio_statistics_t audio_stats; double stddev = 0.0; @@ -3920,13 +3922,17 @@ void video_driver_frame(const void *data, unsigned width, /* TODO/FIXME - add OSD chat text here */ } - if (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->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. */ diff --git a/intl/msg_hash_lbl.h b/intl/msg_hash_lbl.h index 06d1ef5291..ffbe6f84bc 100644 --- a/intl/msg_hash_lbl.h +++ b/intl/msg_hash_lbl.h @@ -1234,6 +1234,10 @@ MSG_HASH( MENU_ENUM_LABEL_FASTFORWARD_RATIO, "fastforward_ratio" ) +MSG_HASH( + MENU_ENUM_LABEL_FASTFORWARD_FRAMESKIP, + "fastforward_frameskip" + ) MSG_HASH( MENU_ENUM_LABEL_FILE_BROWSER_CORE, "file_browser_core" diff --git a/intl/msg_hash_us.h b/intl/msg_hash_us.h index 00bbb9b22c..7740c143f5 100644 --- a/intl/msg_hash_us.h +++ b/intl/msg_hash_us.h @@ -3460,6 +3460,14 @@ MSG_HASH( MENU_ENUM_SUBLABEL_FASTFORWARD_RATIO, "The maximum rate at which content will be run when using fast-forward (e.g., 5.0x for 60 fps content = 300 fps cap). If set to 0.0x, fast-forward ratio is unlimited (no FPS cap)." ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_FASTFORWARD_FRAMESKIP, + "Fast-Forward Frameskip" + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_FASTFORWARD_FRAMESKIP, + "Skip frames according to fast-forward rate. This conserves power and allows the use of 3rd party frame limiting." + ) MSG_HASH( MENU_ENUM_LABEL_VALUE_SLOWMOTION_RATIO, "Slow-Motion Rate" diff --git a/menu/cbs/menu_cbs_sublabel.c b/menu/cbs/menu_cbs_sublabel.c index 0e78511555..ff44c33f98 100644 --- a/menu/cbs/menu_cbs_sublabel.c +++ b/menu/cbs/menu_cbs_sublabel.c @@ -494,6 +494,7 @@ DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_video_gpu_record, MENU_ DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_savestate_auto_index, MENU_ENUM_SUBLABEL_SAVESTATE_AUTO_INDEX) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_block_sram_overwrite, MENU_ENUM_SUBLABEL_BLOCK_SRAM_OVERWRITE) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_fastforward_ratio, MENU_ENUM_SUBLABEL_FASTFORWARD_RATIO) +DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_fastforward_frameskip, MENU_ENUM_SUBLABEL_FASTFORWARD_FRAMESKIP) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_vrr_runloop_enable, MENU_ENUM_SUBLABEL_VRR_RUNLOOP_ENABLE) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_slowmotion_ratio, MENU_ENUM_SUBLABEL_SLOWMOTION_RATIO) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_run_ahead_enabled, MENU_ENUM_SUBLABEL_RUN_AHEAD_ENABLED) @@ -3409,6 +3410,9 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs, case MENU_ENUM_LABEL_FASTFORWARD_RATIO: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_fastforward_ratio); break; + case MENU_ENUM_LABEL_FASTFORWARD_FRAMESKIP: + BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_fastforward_frameskip); + break; case MENU_ENUM_LABEL_VRR_RUNLOOP_ENABLE: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_vrr_runloop_enable); break; diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index 577c199e58..9850028d7b 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -9393,6 +9393,7 @@ unsigned menu_displaylist_build_list( #endif {MENU_ENUM_LABEL_FRAME_TIME_COUNTER_SETTINGS, PARSE_ACTION}, {MENU_ENUM_LABEL_FASTFORWARD_RATIO, PARSE_ONLY_FLOAT}, + {MENU_ENUM_LABEL_FASTFORWARD_FRAMESKIP, PARSE_ONLY_BOOL }, {MENU_ENUM_LABEL_SLOWMOTION_RATIO, PARSE_ONLY_FLOAT}, {MENU_ENUM_LABEL_VRR_RUNLOOP_ENABLE, PARSE_ONLY_BOOL }, {MENU_ENUM_LABEL_MENU_THROTTLE_FRAMERATE, PARSE_ONLY_BOOL }, diff --git a/menu/menu_setting.c b/menu/menu_setting.c index e07f9f5df0..6c7e7c387c 100644 --- a/menu/menu_setting.c +++ b/menu/menu_setting.c @@ -13950,6 +13950,22 @@ static bool setting_append_list( MENU_SETTINGS_LIST_CURRENT_ADD_CMD(list, list_info, CMD_EVENT_SET_FRAME_LIMIT); menu_settings_list_current_add_range(list, list_info, 0, 10, 1.0, true, true); + CONFIG_BOOL( + list, list_info, + &settings->bools.fastforward_frameskip, + MENU_ENUM_LABEL_FASTFORWARD_FRAMESKIP, + MENU_ENUM_LABEL_VALUE_FASTFORWARD_FRAMESKIP, + DEFAULT_FASTFORWARD_FRAMESKIP, + MENU_ENUM_LABEL_VALUE_OFF, + MENU_ENUM_LABEL_VALUE_ON, + &group_info, + &subgroup_info, + parent_group, + general_write_handler, + general_read_handler, + SD_FLAG_NONE + ); + CONFIG_BOOL( list, list_info, &settings->bools.vrr_runloop_enable, diff --git a/msg_hash.h b/msg_hash.h index 03a4f8cbd9..ed8a7ade07 100644 --- a/msg_hash.h +++ b/msg_hash.h @@ -1993,6 +1993,7 @@ enum msg_hash_enums MENU_LABEL(OVERLAY_CENTER_Y), MENU_LABEL(FASTFORWARD_RATIO), + MENU_LABEL(FASTFORWARD_FRAMESKIP), MENU_LABEL(VRR_RUNLOOP_ENABLE), MENU_LABEL(REWIND_ENABLE), MENU_LABEL(CHEAT_APPLY_AFTER_TOGGLE), diff --git a/runloop.c b/runloop.c index 08f35ab9d5..95e8f5c29e 100644 --- a/runloop.c +++ b/runloop.c @@ -4910,6 +4910,22 @@ 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(); @@ -4947,6 +4963,7 @@ 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 @@ -7063,6 +7080,7 @@ 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 diff --git a/runloop.h b/runloop.h index 2beb9b8048..1e29a8a70d 100644 --- a/runloop.h +++ b/runloop.h @@ -216,6 +216,8 @@ 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;