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

Savestates: Some improvements #15438

Merged
merged 8 commits into from
Apr 13, 2024
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 25 additions & 4 deletions rpcs3/Emu/CPU/CPUThread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,9 @@ struct cpu_prof
// Total number of samples
u64 samples = 0, idle = 0;

// Total number of sample collected in reservation operation
u64 reservation_samples = 0;

// Avoid printing replicas or when not much changed
u64 new_samples = 0;

Expand All @@ -101,6 +104,7 @@ struct cpu_prof
samples = 0;
idle = 0;
new_samples = 0;
reservation_samples = 0;
}

static std::string format(const std::multimap<u64, u64, std::greater<u64>>& chart, u64 samples, u64 idle, bool extended_print = false)
Expand Down Expand Up @@ -140,7 +144,7 @@ struct cpu_prof
{
if (cpu_flag::exit - ptr->state)
{
profiler.notice("Thread \"%s\" [0x%08x]: %u samples, %u new (%.4f%% idle): Not enough new samples have been collected since the last print.", ptr->get_name(), ptr->id, samples, new_samples, 100. * idle / samples);
profiler.notice("Thread \"%s\" [0x%08x]: %u samples (%.4f%% idle), %u new, %u reservation (%.4f%%): Not enough new samples have been collected since the last print.", ptr->get_name(), ptr->id, samples, 100. * idle / samples, new_samples, reservation_samples, 100. * reservation_samples / samples);
}

return;
Expand All @@ -156,7 +160,7 @@ struct cpu_prof

// Print results
const std::string results = format(chart, samples, idle);
profiler.notice("Thread \"%s\" [0x%08x]: %u samples, %u new (%.4f%% idle):%s", ptr->get_name(), ptr->id, samples, new_samples, 100. * idle / samples, results);
profiler.notice("Thread \"%s\" [0x%08x]: %u samples (%.4f%% idle), %u new, %u reservation (%.4f%%):\n%s", ptr->get_name(), ptr->id, samples, 100. * idle / samples, new_samples, reservation_samples, 100. * reservation_samples / samples, results);

new_samples = 0;
}
Expand All @@ -177,6 +181,7 @@ struct cpu_prof
std::unordered_map<u64, u64, value_hash<u64>> freq;

u64 samples = 0, idle = 0;
u64 reservation = 0;

for (auto& [_, info] : threads)
{
Expand All @@ -188,6 +193,7 @@ struct cpu_prof

samples += info.samples;
idle += info.idle;
reservation += info.reservation_samples;
}

if (samples == idle)
Expand All @@ -197,7 +203,7 @@ struct cpu_prof

if (new_samples < min_print_all_samples && thread_ctrl::state() != thread_state::aborting)
{
profiler.notice("All Threads: %u samples, %u new (%.4f%% idle): Not enough new samples have been collected since the last print.", samples, new_samples, 100. * idle / samples);
profiler.notice("All Threads: %u samples (%.4f%% idle), %u new, %u reservation (%.4f%%): Not enough new samples have been collected since the last print.", samples, 100. * idle / samples, new_samples, reservation, 100. * reservation / samples);
return;
}

Expand All @@ -207,7 +213,7 @@ struct cpu_prof
}

const std::string results = format(chart, samples, idle, true);
profiler.notice("All Threads: %u samples, %u new (%.4f%% idle):%s", samples, new_samples, 100. * idle / samples, results);
profiler.notice("All Threads: %u samples (%.4f%% idle), %u new, %u reservation (%.4f%%):%s", samples, 100. * idle / samples, new_samples, reservation, 100. * reservation / samples, results);
}
};

Expand Down Expand Up @@ -281,6 +287,14 @@ struct cpu_prof
info.freq[name]++;
info.new_samples++;

if (auto spu = ptr->try_get<spu_thread>())
{
if (spu->raddr)
{
info.reservation_samples++;
}
}

// Append verification time to fixed common name 0000000...chunk-0x3fffc
if (name >> 16 && (name & 0xffff) == 0)
info.freq[0xffff]++;
Expand Down Expand Up @@ -315,6 +329,13 @@ struct cpu_prof
continue;
}

if (!g_cfg.core.spu_debug)
{
// Reduce accuracy in favor of performance when enabled alone
thread_ctrl::wait_for(60, false);
continue;
}

// Wait, roughly for 20µs
thread_ctrl::wait_for(20, false);
}
Expand Down
21 changes: 21 additions & 0 deletions rpcs3/Emu/Cell/Modules/cellSysutilAvc2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,12 +68,33 @@ struct avc2_settings
avc2_settings(const avc2_settings&) = delete;
avc2_settings& operator=(const avc2_settings&) = delete;

