GUI: Progress Dialog On Save State Creation

This commit is contained in:
Eladash 2024-03-24 12:08:42 +02:00 committed by Elad Ashkenazi
parent 707a648a4c
commit 580f9bf03a
6 changed files with 90 additions and 20 deletions

View File

@ -139,7 +139,8 @@ bool zip(const void* src, usz size, fs::file& out, bool multi_thread_it)
return false; return false;
} }
utils::serial compressor(!multi_thread_it || size < 0x40'0000); utils::serial compressor;
compressor.set_expect_little_data(!multi_thread_it || size < 0x40'0000);
compressor.m_file_handler = make_compressed_serialization_file_handler(out); compressor.m_file_handler = make_compressed_serialization_file_handler(out);
std::string_view buffer_view{static_cast<const char*>(src), size}; std::string_view buffer_view{static_cast<const char*>(src), size};

View File

@ -2970,13 +2970,16 @@ 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 *join_thread = make_ptr(new named_thread("Emulation Join Thread"sv, [join_thread, savestate, allow_autoexit, this]() mutable
{ {
named_thread stop_watchdog("Stop Watchdog"sv, [this]() 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, join_ended, this]()
{ {
const auto closed_sucessfully = std::make_shared<atomic_t<bool>>(false); const auto closed_sucessfully = std::make_shared<atomic_t<bool>>(false);
bool is_being_held_longer = false; bool is_being_held_longer = false;
for (int i = 0; thread_ctrl::state() != thread_state::aborting;) for (int i = 0; !*join_ended && thread_ctrl::state() != thread_state::aborting;)
{ {
if (g_watchdog_hold_ctr) if (g_watchdog_hold_ctr)
{ {
@ -3000,6 +3003,25 @@ void Emulator::Kill(bool allow_autoexit, bool savestate, savestate_stage* save_s
thread_ctrl::wait_for(5'000); thread_ctrl::wait_for(5'000);
} }
for (int i = 0; thread_ctrl::state() != thread_state::aborting;)
{
if (auto ar_ptr = to_ar.load())
{
// Total amount of waiting: about 10s
GetCallbacks().on_save_state_progress(closed_sucessfully, ar_ptr);
while (thread_ctrl::state() != thread_state::aborting)
{
thread_ctrl::wait_for(5'000);
}
break;
}
thread_ctrl::wait_for(5'000);
}
*closed_sucessfully = true; *closed_sucessfully = true;
}); });
@ -3018,8 +3040,6 @@ void Emulator::Kill(bool allow_autoexit, bool savestate, savestate_stage* save_s
sys_log.notice("All threads have been stopped."); sys_log.notice("All threads have been stopped.");
std::unique_ptr<utils::serial> to_ar;
fs::pending_file file; fs::pending_file file;
std::string path; std::string path;
@ -3048,13 +3068,15 @@ void Emulator::Kill(bool allow_autoexit, bool savestate, savestate_stage* save_s
break; break;
} }
to_ar = std::make_unique<utils::serial>(); to_ar = stx::make_single<utils::serial>();
to_ar->m_file_handler = make_compressed_serialization_file_handler(file.file); to_ar.load()->m_file_handler = make_compressed_serialization_file_handler(file.file);
signal_system_cache_can_stay(); signal_system_cache_can_stay();
break; break;
} }
*join_ended = true;
if (savestate) if (savestate)
{ {
// Savestate thread // Savestate thread
@ -3065,7 +3087,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); return fmt::format("Emu State Capture Thread: '%s'", g_tls_serialize_name);
}; };
auto& ar = *to_ar; auto& ar = *to_ar.load();
read_used_savestate_versions(); // Reset version data read_used_savestate_versions(); // Reset version data
USING_SERIALIZATION_VERSION(global_version); USING_SERIALIZATION_VERSION(global_version);
@ -3196,6 +3218,10 @@ void Emulator::Kill(bool allow_autoexit, bool savestate, savestate_stage* save_s
ar(std::array<u8, 32>{}); // Reserved for future use ar(std::array<u8, 32>{}); // Reserved for future use
ar(timestamp); ar(timestamp);
// Final file write, the file is ready to be committed
ar.seek_end();
ar.m_file_handler->finalize(ar);
}); });
// Join it // Join it
@ -3209,15 +3235,9 @@ void Emulator::Kill(bool allow_autoexit, bool savestate, savestate_stage* save_s
} }
} }
stop_watchdog = thread_state::aborting;
if (savestate) if (savestate)
{ {
auto& ar = *to_ar; auto& ar = *to_ar.load();
// Final file write, the file is ready to be committed
ar.seek_end();
ar.m_file_handler->finalize(ar);
fs::stat_t file_stat{}; fs::stat_t file_stat{};
@ -3257,6 +3277,8 @@ void Emulator::Kill(bool allow_autoexit, bool savestate, savestate_stage* save_s
ar.set_reading_state(); ar.set_reading_state();
} }
stop_watchdog = thread_state::aborting;
// Log additional debug information - do not do it on the main thread due to the concern of halting UI events // Log additional debug information - do not do it on the main thread due to the concern of halting UI events
if (g_tty && sys_log.notice) if (g_tty && sys_log.notice)

View File

@ -2,6 +2,7 @@
#include "util/types.hpp" #include "util/types.hpp"
#include "util/atomic.hpp" #include "util/atomic.hpp"
#include "util/shared_ptr.hpp"
#include "Utilities/bit_set.h" #include "Utilities/bit_set.h"
#include "config_mode.h" #include "config_mode.h"
#include "games_config.h" #include "games_config.h"
@ -63,6 +64,7 @@ struct EmuCallbacks
std::function<void()> on_ready; std::function<void()> on_ready;
std::function<bool()> on_missing_fw; 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>>, int)> on_emulation_stop_no_response;
std::function<void(std::shared_ptr<atomic_t<bool>>, stx::shared_ptr<utils::serial>)> on_save_state_progress;
std::function<void(bool enabled)> enable_disc_eject; std::function<void(bool enabled)> enable_disc_eject;
std::function<void(bool enabled)> enable_disc_insert; 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 std::function<bool(bool, std::function<void()>)> try_to_quit; // (force_quit, on_exit) Try to close RPCS3

