Add Preemptive Frames to Latency Settings (#14832)

This commit is contained in:
neil4 2023-01-10 00:22:14 -06:00 committed by GitHub
parent 975300a320
commit 7213aada8d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 855 additions and 47 deletions

View File

@ -261,6 +261,12 @@ enum event_command
CMD_EVENT_BSV_RECORDING_TOGGLE, CMD_EVENT_BSV_RECORDING_TOGGLE,
/* Toggle Run-Ahead. */ /* Toggle Run-Ahead. */
CMD_EVENT_RUNAHEAD_TOGGLE, CMD_EVENT_RUNAHEAD_TOGGLE,
/* Toggle Preemtive Frames. */
CMD_EVENT_PREEMPT_TOGGLE,
/* Deinitialize or Reinitialize Preemptive Frames. */
CMD_EVENT_PREEMPT_UPDATE,
/* Force Preemptive Frames to refill its state buffer. */
CMD_EVENT_PREEMPT_RESET_BUFFER,
/* Toggle VRR runloop. */ /* Toggle VRR runloop. */
CMD_EVENT_VRR_RUNLOOP_TOGGLE, CMD_EVENT_VRR_RUNLOOP_TOGGLE,
/* AI service. */ /* AI service. */
@ -505,6 +511,7 @@ static const struct cmd_map map[] = {
{ "VRR_RUNLOOP_TOGGLE", RARCH_VRR_RUNLOOP_TOGGLE }, { "VRR_RUNLOOP_TOGGLE", RARCH_VRR_RUNLOOP_TOGGLE },
{ "RUNAHEAD_TOGGLE", RARCH_RUNAHEAD_TOGGLE }, { "RUNAHEAD_TOGGLE", RARCH_RUNAHEAD_TOGGLE },
{ "PREEMPT_TOGGLE", RARCH_PREEMPT_TOGGLE },
{ "FPS_TOGGLE", RARCH_FPS_TOGGLE }, { "FPS_TOGGLE", RARCH_FPS_TOGGLE },
{ "STATISTICS_TOGGLE", RARCH_STATISTICS_TOGGLE }, { "STATISTICS_TOGGLE", RARCH_STATISTICS_TOGGLE },
{ "AI_SERVICE", RARCH_AI_SERVICE }, { "AI_SERVICE", RARCH_AI_SERVICE },

View File

@ -1299,6 +1299,8 @@
/* Hide warning messages when using the Run Ahead feature. */ /* Hide warning messages when using the Run Ahead feature. */
#define DEFAULT_RUN_AHEAD_HIDE_WARNINGS false #define DEFAULT_RUN_AHEAD_HIDE_WARNINGS false
/* Hide warning messages when using Preemptive Frames. */
#define DEFAULT_PREEMPT_HIDE_WARNINGS false
/* Enable stdin/network command interface. */ /* Enable stdin/network command interface. */
#define DEFAULT_NETWORK_CMD_ENABLE false #define DEFAULT_NETWORK_CMD_ENABLE false

View File

@ -543,6 +543,13 @@ static const struct retro_keybind retro_keybinds_1[] = {
RARCH_RUNAHEAD_TOGGLE, NO_BTN, NO_BTN, 0, RARCH_RUNAHEAD_TOGGLE, NO_BTN, NO_BTN, 0,
true true
}, },
{
NULL, NULL,
AXIS_NONE, AXIS_NONE, AXIS_NONE,
MENU_ENUM_LABEL_VALUE_INPUT_META_PREEMPT_TOGGLE, RETROK_UNKNOWN,
RARCH_PREEMPT_TOGGLE, NO_BTN, NO_BTN, 0,
true
},
{ {
NULL, NULL, NULL, NULL,
AXIS_NONE, AXIS_NONE, AXIS_NONE, AXIS_NONE, AXIS_NONE, AXIS_NONE,
@ -1143,6 +1150,13 @@ static const struct retro_keybind retro_keybinds_1[] = {
RARCH_RUNAHEAD_TOGGLE, NO_BTN, NO_BTN, 0, RARCH_RUNAHEAD_TOGGLE, NO_BTN, NO_BTN, 0,
true true
}, },
{
NULL, NULL,
AXIS_NONE, AXIS_NONE, AXIS_NONE,
MENU_ENUM_LABEL_VALUE_INPUT_META_PREEMPT_TOGGLE, RETROK_UNKNOWN,
RARCH_PREEMPT_TOGGLE, NO_BTN, NO_BTN, 0,
true
},
{ {
NULL, NULL, NULL, NULL,
AXIS_NONE, AXIS_NONE, AXIS_NONE, AXIS_NONE, AXIS_NONE, AXIS_NONE,
@ -1753,6 +1767,13 @@ static const struct retro_keybind retro_keybinds_1[] = {
RARCH_RUNAHEAD_TOGGLE, NO_BTN, NO_BTN, 0, RARCH_RUNAHEAD_TOGGLE, NO_BTN, NO_BTN, 0,
true true
}, },
{
NULL, NULL,
AXIS_NONE, AXIS_NONE, AXIS_NONE,
MENU_ENUM_LABEL_VALUE_INPUT_META_PREEMPT_TOGGLE, RETROK_UNKNOWN,
RARCH_PREEMPT_TOGGLE, NO_BTN, NO_BTN, 0,
true
},
{ {
NULL, NULL, NULL, NULL,
AXIS_NONE, AXIS_NONE, AXIS_NONE, AXIS_NONE, AXIS_NONE, AXIS_NONE,

View File

@ -357,6 +357,7 @@ const struct input_bind_map input_config_bind_map[RARCH_BIND_LIST_END_NULL] = {
DECLARE_META_BIND(2, toggle_vrr_runloop, RARCH_VRR_RUNLOOP_TOGGLE, MENU_ENUM_LABEL_VALUE_INPUT_META_VRR_RUNLOOP_TOGGLE), DECLARE_META_BIND(2, toggle_vrr_runloop, RARCH_VRR_RUNLOOP_TOGGLE, MENU_ENUM_LABEL_VALUE_INPUT_META_VRR_RUNLOOP_TOGGLE),
DECLARE_META_BIND(2, runahead_toggle, RARCH_RUNAHEAD_TOGGLE, MENU_ENUM_LABEL_VALUE_INPUT_META_RUNAHEAD_TOGGLE), DECLARE_META_BIND(2, runahead_toggle, RARCH_RUNAHEAD_TOGGLE, MENU_ENUM_LABEL_VALUE_INPUT_META_RUNAHEAD_TOGGLE),
DECLARE_META_BIND(2, preempt_toggle, RARCH_PREEMPT_TOGGLE, MENU_ENUM_LABEL_VALUE_INPUT_META_PREEMPT_TOGGLE),
DECLARE_META_BIND(2, fps_toggle, RARCH_FPS_TOGGLE, MENU_ENUM_LABEL_VALUE_INPUT_META_FPS_TOGGLE), DECLARE_META_BIND(2, fps_toggle, RARCH_FPS_TOGGLE, MENU_ENUM_LABEL_VALUE_INPUT_META_FPS_TOGGLE),
DECLARE_META_BIND(2, toggle_statistics, RARCH_STATISTICS_TOGGLE, MENU_ENUM_LABEL_VALUE_INPUT_META_STATISTICS_TOGGLE), DECLARE_META_BIND(2, toggle_statistics, RARCH_STATISTICS_TOGGLE, MENU_ENUM_LABEL_VALUE_INPUT_META_STATISTICS_TOGGLE),
DECLARE_META_BIND(2, ai_service, RARCH_AI_SERVICE, MENU_ENUM_LABEL_VALUE_INPUT_META_AI_SERVICE), DECLARE_META_BIND(2, ai_service, RARCH_AI_SERVICE, MENU_ENUM_LABEL_VALUE_INPUT_META_AI_SERVICE),
@ -1686,6 +1687,8 @@ static struct config_bool_setting *populate_settings_bool(
SETTING_BOOL("run_ahead_enabled", &settings->bools.run_ahead_enabled, true, false, false); SETTING_BOOL("run_ahead_enabled", &settings->bools.run_ahead_enabled, true, false, false);
SETTING_BOOL("run_ahead_secondary_instance", &settings->bools.run_ahead_secondary_instance, true, DEFAULT_RUN_AHEAD_SECONDARY_INSTANCE, false); SETTING_BOOL("run_ahead_secondary_instance", &settings->bools.run_ahead_secondary_instance, true, DEFAULT_RUN_AHEAD_SECONDARY_INSTANCE, false);
SETTING_BOOL("run_ahead_hide_warnings", &settings->bools.run_ahead_hide_warnings, true, DEFAULT_RUN_AHEAD_HIDE_WARNINGS, false); SETTING_BOOL("run_ahead_hide_warnings", &settings->bools.run_ahead_hide_warnings, true, DEFAULT_RUN_AHEAD_HIDE_WARNINGS, false);
SETTING_BOOL("preemptive_frames_enable", &settings->bools.preemptive_frames_enable, true, false, false);
SETTING_BOOL("preemptive_frames_hide_warnings", &settings->bools.preemptive_frames_hide_warnings, true, DEFAULT_PREEMPT_HIDE_WARNINGS, false);
SETTING_BOOL("audio_sync", &settings->bools.audio_sync, true, DEFAULT_AUDIO_SYNC, false); SETTING_BOOL("audio_sync", &settings->bools.audio_sync, true, DEFAULT_AUDIO_SYNC, false);
SETTING_BOOL("video_shader_enable", &settings->bools.video_shader_enable, true, DEFAULT_SHADER_ENABLE, false); SETTING_BOOL("video_shader_enable", &settings->bools.video_shader_enable, true, DEFAULT_SHADER_ENABLE, false);
SETTING_BOOL("video_shader_watch_files", &settings->bools.video_shader_watch_files, true, DEFAULT_VIDEO_SHADER_WATCH_FILES, false); SETTING_BOOL("video_shader_watch_files", &settings->bools.video_shader_watch_files, true, DEFAULT_VIDEO_SHADER_WATCH_FILES, false);

View File

@ -885,6 +885,8 @@ typedef struct settings
bool run_ahead_enabled; bool run_ahead_enabled;
bool run_ahead_secondary_instance; bool run_ahead_secondary_instance;
bool run_ahead_hide_warnings; bool run_ahead_hide_warnings;
bool preemptive_frames_enable;
bool preemptive_frames_hide_warnings;
bool pause_nonactive; bool pause_nonactive;
bool pause_on_disconnect; bool pause_on_disconnect;
bool block_sram_overwrite; bool block_sram_overwrite;

View File

@ -120,6 +120,7 @@ enum
RARCH_VRR_RUNLOOP_TOGGLE, RARCH_VRR_RUNLOOP_TOGGLE,
RARCH_RUNAHEAD_TOGGLE, RARCH_RUNAHEAD_TOGGLE,
RARCH_PREEMPT_TOGGLE,
RARCH_FPS_TOGGLE, RARCH_FPS_TOGGLE,
RARCH_STATISTICS_TOGGLE, RARCH_STATISTICS_TOGGLE,
RARCH_AI_SERVICE, RARCH_AI_SERVICE,

View File

@ -235,6 +235,10 @@ int msg_hash_get_help_id_enum(enum msg_hash_enums msg, char *s, size_t len)
snprintf(s, len, snprintf(s, len,
"Toggles Run-Ahead mode on/off."); "Toggles Run-Ahead mode on/off.");
break; break;
case RARCH_PREEMPT_TOGGLE:
snprintf(s, len,
"Toggles Preemptive Frames on/off.");
break;
default: default:
if (string_is_empty(s)) if (string_is_empty(s))
strlcpy(s, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NO_INFORMATION_AVAILABLE), len); strlcpy(s, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NO_INFORMATION_AVAILABLE), len);

View File

@ -3266,6 +3266,22 @@ MSG_HASH(
MENU_ENUM_LABEL_RUN_AHEAD_FRAMES, MENU_ENUM_LABEL_RUN_AHEAD_FRAMES,
"run_ahead_frames" "run_ahead_frames"
) )
MSG_HASH(
MENU_ENUM_LABEL_PREEMPT_ENABLE,
"preemptive_frames_enable"
)
MSG_HASH(
MENU_ENUM_LABEL_PREEMPT_UNSUPPORTED,
"preemptive_frames_unsupported"
)
MSG_HASH(
MENU_ENUM_LABEL_PREEMPT_HIDE_WARNINGS,
"preemptive_frames_hide_warnings"
)
MSG_HASH(
MENU_ENUM_LABEL_PREEMPT_FRAMES,
"preemptive_frames"
)
MSG_HASH( MSG_HASH(
MENU_ENUM_LABEL_SORT_SAVEFILES_ENABLE, MENU_ENUM_LABEL_SORT_SAVEFILES_ENABLE,
"sort_savefiles_enable" "sort_savefiles_enable"

View File

@ -3391,6 +3391,14 @@ MSG_HASH(
MENU_ENUM_SUBLABEL_INPUT_META_RUNAHEAD_TOGGLE, MENU_ENUM_SUBLABEL_INPUT_META_RUNAHEAD_TOGGLE,
"Switches Run-Ahead on/off." "Switches Run-Ahead on/off."
) )
MSG_HASH(
MENU_ENUM_LABEL_VALUE_INPUT_META_PREEMPT_TOGGLE,
"Preemptive Frames (Toggle)"
)
MSG_HASH(
MENU_ENUM_SUBLABEL_INPUT_META_PREEMPT_TOGGLE,
"Switches Preemptive Frames on/off."
)
MSG_HASH( MSG_HASH(
MENU_ENUM_LABEL_VALUE_INPUT_META_FPS_TOGGLE, MENU_ENUM_LABEL_VALUE_INPUT_META_FPS_TOGGLE,
@ -3720,6 +3728,38 @@ MSG_HASH(
MENU_ENUM_SUBLABEL_RUN_AHEAD_HIDE_WARNINGS, MENU_ENUM_SUBLABEL_RUN_AHEAD_HIDE_WARNINGS,
"Hide the warning message that appears when using Run-Ahead and the core does not support save states." "Hide the warning message that appears when using Run-Ahead and the core does not support save states."
) )
MSG_HASH(
MENU_ENUM_LABEL_VALUE_PREEMPT_UNSUPPORTED,
"[Preemptive Frames Unavailable]"
)
MSG_HASH(
MENU_ENUM_SUBLABEL_PREEMPT_UNSUPPORTED,
"Current core is incompatible with preemptive frames due to lack of deterministic save state support."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_PREEMPT_ENABLE,
"Run Preemptive Frames"
)
MSG_HASH(
MENU_ENUM_SUBLABEL_PREEMPT_ENABLE,
"Rerun core logic with the latest input when the controller state changes. Faster than Run-Ahead, but does not prevent audio issues cores may have with loading states."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_PREEMPT_FRAMES,
"Number of Preemptive Frames"
)
MSG_HASH(
MENU_ENUM_SUBLABEL_PREEMPT_FRAMES,
"The number of frames to rerun. Causes gameplay issues such as jitter if the number of lag frames internal to the game is exceeded."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_PREEMPT_HIDE_WARNINGS,
"Hide Preemptive Frames Warnings"
)
MSG_HASH(
MENU_ENUM_SUBLABEL_PREEMPT_HIDE_WARNINGS,
"Hide the warning message that appears when a core is incompatible with preemptive frames."
)
/* Settings > Core */ /* Settings > Core */
@ -13902,6 +13942,34 @@ MSG_HASH(
MSG_RUNAHEAD_FAILED_TO_CREATE_SECONDARY_INSTANCE, MSG_RUNAHEAD_FAILED_TO_CREATE_SECONDARY_INSTANCE,
"Failed to create second instance. Run-Ahead will now use only one instance." "Failed to create second instance. Run-Ahead will now use only one instance."
) )
MSG_HASH(
MSG_PREEMPT_ENABLED,
"Preemptive Frames enabled. Latency frames removed: %u."
)
MSG_HASH(
MSG_PREEMPT_DISABLED,
"Preemptive Frames disabled."
)
MSG_HASH(
MSG_PREEMPT_CORE_DOES_NOT_SUPPORT_SAVESTATES,
"Preemptive Frames has been disabled because this core does not support save states."
)
MSG_HASH(
MSG_PREEMPT_CORE_DOES_NOT_SUPPORT_PREEMPT,
"Preemptive Frames unavailable because this core lacks deterministic save state support."
)
MSG_HASH(
MSG_PREEMPT_FAILED_TO_ALLOCATE,
"Failed to allocate memory for Preemptive Frames."
)
MSG_HASH(
MSG_PREEMPT_FAILED_TO_SAVE_STATE,
"Failed to save state. Preemptive Frames has been disabled."
)
MSG_HASH(
MSG_PREEMPT_FAILED_TO_LOAD_STATE,
"Failed to load state. Preemptive Frames has been disabled."
)
MSG_HASH( MSG_HASH(
MSG_SCANNING_OF_FILE_FINISHED, MSG_SCANNING_OF_FILE_FINISHED,
"Scanning of file finished" "Scanning of file finished"

View File

@ -433,6 +433,7 @@ DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_input_meta_ui_companion_toggle, ME
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_input_meta_vrr_runloop_toggle, MENU_ENUM_SUBLABEL_INPUT_META_VRR_RUNLOOP_TOGGLE) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_input_meta_vrr_runloop_toggle, MENU_ENUM_SUBLABEL_INPUT_META_VRR_RUNLOOP_TOGGLE)
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_input_meta_runahead_toggle, MENU_ENUM_SUBLABEL_INPUT_META_RUNAHEAD_TOGGLE) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_input_meta_runahead_toggle, MENU_ENUM_SUBLABEL_INPUT_META_RUNAHEAD_TOGGLE)
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_input_meta_preempt_toggle, MENU_ENUM_SUBLABEL_INPUT_META_PREEMPT_TOGGLE)
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_input_meta_fps_toggle, MENU_ENUM_SUBLABEL_INPUT_META_FPS_TOGGLE) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_input_meta_fps_toggle, MENU_ENUM_SUBLABEL_INPUT_META_FPS_TOGGLE)
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_input_meta_statistics_toggle, MENU_ENUM_SUBLABEL_INPUT_META_STATISTICS_TOGGLE) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_input_meta_statistics_toggle, MENU_ENUM_SUBLABEL_INPUT_META_STATISTICS_TOGGLE)
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_input_meta_ai_service, MENU_ENUM_SUBLABEL_INPUT_META_AI_SERVICE) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_input_meta_ai_service, MENU_ENUM_SUBLABEL_INPUT_META_AI_SERVICE)
@ -667,6 +668,10 @@ DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_run_ahead_enabled, MENU_
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_run_ahead_secondary_instance, MENU_ENUM_SUBLABEL_RUN_AHEAD_SECONDARY_INSTANCE) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_run_ahead_secondary_instance, MENU_ENUM_SUBLABEL_RUN_AHEAD_SECONDARY_INSTANCE)
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_run_ahead_hide_warnings, MENU_ENUM_SUBLABEL_RUN_AHEAD_HIDE_WARNINGS) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_run_ahead_hide_warnings, MENU_ENUM_SUBLABEL_RUN_AHEAD_HIDE_WARNINGS)
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_run_ahead_frames, MENU_ENUM_SUBLABEL_RUN_AHEAD_FRAMES) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_run_ahead_frames, MENU_ENUM_SUBLABEL_RUN_AHEAD_FRAMES)
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_preempt_unsupported, MENU_ENUM_SUBLABEL_PREEMPT_UNSUPPORTED)
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_preempt_enable, MENU_ENUM_SUBLABEL_PREEMPT_ENABLE)
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_preempt_hide_warnings, MENU_ENUM_SUBLABEL_PREEMPT_HIDE_WARNINGS)
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_preempt_frames, MENU_ENUM_SUBLABEL_PREEMPT_FRAMES)
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_input_block_timeout, MENU_ENUM_SUBLABEL_INPUT_BLOCK_TIMEOUT) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_input_block_timeout, MENU_ENUM_SUBLABEL_INPUT_BLOCK_TIMEOUT)
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_rewind, MENU_ENUM_SUBLABEL_REWIND_ENABLE) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_rewind, MENU_ENUM_SUBLABEL_REWIND_ENABLE)
#ifdef HAVE_CHEATS #ifdef HAVE_CHEATS
@ -2254,6 +2259,9 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs,
case RARCH_RUNAHEAD_TOGGLE: case RARCH_RUNAHEAD_TOGGLE:
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_input_meta_runahead_toggle); BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_input_meta_runahead_toggle);
return 0; return 0;
case RARCH_PREEMPT_TOGGLE:
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_input_meta_preempt_toggle);
return 0;
case RARCH_FPS_TOGGLE: case RARCH_FPS_TOGGLE:
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_input_meta_fps_toggle); BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_input_meta_fps_toggle);
return 0; return 0;
@ -3831,6 +3839,18 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs,
case MENU_ENUM_LABEL_RUN_AHEAD_FRAMES: case MENU_ENUM_LABEL_RUN_AHEAD_FRAMES:
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_run_ahead_frames); BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_run_ahead_frames);
break; break;
case MENU_ENUM_LABEL_PREEMPT_UNSUPPORTED:
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_preempt_unsupported);
break;
case MENU_ENUM_LABEL_PREEMPT_ENABLE:
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_preempt_enable);
break;
case MENU_ENUM_LABEL_PREEMPT_FRAMES:
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_preempt_frames);
break;
case MENU_ENUM_LABEL_PREEMPT_HIDE_WARNINGS:
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_preempt_hide_warnings);
break;
case MENU_ENUM_LABEL_INPUT_BLOCK_TIMEOUT: case MENU_ENUM_LABEL_INPUT_BLOCK_TIMEOUT:
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_input_block_timeout); BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_input_block_timeout);
break; break;

