Merge pull request #9463 from liamwhite/manager-events

EmuThread: refactor
This commit is contained in:
liamwhite 2022-12-20 09:10:41 -05:00 committed by GitHub
commit 1b11e0f0d3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 65 additions and 173 deletions

View file

@ -183,26 +183,20 @@ struct System::Impl {
Initialize(system); Initialize(system);
} }
SystemResultStatus Run() { void Run() {
std::unique_lock<std::mutex> lk(suspend_guard); std::unique_lock<std::mutex> lk(suspend_guard);
status = SystemResultStatus::Success;
kernel.Suspend(false); kernel.Suspend(false);
core_timing.SyncPause(false); core_timing.SyncPause(false);
is_paused.store(false, std::memory_order_relaxed); is_paused.store(false, std::memory_order_relaxed);
return status;
} }
SystemResultStatus Pause() { void Pause() {
std::unique_lock<std::mutex> lk(suspend_guard); std::unique_lock<std::mutex> lk(suspend_guard);
status = SystemResultStatus::Success;
core_timing.SyncPause(true); core_timing.SyncPause(true);
kernel.Suspend(true); kernel.Suspend(true);
is_paused.store(true, std::memory_order_relaxed); is_paused.store(true, std::memory_order_relaxed);
return status;
} }
bool IsPaused() const { bool IsPaused() const {
@ -553,12 +547,12 @@ void System::Initialize() {
impl->Initialize(*this); impl->Initialize(*this);
} }
SystemResultStatus System::Run() { void System::Run() {
return impl->Run(); impl->Run();
} }
SystemResultStatus System::Pause() { void System::Pause() {
return impl->Pause(); impl->Pause();
} }
bool System::IsPaused() const { bool System::IsPaused() const {

View file

@ -152,13 +152,13 @@ public:
* Run the OS and Application * Run the OS and Application
* This function will start emulation and run the relevant devices * This function will start emulation and run the relevant devices
*/ */
[[nodiscard]] SystemResultStatus Run(); void Run();
/** /**
* Pause the OS and Application * Pause the OS and Application
* This function will pause emulation and stop the relevant devices * This function will pause emulation and stop the relevant devices
*/ */
[[nodiscard]] SystemResultStatus Pause(); void Pause();
/// Check if the core is currently paused. /// Check if the core is currently paused.
[[nodiscard]] bool IsPaused() const; [[nodiscard]] bool IsPaused() const;

View file

@ -46,30 +46,28 @@
static Core::Frontend::WindowSystemType GetWindowSystemType(); static Core::Frontend::WindowSystemType GetWindowSystemType();
EmuThread::EmuThread(Core::System& system_) : system{system_} {} EmuThread::EmuThread(Core::System& system) : m_system{system} {}
EmuThread::~EmuThread() = default; EmuThread::~EmuThread() = default;
void EmuThread::run() { void EmuThread::run() {
std::string name = "EmuControlThread"; const char* name = "EmuControlThread";
MicroProfileOnThreadCreate(name.c_str()); MicroProfileOnThreadCreate(name);
Common::SetCurrentThreadName(name.c_str()); Common::SetCurrentThreadName(name);
auto& gpu = system.GPU(); auto& gpu = m_system.GPU();
auto stop_token = stop_source.get_token(); auto stop_token = m_stop_source.get_token();
bool debugger_should_start = system.DebuggerEnabled();
system.RegisterHostThread(); m_system.RegisterHostThread();
// Main process has been loaded. Make the context current to this thread and begin GPU and CPU // Main process has been loaded. Make the context current to this thread and begin GPU and CPU
// execution. // execution.
gpu.ObtainContext(); gpu.ObtainContext();
emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0); emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0);
if (Settings::values.use_disk_shader_cache.GetValue()) { if (Settings::values.use_disk_shader_cache.GetValue()) {
system.Renderer().ReadRasterizer()->LoadDiskResources( m_system.Renderer().ReadRasterizer()->LoadDiskResources(
system.GetCurrentProcessProgramID(), stop_token, m_system.GetCurrentProcessProgramID(), stop_token,
[this](VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total) { [this](VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total) {
emit LoadProgress(stage, value, total); emit LoadProgress(stage, value, total);
}); });
@ -79,57 +77,35 @@ void EmuThread::run() {
gpu.ReleaseContext(); gpu.ReleaseContext();
gpu.Start(); gpu.Start();
system.GetCpuManager().OnGpuReady(); m_system.GetCpuManager().OnGpuReady();
m_system.RegisterExitCallback([this] { m_stop_source.request_stop(); });
system.RegisterExitCallback([this]() { if (m_system.DebuggerEnabled()) {
stop_source.request_stop(); m_system.InitializeDebugger();
SetRunning(false); }
});
// Holds whether the cpu was running during the last iteration,
// so that the DebugModeLeft signal can be emitted before the
// next execution step
bool was_active = false;
while (!stop_token.stop_requested()) { while (!stop_token.stop_requested()) {
if (running) { std::unique_lock lk{m_should_run_mutex};
if (was_active) { if (m_should_run) {
emit DebugModeLeft(); m_system.Run();
} m_is_running.store(true);
m_is_running.notify_all();
running_guard = true; Common::CondvarWait(m_should_run_cv, lk, stop_token, [&] { return !m_should_run; });
Core::SystemResultStatus result = system.Run();
if (result != Core::SystemResultStatus::Success) {
running_guard = false;
this->SetRunning(false);
emit ErrorThrown(result, system.GetStatusDetails());
}
if (debugger_should_start) {
system.InitializeDebugger();
debugger_should_start = false;
}
running_wait.Wait();
result = system.Pause();
if (result != Core::SystemResultStatus::Success) {
running_guard = false;
this->SetRunning(false);
emit ErrorThrown(result, system.GetStatusDetails());
}
running_guard = false;
if (!stop_token.stop_requested()) {
was_active = true;
emit DebugModeEntered();
}
} else { } else {
std::unique_lock lock{running_mutex}; m_system.Pause();
Common::CondvarWait(running_cv, lock, stop_token, [&] { return IsRunning(); }); m_is_running.store(false);
m_is_running.notify_all();
emit DebugModeEntered();
Common::CondvarWait(m_should_run_cv, lk, stop_token, [&] { return m_should_run; });
emit DebugModeLeft();
} }
} }
// Shutdown the main emulated process // Shutdown the main emulated process
system.ShutdownMainProcess(); m_system.DetachDebugger();
m_system.ShutdownMainProcess();
#if MICROPROFILE_ENABLED #if MICROPROFILE_ENABLED
MicroProfileOnThreadExit(); MicroProfileOnThreadExit();

View file

@ -47,7 +47,7 @@ class EmuThread final : public QThread {
Q_OBJECT Q_OBJECT
public: public:
explicit EmuThread(Core::System& system_); explicit EmuThread(Core::System& system);
~EmuThread() override; ~EmuThread() override;
/** /**
@ -57,30 +57,30 @@ public:
void run() override; void run() override;
/** /**
* Sets whether the emulation thread is running or not * Sets whether the emulation thread should run or not
* @param running_ Boolean value, set the emulation thread to running if true * @param should_run Boolean value, set the emulation thread to running if true
* @note This function is thread-safe
*/ */
void SetRunning(bool running_) { void SetRunning(bool should_run) {
std::unique_lock lock{running_mutex}; // TODO: Prevent other threads from modifying the state until we finish.
running = running_; {
lock.unlock(); // Notify the running thread to change state.
running_cv.notify_all(); std::unique_lock run_lk{m_should_run_mutex};
if (!running) { m_should_run = should_run;
running_wait.Set(); m_should_run_cv.notify_one();
/// Wait until effectively paused }
while (running_guard)
; // Wait until paused, if pausing.
if (!should_run) {
m_is_running.wait(true);
} }
} }
/** /**
* Check if the emulation thread is running or not * Check if the emulation thread is running or not
* @return True if the emulation thread is running, otherwise false * @return True if the emulation thread is running, otherwise false
* @note This function is thread-safe
*/ */
bool IsRunning() const { bool IsRunning() const {
return running; return m_is_running.load();
} }
/** /**
@ -88,18 +88,17 @@ public:
*/ */
void ForceStop() { void ForceStop() {
LOG_WARNING(Frontend, "Force stopping EmuThread"); LOG_WARNING(Frontend, "Force stopping EmuThread");
stop_source.request_stop(); m_stop_source.request_stop();
SetRunning(false);
} }
private: private:
bool running = false; Core::System& m_system;
std::stop_source stop_source;
std::mutex running_mutex; std::stop_source m_stop_source;
std::condition_variable_any running_cv; std::mutex m_should_run_mutex;
Common::Event running_wait{}; std::condition_variable_any m_should_run_cv;
std::atomic_bool running_guard{false}; std::atomic<bool> m_is_running{false};
Core::System& system; bool m_should_run{true};
signals: signals:
/** /**
@ -120,8 +119,6 @@ signals:
*/ */
void DebugModeLeft(); void DebugModeLeft();
void ErrorThrown(Core::SystemResultStatus, std::string);
void LoadProgress(VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total); void LoadProgress(VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total);
}; };

View file

@ -1498,7 +1498,7 @@ void GMainWindow::SetupSigInterrupts() {
void GMainWindow::HandleSigInterrupt(int sig) { void GMainWindow::HandleSigInterrupt(int sig) {
if (sig == SIGINT) { if (sig == SIGINT) {
exit(1); _exit(1);
} }
// Calling into Qt directly from a signal handler is not safe, // Calling into Qt directly from a signal handler is not safe,
@ -1794,15 +1794,16 @@ void GMainWindow::ShutdownGame() {
Settings::values.use_speed_limit.SetValue(true); Settings::values.use_speed_limit.SetValue(true);
system->SetShuttingDown(true); system->SetShuttingDown(true);
system->DetachDebugger();
discord_rpc->Pause(); discord_rpc->Pause();
RequestGameExit(); RequestGameExit();
emu_thread->disconnect();
emu_thread->SetRunning(true);
emit EmulationStopping(); emit EmulationStopping();
// Wait for emulation thread to complete and delete it // Wait for emulation thread to complete and delete it
if (!emu_thread->wait(5000)) { if (system->DebuggerEnabled() || !emu_thread->wait(5000)) {
emu_thread->ForceStop(); emu_thread->ForceStop();
emu_thread->wait(); emu_thread->wait();
} }
@ -2919,8 +2920,6 @@ void GMainWindow::OnStartGame() {
emu_thread->SetRunning(true); emu_thread->SetRunning(true);
connect(emu_thread.get(), &EmuThread::ErrorThrown, this, &GMainWindow::OnCoreError);
UpdateMenuState(); UpdateMenuState();
OnTasStateChanged(); OnTasStateChanged();
@ -3904,79 +3903,6 @@ void GMainWindow::OnMouseActivity() {
mouse_center_timer.stop(); mouse_center_timer.stop();
} }
void GMainWindow::OnCoreError(Core::SystemResultStatus result, std::string details) {
QMessageBox::StandardButton answer;
QString status_message;
const QString common_message =
tr("The game you are trying to load requires additional files from your Switch to be "
"dumped "
"before playing.<br/><br/>For more information on dumping these files, please see the "
"following wiki page: <a "
"href='https://yuzu-emu.org/wiki/"
"dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Dumping System "
"Archives and the Shared Fonts from a Switch Console</a>.<br/><br/>Would you like to "
"quit "
"back to the game list? Continuing emulation may result in crashes, corrupted save "
"data, or other bugs.");
switch (result) {
case Core::SystemResultStatus::ErrorSystemFiles: {
QString message;
if (details.empty()) {
message =
tr("yuzu was unable to locate a Switch system archive. %1").arg(common_message);
} else {
message = tr("yuzu was unable to locate a Switch system archive: %1. %2")
.arg(QString::fromStdString(details), common_message);
}
answer = QMessageBox::question(this, tr("System Archive Not Found"), message,
QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
status_message = tr("System Archive Missing");
break;
}
case Core::SystemResultStatus::ErrorSharedFont: {
const QString message =
tr("yuzu was unable to locate the Switch shared fonts. %1").arg(common_message);
answer = QMessageBox::question(this, tr("Shared Fonts Not Found"), message,
QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
status_message = tr("Shared Font Missing");
break;
}
default:
answer = QMessageBox::question(
this, tr("Fatal Error"),
tr("yuzu has encountered a fatal error, please see the log for more details. "
"For more information on accessing the log, please see the following page: "
"<a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>How "
"to "
"Upload the Log File</a>.<br/><br/>Would you like to quit back to the game "
"list? "
"Continuing emulation may result in crashes, corrupted save data, or other "
"bugs."),
QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
status_message = tr("Fatal Error encountered");
break;
}
if (answer == QMessageBox::Yes) {
if (emu_thread) {
ShutdownGame();
Settings::RestoreGlobalState(system->IsPoweredOn());
system->HIDCore().ReloadInputDevices();
UpdateStatusButtons();
}
} else {
// Only show the message if the game is still running.
if (emu_thread) {
emu_thread->SetRunning(true);
message_label->setText(status_message);
}
}
}
void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) { void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) {
if (behavior == ReinitializeKeyBehavior::Warning) { if (behavior == ReinitializeKeyBehavior::Warning) {
const auto res = QMessageBox::information( const auto res = QMessageBox::information(

View file

@ -332,7 +332,6 @@ private slots:
void ResetWindowSize900(); void ResetWindowSize900();
void ResetWindowSize1080(); void ResetWindowSize1080();
void OnCaptureScreenshot(); void OnCaptureScreenshot();
void OnCoreError(Core::SystemResultStatus, std::string);
void OnReinitializeKeys(ReinitializeKeyBehavior behavior); void OnReinitializeKeys(ReinitializeKeyBehavior behavior);
void OnLanguageChanged(const QString& locale); void OnLanguageChanged(const QString& locale);
void OnMouseActivity(); void OnMouseActivity();