diff --git a/src/core/system.cpp b/src/core/system.cpp index 42e7a1aac..2bfccbbe7 100644 --- a/src/core/system.cpp +++ b/src/core/system.cpp @@ -577,6 +577,8 @@ void System::CoreThreadShutdown() HTTPCache::Shutdown(); + VideoThread::Internal::ProcessShutdown(); + s_state.core_thread_handle = {}; #ifdef _WIN32 diff --git a/src/core/video_thread.cpp b/src/core/video_thread.cpp index d1059210a..5c53d6255 100644 --- a/src/core/video_thread.cpp +++ b/src/core/video_thread.cpp @@ -71,6 +71,7 @@ static bool IsCommandFIFOEmpty(); static void WakeThread(); static void WakeThreadIfSleeping(); static bool SleepThread(bool allow_sleep); +static void VideoThreadEntryPoint(); static bool CreateDeviceOnThread(RenderAPI api, bool fullscreen, bool start_fullscreen_ui, bool preserve_imgui_on_failure, Error* error); @@ -100,7 +101,7 @@ struct ALIGN_TO_CACHE_LINE State { // Owned by CPU thread. Timer::Value thread_spin_time = 0; - Threading::ThreadHandle thread_handle; + Threading::Thread thread; Common::unique_aligned_ptr command_fifo_data; WindowInfo render_window_info; std::optional requested_renderer; @@ -134,7 +135,7 @@ static State s_state; const Threading::ThreadHandle& VideoThread::Internal::GetThreadHandle() { - return s_state.thread_handle; + return s_state.thread; } void VideoThread::ResetCommandFIFO() @@ -151,9 +152,12 @@ void VideoThread::Internal::ProcessStartup() s_state.command_fifo_data = Common::make_unique_aligned_for_overwrite(HOST_CACHE_LINE_SIZE, COMMAND_QUEUE_SIZE); s_state.use_thread = g_settings.gpu_use_thread; s_state.run_idle_reasons = static_cast(RunIdleReason::NoGPUBackend); + + // Thread is always started/persists regardless of whether it is used or not. + s_state.thread.Start(&VideoThread::VideoThreadEntryPoint); } -void VideoThread::Internal::RequestShutdown() +void VideoThread::Internal::ProcessShutdown() { INFO_LOG("Shutting down video thread..."); SyncThread(false); @@ -161,6 +165,9 @@ void VideoThread::Internal::RequestShutdown() // Thread must be enabled to shut it down. SetThreadEnabled(true); PushCommandAndWakeThread(AllocateCommand(VideoThreadCommandType::Shutdown, sizeof(VideoThreadCommand))); + s_state.thread.Join(); + + INFO_LOG("Video thread shutdown complete."); } VideoThreadCommand* VideoThread::AllocateCommand(VideoThreadCommandType command, u32 size) @@ -407,9 +414,9 @@ bool VideoThread::SleepThread(bool allow_sleep) } } -void VideoThread::Internal::VideoThreadEntryPoint() +void VideoThread::VideoThreadEntryPoint() { - s_state.thread_handle = Threading::ThreadHandle::GetForCallingThread(); + Threading::SetNameOfCurrentThread("Video Thread"); // Take a local copy of the FIFO, that way it's not ping-ponging between the threads. u8* const command_fifo_data = s_state.command_fifo_data.get(); @@ -427,7 +434,7 @@ void VideoThread::Internal::VideoThreadEntryPoint() } else { - DoRunIdle(); + VideoThread::Internal::DoRunIdle(); continue; } } @@ -503,7 +510,6 @@ void VideoThread::Internal::VideoThreadEntryPoint() // Should have consumed everything, and be shutdown. DebugAssert(read_ptr == write_ptr); s_state.command_fifo_read_ptr.store(read_ptr, std::memory_order_release); - s_state.thread_handle = {}; return; } break; @@ -1319,7 +1325,7 @@ void VideoThread::ReportFatalErrorAndShutdown(std::string_view reason) bool VideoThread::IsOnThread() { - return (!s_state.use_thread || s_state.thread_handle.IsCallingThread()); + return (!s_state.use_thread || s_state.thread.IsCallingThread()); } bool VideoThread::IsUsingThread() diff --git a/src/core/video_thread.h b/src/core/video_thread.h index 44e5c5bf0..b06ebdb77 100644 --- a/src/core/video_thread.h +++ b/src/core/video_thread.h @@ -105,9 +105,8 @@ void SyncThread(bool spin); namespace Internal { const Threading::ThreadHandle& GetThreadHandle(); void ProcessStartup(); +void ProcessShutdown(); void DoRunIdle(); -void RequestShutdown(); -void VideoThreadEntryPoint(); bool PresentFrameAndRestoreContext(); } // namespace Internal } // namespace VideoThread diff --git a/src/duckstation-qt/qthost.cpp b/src/duckstation-qt/qthost.cpp index a5b5335cc..d76f7abfe 100644 --- a/src/duckstation-qt/qthost.cpp +++ b/src/duckstation-qt/qthost.cpp @@ -2232,9 +2232,6 @@ void CoreThread::run() createBackgroundControllerPollTimer(); startBackgroundControllerPollTimer(); - // kick off GPU thread - Threading::Thread video_thread(&CoreThread::videoThreadEntryPoint); - // main loop while (!m_shutdown_flag) { @@ -2261,10 +2258,6 @@ void CoreThread::run() destroyBackgroundControllerPollTimer(); - // tell GPU thread to exit - VideoThread::Internal::RequestShutdown(); - video_thread.Join(); - // join worker threads QtHost::s_async_task_queue.SetWorkerCount(0); @@ -2277,12 +2270,6 @@ void CoreThread::run() m_event_loop = nullptr; } -void CoreThread::videoThreadEntryPoint() -{ - Threading::SetNameOfCurrentThread("Video Thread"); - VideoThread::Internal::VideoThreadEntryPoint(); -} - void Host::FrameDoneOnVideoThread(GPUBackend* gpu_backend, u32 frame_number) { } diff --git a/src/duckstation-qt/qthost.h b/src/duckstation-qt/qthost.h index 2c56739cb..555d455e2 100644 --- a/src/duckstation-qt/qthost.h +++ b/src/duckstation-qt/qthost.h @@ -208,8 +208,6 @@ private: void confirmActionWithSafetyCheck(const QString& action, bool check_achievements, bool cancel_resume_on_accept, std::function callback) const; - static void videoThreadEntryPoint(); - QThread* m_ui_thread; QEventLoop* m_event_loop = nullptr; QTimer* m_background_controller_polling_timer = nullptr; diff --git a/src/duckstation-regtest/regtest_host.cpp b/src/duckstation-regtest/regtest_host.cpp index e651a005e..cdc02cd2b 100644 --- a/src/duckstation-regtest/regtest_host.cpp +++ b/src/duckstation-regtest/regtest_host.cpp @@ -59,7 +59,6 @@ static bool SetNewDataRoot(const std::string& filename); static void DumpSystemStateHashes(); static std::string GetFrameDumpPath(u32 frame); static void ProcessCoreThreadEvents(); -static void VideoThreadEntryPoint(); struct RegTestHostState { @@ -74,8 +73,6 @@ ALIGN_TO_CACHE_LINE static TaskQueue s_async_task_queue; } // namespace RegTestHost -static Threading::Thread s_video_thread; - static u32 s_frames_to_run = 60 * 60; static u32 s_frames_remaining = 0; static u32 s_frame_dump_interval = 0; @@ -705,12 +702,6 @@ void RegTestHost::HookSignals() #endif } -void RegTestHost::VideoThreadEntryPoint() -{ - Threading::SetNameOfCurrentThread("Video Thread"); - VideoThread::Internal::VideoThreadEntryPoint(); -} - void RegTestHost::DumpSystemStateHashes() { Error error; @@ -1006,7 +997,6 @@ int main(int argc, char* argv[]) RegTestHost::s_async_task_queue.SetWorkerCount(1); RegTestHost::HookSignals(); - s_video_thread.Start(&RegTestHost::VideoThreadEntryPoint); int result = -1; INFO_LOG("Trying to boot '{}'...", autoboot->path); @@ -1053,12 +1043,6 @@ int main(int argc, char* argv[]) result = 0; cleanup: - if (s_video_thread.Joinable()) - { - VideoThread::Internal::RequestShutdown(); - s_video_thread.Join(); - } - RegTestHost::s_async_task_queue.SetWorkerCount(0); RegTestHost::ProcessCoreThreadEvents();