Merge pull request #57 from DavidutzDev/fix/nvidia-580-threaded-egl

fix(nvidia): keep threaded optimizations off for driver 580 (Pascal black screen)
This commit is contained in:
Kalah authored and GitHub committed 2026-10-18 15:18:21 +02:00
commit 455ab66161
3 files changed
+40 -13

No files matched your search

+21
View File
@@ -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.
+15 -12
View File
@@ -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;
}
+4 -1
View File
@@ -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