SAVESTATE_INIT_POS(52);

vm::ptr<CellSysutilAvc2Callback> avc2_cb{};
vm::ptr<void> avc2_cb_arg{};
u32 streaming_mode = CELL_SYSUTIL_AVC2_STREAMING_MODE_NORMAL;
u8 mic_out_stream_sharing = 0;
u8 video_stream_sharing = 0;
u32 total_video_bitrate = 0;

avc2_settings(utils::serial& ar) noexcept
{
[[maybe_unused]] const s32 version = GET_SERIALIZATION_VERSION(cellSysutil);

if (version == 0)
{
return;
}

save(ar);
}

void save(utils::serial& ar)
{
GET_OR_USE_SERIALIZATION_VERSION(ar.is_writing(), cellSysutil);

ar(avc2_cb, avc2_cb_arg, streaming_mode, mic_out_stream_sharing, video_stream_sharing, total_video_bitrate);
}
};

error_code cellSysutilAvc2GetPlayerInfo(vm::cptr<SceNpMatching2RoomMemberId> player_id, vm::ptr<CellSysutilAvc2PlayerInfo> player_info)
Expand Down
2 changes: 1 addition & 1 deletion rpcs3/Emu/Cell/SPUThread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1842,7 +1842,7 @@ void spu_thread::cpu_work()
const u32 pos_at = pc / 4;
const u32 pos_bit = 1u << (pos_at % 8);

