From ced24079cb62952070fe0ad7377ea27a9be9a113 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 3 Aug 2026 23:47:49 +0200 Subject: [PATCH] Error handling cleanup, with some Claude help --- SDL/SDLGLGraphicsContext.cpp | 130 +++++++++++++++++++++++------------ SDL/SDLGLGraphicsContext.h | 2 +- SDL/SDLMain.cpp | 22 +++--- 3 files changed, 102 insertions(+), 52 deletions(-) diff --git a/SDL/SDLGLGraphicsContext.cpp b/SDL/SDLGLGraphicsContext.cpp index 71e5daf634..16dbac2e65 100644 --- a/SDL/SDLGLGraphicsContext.cpp +++ b/SDL/SDLGLGraphicsContext.cpp @@ -8,6 +8,7 @@ #include "Common/System/NativeApp.h" #include "Common/System/System.h" #include "Common/System/Display.h" +#include "Common/StringUtils.h" #include "Core/Config.h" #include "Core/ConfigValues.h" #include "Core/System.h" @@ -32,7 +33,7 @@ static bool g_XDisplayOpen = false; static EGLNativeWindowType g_Window = (EGLNativeWindowType)nullptr; static bool useEGLSwap = false; -int CheckEGLErrors(const char *file, int line) { +int CheckEGLErrors(const char *file, int line, std::string *errorMessage) { EGLenum error; const char *errortext = "unknown"; error = eglGetError(); @@ -54,27 +55,25 @@ int CheckEGLErrors(const char *file, int line) { case EGL_BAD_NATIVE_WINDOW: errortext = "EGL_BAD_NATIVE_WINDOW"; break; default: errortext = "unknown"; break; } - fprintf( stderr, "ERROR: EGL Error %s detected in file %s at line %d (0x%X)\n", errortext, file, line, error ); + if (errorMessage) { + *errorMessage += StringFromFormat("EGL Error %s detected in file %s at line %d (0x%X)\n", errortext, file, line, error); + } return 1; } -#define EGL_ERROR(str, check) { \ - if (check) CheckEGLErrors( __FILE__, __LINE__ ); \ - fprintf(stderr, "EGL ERROR: " str "\n"); \ - return 1; \ - } - -static bool EGL_OpenInit() { - // NOTE: Not using the EGL_ERROR macro here since it always does "return 1", - // which means "success" for this bool-returning function. +static bool EGL_OpenInit(std::string *errorMessage) { if ((g_eglDisplay = eglGetDisplay(g_Display)) == EGL_NO_DISPLAY) { - CheckEGLErrors(__FILE__, __LINE__); - fprintf(stderr, "EGL ERROR: Unable to create EGL display.\n"); + CheckEGLErrors(__FILE__, __LINE__, errorMessage); + if (errorMessage) { + *errorMessage += "Unable to create EGL display.\n"; + } return false; } if (eglInitialize(g_eglDisplay, NULL, NULL) != EGL_TRUE) { - CheckEGLErrors(__FILE__, __LINE__); - fprintf(stderr, "EGL ERROR: Unable to initialize EGL display.\n"); + CheckEGLErrors(__FILE__, __LINE__, errorMessage); + if (errorMessage) { + *errorMessage += "Unable to initialize EGL display.\n"; + } eglTerminate(g_eglDisplay); g_eglDisplay = EGL_NO_DISPLAY; return false; @@ -83,15 +82,19 @@ static bool EGL_OpenInit() { return true; } -static int8_t EGL_Open(SDL_Window *window) { +static int8_t EGL_Open(SDL_Window *window, std::string *errorMessage) { #if defined(USING_FBDEV) g_Display = (EGLNativeDisplayType)nullptr; g_Window = (EGLNativeWindowType)nullptr; #elif defined(__APPLE__) g_Display = (EGLNativeDisplayType)XOpenDisplay(nullptr); g_XDisplayOpen = g_Display != nullptr; - if (!g_XDisplayOpen) - EGL_ERROR("Unable to get display!", false); + if (!g_XDisplayOpen) { + if (errorMessage) { + *errorMessage += "Unable to get display!\n"; + } + return 1; + } g_Window = (EGLNativeWindowType)nullptr; #else // Get the SDL window native handle @@ -107,26 +110,36 @@ static int8_t EGL_Open(SDL_Window *window) { g_Display = (EGLNativeDisplayType)waylandDisplay; g_Window = (EGLNativeWindowType)waylandEGLWindow; } else { - fprintf(stderr, "ERROR: Unable to retrieve native window properties\n"); + if (errorMessage) { + *errorMessage += "Unable to retrieve native window properties, falling back to X11.\n"; + } g_Display = (EGLNativeDisplayType)XOpenDisplay(nullptr); g_XDisplayOpen = g_Display != nullptr; - if (!g_XDisplayOpen) - EGL_ERROR("Unable to get display!", false); + if (!g_XDisplayOpen) { + if (errorMessage) { + *errorMessage += "Unable to get display!\n"; + } + return 1; + } g_Window = (EGLNativeWindowType)nullptr; } } - if (!EGL_OpenInit()) { + if (!EGL_OpenInit(errorMessage)) { g_Display = (EGLNativeDisplayType)XOpenDisplay(nullptr); g_XDisplayOpen = g_Display != nullptr; - if (!g_XDisplayOpen) - EGL_ERROR("Unable to get display!", false); + if (!g_XDisplayOpen) { + if (errorMessage) { + *errorMessage += "Unable to get display!\n"; + } + return 1; + } g_Window = (EGLNativeWindowType)nullptr; } #endif if (g_eglDisplay == EGL_NO_DISPLAY) - EGL_OpenInit(); + EGL_OpenInit(errorMessage); return g_eglDisplay == EGL_NO_DISPLAY ? 1 : 0; } @@ -134,18 +147,26 @@ static int8_t EGL_Open(SDL_Window *window) { #define EGL_OPENGL_ES3_BIT_KHR (1 << 6) #endif -EGLConfig EGL_FindConfig(int *contextVersion) { +EGLConfig EGL_FindConfig(int *contextVersion, std::string *errorMessage) { std::vector configs; EGLint numConfigs = 0; EGLBoolean result = eglGetConfigs(g_eglDisplay, nullptr, 0, &numConfigs); if (result != EGL_TRUE || numConfigs == 0) { + CheckEGLErrors(__FILE__, __LINE__, errorMessage); + if (errorMessage) { + *errorMessage += "eglGetConfigs failed to return any configs.\n"; + } return nullptr; } configs.resize(numConfigs); result = eglGetConfigs(g_eglDisplay, &configs[0], numConfigs, &numConfigs); if (result != EGL_TRUE || numConfigs == 0) { + CheckEGLErrors(__FILE__, __LINE__, errorMessage); + if (errorMessage) { + *errorMessage += "eglGetConfigs failed to return any configs.\n"; + } return nullptr; } @@ -228,11 +249,13 @@ EGLConfig EGL_FindConfig(int *contextVersion) { return best; } -int8_t EGL_Init(SDL_Window *window) { +int8_t EGL_Init(SDL_Window *window, std::string *errorMessage) { int contextVersion = 0; - EGLConfig eglConfig = EGL_FindConfig(&contextVersion); + EGLConfig eglConfig = EGL_FindConfig(&contextVersion, errorMessage); if (!eglConfig) { - EGL_ERROR("Unable to find a usable EGL config.", true); + if (errorMessage) { + *errorMessage += "Unable to find a usable EGL config.\n"; + } return 1; } @@ -246,18 +269,27 @@ int8_t EGL_Init(SDL_Window *window) { g_eglContext = eglCreateContext(g_eglDisplay, eglConfig, nullptr, contextAttributes); if (g_eglContext == EGL_NO_CONTEXT) { - EGL_ERROR("Unable to create GLES context!", true); + CheckEGLErrors(__FILE__, __LINE__, errorMessage); + if (errorMessage) { + *errorMessage += "Unable to create GLES context!\n"; + } return 1; } g_eglSurface = eglCreateWindowSurface(g_eglDisplay, eglConfig, g_Window, nullptr); if (g_eglSurface == EGL_NO_SURFACE) { - EGL_ERROR("Unable to create EGL surface!", true); + CheckEGLErrors(__FILE__, __LINE__, errorMessage); + if (errorMessage) { + *errorMessage += "Unable to create EGL surface!\n"; + } return 1; } if (eglMakeCurrent(g_eglDisplay, g_eglSurface, g_eglSurface, g_eglContext) != EGL_TRUE) { - EGL_ERROR("Unable to make GLES context current.", true); + CheckEGLErrors(__FILE__, __LINE__, errorMessage); + if (errorMessage) { + *errorMessage += "Unable to make GLES context current.\n"; + } return 1; } @@ -290,7 +322,7 @@ void EGL_Close() { #endif // USING_EGL -SDL_Window *CreateSDLGLWindowAndContext(int x, int y, int w, int h, int mode, int forceGLVersion, SDL_GLContext *glContextOut) { +SDL_Window *CreateSDLGLWindowAndContext(int x, int y, int w, int h, int mode, int forceGLVersion, SDL_GLContext *glContextOut, std::string *errorMessage) { // We start hidden because we have to try several windows. // On Mac, full screen animates so each attempt is slow. mode |= SDL_WINDOW_OPENGL | SDL_WINDOW_HIDDEN; @@ -333,7 +365,9 @@ SDL_Window *CreateSDLGLWindowAndContext(int x, int y, int w, int h, int mode, in window = SDL_CreateWindow("PPSSPP", w, h, (SDL_WindowFlags)mode); if (!window) { // Definitely don't shutdown here: we'll keep trying more GL versions. - fprintf(stderr, "SDL_CreateWindow failed for GL %d.%d: %s\n", ver.major, ver.minor, SDL_GetError()); + if (errorMessage) { + *errorMessage += StringFromFormat("SDL_CreateWindow failed for GL %d.%d: %s\n", ver.major, ver.minor, SDL_GetError()); + } // Skip the DestroyWindow. continue; } @@ -360,14 +394,18 @@ SDL_Window *CreateSDLGLWindowAndContext(int x, int y, int w, int h, int mode, in window = SDL_CreateWindow("PPSSPP", w, h, (SDL_WindowFlags)mode); if (window == nullptr) { - fprintf(stderr, "SDL_CreateWindow failed: %s\n", SDL_GetError()); + if (errorMessage) { + *errorMessage += StringFromFormat("SDL_CreateWindow failed: %s\n", SDL_GetError()); + } return nullptr; } glContext = SDL_GL_CreateContext(window); if (!glContext) { // OK, now we really have tried everything. We give up. - fprintf(stderr, "SDL_GL_CreateContext failed: %s\n", SDL_GetError()); + if (errorMessage) { + *errorMessage += StringFromFormat("SDL_GL_CreateContext failed: %s\n", SDL_GetError()); + } SDL_DestroyWindow(window); return nullptr; } @@ -379,10 +417,12 @@ SDL_Window *CreateSDLGLWindowAndContext(int x, int y, int w, int h, int mode, in } #ifdef USING_EGL - if (EGL_Open(window) != 0) { - fprintf(stderr, "EGL_Open() failed\n"); - } else if (EGL_Init(window) != 0) { - fprintf(stderr, "EGL_Init() failed\n"); + // EGL is optional here - if it fails, we just keep using the regular SDL/GLX swap set up above. + std::string eglError; + if (EGL_Open(window, &eglError) != 0) { + WARN_LOG(Log::G3D, "EGL_Open() failed: %s", eglError.c_str()); + } else if (EGL_Init(window, &eglError) != 0) { + WARN_LOG(Log::G3D, "EGL_Init() failed: %s", eglError.c_str()); } else { useEGLSwap = true; } @@ -397,7 +437,9 @@ SDL_Window *CreateSDLGLWindowAndContext(int x, int y, int w, int h, int mode, in GLenum glew_err = glewInit(); // glx is not required, igore. if (glew_err != GLEW_OK && glew_err != GLEW_ERROR_NO_GLX_DISPLAY) { - fprintf(stderr, "Failed to initialize glew!\n"); + if (errorMessage) { + *errorMessage += StringFromFormat("Failed to initialize glew: %s\n", (const char *)glewGetErrorString(glew_err)); + } SDL_GL_DestroyContext(glContext); SDL_DestroyWindow(window); return nullptr; @@ -407,9 +449,11 @@ SDL_Window *CreateSDLGLWindowAndContext(int x, int y, int w, int h, int mode, in glGetError(); if (GLEW_VERSION_2_0) { - fprintf(stderr, "OpenGL 2.0 or higher.\n"); + INFO_LOG(Log::G3D, "OpenGL 2.0 or higher."); } else { - fprintf(stderr, "Sorry, this program requires OpenGL 2.0.\n"); + if (errorMessage) { + *errorMessage += "Sorry, this program requires OpenGL 2.0.\n"; + } SDL_GL_DestroyContext(glContext); SDL_DestroyWindow(window); return nullptr; diff --git a/SDL/SDLGLGraphicsContext.h b/SDL/SDLGLGraphicsContext.h index e2cc6e6fa0..efc7259d7d 100644 --- a/SDL/SDLGLGraphicsContext.h +++ b/SDL/SDLGLGraphicsContext.h @@ -46,4 +46,4 @@ private: GLRenderManager *renderManager_ = nullptr; }; -SDL_Window *CreateSDLGLWindowAndContext(int x, int y, int w, int h, int mode, int forceGLVersion, SDL_GLContext *glContextOut); +SDL_Window *CreateSDLGLWindowAndContext(int x, int y, int w, int h, int mode, int forceGLVersion, SDL_GLContext *glContextOut, std::string *errorMessage); diff --git a/SDL/SDLMain.cpp b/SDL/SDLMain.cpp index 96c0af9ea0..63a35c1473 100644 --- a/SDL/SDLMain.cpp +++ b/SDL/SDLMain.cpp @@ -1748,7 +1748,7 @@ void UpdateSDLCursor() { #endif } -bool DetermineVulkanWindowSystem(SDL_Window *window, WindowSystem *windowSystem, void **data1, void **data2) { +bool DetermineVulkanWindowSystem(SDL_Window *window, WindowSystem *windowSystem, void **data1, void **data2, std::string *errorMessage) { _dbg_assert_(window); SDL_PropertiesID windowProps = SDL_GetWindowProperties(window); void *x11Display = SDL_GetPointerProperty(windowProps, SDL_PROP_WINDOW_X11_DISPLAY_POINTER, nullptr); @@ -1794,7 +1794,9 @@ bool DetermineVulkanWindowSystem(SDL_Window *window, WindowSystem *windowSystem, } #endif #endif // VK_USE_PLATFORM_METAL_EXT - fprintf(stderr, "Unable to determine Vulkan window system from SDL3 window properties\n"); + if (errorMessage) { + *errorMessage = "Unable to determine Vulkan window system from SDL3 window properties"; + } return false; } @@ -2067,25 +2069,29 @@ int main(int argc, char *argv[]) { GraphicsContext *ctx = nullptr; if (backend == GPUBackend::OPENGL) { SDL_GLContext glContext = nullptr; - window = CreateSDLGLWindowAndContext(x, y, w, h, mode, cmdLineOptions.force_gl_version, &glContext); + window = CreateSDLGLWindowAndContext(x, y, w, h, mode, cmdLineOptions.force_gl_version, &glContext, errorMessage); data1 = (void *)window; data2 = (void *)glContext; ctx = new SDLGLGraphicsContext(); } else { - mode |= SDL_WINDOW_VULKAN | SDL_WINDOW_HIDDEN; - window = SDL_CreateWindow("Initializing graphics...", w, h, (SDL_WindowFlags)mode); + // Use a local copy of mode: this flag combination is Vulkan-specific, and if we fall back to + // OpenGL below, we don't want SDL_WINDOW_VULKAN to stick around and get OR'd in there too. + Uint32 vulkanMode = mode | SDL_WINDOW_VULKAN | SDL_WINDOW_HIDDEN; + window = SDL_CreateWindow("Initializing graphics...", w, h, (SDL_WindowFlags)vulkanMode); if (!window) { - fprintf(stderr, "Error creating SDL window: %s\n", SDL_GetError()); - exit(1); + if (errorMessage) { + *errorMessage = StringFromFormat("Error creating SDL window: %s", SDL_GetError()); + } + return false; } if (x != SDL_WINDOWPOS_UNDEFINED && y != SDL_WINDOWPOS_UNDEFINED) { SDL_SetWindowPosition(window, x, y); } // Overwrite the surface init params with what we need for Vulkan.. - if (!DetermineVulkanWindowSystem(window, &windowSystem, &data1, &data2)) { + if (!DetermineVulkanWindowSystem(window, &windowSystem, &data1, &data2, errorMessage)) { return false; } // NOTE : This should match the lines below in the Vulkan case.