Cleanup a lot of the bootup state management.

This commit is contained in:
Henrik Rydgård
2025-03-30 14:02:29 +02:00
parent e6ea158dcc
commit 088a02bfdb
30 changed files with 239 additions and 257 deletions
+130 -111
View File
@@ -71,6 +71,7 @@
#include "GPU/GPUCommon.h"
#include "GPU/Debugger/Playback.h"
#include "GPU/Debugger/RecordFormat.h"
#include "UI/DiscordIntegration.h"
enum CPUThreadState {
CPU_THREAD_NOT_RUNNING,
@@ -94,11 +95,11 @@ static volatile CPUThreadState cpuThreadState = CPU_THREAD_NOT_RUNNING;
static GPUBackend gpuBackend;
static std::string gpuBackendDevice;
// Ugly!
static volatile bool pspIsInited = false;
static volatile bool pspIsIniting = false;
static volatile bool pspIsQuitting = false;
static volatile bool pspIsRebooting = false;
static BootState g_bootState = BootState::Off;
BootState PSP_GetBootState() {
return g_bootState;
}
void ResetUIState() {
globalUIState = UISTATE_MENU;
@@ -131,21 +132,7 @@ std::string GetGPUBackendDevice() {
return gpuBackendDevice;
}
bool CPU_IsReady() {
if (coreState == CORE_POWERUP)
return false;
return cpuThreadState == CPU_THREAD_RUNNING || cpuThreadState == CPU_THREAD_NOT_RUNNING;
}
bool CPU_IsShutdown() {
return cpuThreadState == CPU_THREAD_NOT_RUNNING;
}
bool CPU_HasPendingAction() {
return cpuThreadState != CPU_THREAD_RUNNING;
}
void CPU_Shutdown();
void CPU_Shutdown(bool success);
static Path SymbolMapFilename(const Path &currentFilename, const char *ext) {
File::FileInfo info{};
@@ -249,6 +236,24 @@ static void GetBootError(IdentifiedFileType type, std::string *errorString) {
}
}
static void ShowCompatWarnings(const Compatibility &compat) {
// UI changes are best done after PSP_InitStart.
if (compat.flags().RequireBufferedRendering && g_Config.bSkipBufferEffects && !g_Config.bSoftwareRendering) {
auto gr = GetI18NCategory(I18NCat::GRAPHICS);
g_OSD.Show(OSDType::MESSAGE_WARNING, gr->T("BufferedRenderingRequired", "Warning: This game requires Rendering Mode to be set to Buffered."), 10.0f);
}
if (compat.flags().RequireBlockTransfer && g_Config.iSkipGPUReadbackMode != (int)SkipGPUReadbackMode::NO_SKIP && !PSP_CoreParameter().compat.flags().ForceEnableGPUReadback) {
auto gr = GetI18NCategory(I18NCat::GRAPHICS);
g_OSD.Show(OSDType::MESSAGE_WARNING, gr->T("BlockTransferRequired", "Warning: This game requires Skip GPU Readbacks be set to No."), 10.0f);
}
if (compat.flags().RequireDefaultCPUClock && g_Config.iLockedCPUSpeed != 0) {
auto gr = GetI18NCategory(I18NCat::GRAPHICS);
g_OSD.Show(OSDType::MESSAGE_WARNING, gr->T("DefaultCPUClockRequired", "Warning: This game requires the CPU clock to be set to default."), 10.0f);
}
}
// NOTE: The loader has already been fully resolved (ResolveFileLoaderTarget) and identified here.
static bool CPU_Init(FileLoader *fileLoader, IdentifiedFileType type, std::string *errorString) {
// Default memory settings
@@ -263,6 +268,8 @@ static bool CPU_Init(FileLoader *fileLoader, IdentifiedFileType type, std::strin
std::string geDumpDiscID;
std::string gameTitle = "Unidentified PSP title";
switch (type) {
case IdentifiedFileType::PSP_ISO:
case IdentifiedFileType::PSP_ISO_NP:
@@ -294,6 +301,7 @@ static bool CPU_Init(FileLoader *fileLoader, IdentifiedFileType type, std::strin
if (DiscIDFromGEDumpPath(g_CoreParameter.fileToStart, fileLoader, &geDumpDiscID)) {
// Store in SFO, otherwise it'll generate a fake disc ID.
g_paramSFO.SetValue("DISC_ID", geDumpDiscID, 16);
gameTitle = g_CoreParameter.fileToStart.GetFilename();
}
break;
default:
@@ -306,6 +314,15 @@ static bool CPU_Init(FileLoader *fileLoader, IdentifiedFileType type, std::strin
}
}
// OK, pretty confident we have a PSP game.
if (g_paramSFO.IsValid()) {
gameTitle = SanitizeString(g_paramSFO.GetValueString("TITLE"), StringRestriction::NoLineBreaksOrSpecials);
}
std::string title = StringFromFormat("%s : %s", g_paramSFO.GetValueString("DISC_ID").c_str(), gameTitle.c_str());
INFO_LOG(Log::Loader, "%s", title.c_str());
System_SetWindowTitle(title);
currentMIPS = &mipsr4k;
g_symbolMap = new SymbolMap();
@@ -319,6 +336,7 @@ static bool CPU_Init(FileLoader *fileLoader, IdentifiedFileType type, std::strin
// Homebrew usually has an empty discID, and even if they do have a disc id, it's not
// likely to collide with any commercial ones.
g_CoreParameter.compat.Load(g_paramSFO.GetDiscID());
ShowCompatWarnings(g_CoreParameter.compat);
// Compat settings can override the software renderer, take care of that here.
if (g_Config.bSoftwareRendering || PSP_CoreParameter().compat.flags().ForceSoftwareRenderer) {
@@ -329,7 +347,6 @@ static bool CPU_Init(FileLoader *fileLoader, IdentifiedFileType type, std::strin
if (!Memory::Init()) {
// We're screwed.
*errorString = "Memory init failed";
CPU_Shutdown();
return false;
}
@@ -370,7 +387,10 @@ static bool CPU_Init(FileLoader *fileLoader, IdentifiedFileType type, std::strin
dir = ResolvePBPDirectory(Path(dir)).ToString();
pspFileSystem.SetStartingDirectory("ms0:/" + dir.substr(pos));
}
return Load_PSP_ELF_PBP(fileLoader, errorString);
if (!Load_PSP_ELF_PBP(fileLoader, errorString)) {
return false;
}
break;
}
// Looks like a wrong fall through but is not, both paths are handled above.
@@ -378,17 +398,26 @@ static bool CPU_Init(FileLoader *fileLoader, IdentifiedFileType type, std::strin
case IdentifiedFileType::PSP_ELF:
{
INFO_LOG(Log::Loader, "File is an ELF or loose PBP! %s", fileLoader->GetPath().c_str());
return Load_PSP_ELF_PBP(fileLoader, errorString);
if (!Load_PSP_ELF_PBP(fileLoader, errorString)) {
return false;
}
break;
}
case IdentifiedFileType::PSP_ISO:
case IdentifiedFileType::PSP_ISO_NP:
case IdentifiedFileType::PSP_DISC_DIRECTORY: // behaves the same as the mounting is already done by now
pspFileSystem.SetStartingDirectory("disc0:/PSP_GAME/USRDIR");
return Load_PSP_ISO(fileLoader, errorString);
if (!Load_PSP_ISO(fileLoader, errorString)) {
return false;
}
break;
case IdentifiedFileType::PPSSPP_GE_DUMP:
return Load_PSP_GE_Dump(fileLoader, errorString);
if (!Load_PSP_GE_Dump(fileLoader, errorString)) {
return false;
}
break;
default:
GetBootError(type, errorString);
@@ -405,20 +434,12 @@ static bool CPU_Init(FileLoader *fileLoader, IdentifiedFileType type, std::strin
return true;
}
PSP_LoadingLock::PSP_LoadingLock() {
loadingLock.lock();
}
PSP_LoadingLock::~PSP_LoadingLock() {
loadingLock.unlock();
}
void CPU_Shutdown() {
void CPU_Shutdown(bool success) {
UninstallExceptionHandler();
GPURecord::Replay_Unload();
if (g_Config.bAutoSaveSymbolMap) {
if (g_Config.bAutoSaveSymbolMap && success) {
SaveSymbolMapIfSupported();
}
@@ -473,30 +494,32 @@ void PSP_ForceDebugStats(bool enable) {
}
bool PSP_InitStart(const CoreParameter &coreParam) {
if (pspIsIniting || pspIsQuitting) {
ERROR_LOG(Log::System, "Can't start loader thread - initing or quitting");
if (g_bootState != BootState::Off) {
ERROR_LOG(Log::System, "Can't start loader thread - already on.");
return false;
}
_dbg_assert_(coreState != CORE_POWERUP);
coreState = CORE_POWERUP;
g_bootState = BootState::Booting;
GraphicsContext *temp = g_CoreParameter.graphicsContext;
g_CoreParameter = coreParam;
if (g_CoreParameter.graphicsContext == nullptr) {
g_CoreParameter.graphicsContext = temp;
}
g_CoreParameter.errorString.clear();
pspIsIniting = true;
std::string *error_string = &g_CoreParameter.errorString;
INFO_LOG(Log::System, "Starting loader thread...");
_dbg_assert_(!g_loadingThread.joinable());
g_loadingThread = std::thread([error_string]() {
SetCurrentThreadName("ExecLoader");
PSP_LoadingLock guard;
if (coreState != CORE_POWERUP)
return;
AndroidJNIThreadContext jniContext;
@@ -532,14 +555,14 @@ bool PSP_InitStart(const CoreParameter &coreParam) {
// TODO: The reason we pass in g_CoreParameter.errorString here is that it's persistent -
// it gets written to from the loader thread that gets spawned.
if (!CPU_Init(loadedFile, type, &g_CoreParameter.errorString)) {
CPU_Shutdown();
CPU_Shutdown(false);
coreState = CORE_BOOT_ERROR;
g_CoreParameter.fileToStart.clear();
*error_string = g_CoreParameter.errorString;
if (error_string->empty()) {
*error_string = "Failed initializing CPU/Memory";
}
pspIsIniting = false;
g_bootState = BootState::Failed;
return;
}
@@ -549,101 +572,84 @@ bool PSP_InitStart(const CoreParameter &coreParam) {
} else {
coreState = CORE_RUNNING_CPU;
}
g_bootState = BootState::Complete;
});
return true;
}
bool PSP_InitUpdate(std::string *error_string) {
if (pspIsInited || !pspIsIniting) {
return true;
BootState PSP_InitUpdate(std::string *error_string) {
if (g_bootState == BootState::Booting || g_bootState == BootState::Off) {
// We're done already.
return g_bootState;
}
if (!CPU_IsReady()) {
return false;
}
bool success = !g_CoreParameter.fileToStart.empty();
if (!g_CoreParameter.errorString.empty()) {
*error_string = g_CoreParameter.errorString;
}
_dbg_assert_(g_bootState == BootState::Complete || g_bootState == BootState::Failed);
// Since we load on a background thread, wait for startup to complete.
_dbg_assert_(g_loadingThread.joinable());
g_loadingThread.join();
if (success && gpu == nullptr) {
if (g_bootState == BootState::Failed) {
// Failed! (Note: PSP_Shutdown was already called on the loader thread).
Core_NotifyLifecycle(CoreLifecycle::START_COMPLETE);
*error_string = g_CoreParameter.errorString;
return g_bootState;
}
// Ok, async boot completed, let's finish up things on the main thread.
if (!gpu) { // should be!
INFO_LOG(Log::System, "Starting graphics...");
Draw::DrawContext *draw = g_CoreParameter.graphicsContext ? g_CoreParameter.graphicsContext->GetDrawContext() : nullptr;
success = GPU_Init(g_CoreParameter.graphicsContext, draw);
bool success = GPU_Init(g_CoreParameter.graphicsContext, draw);
if (!success) {
*error_string = "Unable to initialize rendering engine.";
PSP_Shutdown(false);
g_bootState = BootState::Failed;
return g_bootState;
}
}
if (!success) {
pspIsRebooting = false;
PSP_Shutdown();
return true;
// TODO: This should all be checked during GPU_Init.
if (!GPU_IsStarted()) {
*error_string = "Unable to initialize rendering engine.";
PSP_Shutdown(false);
g_bootState = BootState::Failed;
}
pspIsInited = GPU_IsReady();
pspIsIniting = !pspIsInited;
if (pspIsInited) {
Core_NotifyLifecycle(CoreLifecycle::START_COMPLETE);
pspIsRebooting = false;
// If GPU init failed during IsReady checks, bail.
if (!GPU_IsStarted()) {
*error_string = "Unable to initialize rendering engine.";
pspIsRebooting = false;
PSP_Shutdown();
return true;
}
}
return pspIsInited;
Core_NotifyLifecycle(CoreLifecycle::START_COMPLETE);
return g_bootState;
}
// Most platforms should not use this one, they should call PSP_InitStart and then do their thing
// while repeatedly calling PSP_InitUpdate. This is basically just for libretro convenience.
bool PSP_Init(const CoreParameter &coreParam, std::string *error_string) {
BootState PSP_Init(const CoreParameter &coreParam, std::string *error_string) {
// InitStart doesn't really fail anymore.
if (!PSP_InitStart(coreParam))
return false;
return BootState::Failed;
while (!PSP_InitUpdate(error_string))
sleep_ms(10, "psp-init-poll");
return pspIsInited;
while (true) {
BootState state = PSP_InitUpdate(error_string);
if (state != BootState::Booting) {
return state;
}
sleep_ms(5, "psp-init-poll");
}
}
bool PSP_IsIniting() {
return pspIsIniting;
}
bool PSP_IsInited() {
return pspIsInited && !pspIsQuitting && !pspIsRebooting;
}
bool PSP_IsRebooting() {
return pspIsRebooting;
}
bool PSP_IsQuitting() {
return pspIsQuitting;
}
void PSP_Shutdown() {
void PSP_Shutdown(bool success) {
// Reduce the risk for weird races with the Windows GE debugger.
gpuDebug = nullptr;
Achievements::UnloadGame();
// Do nothing if we never inited.
if (!pspIsInited && !pspIsIniting && !pspIsQuitting) {
if (g_bootState == BootState::Off) {
return;
}
// Make sure things know right away that PSP memory, etc. is going away.
pspIsQuitting = !pspIsRebooting;
if (coreState == CORE_RUNNING_CPU)
Core_Stop();
@@ -651,29 +657,42 @@ void PSP_Shutdown() {
MIPSAnalyst::StoreHashMap();
}
if (pspIsIniting)
if (g_bootState == BootState::Booting) {
// This should only happen during failures.
Core_NotifyLifecycle(CoreLifecycle::START_COMPLETE);
}
Core_NotifyLifecycle(CoreLifecycle::STOPPING);
CPU_Shutdown();
CPU_Shutdown(success);
GPU_Shutdown();
g_paramSFO.Clear();
System_SetWindowTitle("");
currentMIPS = 0;
pspIsInited = false;
pspIsIniting = false;
pspIsQuitting = false;
currentMIPS = nullptr;
g_Config.unloadGameConfig();
Core_NotifyLifecycle(CoreLifecycle::STOPPED);
if (success) {
g_bootState = BootState::Off;
}
}
bool PSP_Reboot(std::string *error_string) {
if (!pspIsInited || pspIsQuitting)
return false;
// Call this after handling BootState::Failed.
void PSP_CancelBoot() {
_dbg_assert_(g_bootState == BootState::Failed);
g_bootState = BootState::Off;
}
BootState PSP_Reboot(std::string *error_string) {
if (g_bootState != BootState::Complete) {
return g_bootState;
}
pspIsRebooting = true;
Core_Stop();
Core_WaitInactive();
PSP_Shutdown();
PSP_Shutdown(true);
std::string resetError;
return PSP_Init(PSP_CoreParameter(), error_string);
}