From 9a86101769408a8b4b31c5fa510dbb9630c8ce39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 16 May 2025 00:34:10 +0200 Subject: [PATCH] Avoid getting stuck in a load-loop when auto-loading a bad savestate. Make some code clearer. --- Core/SaveState.cpp | 9 ++--- Core/SaveState.h | 4 +- Core/System.cpp | 13 ------- Core/System.h | 4 +- UI/EmuScreen.cpp | 80 ++++++++++++++++++++++++++------------ UI/EmuScreen.h | 1 + android/jni/TestRunner.cpp | 3 -- headless/Headless.cpp | 11 ++++-- 8 files changed, 70 insertions(+), 55 deletions(-) diff --git a/Core/SaveState.cpp b/Core/SaveState.cpp index 32a30acf02..f70098d4d5 100644 --- a/Core/SaveState.cpp +++ b/Core/SaveState.cpp @@ -1139,15 +1139,12 @@ double g_lastSaveTime = -1.0; lastSaveDataGeneration = saveDataGeneration; } - void Cleanup() { - // TODO: Handle this better. + bool PollRestartNeeded() { if (needsRestart) { - std::string error_string; - PSP_Reboot(&error_string); - System_Notify(SystemNotification::BOOT_DONE); - System_Notify(SystemNotification::DISASSEMBLY); needsRestart = false; + return true; } + return false; } void Init() diff --git a/Core/SaveState.h b/Core/SaveState.h index 8260dc15fa..abc0d157bc 100644 --- a/Core/SaveState.h +++ b/Core/SaveState.h @@ -108,8 +108,8 @@ namespace SaveState { // Notify save state code that new save data has been written. void NotifySaveData(); - // Cleanup by triggering a restart if needed. - void Cleanup(); + // Checks whether a bad load required the caller to trigger a restart (and if returns true, resets the flag internally). + bool PollRestartNeeded(); // Returns the time since last save. -1 if N/A. double SecondsSinceLastSavestate(); diff --git a/Core/System.cpp b/Core/System.cpp index d785e0835f..b2e41a986f 100644 --- a/Core/System.cpp +++ b/Core/System.cpp @@ -702,19 +702,6 @@ BootState PSP_Reboot(std::string *error_string) { return PSP_Init(PSP_CoreParameter(), error_string); } -void PSP_BeginHostFrame() { - if (gpu) { - gpu->BeginHostFrame(); - } -} - -void PSP_EndHostFrame() { - if (gpu) { - gpu->EndHostFrame(); - } - SaveState::Cleanup(); -} - void PSP_RunLoopWhileState() { // We just run the CPU until we get to vblank. This will quickly sync up pretty nicely. // The actual number of cycles doesn't matter so much here as we will break due to CORE_NEXTFRAME, most of the time hopefully... diff --git a/Core/System.h b/Core/System.h index db3078eab7..6e964e0433 100644 --- a/Core/System.h +++ b/Core/System.h @@ -102,12 +102,10 @@ BootState PSP_Reboot(std::string *error_string); FileLoader *PSP_LoadedFile(); -void PSP_BeginHostFrame(); -void PSP_EndHostFrame(); void PSP_RunLoopWhileState(); void PSP_RunLoopFor(int cycles); -// Call before PSP_BeginHostFrame() in order to not miss any GPU stats. +// Call before gpu->BeginHostFrame() in order to not miss any GPU stats. void PSP_UpdateDebugStats(bool collectStats); // Increments or decrements an internal counter. Intended to be used by debuggers. void PSP_ForceDebugStats(bool enable); diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index 7212b49f69..b9fb70f0ec 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -545,6 +545,7 @@ void EmuScreen::sendMessage(UIMessage message, const char *value) { RecreateViews(); _dbg_assert_(coreState == CORE_POWERDOWN); if (!PSP_InitStart(PSP_CoreParameter())) { + bootPending_ = false; WARN_LOG(Log::Loader, "Error resetting"); screenManager()->switchScreen(new MainScreen()); return; @@ -773,17 +774,6 @@ void EmuScreen::onVKey(VirtKey virtualKeyCode, bool down) { } break; - case VIRTKEY_PAUSE_NO_MENU: - if (down && !NetworkWarnUserIfOnlineAndCantSpeed()) { - // We re-use debug break/resume to implement pause/resume without a menu. - if (coreState == CORE_STEPPING_CPU) { // should we check reason? - Core_Resume(); - } else { - Core_Break(BreakReason::UIPause); - } - } - break; - case VIRTKEY_RESET_EMULATION: if (down) { System_PostUIMessage(UIMessage::REQUEST_GAME_RESET); @@ -812,15 +802,6 @@ void EmuScreen::onVKey(VirtKey virtualKeyCode, bool down) { break; #endif - case VIRTKEY_REWIND: - if (down && !Achievements::WarnUserIfHardcoreModeActive(false) && !NetworkWarnUserIfOnlineAndCantSavestate() && !bootPending_) { - if (SaveState::CanRewind()) { - SaveState::Rewind(&AfterSaveStateAction); - } else { - g_OSD.Show(OSDType::MESSAGE_WARNING, sc->T("norewind", "No rewind save states available"), 2.0); - } - } - break; case VIRTKEY_SAVE_STATE: if (down && !Achievements::WarnUserIfHardcoreModeActive(true) && !NetworkWarnUserIfOnlineAndCantSavestate() && !bootPending_) { SaveState::SaveSlot(gamePath_, g_Config.iCurrentStateSlot, &AfterSaveStateAction); @@ -964,6 +945,27 @@ void EmuScreen::ProcessVKey(VirtKey virtKey) { } break; + case VIRTKEY_REWIND: + if (!Achievements::WarnUserIfHardcoreModeActive(false) && !NetworkWarnUserIfOnlineAndCantSavestate() && !bootPending_) { + if (SaveState::CanRewind()) { + SaveState::Rewind(&AfterSaveStateAction); + } else { + g_OSD.Show(OSDType::MESSAGE_WARNING, sc->T("norewind", "No rewind save states available"), 2.0); + } + } + break; + + case VIRTKEY_PAUSE_NO_MENU: + if (!NetworkWarnUserIfOnlineAndCantSpeed()) { + // We re-use debug break/resume to implement pause/resume without a menu. + if (coreState == CORE_STEPPING_CPU) { // should we check reason? + Core_Resume(); + } else { + Core_Break(BreakReason::UIPause); + } + } + break; + case VIRTKEY_EXIT_APP: { std::string confirmExitMessage = GetConfirmExitMessage(); @@ -1578,9 +1580,9 @@ ScreenRenderFlags EmuScreen::render(ScreenRenderMode mode) { // Just to make sure. if (PSP_IsInited() && !skipBufferEffects) { _dbg_assert_(gpu); - PSP_BeginHostFrame(); + gpu->BeginHostFrame(); gpu->CopyDisplayToOutput(true); - PSP_EndHostFrame(); + gpu->EndHostFrame(); } if (gpu && gpu->PresentedThisFrame()) { framebufferBound = true; @@ -1644,7 +1646,9 @@ ScreenRenderFlags EmuScreen::render(ScreenRenderMode mode) { bool blockedExecution = Achievements::IsBlockingExecution(); uint32_t clearColor = 0; if (!blockedExecution) { - PSP_BeginHostFrame(); + if (gpu) { + gpu->BeginHostFrame(); + } if (SaveState::Process()) { // We might have lost the framebuffer bind if we had one, due to a readback. if (framebufferBound) { @@ -1697,7 +1701,24 @@ ScreenRenderFlags EmuScreen::render(ScreenRenderMode mode) { break; } - PSP_EndHostFrame(); + if (gpu) { + gpu->EndHostFrame(); + } + + if (SaveState::PollRestartNeeded() && !bootPending_) { + PSP_Shutdown(true); + Achievements::UnloadGame(); + + // Restart the boot process + bootPending_ = true; + RecreateViews(); + _dbg_assert_(coreState == CORE_POWERDOWN); + if (!PSP_InitStart(PSP_CoreParameter())) { + bootPending_ = false; + WARN_LOG(Log::Loader, "Error resetting"); + screenManager()->switchScreen(new MainScreen()); + } + } } if (frameStep_) { @@ -1942,6 +1963,10 @@ void EmuScreen::renderUI() { } void EmuScreen::AutoLoadSaveState() { + if (autoLoadFailed_) { + return; + } + int autoSlot = -1; //check if save state has save, if so, load @@ -1960,7 +1985,12 @@ void EmuScreen::AutoLoadSaveState() { } if (g_Config.iAutoLoadSaveState && autoSlot != -1) { - SaveState::LoadSlot(gamePath_, autoSlot, &AfterSaveStateAction); + SaveState::LoadSlot(gamePath_, autoSlot, [this](SaveState::Status status, std::string_view message) { + AfterSaveStateAction(status, message); + if (status == SaveState::Status::FAILURE) { + autoLoadFailed_ = true; + } + }); g_Config.iCurrentStateSlot = autoSlot; } } diff --git a/UI/EmuScreen.h b/UI/EmuScreen.h index ae84e3708d..5baa7f85f8 100644 --- a/UI/EmuScreen.h +++ b/UI/EmuScreen.h @@ -155,6 +155,7 @@ private: #ifndef MOBILE_DEVICE bool startDumping_ = false; #endif + bool autoLoadFailed_ = false; // to prevent repeat reloads }; bool MustRunBehind(); diff --git a/android/jni/TestRunner.cpp b/android/jni/TestRunner.cpp index ed0d9a7037..7779e8d08e 100644 --- a/android/jni/TestRunner.cpp +++ b/android/jni/TestRunner.cpp @@ -127,8 +127,6 @@ bool RunTests() { return false; } - PSP_BeginHostFrame(); - // Run the emu until the test exits INFO_LOG(Log::System, "Test: Entering runloop."); while (true) { @@ -145,7 +143,6 @@ bool RunTests() { break; } } - PSP_EndHostFrame(); std::string expect_results; if (!File::ReadTextFileToString(expectedFile, &expect_results)) { diff --git a/headless/Headless.cpp b/headless/Headless.cpp index 40b57e9877..225e427329 100644 --- a/headless/Headless.cpp +++ b/headless/Headless.cpp @@ -215,10 +215,13 @@ bool RunAutoTest(HeadlessHost *headlessHost, CoreParameter &coreParameter, const PSP_UpdateDebugStats((DebugOverlay)g_Config.iDebugOverlay == DebugOverlay::DEBUG_STATS || g_Config.bLogFrameDrops); - PSP_BeginHostFrame(); + if (gpu) { + gpu->BeginHostFrame(); + } Draw::DrawContext *draw = coreParameter.graphicsContext ? coreParameter.graphicsContext->GetDrawContext() : nullptr; - if (draw) + if (draw) { draw->BeginFrame(Draw::DebugFlags::NONE); + } bool passed = true; double deadline = time_now_d() + opt.timeout; @@ -255,7 +258,9 @@ bool RunAutoTest(HeadlessHost *headlessHost, CoreParameter &coreParameter, const Core_Stop(); } } - PSP_EndHostFrame(); + if (gpu) { + gpu->EndHostFrame(); + } if (draw) { draw->BindFramebufferAsRenderTarget(nullptr, { Draw::RPAction::CLEAR, Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE }, "Headless");