forked from suyu/suyu
EmuThread: refactor
This commit is contained in:
parent
5da72a891f
commit
d3123079e8
6 changed files with 64 additions and 172 deletions
|
@ -183,26 +183,20 @@ struct System::Impl {
|
|||
Initialize(system);
|
||||
}
|
||||
|
||||
SystemResultStatus Run() {
|
||||
void Run() {
|
||||
std::unique_lock<std::mutex> lk(suspend_guard);
|
||||
status = SystemResultStatus::Success;
|
||||
|
||||
kernel.Suspend(false);
|
||||
core_timing.SyncPause(false);
|
||||
is_paused.store(false, std::memory_order_relaxed);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
SystemResultStatus Pause() {
|
||||
void Pause() {
|
||||
std::unique_lock<std::mutex> lk(suspend_guard);
|
||||
status = SystemResultStatus::Success;
|
||||
|
||||
core_timing.SyncPause(true);
|
||||
kernel.Suspend(true);
|
||||
is_paused.store(true, std::memory_order_relaxed);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
bool IsPaused() const {
|
||||
|
@ -553,12 +547,12 @@ void System::Initialize() {
|
|||
impl->Initialize(*this);
|
||||
}
|
||||
|
||||
SystemResultStatus System::Run() {
|
||||
return impl->Run();
|
||||
void System::Run() {
|
||||
impl->Run();
|
||||
}
|
||||
|
||||
SystemResultStatus System::Pause() {
|
||||
return impl->Pause();
|
||||
void System::Pause() {
|
||||
impl->Pause();
|
||||
}
|
||||
|
||||
bool System::IsPaused() const {
|
||||
|
|
|
@ -152,13 +152,13 @@ public:
|
|||
* Run the OS and Application
|
||||
* This function will start emulation and run the relevant devices
|
||||
*/
|
||||
[[nodiscard]] SystemResultStatus Run();
|
||||
void Run();
|
||||
|
||||
/**
|
||||
* Pause the OS and Application
|
||||
* This function will pause emulation and stop the relevant devices
|
||||
*/
|
||||
[[nodiscard]] SystemResultStatus Pause();
|
||||
void Pause();
|
||||
|
||||
/// Check if the core is currently paused.
|
||||
[[nodiscard]] bool IsPaused() const;
|
||||
|
|
|
@ -46,30 +46,28 @@
|
|||
|
||||
static Core::Frontend::WindowSystemType GetWindowSystemType();
|
||||
|
||||
EmuThread::EmuThread(Core::System& system_) : system{system_} {}
|
||||
EmuThread::EmuThread(Core::System& system) : m_system{system} {}
|
||||
|
||||
EmuThread::~EmuThread() = default;
|
||||
|
||||
void EmuThread::run() {
|
||||
std::string name = "EmuControlThread";
|
||||
MicroProfileOnThreadCreate(name.c_str());
|
||||
Common::SetCurrentThreadName(name.c_str());
|
||||
const char* name = "EmuControlThread";
|
||||
MicroProfileOnThreadCreate(name);
|
||||
Common::SetCurrentThreadName(name);
|
||||
|
||||
auto& gpu = system.GPU();
|
||||
auto stop_token = stop_source.get_token();
|
||||
bool debugger_should_start = system.DebuggerEnabled();
|
||||
auto& gpu = m_system.GPU();
|
||||
auto stop_token = m_stop_source.get_token();
|
||||
|
||||
system.RegisterHostThread();
|
||||
m_system.RegisterHostThread();
|
||||
|
||||
// Main process has been loaded. Make the context current to this thread and begin GPU and CPU
|
||||
// execution.
|
||||
gpu.ObtainContext();
|
||||
|
||||
emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0);
|
||||
|
||||
if (Settings::values.use_disk_shader_cache.GetValue()) {
|
||||
system.Renderer().ReadRasterizer()->LoadDiskResources(
|
||||
system.GetCurrentProcessProgramID(), stop_token,
|
||||
m_system.Renderer().ReadRasterizer()->LoadDiskResources(
|
||||
m_system.GetCurrentProcessProgramID(), stop_token,
|
||||
[this](VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total) {
|
||||
emit LoadProgress(stage, value, total);
|
||||
});
|
||||
|
@ -79,57 +77,35 @@ void EmuThread::run() {
|
|||
gpu.ReleaseContext();
|
||||
gpu.Start();
|
||||
|
||||
system.GetCpuManager().OnGpuReady();
|
||||
m_system.GetCpuManager().OnGpuReady();
|
||||
m_system.RegisterExitCallback([this] { m_stop_source.request_stop(); });
|
||||
|
||||
system.RegisterExitCallback([this]() {
|
||||
stop_source.request_stop();
|
||||
SetRunning(false);
|
||||
});
|
||||
if (m_system.DebuggerEnabled()) {
|
||||
m_system.InitializeDebugger();
|
||||
}
|
||||
|
||||
// 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()) {
|
||||
if (running) {
|
||||
if (was_active) {
|
||||
emit DebugModeLeft();
|
||||
}
|
||||
std::unique_lock lk{m_should_run_mutex};
|
||||
if (m_should_run) {
|
||||
m_system.Run();
|
||||
m_is_running.store(true);
|
||||
m_is_running.notify_all();
|
||||
|
||||
running_guard = true;
|
||||
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();
|
||||
}
|
||||
Common::CondvarWait(m_should_run_cv, lk, stop_token, [&] { return !m_should_run; });
|
||||
} else {
|
||||
std::unique_lock lock{running_mutex};
|
||||
Common::CondvarWait(running_cv, lock, stop_token, [&] { return IsRunning(); });
|
||||
m_system.Pause();
|
||||
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
|
||||
system.ShutdownMainProcess();
|
||||
m_system.DetachDebugger();
|
||||
m_system.ShutdownMainProcess();
|
||||
|
||||
#if MICROPROFILE_ENABLED
|
||||
MicroProfileOnThreadExit();
|
||||
|
|
|
@ -47,7 +47,7 @@ class EmuThread final : public QThread {
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit EmuThread(Core::System& system_);
|
||||
explicit EmuThread(Core::System& system);
|
||||
~EmuThread() override;
|
||||
|
||||
/**
|
||||
|
@ -57,30 +57,30 @@ public:
|
|||
void run() override;
|
||||
|
||||
/**
|
||||
* Sets whether the emulation thread is running or not
|
||||
* @param running_ Boolean value, set the emulation thread to running if true
|
||||
* @note This function is thread-safe
|
||||
* Sets whether the emulation thread should run or not
|
||||
* @param should_run Boolean value, set the emulation thread to running if true
|
||||
*/
|
||||
void SetRunning(bool running_) {
|
||||
std::unique_lock lock{running_mutex};
|
||||
running = running_;
|
||||
lock.unlock();
|
||||
running_cv.notify_all();
|
||||
if (!running) {
|
||||
running_wait.Set();
|
||||
/// Wait until effectively paused
|
||||
while (running_guard)
|
||||
;
|
||||
void SetRunning(bool should_run) {
|
||||
// TODO: Prevent other threads from modifying the state until we finish.
|
||||
{
|
||||
// Notify the running thread to change state.
|
||||
std::unique_lock run_lk{m_should_run_mutex};
|
||||
m_should_run = should_run;
|
||||
m_should_run_cv.notify_one();
|
||||
}
|
||||
|
||||
// Wait until paused, if pausing.
|
||||
if (!should_run) {
|
||||
m_is_running.wait(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the emulation thread is running or not
|
||||
* @return True if the emulation thread is running, otherwise false
|
||||
* @note This function is thread-safe
|
||||
*/
|
||||
bool IsRunning() const {
|
||||
return running;
|
||||
return m_is_running.load();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -88,18 +88,17 @@ public:
|
|||
*/
|
||||
void ForceStop() {
|
||||
LOG_WARNING(Frontend, "Force stopping EmuThread");
|
||||
stop_source.request_stop();
|
||||
SetRunning(false);
|
||||
m_stop_source.request_stop();
|
||||
}
|
||||
|
||||
private:
|
||||
bool running = false;
|
||||
std::stop_source stop_source;
|
||||
std::mutex running_mutex;
|
||||
std::condition_variable_any running_cv;
|
||||
Common::Event running_wait{};
|
||||
std::atomic_bool running_guard{false};
|
||||
Core::System& system;
|
||||
Core::System& m_system;
|
||||
|
||||
std::stop_source m_stop_source;
|
||||
std::mutex m_should_run_mutex;
|
||||
std::condition_variable_any m_should_run_cv;
|
||||
std::atomic<bool> m_is_running{false};
|
||||
bool m_should_run{true};
|
||||
|
||||
signals:
|
||||
/**
|
||||
|
@ -120,8 +119,6 @@ signals:
|
|||
*/
|
||||
void DebugModeLeft();
|
||||
|
||||
void ErrorThrown(Core::SystemResultStatus, std::string);
|
||||
|
||||
void LoadProgress(VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total);
|
||||
};
|
||||
|
||||
|
|
|
@ -1794,15 +1794,16 @@ void GMainWindow::ShutdownGame() {
|
|||
Settings::values.use_speed_limit.SetValue(true);
|
||||
|
||||
system->SetShuttingDown(true);
|
||||
system->DetachDebugger();
|
||||
discord_rpc->Pause();
|
||||
|
||||
RequestGameExit();
|
||||
emu_thread->disconnect();
|
||||
emu_thread->SetRunning(true);
|
||||
|
||||
emit EmulationStopping();
|
||||
|
||||
// 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->wait();
|
||||
}
|
||||
|
@ -2916,8 +2917,6 @@ void GMainWindow::OnStartGame() {
|
|||
|
||||
emu_thread->SetRunning(true);
|
||||
|
||||
connect(emu_thread.get(), &EmuThread::ErrorThrown, this, &GMainWindow::OnCoreError);
|
||||
|
||||
UpdateMenuState();
|
||||
OnTasStateChanged();
|
||||
|
||||
|
@ -3901,79 +3900,6 @@ void GMainWindow::OnMouseActivity() {
|
|||
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) {
|
||||
if (behavior == ReinitializeKeyBehavior::Warning) {
|
||||
const auto res = QMessageBox::information(
|
||||
|
|
|
@ -332,7 +332,6 @@ private slots:
|
|||
void ResetWindowSize900();
|
||||
void ResetWindowSize1080();
|
||||
void OnCaptureScreenshot();
|
||||
void OnCoreError(Core::SystemResultStatus, std::string);
|
||||
void OnReinitializeKeys(ReinitializeKeyBehavior behavior);
|
||||
void OnLanguageChanged(const QString& locale);
|
||||
void OnMouseActivity();
|
||||
|
|
Loading…
Reference in a new issue