diff --git a/Core/System.cpp b/Core/System.cpp index 56798eb643..b0be410198 100644 --- a/Core/System.cpp +++ b/Core/System.cpp @@ -110,6 +110,10 @@ static bool g_fileLoggingWasEnabled; // CPU and loader threads move it along. static std::atomic g_bootState = BootState::Off; +// See PSP_NotifyGraphicsDeviceLost. +static std::mutex g_graphicsDeviceMutex; +static bool g_graphicsDeviceLost = false; + BootState PSP_GetBootState() { return g_bootState; } @@ -749,6 +753,16 @@ static bool InitGPU(std::string *error_string) { return true; } +void PSP_NotifyGraphicsDeviceLost() { + std::lock_guard lock(g_graphicsDeviceMutex); + g_graphicsDeviceLost = true; +} + +void PSP_NotifyGraphicsDeviceRestored() { + std::lock_guard lock(g_graphicsDeviceMutex); + g_graphicsDeviceLost = false; +} + bool PSP_InitStart(const CoreParameter &coreParam) { if (g_bootState != BootState::Off) { ERROR_LOG(Log::Loader, "Can't start loader thread - already on."); @@ -825,8 +839,12 @@ bool PSP_InitStart(const CoreParameter &coreParam) { // Must not stamp Complete over the Failed that InitGPU sets - it has already run // CPU_Shutdown(), so PSP_InitUpdate would take the success path on a core that no longer // exists, right down to a null Memory::base. - if (!InitGPU(errorString)) { - return; + { + // Held while creating the GPU, so the device can't be lost meanwhile. + std::lock_guard lock(g_graphicsDeviceMutex); + if (!g_graphicsDeviceLost && !InitGPU(errorString)) { + return; + } } #endif g_bootState = BootState::Complete; @@ -858,15 +876,26 @@ BootState PSP_InitUpdate(std::string *error_string) { return BootState::Failed; } -#ifdef __LIBRETRO__ - if (!InitGPU(error_string)) { - // Same as the Failed branch above - the core is already gone. - Core_NotifyLifecycle(CoreLifecycle::START_COMPLETE); - *error_string = g_CoreParameter.errorString; - g_bootState = BootState::Off; - return BootState::Failed; + // On libretro, and if the graphics device was lost when the boot thread got there, the GPU is created here. + if (!gpu) { + std::lock_guard lock(g_graphicsDeviceMutex); + bool success; + if (g_graphicsDeviceLost) { + // Exiting during boot, with the device gone. + g_CoreParameter.errorString = "Graphics device lost during boot"; + CPU_Shutdown(false); + success = false; + } else { + success = InitGPU(&g_CoreParameter.errorString); + } + if (!success) { + // Same as the Failed branch above - the core is already gone. + Core_NotifyLifecycle(CoreLifecycle::START_COMPLETE); + *error_string = g_CoreParameter.errorString; + g_bootState = BootState::Off; + return BootState::Failed; + } } -#endif // Ok, async part of the boot completed, let's finish up things on the main thread. if (gpu) { diff --git a/Core/System.h b/Core/System.h index 8e241d294f..c1225ec462 100644 --- a/Core/System.h +++ b/Core/System.h @@ -78,6 +78,12 @@ BootState PSP_Init(const CoreParameter &coreParam, std::string *error_string); // The main thread needs to block on this in some situations, like losing the surface. BootState PollBootState(); +// The boot thread creates the GPU at the end of a boot. If the graphics device is lost then (like when the +// Android app is in the background), it leaves that to PSP_InitUpdate, which runs once rendering resumes. +// Lost waits for a GPU creation in progress to finish, so the GPU can be told about the loss. +void PSP_NotifyGraphicsDeviceLost(); +void PSP_NotifyGraphicsDeviceRestored(); + void PSP_Shutdown(bool success); FileLoader *PSP_LoadedFile(); diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index 397b5734cd..57d6fc4569 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -455,9 +455,12 @@ EmuScreen::~EmuScreen() { g_Config.TimeTracker().Stop(gameID); if (bootPending_) { - // We probably quit during boot, got blocked in lostdevice, and then didn't end up in update again to call PSP_InitUpdate. - // So we need to finish and join the boot thread before we can exit. - _dbg_assert_(PollBootState() != BootState::Booting); + // We quit during boot (maybe from the background, without the graphics device coming back), + // and didn't end up in update again to call PSP_InitUpdate. So we need to finish and join the + // boot thread before we can exit. + while (PollBootState() == BootState::Booting) { + sleep_ms(10, "exit-during-boot"); + } // Make sure we join the boot thread, by calling PSP_InitUpdate. std::string error_string = "(unknown error)"; PSP_InitUpdate(&error_string); @@ -1361,13 +1364,7 @@ void EmuScreen::CreateViews() { } void EmuScreen::deviceLost() { - // If we are currently in the middle of boot, we have to block here! - // Otherwise the boot thread will encounter draw_ == nullptr and weird stuff like that. - // We're doing this in a very ugly way for now. - while (PollBootState() == BootState::Booting) { - sleep_ms(100, "device-lost-during-boot"); - } - + // A boot in progress doesn't create the GPU while the device is lost, see PSP_NotifyGraphicsDeviceLost. UIScreen::deviceLost(); } diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index db4d973c42..2a223794c8 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -978,6 +978,9 @@ bool NativeInitGraphics(GraphicsContext *graphicsContext) { ImGui_ImplThin3d_CreateDeviceObjects(g_draw); } + // Last, so that a GPU the boot thread creates from now on doesn't also get the DeviceRestore above. + PSP_NotifyGraphicsDeviceRestored(); + #if !PPSSPP_PLATFORM(UWP) // Now, rather than only at shutdown: on mobile the app can be killed without one. SaveFixedSPIRVCache(); @@ -1043,6 +1046,9 @@ bool CreateGlobalPipelines() { void NativeShutdownGraphics(GraphicsContext *graphicsContext) { INFO_LOG(Log::System, "NativeShutdownGraphics begin"); + // First, so that a boot that's creating its GPU finishes that before the DeviceLost calls below. + PSP_NotifyGraphicsDeviceLost(); + #if !PPSSPP_PLATFORM(UWP) SaveFixedSPIRVCache(); #endif