Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sly Savestates Improvements #12870

Merged
merged 6 commits into from Oct 23, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
47 changes: 28 additions & 19 deletions rpcs3/Emu/System.cpp
Expand Up @@ -81,6 +81,7 @@ extern std::pair<std::shared_ptr<lv2_overlay>, CellError> ppu_load_overlay(const
extern bool ppu_load_rel_exec(const ppu_rel_object&);
extern bool is_savestate_version_compatible(const std::vector<std::pair<u16, u16>>& data, bool is_boot_check);
extern std::vector<std::pair<u16, u16>> read_used_savestate_versions();
std::string get_savestate_path(std::string_view title_id, std::string_view boot_path);

fs::file g_tty;
atomic_t<s64> g_tty_size{0};
Expand Down Expand Up @@ -676,19 +677,27 @@ game_boot_result Emulator::BootGame(const std::string& path, const std::string&

auto error = Load(title_id, add_only);

if (g_cfg.savestate.suspend_emu && m_ar)
if (g_cfg.savestate.suspend_emu)
{
std::string old_path = path.substr(0, path.find_last_not_of(fs::delim) + 1);
const usz insert_pos = old_path.find_last_of(fs::delim) + 1;
const auto prefix = "used_"sv;

if (old_path.compare(insert_pos, prefix.size(), prefix) != 0)
for (std::string old_path : std::initializer_list<std::string>{m_ar ? path : "", m_title_id.empty() ? "" : get_savestate_path(m_title_id, path)})
{
old_path.insert(insert_pos, prefix);
if (old_path.empty())
{
continue;
}

old_path = old_path.substr(0, old_path.find_last_not_of(fs::delim) + 1);
const usz insert_pos = old_path.find_last_of(fs::delim) + 1;
const auto prefix = "used_"sv;

if (fs::rename(path, old_path, true))
if (old_path.compare(insert_pos, prefix.size(), prefix) != 0)
{
sys_log.notice("Savestate has been moved to path='%s'", old_path);
old_path.insert(insert_pos, prefix);

if (fs::rename(path, old_path, true))
{
sys_log.notice("Savestate has been moved to path='%s'", old_path);
}
}
}
}
Expand Down Expand Up @@ -2393,20 +2402,13 @@ std::shared_ptr<utils::serial> Emulator::Kill(bool allow_autoexit, bool savestat

if (!_path.empty())
{
if (!g_cfg.savestate.suspend_emu)
{
save_tar(_path);
}
else
{
ar(usz{});
}
save_tar(_path);
}
};

auto save_hdd0 = [&]()
{
if (!g_cfg.savestate.suspend_emu && g_cfg.savestate.save_disc_game_data)
if (g_cfg.savestate.save_disc_game_data)
{
const std::string path = vfs::get("/dev_hdd0/game/");

Expand Down Expand Up @@ -2512,7 +2514,7 @@ std::shared_ptr<utils::serial> Emulator::Kill(bool allow_autoexit, bool savestat

if (savestate)
{
const std::string path = fs::get_cache_dir() + "/savestates/" + (m_title_id.empty() ? m_path.substr(m_path.find_last_of(fs::delim) + 1) : m_title_id) + ".SAVESTAT";
const std::string path = get_savestate_path(m_title_id, m_path);

fs::pending_file file(path);

Expand Down Expand Up @@ -2557,6 +2559,13 @@ std::shared_ptr<utils::serial> Emulator::Kill(bool allow_autoexit, bool savestat
}

ar.set_reading_state();

if (!g_cfg.savestate.suspend_emu)
{
to_ar.reset();
Restart();
return to_ar;
}
}

// Boot arg cleanup (preserved in the case restarting)
Expand Down
5 changes: 5 additions & 0 deletions rpcs3/Emu/savestate_utils.cpp
Expand Up @@ -146,6 +146,11 @@ bool is_savestate_version_compatible(const std::vector<std::pair<u16, u16>>& dat
return ok;
}

std::string get_savestate_path(std::string_view title_id, std::string_view boot_path)
{
return fs::get_cache_dir() + "/savestates/" + std::string{title_id.empty() ? boot_path.substr(boot_path.find_last_of(fs::delim) + 1) : title_id} + ".SAVESTAT";
}

bool is_savestate_compatible(const fs::file& file)
{
return is_savestate_version_compatible(get_savestate_versioning_data(file), false);
Expand Down
2 changes: 1 addition & 1 deletion rpcs3/Emu/system_config.h
Expand Up @@ -315,7 +315,7 @@ struct cfg_root : cfg::node
node_savestate(cfg::node* _this) : cfg::node(_this, "Savestate") {}

cfg::_bool start_paused{ this, "Start Paused" }; // Pause on first frame
cfg::_bool suspend_emu{ this, "Suspend Emulation Savestate Mode", true }; // Close emulation when saving, delete save after loading
cfg::_bool suspend_emu{ this, "Suspend Emulation Savestate Mode", false }; // Close emulation when saving, delete save after loading
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};
Expand Down
4 changes: 4 additions & 0 deletions rpcs3/rpcs3qt/emu_settings_type.h
Expand Up @@ -35,6 +35,7 @@ enum class emu_settings_type
SPUCache,
DebugConsoleMode,
SilenceAllLogs,
SuspendEmulationSavestateMode,
MaxSPURSThreads,
SleepTimersAccuracy,
ClocksScale,
Expand Down Expand Up @@ -354,4 +355,7 @@ inline static const QMap<emu_settings_type, cfg_location> settings_location =
{ emu_settings_type::LimitCacheSize, { "VFS", "Limit disk cache size"}},
{ emu_settings_type::MaximumCacheSize, { "VFS", "Disk cache maximum size (MB)"}},
{ emu_settings_type::ConsoleTimeOffset, { "System", "Console time offset (s)"}},

// Savestates
{ emu_settings_type::SuspendEmulationSavestateMode, { "Savestate", "Suspend Emulation Savestate Mode" }},
};
7 changes: 7 additions & 0 deletions rpcs3/rpcs3qt/main_window.cpp
Expand Up @@ -1735,6 +1735,7 @@ void main_window::EnableMenus(bool enabled) const
ui->toolsRsxDebuggerAct->setEnabled(enabled);
ui->toolsStringSearchAct->setEnabled(enabled);
ui->actionCreate_RSX_Capture->setEnabled(enabled);
ui->actionCreate_Savestate->setEnabled(enabled);
}

void main_window::OnEnableDiscEject(bool enabled) const
Expand Down Expand Up @@ -2023,6 +2024,12 @@ void main_window::CreateConnects()
g_user_asked_for_frame_capture = true;
});

connect(ui->actionCreate_Savestate, &QAction::triggered, this, []()
{
gui_log.notice("User triggered savestate creation from utilities.");
Emu.Kill(false, true);
});

connect(ui->bootSavestateAct, &QAction::triggered, this, &main_window::BootSavestate);

connect(ui->addGamesAct, &QAction::triggered, this, [this]()
Expand Down
8 changes: 8 additions & 0 deletions rpcs3/rpcs3qt/main_window.ui
Expand Up @@ -1145,6 +1145,14 @@
<string>Create RSX Capture</string>
</property>
</action>
<action name="actionCreate_Savestate">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Create Savestate</string>
</property>
</action>
<action name="actionManage_Game_Patches">
<property name="text">
<string>Game Patches</string>
Expand Down
3 changes: 3 additions & 0 deletions rpcs3/rpcs3qt/settings_dialog.cpp
Expand Up @@ -1325,6 +1325,9 @@ settings_dialog::settings_dialog(std::shared_ptr<gui_settings> gui_settings, std
m_emu_settings->EnhanceCheckBox(ui->accuratePPUFPCC, emu_settings_type::AccuratePPUFPCC);
SubscribeTooltip(ui->accuratePPUFPCC, tooltips.settings.accurate_ppufpcc);

m_emu_settings->EnhanceCheckBox(ui->suspendSavestates, emu_settings_type::SuspendEmulationSavestateMode);
SubscribeTooltip(ui->suspendSavestates, tooltips.settings.suspend_savestates);

m_emu_settings->EnhanceCheckBox(ui->silenceAllLogs, emu_settings_type::SilenceAllLogs);
SubscribeTooltip(ui->silenceAllLogs, tooltips.settings.silence_all_logs);

Expand Down
7 changes: 7 additions & 0 deletions rpcs3/rpcs3qt/settings_dialog.ui
Expand Up @@ -2334,6 +2334,13 @@
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="suspendSavestates">
<property name="text">
<string>Suspend-Emulation Savestates Mode</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="silenceAllLogs">
<property name="text">
Expand Down
1 change: 1 addition & 0 deletions rpcs3/rpcs3qt/tooltips.h
Expand Up @@ -42,6 +42,7 @@ class Tooltips : public QObject
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 allow_host_labels = tr("Allows the host GPU to synchronize with CELL directly. This incurs a performance penalty, but exposes the true state of GPU objects to the guest CPU. Can help eliminate visual noise and glitching at the cost of performance. Use with caution.");
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 artefacts 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.");

// audio

Expand Down