View File

@ -9265,6 +9265,7 @@ unsigned menu_displaylist_build_list(
#ifdef HAVE_RUNAHEAD #ifdef HAVE_RUNAHEAD
bool runahead_supported = true; bool runahead_supported = true;
bool runahead_enabled = settings->bools.run_ahead_enabled; bool runahead_enabled = settings->bools.run_ahead_enabled;
bool preempt_enabled = settings->bools.preemptive_frames_enable;
#endif #endif
menu_displaylist_build_info_selective_t build_list[] = { menu_displaylist_build_info_selective_t build_list[] = {
{MENU_ENUM_LABEL_VIDEO_FRAME_DELAY, PARSE_ONLY_UINT, true }, {MENU_ENUM_LABEL_VIDEO_FRAME_DELAY, PARSE_ONLY_UINT, true },
@ -9277,6 +9278,9 @@ unsigned menu_displaylist_build_list(
{MENU_ENUM_LABEL_RUN_AHEAD_FRAMES, PARSE_ONLY_UINT, false }, {MENU_ENUM_LABEL_RUN_AHEAD_FRAMES, PARSE_ONLY_UINT, false },
{MENU_ENUM_LABEL_RUN_AHEAD_SECONDARY_INSTANCE, PARSE_ONLY_BOOL, false }, {MENU_ENUM_LABEL_RUN_AHEAD_SECONDARY_INSTANCE, PARSE_ONLY_BOOL, false },
{MENU_ENUM_LABEL_RUN_AHEAD_HIDE_WARNINGS, PARSE_ONLY_BOOL, false }, {MENU_ENUM_LABEL_RUN_AHEAD_HIDE_WARNINGS, PARSE_ONLY_BOOL, false },
{MENU_ENUM_LABEL_PREEMPT_ENABLE, PARSE_ONLY_BOOL, false },
{MENU_ENUM_LABEL_PREEMPT_FRAMES, PARSE_ONLY_UINT, false },
{MENU_ENUM_LABEL_PREEMPT_HIDE_WARNINGS, PARSE_ONLY_BOOL, false },
#endif #endif
}; };
@ -9330,6 +9334,7 @@ unsigned menu_displaylist_build_list(
switch (build_list[i].enum_idx) switch (build_list[i].enum_idx)
{ {
case MENU_ENUM_LABEL_RUN_AHEAD_ENABLED: case MENU_ENUM_LABEL_RUN_AHEAD_ENABLED:
case MENU_ENUM_LABEL_PREEMPT_ENABLE:
build_list[i].checked = true; build_list[i].checked = true;
break; break;
case MENU_ENUM_LABEL_RUN_AHEAD_FRAMES: case MENU_ENUM_LABEL_RUN_AHEAD_FRAMES:
@ -9338,6 +9343,11 @@ unsigned menu_displaylist_build_list(
if (runahead_enabled) if (runahead_enabled)
build_list[i].checked = true; build_list[i].checked = true;
break; break;
case MENU_ENUM_LABEL_PREEMPT_FRAMES:
case MENU_ENUM_LABEL_PREEMPT_HIDE_WARNINGS:
if (preempt_enabled)
build_list[i].checked = true;
break;
default: default:
break; break;
} }
@ -9356,13 +9366,21 @@ unsigned menu_displaylist_build_list(
} }
#ifdef HAVE_RUNAHEAD #ifdef HAVE_RUNAHEAD
if (!runahead_supported && if (!runahead_supported)
menu_entries_append(list, {
if (menu_entries_append(list,
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_RUN_AHEAD_UNSUPPORTED), msg_hash_to_str(MENU_ENUM_LABEL_VALUE_RUN_AHEAD_UNSUPPORTED),
msg_hash_to_str(MENU_ENUM_LABEL_RUN_AHEAD_UNSUPPORTED), msg_hash_to_str(MENU_ENUM_LABEL_RUN_AHEAD_UNSUPPORTED),
MENU_ENUM_LABEL_RUN_AHEAD_UNSUPPORTED, MENU_ENUM_LABEL_RUN_AHEAD_UNSUPPORTED,
FILE_TYPE_NONE, 0, 0, NULL)) FILE_TYPE_NONE, 0, 0, NULL))
count++; count++;
if (menu_entries_append(list,
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_PREEMPT_UNSUPPORTED),
msg_hash_to_str(MENU_ENUM_LABEL_PREEMPT_UNSUPPORTED),
MENU_ENUM_LABEL_PREEMPT_UNSUPPORTED,
FILE_TYPE_NONE, 0, 0, NULL))
count++;
}
#endif #endif
if (MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(list, if (MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(list,
MENU_ENUM_LABEL_GAMEMODE_ENABLE, PARSE_ONLY_BOOL, false) == 0) MENU_ENUM_LABEL_GAMEMODE_ENABLE, PARSE_ONLY_BOOL, false) == 0)

View File

@ -6982,6 +6982,13 @@ void retroarch_menu_running_finished(bool quit)
command_event(CMD_EVENT_GAME_FOCUS_TOGGLE, &game_focus_cmd); command_event(CMD_EVENT_GAME_FOCUS_TOGGLE, &game_focus_cmd);
} }
} }
#if HAVE_RUNAHEAD
/* Preemptive Frames isn't run behind the menu,
* so its savestate buffer is out of date. */
if (!settings->bools.menu_pause_libretro)
command_event(CMD_EVENT_PREEMPT_RESET_BUFFER, NULL);
#endif
} }
/* Ensure that menu screensaver is disabled when /* Ensure that menu screensaver is disabled when

View File

@ -8654,31 +8654,6 @@ static void general_write_handler(rarch_setting_t *setting)
default_aspect; default_aspect;
} }
break; break;
#if defined(HAVE_RUNAHEAD) && (defined(HAVE_DYNAMIC) || defined(HAVE_DYLIB))
case MENU_ENUM_LABEL_RUN_AHEAD_ENABLED:
case MENU_ENUM_LABEL_RUN_AHEAD_FRAMES:
case MENU_ENUM_LABEL_RUN_AHEAD_SECONDARY_INSTANCE:
{
settings_t *settings = config_get_ptr();
bool run_ahead_enabled = settings->bools.run_ahead_enabled;
unsigned run_ahead_frames = settings->uints.run_ahead_frames;
bool run_ahead_secondary_instance = settings->bools.run_ahead_secondary_instance;
/* If any changes here will cause second
* instance runahead to be enabled, must
* re-apply cheats to ensure that they
* propagate to the newly-created secondary
* core */
if (run_ahead_enabled &&
(run_ahead_frames > 0) &&
run_ahead_secondary_instance &&
!retroarch_ctl(RARCH_CTL_IS_SECOND_CORE_LOADED, NULL) &&
retroarch_ctl(RARCH_CTL_IS_SECOND_CORE_AVAILABLE, NULL) &&
command_event(CMD_EVENT_LOAD_SECOND_CORE, NULL))
command_event(CMD_EVENT_CHEATS_APPLY, NULL);
}
break;
#endif
default: default:
/* Special cases */ /* Special cases */
@ -8713,6 +8688,104 @@ static void frontend_log_level_change_handler(rarch_setting_t *setting)
verbosity_set_log_level(*setting->value.target.unsigned_integer); verbosity_set_log_level(*setting->value.target.unsigned_integer);
} }
#ifdef HAVE_RUNAHEAD
static void runahead_change_handler(rarch_setting_t *setting)
{
settings_t *settings = config_get_ptr();
bool run_ahead_enabled = settings->bools.run_ahead_enabled;
bool preempt_enabled = settings->bools.preemptive_frames_enable;
bool refresh = false;
#if (defined(HAVE_DYNAMIC) || defined(HAVE_DYLIB))
unsigned run_ahead_frames = settings->uints.run_ahead_frames;
bool run_ahead_secondary_instance = settings->bools.run_ahead_secondary_instance;
#endif
if (!setting)
return;
switch (setting->enum_idx)
{
case MENU_ENUM_LABEL_RUN_AHEAD_ENABLED:
if (run_ahead_enabled && preempt_enabled)
{
/* Disable preemptive frames and inform user */
settings->bools.preemptive_frames_enable = false;
runloop_preempt_deinit();
runloop_msg_queue_push(
msg_hash_to_str(MSG_PREEMPT_DISABLED), 1, 100, false,
NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
}
menu_entries_ctl(MENU_ENTRIES_CTL_SET_REFRESH, &refresh);
/* fall-through */
case MENU_ENUM_LABEL_RUN_AHEAD_FRAMES:
#if (defined(HAVE_DYNAMIC) || defined(HAVE_DYLIB))
case MENU_ENUM_LABEL_RUN_AHEAD_SECONDARY_INSTANCE:
/* If any changes here will cause second
* instance runahead to be enabled, must
* re-apply cheats to ensure that they
* propagate to the newly-created secondary
* core */
if ( run_ahead_enabled
&& (run_ahead_frames > 0)
&& run_ahead_secondary_instance
&& !retroarch_ctl(RARCH_CTL_IS_SECOND_CORE_LOADED, NULL)
&& retroarch_ctl(RARCH_CTL_IS_SECOND_CORE_AVAILABLE, NULL)
&& command_event(CMD_EVENT_LOAD_SECOND_CORE, NULL))
command_event(CMD_EVENT_CHEATS_APPLY, NULL);
#endif
break;
default:
break;
}
}
static void preempt_change_handler(rarch_setting_t *setting)
{
settings_t *settings = config_get_ptr();
bool preempt_enabled = settings->bools.preemptive_frames_enable;
bool run_ahead_enabled = settings->bools.run_ahead_enabled;
preempt_t *preempt = runloop_state_get_ptr()->preempt_data;
bool refresh = false;
bool netplay_enabled;
#ifdef HAVE_NETWORKING
netplay_enabled = netplay_driver_ctl(RARCH_NETPLAY_CTL_IS_ENABLED, NULL);
#else
netplay_enabled = false;
#endif
if (!setting)
return;
switch (setting->enum_idx)
{
case MENU_ENUM_LABEL_PREEMPT_ENABLE:
if (preempt_enabled && run_ahead_enabled)
{
/* Disable runahead and inform user */
settings->bools.run_ahead_enabled = false;
runloop_msg_queue_push(
msg_hash_to_str(MSG_RUNAHEAD_DISABLED), 1, 100, false,
NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
}
if ((preempt_enabled != !!preempt) && !netplay_enabled)
command_event(CMD_EVENT_PREEMPT_UPDATE, NULL);
menu_entries_ctl(MENU_ENTRIES_CTL_SET_REFRESH, &refresh);
break;
case MENU_ENUM_LABEL_PREEMPT_FRAMES:
if ( preempt
&& preempt->frames != settings->uints.run_ahead_frames
&& !netplay_enabled)
command_event(CMD_EVENT_PREEMPT_UPDATE, NULL);
break;
default:
break;
}
}
#endif
#ifdef HAVE_OVERLAY #ifdef HAVE_OVERLAY
static void overlay_enable_toggle_change_handler(rarch_setting_t *setting) static void overlay_enable_toggle_change_handler(rarch_setting_t *setting)
{ {
@ -14793,6 +14866,7 @@ static bool setting_append_list(
(*list)[list_info->index - 1].action_ok = &setting_action_ok_uint; (*list)[list_info->index - 1].action_ok = &setting_action_ok_uint;
menu_settings_list_current_add_range(list, list_info, 1, 10, 0.1, true, true); menu_settings_list_current_add_range(list, list_info, 1, 10, 0.1, true, true);
#ifdef HAVE_RUNAHEAD
CONFIG_BOOL( CONFIG_BOOL(
list, list_info, list, list_info,
&settings->bools.run_ahead_enabled, &settings->bools.run_ahead_enabled,
@ -14808,9 +14882,7 @@ static bool setting_append_list(
general_read_handler, general_read_handler,
SD_FLAG_NONE SD_FLAG_NONE
); );
(*list)[list_info->index - 1].action_ok = setting_bool_action_left_with_refresh; (*list)[list_info->index - 1].change_handler = runahead_change_handler;
(*list)[list_info->index - 1].action_left = setting_bool_action_left_with_refresh;
(*list)[list_info->index - 1].action_right = setting_bool_action_right_with_refresh;
CONFIG_UINT( CONFIG_UINT(
list, list_info, list, list_info,
@ -14826,7 +14898,8 @@ static bool setting_append_list(
(*list)[list_info->index - 1].ui_type = ST_UI_TYPE_UINT_COMBOBOX; (*list)[list_info->index - 1].ui_type = ST_UI_TYPE_UINT_COMBOBOX;
(*list)[list_info->index - 1].action_ok = &setting_action_ok_uint; (*list)[list_info->index - 1].action_ok = &setting_action_ok_uint;
(*list)[list_info->index - 1].offset_by = 1; (*list)[list_info->index - 1].offset_by = 1;
menu_settings_list_current_add_range(list, list_info, 1, 12, 1, true, true); (*list)[list_info->index - 1].change_handler = runahead_change_handler;
menu_settings_list_current_add_range(list, list_info, 1, MAX_RUNAHEAD_FRAMES, 1, true, true);
#if defined(HAVE_DYNAMIC) || defined(HAVE_DYLIB) #if defined(HAVE_DYNAMIC) || defined(HAVE_DYLIB)
CONFIG_BOOL( CONFIG_BOOL(
@ -14844,6 +14917,7 @@ static bool setting_append_list(
general_read_handler, general_read_handler,
SD_FLAG_NONE SD_FLAG_NONE
); );
(*list)[list_info->index - 1].change_handler = runahead_change_handler;
#endif #endif
CONFIG_BOOL( CONFIG_BOOL(
@ -14862,6 +14936,56 @@ static bool setting_append_list(
SD_FLAG_ADVANCED SD_FLAG_ADVANCED
); );
CONFIG_BOOL(
list, list_info,
&settings->bools.preemptive_frames_enable,
MENU_ENUM_LABEL_PREEMPT_ENABLE,
MENU_ENUM_LABEL_VALUE_PREEMPT_ENABLE,
false,
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);
(*list)[list_info->index - 1].change_handler = preempt_change_handler;
CONFIG_UINT(
list, list_info,
&settings->uints.run_ahead_frames,
MENU_ENUM_LABEL_PREEMPT_FRAMES,
MENU_ENUM_LABEL_VALUE_PREEMPT_FRAMES,
1,
&group_info,
&subgroup_info,
parent_group,
general_write_handler,
general_read_handler);
(*list)[list_info->index - 1].ui_type = ST_UI_TYPE_UINT_COMBOBOX;
(*list)[list_info->index - 1].action_ok = &setting_action_ok_uint;
(*list)[list_info->index - 1].offset_by = 1;
(*list)[list_info->index - 1].change_handler = preempt_change_handler;
menu_settings_list_current_add_range(list, list_info, 1, MAX_RUNAHEAD_FRAMES, 1, true, true);
CONFIG_BOOL(
list, list_info,
&settings->bools.preemptive_frames_hide_warnings,
MENU_ENUM_LABEL_PREEMPT_HIDE_WARNINGS,
MENU_ENUM_LABEL_VALUE_PREEMPT_HIDE_WARNINGS,
DEFAULT_PREEMPT_HIDE_WARNINGS,
MENU_ENUM_LABEL_VALUE_OFF,
MENU_ENUM_LABEL_VALUE_ON,
&group_info,
&subgroup_info,
parent_group,
general_write_handler,
general_read_handler,
SD_FLAG_ADVANCED
);
#endif
#ifdef ANDROID #ifdef ANDROID
CONFIG_UINT( CONFIG_UINT(
list, list_info, list, list_info,

View File

@ -520,6 +520,13 @@ enum msg_hash_enums
MSG_RUNAHEAD_FAILED_TO_SAVE_STATE, MSG_RUNAHEAD_FAILED_TO_SAVE_STATE,
MSG_RUNAHEAD_FAILED_TO_LOAD_STATE, MSG_RUNAHEAD_FAILED_TO_LOAD_STATE,
MSG_RUNAHEAD_FAILED_TO_CREATE_SECONDARY_INSTANCE, MSG_RUNAHEAD_FAILED_TO_CREATE_SECONDARY_INSTANCE,
MSG_PREEMPT_ENABLED,
MSG_PREEMPT_DISABLED,
MSG_PREEMPT_CORE_DOES_NOT_SUPPORT_SAVESTATES,
MSG_PREEMPT_CORE_DOES_NOT_SUPPORT_PREEMPT,
MSG_PREEMPT_FAILED_TO_ALLOCATE,
MSG_PREEMPT_FAILED_TO_SAVE_STATE,
MSG_PREEMPT_FAILED_TO_LOAD_STATE,
MSG_MISSING_ASSETS, MSG_MISSING_ASSETS,
MSG_RGUI_MISSING_FONTS, MSG_RGUI_MISSING_FONTS,
MSG_RGUI_INVALID_LANGUAGE, MSG_RGUI_INVALID_LANGUAGE,
@ -1033,6 +1040,7 @@ enum msg_hash_enums
MENU_ENUM_LABEL_VALUE_INPUT_META_VRR_RUNLOOP_TOGGLE, MENU_ENUM_LABEL_VALUE_INPUT_META_VRR_RUNLOOP_TOGGLE,
MENU_ENUM_LABEL_VALUE_INPUT_META_RUNAHEAD_TOGGLE, MENU_ENUM_LABEL_VALUE_INPUT_META_RUNAHEAD_TOGGLE,
MENU_ENUM_LABEL_VALUE_INPUT_META_PREEMPT_TOGGLE,
MENU_ENUM_LABEL_VALUE_INPUT_META_FPS_TOGGLE, MENU_ENUM_LABEL_VALUE_INPUT_META_FPS_TOGGLE,
MENU_ENUM_LABEL_VALUE_INPUT_META_STATISTICS_TOGGLE, MENU_ENUM_LABEL_VALUE_INPUT_META_STATISTICS_TOGGLE,
MENU_ENUM_LABEL_VALUE_INPUT_META_AI_SERVICE, MENU_ENUM_LABEL_VALUE_INPUT_META_AI_SERVICE,
@ -1109,6 +1117,7 @@ enum msg_hash_enums
MENU_ENUM_SUBLABEL_INPUT_META_VRR_RUNLOOP_TOGGLE, MENU_ENUM_SUBLABEL_INPUT_META_VRR_RUNLOOP_TOGGLE,
MENU_ENUM_SUBLABEL_INPUT_META_RUNAHEAD_TOGGLE, MENU_ENUM_SUBLABEL_INPUT_META_RUNAHEAD_TOGGLE,
MENU_ENUM_SUBLABEL_INPUT_META_PREEMPT_TOGGLE,
MENU_ENUM_SUBLABEL_INPUT_META_FPS_TOGGLE, MENU_ENUM_SUBLABEL_INPUT_META_FPS_TOGGLE,
MENU_ENUM_SUBLABEL_INPUT_META_STATISTICS_TOGGLE, MENU_ENUM_SUBLABEL_INPUT_META_STATISTICS_TOGGLE,
MENU_ENUM_SUBLABEL_INPUT_META_AI_SERVICE, MENU_ENUM_SUBLABEL_INPUT_META_AI_SERVICE,
@ -2444,6 +2453,10 @@ enum msg_hash_enums
MENU_LABEL(RUN_AHEAD_SECONDARY_INSTANCE), MENU_LABEL(RUN_AHEAD_SECONDARY_INSTANCE),
MENU_LABEL(RUN_AHEAD_HIDE_WARNINGS), MENU_LABEL(RUN_AHEAD_HIDE_WARNINGS),
MENU_LABEL(RUN_AHEAD_FRAMES), MENU_LABEL(RUN_AHEAD_FRAMES),
MENU_LABEL(PREEMPT_UNSUPPORTED),
MENU_LABEL(PREEMPT_ENABLE),
MENU_LABEL(PREEMPT_FRAMES),
MENU_LABEL(PREEMPT_HIDE_WARNINGS),
MENU_LABEL(INPUT_BLOCK_TIMEOUT), MENU_LABEL(INPUT_BLOCK_TIMEOUT),
MENU_LABEL(TURBO), MENU_LABEL(TURBO),

View File

@ -8435,6 +8435,11 @@ void deinit_netplay(void)
net_st->data = NULL; net_st->data = NULL;
net_st->flags &= ~(NET_DRIVER_ST_FLAG_NETPLAY_ENABLED net_st->flags &= ~(NET_DRIVER_ST_FLAG_NETPLAY_ENABLED
| NET_DRIVER_ST_FLAG_NETPLAY_IS_CLIENT); | NET_DRIVER_ST_FLAG_NETPLAY_IS_CLIENT);
#if HAVE_RUNAHEAD
/* Reinitialize preemptive frames if enabled */
runloop_preempt_init();
#endif
} }
free(net_st->client_info); free(net_st->client_info);

View File

@ -2228,10 +2228,8 @@ bool command_event(enum event_command cmd, void *data)
} }
break; break;
case CMD_EVENT_RUNAHEAD_TOGGLE: case CMD_EVENT_RUNAHEAD_TOGGLE:
#if HAVE_RUNAHEAD
{ {
char msg[256];
msg[0] = '\0';
if (!core_info_current_supports_runahead()) if (!core_info_current_supports_runahead())
{ {
runloop_msg_queue_push(msg_hash_to_str(MSG_RUNAHEAD_CORE_DOES_NOT_SUPPORT_RUNAHEAD), runloop_msg_queue_push(msg_hash_to_str(MSG_RUNAHEAD_CORE_DOES_NOT_SUPPORT_RUNAHEAD),
@ -2249,25 +2247,86 @@ bool command_event(enum event_command cmd, void *data)
1, 100, false, 1, 100, false,
NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
} }
else if (!settings->bools.run_ahead_secondary_instance)
{
snprintf(msg, sizeof(msg), msg_hash_to_str(MSG_RUNAHEAD_ENABLED),
settings->uints.run_ahead_frames);
runloop_msg_queue_push(
msg, 1, 100, false,
NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
}
else else
{ {
snprintf(msg, sizeof(msg), msg_hash_to_str(MSG_RUNAHEAD_ENABLED_WITH_SECOND_INSTANCE), char msg[256];
settings->uints.run_ahead_frames);
if (!settings->bools.run_ahead_secondary_instance)
{
snprintf(msg, sizeof(msg), msg_hash_to_str(MSG_RUNAHEAD_ENABLED),
settings->uints.run_ahead_frames);
runloop_msg_queue_push(
msg, 1, 100, false,
NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
}
else
{
snprintf(msg, sizeof(msg), msg_hash_to_str(MSG_RUNAHEAD_ENABLED_WITH_SECOND_INSTANCE),
settings->uints.run_ahead_frames);
runloop_msg_queue_push(
msg, 1, 100, false,
NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
}
/* Disable preemptive frames */
settings->bools.preemptive_frames_enable = false;
runloop_preempt_deinit();
}
}
#endif
break;
case CMD_EVENT_PREEMPT_TOGGLE:
#if HAVE_RUNAHEAD
{
bool old_warn = settings->bools.preemptive_frames_hide_warnings;
bool old_inited = runloop_st->preempt_data;
/* Toggle with warnings shown */
settings->bools.preemptive_frames_hide_warnings = false;
settings->bools.preemptive_frames_enable =
!(settings->bools.preemptive_frames_enable);
command_event(CMD_EVENT_PREEMPT_UPDATE, NULL);
settings->bools.preemptive_frames_hide_warnings = old_warn;
if (old_inited && !runloop_st->preempt_data)
{
runloop_msg_queue_push(msg_hash_to_str(MSG_PREEMPT_DISABLED),
1, 100, false,
NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
}
else if (runloop_st->preempt_data)
{
char msg[256];
snprintf(msg, sizeof(msg), msg_hash_to_str(MSG_PREEMPT_ENABLED),
settings->uints.run_ahead_frames);
runloop_msg_queue_push( runloop_msg_queue_push(
msg, 1, 100, false, msg, 1, 100, false,
NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
/* Disable runahead */
settings->bools.run_ahead_enabled = false;
} }
else /* Failed to init */
settings->bools.preemptive_frames_enable = false;
} }
#endif
break;
case CMD_EVENT_PREEMPT_UPDATE:
#if HAVE_RUNAHEAD
runloop_preempt_deinit();
runloop_preempt_init();
#endif
break;
case CMD_EVENT_PREEMPT_RESET_BUFFER:
#if HAVE_RUNAHEAD
if (runloop_st->preempt_data)
runloop_st->preempt_data->frame_count = 0;
#endif
break; break;
case CMD_EVENT_RECORDING_TOGGLE: case CMD_EVENT_RECORDING_TOGGLE:
if (recording_st->enable) if (recording_st->enable)
@ -2381,6 +2440,10 @@ bool command_event(enum event_command cmd, void *data)
#endif #endif
if (!command_event_main_state(cmd)) if (!command_event_main_state(cmd))
return false; return false;
#if HAVE_RUNAHEAD
command_event(CMD_EVENT_PREEMPT_RESET_BUFFER, NULL);
#endif
} }
break; break;
case CMD_EVENT_UNDO_LOAD_STATE: case CMD_EVENT_UNDO_LOAD_STATE:
@ -2427,6 +2490,9 @@ bool command_event(enum event_command cmd, void *data)
if (settings->bools.video_frame_delay_auto) if (settings->bools.video_frame_delay_auto)
video_st->frame_delay_target = 0; video_st->frame_delay_target = 0;
#if HAVE_RUNAHEAD
command_event(CMD_EVENT_PREEMPT_RESET_BUFFER, NULL);
#endif
return false; return false;
case CMD_EVENT_SAVE_STATE: case CMD_EVENT_SAVE_STATE:
case CMD_EVENT_SAVE_STATE_TO_RAM: case CMD_EVENT_SAVE_STATE_TO_RAM:
@ -2977,6 +3043,9 @@ bool command_event(enum event_command cmd, void *data)
* RetroArch */ * RetroArch */
if (!(runloop_st->flags & RUNLOOP_FLAG_RUNAHEAD_AVAILABLE)) if (!(runloop_st->flags & RUNLOOP_FLAG_RUNAHEAD_AVAILABLE))
runloop_runahead_clear_variables(runloop_st); runloop_runahead_clear_variables(runloop_st);
/* Deallocate preemptive frames */
runloop_preempt_deinit();
#endif #endif
if (hwr) if (hwr)
@ -3512,6 +3581,10 @@ bool command_event(enum event_command cmd, void *data)
return false; return false;
} }
#if HAVE_RUNAHEAD
/* Deinit preemptive frames; not compatible with netplay */
runloop_preempt_deinit();
#endif
} }
break; break;
case CMD_EVENT_NETPLAY_DISCONNECT: case CMD_EVENT_NETPLAY_DISCONNECT:
@ -6171,6 +6244,12 @@ bool retroarch_main_init(int argc, char *argv[])
audio_driver_load_system_sounds(); audio_driver_load_system_sounds();
#endif #endif
#if defined(HAVE_RUNAHEAD) && defined(HAVE_NETWORKING)
if (!netplay_driver_ctl(RARCH_NETPLAY_CTL_IS_ENABLED, NULL))
#elif defined(HAVE_RUNAHEAD)
runloop_preempt_init();
#endif
return true; return true;
error: error:

384
runloop.c
View File

@ -4974,6 +4974,380 @@ force_input_dirty:
core_run(); core_run();
runloop_st->flags |= RUNLOOP_FLAG_RUNAHEAD_FORCE_INPUT_DIRTY; runloop_st->flags |= RUNLOOP_FLAG_RUNAHEAD_FORCE_INPUT_DIRTY;
} }
/* Preemptive Frames */
static int16_t preempt_input_state(unsigned port,
unsigned device, unsigned index, unsigned id)
{
settings_t *settings = config_get_ptr();
runloop_state_t *runloop_st = &runloop_state;
preempt_t *preempt = runloop_st->preempt_data;
unsigned device_class = device & RETRO_DEVICE_MASK;
unsigned *port_map = settings->uints.input_remap_port_map[port];
uint8_t p;
switch (device_class)
{
case RETRO_DEVICE_ANALOG:
/* Add requested inputs to mask */
while ((p = *(port_map++)) < MAX_USERS)
preempt->analog_mask[p] |= (1 << (id + index * 2));
break;
case RETRO_DEVICE_LIGHTGUN:
case RETRO_DEVICE_MOUSE:
case RETRO_DEVICE_POINTER:
/* Set pointing device for this port */
while ((p = *(port_map++)) < MAX_USERS)
preempt->ptr_dev[p] = device_class;
break;
default:
break;
}
return input_driver_state_wrapper(port, device, index, id);
}
static const char* preempt_allocate(const uint8_t frames)
{
runloop_state_t *runloop_st = &runloop_state;
preempt_t *preempt = (preempt_t*)calloc(1, sizeof(preempt_t));
retro_ctx_size_info_t info;
uint8_t i;
if (!(runloop_st->preempt_data = preempt))
return msg_hash_to_str(MSG_PREEMPT_FAILED_TO_ALLOCATE);
core_serialize_size_special(&info);
if (!info.size)
return msg_hash_to_str(MSG_PREEMPT_CORE_DOES_NOT_SUPPORT_SAVESTATES);
preempt->state_size = info.size;
preempt->frames = frames;
for (i = 0; i < frames; i++)
{
preempt->buffer[i] = malloc(preempt->state_size);
if (!preempt->buffer[i])
return msg_hash_to_str(MSG_PREEMPT_FAILED_TO_ALLOCATE);
}
return NULL;
}
/**
* runloop_preempt_init:
*
* @return true on success, false on failure
*
* Allocates savestate buffer and sets overrides for preemptive frames.
**/
bool runloop_preempt_init(void)
{
runloop_state_t *runloop_st = &runloop_state;
settings_t *settings = config_get_ptr();
const char *failed_str = NULL;
if ( runloop_st->preempt_data
|| !settings->bools.preemptive_frames_enable
|| !settings->uints.run_ahead_frames
|| !(runloop_st->current_core.flags & RETRO_CORE_FLAG_GAME_LOADED))
return false;
/* Check if supported - same requirements as runahead */
if (!core_info_current_supports_runahead())
{
failed_str = msg_hash_to_str(MSG_PREEMPT_CORE_DOES_NOT_SUPPORT_PREEMPT);
goto error;
}
/* Set flags to block runahead and request 'same instance' states */
runloop_st->flags &= ~(RUNLOOP_FLAG_RUNAHEAD_AVAILABLE
| RUNLOOP_FLAG_RUNAHEAD_SECONDARY_CORE_AVAILABLE);
/* Allocate - same 'frames' setting as runahead */
if ((failed_str = preempt_allocate(settings->uints.run_ahead_frames)))
goto error;
/* Only poll in preempt_run() */
runloop_st->current_core.retro_set_input_poll(retro_input_poll_null);
/* Track requested analog states and pointing device types */
runloop_st->current_core.retro_set_input_state(preempt_input_state);
return true;
error:
runloop_preempt_deinit();
if (!config_get_ptr()->bools.preemptive_frames_hide_warnings)
runloop_msg_queue_push(
failed_str, 0, 2 * 60, true,
NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
RARCH_WARN("[Preemptive Frames]: %s\n", failed_str);
return false;
}
/**
* runloop_preempt_deinit:
*
* Frees preempt object and unsets overrides.
**/
void runloop_preempt_deinit(void)
{
runloop_state_t *runloop_st = &runloop_state;
preempt_t *preempt = runloop_st->preempt_data;
struct retro_core_t *current_core = &runloop_st->current_core;
size_t i;
if (preempt == NULL)
return;
/* Free memory */
for (i = 0; i < preempt->frames; i++)
free(preempt->buffer[i]);
free(preempt);
runloop_st->preempt_data = NULL;
/* Undo overrides */
runloop_st->flags |= (RUNLOOP_FLAG_RUNAHEAD_AVAILABLE
| RUNLOOP_FLAG_RUNAHEAD_SECONDARY_CORE_AVAILABLE);
if (current_core->retro_set_input_poll)
current_core->retro_set_input_poll(runloop_st->input_poll_callback_original);
if (current_core->retro_set_input_state)
current_core->retro_set_input_state(runloop_st->retro_ctx.state_cb);
}
static INLINE bool preempt_analog_input_dirty(preempt_t *preempt,
retro_input_state_t state_cb, unsigned port, unsigned mapped_port)
{
int16_t state[20] = {0};
uint8_t base, i;
/* axes */
for (i = 0; i < 2; i++)
{
base = i * 2;
if (preempt->analog_mask[port] & (1 << (base )))
state[base ] = state_cb(mapped_port, RETRO_DEVICE_ANALOG, i, 0);
if (preempt->analog_mask[port] & (1 << (base + 1)))
state[base + 1] = state_cb(mapped_port, RETRO_DEVICE_ANALOG, i, 1);
}
/* buttons */
for (i = 0; i < RARCH_FIRST_CUSTOM_BIND; i++)
{
if (preempt->analog_mask[port] & (1 << (i + 4)))
state[i + 4] = state_cb(mapped_port, RETRO_DEVICE_ANALOG,
RETRO_DEVICE_INDEX_ANALOG_BUTTON, i);
}
if (memcmp(preempt->analog_state[port], state, sizeof(state)) == 0)
return false;
memcpy(preempt->analog_state[port], state, sizeof(state));
return true;
}
static INLINE bool preempt_ptr_input_dirty(preempt_t *preempt,
retro_input_state_t state_cb, unsigned device,
unsigned port, unsigned mapped_port)
{
int16_t state[4] = {0};
unsigned count_id = 0;
unsigned x_id = 0;
unsigned id, max_id;
switch (device)
{
case RETRO_DEVICE_MOUSE:
max_id = RETRO_DEVICE_ID_MOUSE_BUTTON_5;
break;
case RETRO_DEVICE_LIGHTGUN:
x_id = RETRO_DEVICE_ID_LIGHTGUN_SCREEN_X;
max_id = RETRO_DEVICE_ID_LIGHTGUN_DPAD_RIGHT;
break;
case RETRO_DEVICE_POINTER:
max_id = RETRO_DEVICE_ID_POINTER_PRESSED;
count_id = RETRO_DEVICE_ID_POINTER_COUNT;
break;
default:
return false;
}
/* x, y */
state[0] = state_cb(mapped_port, device, 0, x_id );
state[1] = state_cb(mapped_port, device, 0, x_id + 1);
/* buttons */
for (id = 2; id <= max_id; id++)
state[2] |= state_cb(mapped_port, device, 0, id) ? 1 << id : 0;
/* ptr count */
if (count_id)
state[3] = state_cb(mapped_port, device, 0, count_id);
if (memcmp(preempt->ptrdev_state[port], state, sizeof(state)) == 0)
return false;
memcpy(preempt->ptrdev_state[port], state, sizeof(state));
return true;
}
static INLINE void preempt_input_poll(preempt_t *preempt)
{
settings_t *settings = config_get_ptr();
runloop_state_t *runloop_st = &runloop_state;
retro_input_state_t state_cb = input_driver_state_wrapper;
unsigned max_users = settings->uints.input_max_users;
int16_t joypad_state;
unsigned p;
input_driver_poll();
/* Check for input state changes */
for (p = 0; p < max_users; p++)
{
unsigned mapped_port = settings->uints.input_remap_ports[p];
unsigned device = settings->uints.input_libretro_device[mapped_port]
& RETRO_DEVICE_MASK;
switch (device)
{
case RETRO_DEVICE_JOYPAD:
case RETRO_DEVICE_ANALOG:
/* Check full digital joypad */
joypad_state = state_cb(mapped_port, RETRO_DEVICE_JOYPAD,
0, RETRO_DEVICE_ID_JOYPAD_MASK);
if (joypad_state != preempt->joypad_state[p])
{
preempt->joypad_state[p] = joypad_state;
runloop_st->flags |= RUNLOOP_FLAG_INPUT_IS_DIRTY;
}
/* Check requested analogs */
if ( preempt->analog_mask[p]
&& preempt_analog_input_dirty(preempt, state_cb, p, mapped_port))
runloop_st->flags |= RUNLOOP_FLAG_INPUT_IS_DIRTY;
break;
case RETRO_DEVICE_MOUSE:
case RETRO_DEVICE_LIGHTGUN:
case RETRO_DEVICE_POINTER:
/* Check full device state */
if (preempt_ptr_input_dirty(
preempt, state_cb, preempt->ptr_dev[p], p, mapped_port))
runloop_st->flags |= RUNLOOP_FLAG_INPUT_IS_DIRTY;
break;
default:
break;
}
}
/* Clear requested inputs */
memset(preempt->analog_mask, 0, max_users * sizeof(uint32_t));
memset(preempt->ptr_dev, 0, max_users * sizeof(uint8_t));
}
static INLINE void preempt_suspend_av(bool suspend)
{
audio_driver_state_t *audio_st = audio_state_get_ptr();
video_driver_state_t *video_st = video_state_get_ptr();
if (suspend)
{
audio_st->flags |= AUDIO_FLAG_SUSPENDED;
video_st->flags &= ~VIDEO_FLAG_ACTIVE;
}
else
{
audio_st->flags &= ~AUDIO_FLAG_SUSPENDED;
video_st->flags |= VIDEO_FLAG_ACTIVE;
}
}
/* macro for preempt_run */
#define PREEMPT_NEXT_PTR(x) ((x + 1) % preempt->frames)
/**
* preempt_run:
* @preempt : pointer to preemptive frames object
*
* Call in place of core_run() for preemptive frames.
**/
static void preempt_run(preempt_t *preempt)
{
runloop_state_t *runloop_st = &runloop_state;
struct retro_core_t *current_core = &runloop_st->current_core;
const char *failed_str = NULL;
/* Poll and check for dirty input */
preempt_input_poll(preempt);
runloop_st->flags |= RUNLOOP_FLAG_REQUEST_SPECIAL_SAVESTATE;
if ((runloop_st->flags & RUNLOOP_FLAG_INPUT_IS_DIRTY)
&& preempt->frame_count >= preempt->frames)
{
/* Suspend A/V and run preemptive frames */
preempt_suspend_av(true);
if (!current_core->retro_unserialize(
preempt->buffer[preempt->start_ptr], preempt->state_size))
{
failed_str = msg_hash_to_str(MSG_PREEMPT_FAILED_TO_LOAD_STATE);
goto error;
}
current_core->retro_run();
preempt->replay_ptr = PREEMPT_NEXT_PTR(preempt->start_ptr);
while (preempt->replay_ptr != preempt->start_ptr)
{
if (!current_core->retro_serialize(
preempt->buffer[preempt->replay_ptr], preempt->state_size))
{
failed_str = msg_hash_to_str(MSG_PREEMPT_FAILED_TO_SAVE_STATE);
goto error;
}
current_core->retro_run();
preempt->replay_ptr = PREEMPT_NEXT_PTR(preempt->replay_ptr);
}
preempt_suspend_av(false);
}
/* Save current state and set start_ptr to oldest state */
if (!current_core->retro_serialize(
preempt->buffer[preempt->start_ptr], preempt->state_size))
{
failed_str = msg_hash_to_str(MSG_PREEMPT_FAILED_TO_SAVE_STATE);
goto error;
}
preempt->start_ptr = PREEMPT_NEXT_PTR(preempt->start_ptr);
runloop_st->flags &= ~(RUNLOOP_FLAG_REQUEST_SPECIAL_SAVESTATE
| RUNLOOP_FLAG_INPUT_IS_DIRTY);
/* Run normal frame */
current_core->retro_run();
preempt->frame_count++;
return;
error:
runloop_st->flags &= ~(RUNLOOP_FLAG_REQUEST_SPECIAL_SAVESTATE
| RUNLOOP_FLAG_INPUT_IS_DIRTY);
preempt_suspend_av(false);
runloop_preempt_deinit();
if (!config_get_ptr()->bools.preemptive_frames_hide_warnings)
runloop_msg_queue_push(
failed_str, 0, 2 * 60, true,
NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
RARCH_ERR("[Preemptive Frames]: %s\n", failed_str);
}
#endif #endif
static retro_time_t runloop_core_runtime_tick( static retro_time_t runloop_core_runtime_tick(
@ -5583,6 +5957,8 @@ static void core_init_libretro_cbs(runloop_state_t *runloop_st,
runloop_st->current_core.retro_set_input_state(state_cb); runloop_st->current_core.retro_set_input_state(state_cb);
runloop_st->current_core.retro_set_input_poll(core_input_state_poll_maybe); runloop_st->current_core.retro_set_input_poll(core_input_state_poll_maybe);
runloop_st->input_poll_callback_original = core_input_state_poll_maybe;
core_set_default_callbacks(cbs); core_set_default_callbacks(cbs);
#ifdef HAVE_NETWORKING #ifdef HAVE_NETWORKING
@ -7169,6 +7545,9 @@ static enum runloop_state_enum runloop_check_state(
/* Check if we have pressed the Run-Ahead toggle button */ /* Check if we have pressed the Run-Ahead toggle button */
HOTKEY_CHECK(RARCH_RUNAHEAD_TOGGLE, CMD_EVENT_RUNAHEAD_TOGGLE, true, NULL); HOTKEY_CHECK(RARCH_RUNAHEAD_TOGGLE, CMD_EVENT_RUNAHEAD_TOGGLE, true, NULL);
/* Check if we have pressed the Preemptive Frames toggle button */
HOTKEY_CHECK(RARCH_PREEMPT_TOGGLE, CMD_EVENT_PREEMPT_TOGGLE, true, NULL);
/* Check if we have pressed the AI Service toggle button */ /* Check if we have pressed the AI Service toggle button */
HOTKEY_CHECK(RARCH_AI_SERVICE, CMD_EVENT_AI_SERVICE_TOGGLE, true, NULL); HOTKEY_CHECK(RARCH_AI_SERVICE, CMD_EVENT_AI_SERVICE_TOGGLE, true, NULL);
@ -8004,6 +8383,8 @@ int runloop_iterate(void)
run_ahead_num_frames, run_ahead_num_frames,
run_ahead_hide_warnings, run_ahead_hide_warnings,
run_ahead_secondary_instance); run_ahead_secondary_instance);
else if (runloop_st->preempt_data)
preempt_run(runloop_st->preempt_data);
else else
#endif #endif
core_run(); core_run();
@ -8522,6 +8903,9 @@ bool core_unserialize(retro_ctx_serialize_info_t *info)
#ifdef HAVE_NETWORKING #ifdef HAVE_NETWORKING
netplay_driver_ctl(RARCH_NETPLAY_CTL_LOAD_SAVESTATE, info); netplay_driver_ctl(RARCH_NETPLAY_CTL_LOAD_SAVESTATE, info);
#endif #endif
#if HAVE_RUNAHEAD
command_event(CMD_EVENT_PREEMPT_RESET_BUFFER, NULL);
#endif
return true; return true;
} }

View File

@ -159,7 +159,9 @@ typedef struct core_options_callbacks
} core_options_callbacks_t; } core_options_callbacks_t;
#ifdef HAVE_RUNAHEAD #ifdef HAVE_RUNAHEAD
typedef bool (*runahead_load_state_function)(const void*, size_t); #define MAX_RUNAHEAD_FRAMES 12
typedef bool(*runahead_load_state_function)(const void*, size_t);
typedef void *(*constructor_t)(void); typedef void *(*constructor_t)(void);
typedef void (*destructor_t )(void*); typedef void (*destructor_t )(void*);
@ -172,6 +174,33 @@ typedef struct my_list_t
int capacity; int capacity;
int size; int size;
} my_list; } my_list;
typedef struct preemptive_frames_data
{
/* Savestate buffer */
void* buffer[MAX_RUNAHEAD_FRAMES];
size_t state_size;
/* Number of latency frames to remove */
uint8_t frames;
/* Buffer indexes for replays */
uint8_t start_ptr;
uint8_t replay_ptr;
/* Frame count since buffer init/reset */
uint64_t frame_count;
/* Input states. Replays triggered on changes */
int16_t joypad_state[MAX_USERS];
int16_t analog_state[MAX_USERS][20];
int16_t ptrdev_state[MAX_USERS][4];
/* Pointing device requested */
uint8_t ptr_dev[MAX_USERS];
/* Mask of analog states requested */
uint32_t analog_mask[MAX_USERS];
} preempt_t;
#endif #endif
struct runloop struct runloop
@ -197,6 +226,7 @@ struct runloop
#endif #endif
my_list *runahead_save_state_list; my_list *runahead_save_state_list;
my_list *input_state_list; my_list *input_state_list;
preempt_t *preempt_data;
#endif #endif
#ifdef HAVE_REWIND #ifdef HAVE_REWIND
@ -208,6 +238,7 @@ struct runloop
struct retro_subsystem_info subsystem_data[SUBSYSTEM_MAX_SUBSYSTEMS]; struct retro_subsystem_info subsystem_data[SUBSYSTEM_MAX_SUBSYSTEMS];
struct retro_callbacks retro_ctx; /* ptr alignment */ struct retro_callbacks retro_ctx; /* ptr alignment */
msg_queue_t msg_queue; /* ptr alignment */ msg_queue_t msg_queue; /* ptr alignment */
retro_input_poll_t input_poll_callback_original; /* ptr alignment */
retro_input_state_t input_state_callback_original; /* ptr alignment */ retro_input_state_t input_state_callback_original; /* ptr alignment */
#ifdef HAVE_RUNAHEAD #ifdef HAVE_RUNAHEAD
function_t retro_reset_callback_original; /* ptr alignment */ function_t retro_reset_callback_original; /* ptr alignment */
@ -384,6 +415,9 @@ void runloop_event_deinit_core(void);
#ifdef HAVE_RUNAHEAD #ifdef HAVE_RUNAHEAD
void runloop_runahead_clear_variables(runloop_state_t *runloop_st); void runloop_runahead_clear_variables(runloop_state_t *runloop_st);
bool runloop_preempt_init(void);
void runloop_preempt_deinit(void);
#endif #endif
bool runloop_event_init_core( bool runloop_event_init_core(