From 735dc5ba2ad50e9ebfdb8f3b4e8a92176ad3c762 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sat, 2 May 2026 23:41:19 +1000 Subject: [PATCH] Core: Centralize the async task queue Will be needed for multithreaded shader compilation, and it was duplicated code anyway. --- src/core/core.cpp | 23 ++++++++++++++++++++++- src/core/core_private.h | 2 +- src/duckstation-qt/qthost.cpp | 24 +----------------------- src/duckstation-regtest/regtest_host.cpp | 19 +------------------ 4 files changed, 25 insertions(+), 43 deletions(-) diff --git a/src/core/core.cpp b/src/core/core.cpp index b106e516b..dd408a010 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -28,6 +28,7 @@ #include "common/path.h" #include "common/ryml_helpers.h" #include "common/string_util.h" +#include "common/task_queue.h" #include "common/threading.h" #include "common/timer.h" @@ -49,6 +50,9 @@ LOG_CHANNEL(Core); namespace Core { +/// Use two async worker threads, should be enough for most tasks. +static constexpr u32 NUM_ASYNC_WORKER_THREADS = 2; + static bool SetAppRootAndResources(const char* resources_subdir, Error* error); static bool SetDataRoot(Error* error); static void SetDefaultSettings(SettingsInterface& si, bool host, bool system, bool controller); @@ -66,6 +70,8 @@ struct CoreLocals Threading::ThreadHandle core_thread_handle; Timer::Value process_start_time = 0; + + TaskQueue async_task_queue; }; } // namespace @@ -695,7 +701,7 @@ void Core::ProcessShutdown() System::ReleasePersistentMemory(); } -bool Core::CoreThreadInitialize(Error* error) +bool Core::CoreThreadInitialize(bool disable_worker_threads, Error* error) { #ifdef _WIN32 // On Win32, we have a bunch of things which use COM (e.g. SDL, Cubeb, etc). @@ -711,6 +717,9 @@ bool Core::CoreThreadInitialize(Error* error) s_locals.core_thread_handle = Threading::ThreadHandle::GetForCallingThread(); + if (!disable_worker_threads) + s_locals.async_task_queue.SetWorkerCount(NUM_ASYNC_WORKER_THREADS); + System::LoadSettings(false); LogStartupInformation(); @@ -729,6 +738,8 @@ bool Core::CoreThreadInitialize(Error* error) void Core::CoreThreadShutdown() { + s_locals.async_task_queue.SetWorkerCount(0); + #ifdef ENABLE_DISCORD_PRESENCE DiscordPresence::Shutdown(); #endif @@ -760,6 +771,16 @@ bool Host::IsOnCoreThread() return Core::s_locals.core_thread_handle.IsCallingThread(); } +void Host::QueueAsyncTask(std::function function) +{ + Core::s_locals.async_task_queue.SubmitTask(std::move(function)); +} + +void Host::WaitForAllAsyncTasks() +{ + Core::s_locals.async_task_queue.WaitForAll(); +} + float Core::GetProcessUptime() { return static_cast(Timer::ConvertValueToSeconds(Timer::GetCurrentValue() - s_locals.process_start_time)); diff --git a/src/core/core_private.h b/src/core/core_private.h index a79de68c1..491fef89f 100644 --- a/src/core/core_private.h +++ b/src/core/core_private.h @@ -42,7 +42,7 @@ bool ProcessStartup(Error* error); void ProcessShutdown(); /// Called on CPU thread initialization. -bool CoreThreadInitialize(Error* error); +bool CoreThreadInitialize(bool disable_worker_threads, Error* error); /// Called on CPU thread shutdown. void CoreThreadShutdown(); diff --git a/src/duckstation-qt/qthost.cpp b/src/duckstation-qt/qthost.cpp index 1dee84a00..5a2775d08 100644 --- a/src/duckstation-qt/qthost.cpp +++ b/src/duckstation-qt/qthost.cpp @@ -46,7 +46,6 @@ #include "common/path.h" #include "common/scoped_guard.h" #include "common/string_util.h" -#include "common/task_queue.h" #include "common/threading.h" #include "util/audio_stream.h" @@ -106,9 +105,6 @@ QT_TRANSLATE_NOOP("MAC_APPLICATION_MENU", "About %1") static constexpr u32 SETTINGS_SAVE_DELAY = 1000; -/// Use two async worker threads, should be enough for most tasks. -static constexpr u32 NUM_ASYNC_WORKER_THREADS = 2; - /// Interval at which the controllers are polled when the system is not active. static constexpr int BACKGROUND_CONTROLLER_POLLING_INTERVAL_WITH_DEVICES = 100; static constexpr int BACKGROUND_CONTROLLER_POLLING_INTERVAL_WITHOUT_DEVICES = 1000; @@ -180,7 +176,6 @@ struct State } // namespace ALIGN_TO_CACHE_LINE static State s_state; -ALIGN_TO_CACHE_LINE static TaskQueue s_async_task_queue; } // namespace QtHost @@ -1712,16 +1707,6 @@ void Host::RunOnUIThread(std::function function, bool block /* = false*/ block ? Qt::BlockingQueuedConnection : Qt::QueuedConnection, std::move(function)); } -void Host::QueueAsyncTask(std::function function) -{ - QtHost::s_async_task_queue.SubmitTask(std::move(function)); -} - -void Host::WaitForAllAsyncTasks() -{ - QtHost::s_async_task_queue.WaitForAll(); -} - QtAsyncTask::QtAsyncTask(WorkCallback callback) { m_callback = std::move(callback); @@ -2218,7 +2203,7 @@ void CoreThread::run() // input source setup must happen on emu thread { Error startup_error; - if (!Core::CoreThreadInitialize(&startup_error)) + if (!Core::CoreThreadInitialize(false, &startup_error)) { moveToThread(m_ui_thread); Host::ReportFatalError("Fatal Startup Error", startup_error.GetDescription()); @@ -2226,10 +2211,6 @@ void CoreThread::run() } } - // start up worker threads - // TODO: Replace this with QThreads - QtHost::s_async_task_queue.SetWorkerCount(NUM_ASYNC_WORKER_THREADS); - // connections connect(qApp, &QGuiApplication::applicationStateChanged, this, &CoreThread::applicationStateChanged); @@ -2266,9 +2247,6 @@ void CoreThread::run() destroyBackgroundControllerPollTimer(); - // join worker threads - QtHost::s_async_task_queue.SetWorkerCount(0); - // and tidy up everything left Core::CoreThreadShutdown(); diff --git a/src/duckstation-regtest/regtest_host.cpp b/src/duckstation-regtest/regtest_host.cpp index eecf08a91..982cfa285 100644 --- a/src/duckstation-regtest/regtest_host.cpp +++ b/src/duckstation-regtest/regtest_host.cpp @@ -36,7 +36,6 @@ #include "common/path.h" #include "common/sha256_digest.h" #include "common/string_util.h" -#include "common/task_queue.h" #include "common/threading.h" #include "common/time_helpers.h" #include "common/timer.h" @@ -71,7 +70,6 @@ struct RegTestHostState }; static RegTestHostState s_state; -ALIGN_TO_CACHE_LINE static TaskQueue s_async_task_queue; } // namespace RegTestHost @@ -365,16 +363,6 @@ void Host::RunOnUIThread(std::function function, bool block /* = false * RunOnCoreThread(std::move(function), block); } -void Host::QueueAsyncTask(std::function function) -{ - RegTestHost::s_async_task_queue.SubmitTask(std::move(function)); -} - -void Host::WaitForAllAsyncTasks() -{ - RegTestHost::s_async_task_queue.WaitForAll(); -} - void Host::RequestResizeHostDisplay(s32 width, s32 height) { // @@ -989,15 +977,12 @@ int main(int argc, char* argv[]) if (!RegTestHost::SetNewDataRoot(autoboot->path)) return EXIT_FAILURE; - if (!Core::CoreThreadInitialize(&error)) + if (!Core::CoreThreadInitialize(true, &error)) { ERROR_LOG("CoreThreadInitialize() failed: {}", error.GetDescription()); return EXIT_FAILURE; } - // Only one async worker, keep the CPU usage down so we can parallelize execution of regtest itself. - RegTestHost::s_async_task_queue.SetWorkerCount(1); - RegTestHost::HookSignals(); int result = -1; @@ -1045,8 +1030,6 @@ int main(int argc, char* argv[]) result = 0; cleanup: - RegTestHost::s_async_task_queue.SetWorkerCount(0); - RegTestHost::ProcessCoreThreadEvents(); Core::CoreThreadShutdown(); Core::ProcessShutdown();