diff --git a/Source/Core/Core/Core.cpp b/Source/Core/Core/Core.cpp index 1687c9900f..de3ac7f009 100644 --- a/Source/Core/Core/Core.cpp +++ b/Source/Core/Core/Core.cpp @@ -127,6 +127,7 @@ static std::queue s_host_jobs_queue; static Common::Event s_cpu_thread_job_finished; static thread_local bool tls_is_cpu_thread = false; +static thread_local bool tls_is_gpu_thread = false; static void EmuThread(std::unique_ptr boot, WindowSystemInfo wsi); @@ -203,14 +204,7 @@ bool IsCPUThread() bool IsGPUThread() { - if (Core::System::GetInstance().IsDualCoreMode()) - { - return (s_emu_thread.joinable() && (s_emu_thread.get_id() == std::this_thread::get_id())); - } - else - { - return IsCPUThread(); - } + return tls_is_gpu_thread; } bool WantsDeterminism() @@ -313,6 +307,16 @@ void UndeclareAsCPUThread() tls_is_cpu_thread = false; } +void DeclareAsGPUThread() +{ + tls_is_gpu_thread = true; +} + +void UndeclareAsGPUThread() +{ + tls_is_gpu_thread = false; +} + // For the CPU Thread only. static void CPUSetInitialExecutionState(bool force_paused = false) { @@ -459,6 +463,8 @@ static void EmuThread(std::unique_ptr boot, WindowSystemInfo wsi Common::SetCurrentThreadName("Emuthread - Starting"); + DeclareAsGPUThread(); + // For a time this acts as the CPU thread... DeclareAsCPUThread(); s_frame_step = false; diff --git a/Source/Core/Core/Core.h b/Source/Core/Core/Core.h index 52042b6409..1e6e240682 100644 --- a/Source/Core/Core/Core.h +++ b/Source/Core/Core/Core.h @@ -98,6 +98,8 @@ void Shutdown(); void DeclareAsCPUThread(); void UndeclareAsCPUThread(); +void DeclareAsGPUThread(); +void UndeclareAsGPUThread(); std::string StopMessage(bool main_thread, std::string_view message); diff --git a/Source/Core/DolphinQt/Host.cpp b/Source/Core/DolphinQt/Host.cpp index bc9d6103b3..9690c1bc5b 100644 --- a/Source/Core/DolphinQt/Host.cpp +++ b/Source/Core/DolphinQt/Host.cpp @@ -3,6 +3,8 @@ #include "DolphinQt/Host.h" +#include + #include #include #include @@ -85,6 +87,19 @@ void Host::SetMainWindowHandle(void* handle) m_main_window_handle = handle; } +static void RunWithGPUThreadInactive(std::function f) +{ + // If we are the GPU thread in dual core mode, we cannot safely call + // RunAsCPUThread, since RunAsCPUThread will need to pause the GPU thread + // and the GPU thread is waiting for us to finish. Fortunately, if we know + // that the GPU thread is waiting for us, we can just run f directly. + + if (Core::IsGPUThread()) + f(); + else + Core::RunAsCPUThread(std::move(f)); +} + bool Host::GetRenderFocus() { #ifdef _WIN32 @@ -107,10 +122,12 @@ void Host::SetRenderFocus(bool focus) { m_render_focus = focus; if (g_renderer && m_render_fullscreen && g_ActiveConfig.ExclusiveFullscreenEnabled()) - Core::RunAsCPUThread([focus] { + { + RunWithGPUThreadInactive([focus] { if (!Config::Get(Config::MAIN_RENDER_TO_MAIN)) g_renderer->SetFullscreen(focus); }); + } } void Host::SetRenderFullFocus(bool focus) @@ -138,7 +155,9 @@ void Host::SetRenderFullscreen(bool fullscreen) if (g_renderer && g_renderer->IsFullscreen() != fullscreen && g_ActiveConfig.ExclusiveFullscreenEnabled()) - Core::RunAsCPUThread([fullscreen] { g_renderer->SetFullscreen(fullscreen); }); + { + RunWithGPUThreadInactive([fullscreen] { g_renderer->SetFullscreen(fullscreen); }); + } } void Host::ResizeSurface(int new_width, int new_height) diff --git a/Source/Core/DolphinQt/Main.cpp b/Source/Core/DolphinQt/Main.cpp index 507765a143..8ffd52dbe5 100644 --- a/Source/Core/DolphinQt/Main.cpp +++ b/Source/Core/DolphinQt/Main.cpp @@ -41,20 +41,31 @@ static bool QtMsgAlertHandler(const char* caption, const char* text, bool yes_no Common::MsgType style) { const bool called_from_cpu_thread = Core::IsCPUThread(); + const bool called_from_gpu_thread = Core::IsGPUThread(); std::optional r = RunOnObject(QApplication::instance(), [&] { - Common::ScopeGuard scope_guard(&Core::UndeclareAsCPUThread); + Common::ScopeGuard cpu_scope_guard(&Core::UndeclareAsCPUThread); + Common::ScopeGuard gpu_scope_guard(&Core::UndeclareAsGPUThread); + + if (!called_from_cpu_thread) + cpu_scope_guard.Dismiss(); + if (!called_from_gpu_thread) + gpu_scope_guard.Dismiss(); + if (called_from_cpu_thread) { - // Temporarily declare this as the CPU thread to avoid getting a deadlock if any DolphinQt - // code calls RunAsCPUThread while the CPU thread is blocked on this function returning. - // Notably, if the panic alert steals focus from RenderWidget, Host::SetRenderFocus gets - // called, which can attempt to use RunAsCPUThread to get us out of exclusive fullscreen. + // If the panic alert that we are about to create steals the focus from RenderWidget, + // Host::SetRenderFocus gets called, which can attempt to use RunAsCPUThread to get us out + // of exclusive fullscreen. If we don't declare ourselves as the CPU thread, RunAsCPUThread + // calls PauseAndLock, which causes a deadlock if the CPU thread is waiting on us returning. Core::DeclareAsCPUThread(); } - else + if (called_from_gpu_thread) { - scope_guard.Dismiss(); + // We also need to avoid getting a deadlock when the GPU thread is waiting on us returning. + // Declaring ourselves as the GPU thread does not alter the behavior of RunAsCPUThread or + // PauseAndLock, but it does make Host::SetRenderFocus not call RunAsCPUThread. + Core::DeclareAsGPUThread(); } ModalMessageBox message_box(QApplication::activeWindow(), Qt::ApplicationModal); diff --git a/Source/Core/DolphinQt/RenderWidget.cpp b/Source/Core/DolphinQt/RenderWidget.cpp index 2430b7a415..6c34f7ebc6 100644 --- a/Source/Core/DolphinQt/RenderWidget.cpp +++ b/Source/Core/DolphinQt/RenderWidget.cpp @@ -421,10 +421,10 @@ bool RenderWidget::event(QEvent* event) if (Config::Get(Config::MAIN_PAUSE_ON_FOCUS_LOST) && Core::GetState() == Core::State::Running) { - // If we are declared as the CPU thread, it means that the real CPU thread is waiting - // for us to finish showing a panic alert (with that panic alert likely being the cause - // of this event), so trying to pause the real CPU thread would cause a deadlock - if (!Core::IsCPUThread()) + // If we are declared as the CPU or GPU thread, it means that the real CPU or GPU thread + // is waiting for us to finish showing a panic alert (with that panic alert likely being + // the cause of this event), so trying to pause the core would cause a deadlock + if (!Core::IsCPUThread() && !Core::IsGPUThread()) Core::SetState(Core::State::Paused); }