if (local_breakpoints[pos_at] & pos_bit)
if (local_breakpoints[pos_at / 8] & pos_bit)
{
// Ignore repeatations until a different instruction is issued
if (pc != current_bp_pc)
Expand Down
54 changes: 40 additions & 14 deletions rpcs3/Emu/System.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3022,11 +3022,14 @@ void Emulator::Kill(bool allow_autoexit, bool savestate, savestate_stage* save_s
*join_thread = make_ptr(new named_thread("Emulation Join Thread"sv, [join_thread, savestate, allow_autoexit, this]() mutable
{
fs::pending_file file;
std::shared_ptr<stx::init_mutex> init_mtx = std::make_shared<stx::init_mutex>();
std::shared_ptr<bool> join_ended = std::make_shared<bool>(false);
atomic_ptr<utils::serial> to_ar;

named_thread stop_watchdog("Stop Watchdog"sv, [&to_ar, init_mtx, join_ended, this]()
auto verbose_message = std::make_shared<atomic_ptr<std::string>>();
auto init_mtx = std::make_shared<stx::init_mutex>();
auto join_ended = std::make_shared<bool>(false);
auto to_ar = std::make_shared<atomic_ptr<utils::serial>>();

auto stop_watchdog = make_ptr(new named_thread("Stop Watchdog"sv,
[to_ar, init_mtx, join_ended, verbose_message, this]()
{
const auto closed_sucessfully = std::make_shared<atomic_t<bool>>(false);

Expand Down Expand Up @@ -3058,10 +3061,10 @@ void Emulator::Kill(bool allow_autoexit, bool savestate, savestate_stage* save_s

while (thread_ctrl::state() != thread_state::aborting)
{
if (auto ar_ptr = to_ar.load())
if (auto ar_ptr = to_ar->load())
{
// Total amount of waiting: about 10s
GetCallbacks().on_save_state_progress(closed_sucessfully, ar_ptr, init_mtx);
GetCallbacks().on_save_state_progress(closed_sucessfully, ar_ptr, verbose_message.get(), init_mtx);

while (thread_ctrl::state() != thread_state::aborting)
{
Expand All @@ -3076,7 +3079,7 @@ void Emulator::Kill(bool allow_autoexit, bool savestate, savestate_stage* save_s


*closed_sucessfully = true;
});
}));

// Join threads
for (const auto& [type, data] : *g_fxo)
Expand All @@ -3097,8 +3100,15 @@ void Emulator::Kill(bool allow_autoexit, bool savestate, savestate_stage* save_s

static_cast<void>(init_mtx->init());

auto set_progress_message = [&](std::string_view text)
{
*verbose_message = stx::make_single<std::string>(text);
};

while (savestate)
{
set_progress_message("Creating File");

path = get_savestate_file(m_title_id, m_path, 0, 0);

// The function is meant for reading files, so if there is no GZ file it would not return compressed file path
Expand All @@ -3124,7 +3134,7 @@ void Emulator::Kill(bool allow_autoexit, bool savestate, savestate_stage* save_s

auto serial_ptr = stx::make_single<utils::serial>();
serial_ptr->m_file_handler = make_compressed_serialization_file_handler(file.file);
to_ar = std::move(serial_ptr);
*to_ar = std::move(serial_ptr);

signal_system_cache_can_stay();
break;
Expand All @@ -3142,7 +3152,7 @@ void Emulator::Kill(bool allow_autoexit, bool savestate, savestate_stage* save_s
return fmt::format("Emu State Capture Thread: '%s'", g_tls_serialize_name);
};

auto& ar = *to_ar.load();
auto& ar = *to_ar->load();

read_used_savestate_versions(); // Reset version data
USING_SERIALIZATION_VERSION(global_version);
Expand Down Expand Up @@ -3217,6 +3227,8 @@ void Emulator::Kill(bool allow_autoexit, bool savestate, savestate_stage* save_s
ar(std::string{});
};

set_progress_message("Creating Header");

ar("RPCS3SAV"_u64);
ar(std::endian::native == std::endian::little);
ar(g_cfg.savestate.state_inspection_mode.get());
Expand Down Expand Up @@ -3256,12 +3268,22 @@ void Emulator::Kill(bool allow_autoexit, bool savestate, savestate_stage* save_s

ar(klic.empty() ? std::array<u8, 16>{} : std::bit_cast<std::array<u8, 16>>(klic[0]));
ar(m_game_dir);

set_progress_message("Saving HDD1");
save_hdd1();
set_progress_message("Saving HDD0");
save_hdd0();

ar(std::array<u8, 32>{}); // Reserved for future use

set_progress_message("Saving VMemory");
vm::save(ar);

set_progress_message("Saving FXO");
g_fxo->save(ar);

set_progress_message("Finalizing File");

bs_t<SaveStateExtentionFlags1> extension_flags{SaveStateExtentionFlags1::SupportsMenuOpenResume};

if (g_fxo->get<SysutilMenuOpenStatus>().active)
Expand All @@ -3285,18 +3307,17 @@ void Emulator::Kill(bool allow_autoexit, bool savestate, savestate_stage* save_s
if (emu_state_cap_thread == thread_state::errored)
{
sys_log.error("Saving savestate failed due to fatal error!");
to_ar.reset();
to_ar->reset();
savestate = false;
}
}

stop_watchdog = thread_state::finished;
static_cast<void>(init_mtx->reset());

if (savestate)
{
fs::stat_t file_stat{};

set_progress_message("Commiting File");

if (!file.commit() || !fs::get_stat(path, file_stat))
{
sys_log.error("Failed to write savestate to file! (path='%s', %s)", path, fs::g_tls_error);
Expand Down Expand Up @@ -3394,8 +3415,10 @@ void Emulator::Kill(bool allow_autoexit, bool savestate, savestate_stage* save_s
}
}

set_progress_message("Resetting Objects");

// Final termination from main thread (move the last ownership of join thread in order to destroy it)
CallFromMainThread([join_thread = std::move(join_thread), allow_autoexit, this]() mutable
CallFromMainThread([join_thread = std::move(join_thread), verbose_message, stop_watchdog, init_mtx, allow_autoexit, this]()
{
cpu_thread::cleanup();

Expand All @@ -3407,6 +3430,9 @@ void Emulator::Kill(bool allow_autoexit, bool savestate, savestate_stage* save_s

vm::close();

*stop_watchdog = thread_state::finished;
static_cast<void>(init_mtx->reset());

jit_runtime::finalize();

perf_stat_base::report();
Expand Down
2 changes: 1 addition & 1 deletion rpcs3/Emu/System.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ struct EmuCallbacks
std::function<void()> on_ready;
std::function<bool()> on_missing_fw;
std::function<void(std::shared_ptr<atomic_t<bool>>, int)> on_emulation_stop_no_response;
std::function<void(std::shared_ptr<atomic_t<bool>>, stx::shared_ptr<utils::serial>, std::shared_ptr<void>)> on_save_state_progress;
std::function<void(std::shared_ptr<atomic_t<bool>>, stx::shared_ptr<utils::serial>, stx::atomic_ptr<std::string>*, std::shared_ptr<void>)> on_save_state_progress;
std::function<void(bool enabled)> enable_disc_eject;
std::function<void(bool enabled)> enable_disc_insert;
std::function<bool(bool, std::function<void()>)> try_to_quit; // (force_quit, on_exit) Try to close RPCS3
Expand Down
Loading