From 32f975ef8595788dc2f2a7ff01340e0ac4155f41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 21 Jul 2026 13:10:31 +0200 Subject: [PATCH] Windows: Move the graphics context creation to main.cpp --- Core/EmuThread.cpp | 123 +++------------------------------------------ Core/EmuThread.h | 7 ++- Windows/main.cpp | 116 +++++++++++++++++++++++++++++++++++++++++- 3 files changed, 127 insertions(+), 119 deletions(-) diff --git a/Core/EmuThread.cpp b/Core/EmuThread.cpp index 95c861613f..3367be61cb 100644 --- a/Core/EmuThread.cpp +++ b/Core/EmuThread.cpp @@ -20,18 +20,11 @@ #include "Windows/W32Util/Misc.h" #include "Windows/MainWindow.h" #include "Core/EmuThread.h" -#include "Core/Reporting.h" #include "Core/Core.h" #include "Core/System.h" #include "Core/Config.h" #include "Core/ConfigValues.h" -#if PPSSPP_API(ANY_GL) -#include "Windows/GPU/WindowsGLContext.h" -#endif -#include "Windows/GPU/WindowsVulkanContext.h" -#include "Windows/GPU/D3D11Context.h" - enum class EmuThreadState { DISABLED, START_REQUESTED, @@ -47,9 +40,8 @@ static std::string g_error_message; static bool g_inLoop; class GraphicsContext; -static GraphicsContext *g_graphicsContext; -void MainThreadFunc(); +void MainThreadFunc(GraphicsContext *graphicsContext); bool MainThread_Ready() { return g_inLoop; @@ -102,111 +94,13 @@ static void EmuThreadJoin() { INFO_LOG(Log::System, "EmuThreadJoin - joined"); } -bool CreateGraphicsBackend(std::string *error_message, GraphicsContext **ctx) { - WindowsGraphicsContext *graphicsContext = nullptr; - switch (g_Config.iGPUBackend) { -#if PPSSPP_API(ANY_GL) - case (int)GPUBackend::OPENGL: - graphicsContext = new WindowsGLContext(); - break; -#endif - case (int)GPUBackend::DIRECT3D11: - graphicsContext = new D3D11Context(); - break; - case (int)GPUBackend::VULKAN: - graphicsContext = new WindowsVulkanContext(); - break; - default: - return false; - } - - if (graphicsContext->Init(MainWindow::GetHInstance(), MainWindow::GetHWND(), error_message)) { - *ctx = graphicsContext; - return true; - } else { - delete graphicsContext; - *ctx = nullptr; - return false; - } -} - -void MainThreadFunc() { +void MainThreadFunc(GraphicsContext *graphicsContext) { const bool useEmuThread = g_Config.iGPUBackend == (int)GPUBackend::OPENGL; SetCurrentThreadName(useEmuThread ? "RenderThread" : "EmuThread"); - const bool performingRestart = NativeIsRestarting(); - - if (g_Config.sFailedGPUBackends.find("ALL") != std::string::npos) { - Reporting::ReportMessage("Graphics init error: %s", "ALL"); - - auto err = GetI18NCategory(I18NCat::ERRORS); - const char *defaultErrorAll = "PPSSPP failed to startup with any graphics backend. Try upgrading your graphics and other drivers."; - std::string_view genericError = err->T("GenericAllStartupError", defaultErrorAll); - std::wstring title = ConvertUTF8ToWString(err->T("GenericGraphicsError", "Graphics Error")); - MessageBox(0, ConvertUTF8ToWString(genericError).c_str(), title.c_str(), MB_OK); - - // Let's continue (and probably crash) just so they have a way to keep trying. - } - - System_Notify(SystemNotification::UI); - std::string error_string; - bool success = CreateGraphicsBackend(&error_string, &g_graphicsContext); - - if (success) { - // Main thread is the render thread. - success = g_graphicsContext->InitFromRenderThread(&error_string); - } - - if (!success) { - // Before anything: are we restarting right now? - if (performingRestart) { - // Okay, switching graphics didn't work out. Probably a driver bug - fallback to restart. - // This happens on NVIDIA when switching OpenGL -> Vulkan. - g_Config.Save("switch_graphics_failed"); - W32Util::ExitAndRestart(); - } - - auto err = GetI18NCategory(I18NCat::ERRORS); - Reporting::ReportMessage("Graphics init error: %s", error_string.c_str()); - - const char *defaultErrorVulkan = "Failed initializing graphics. Try upgrading your graphics drivers.\n\nWould you like to try switching to OpenGL?\n\nError message:"; - const char *defaultErrorOpenGL = "Failed initializing graphics. Try upgrading your graphics drivers.\n\nWould you like to try switching to DirectX 9?\n\nError message:"; - const 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:"; - std::string_view genericError; - GPUBackend nextBackend = GPUBackend::VULKAN; - switch (g_Config.iGPUBackend) { - case (int)GPUBackend::VULKAN: - nextBackend = GPUBackend::OPENGL; - genericError = err->T("GenericVulkanError", defaultErrorVulkan); - break; - case (int)GPUBackend::OPENGL: - default: - nextBackend = GPUBackend::DIRECT3D11; - genericError = err->T("GenericOpenGLError", defaultErrorOpenGL); - break; - } - std::string full_error = StringFromFormat("%.*s\n\n%s", (int)genericError.size(), genericError.data(), error_string.c_str()); - std::wstring title = ConvertUTF8ToWString(err->T("GenericGraphicsError", "Graphics Error")); - bool yes = IDYES == MessageBox(0, ConvertUTF8ToWString(full_error).c_str(), title.c_str(), MB_ICONERROR | MB_YESNO); - ERROR_LOG(Log::Boot, "%s", full_error.c_str()); - - if (yes) { - // Change the config to the alternative and restart. - g_Config.iGPUBackend = (int)nextBackend; - // Clear this to ensure we try their selection. - g_Config.sFailedGPUBackends.clear(); - g_Config.Save("save_graphics_fallback"); - - W32Util::ExitAndRestart(); - } - - // No safe way out without graphics. - ExitProcess(1); - } - - GraphicsContext *graphicsContext = g_graphicsContext; + bool success = graphicsContext->InitFromRenderThread(&error_string); if (!useEmuThread) { NativeInitGraphics(graphicsContext); @@ -266,14 +160,9 @@ void MainThreadFunc() { NativeShutdownGraphics(); } - g_graphicsContext->ThreadEnd(); - g_graphicsContext->ShutdownFromRenderThread(); - g_graphicsContext->Shutdown(); - - delete g_graphicsContext; - g_graphicsContext = nullptr; - - PostMessage(MainWindow::GetHWND(), MainWindow::WM_USER_UPDATE_UI, 0, 0); + graphicsContext->ThreadEnd(); + graphicsContext->ShutdownFromRenderThread(); + graphicsContext->Shutdown(); } #endif diff --git a/Core/EmuThread.h b/Core/EmuThread.h index 71ccf03690..470ea7366c 100644 --- a/Core/EmuThread.h +++ b/Core/EmuThread.h @@ -22,4 +22,9 @@ bool MainThread_Ready(); -void MainThreadFunc(); +class GraphicsContext; + +// Doesn't take ownership of the graphicsContext, you have to delete it. +// This should be used by platforms that launch a separate thread and doesn't +// need to run a polling loop in it. +void MainThreadFunc(GraphicsContext *graphicsContext); diff --git a/Windows/main.cpp b/Windows/main.cpp index 0941a1f8d4..aa09e24190 100644 --- a/Windows/main.cpp +++ b/Windows/main.cpp @@ -56,6 +56,7 @@ #include "Core/HLE/Plugins.h" #include "Core/RetroAchievements.h" #include "Core/EmuThread.h" +#include "Core/Reporting.h" #include "Windows/WindowsAudio.h" #include "ext/disarm.h" @@ -75,7 +76,11 @@ #include "Windows/Debugger/Debugger_VFPUDlg.h" #if PPSSPP_API(ANY_GL) #include "Windows/GEDebugger/GEDebugger.h" +#include "Windows/GPU/WindowsGLContext.h" #endif +#include "Windows/GPU/WindowsVulkanContext.h" +#include "Windows/GPU/D3D11Context.h" + #include "Windows/W32Util/ContextMenu.h" #include "Windows/W32Util/DialogManager.h" #include "Windows/W32Util/DarkMode.h" @@ -1023,6 +1028,108 @@ static void WinMainCleanup() { } } +bool CreateGraphicsBackend(std::string *error_message, GraphicsContext **ctx) { + WindowsGraphicsContext *graphicsContext = nullptr; + switch (g_Config.iGPUBackend) { +#if PPSSPP_API(ANY_GL) + case (int)GPUBackend::OPENGL: + graphicsContext = new WindowsGLContext(); + break; +#endif + case (int)GPUBackend::DIRECT3D11: + graphicsContext = new D3D11Context(); + break; + case (int)GPUBackend::VULKAN: + graphicsContext = new WindowsVulkanContext(); + break; + default: + return false; + } + + if (graphicsContext->Init(MainWindow::GetHInstance(), MainWindow::GetHWND(), error_message)) { + *ctx = graphicsContext; + return true; + } else { + delete graphicsContext; + *ctx = nullptr; + return false; + } +} + +// returns null if failed. +static GraphicsContext *CreateGraphicsContextOrExit() { + const bool performingRestart = NativeIsRestarting(); + + if (g_Config.sFailedGPUBackends.find("ALL") != std::string::npos) { + Reporting::ReportMessage("Graphics init error: %s", "ALL"); + + auto err = GetI18NCategory(I18NCat::ERRORS); + const char *defaultErrorAll = "PPSSPP failed to startup with any graphics backend. Try upgrading your graphics and other drivers."; + std::string_view genericError = err->T("GenericAllStartupError", defaultErrorAll); + std::wstring title = ConvertUTF8ToWString(err->T("GenericGraphicsError", "Graphics Error")); + MessageBox(0, ConvertUTF8ToWString(genericError).c_str(), title.c_str(), MB_OK); + + // Let's continue (and probably crash) just so they have a way to keep trying. + } + + System_Notify(SystemNotification::UI); + + GraphicsContext *graphicsContext = nullptr; + std::string error_string; + const bool success = CreateGraphicsBackend(&error_string, &graphicsContext); + if (success) { + return graphicsContext; + } + + // Before anything: are we restarting right now? + if (performingRestart) { + // Okay, switching graphics didn't work out. Probably a driver bug - fallback to restart. + // This happens on NVIDIA when switching OpenGL -> Vulkan. + g_Config.Save("switch_graphics_failed"); + W32Util::ExitAndRestart(); + return nullptr; + } + + auto err = GetI18NCategory(I18NCat::ERRORS); + Reporting::ReportMessage("Graphics init error: %s", error_string.c_str()); + + const char *defaultErrorVulkan = "Failed initializing graphics. Try upgrading your graphics drivers.\n\nWould you like to try switching to OpenGL?\n\nError message:"; + const char *defaultErrorOpenGL = "Failed initializing graphics. Try upgrading your graphics drivers.\n\nWould you like to try switching to DirectX 9?\n\nError message:"; + const 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:"; + std::string_view genericError; + GPUBackend nextBackend = GPUBackend::VULKAN; + switch (g_Config.iGPUBackend) { + case (int)GPUBackend::VULKAN: + nextBackend = GPUBackend::OPENGL; + genericError = err->T("GenericVulkanError", defaultErrorVulkan); + break; + case (int)GPUBackend::OPENGL: + default: + nextBackend = GPUBackend::DIRECT3D11; + genericError = err->T("GenericOpenGLError", defaultErrorOpenGL); + break; + } + std::string full_error = StringFromFormat("%.*s\n\n%s", (int)genericError.size(), genericError.data(), error_string.c_str()); + std::wstring title = ConvertUTF8ToWString(err->T("GenericGraphicsError", "Graphics Error")); + bool yes = IDYES == MessageBox(0, ConvertUTF8ToWString(full_error).c_str(), title.c_str(), MB_ICONERROR | MB_YESNO); + ERROR_LOG(Log::Boot, "%s", full_error.c_str()); + + if (yes) { + // Change the config to the alternative and restart. + g_Config.iGPUBackend = (int)nextBackend; + // Clear this to ensure we try their selection. + g_Config.sFailedGPUBackends.clear(); + g_Config.Save("save_graphics_fallback"); + + W32Util::ExitAndRestart(); + return nullptr; + } + + // No safe way out without graphics. + ExitProcess(1); + return nullptr; +} + int WINAPI WinMain(HINSTANCE _hInstance, HINSTANCE hPrevInstance, LPSTR szCmdLine, int iCmdShow) { const std::vector wideArgs = GetWideCmdLine(); // Check for the Vulkan workaround before any serious init. @@ -1154,7 +1261,14 @@ int WINAPI WinMain(HINSTANCE _hInstance, HINSTANCE hPrevInstance, LPSTR szCmdLin System_SetWindowTitle(""); std::thread mainThread = std::thread([]() { - MainThreadFunc(); + std::unique_ptr graphicsContext(CreateGraphicsContextOrExit()); + if (!graphicsContext) { + // We're screwed. Should never get here. + _dbg_assert_(false); + return; + } + MainThreadFunc(graphicsContext.get()); + PostMessage(MainWindow::GetHWND(), MainWindow::WM_USER_UPDATE_UI, 0, 0); }); // This thread can become the render thread or the emu thread depending on the backend.