From a4ae019b046f5547a37285e5cace8ed3c751914a Mon Sep 17 00:00:00 2001 From: ryanmagoon Date: Tue, 14 Apr 2026 00:18:42 -0400 Subject: [PATCH] Fix save state crash in dual-core mode In dual-core mode, retro_serialize and retro_unserialize queue DoState to the CPU thread via RunOnCPUThread. The CPU thread does not have the frontend's GL context current, so any GL calls during DoState segfault. Temporarily declare the calling thread as the CPU thread so DoState runs inline where the GL context is valid. Tested on macOS arm64 with a third-party libretro frontend using an offscreen CGL context. --- Source/Core/DolphinLibretro/Main.cpp | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/Source/Core/DolphinLibretro/Main.cpp b/Source/Core/DolphinLibretro/Main.cpp index 5426507e16b9..2130f7f8eaf5 100644 --- a/Source/Core/DolphinLibretro/Main.cpp +++ b/Source/Core/DolphinLibretro/Main.cpp @@ -489,17 +489,24 @@ bool retro_serialize(void* data, size_t size) if (system.IsDualCoreMode()) ar->SetPassthrough(true); - Core::RunOnCPUThread(Core::System::GetInstance(), [&] { + const bool was_cpu = Core::IsCPUThread(); + if (!was_cpu) + Core::DeclareAsCPUThread(); + Core::RunOnCPUThread(Core::System::GetInstance(), [&] { PointerWrap p((u8**)&data, size, PointerWrap::Mode::Write); State::DoState(Core::System::GetInstance(), p); }, true); + if (!was_cpu) + Core::UndeclareAsCPUThread(); + if (system.IsDualCoreMode()) ar->SetPassthrough(false); return true; } + bool retro_unserialize(const void* data, size_t size) { Core::System& system = Core::System::GetInstance(); @@ -508,11 +515,18 @@ bool retro_unserialize(const void* data, size_t size) if (system.IsDualCoreMode()) ar->SetPassthrough(true); + const bool was_cpu = Core::IsCPUThread(); + if (!was_cpu) + Core::DeclareAsCPUThread(); + Core::RunOnCPUThread(Core::System::GetInstance(), [&] { PointerWrap p((u8**)&data, size, PointerWrap::Mode::Read); State::DoState(Core::System::GetInstance(), p); }, true); + if (!was_cpu) + Core::UndeclareAsCPUThread(); + if (system.IsDualCoreMode()) ar->SetPassthrough(false);