diff --git a/docs/TROUBLESHOOTING.md b/docs/TROUBLESHOOTING.md index 7d8b3f4..3075637 100644 --- a/docs/TROUBLESHOOTING.md +++ b/docs/TROUBLESHOOTING.md @@ -348,6 +348,27 @@ with sway/Xwayland using CPU readback. The initial polling change improved their reported rate from about 20 to 27 FPS. Those observations predate the narrowed integration; they do not establish hybrid-GPU or general NVIDIA support. +### NVIDIA 580 (Pascal and older): black game window + +Driver 580 is the last branch for Pascal (GTX 10-series) and older GPUs. With +`__GL_THREADED_OPTIMIZATIONS=1`, this driver with egl-x11 1.0.6 rejects the X11 +`EGLDisplay` with `EGL_BAD_DISPLAY` on every thread that has a context current. +The display stays valid on other threads, and `eglReleaseThread()` makes it work +again on the failing one. A small native EGL program outside Darling shows the +same: `eglQueryString(display, EGL_VENDOR)` after `eglMakeCurrent` returns +`NVIDIA` without the variable and fails with `EGL_BAD_DISPLAY` with it. + +In the Riot client the main thread loses EGL right after its first window frame; +`CGLCreateContext` then fails with CGL error 10004 and, if another display is +opened, glvnd hands it to Mesa's software renderer, which cannot present the +game's NVIDIA Vulkan frames. The launcher therefore leaves threaded optimizations +off (`__GL_THREADED_OPTIMIZATIONS=0`). Setting the variable to `1` before launch +restores the old behaviour. + +Tested on a GTX 1060 6GB with driver 580.178.04 on NixOS, Hyprland and Xwayland: +the client and the tutorial match render on the NVIDIA GPU. Newer drivers were not +retested with the option off. + ### Host-side checks These are host settings, not game settings. Run on AC power, and use a performance power profile (`powerprofilesctl set performance`, or `asusctl profile -P Performance` on ASUS laptops). On the tested desktop the game is limited mostly by its own main thread on the CPU, not by the GPU (see [status](STATUS.md#game-fps-2026-10-1314)), so laptop CPU power limits matter as much as the GPU choice. diff --git a/patches/darling.patch b/patches/darling.patch index 66477f9..49e767f 100644 --- a/patches/darling.patch +++ b/patches/darling.patch @@ -6257,7 +6257,7 @@ index deca16b87..d75d5e22d 100644 set_property(TARGET OpenGL APPEND_STRING PROPERTY LINK_FLAGS diff --git a/src/frameworks/OpenGL/OpenGL.c b/src/frameworks/OpenGL/OpenGL.c -index ffd7fe054..1ff212d34 100644 +index ffd7fe054..5b5712ea2 100644 --- a/src/frameworks/OpenGL/OpenGL.c +++ b/src/frameworks/OpenGL/OpenGL.c @@ -2,11 +2,22 @@ @@ -6527,11 +6527,14 @@ index ffd7fe054..1ff212d34 100644 if (surface == EGL_NO_SURFACE) { return NULL; -@@ -171,14 +309,18 @@ CGLWindowRef CGLGetWindow(void *native_window) { +@@ -171,14 +309,21 @@ CGLWindowRef CGLGetWindow(void *native_window) { } CGL_EXPORT void CGLDestroyWindow(CGLWindowRef window) { - eglDestroySurface(display, (EGLSurface) window); ++ /* CGLGetWindow returns NULL when eglCreateWindowSurface fails. */ ++ if (window == NULL) ++ return; + p_eglDestroySurface(display, (EGLSurface) window); } @@ -6548,7 +6551,7 @@ index ffd7fe054..1ff212d34 100644 } static pthread_key_t current_context_key; -@@ -198,18 +340,42 @@ CGLContextObj CGLGetCurrentContext(void) { +@@ -198,18 +343,42 @@ CGLContextObj CGLGetCurrentContext(void) { return pthread_getspecific(key); } @@ -6596,7 +6599,7 @@ index ffd7fe054..1ff212d34 100644 } CGLError CGLSetFullScreen(CGLContextObj ctx) { -@@ -223,11 +389,13 @@ CGLError CGLChoosePixelFormat( +@@ -223,11 +392,13 @@ CGLError CGLChoosePixelFormat( CGLPixelFormatObj *result, GLint *number_of_screens ) { @@ -6611,7 +6614,7 @@ index ffd7fe054..1ff212d34 100644 for (int i = 0; i < count; i++) { format->attributes[i] = attrs[i]; } -@@ -251,6 +419,10 @@ CGLError CGLDescribePixelFormat( +@@ -251,6 +422,10 @@ CGLError CGLDescribePixelFormat( CGLPixelFormatAttribute attr, GLint *value ) { @@ -6622,7 +6625,7 @@ index ffd7fe054..1ff212d34 100644 for (int i = 0; format->attributes[i] != 0; i++) { int has_arg = attribute_has_argument(format->attributes[i]); -@@ -304,24 +476,67 @@ GLuint CGLGetPixelFormatRetainCount(CGLPixelFormatObj pixelFormat) { +@@ -304,24 +479,67 @@ GLuint CGLGetPixelFormatRetainCount(CGLPixelFormatObj pixelFormat) { } CGLError CGLCreateContext(CGLPixelFormatObj pixelFormat, CGLContextObj share, CGLContextObj *resultp) { @@ -6691,7 +6694,7 @@ index ffd7fe054..1ff212d34 100644 *resultp = context; -@@ -333,7 +548,7 @@ CGLContextObj CGLRetainContext(CGLContextObj context) { +@@ -333,7 +551,7 @@ CGLContextObj CGLRetainContext(CGLContextObj context) { return NULL; } @@ -6700,7 +6703,7 @@ index ffd7fe054..1ff212d34 100644 return context; } -@@ -342,11 +557,17 @@ void CGLReleaseContext(CGLContextObj context) { +@@ -342,11 +560,17 @@ void CGLReleaseContext(CGLContextObj context) { return; } @@ -6721,7 +6724,7 @@ index ffd7fe054..1ff212d34 100644 if (CGLGetCurrentContext() == context) { CGLSetCurrentContext(NULL); -@@ -354,7 +575,11 @@ void CGLReleaseContext(CGLContextObj context) { +@@ -354,7 +578,11 @@ void CGLReleaseContext(CGLContextObj context) { pthread_mutex_destroy(&(context->lock)); @@ -6734,7 +6737,7 @@ index ffd7fe054..1ff212d34 100644 free(context); } -@@ -383,9 +608,330 @@ CGLError CGLUnlockContext(CGLContextObj context) { +@@ -383,9 +611,330 @@ CGLError CGLUnlockContext(CGLContextObj context) { return kCGLNoError; } @@ -7067,7 +7070,7 @@ index ffd7fe054..1ff212d34 100644 } CGLError CGLSetParameter(CGLContextObj context, CGLContextParameter parameter, const GLint *value) { -@@ -394,8 +940,9 @@ CGLError CGLSetParameter(CGLContextObj context, CGLContextParameter parameter, c +@@ -394,8 +943,9 @@ CGLError CGLSetParameter(CGLContextObj context, CGLContextParameter parameter, c if (parameter == kCGLCPSwapInterval) { @@ -7078,7 +7081,7 @@ index ffd7fe054..1ff212d34 100644 if (success) context->swap_interval = v; return success ? kCGLNoError : kCGLBadValue; -@@ -417,14 +964,19 @@ CGLError CGLGetParameter(CGLContextObj context, CGLContextParameter parameter, G +@@ -417,14 +967,19 @@ CGLError CGLGetParameter(CGLContextObj context, CGLContextParameter parameter, G return kCGLNoError; } diff --git a/tools/run-riot-client.sh b/tools/run-riot-client.sh index 75b6ec7..94ea1be 100755 --- a/tools/run-riot-client.sh +++ b/tools/run-riot-client.sh @@ -92,7 +92,10 @@ if [ -n "${RCLOL_REFRESH_HZ-}" ]; then export RCLOL_REFRESH_HZ; fi export MESA_NO_ERROR="${MESA_NO_ERROR:-1}" # NVIDIA proprietary driver: limit pre-rendered frames. export __GL_MaxFramesAllowed="${__GL_MaxFramesAllowed:-1}" -export __GL_THREADED_OPTIMIZATIONS="${__GL_THREADED_OPTIMIZATIONS:-1}" +# Threaded optimizations stay off: with them, NVIDIA 580 (the last branch for +# Pascal GPUs) with egl-x11 1.0.6 rejects the X11 EGLDisplay with EGL_BAD_DISPLAY +# on every thread that has a context current, and the game window stays black. +export __GL_THREADED_OPTIMIZATIONS="${__GL_THREADED_OPTIMIZATIONS:-0}" export __GL_YIELD="${__GL_YIELD:-NOTHING}" # Reduce timer slack for tighter scheduling in the game process tree. if [ -w /proc/self/timerslack_ns ]; then