diff --git a/src/core/analog_controller.cpp b/src/core/analog_controller.cpp index 140b5d752..b2f377c61 100644 --- a/src/core/analog_controller.cpp +++ b/src/core/analog_controller.cpp @@ -262,7 +262,7 @@ void AnalogController::SetBindState(u32 index, float value) } if (std::memcmp(m_axis_state.data(), prev_axis_state.data(), m_axis_state.size()) != 0) - System::SetRunaheadReplayFlag(); + System::SetRunaheadReplayFlag(true); #undef MERGE @@ -274,14 +274,14 @@ void AnalogController::SetBindState(u32 index, float value) if (value >= m_button_deadzone) { if (m_button_state & bit) - System::SetRunaheadReplayFlag(); + System::SetRunaheadReplayFlag(false); m_button_state &= ~(bit); } else { if (!(m_button_state & bit)) - System::SetRunaheadReplayFlag(); + System::SetRunaheadReplayFlag(false); m_button_state |= bit; } diff --git a/src/core/analog_joystick.cpp b/src/core/analog_joystick.cpp index 671cb41d9..84514d961 100644 --- a/src/core/analog_joystick.cpp +++ b/src/core/analog_joystick.cpp @@ -101,8 +101,8 @@ void AnalogJoystick::SetBindState(u32 index, float value) return; const u8 u8_value = static_cast(std::clamp(value * m_analog_sensitivity * 255.0f, 0.0f, 255.0f)); - if (u8_value != m_half_axis_state[sub_index]) - System::SetRunaheadReplayFlag(); + if (m_half_axis_state[sub_index] == u8_value) + return; m_half_axis_state[sub_index] = u8_value; @@ -181,7 +181,7 @@ void AnalogJoystick::SetBindState(u32 index, float value) } if (std::memcmp(m_axis_state.data(), prev_axis_state.data(), m_axis_state.size()) != 0) - System::SetRunaheadReplayFlag(); + System::SetRunaheadReplayFlag(true); #undef MERGE @@ -193,14 +193,14 @@ void AnalogJoystick::SetBindState(u32 index, float value) if (value >= 0.5f) { if (m_button_state & bit) - System::SetRunaheadReplayFlag(); + System::SetRunaheadReplayFlag(false); m_button_state &= ~(bit); } else { if (!(m_button_state & bit)) - System::SetRunaheadReplayFlag(); + System::SetRunaheadReplayFlag(false); m_button_state |= bit; } diff --git a/src/core/ddgo_controller.cpp b/src/core/ddgo_controller.cpp index 274288708..67db79ed3 100644 --- a/src/core/ddgo_controller.cpp +++ b/src/core/ddgo_controller.cpp @@ -114,14 +114,14 @@ void DDGoController::SetBindState(u32 index, float value) if (pressed) { if (m_button_state & bit) - System::SetRunaheadReplayFlag(); + System::SetRunaheadReplayFlag(false); m_button_state &= ~bit; } else { if (!(m_button_state & bit)) - System::SetRunaheadReplayFlag(); + System::SetRunaheadReplayFlag(false); m_button_state |= bit; } @@ -215,7 +215,7 @@ void DDGoController::SetPowerLevel(u32 level) m_power_level = Truncate8(level); m_power_transition_frames_remaining = m_power_transition_frames; UpdatePowerBits(); - System::SetRunaheadReplayFlag(); + System::SetRunaheadReplayFlag(false); } void DDGoController::UpdatePowerBits() @@ -251,7 +251,7 @@ void DDGoController::SetBrakeLevel(u32 level) m_brake_level = Truncate8(level); m_brake_transition_frames_remaining = m_brake_transition_frames; UpdateBrakeBits(); - System::SetRunaheadReplayFlag(); + System::SetRunaheadReplayFlag(false); } void DDGoController::UpdateBrakeBits() diff --git a/src/core/digital_controller.cpp b/src/core/digital_controller.cpp index cede40366..d0c6eb484 100644 --- a/src/core/digital_controller.cpp +++ b/src/core/digital_controller.cpp @@ -64,14 +64,14 @@ void DigitalController::SetBindState(u32 index, float value) if (pressed) { if (m_button_state & bit) - System::SetRunaheadReplayFlag(); + System::SetRunaheadReplayFlag(false); m_button_state &= ~bit; } else { if (!(m_button_state & bit)) - System::SetRunaheadReplayFlag(); + System::SetRunaheadReplayFlag(false); m_button_state |= bit; } diff --git a/src/core/fullscreen_ui.cpp b/src/core/fullscreen_ui.cpp index 39bfe571e..216607956 100644 --- a/src/core/fullscreen_ui.cpp +++ b/src/core/fullscreen_ui.cpp @@ -4662,6 +4662,11 @@ void FullscreenUI::DrawEmulationSettingsPage() "high system requirements."), "Main", "RunaheadFrameCount", 0, runahead_options); + DrawToggleSetting( + bsi, FSUI_ICONVSTR(ICON_PF_ANALOG_ANY, "Runahead for Analog Input"), + FSUI_VSTR("Activates runahead when analog input changes, which significantly increases system requirements."), + "Main", "RunaheadForAnalogInput", false, runahead_enabled); + TinyString rewind_summary; if (runahead_enabled) { @@ -9559,6 +9564,7 @@ TRANSLATE_NOOP("FullscreenUI", "Achievements"); TRANSLATE_NOOP("FullscreenUI", "Achievements Settings"); TRANSLATE_NOOP("FullscreenUI", "Achievements are not enabled."); TRANSLATE_NOOP("FullscreenUI", "Achievements: "); +TRANSLATE_NOOP("FullscreenUI", "Activates runahead when analog input changes, which significantly increases system requirements."); TRANSLATE_NOOP("FullscreenUI", "Add Search Directory"); TRANSLATE_NOOP("FullscreenUI", "Add Shader"); TRANSLATE_NOOP("FullscreenUI", "Adds a new directory to the game search list."); @@ -9770,7 +9776,6 @@ TRANSLATE_NOOP("FullscreenUI", "Enable VRAM Write Replacement"); TRANSLATE_NOOP("FullscreenUI", "Enable XInput Input Source"); TRANSLATE_NOOP("FullscreenUI", "Enable debugging when supported by the host's renderer API. Only for developer use."); TRANSLATE_NOOP("FullscreenUI", "Enable/Disable the Player LED on DualSense controllers."); -TRANSLATE_NOOP("FullscreenUI", "Enable/Disable using the DualSense controller's Mic Mute LED to indicate when Analog Mode is active."); TRANSLATE_NOOP("FullscreenUI", "Enables alignment and bus exceptions. Not needed for any known games."); TRANSLATE_NOOP("FullscreenUI", "Enables an additional 6MB of RAM to obtain a total of 2+6 = 8MB, usually present on dev consoles."); TRANSLATE_NOOP("FullscreenUI", "Enables an additional three controller slots on each port. Not supported in all games."); @@ -9941,6 +9946,7 @@ TRANSLATE_NOOP("FullscreenUI", "Mute CD Audio"); TRANSLATE_NOOP("FullscreenUI", "Navigate"); TRANSLATE_NOOP("FullscreenUI", "No"); TRANSLATE_NOOP("FullscreenUI", "No Game Selected"); +TRANSLATE_NOOP("FullscreenUI", "No LED"); TRANSLATE_NOOP("FullscreenUI", "No Vibration"); TRANSLATE_NOOP("FullscreenUI", "No cheats are available for this game."); TRANSLATE_NOOP("FullscreenUI", "No devices with vibration motors were detected."); @@ -10056,10 +10062,10 @@ TRANSLATE_NOOP("FullscreenUI", "Right: "); TRANSLATE_NOOP("FullscreenUI", "Round Upscaled Texture Coordinates"); TRANSLATE_NOOP("FullscreenUI", "Rounds texture coordinates instead of flooring when upscaling. Can fix misaligned textures in some games, but break others, and is incompatible with texture filtering."); TRANSLATE_NOOP("FullscreenUI", "Runahead"); +TRANSLATE_NOOP("FullscreenUI", "Runahead for Analog Input"); TRANSLATE_NOOP("FullscreenUI", "Runahead/Rewind"); TRANSLATE_NOOP("FullscreenUI", "Runs the software renderer in parallel for VRAM readbacks. On some systems, this may result in greater performance when using graphical enhancements with the hardware renderer."); TRANSLATE_NOOP("FullscreenUI", "SDL DualSense Player LED"); -TRANSLATE_NOOP("FullscreenUI", "SDL DualSense Mic Mute LED for Analog Mode"); TRANSLATE_NOOP("FullscreenUI", "SDL DualShock 4 / DualSense Enhanced Mode"); TRANSLATE_NOOP("FullscreenUI", "Safe Mode"); TRANSLATE_NOOP("FullscreenUI", "Save Controller Preset"); diff --git a/src/core/imgui_overlays.cpp b/src/core/imgui_overlays.cpp index 581e3b6b2..282089558 100644 --- a/src/core/imgui_overlays.cpp +++ b/src/core/imgui_overlays.cpp @@ -561,7 +561,7 @@ void ImGuiManager::DrawEnhancementsOverlay(const GPUBackend* gpu) if (g_settings.rewind_enable) text.append_format(" RW={}/{}", g_settings.rewind_save_frequency, g_settings.rewind_save_slots); if (g_settings.IsRunaheadEnabled()) - text.append_format(" RA={}", g_settings.runahead_frames); + text.append_format(" RA={}{}", g_settings.runahead_frames, g_settings.runahead_for_analog_input ? "+A" : ""); if (g_settings.cpu_overclock_active) text.append_format(" CPU={}%", g_settings.GetCPUOverclockPercent()); diff --git a/src/core/jogcon.cpp b/src/core/jogcon.cpp index 3a2b775a2..e8d43c904 100644 --- a/src/core/jogcon.cpp +++ b/src/core/jogcon.cpp @@ -126,7 +126,7 @@ void JogCon::SetBindState(u32 index, float value) -static_cast((static_cast(m_half_axis_state[static_cast(HalfAxis::SteeringLeft)]) + 1) / 2); if (m_steering_state != prev_steering_state) - System::SetRunaheadReplayFlag(); + System::SetRunaheadReplayFlag(true); } const u16 bit = u16(1) << static_cast(index); @@ -134,14 +134,14 @@ void JogCon::SetBindState(u32 index, float value) if (value >= m_button_deadzone) { if (m_button_state & bit) - System::SetRunaheadReplayFlag(); + System::SetRunaheadReplayFlag(false); m_button_state &= ~(bit); } else { if (!(m_button_state & bit)) - System::SetRunaheadReplayFlag(); + System::SetRunaheadReplayFlag(false); m_button_state |= bit; } diff --git a/src/core/negcon.cpp b/src/core/negcon.cpp index 0183b5ee3..87169fb37 100644 --- a/src/core/negcon.cpp +++ b/src/core/negcon.cpp @@ -137,14 +137,14 @@ void NeGcon::SetBindState(u32 index, float value) if (value >= 0.5f) { if (m_button_state & bit) - System::SetRunaheadReplayFlag(); + System::SetRunaheadReplayFlag(false); m_button_state &= ~bit; } else { if (!(m_button_state & bit)) - System::SetRunaheadReplayFlag(); + System::SetRunaheadReplayFlag(false); m_button_state |= bit; } diff --git a/src/core/negcon_rumble.cpp b/src/core/negcon_rumble.cpp index f4893afab..85127f348 100644 --- a/src/core/negcon_rumble.cpp +++ b/src/core/negcon_rumble.cpp @@ -194,14 +194,14 @@ void NeGconRumble::SetBindState(u32 index, float value) if (value >= 0.5f) { if (m_button_state & bit) - System::SetRunaheadReplayFlag(); + System::SetRunaheadReplayFlag(false); m_button_state &= ~bit; } else { if (!(m_button_state & bit)) - System::SetRunaheadReplayFlag(); + System::SetRunaheadReplayFlag(false); m_button_state |= bit; } diff --git a/src/core/settings.cpp b/src/core/settings.cpp index 496499897..c872c01ed 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp @@ -216,6 +216,7 @@ void Settings::Load(const SettingsInterface& si, const SettingsInterface& contro rewind_save_frequency = si.GetFloatValue("Main", "RewindFrequency", 10.0f); rewind_save_slots = static_cast(std::min(si.GetUIntValue("Main", "RewindSaveSlots", 10u), 65535u)); runahead_frames = static_cast(std::min(si.GetUIntValue("Main", "RunaheadFrameCount", 0u), 255u)); + runahead_for_analog_input = si.GetBoolValue("Main", "RunaheadForAnalogInput", false); cpu_execution_mode = ParseCPUExecutionMode( @@ -598,6 +599,7 @@ void Settings::Save(SettingsInterface& si, bool ignore_base) const si.SetFloatValue("Main", "RewindFrequency", rewind_save_frequency); si.SetUIntValue("Main", "RewindSaveSlots", rewind_save_slots); si.SetUIntValue("Main", "RunaheadFrameCount", runahead_frames); + si.SetBoolValue("Main", "RunaheadForAnalogInput", runahead_for_analog_input); si.SetStringValue("CPU", "ExecutionMode", GetCPUExecutionModeName(cpu_execution_mode)); si.SetBoolValue("CPU", "OverclockEnable", cpu_overclock_enable); @@ -1061,6 +1063,7 @@ void Settings::ApplySettingRestrictions() g_settings.mdec_use_old_routines = false; g_settings.bios_patch_fast_boot = false; g_settings.runahead_frames = 0; + g_settings.runahead_for_analog_input = false; g_settings.rewind_enable = false; g_settings.pio_device_type = PIODeviceType::None; g_settings.pcdrv_enable = false; diff --git a/src/core/settings.h b/src/core/settings.h index 6842d7b6d..1432aa9fb 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -322,6 +322,7 @@ struct Settings : public GPUSettings bool bios_fast_forward_boot : 1 = false; bool rewind_enable : 1 = false; + bool runahead_for_analog_input : 1 = false; bool apply_compatibility_settings : 1 = true; bool apply_game_settings : 1 = true; diff --git a/src/core/system.cpp b/src/core/system.cpp index db5611f92..ac740e797 100644 --- a/src/core/system.cpp +++ b/src/core/system.cpp @@ -5149,11 +5149,14 @@ bool System::DoRunahead() return false; } -void System::SetRunaheadReplayFlag() +void System::SetRunaheadReplayFlag(bool is_analog_input) { if (s_state.runahead_frames == 0 || s_state.memory_save_state_count == 0) return; + if (is_analog_input && !g_settings.runahead_for_analog_input) + return; + #ifdef PROFILE_MEMORY_SAVE_STATES DEV_LOG("Runahead rewind pending..."); #endif diff --git a/src/core/system.h b/src/core/system.h index 9f96175f8..1a79d6daf 100644 --- a/src/core/system.h +++ b/src/core/system.h @@ -437,7 +437,7 @@ std::string GetImageForLoadingScreen(const std::string& game_path); ////////////////////////////////////////////////////////////////////////// void CalculateRewindMemoryUsage(u32 num_saves, u32 resolution_scale, u64* ram_usage, u64* vram_usage); void ClearMemorySaveStates(bool reallocate_resources, bool recycle_textures); -void SetRunaheadReplayFlag(); +void SetRunaheadReplayFlag(bool is_analog_input); /// Asynchronous work tasks, complete on worker thread. void QueueAsyncTask(std::function function); diff --git a/src/duckstation-qt/emulationsettingswidget.cpp b/src/duckstation-qt/emulationsettingswidget.cpp index c8df45793..774baadf7 100644 --- a/src/duckstation-qt/emulationsettingswidget.cpp +++ b/src/duckstation-qt/emulationsettingswidget.cpp @@ -31,6 +31,8 @@ EmulationSettingsWidget::EmulationSettingsWidget(SettingsWindow* dialog, QWidget SettingWidgetBinder::BindWidgetToFloatSetting(sif, m_ui.rewindSaveFrequency, "Main", "RewindFrequency", 10.0f); SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.rewindSaveSlots, "Main", "RewindSaveSlots", 10); SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.runaheadFrames, "Main", "RunaheadFrameCount", 0); + SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.runaheadForAnalogInput, "Main", "RunaheadForAnalogInput", + false); const float effective_emulation_speed = m_dialog->getEffectiveFloatValue("Main", "EmulationSpeed", 1.0f); fillComboBoxWithEmulationSpeeds(m_ui.emulationSpeed, effective_emulation_speed); @@ -144,6 +146,9 @@ EmulationSettingsWidget::EmulationSettingsWidget(SettingsWindow* dialog, QWidget m_ui.runaheadFrames, tr("Runahead"), tr("Disabled"), tr( "Simulates the system ahead of time and rolls back/replays to reduce input lag. Very high system requirements.")); + dialog->registerWidgetHelp( + m_ui.runaheadForAnalogInput, tr("Enable for Analog Input"), tr("Unchecked"), + tr("Activates runahead when analog input changes, which significantly increases system requirements.")); onOptimalFramePacingChanged(); updateSkipDuplicateFramesEnabled(); @@ -239,6 +244,7 @@ void EmulationSettingsWidget::updateRewind() const bool rewind_enabled = m_dialog->getEffectiveBoolValue("Main", "RewindEnable", false); const bool runahead_enabled = m_dialog->getIntValue("Main", "RunaheadFrameCount", 0) > 0; m_ui.rewindEnable->setEnabled(!runahead_enabled); + m_ui.runaheadForAnalogInput->setEnabled(runahead_enabled); if (!runahead_enabled && rewind_enabled) { diff --git a/src/duckstation-qt/emulationsettingswidget.ui b/src/duckstation-qt/emulationsettingswidget.ui index c567bf296..9e2908748 100644 --- a/src/duckstation-qt/emulationsettingswidget.ui +++ b/src/duckstation-qt/emulationsettingswidget.ui @@ -7,7 +7,7 @@ 0 0 618 - 440 + 481 @@ -133,16 +133,9 @@ - Rewind/Runahead + Rewind - - - - - Enable Rewinding - - - + @@ -150,16 +143,26 @@ - - + + - Seconds + Frames + + + 1 - 3600.000000000000000 + 10000 - - 0.100000000000000 + + + + + + TextLabel + + + true @@ -170,27 +173,43 @@ - - + + - Frames - - - 1 + Seconds - 10000 + 3600.000000000000000 + + + 0.100000000000000 + + + + + + + Enable Rewinding - + + + + + + + Runahead + + + Runahead: - + @@ -249,13 +268,10 @@ - - + + - TextLabel - - - true + Enable for Analog Input @@ -269,8 +285,8 @@ - 20 - 40 + 0 + 0