mirror of
https://github.com/stenzek/duckstation.git
synced 2026-02-11 17:04:33 +00:00
System: Add 'Runahead for Analog Input' option
Mitigates the performance impact of serializing PGXP state.
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -101,8 +101,8 @@ void AnalogJoystick::SetBindState(u32 index, float value)
|
||||
return;
|
||||
|
||||
const u8 u8_value = static_cast<u8>(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;
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -126,7 +126,7 @@ void JogCon::SetBindState(u32 index, float value)
|
||||
-static_cast<s8>((static_cast<u32>(m_half_axis_state[static_cast<u32>(HalfAxis::SteeringLeft)]) + 1) / 2);
|
||||
|
||||
if (m_steering_state != prev_steering_state)
|
||||
System::SetRunaheadReplayFlag();
|
||||
System::SetRunaheadReplayFlag(true);
|
||||
}
|
||||
|
||||
const u16 bit = u16(1) << static_cast<u8>(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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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<u16>(std::min(si.GetUIntValue("Main", "RewindSaveSlots", 10u), 65535u));
|
||||
runahead_frames = static_cast<u8>(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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<void()> function);
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>618</width>
|
||||
<height>440</height>
|
||||
<height>481</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
@@ -133,16 +133,9 @@
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="title">
|
||||
<string>Rewind/Runahead</string>
|
||||
<string>Rewind</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_3" columnstretch="0,1">
|
||||
<item row="0" column="0">
|
||||
<widget class="QCheckBox" name="rewindEnable">
|
||||
<property name="text">
|
||||
<string>Enable Rewinding</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<layout class="QGridLayout" name="gridLayout_3" columnstretch="0,0">
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
@@ -150,26 +143,6 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QDoubleSpinBox" name="rewindSaveFrequency">
|
||||
<property name="suffix">
|
||||
<string> Seconds</string>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<double>3600.000000000000000</double>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<double>0.100000000000000</double>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Rewind Buffer Size:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QSpinBox" name="rewindSaveSlots">
|
||||
<property name="suffix">
|
||||
@@ -183,14 +156,60 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<item row="3" column="0" colspan="2">
|
||||
<widget class="QLabel" name="rewindSummary">
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Rewind Buffer Size:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QDoubleSpinBox" name="rewindSaveFrequency">
|
||||
<property name="suffix">
|
||||
<string> Seconds</string>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<double>3600.000000000000000</double>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<double>0.100000000000000</double>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="rewindEnable">
|
||||
<property name="text">
|
||||
<string>Enable Rewinding</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_3">
|
||||
<property name="title">
|
||||
<string>Runahead</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_4">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Runahead:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="runaheadFrames">
|
||||
<item>
|
||||
<property name="text">
|
||||
@@ -249,13 +268,10 @@
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0" colspan="2">
|
||||
<widget class="QLabel" name="rewindSummary">
|
||||
<item row="1" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="runaheadForAnalogInput">
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
<string>Enable for Analog Input</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -269,8 +285,8 @@
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
|
||||
Reference in New Issue
Block a user