From c54f16ae57358479f50aa066de2fa8b7f2bb1c24 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Tue, 15 Sep 2026 15:03:19 +1000 Subject: [PATCH] VideoThread: Snapshot fullscreen state for resize Pass the core-thread fullscreen state through the queued resize callback so video-thread recovery does not read mutable core-owned state. Assert fullscreen entry points stay on the core thread. --- src/core/video_thread.cpp | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/core/video_thread.cpp b/src/core/video_thread.cpp index b68fa0512..bb61af437 100644 --- a/src/core/video_thread.cpp +++ b/src/core/video_thread.cpp @@ -78,7 +78,7 @@ static void VideoThreadEntryPoint(); static bool CreateDeviceOnThread(RenderAPI api, bool fullscreen, bool start_fullscreen_ui, bool preserve_imgui_on_failure, Error* error); static void DestroyDeviceOnThread(bool preserve_imgui_state); -static void ResizeRenderWindowOnThread(u32 width, u32 height, float scale, float refresh_rate); +static void ResizeRenderWindowOnThread(u32 width, u32 height, float scale, float refresh_rate, bool fullscreen); static void RecreateRenderWindowOnThread(bool fullscreen, bool allow_exclusive_fullscreen); static void RenderWindowResizedOnThread(); static bool CheckExclusiveFullscreenOnThread(); @@ -1375,6 +1375,8 @@ bool VideoThread::IsUsingThread() void VideoThread::ResizeRenderWindow(s32 width, s32 height, float scale, float refresh_rate) { + DebugAssert(Host::IsOnCoreThread()); + const u16 clamped_width = static_cast(std::clamp(width, 1, std::numeric_limits::max())); const u16 clamped_height = static_cast(std::clamp(height, 1, std::numeric_limits::max())); const bool size_changed = (s_state.render_window_info.surface_width != clamped_width || @@ -1386,8 +1388,8 @@ void VideoThread::ResizeRenderWindow(s32 width, s32 height, float scale, float r s_state.render_window_info.surface_scale = scale; s_state.render_window_info.surface_refresh_rate = refresh_rate; - RunOnThread([clamped_width, clamped_height, scale, refresh_rate]() { - ResizeRenderWindowOnThread(clamped_width, clamped_height, scale, refresh_rate); + RunOnThread([clamped_width, clamped_height, scale, refresh_rate, fullscreen = s_state.fullscreen_state]() { + ResizeRenderWindowOnThread(clamped_width, clamped_height, scale, refresh_rate, fullscreen); }); if (System::IsValid()) @@ -1399,7 +1401,7 @@ void VideoThread::ResizeRenderWindow(s32 width, s32 height, float scale, float r } } -void VideoThread::ResizeRenderWindowOnThread(u32 width, u32 height, float scale, float refresh_rate) +void VideoThread::ResizeRenderWindowOnThread(u32 width, u32 height, float scale, float refresh_rate, bool fullscreen) { // We should _not_ be getting this without a device, since we should have shut down. if (!g_gpu_device || !g_gpu_device->HasMainSwapChain()) @@ -1411,9 +1413,8 @@ void VideoThread::ResizeRenderWindowOnThread(u32 width, u32 height, float scale, GPUSwapChain* const swap_chain = g_gpu_device->GetMainSwapChain(); if (!swap_chain->ResizeBuffers(width, height, &error)) { - // ick, CPU thread read, but this is unlikely to happen in the first place ERROR_LOG("Failed to resize main swap chain: {}", error.GetDescription()); - RecreateRenderWindowOnThread(s_state.fullscreen_state, true); + RecreateRenderWindowOnThread(fullscreen, true); return; } @@ -1425,11 +1426,15 @@ void VideoThread::ResizeRenderWindowOnThread(u32 width, u32 height, float scale, void VideoThread::RecreateRenderWindow() { + DebugAssert(Host::IsOnCoreThread()); + RunOnThread([fullscreen = s_state.fullscreen_state]() { RecreateRenderWindowOnThread(fullscreen, true); }); } void VideoThread::SetFullscreen(bool fullscreen) { + DebugAssert(Host::IsOnCoreThread()); + // Technically not safe to read g_gpu_device here on the CPU thread, but we do sync on create/destroy. if (s_state.fullscreen_state == fullscreen || !Host::CanChangeFullscreenMode(fullscreen) || !g_gpu_device) return; @@ -1440,6 +1445,8 @@ void VideoThread::SetFullscreen(bool fullscreen) void VideoThread::SetFullscreenWithCompletionHandler(bool fullscreen, AsyncCallType completion_handler) { + DebugAssert(Host::IsOnCoreThread()); + if (s_state.fullscreen_state == fullscreen || !Host::CanChangeFullscreenMode(fullscreen) || !g_gpu_device) { if (completion_handler)