mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-12-16 04:09:39 +00:00
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.
This commit is contained in:
commit
3d764f7b42
@ -7,6 +7,7 @@
|
|||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <future>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <queue>
|
#include <queue>
|
||||||
@ -100,7 +101,6 @@ static bool s_wants_determinism;
|
|||||||
static std::thread s_emu_thread;
|
static std::thread s_emu_thread;
|
||||||
static std::vector<StateChangedCallbackFunc> s_on_state_changed_callbacks;
|
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_is_throttler_temp_disabled = false;
|
||||||
static bool s_frame_step = false;
|
static bool s_frame_step = false;
|
||||||
static std::atomic<bool> s_stop_frame_step;
|
static std::atomic<bool> s_stop_frame_step;
|
||||||
@ -289,16 +289,6 @@ void Stop(Core::System& system) // - Hammertime!
|
|||||||
// Stop the CPU
|
// Stop the CPU
|
||||||
INFO_LOG_FMT(CONSOLE, "{}", StopMessage(true, "Stop CPU"));
|
INFO_LOG_FMT(CONSOLE, "{}", StopMessage(true, "Stop CPU"));
|
||||||
system.GetCPU().Stop();
|
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()
|
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,
|
static void CpuThread(Core::System& system, const std::optional<std::string>& savestate_path,
|
||||||
bool delete_savestate)
|
bool delete_savestate)
|
||||||
{
|
{
|
||||||
DeclareAsCPUThread();
|
|
||||||
|
|
||||||
if (system.IsDualCoreMode())
|
if (system.IsDualCoreMode())
|
||||||
Common::SetCurrentThreadName("CPU thread");
|
Common::SetCurrentThreadName("CPU thread");
|
||||||
else
|
else
|
||||||
@ -418,17 +406,16 @@ static void CpuThread(Core::System& system, const std::optional<std::string>& sa
|
|||||||
|
|
||||||
if (GDBStub::IsActive())
|
if (GDBStub::IsActive())
|
||||||
{
|
{
|
||||||
|
INFO_LOG_FMT(CONSOLE, "{}", StopMessage(true, "Stopping GDB ..."));
|
||||||
GDBStub::Deinit();
|
GDBStub::Deinit();
|
||||||
|
INFO_LOG_FMT(CONSOLE, "{}", StopMessage(true, "GDB stopped."));
|
||||||
INFO_LOG_FMT(GDB_STUB, "Killed by CPU shutdown");
|
INFO_LOG_FMT(GDB_STUB, "Killed by CPU shutdown");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void FifoPlayerThread(Core::System& system, const std::optional<std::string>& savestate_path,
|
static void FifoPlayerThread(Core::System& system, const std::optional<std::string>& savestate_path,
|
||||||
bool delete_savestate)
|
bool delete_savestate)
|
||||||
{
|
{
|
||||||
DeclareAsCPUThread();
|
|
||||||
|
|
||||||
if (system.IsDualCoreMode())
|
if (system.IsDualCoreMode())
|
||||||
Common::SetCurrentThreadName("FIFO player thread");
|
Common::SetCurrentThreadName("FIFO player thread");
|
||||||
else
|
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
|
// Initialize and create emulation thread
|
||||||
// Call browser: Init():s_emu_thread().
|
// Call browser: Init():s_emu_thread().
|
||||||
// See the BootManager.cpp file description for a complete call schedule.
|
// 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");
|
Common::SetCurrentThreadName("Emuthread - Starting");
|
||||||
|
|
||||||
DeclareAsGPUThread();
|
// This will become the CPU thread.
|
||||||
|
|
||||||
// For a time this acts as the CPU thread...
|
|
||||||
DeclareAsCPUThread();
|
DeclareAsCPUThread();
|
||||||
|
|
||||||
s_frame_step = false;
|
s_frame_step = false;
|
||||||
|
|
||||||
// If settings have changed since the previous run, notify callbacks.
|
// 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);
|
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!");
|
PanicAlertFmt("Failed to initialize video backend!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Common::ScopeGuard video_guard{[] {
|
|
||||||
// Clear on screen messages that haven't expired
|
|
||||||
OSD::ClearMessages();
|
|
||||||
|
|
||||||
g_video_backend->Shutdown();
|
|
||||||
}};
|
|
||||||
|
|
||||||
if (cpu_info.HTT)
|
if (cpu_info.HTT)
|
||||||
Config::SetBaseOrCurrent(Config::MAIN_DSP_THREAD, cpu_info.num_cores > 4);
|
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);
|
system.GetPowerPC().SetMode(PowerPC::CoreMode::Interpreter);
|
||||||
|
|
||||||
// Determine the CPU thread function
|
// Determine the CPU thread function
|
||||||
void (*cpuThreadFunc)(Core::System& system, const std::optional<std::string>& savestate_path,
|
const auto cpu_thread_func =
|
||||||
bool delete_savestate);
|
std::holds_alternative<BootParameters::DFF>(boot->parameters) ? FifoPlayerThread : CpuThread;
|
||||||
if (std::holds_alternative<BootParameters::DFF>(boot->parameters))
|
|
||||||
cpuThreadFunc = FifoPlayerThread;
|
|
||||||
else
|
|
||||||
cpuThreadFunc = CpuThread;
|
|
||||||
|
|
||||||
std::optional<DiscIO::Riivolution::SavegameRedirect> savegame_redirect = std::nullopt;
|
std::optional<DiscIO::Riivolution::SavegameRedirect> savegame_redirect = std::nullopt;
|
||||||
if (system.IsWii())
|
if (system.IsWii())
|
||||||
@ -628,42 +673,8 @@ static void EmuThread(Core::System& system, std::unique_ptr<BootParameters> boot
|
|||||||
|
|
||||||
UpdateTitle(system);
|
UpdateTitle(system);
|
||||||
|
|
||||||
// ENTER THE VIDEO THREAD LOOP
|
// Become the CPU thread.
|
||||||
if (system.IsDualCoreMode())
|
cpu_thread_func(system, savestate_path, delete_savestate);
|
||||||
{
|
|
||||||
// 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."));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set or get the running state
|
// Set or get the running state
|
||||||
|
|||||||
@ -109,10 +109,6 @@ void CPUManager::Run()
|
|||||||
{
|
{
|
||||||
auto& power_pc = m_system.GetPowerPC();
|
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
|
// Start a separate time tracker thread
|
||||||
std::thread timing;
|
std::thread timing;
|
||||||
if (Config::Get(Config::MAIN_TIME_TRACKING))
|
if (Config::Get(Config::MAIN_TIME_TRACKING))
|
||||||
|
|||||||
@ -42,9 +42,6 @@ void AsyncRequests::QueueEvent(Event&& event)
|
|||||||
{
|
{
|
||||||
m_empty.Clear();
|
m_empty.Clear();
|
||||||
|
|
||||||
if (!m_enable)
|
|
||||||
return;
|
|
||||||
|
|
||||||
m_queue.push(std::move(event));
|
m_queue.push(std::move(event));
|
||||||
|
|
||||||
auto& system = Core::System::GetInstance();
|
auto& system = Core::System::GetInstance();
|
||||||
@ -57,20 +54,6 @@ void AsyncRequests::WaitForEmptyQueue()
|
|||||||
m_cond.wait(lock, [this] { return m_queue.empty(); });
|
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)
|
void AsyncRequests::SetPassthrough(bool enable)
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> lock(m_mutex);
|
std::unique_lock<std::mutex> lock(m_mutex);
|
||||||
|
|||||||
@ -26,7 +26,6 @@ public:
|
|||||||
PullEventsInternal();
|
PullEventsInternal();
|
||||||
}
|
}
|
||||||
void WaitForEmptyQueue();
|
void WaitForEmptyQueue();
|
||||||
void SetEnable(bool enable);
|
|
||||||
void SetPassthrough(bool enable);
|
void SetPassthrough(bool enable);
|
||||||
|
|
||||||
template <typename F>
|
template <typename F>
|
||||||
@ -74,6 +73,5 @@ private:
|
|||||||
std::mutex m_mutex;
|
std::mutex m_mutex;
|
||||||
std::condition_variable m_cond;
|
std::condition_variable m_cond;
|
||||||
|
|
||||||
bool m_enable = false;
|
|
||||||
bool m_passthrough = true;
|
bool m_passthrough = true;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -287,9 +287,6 @@ void FifoManager::ResetVideoBuffer()
|
|||||||
// Purpose: Keep the Core HW updated about the CPU-GPU distance
|
// Purpose: Keep the Core HW updated about the CPU-GPU distance
|
||||||
void FifoManager::RunGpuLoop()
|
void FifoManager::RunGpuLoop()
|
||||||
{
|
{
|
||||||
AsyncRequests::GetInstance()->SetEnable(true);
|
|
||||||
AsyncRequests::GetInstance()->SetPassthrough(false);
|
|
||||||
|
|
||||||
m_gpu_mainloop.Run(
|
m_gpu_mainloop.Run(
|
||||||
[this] {
|
[this] {
|
||||||
// Run events from the CPU thread.
|
// Run events from the CPU thread.
|
||||||
@ -391,9 +388,6 @@ void FifoManager::RunGpuLoop()
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
100);
|
100);
|
||||||
|
|
||||||
AsyncRequests::GetInstance()->SetEnable(false);
|
|
||||||
AsyncRequests::GetInstance()->SetPassthrough(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FifoManager::FlushGpu()
|
void FifoManager::FlushGpu()
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user