diff --git a/Core/Config.cpp b/Core/Config.cpp index 28953d0647..537757169e 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -89,6 +89,7 @@ void Config::Load(const char *iniFileName) #else cpu->Get("Jit", &bJit, true); #endif + cpu->Get("UseCPUThread", &bUseCPUThread, false); cpu->Get("FastMemory", &bFastMemory, false); cpu->Get("CPUSpeed", &iLockedCPUSpeed, false); @@ -226,6 +227,7 @@ void Config::Save() IniFile::Section *cpu = iniFile.GetOrCreateSection("CPU"); cpu->Set("Jit", bJit); + cpu->Set("UseCPUThread", bUseCPUThread); cpu->Set("FastMemory", bFastMemory); cpu->Set("CPUSpeed", iLockedCPUSpeed); diff --git a/Core/Config.h b/Core/Config.h index 05905d8b5b..eda4cf6b9c 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -56,6 +56,7 @@ public: bool bIgnoreBadMemAccess; bool bFastMemory; bool bJit; + bool bUseCPUThread; int iLockedCPUSpeed; bool bAutoSaveSymbolMap; std::string sReportHost; diff --git a/Core/CoreParameter.h b/Core/CoreParameter.h index f06ffd3834..64ad61c890 100644 --- a/Core/CoreParameter.h +++ b/Core/CoreParameter.h @@ -40,6 +40,7 @@ struct CoreParameter std::string fileToStart; std::string mountIso; // If non-empty, and fileToStart is an ELF or PBP, will mount this ISO in the background. + std::string errorString; bool startPaused; bool disableG3Dlog; diff --git a/Core/System.cpp b/Core/System.cpp index ea434b71bc..990c53f7a3 100644 --- a/Core/System.cpp +++ b/Core/System.cpp @@ -39,31 +39,73 @@ #include "Core/PSPLoaders.h" #include "Core/ELF/ParamSFO.h" #include "Core/SaveState.h" +#include "Common/StdConditionVariable.h" +#include "Common/Thread.h" +#include "Common/StdThread.h" #include "Common/LogManager.h" +#include "GPU/GPUState.h" +#include "GPU/GPUInterface.h" + +enum CPUThreadState { + CPU_THREAD_NOT_RUNNING, + CPU_THREAD_PENDING, + CPU_THREAD_STARTING, + CPU_THREAD_RUNNING, + CPU_THREAD_SHUTDOWN, + + CPU_THREAD_EXECUTE, +}; + MetaFileSystem pspFileSystem; ParamSFOData g_paramSFO; GlobalUIState globalUIState; static CoreParameter coreParameter; static PSPMixer *mixer; +static std::thread *cpuThread = NULL; +static std::mutex cpuThreadLock; +static std::condition_variable cpuThreadCond; +static u64 cpuThreadUntil; // This can be read and written from ANYWHERE. volatile CoreState coreState = CORE_STEPPING; // Note: intentionally not used for CORE_NEXTFRAME. volatile bool coreStatePending = false; +static volatile CPUThreadState cpuThreadState = CPU_THREAD_NOT_RUNNING; -void Core_UpdateState(CoreState newState) -{ - if ((coreState == CORE_RUNNING || coreState == CORE_NEXTFRAME) && newState != CORE_RUNNING) - coreStatePending = true; - coreState = newState; +bool CPU_NextState(CPUThreadState from, CPUThreadState to) { + if (cpuThreadState == from) { + cpuThreadState = to; + cpuThreadCond.notify_all(); + return true; + } else { + return false; + } } -bool PSP_Init(const CoreParameter &coreParam, std::string *error_string) -{ - INFO_LOG(HLE, "PPSSPP %s", PPSSPP_GIT_VERSION); +void CPU_SetState(CPUThreadState to) { + cpuThreadState = to; + cpuThreadCond.notify_all(); +} - coreParameter = coreParam; +bool CPU_IsReady() { + 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_WaitStatus(bool (*pred)()) { + std::unique_lock uniqueLock(cpuThreadLock); + cpuThreadCond.wait(uniqueLock, pred); +} + +void CPU_Init() { currentCPU = &mipsr4k; numCPUs = 1; @@ -73,11 +115,12 @@ bool PSP_Init(const CoreParameter &coreParam, std::string *error_string) g_RemasterMode = false; g_DoubleTextureCoordinates = false; - std::string filename = coreParam.fileToStart; + std::string filename = coreParameter.fileToStart; EmuFileType type = Identify_File(filename); - if(type == FILETYPE_PSP_ISO || type == FILETYPE_PSP_ISO_NP || type == FILETYPE_PSP_DISC_DIRECTORY) + if (type == FILETYPE_PSP_ISO || type == FILETYPE_PSP_ISO_NP || type == FILETYPE_PSP_DISC_DIRECTORY) { InitMemoryForGameISO(filename); + } Memory::Init(); mipsr4k.Reset(); @@ -85,14 +128,12 @@ bool PSP_Init(const CoreParameter &coreParam, std::string *error_string) host->AttemptLoadSymbolMap(); - if (coreParameter.enableSound) - { + if (coreParameter.enableSound) { mixer = new PSPMixer(); host->InitSound(mixer); } - if (coreParameter.disableG3Dlog) - { + if (coreParameter.disableG3Dlog) { LogManager::GetInstance()->SetEnable(LogTypes::G3D, false); } @@ -104,7 +145,7 @@ bool PSP_Init(const CoreParameter &coreParam, std::string *error_string) // TODO: Check Game INI here for settings, patches and cheats, and modify coreParameter accordingly // Why did we check for CORE_POWERDOWN here? - if (!LoadFile(filename, error_string)) { // || coreState == CORE_POWERDOWN) { + if (!LoadFile(filename, &coreParameter.errorString)) { pspFileSystem.Shutdown(); CoreTiming::Shutdown(); __KernelShutdown(); @@ -112,36 +153,27 @@ bool PSP_Init(const CoreParameter &coreParam, std::string *error_string) host->ShutdownSound(); Memory::Shutdown(); coreParameter.fileToStart = ""; - return false; + CPU_SetState(CPU_THREAD_NOT_RUNNING); + return; } - if (coreParam.updateRecent) + if (coreParameter.updateRecent) { g_Config.AddRecent(filename); + } - // Setup JIT here. - if (coreParameter.startPaused) - coreState = CORE_STEPPING; - else - coreState = CORE_RUNNING; - return true; + coreState = coreParameter.startPaused ? CORE_STEPPING : CORE_RUNNING; } -bool PSP_IsInited() -{ - return currentCPU != 0; -} - -void PSP_Shutdown() -{ +void CPU_Shutdown() { pspFileSystem.Shutdown(); CoreTiming::Shutdown(); - if (g_Config.bAutoSaveSymbolMap) + if (g_Config.bAutoSaveSymbolMap) { host->SaveSymbolMap(); + } - if (coreParameter.enableSound) - { + if (coreParameter.enableSound) { host->ShutdownSound(); mixer = 0; // deleted in ShutdownSound } @@ -151,17 +183,112 @@ void PSP_Shutdown() currentCPU = 0; } +void CPU_RunLoop() { + Common::SetCurrentThreadName("CPUThread"); + if (!CPU_NextState(CPU_THREAD_PENDING, CPU_THREAD_STARTING)) { + ERROR_LOG(CPU, "CPU thread in unexpected state: %d", cpuThreadState); + return; + } + + CPU_Init(); + CPU_NextState(CPU_THREAD_STARTING, CPU_THREAD_RUNNING); + + while (cpuThreadState != CPU_THREAD_SHUTDOWN) + { + CPU_WaitStatus(&CPU_HasPendingAction); + switch (cpuThreadState) { + case CPU_THREAD_EXECUTE: + mipsr4k.RunLoopUntil(cpuThreadUntil); + CPU_NextState(CPU_THREAD_EXECUTE, CPU_THREAD_RUNNING); + break; + + // These are fine, just keep looping. + case CPU_THREAD_RUNNING: + case CPU_THREAD_SHUTDOWN: + break; + + default: + ERROR_LOG(CPU, "CPU thread in unexpected state: %d", cpuThreadState); + // Begin shutdown, otherwise we'd just spin on this bad state. + cpuThreadState = CPU_THREAD_SHUTDOWN; + cpuThreadCond.notify_all(); + break; + } + } + + if (coreState != CORE_ERROR) { + coreState = CORE_POWERDOWN; + } + + CPU_Shutdown(); + CPU_SetState(CPU_THREAD_NOT_RUNNING); +} + +void Core_UpdateState(CoreState newState) { + if ((coreState == CORE_RUNNING || coreState == CORE_NEXTFRAME) && newState != CORE_RUNNING) + coreStatePending = true; + coreState = newState; +} + +bool PSP_Init(const CoreParameter &coreParam, std::string *error_string) { + INFO_LOG(HLE, "PPSSPP %s", PPSSPP_GIT_VERSION); + + coreParameter = coreParam; + coreParameter.errorString = ""; + + if (g_Config.bUseCPUThread) { + CPU_SetState(CPU_THREAD_PENDING); + cpuThread = new std::thread(&CPU_RunLoop); + CPU_WaitStatus(&CPU_IsReady); + } else { + CPU_Init(); + } + + bool success = coreParameter.fileToStart != ""; + *error_string = coreParam.errorString; + if (success) { + GPU_Init(); + } + return success; +} + +bool PSP_IsInited() { + return currentCPU != 0; +} + +void PSP_Shutdown() { + if (coreState == CORE_RUNNING) + coreState = CORE_ERROR; + if (g_Config.bUseCPUThread) { + CPU_SetState(CPU_THREAD_SHUTDOWN); + CPU_WaitStatus(&CPU_IsShutdown); + } else { + CPU_Shutdown(); + } + GPU_Shutdown(); +} + void PSP_RunLoopUntil(u64 globalticks) { SaveState::Process(); - mipsr4k.RunLoopUntil(globalticks); + + if (g_Config.bUseCPUThread) { + cpuThreadUntil = globalticks; + if (CPU_NextState(CPU_THREAD_RUNNING, CPU_THREAD_EXECUTE)) { + // TODO: Run GPU here. + CPU_WaitStatus(&CPU_IsReady); + } else { + ERROR_LOG(CPU, "Unable to execute CPU run loop, unexpected state: %d", cpuThreadState); + } + } else { + mipsr4k.RunLoopUntil(globalticks); + } } void PSP_RunLoopFor(int cycles) { PSP_RunLoopUntil(CoreTiming::GetTicks() + cycles); } -CoreParameter &PSP_CoreParameter() -{ +CoreParameter &PSP_CoreParameter() { return coreParameter; } diff --git a/GPU/GPUState.cpp b/GPU/GPUState.cpp index 159423d563..32e103c650 100644 --- a/GPU/GPUState.cpp +++ b/GPU/GPUState.cpp @@ -28,6 +28,25 @@ GPUStateCache gstate_c; GPUInterface *gpu; GPUStatistics gpuStats; +void GPU_Init() { + switch (PSP_CoreParameter().gpuCore) { + case GPU_NULL: + gpu = new NullGPU(); + break; + case GPU_GLES: + gpu = new GLES_GPU(); + break; + case GPU_SOFTWARE: + gpu = new NullGPU(); + break; + } +} + +void GPU_Shutdown() { + delete gpu; + gpu = 0; +} + void InitGfxState() { memset(&gstate, 0, sizeof(gstate)); @@ -56,24 +75,10 @@ void InitGfxState() for (int i = 0; i < 8; i++) { memcpy(gstate.boneMatrix + i * 12, identity4x3, 12 * sizeof(float)); } - - switch (PSP_CoreParameter().gpuCore) { - case GPU_NULL: - gpu = new NullGPU(); - break; - case GPU_GLES: - gpu = new GLES_GPU(); - break; - case GPU_SOFTWARE: - gpu = new NullGPU(); - break; - } } void ShutdownGfxState() { - delete gpu; - gpu = NULL; } // When you have changed state outside the psp gfx core, diff --git a/GPU/GPUState.h b/GPU/GPUState.h index 2e1cb1676b..1ef42432c0 100644 --- a/GPU/GPUState.h +++ b/GPU/GPUState.h @@ -427,6 +427,9 @@ struct GPUStatistics { int numFBOs; }; +void GPU_Init(); +void GPU_Shutdown(); + void InitGfxState(); void ShutdownGfxState(); void ReapplyGfxState();