mirror of
https://github.com/libretro/RetroArch
synced 2025-02-02 05:54:16 +00:00
Add Preemptive Frames to Latency Settings (#14832)
This commit is contained in:
parent
975300a320
commit
7213aada8d
@ -261,6 +261,12 @@ enum event_command
|
||||
CMD_EVENT_BSV_RECORDING_TOGGLE,
|
||||
/* Toggle Run-Ahead. */
|
||||
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. */
|
||||
CMD_EVENT_VRR_RUNLOOP_TOGGLE,
|
||||
/* AI service. */
|
||||
@ -505,6 +511,7 @@ static const struct cmd_map map[] = {
|
||||
|
||||
{ "VRR_RUNLOOP_TOGGLE", RARCH_VRR_RUNLOOP_TOGGLE },
|
||||
{ "RUNAHEAD_TOGGLE", RARCH_RUNAHEAD_TOGGLE },
|
||||
{ "PREEMPT_TOGGLE", RARCH_PREEMPT_TOGGLE },
|
||||
{ "FPS_TOGGLE", RARCH_FPS_TOGGLE },
|
||||
{ "STATISTICS_TOGGLE", RARCH_STATISTICS_TOGGLE },
|
||||
{ "AI_SERVICE", RARCH_AI_SERVICE },
|
||||
|
@ -1299,6 +1299,8 @@
|
||||
|
||||
/* Hide warning messages when using the Run Ahead feature. */
|
||||
#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. */
|
||||
#define DEFAULT_NETWORK_CMD_ENABLE false
|
||||
|
@ -543,6 +543,13 @@ static const struct retro_keybind retro_keybinds_1[] = {
|
||||
RARCH_RUNAHEAD_TOGGLE, NO_BTN, NO_BTN, 0,
|
||||
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,
|
||||
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,
|
||||
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,
|
||||
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,
|
||||
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,
|
||||
AXIS_NONE, AXIS_NONE, AXIS_NONE,
|
||||
|
@ -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, 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, 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),
|
||||
@ -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_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("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("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);
|
||||
|
@ -885,6 +885,8 @@ typedef struct settings
|
||||
bool run_ahead_enabled;
|
||||
bool run_ahead_secondary_instance;
|
||||
bool run_ahead_hide_warnings;
|
||||
bool preemptive_frames_enable;
|
||||
bool preemptive_frames_hide_warnings;
|
||||
bool pause_nonactive;
|
||||
bool pause_on_disconnect;
|
||||
bool block_sram_overwrite;
|
||||
|
@ -120,6 +120,7 @@ enum
|
||||
|
||||
RARCH_VRR_RUNLOOP_TOGGLE,
|
||||
RARCH_RUNAHEAD_TOGGLE,
|
||||
RARCH_PREEMPT_TOGGLE,
|
||||
RARCH_FPS_TOGGLE,
|
||||
RARCH_STATISTICS_TOGGLE,
|
||||
RARCH_AI_SERVICE,
|
||||
|
@ -235,6 +235,10 @@ int msg_hash_get_help_id_enum(enum msg_hash_enums msg, char *s, size_t len)
|
||||
snprintf(s, len,
|
||||
"Toggles Run-Ahead mode on/off.");
|
||||
break;
|
||||
case RARCH_PREEMPT_TOGGLE:
|
||||
snprintf(s, len,
|
||||
"Toggles Preemptive Frames on/off.");
|
||||
break;
|
||||
default:
|
||||
if (string_is_empty(s))
|
||||
strlcpy(s, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NO_INFORMATION_AVAILABLE), len);
|
||||
|
@ -3266,6 +3266,22 @@ MSG_HASH(
|
||||
MENU_ENUM_LABEL_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(
|
||||
MENU_ENUM_LABEL_SORT_SAVEFILES_ENABLE,
|
||||
"sort_savefiles_enable"
|
||||
|
@ -3391,6 +3391,14 @@ MSG_HASH(
|
||||
MENU_ENUM_SUBLABEL_INPUT_META_RUNAHEAD_TOGGLE,
|
||||
"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(
|
||||
MENU_ENUM_LABEL_VALUE_INPUT_META_FPS_TOGGLE,
|
||||
@ -3720,6 +3728,38 @@ MSG_HASH(
|
||||
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."
|
||||
)
|
||||
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 */
|
||||
|
||||
@ -13902,6 +13942,34 @@ MSG_HASH(
|
||||
MSG_RUNAHEAD_FAILED_TO_CREATE_SECONDARY_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_SCANNING_OF_FILE_FINISHED,
|
||||
"Scanning of file finished"
|
||||
|
@ -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_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_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)
|
||||
@ -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_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_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_rewind, MENU_ENUM_SUBLABEL_REWIND_ENABLE)
|
||||
#ifdef HAVE_CHEATS
|
||||
@ -2254,6 +2259,9 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs,
|
||||
case RARCH_RUNAHEAD_TOGGLE:
|
||||
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_input_meta_runahead_toggle);
|
||||
return 0;
|
||||
case RARCH_PREEMPT_TOGGLE:
|
||||
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_input_meta_preempt_toggle);
|
||||
return 0;
|
||||
case RARCH_FPS_TOGGLE:
|
||||
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_input_meta_fps_toggle);
|
||||
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:
|
||||
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_run_ahead_frames);
|
||||
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:
|
||||
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_input_block_timeout);
|
||||
break;
|
||||
|
@ -9265,6 +9265,7 @@ unsigned menu_displaylist_build_list(
|
||||
#ifdef HAVE_RUNAHEAD
|
||||
bool runahead_supported = true;
|
||||
bool runahead_enabled = settings->bools.run_ahead_enabled;
|
||||
bool preempt_enabled = settings->bools.preemptive_frames_enable;
|
||||
#endif
|
||||
menu_displaylist_build_info_selective_t build_list[] = {
|
||||
{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_SECONDARY_INSTANCE, 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
|
||||
};
|
||||
|
||||
@ -9330,6 +9334,7 @@ unsigned menu_displaylist_build_list(
|
||||
switch (build_list[i].enum_idx)
|
||||
{
|
||||
case MENU_ENUM_LABEL_RUN_AHEAD_ENABLED:
|
||||
case MENU_ENUM_LABEL_PREEMPT_ENABLE:
|
||||
build_list[i].checked = true;
|
||||
break;
|
||||
case MENU_ENUM_LABEL_RUN_AHEAD_FRAMES:
|
||||
@ -9338,6 +9343,11 @@ unsigned menu_displaylist_build_list(
|
||||
if (runahead_enabled)
|
||||
build_list[i].checked = true;
|
||||
break;
|
||||
case MENU_ENUM_LABEL_PREEMPT_FRAMES:
|
||||
case MENU_ENUM_LABEL_PREEMPT_HIDE_WARNINGS:
|
||||
if (preempt_enabled)
|
||||
build_list[i].checked = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -9356,13 +9366,21 @@ unsigned menu_displaylist_build_list(
|
||||
}
|
||||
|
||||
#ifdef HAVE_RUNAHEAD
|
||||
if (!runahead_supported &&
|
||||
menu_entries_append(list,
|
||||
if (!runahead_supported)
|
||||
{
|
||||
if (menu_entries_append(list,
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_RUN_AHEAD_UNSUPPORTED),
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_RUN_AHEAD_UNSUPPORTED),
|
||||
MENU_ENUM_LABEL_RUN_AHEAD_UNSUPPORTED,
|
||||
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
|
||||
if (MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(list,
|
||||
MENU_ENUM_LABEL_GAMEMODE_ENABLE, PARSE_ONLY_BOOL, false) == 0)
|
||||
|
@ -6982,6 +6982,13 @@ void retroarch_menu_running_finished(bool quit)
|
||||
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
|
||||
|
@ -8654,31 +8654,6 @@ static void general_write_handler(rarch_setting_t *setting)
|
||||
default_aspect;
|
||||
}
|
||||
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:
|
||||
/* 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);
|
||||
}
|
||||
|
||||
#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
|
||||
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;
|
||||
menu_settings_list_current_add_range(list, list_info, 1, 10, 0.1, true, true);
|
||||
|
||||
#ifdef HAVE_RUNAHEAD
|
||||
CONFIG_BOOL(
|
||||
list, list_info,
|
||||
&settings->bools.run_ahead_enabled,
|
||||
@ -14808,9 +14882,7 @@ static bool setting_append_list(
|
||||
general_read_handler,
|
||||
SD_FLAG_NONE
|
||||
);
|
||||
(*list)[list_info->index - 1].action_ok = setting_bool_action_left_with_refresh;
|
||||
(*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;
|
||||
(*list)[list_info->index - 1].change_handler = runahead_change_handler;
|
||||
|
||||
CONFIG_UINT(
|
||||
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].action_ok = &setting_action_ok_uint;
|
||||
(*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)
|
||||
CONFIG_BOOL(
|
||||
@ -14844,6 +14917,7 @@ static bool setting_append_list(
|
||||
general_read_handler,
|
||||
SD_FLAG_NONE
|
||||
);
|
||||
(*list)[list_info->index - 1].change_handler = runahead_change_handler;
|
||||
#endif
|
||||
|
||||
CONFIG_BOOL(
|
||||
@ -14862,6 +14936,56 @@ static bool setting_append_list(
|
||||
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
|
||||
CONFIG_UINT(
|
||||
list, list_info,
|
||||
|
13
msg_hash.h
13
msg_hash.h
@ -520,6 +520,13 @@ enum msg_hash_enums
|
||||
MSG_RUNAHEAD_FAILED_TO_SAVE_STATE,
|
||||
MSG_RUNAHEAD_FAILED_TO_LOAD_STATE,
|
||||
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_RGUI_MISSING_FONTS,
|
||||
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_RUNAHEAD_TOGGLE,
|
||||
MENU_ENUM_LABEL_VALUE_INPUT_META_PREEMPT_TOGGLE,
|
||||
MENU_ENUM_LABEL_VALUE_INPUT_META_FPS_TOGGLE,
|
||||
MENU_ENUM_LABEL_VALUE_INPUT_META_STATISTICS_TOGGLE,
|
||||
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_RUNAHEAD_TOGGLE,
|
||||
MENU_ENUM_SUBLABEL_INPUT_META_PREEMPT_TOGGLE,
|
||||
MENU_ENUM_SUBLABEL_INPUT_META_FPS_TOGGLE,
|
||||
MENU_ENUM_SUBLABEL_INPUT_META_STATISTICS_TOGGLE,
|
||||
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_HIDE_WARNINGS),
|
||||
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(TURBO),
|
||||
|
||||
|
@ -8435,6 +8435,11 @@ void deinit_netplay(void)
|
||||
net_st->data = NULL;
|
||||
net_st->flags &= ~(NET_DRIVER_ST_FLAG_NETPLAY_ENABLED
|
||||
| NET_DRIVER_ST_FLAG_NETPLAY_IS_CLIENT);
|
||||
|
||||
#if HAVE_RUNAHEAD
|
||||
/* Reinitialize preemptive frames if enabled */
|
||||
runloop_preempt_init();
|
||||
#endif
|
||||
}
|
||||
|
||||
free(net_st->client_info);
|
||||
|
107
retroarch.c
107
retroarch.c
@ -2228,10 +2228,8 @@ bool command_event(enum event_command cmd, void *data)
|
||||
}
|
||||
break;
|
||||
case CMD_EVENT_RUNAHEAD_TOGGLE:
|
||||
#if HAVE_RUNAHEAD
|
||||
{
|
||||
char msg[256];
|
||||
msg[0] = '\0';
|
||||
|
||||
if (!core_info_current_supports_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,
|
||||
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
|
||||
{
|
||||
snprintf(msg, sizeof(msg), msg_hash_to_str(MSG_RUNAHEAD_ENABLED_WITH_SECOND_INSTANCE),
|
||||
settings->uints.run_ahead_frames);
|
||||
char msg[256];
|
||||
|
||||
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(
|
||||
msg, 1, 100, false,
|
||||
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;
|
||||
case CMD_EVENT_RECORDING_TOGGLE:
|
||||
if (recording_st->enable)
|
||||
@ -2381,6 +2440,10 @@ bool command_event(enum event_command cmd, void *data)
|
||||
#endif
|
||||
if (!command_event_main_state(cmd))
|
||||
return false;
|
||||
|
||||
#if HAVE_RUNAHEAD
|
||||
command_event(CMD_EVENT_PREEMPT_RESET_BUFFER, NULL);
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
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)
|
||||
video_st->frame_delay_target = 0;
|
||||
|
||||
#if HAVE_RUNAHEAD
|
||||
command_event(CMD_EVENT_PREEMPT_RESET_BUFFER, NULL);
|
||||
#endif
|
||||
return false;
|
||||
case CMD_EVENT_SAVE_STATE:
|
||||
case CMD_EVENT_SAVE_STATE_TO_RAM:
|
||||
@ -2977,6 +3043,9 @@ bool command_event(enum event_command cmd, void *data)
|
||||
* RetroArch */
|
||||
if (!(runloop_st->flags & RUNLOOP_FLAG_RUNAHEAD_AVAILABLE))
|
||||
runloop_runahead_clear_variables(runloop_st);
|
||||
|
||||
/* Deallocate preemptive frames */
|
||||
runloop_preempt_deinit();
|
||||
#endif
|
||||
|
||||
if (hwr)
|
||||
@ -3512,6 +3581,10 @@ bool command_event(enum event_command cmd, void *data)
|
||||
|
||||
return false;
|
||||
}
|
||||
#if HAVE_RUNAHEAD
|
||||
/* Deinit preemptive frames; not compatible with netplay */
|
||||
runloop_preempt_deinit();
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
case CMD_EVENT_NETPLAY_DISCONNECT:
|
||||
@ -6171,6 +6244,12 @@ bool retroarch_main_init(int argc, char *argv[])
|
||||
audio_driver_load_system_sounds();
|
||||
#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;
|
||||
|
||||
error:
|
||||
|
384
runloop.c
384
runloop.c
@ -4974,6 +4974,380 @@ force_input_dirty:
|
||||
core_run();
|
||||
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
|
||||
|
||||
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_poll(core_input_state_poll_maybe);
|
||||
|
||||
runloop_st->input_poll_callback_original = core_input_state_poll_maybe;
|
||||
|
||||
core_set_default_callbacks(cbs);
|
||||
|
||||
#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 */
|
||||
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 */
|
||||
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_hide_warnings,
|
||||
run_ahead_secondary_instance);
|
||||
else if (runloop_st->preempt_data)
|
||||
preempt_run(runloop_st->preempt_data);
|
||||
else
|
||||
#endif
|
||||
core_run();
|
||||
@ -8522,6 +8903,9 @@ bool core_unserialize(retro_ctx_serialize_info_t *info)
|
||||
#ifdef HAVE_NETWORKING
|
||||
netplay_driver_ctl(RARCH_NETPLAY_CTL_LOAD_SAVESTATE, info);
|
||||
#endif
|
||||
#if HAVE_RUNAHEAD
|
||||
command_event(CMD_EVENT_PREEMPT_RESET_BUFFER, NULL);
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
36
runloop.h
36
runloop.h
@ -159,7 +159,9 @@ typedef struct core_options_callbacks
|
||||
} core_options_callbacks_t;
|
||||
|
||||
#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 (*destructor_t )(void*);
|
||||
@ -172,6 +174,33 @@ typedef struct my_list_t
|
||||
int capacity;
|
||||
int size;
|
||||
} 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
|
||||
|
||||
struct runloop
|
||||
@ -197,6 +226,7 @@ struct runloop
|
||||
#endif
|
||||
my_list *runahead_save_state_list;
|
||||
my_list *input_state_list;
|
||||
preempt_t *preempt_data;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_REWIND
|
||||
@ -208,6 +238,7 @@ struct runloop
|
||||
struct retro_subsystem_info subsystem_data[SUBSYSTEM_MAX_SUBSYSTEMS];
|
||||
struct retro_callbacks retro_ctx; /* 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 */
|
||||
#ifdef HAVE_RUNAHEAD
|
||||
function_t retro_reset_callback_original; /* ptr alignment */
|
||||
@ -384,6 +415,9 @@ void runloop_event_deinit_core(void);
|
||||
|
||||
#ifdef HAVE_RUNAHEAD
|
||||
void runloop_runahead_clear_variables(runloop_state_t *runloop_st);
|
||||
|
||||
bool runloop_preempt_init(void);
|
||||
void runloop_preempt_deinit(void);
|
||||
#endif
|
||||
|
||||
bool runloop_event_init_core(
|
||||
|
Loading…
x
Reference in New Issue
Block a user