summaryrefslogtreecommitdiff
path: root/Source/Core
diff options
context:
space:
mode:
authorJosJuice <josjuice@gmail.com>2025-10-28 21:18:47 +0100
committerGitHub <noreply@github.com>2025-10-28 21:18:47 +0100
commit3d764f7b42b860db834159bb2db12e88abdffbed (patch)
tree31875b1f53c28aa61fdfc8c89de7b374e10b7a16 /Source/Core
parent509584d3d3c8b7a66330d2bdb16e609e20f2dbbf (diff)
parentc69f868fff9bbbd3099abeb00a7a8da8dc930ecf (diff)
Merge pull request #14032 from jordan-woyak/spawn-gpu-thread
Core: Make EmuThread spawn a GPU thread and become the CPU thread in dual-core mode.
Diffstat (limited to 'Source/Core')
-rw-r--r--Source/Core/Core/Core.cpp147
-rw-r--r--Source/Core/Core/HW/CPU.cpp4
-rw-r--r--Source/Core/VideoCommon/AsyncRequests.cpp17
-rw-r--r--Source/Core/VideoCommon/AsyncRequests.h2
-rw-r--r--Source/Core/VideoCommon/Fifo.cpp6
5 files changed, 79 insertions, 97 deletions
diff --git a/Source/Core/Core/Core.cpp b/Source/Core/Core/Core.cpp
index 3292abbff9..991f2f09df 100644
--- a/Source/Core/Core/Core.cpp
+++ b/Source/Core/Core/Core.cpp
@@ -7,6 +7,7 @@
#include <atomic>
#include <cstring>
#include <functional>
+#include <future>
#include <mutex>
#include <optional>
#include <queue>
@@ -100,7 +101,6 @@ static bool s_wants_determinism;
static std::thread s_emu_thread;
static std::vector<StateChangedCallbackFunc> s_on_state_changed_callbacks;
-static std::thread s_cpu_thread;
static bool s_is_throttler_temp_disabled = false;
static bool s_frame_step = false;
static std::atomic<bool> s_stop_frame_step;
@@ -289,16 +289,6 @@ void Stop(Core::System& system) // - Hammertime!
// Stop the CPU
INFO_LOG_FMT(CONSOLE, "{}", StopMessage(true, "Stop CPU"));
system.GetCPU().Stop();
-
- if (system.IsDualCoreMode())
- {
- // FIFO processing should now exit so that EmuThread()
- // will continue concurrently with the rest of the commands
- // in this function. We no longer rely on Postmessage.
- INFO_LOG_FMT(CONSOLE, "{}", StopMessage(true, "Wait for Video Loop to exit ..."));
-
- system.GetFifo().ExitGpuLoop();
- }
}
void DeclareAsCPUThread()
@@ -348,8 +338,6 @@ static void CPUSetInitialExecutionState(bool force_paused = false)
static void CpuThread(Core::System& system, const std::optional<std::string>& savestate_path,
bool delete_savestate)
{
- DeclareAsCPUThread();
-
if (system.IsDualCoreMode())
Common::SetCurrentThreadName("CPU thread");
else
@@ -418,17 +406,16 @@ static void CpuThread(Core::System& system, const std::optional<std::string>& sa
if (GDBStub::IsActive())
{
+ INFO_LOG_FMT(CONSOLE, "{}", StopMessage(true, "Stopping GDB ..."));
GDBStub::Deinit();
+ INFO_LOG_FMT(CONSOLE, "{}", StopMessage(true, "GDB stopped."));
INFO_LOG_FMT(GDB_STUB, "Killed by CPU shutdown");
- return;
}
}
static void FifoPlayerThread(Core::System& system, const std::optional<std::string>& savestate_path,
bool delete_savestate)
{
- DeclareAsCPUThread();
-
if (system.IsDualCoreMode())
Common::SetCurrentThreadName("FIFO player thread");
else
@@ -459,6 +446,72 @@ static void FifoPlayerThread(Core::System& system, const std::optional<std::stri
}
}
+// Returns a RAII object for video backend initialization and deinitialization.
+// Returns nullptr on failure.
+[[nodiscard]] static auto GetInitializedVideoGuard(Core::System& system,
+ const WindowSystemInfo& wsi)
+{
+ using GuardType = Common::ScopeGuard<Common::MoveOnlyFunction<void()>>;
+ using ReturnType = std::unique_ptr<GuardType>;
+
+ const auto init_video = [&] {
+ DeclareAsGPUThread();
+
+ AsyncRequests::GetInstance()->SetPassthrough(!system.IsDualCoreMode());
+
+ // Must happen on the proper thread for some video backends, e.g. OpenGL.
+ return g_video_backend->Initialize(wsi);
+ };
+
+ const auto deinit_video = [] {
+ // Clear on screen messages that haven't expired
+ OSD::ClearMessages();
+
+ g_video_backend->Shutdown();
+ };
+
+ if (system.IsDualCoreMode())
+ {
+ std::promise<bool> init_from_thread;
+
+ // Spawn the GPU thread.
+ std::thread gpu_thread{[&] {
+ Common::SetCurrentThreadName("Video thread");
+
+ const bool is_init = init_video();
+ init_from_thread.set_value(is_init);
+
+ if (!is_init)
+ return;
+
+ system.GetFifo().RunGpuLoop();
+ INFO_LOG_FMT(CONSOLE, "{}", StopMessage(false, "Video Loop Ended"));
+
+ deinit_video();
+ }};
+
+ if (init_from_thread.get_future().get())
+ {
+ // Return a scope guard that signals the GPU thread to stop then joins it.
+ return std::make_unique<GuardType>([&, gpu_thread = std::move(gpu_thread)]() mutable {
+ INFO_LOG_FMT(CONSOLE, "{}", StopMessage(true, "Wait for Video Loop to exit ..."));
+ system.GetFifo().ExitGpuLoop();
+ gpu_thread.join();
+ INFO_LOG_FMT(CONSOLE, "{}", StopMessage(true, "GPU thread stopped."));
+ });
+ }
+
+ gpu_thread.join();
+ }
+ else // SingleCore mode
+ {
+ if (init_video())
+ return std::make_unique<GuardType>(deinit_video);
+ }
+
+ return ReturnType{};
+}
+
// Initialize and create emulation thread
// Call browser: Init():s_emu_thread().
// See the BootManager.cpp file description for a complete call schedule.
@@ -476,10 +529,9 @@ static void EmuThread(Core::System& system, std::unique_ptr<BootParameters> boot
Common::SetCurrentThreadName("Emuthread - Starting");
- DeclareAsGPUThread();
-
- // For a time this acts as the CPU thread...
+ // This will become the CPU thread.
DeclareAsCPUThread();
+
s_frame_step = false;
// If settings have changed since the previous run, notify callbacks.
@@ -550,17 +602,14 @@ static void EmuThread(Core::System& system, std::unique_ptr<BootParameters> boot
system.GetPowerPC().GetDebugInterface().Clear(guard);
}};
- if (!g_video_backend->Initialize(wsi))
+ // In single-core mode: This holds a video backend shutdown function.
+ // In dual-core mode: This holds a GPU thread stopping function (which does the backend shutdown).
+ const auto video_guard = GetInitializedVideoGuard(system, wsi);
+ if (!video_guard)
{
PanicAlertFmt("Failed to initialize video backend!");
return;
}
- Common::ScopeGuard video_guard{[] {
- // Clear on screen messages that haven't expired
- OSD::ClearMessages();
-
- g_video_backend->Shutdown();
- }};
if (cpu_info.HTT)
Config::SetBaseOrCurrent(Config::MAIN_DSP_THREAD, cpu_info.num_cores > 4);
@@ -583,12 +632,8 @@ static void EmuThread(Core::System& system, std::unique_ptr<BootParameters> boot
system.GetPowerPC().SetMode(PowerPC::CoreMode::Interpreter);
// Determine the CPU thread function
- void (*cpuThreadFunc)(Core::System& system, const std::optional<std::string>& savestate_path,
- bool delete_savestate);
- if (std::holds_alternative<BootParameters::DFF>(boot->parameters))
- cpuThreadFunc = FifoPlayerThread;
- else
- cpuThreadFunc = CpuThread;
+ const auto cpu_thread_func =
+ std::holds_alternative<BootParameters::DFF>(boot->parameters) ? FifoPlayerThread : CpuThread;
std::optional<DiscIO::Riivolution::SavegameRedirect> savegame_redirect = std::nullopt;
if (system.IsWii())
@@ -628,42 +673,8 @@ static void EmuThread(Core::System& system, std::unique_ptr<BootParameters> boot
UpdateTitle(system);
- // ENTER THE VIDEO THREAD LOOP
- if (system.IsDualCoreMode())
- {
- // This thread, after creating the EmuWindow, spawns a CPU
- // thread, and then takes over and becomes the video thread
- Common::SetCurrentThreadName("Video thread");
- UndeclareAsCPUThread();
- Common::FPU::LoadDefaultSIMDState();
-
- // Spawn the CPU thread. The CPU thread will signal the event that boot is complete.
- s_cpu_thread =
- std::thread(cpuThreadFunc, std::ref(system), std::ref(savestate_path), delete_savestate);
-
- // become the GPU thread
- system.GetFifo().RunGpuLoop();
-
- // We have now exited the Video Loop
- INFO_LOG_FMT(CONSOLE, "{}", StopMessage(false, "Video Loop Ended"));
-
- // Join with the CPU thread.
- s_cpu_thread.join();
- INFO_LOG_FMT(CONSOLE, "{}", StopMessage(true, "CPU thread stopped."));
-
- // Redeclare this thread as the CPU thread, so that the code running in the scope guards doesn't
- // think we're doing anything unsafe by doing stuff that could race with the CPU thread.
- DeclareAsCPUThread();
- }
- else // SingleCore mode
- {
- // Become the CPU thread
- cpuThreadFunc(system, savestate_path, delete_savestate);
- }
-
- INFO_LOG_FMT(CONSOLE, "{}", StopMessage(true, "Stopping GDB ..."));
- GDBStub::Deinit();
- INFO_LOG_FMT(CONSOLE, "{}", StopMessage(true, "GDB stopped."));
+ // Become the CPU thread.
+ cpu_thread_func(system, savestate_path, delete_savestate);
}
// Set or get the running state
diff --git a/Source/Core/Core/HW/CPU.cpp b/Source/Core/Core/HW/CPU.cpp
index 82b0cb9c4b..2f07e384b1 100644
--- a/Source/Core/Core/HW/CPU.cpp
+++ b/Source/Core/Core/HW/CPU.cpp
@@ -109,10 +109,6 @@ void CPUManager::Run()
{
auto& power_pc = m_system.GetPowerPC();
- // Updating the host CPU's rounding mode must be done on the CPU thread.
- // We can't rely on PowerPC::Init doing it, since it's called from EmuThread.
- PowerPC::RoundingModeUpdated(power_pc.GetPPCState());
-
// Start a separate time tracker thread
std::thread timing;
if (Config::Get(Config::MAIN_TIME_TRACKING))
diff --git a/Source/Core/VideoCommon/AsyncRequests.cpp b/Source/Core/VideoCommon/AsyncRequests.cpp
index 0469d3ebb9..6671f93293 100644
--- a/Source/Core/VideoCommon/AsyncRequests.cpp
+++ b/Source/Core/VideoCommon/AsyncRequests.cpp
@@ -42,9 +42,6 @@ void AsyncRequests::QueueEvent(Event&& event)
{
m_empty.Clear();
- if (!m_enable)
- return;
-
m_queue.push(std::move(event));
auto& system = Core::System::GetInstance();
@@ -57,20 +54,6 @@ void AsyncRequests::WaitForEmptyQueue()
m_cond.wait(lock, [this] { return m_queue.empty(); });
}
-void AsyncRequests::SetEnable(bool enable)
-{
- std::unique_lock<std::mutex> lock(m_mutex);
- m_enable = enable;
-
- if (!enable)
- {
- // flush the queue on disabling
- while (!m_queue.empty())
- m_queue.pop();
- m_cond.notify_one();
- }
-}
-
void AsyncRequests::SetPassthrough(bool enable)
{
std::unique_lock<std::mutex> lock(m_mutex);
diff --git a/Source/Core/VideoCommon/AsyncRequests.h b/Source/Core/VideoCommon/AsyncRequests.h
index 1c72b55b85..7b8cdd036b 100644
--- a/Source/Core/VideoCommon/AsyncRequests.h
+++ b/Source/Core/VideoCommon/AsyncRequests.h
@@ -26,7 +26,6 @@ public:
PullEventsInternal();
}
void WaitForEmptyQueue();
- void SetEnable(bool enable);
void SetPassthrough(bool enable);
template <typename F>
@@ -74,6 +73,5 @@ private:
std::mutex m_mutex;
std::condition_variable m_cond;
- bool m_enable = false;
bool m_passthrough = true;
};
diff --git a/Source/Core/VideoCommon/Fifo.cpp b/Source/Core/VideoCommon/Fifo.cpp
index 513b655408..0e7bb4c341 100644
--- a/Source/Core/VideoCommon/Fifo.cpp
+++ b/Source/Core/VideoCommon/Fifo.cpp
@@ -287,9 +287,6 @@ void FifoManager::ResetVideoBuffer()
// Purpose: Keep the Core HW updated about the CPU-GPU distance
void FifoManager::RunGpuLoop()
{
- AsyncRequests::GetInstance()->SetEnable(true);
- AsyncRequests::GetInstance()->SetPassthrough(false);
-
m_gpu_mainloop.Run(
[this] {
// Run events from the CPU thread.
@@ -391,9 +388,6 @@ void FifoManager::RunGpuLoop()
}
},
100);
-
- AsyncRequests::GetInstance()->SetEnable(false);
- AsyncRequests::GetInstance()->SetPassthrough(true);
}
void FifoManager::FlushGpu()