diff --git a/Common/ExceptionHandlerSetup.h b/Common/ExceptionHandlerSetup.h index 8f88176e0e..e7917cb3f2 100644 --- a/Common/ExceptionHandlerSetup.h +++ b/Common/ExceptionHandlerSetup.h @@ -14,4 +14,6 @@ typedef bool (*BadAccessHandler)(uintptr_t address, void *context); void InstallExceptionHandler(BadAccessHandler accessHandler); + +// Implementation note: This must be a no-op if InstallExceptionHandler hasn't been called. void UninstallExceptionHandler(); diff --git a/Core/HLE/sceKernelMemory.cpp b/Core/HLE/sceKernelMemory.cpp index 160bb66f4c..1750db1f71 100644 --- a/Core/HLE/sceKernelMemory.cpp +++ b/Core/HLE/sceKernelMemory.cpp @@ -434,7 +434,7 @@ void __KernelMemoryInit() kernelMemory.Init(PSP_GetKernelMemoryBase(), PSP_GetKernelMemoryEnd() - PSP_GetKernelMemoryBase(), false); userMemory.Init(PSP_GetUserMemoryBase(), PSP_GetUserMemoryEnd() - PSP_GetUserMemoryBase(), false); volatileMemory.Init(PSP_GetVolatileMemoryStart(), PSP_GetVolatileMemoryEnd() - PSP_GetVolatileMemoryStart(), false); - ParallelMemset(&g_threadManager, Memory::GetPointerWrite(PSP_GetKernelMemoryBase()), 0, PSP_GetUserMemoryEnd() - PSP_GetKernelMemoryBase(), TaskPriority::HIGH); + Memory::Memset(PSP_GetKernelMemoryBase(), 0, PSP_GetUserMemoryEnd() - PSP_GetKernelMemoryBase()); NotifyMemInfo(MemBlockFlags::WRITE, PSP_GetKernelMemoryBase(), PSP_GetUserMemoryEnd() - PSP_GetKernelMemoryBase(), "MemInit"); INFO_LOG(Log::sceKernel, "Kernel and user memory pools initialized"); diff --git a/Core/PSPLoaders.cpp b/Core/PSPLoaders.cpp index ba088648fd..ff24783d97 100644 --- a/Core/PSPLoaders.cpp +++ b/Core/PSPLoaders.cpp @@ -15,10 +15,7 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. -#include - #include "Core/Core.h" -#include "Common/Thread/ThreadUtil.h" #include "Common/System/Request.h" #include "Common/File/AndroidContentURI.h" @@ -49,8 +46,6 @@ #include "Core/PSPLoaders.h" #include "Core/HLE/sceKernelModule.h" -static std::thread g_loadingThread; - static void UseLargeMem(int memsize) { if (memsize != 1) { // Nothing requested. @@ -252,7 +247,6 @@ bool Load_PSP_ISO(FileLoader *fileLoader, std::string *error_string) { } else { *error_string = "A PSP game couldn't be found on the disc."; } - coreState = CORE_BOOT_ERROR; return false; } @@ -270,38 +264,11 @@ bool Load_PSP_ISO(FileLoader *fileLoader, std::string *error_string) { g_Config.loadGameConfig(id, g_paramSFO.GetValueString("TITLE")); System_PostUIMessage(UIMessage::CONFIG_LOADED); INFO_LOG(Log::Loader, "Loading %s...", bootpath.c_str()); - - PSPLoaders_Shutdown(); - // Note: this thread reads the game binary, loads caches, and links HLE while UI spins. - // To do something deterministically when the game starts, disabling this thread won't be enough. - // Instead: Use Core_ListenLifecycle() or watch coreState. - g_loadingThread = std::thread([bootpath] { - SetCurrentThreadName("ExecLoader"); - PSP_LoadingLock guard; - if (coreState != CORE_POWERUP) - return; - - AndroidJNIThreadContext jniContext; - - INFO_LOG(Log::System, "Loading executable..."); - // TODO: We can't use the initial error_string pointer. - bool success = __KernelLoadExec(bootpath.c_str(), 0, &PSP_CoreParameter().errorString); - if (success && coreState == CORE_POWERUP) { - if (PSP_CoreParameter().startBreak) { - coreState = CORE_STEPPING_CPU; - System_Notify(SystemNotification::DEBUG_MODE_CHANGE); - } else { - coreState = CORE_RUNNING_CPU; - } - } else { - coreState = CORE_BOOT_ERROR; - // TODO: This is a crummy way to communicate the error... - PSP_CoreParameter().fileToStart.clear(); - } - }); - return true; + // TODO: We can't use the initial error_string pointer. + return __KernelLoadExec(bootpath.c_str(), 0, &PSP_CoreParameter().errorString); } +// TODO: Move this to common. Merge with ResolvePath? static Path NormalizePath(const Path &path) { if (path.Type() != PathType::NATIVE) { // Nothing to do - these can't be non-normalized. @@ -375,7 +342,6 @@ bool Load_PSP_ELF_PBP(FileLoader *fileLoader, std::string *error_string) { // If root is not a subpath of path, we can't boot the game. if (!pathNorm.StartsWith(rootNorm)) { *error_string = "Cannot boot ELF located outside mountRoot."; - coreState = CORE_BOOT_ERROR; return false; } @@ -438,65 +404,12 @@ bool Load_PSP_ELF_PBP(FileLoader *fileLoader, std::string *error_string) { File::Rename(oldNamePrefix.WithExtraExtension(".jpg"), newPrefix.WithExtraExtension(".jpg")); } - PSPLoaders_Shutdown(); - // Note: See Load_PSP_ISO for notes about this thread. - g_loadingThread = std::thread([finalName] { - SetCurrentThreadName("ExecLoader"); - PSP_LoadingLock guard; - if (coreState != CORE_POWERUP) - return; - - AndroidJNIThreadContext jniContext; - - bool success = __KernelLoadExec(finalName.c_str(), 0, &PSP_CoreParameter().errorString); - if (success && coreState == CORE_POWERUP) { - if (PSP_CoreParameter().startBreak) { - coreState = CORE_STEPPING_CPU; - System_Notify(SystemNotification::DEBUG_MODE_CHANGE); - } else { - coreState = CORE_RUNNING_CPU; - } - } else { - coreState = CORE_BOOT_ERROR; - // TODO: This is a crummy way to communicate the error... - PSP_CoreParameter().fileToStart.clear(); - } - }); - return true; + return __KernelLoadExec(finalName.c_str(), 0, error_string); } bool Load_PSP_GE_Dump(FileLoader *fileLoader, std::string *error_string) { auto umd = std::make_shared(&pspFileSystem, fileLoader, "data.ppdmp"); pspFileSystem.Mount("disc0:", umd); - PSPLoaders_Shutdown(); - // Note: See Load_PSP_ISO for notes about this thread. - g_loadingThread = std::thread([] { - SetCurrentThreadName("ExecLoader"); - PSP_LoadingLock guard; - if (coreState != CORE_POWERUP) - return; - - AndroidJNIThreadContext jniContext; - - bool success = __KernelLoadGEDump("disc0:/data.ppdmp", &PSP_CoreParameter().errorString); - if (success && coreState == CORE_POWERUP) { - if (PSP_CoreParameter().startBreak) { - coreState = CORE_STEPPING_CPU; - System_Notify(SystemNotification::DEBUG_MODE_CHANGE); - } else { - coreState = CORE_RUNNING_CPU; - } - } else { - coreState = CORE_BOOT_ERROR; - // TODO: This is a crummy way to communicate the error... - PSP_CoreParameter().fileToStart.clear(); - } - }); - return true; -} - -void PSPLoaders_Shutdown() { - if (g_loadingThread.joinable()) - g_loadingThread.join(); + return __KernelLoadGEDump("disc0:/data.ppdmp", &PSP_CoreParameter().errorString); } diff --git a/Core/RetroAchievements.cpp b/Core/RetroAchievements.cpp index 21164a36ca..3ad961b140 100644 --- a/Core/RetroAchievements.cpp +++ b/Core/RetroAchievements.cpp @@ -966,10 +966,6 @@ void identify_and_load_callback(int result, const char *error_message, rc_client g_isIdentifying = false; } -bool IsReadyToStart() { - return !g_isLoggingIn; -} - void SetGame(const Path &path, IdentifiedFileType fileType, FileLoader *fileLoader) { bool homebrew = false; switch (fileType) { diff --git a/Core/RetroAchievements.h b/Core/RetroAchievements.h index 85034abea2..0ec638765c 100644 --- a/Core/RetroAchievements.h +++ b/Core/RetroAchievements.h @@ -96,7 +96,6 @@ bool HasAchievementsOrLeaderboards(); bool LoginAsync(const char *username, const char *password); void Logout(); -bool IsReadyToStart(); void SetGame(const Path &path, IdentifiedFileType fileType, FileLoader *fileLoader); void ChangeUMD(const Path &path, FileLoader *fileLoader); // for in-game UMD change void UnloadGame(); // Call when leaving a game. diff --git a/Core/System.cpp b/Core/System.cpp index 2be1cb8007..0bc36fd7f0 100644 --- a/Core/System.cpp +++ b/Core/System.cpp @@ -40,6 +40,7 @@ #include "Common/File/DirListing.h" #include "Common/File/AndroidContentURI.h" #include "Common/TimeUtil.h" +#include "Common/Thread/ThreadUtil.h" #include "Common/GraphicsContext.h" #include "Core/RetroAchievements.h" #include "Core/MemFault.h" @@ -83,6 +84,7 @@ CoreParameter g_CoreParameter; static FileLoader *g_loadedFile; // For background loading thread. static std::mutex loadingLock; +static std::thread g_loadingThread; bool coreCollectDebugStats = false; static int coreCollectDebugStatsCounter = 0; @@ -218,7 +220,7 @@ static void GetBootError(IdentifiedFileType type, std::string *errorString) { #ifdef WIN32 *errorString = "RAR file detected (Require WINRAR)"; #else - *error_string = "RAR file detected (Require UnRAR)"; + *errorString = "RAR file detected (Require UnRAR)"; #endif break; @@ -226,7 +228,7 @@ static void GetBootError(IdentifiedFileType type, std::string *errorString) { #ifdef WIN32 *errorString = "ZIP file detected (Require WINRAR)"; #else - *error_string = "ZIP file detected (Require UnRAR)"; + *errorString = "ZIP file detected (Require UnRAR)"; #endif break; @@ -249,8 +251,6 @@ static void GetBootError(IdentifiedFileType type, std::string *errorString) { // NOTE: The loader has already been fully resolved (ResolveFileLoaderTarget) and identified here. static bool CPU_Init(FileLoader *fileLoader, IdentifiedFileType type, std::string *errorString) { - coreState = CORE_POWERUP; - // Default memory settings // Seems to be the safest place currently.. Memory::g_MemorySize = Memory::RAM_NORMAL_SIZE; // 32 MB of ram by default @@ -416,10 +416,6 @@ PSP_LoadingLock::~PSP_LoadingLock() { void CPU_Shutdown() { UninstallExceptionHandler(); - // Since we load on a background thread, wait for startup to complete. - PSP_LoadingLock lock; - PSPLoaders_Shutdown(); - GPURecord::Replay_Unload(); if (g_Config.bAutoSaveSymbolMap) { @@ -441,15 +437,14 @@ void CPU_Shutdown() { g_loadedFile = nullptr; delete g_CoreParameter.mountIsoLoader; + g_CoreParameter.mountIsoLoader = nullptr; delete g_symbolMap; g_symbolMap = nullptr; g_lua.Shutdown(); - - g_CoreParameter.mountIsoLoader = nullptr; } -// TODO: Maybe loadedFile doesn't even belong here... +// Used for UMD switching only. void UpdateLoadedFile(FileLoader *fileLoader) { delete g_loadedFile; g_loadedFile = fileLoader; @@ -477,20 +472,14 @@ void PSP_ForceDebugStats(bool enable) { _assert_(coreCollectDebugStatsCounter >= 0); } -bool PSP_InitStart(const CoreParameter &coreParam, std::string *error_string) { +bool PSP_InitStart(const CoreParameter &coreParam) { if (pspIsIniting || pspIsQuitting) { + ERROR_LOG(Log::System, "Can't start loader thread - initing or quitting"); return false; } - if (!Achievements::IsReadyToStart()) { - return false; - } + coreState = CORE_POWERUP; - // TODO: Move almost all of this into the thread. - - NOTICE_LOG(Log::Boot, "PPSSPP %s", PPSSPP_GIT_VERSION); - - Core_NotifyLifecycle(CoreLifecycle::STARTING); GraphicsContext *temp = g_CoreParameter.graphicsContext; g_CoreParameter = coreParam; if (g_CoreParameter.graphicsContext == nullptr) { @@ -499,47 +488,69 @@ bool PSP_InitStart(const CoreParameter &coreParam, std::string *error_string) { g_CoreParameter.errorString.clear(); pspIsIniting = true; - Path filename = g_CoreParameter.fileToStart; - FileLoader *loadedFile = ResolveFileLoaderTarget(ConstructFileLoader(filename)); + std::string *error_string = &g_CoreParameter.errorString; - IdentifiedFileType type = Identify_File(loadedFile, &g_CoreParameter.errorString); - g_CoreParameter.fileType = type; + INFO_LOG(Log::System, "Starting loader thread..."); - if (System_GetPropertyBool(SYSPROP_ENOUGH_RAM_FOR_FULL_ISO)) { - if (g_Config.bCacheFullIsoInRam) { - switch (g_CoreParameter.fileType) { - case IdentifiedFileType::PSP_ISO: - case IdentifiedFileType::PSP_ISO_NP: - loadedFile = new RamCachingFileLoader(loadedFile); - break; - default: - INFO_LOG(Log::System, "RAM caching is on, but file is not an ISO, so ignoring"); - break; + g_loadingThread = std::thread([error_string]() { + SetCurrentThreadName("ExecLoader"); + PSP_LoadingLock guard; + if (coreState != CORE_POWERUP) + return; + + AndroidJNIThreadContext jniContext; + + NOTICE_LOG(Log::Boot, "PPSSPP %s", PPSSPP_GIT_VERSION); + + Core_NotifyLifecycle(CoreLifecycle::STARTING); + + Path filename = g_CoreParameter.fileToStart; + FileLoader *loadedFile = ResolveFileLoaderTarget(ConstructFileLoader(filename)); + + IdentifiedFileType type = Identify_File(loadedFile, &g_CoreParameter.errorString); + g_CoreParameter.fileType = type; + + if (System_GetPropertyBool(SYSPROP_ENOUGH_RAM_FOR_FULL_ISO)) { + if (g_Config.bCacheFullIsoInRam) { + switch (g_CoreParameter.fileType) { + case IdentifiedFileType::PSP_ISO: + case IdentifiedFileType::PSP_ISO_NP: + loadedFile = new RamCachingFileLoader(loadedFile); + break; + default: + INFO_LOG(Log::System, "RAM caching is on, but file is not an ISO, so ignoring"); + break; + } } } - } - if (g_Config.bAchievementsEnable) { - // Need to re-identify after ResolveFileLoaderTarget - although in practice probably not, - // but also, re-using the identification would require some plumbing, to be done later. - std::string errorString; - Achievements::SetGame(filename, type, loadedFile); - } - - // 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(); - *error_string = g_CoreParameter.errorString; - if (error_string->empty()) { - *error_string = "Failed initializing CPU/Memory"; + if (g_Config.bAchievementsEnable) { + std::string errorString; + Achievements::SetGame(filename, type, loadedFile); } - pspIsIniting = false; - return false; - } - // After CPU_Init returns, the loader thread will keep working for a bit, while we exit here and come back later through - // PSP_InitUpdate. + // 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(); + 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; + return; + } + + if (PSP_CoreParameter().startBreak) { + coreState = CORE_STEPPING_CPU; + System_Notify(SystemNotification::DEBUG_MODE_CHANGE); + } else { + coreState = CORE_RUNNING_CPU; + } + }); + return true; } @@ -557,6 +568,10 @@ bool PSP_InitUpdate(std::string *error_string) { *error_string = g_CoreParameter.errorString; } + // Since we load on a background thread, wait for startup to complete. + _dbg_assert_(g_loadingThread.joinable()); + g_loadingThread.join(); + if (success && gpu == nullptr) { INFO_LOG(Log::System, "Starting graphics..."); Draw::DrawContext *draw = g_CoreParameter.graphicsContext ? g_CoreParameter.graphicsContext->GetDrawContext() : nullptr; @@ -591,7 +606,8 @@ bool PSP_InitUpdate(std::string *error_string) { // 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) { - if (!PSP_InitStart(coreParam, error_string)) + // InitStart doesn't really fail anymore. + if (!PSP_InitStart(coreParam)) return false; while (!PSP_InitUpdate(error_string)) diff --git a/Core/System.h b/Core/System.h index a08309be54..b5b451f23e 100644 --- a/Core/System.h +++ b/Core/System.h @@ -72,7 +72,7 @@ GPUBackend GetGPUBackend(); std::string GetGPUBackendDevice(); bool PSP_Init(const CoreParameter &coreParam, std::string *error_string); -bool PSP_InitStart(const CoreParameter &coreParam, std::string *error_string); +bool PSP_InitStart(const CoreParameter &coreParam); bool PSP_InitUpdate(std::string *error_string); bool PSP_IsIniting(); bool PSP_IsInited(); diff --git a/Core/Util/PPGeDraw.cpp b/Core/Util/PPGeDraw.cpp index 834c92d526..a442202f43 100644 --- a/Core/Util/PPGeDraw.cpp +++ b/Core/Util/PPGeDraw.cpp @@ -251,6 +251,8 @@ void __PPGeInit() { int height[12]{}; int flags = 0; + // TODO: Load the atlas on a thread! + bool loadedZIM = !skipZIM && LoadZIM("ppge_atlas.zim", width, height, &flags, imageData); if (!skipZIM && !loadedZIM) { ERROR_LOG(Log::sceGe, "Failed to load ppge_atlas.zim.\n\nPlace it in the directory \"assets\" under your PPSSPP directory.\n\nPPGe stuff will not be drawn."); diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index c4125d9c91..2d8e8874d1 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -355,10 +355,9 @@ void EmuScreen::bootGame(const Path &filename) { coreParam.pixelWidth = g_display.pixel_xres; coreParam.pixelHeight = g_display.pixel_yres; - std::string error_string; - if (!PSP_InitStart(coreParam, &error_string)) { + // PSP_InitStart can't really fail anymore, unless it's called at the wrong time. It just starts the loader thread. + if (!PSP_InitStart(coreParam)) { bootPending_ = false; - errorMessage_ = error_string; ERROR_LOG(Log::Boot, "InitStart bootGame error: %s", errorMessage_.c_str()); } @@ -552,10 +551,8 @@ void EmuScreen::sendMessage(UIMessage message, const char *value) { PSP_Shutdown(); bootPending_ = true; System_Notify(SystemNotification::DISASSEMBLY); - - std::string resetError; - if (!PSP_InitStart(PSP_CoreParameter(), &resetError)) { - ERROR_LOG(Log::Loader, "Error resetting: %s", resetError.c_str()); + if (!PSP_InitStart(PSP_CoreParameter())) { + ERROR_LOG(Log::Loader, "Error resetting"); stopRender_ = true; screenManager()->switchScreen(new MainScreen()); return; diff --git a/headless/Headless.cpp b/headless/Headless.cpp index 4cf7235f3b..e525f768a9 100644 --- a/headless/Headless.cpp +++ b/headless/Headless.cpp @@ -184,9 +184,9 @@ bool RunAutoTest(HeadlessHost *headlessHost, CoreParameter &coreParameter, const if (opt.compare || opt.bench) coreParameter.collectDebugOutput = &output; - std::string error_string; - if (!PSP_InitStart(coreParameter, &error_string)) { - fprintf(stderr, "Failed to start '%s'. Error: %s\n", coreParameter.fileToStart.c_str(), error_string.c_str()); + if (!PSP_InitStart(coreParameter)) { + // Shouldn't really happen anymore, the errors happen later in PSP_InitUpdate. + fprintf(stderr, "Failed to start '%s'.\n", coreParameter.fileToStart.c_str()); printf("TESTERROR\n"); TeamCityPrint("testIgnored name='%s' message='PRX/ELF missing'", currentTestName.c_str()); GitHubActionsPrint("error", "PRX/ELF missing for %s", currentTestName.c_str()); @@ -198,9 +198,12 @@ bool RunAutoTest(HeadlessHost *headlessHost, CoreParameter &coreParameter, const if (opt.compare) headlessHost->SetComparisonScreenshot(ExpectedScreenshotFromFilename(coreParameter.fileToStart), opt.maxScreenshotError); + std::string error_string; while (!PSP_InitUpdate(&error_string)) sleep_ms(1, "auto-test"); + if (!PSP_IsInited()) { + TeamCityPrint("%s", error_string.c_str()); TeamCityPrint("testFailed name='%s' message='Startup failed'", currentTestName.c_str()); TeamCityPrint("testFinished name='%s'", currentTestName.c_str()); GitHubActionsPrint("error", "Test init failed for %s", currentTestName.c_str());