Add Savestate-Compatible mode

This commit is contained in:
Eladash 2023-09-29 05:42:14 +03:00 committed by Elad Ashkenazi
parent 948ee96f1a
commit 5baec6cb58
7 changed files with 56 additions and 18 deletions

View File

@ -1390,8 +1390,8 @@ extern bool try_lock_spu_threads_in_a_state_compatible_with_savestates(bool reve
return &spu_list;
};
// Attempt to lock for half a second, if somehow takes longer abort it
for (u64 start = 0, passed_count = 0; passed_count < 10; std::this_thread::yield())
// Attempt to lock for a second, if somehow takes longer abort it
for (u64 start = 0, passed_count = 0; passed_count < 10;)
{
if (revert_lock)
{
@ -1413,12 +1413,12 @@ extern bool try_lock_spu_threads_in_a_state_compatible_with_savestates(bool reve
// Try to fetch SPUs out of critical section
const auto spu_list = get_spus(true);
// Avoid using suspend_all when more than one thread is known to be unsavable
// Avoid using suspend_all when more than 2 threads known to be unsavable
u32 unsavable_threads = 0;
for (auto& spu : *spu_list)
{
if (spu->unsavable && !spu->state.all_of(cpu_flag::wait + cpu_flag::exit))
if (spu->unsavable)
{
unsavable_threads++;
@ -1431,6 +1431,7 @@ extern bool try_lock_spu_threads_in_a_state_compatible_with_savestates(bool reve
if (unsavable_threads >= 3)
{
std::this_thread::yield();
continue;
}
@ -1445,19 +1446,24 @@ extern bool try_lock_spu_threads_in_a_state_compatible_with_savestates(bool reve
return true;
}
bool failed = false;
const bool is_emu_paused = Emu.IsPaused();
for (auto& spu : *spu_list)
{
if (spu->unsavable && !spu->state.all_of(cpu_flag::wait + cpu_flag::exit))
if (spu->unsavable)
{
return true;
failed = true;
break;
}
if (Emu.IsPaused())
if (is_emu_paused)
{
// If emulation is paused, we can only hope it's already in a state compatible with savestates
if (!(spu->state & (cpu_flag::dbg_global_pause + cpu_flag::dbg_pause)))
{
return true;
failed = true;
break;
}
}
else
@ -1467,7 +1473,16 @@ extern bool try_lock_spu_threads_in_a_state_compatible_with_savestates(bool reve
}
}
return false;
if (failed && paused_anyone)
{
// For faster signalling, first remove state flags then batch notifications
for (auto& spu : *spu_list)
{
spu->state -= cpu_flag::dbg_global_pause;
}
}
return failed;
}))
{
if (Emu.IsPaused())
@ -1478,15 +1493,10 @@ extern bool try_lock_spu_threads_in_a_state_compatible_with_savestates(bool reve
if (!paused_anyone)
{
// Need not do anything
std::this_thread::yield();
continue;
}
// For faster signalling, first remove state flags then batch notifications
for (auto& spu : *spu_list)
{
spu->state -= cpu_flag::dbg_global_pause;
}
for (auto& spu : *spu_list)
{
if (spu->state & cpu_flag::wait)
@ -1495,6 +1505,7 @@ extern bool try_lock_spu_threads_in_a_state_compatible_with_savestates(bool reve
}
}
std::this_thread::yield();
continue;
}

View File

@ -7110,10 +7110,23 @@ public:
case SPU_RdEventStat:
{
update_pc();
//ensure_gpr_stores();
m_ir->CreateStore(m_ir->getInt8(1), spu_ptr<u8>(&spu_thread::unsavable));
if (g_cfg.savestate.compatible_mode)
{
ensure_gpr_stores();
}
else
{
m_ir->CreateStore(m_ir->getInt8(1), spu_ptr<u8>(&spu_thread::unsavable));
}
res.value = call("spu_read_events", &exec_read_events, m_thread);
m_ir->CreateStore(m_ir->getInt8(0), spu_ptr<u8>(&spu_thread::unsavable));
if (!g_cfg.savestate.compatible_mode)
{
m_ir->CreateStore(m_ir->getInt8(0), spu_ptr<u8>(&spu_thread::unsavable));
}
break;
}
case SPU_RdMachStat:

View File

@ -317,6 +317,7 @@ struct cfg_root : cfg::node
cfg::_bool start_paused{ this, "Start Paused", false }; // Pause on first frame
cfg::_bool suspend_emu{ this, "Suspend Emulation Savestate Mode", false }; // Close emulation when saving, delete save after loading
cfg::_bool compatible_mode{ this, "Compatible Savestate Mode", false }; // SPU emulation optimized for savestate compatibility (off by default for performance reasons)
cfg::_bool state_inspection_mode{ this, "Inspection Mode Savestates" }; // Save memory stored in executable files, thus allowing to view state without any files (for debugging)
cfg::_bool save_disc_game_data{ this, "Save Disc Game Data", false };
} savestate{this};

View File

@ -36,6 +36,7 @@ enum class emu_settings_type
DebugConsoleMode,
SilenceAllLogs,
SuspendEmulationSavestateMode,
CompatibleEmulationSavestateMode,
StartSavestatePaused,
MaxSPURSThreads,
SleepTimersAccuracy,
@ -380,5 +381,6 @@ inline static const QMap<emu_settings_type, cfg_location> settings_location =
// Savestates
{ emu_settings_type::SuspendEmulationSavestateMode, { "Savestate", "Suspend Emulation Savestate Mode" }},
{ emu_settings_type::CompatibleEmulationSavestateMode, { "Savestate", "Compatible Savestate Mode" }},
{ emu_settings_type::StartSavestatePaused, { "Savestate", "Start Paused" }},
};

View File

@ -1465,6 +1465,9 @@ settings_dialog::settings_dialog(std::shared_ptr<gui_settings> gui_settings, std
m_emu_settings->EnhanceCheckBox(ui->suspendSavestates, emu_settings_type::SuspendEmulationSavestateMode);
SubscribeTooltip(ui->suspendSavestates, tooltips.settings.suspend_savestates);
m_emu_settings->EnhanceCheckBox(ui->compatibleSavestates, emu_settings_type::CompatibleEmulationSavestateMode);
SubscribeTooltip(ui->compatibleSavestates, tooltips.settings.compatible_savestates);
m_emu_settings->EnhanceCheckBox(ui->silenceAllLogs, emu_settings_type::SilenceAllLogs);
SubscribeTooltip(ui->silenceAllLogs, tooltips.settings.silence_all_logs);

View File

@ -2421,6 +2421,13 @@
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="compatibleSavestates">
<property name="text">
<string>SPU-Compatible Savestates Mode</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="silenceAllLogs">
<property name="text">

View File

@ -52,6 +52,7 @@ public:
const QString vulkan_async_scheduler = tr("Determines how to schedule GPU async compute jobs when using asynchronous streaming.\nUse 'Safe' mode for more spec compliant behavior at the cost of some CPU overhead. This setting works with all devices.\nUse 'Fast' to use a faster but hacky version. This option is internally disabled for NVIDIA GPUs due to causing GPU hangs.");
const QString disable_msl_fast_math = tr("Disables Fast Math for MSL shaders, which may violate the IEEE 754 standard.\nDisabling it may fix some artifacts, especially on Apple GPUs, at the cost of performance.");
const QString suspend_savestates = tr("When this mode is on, emulation exits when saving and the savestate file is concealed after loading it, preventing reuse by RPCS3.\nThis mode is like hibernation of emulation: if you don't want to be able to cheat using savestates when playing the game, consider using this mode.\nDo note that the savestate file is not gone completely, just ignored by RPCS3. You can manually relaunch it if needed.");
const QString compatible_savestates = tr("When this mode is on, SPU emulation prioritizes savestate compatibility, however, it may reduce performance slightly.\nWhen this mode is off, some games may not allow making a savestate and show an SPU pause error in the log.");
const QString paused_savestates = tr("When this mode is on, savestates are loaded and paused on the first frame.\nThis allows players to prepare for gameplay without being thrown into the action immediately.");
// audio