diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 0c13417ba..f3f8a32f8 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -143,6 +143,7 @@ add_library(core
video_thread.cpp
video_thread.h
video_thread_commands.h
+ video_thread_private.h
)
set(RECOMPILER_SRCS
diff --git a/src/core/core.cpp b/src/core/core.cpp
index d6ff46716..b106e516b 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -11,6 +11,7 @@
#include "system.h"
#include "system_private.h"
#include "video_thread.h"
+#include "video_thread_private.h"
#include "util/gpu_device.h"
#include "util/http_cache.h"
@@ -714,7 +715,7 @@ bool Core::CoreThreadInitialize(Error* error)
LogStartupInformation();
- VideoThread::Internal::ProcessStartup();
+ VideoThread::ProcessStartup();
Achievements::Initialize();
@@ -740,7 +741,7 @@ void Core::CoreThreadShutdown()
HTTPCache::Shutdown();
- VideoThread::Internal::ProcessShutdown();
+ VideoThread::ProcessShutdown();
s_locals.core_thread_handle = {};
diff --git a/src/core/core.vcxproj b/src/core/core.vcxproj
index fcc2717c8..b3e1f4c42 100644
--- a/src/core/core.vcxproj
+++ b/src/core/core.vcxproj
@@ -179,6 +179,7 @@
+
diff --git a/src/core/core.vcxproj.filters b/src/core/core.vcxproj.filters
index 3afaeecdb..2dd895aca 100644
--- a/src/core/core.vcxproj.filters
+++ b/src/core/core.vcxproj.filters
@@ -160,6 +160,7 @@
+
diff --git a/src/core/fullscreenui_widgets.cpp b/src/core/fullscreenui_widgets.cpp
index 380ced3f0..b1e07c907 100644
--- a/src/core/fullscreenui_widgets.cpp
+++ b/src/core/fullscreenui_widgets.cpp
@@ -6354,7 +6354,7 @@ void FullscreenUI::LoadingScreenProgressCallback::Close()
else
{
// since this was pushing frames, we need to restore the context. do that by pushing a frame ourselves
- VideoThread::Internal::PresentFrameAndRestoreContext();
+ VideoThread::PresentFrameAndRestoreContext();
}
m_last_progress_percent = -1;
diff --git a/src/core/host.h b/src/core/host.h
index 7487cea9a..fcb9af02c 100644
--- a/src/core/host.h
+++ b/src/core/host.h
@@ -17,6 +17,8 @@ namespace Threading {
class ThreadHandle;
}
+enum class WindowInfoType : u8;
+
namespace Host {
/// Returns true if the specified resource file exists.
@@ -87,4 +89,10 @@ void WaitForAllAsyncTasks();
/// Commits any changes made to the base settings layer to the host.
void CommitBaseSettingChanges();
+/// Returns the window type for the host.
+WindowInfoType GetRenderWindowInfoType();
+
+/// Changes the screensaver inhibit state.
+bool SetScreensaverInhibit(bool inhibit, Error* error);
+
} // namespace Host
diff --git a/src/core/performance_counters.cpp b/src/core/performance_counters.cpp
index 776c19a50..a9f8741e1 100644
--- a/src/core/performance_counters.cpp
+++ b/src/core/performance_counters.cpp
@@ -156,7 +156,7 @@ void PerformanceCounters::Reset()
s_state.last_frame_number = frame_number;
s_state.last_internal_frame_number = internal_frame_number;
s_state.last_core_thread_time = Host::GetCoreThreadHandle().GetCPUTime();
- s_state.last_video_thread_time = VideoThread::Internal::GetThreadHandle().GetCPUTime();
+ s_state.last_video_thread_time = VideoThread::GetThreadHandle().GetCPUTime();
s_state.average_frame_time_accumulator = 0.0f;
s_state.minimum_frame_time_accumulator = 0.0f;
@@ -207,7 +207,7 @@ void PerformanceCounters::Update(GPUBackend* gpu, u32 frame_number, u32 internal
s_state.speed = (s_state.vps / System::GetVideoFrameRate()) * 100.0f;
const u64 core_thread_time = Host::GetCoreThreadHandle().GetCPUTime();
- const u64 video_thread_time = VideoThread::Internal::GetThreadHandle().GetCPUTime();
+ const u64 video_thread_time = VideoThread::GetThreadHandle().GetCPUTime();
const u64 core_thread_delta = core_thread_time - s_state.last_core_thread_time;
const u64 video_thread_delta = video_thread_time - s_state.last_video_thread_time;
s_state.last_core_thread_time = core_thread_time;
diff --git a/src/core/settings.cpp b/src/core/settings.cpp
index d1cea7830..74a9d1113 100644
--- a/src/core/settings.cpp
+++ b/src/core/settings.cpp
@@ -6,9 +6,9 @@
#include "controller.h"
#include "core.h"
#include "gte_types.h"
+#include "host.h"
#include "imgui_overlays.h"
#include "system.h"
-#include "video_thread.h"
#include "util/gpu_device.h"
#include "util/imgui_manager.h"
diff --git a/src/core/system.cpp b/src/core/system.cpp
index 03d72efb8..2ee78b35b 100644
--- a/src/core/system.cpp
+++ b/src/core/system.cpp
@@ -221,7 +221,7 @@ static void UpdateSessionTime(const std::string& prev_serial);
namespace {
-struct ALIGN_TO_CACHE_LINE StateVars
+struct StateVars
{
TickCount ticks_per_second = 0;
TickCount max_slice_ticks = 0;
@@ -311,7 +311,7 @@ struct ALIGN_TO_CACHE_LINE StateVars
} // namespace
-static StateVars s_state;
+ALIGN_TO_CACHE_LINE static StateVars s_state;
} // namespace System
@@ -3584,7 +3584,7 @@ void System::UpdateSpeedLimiterState()
core_thread.SetTimeConstraints(s_state.optimal_frame_pacing, new_scheduler_period, typical_time,
new_scheduler_period);
}
- const Threading::ThreadHandle& video_thread = VideoThread::Internal::GetThreadHandle();
+ const Threading::ThreadHandle& video_thread = VideoThread::GetThreadHandle();
if (video_thread)
{
video_thread.SetTimeConstraints(s_state.optimal_frame_pacing, new_scheduler_period, typical_time,
diff --git a/src/core/video_presenter.cpp b/src/core/video_presenter.cpp
index 15a286622..49cbd29a4 100644
--- a/src/core/video_presenter.cpp
+++ b/src/core/video_presenter.cpp
@@ -1808,7 +1808,7 @@ void VideoPresenter::ReloadPostProcessingSettings(bool display, bool internal, b
// trigger represent of frame
if (VideoThread::IsSystemPaused())
- VideoThread::Internal::PresentFrameAndRestoreContext();
+ VideoThread::PresentFrameAndRestoreContext();
});
}
diff --git a/src/core/video_thread.cpp b/src/core/video_thread.cpp
index e76c8f953..8db926184 100644
--- a/src/core/video_thread.cpp
+++ b/src/core/video_thread.cpp
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: 2019-2025 Connor McLaughlin
+// SPDX-FileCopyrightText: 2019-2026 Connor McLaughlin
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
#include "video_thread.h"
@@ -16,6 +16,7 @@
#include "system_private.h"
#include "video_presenter.h"
#include "video_thread_commands.h"
+#include "video_thread_private.h"
#include "util/gpu_device.h"
#include "util/imgui_manager.h"
@@ -133,7 +134,7 @@ static State s_state;
} // namespace VideoThread
-const Threading::ThreadHandle& VideoThread::Internal::GetThreadHandle()
+const Threading::ThreadHandle& VideoThread::GetThreadHandle()
{
return s_state.thread;
}
@@ -146,7 +147,7 @@ void VideoThread::ResetCommandFIFO()
s_state.command_fifo_read_ptr.store(0, std::memory_order_release);
}
-void VideoThread::Internal::ProcessStartup()
+void VideoThread::ProcessStartup()
{
s_state.thread_spin_time = Timer::ConvertNanosecondsToValue(THREAD_SPIN_TIME_US * 1000.0);
s_state.command_fifo_data = Common::make_unique_aligned_for_overwrite(HOST_CACHE_LINE_SIZE, COMMAND_QUEUE_SIZE);
@@ -157,7 +158,7 @@ void VideoThread::Internal::ProcessStartup()
s_state.thread.Start(&VideoThread::VideoThreadEntryPoint);
}
-void VideoThread::Internal::ProcessShutdown()
+void VideoThread::ProcessShutdown()
{
INFO_LOG("Shutting down video thread...");
SyncThread(false);
@@ -434,7 +435,7 @@ void VideoThread::VideoThreadEntryPoint()
}
else
{
- VideoThread::Internal::DoRunIdle();
+ DoRunIdle();
continue;
}
}
@@ -524,7 +525,7 @@ void VideoThread::VideoThreadEntryPoint()
}
}
-void VideoThread::Internal::DoRunIdle()
+void VideoThread::DoRunIdle()
{
if (!g_gpu_device->HasMainSwapChain()) [[unlikely]]
{
@@ -1044,7 +1045,7 @@ void VideoThread::DestroyGPUPresenterOnThread()
VideoPresenter::Shutdown();
}
-bool VideoThread::Internal::PresentFrameAndRestoreContext()
+bool VideoThread::PresentFrameAndRestoreContext()
{
DebugAssert(IsOnThread());
@@ -1142,7 +1143,7 @@ void VideoThread::UpdateSettingsOnThread(GPUSettings&& new_settings)
}
if (ImGuiManager::UpdateDebugWindowConfig())
- Internal::PresentFrameAndRestoreContext();
+ PresentFrameAndRestoreContext();
else
s_state.gpu_backend->RestoreDeviceContext();
}
@@ -1235,7 +1236,7 @@ void VideoThread::UpdateSettings(bool gpu_settings_changed, bool device_settings
if (s_state.gpu_backend)
{
if (ImGuiManager::UpdateDebugWindowConfig())
- Internal::PresentFrameAndRestoreContext();
+ PresentFrameAndRestoreContext();
}
});
#endif
@@ -1509,8 +1510,8 @@ void VideoThread::RenderWindowResizedOnThread()
{
// Hackity hack, on some systems, presenting a single frame isn't enough to actually get it
// displayed. Two seems to be good enough. Maybe something to do with direct scanout.
- Internal::PresentFrameAndRestoreContext();
- Internal::PresentFrameAndRestoreContext();
+ PresentFrameAndRestoreContext();
+ PresentFrameAndRestoreContext();
}
}
@@ -1553,7 +1554,7 @@ void VideoThread::PresentCurrentFrame()
// But we shouldn't be not running idle without a GPU backend.
if (s_state.gpu_backend)
- Internal::PresentFrameAndRestoreContext();
+ PresentFrameAndRestoreContext();
});
}
diff --git a/src/core/video_thread.h b/src/core/video_thread.h
index b06ebdb77..6d3229c71 100644
--- a/src/core/video_thread.h
+++ b/src/core/video_thread.h
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin
+// SPDX-FileCopyrightText: 2019-2026 Connor McLaughlin
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
#pragma once
@@ -18,7 +18,6 @@ class ThreadHandle;
enum class RenderAPI : u8;
enum class GPUVSyncMode : u8;
-enum class WindowInfoType : u8;
enum class GPURenderer : u8;
enum class VideoThreadCommandType : u8;
@@ -102,35 +101,10 @@ void PushCommandAndWakeThread(VideoThreadCommand* cmd);
void PushCommandAndSync(VideoThreadCommand* cmd, bool spin);
void SyncThread(bool spin);
-namespace Internal {
+/// Returns a handle to the video thread.
const Threading::ThreadHandle& GetThreadHandle();
-void ProcessStartup();
-void ProcessShutdown();
-void DoRunIdle();
-bool PresentFrameAndRestoreContext();
-} // namespace Internal
-} // namespace VideoThread
-
-namespace Host {
-
-/// Called when the core is creating a render device.
-/// This could also be fullscreen transition.
-std::optional AcquireRenderWindow(RenderAPI render_api, bool fullscreen, bool exclusive_fullscreen,
- Error* error);
-
-/// Returns the window type for the host.
-WindowInfoType GetRenderWindowInfoType();
-/// Called when the core is finished with a render window.
-void ReleaseRenderWindow();
-
-/// Called before a fullscreen transition occurs.
-bool CanChangeFullscreenMode(bool new_fullscreen_state);
-
-/// Called when the pause state changes, or fullscreen UI opens.
-void OnVideoThreadRunIdleChanged(bool is_active);
-
-/// Changes the screensaver inhibit state.
-bool SetScreensaverInhibit(bool inhibit, Error* error);
+/// Presents the current frame and restores the GPU context, only carefully call on the video thread.
+bool PresentFrameAndRestoreContext();
-} // namespace Host
+} // namespace VideoThread
diff --git a/src/core/video_thread_private.h b/src/core/video_thread_private.h
new file mode 100644
index 000000000..fd782928d
--- /dev/null
+++ b/src/core/video_thread_private.h
@@ -0,0 +1,32 @@
+// SPDX-FileCopyrightText: 2019-2026 Connor McLaughlin
+// SPDX-License-Identifier: CC-BY-NC-ND-4.0
+
+#pragma once
+
+#include "video_thread.h"
+
+namespace VideoThread {
+
+void ProcessStartup();
+void ProcessShutdown();
+void DoRunIdle();
+
+} // namespace VideoThread
+
+namespace Host {
+
+/// Called when the core is creating a render device.
+/// This could also be fullscreen transition.
+std::optional AcquireRenderWindow(RenderAPI render_api, bool fullscreen, bool exclusive_fullscreen,
+ Error* error);
+
+/// Called when the core is finished with a render window.
+void ReleaseRenderWindow();
+
+/// Called before a fullscreen transition occurs.
+bool CanChangeFullscreenMode(bool new_fullscreen_state);
+
+/// Called when the pause state changes, or fullscreen UI opens.
+void OnVideoThreadRunIdleChanged(bool is_active);
+
+} // namespace Host
diff --git a/src/duckstation-qt/qthost.cpp b/src/duckstation-qt/qthost.cpp
index 55567abb2..1dee84a00 100644
--- a/src/duckstation-qt/qthost.cpp
+++ b/src/duckstation-qt/qthost.cpp
@@ -35,6 +35,7 @@
#include "core/system_private.h"
#include "core/video_presenter.h"
#include "core/video_thread.h"
+#include "core/video_thread_private.h"
#include "common/assert.h"
#include "common/crash_handler.h"
@@ -2252,7 +2253,7 @@ void CoreThread::run()
// have to double-check the condition after processing events, because the events could shut us down
if (!VideoThread::IsUsingThread() && VideoThread::IsRunningIdle())
- VideoThread::Internal::DoRunIdle();
+ VideoThread::DoRunIdle();
}
else
{
diff --git a/src/duckstation-qt/qtwindowinfo.cpp b/src/duckstation-qt/qtwindowinfo.cpp
index be3d47c3a..dce4403c1 100644
--- a/src/duckstation-qt/qtwindowinfo.cpp
+++ b/src/duckstation-qt/qtwindowinfo.cpp
@@ -5,7 +5,7 @@
#include "qtutils.h"
#include "core/core.h"
-#include "core/video_thread.h"
+#include "core/host.h"
#include "util/gpu_device.h"
diff --git a/src/duckstation-regtest/regtest_host.cpp b/src/duckstation-regtest/regtest_host.cpp
index 5729a3510..eecf08a91 100644
--- a/src/duckstation-regtest/regtest_host.cpp
+++ b/src/duckstation-regtest/regtest_host.cpp
@@ -18,6 +18,7 @@
#include "core/system_private.h"
#include "core/video_presenter.h"
#include "core/video_thread.h"
+#include "core/video_thread_private.h"
#include "scmversion/scmversion.h"