CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
Path: blob/master/Windows/EmuThread.cpp
Views: 1401
#include <mutex>1#include <atomic>2#include <thread>34#include "Common/System/NativeApp.h"5#include "Common/System/System.h"6#include "Common/System/Request.h"7#include "Common/Data/Text/I18n.h"8#include "Common/Input/InputState.h"9#include "Common/Data/Encoding/Utf8.h"10#include "Common/Log.h"11#include "Common/StringUtils.h"12#include "Common/GraphicsContext.h"13#include "Common/TimeUtil.h"14#include "Common/Thread/ThreadUtil.h"1516#include "Windows/EmuThread.h"17#include "Windows/W32Util/Misc.h"18#include "Windows/MainWindow.h"19#include "Windows/resource.h"20#include "Windows/WindowsHost.h"21#include "Core/Reporting.h"22#include "Core/MemMap.h"23#include "Core/Core.h"24#include "Core/System.h"25#include "Core/Config.h"26#include "Core/ConfigValues.h"2728#if PPSSPP_API(ANY_GL)29#include "Windows/GPU/WindowsGLContext.h"30#endif31#include "Windows/GPU/WindowsVulkanContext.h"32#include "Windows/GPU/D3D9Context.h"33#include "Windows/GPU/D3D11Context.h"3435enum class EmuThreadState {36DISABLED,37START_REQUESTED,38RUNNING,39QUIT_REQUESTED,40STOPPED,41};4243static std::thread emuThread;44static std::atomic<int> emuThreadState((int)EmuThreadState::DISABLED);4546static std::thread mainThread;47static bool useEmuThread;48static std::string g_error_message;49static bool g_inLoop;5051extern std::vector<std::wstring> GetWideCmdLine();5253class GraphicsContext;54static GraphicsContext *g_graphicsContext;5556void MainThreadFunc();5758// On most other platforms, we let the "main" thread become the render thread and59// start a separate emu thread from that, if needed. Should probably switch to that60// to make it the same on all platforms.61void MainThread_Start(bool separateEmuThread) {62useEmuThread = separateEmuThread;63mainThread = std::thread(&MainThreadFunc);64}6566void MainThread_Stop() {67// Already stopped?68UpdateUIState(UISTATE_EXIT);69Core_Stop();70mainThread.join();71}7273bool MainThread_Ready() {74return g_inLoop;75}7677static void EmuThreadFunc(GraphicsContext *graphicsContext) {78SetCurrentThreadName("Emu");7980// There's no real requirement that NativeInit happen on this thread.81// We just call the update/render loop here.82emuThreadState = (int)EmuThreadState::RUNNING;8384NativeInitGraphics(graphicsContext);8586while (emuThreadState != (int)EmuThreadState::QUIT_REQUESTED) {87// We're here again, so the game quit. Restart Core_Run() which controls the UI.88// This way they can load a new game.89if (!Core_IsActive())90UpdateUIState(UISTATE_MENU);91if (!Core_Run(g_graphicsContext)) {92emuThreadState = (int)EmuThreadState::QUIT_REQUESTED;93}94}9596emuThreadState = (int)EmuThreadState::STOPPED;9798NativeShutdownGraphics();99100// Ask the main thread to stop. This prevents a hang on a race condition.101graphicsContext->StopThread();102}103104static void EmuThreadStart(GraphicsContext *graphicsContext) {105emuThreadState = (int)EmuThreadState::START_REQUESTED;106emuThread = std::thread(&EmuThreadFunc, graphicsContext);107}108109static void EmuThreadStop() {110if (emuThreadState != (int)EmuThreadState::QUIT_REQUESTED &&111emuThreadState != (int)EmuThreadState::STOPPED) {112emuThreadState = (int)EmuThreadState::QUIT_REQUESTED;113}114}115116static void EmuThreadJoin() {117emuThread.join();118INFO_LOG(Log::System, "EmuThreadJoin - joined");119}120121bool CreateGraphicsBackend(std::string *error_message, GraphicsContext **ctx) {122WindowsGraphicsContext *graphicsContext = nullptr;123switch (g_Config.iGPUBackend) {124#if PPSSPP_API(ANY_GL)125case (int)GPUBackend::OPENGL:126graphicsContext = new WindowsGLContext();127break;128#endif129case (int)GPUBackend::DIRECT3D9:130graphicsContext = new D3D9Context();131break;132case (int)GPUBackend::DIRECT3D11:133graphicsContext = new D3D11Context();134break;135case (int)GPUBackend::VULKAN:136graphicsContext = new WindowsVulkanContext();137break;138default:139return false;140}141142if (graphicsContext->Init(MainWindow::GetHInstance(), MainWindow::GetDisplayHWND(), error_message)) {143*ctx = graphicsContext;144return true;145} else {146delete graphicsContext;147*ctx = nullptr;148return false;149}150}151152void MainThreadFunc() {153// We'll start up a separate thread we'll call Emu154SetCurrentThreadName(useEmuThread ? "Render" : "Emu");155156SetConsolePosition();157158System_SetWindowTitle("");159160// We convert command line arguments to UTF-8 immediately.161std::vector<std::wstring> wideArgs = GetWideCmdLine();162std::vector<std::string> argsUTF8;163for (auto& string : wideArgs) {164argsUTF8.push_back(ConvertWStringToUTF8(string));165}166std::vector<const char *> args;167for (auto& string : argsUTF8) {168args.push_back(string.c_str());169}170bool performingRestart = NativeIsRestarting();171NativeInit(static_cast<int>(args.size()), &args[0], "", "", nullptr);172173if (g_Config.iGPUBackend == (int)GPUBackend::OPENGL) {174if (!useEmuThread) {175// Okay, we must've switched to OpenGL. Let's flip the emu thread on.176useEmuThread = true;177SetCurrentThreadName("Render");178}179} else if (useEmuThread) {180// We must've failed over from OpenGL, flip the emu thread off.181useEmuThread = false;182SetCurrentThreadName("Emu");183}184185if (g_Config.sFailedGPUBackends.find("ALL") != std::string::npos) {186Reporting::ReportMessage("Graphics init error: %s", "ALL");187188auto err = GetI18NCategory(I18NCat::ERRORS);189const char *defaultErrorAll = "PPSSPP failed to startup with any graphics backend. Try upgrading your graphics and other drivers.";190std::string_view genericError = err->T("GenericAllStartupError", defaultErrorAll);191std::wstring title = ConvertUTF8ToWString(err->T("GenericGraphicsError", "Graphics Error"));192MessageBox(0, ConvertUTF8ToWString(genericError).c_str(), title.c_str(), MB_OK);193194// Let's continue (and probably crash) just so they have a way to keep trying.195}196197System_Notify(SystemNotification::UI);198199std::string error_string;200bool success = CreateGraphicsBackend(&error_string, &g_graphicsContext);201202if (success) {203// Main thread is the render thread.204success = g_graphicsContext->InitFromRenderThread(&error_string);205}206207if (!success) {208// Before anything: are we restarting right now?209if (performingRestart) {210// Okay, switching graphics didn't work out. Probably a driver bug - fallback to restart.211// This happens on NVIDIA when switching OpenGL -> Vulkan.212g_Config.Save("switch_graphics_failed");213W32Util::ExitAndRestart();214}215216auto err = GetI18NCategory(I18NCat::ERRORS);217Reporting::ReportMessage("Graphics init error: %s", error_string.c_str());218219const char *defaultErrorVulkan = "Failed initializing graphics. Try upgrading your graphics drivers.\n\nWould you like to try switching to OpenGL?\n\nError message:";220const char *defaultErrorOpenGL = "Failed initializing graphics. Try upgrading your graphics drivers.\n\nWould you like to try switching to DirectX 9?\n\nError message:";221const char *defaultErrorDirect3D9 = "Failed initializing graphics. Try upgrading your graphics drivers and directx 9 runtime.\n\nWould you like to try switching to OpenGL?\n\nError message:";222std::string_view genericError;223GPUBackend nextBackend = GPUBackend::DIRECT3D9;224switch (g_Config.iGPUBackend) {225case (int)GPUBackend::DIRECT3D9:226nextBackend = GPUBackend::OPENGL;227genericError = err->T("GenericDirect3D9Error", defaultErrorDirect3D9);228break;229case (int)GPUBackend::VULKAN:230nextBackend = GPUBackend::OPENGL;231genericError = err->T("GenericVulkanError", defaultErrorVulkan);232break;233case (int)GPUBackend::OPENGL:234default:235nextBackend = GPUBackend::DIRECT3D9;236genericError = err->T("GenericOpenGLError", defaultErrorOpenGL);237break;238}239std::string full_error = StringFromFormat("%.*s\n\n%s", (int)genericError.size(), genericError.data(), error_string.c_str());240std::wstring title = ConvertUTF8ToWString(err->T("GenericGraphicsError", "Graphics Error"));241bool yes = IDYES == MessageBox(0, ConvertUTF8ToWString(full_error).c_str(), title.c_str(), MB_ICONERROR | MB_YESNO);242ERROR_LOG(Log::Boot, "%s", full_error.c_str());243244if (yes) {245// Change the config to the alternative and restart.246g_Config.iGPUBackend = (int)nextBackend;247// Clear this to ensure we try their selection.248g_Config.sFailedGPUBackends.clear();249g_Config.Save("save_graphics_fallback");250251W32Util::ExitAndRestart();252} else {253if (g_Config.iGPUBackend == (int)GPUBackend::DIRECT3D9) {254// Allow the user to download the DX9 runtime.255System_LaunchUrl(LaunchUrlType::BROWSER_URL, "https://www.microsoft.com/en-us/download/details.aspx?id=34429");256}257}258259// No safe way out without graphics.260ExitProcess(1);261}262263GraphicsContext *graphicsContext = g_graphicsContext;264265if (!useEmuThread) {266NativeInitGraphics(graphicsContext);267NativeResized();268}269270DEBUG_LOG(Log::Boot, "Done.");271272if (coreState == CORE_POWERDOWN) {273INFO_LOG(Log::Boot, "Exit before core loop.");274goto shutdown;275}276277g_inLoop = true;278279if (useEmuThread) {280EmuThreadStart(graphicsContext);281}282graphicsContext->ThreadStart();283284if (g_Config.bBrowse)285PostMessage(MainWindow::GetHWND(), WM_COMMAND, ID_FILE_LOAD, 0);286287Core_EnableStepping(false);288289if (useEmuThread) {290while (emuThreadState != (int)EmuThreadState::DISABLED) {291graphicsContext->ThreadFrame();292if (GetUIState() == UISTATE_EXIT) {293break;294}295}296} else {297while (GetUIState() != UISTATE_EXIT) {298// We're here again, so the game quit. Restart Core_Run() which controls the UI.299// This way they can load a new game.300if (!Core_IsActive())301UpdateUIState(UISTATE_MENU);302Core_Run(g_graphicsContext);303if (coreState == CORE_BOOT_ERROR) {304break;305}306}307}308Core_Stop();309if (!useEmuThread) {310// Process the shutdown. Without this, non-GL delays 800ms on shutdown.311Core_Run(g_graphicsContext);312}313Core_WaitInactive(800);314315g_inLoop = false;316317if (useEmuThread) {318EmuThreadStop();319while (graphicsContext->ThreadFrame()) {320// Need to keep eating frames to allow the EmuThread to exit correctly.321continue;322}323EmuThreadJoin();324}325326shutdown:327328if (!useEmuThread) {329NativeShutdownGraphics();330}331332g_graphicsContext->ThreadEnd();333g_graphicsContext->ShutdownFromRenderThread();334335g_graphicsContext->Shutdown();336337delete g_graphicsContext;338339UpdateConsolePosition();340NativeShutdown();341342PostMessage(MainWindow::GetHWND(), MainWindow::WM_USER_UPDATE_UI, 0, 0);343}344345346