View File

@ -148,6 +148,10 @@ void headless_application::InitializeCallbacks()
} }
}; };
callbacks.on_save_state_progress = [](std::shared_ptr<atomic_t<bool>>, stx::shared_ptr<utils::serial>)
{
};
callbacks.enable_disc_eject = [](bool) {}; callbacks.enable_disc_eject = [](bool) {};
callbacks.enable_disc_insert = [](bool) {}; callbacks.enable_disc_insert = [](bool) {};

View File

@ -28,6 +28,7 @@
#include "recvmessage_dialog_frame.h" #include "recvmessage_dialog_frame.h"
#include "sendmessage_dialog_frame.h" #include "sendmessage_dialog_frame.h"
#include "stylesheets.h" #include "stylesheets.h"
#include "progress_dialog.h"
#include <QScreen> #include <QScreen>
#include <QFontDatabase> #include <QFontDatabase>
@ -665,6 +666,45 @@ void gui_application::InitializeCallbacks()
}); });
}; };
callbacks.on_save_state_progress = [this](std::shared_ptr<atomic_t<bool>> closed_successfully, stx::shared_ptr<utils::serial> ar_ptr)
{
Emu.CallFromMainThread([this, closed_successfully, ar_ptr]
{
const auto half_seconds = std::make_shared<int>(1);
progress_dialog* pdlg = new progress_dialog(tr("Creating Save-State / Do Not Close RPCS3"), tr("Please wait..."), tr("Hide Progress"), 0, 100, true, m_main_window);
pdlg->setAutoReset(false);
pdlg->setAutoClose(true);
pdlg->show();
QString text_base = tr("Waiting for %0 second(s), %1 written");
pdlg->setLabelText(text_base.arg(0).arg("0B"));
pdlg->setAttribute(Qt::WA_DeleteOnClose);
QTimer* update_timer = new QTimer(pdlg);
connect(update_timer, &QTimer::timeout, [pdlg, ar_ptr, half_seconds, text_base, closed_successfully]()
{
*half_seconds += 1;
const usz bytes_written = atomic_storage<usz>::load(ar_ptr->pos);
pdlg->setLabelText(text_base.arg(*half_seconds / 2).arg(gui::utils::format_byte_size(bytes_written)));
// 300MB -> 50%, 600MB -> 75%, 1200MB -> 87.5% etc
pdlg->setValue(std::clamp(static_cast<int>(100. - 100. / std::pow(2., std::fmax(0.01, bytes_written * 1. / (300 * 1024 * 1024)))), 2, 100));
if (*closed_successfully)
{
pdlg->reject();
}
});
pdlg->open();
update_timer->start(500);
});
};
callbacks.add_breakpoint = [this](u32 addr) callbacks.add_breakpoint = [this](u32 addr)
{ {
Emu.BlockingCallFromMainThread([this, addr]() Emu.BlockingCallFromMainThread([this, addr]()

View File

@ -83,11 +83,7 @@ public:
usz m_max_data = umax; usz m_max_data = umax;
std::unique_ptr<serialization_file_handler> m_file_handler; std::unique_ptr<serialization_file_handler> m_file_handler;
serial(bool expect_little_data = false) noexcept serial() noexcept = default;
: m_expect_little_data(expect_little_data)
{
}
serial(const serial&) = delete; serial(const serial&) = delete;
serial& operator=(const serial&) = delete; serial& operator=(const serial&) = delete;
explicit serial(serial&&) noexcept = default; explicit serial(serial&&) noexcept = default;
@ -100,6 +96,11 @@ public:
return m_is_writing; return m_is_writing;
} }
void set_expect_little_data(bool value)
{
m_expect_little_data = value;
}
// Return true if small amounts of both input and output memory are expected (performance hint) // Return true if small amounts of both input and output memory are expected (performance hint)
bool expect_little_data() const bool expect_little_data() const
{ {