SDL
SDL copied to clipboard
EGL error when using render thread on Wayland
I have a threaded GL setup where I create a pair of shared GL contexts on my main thread. Then I start a render thread and bind the second context there. When I do so (on Fedora 39 / Wayland) I consistently get an EGL error:
Failed to bind OpenGL context: Unable to make EGL context current (call to eglMakeCurrent failed, reporting an error of EGL_BAD_ACCESS)
It works fine if I disable the render thread. It also works fine with SDL_VIDEODRIVER=X11
. If I move the creation of the second context to the render thread it fails with the same error. (IIRC this is a no-no on other platforms too) I might be able to help look into this deeper, but it's been a while since I've touched EGL, and I've never worked with Wayland directly.
Here's a minimal replication of the error:
#include <assert.h>
#include <SDL.h>
SDL_Window* WINDOW;
static int render_thread(void* user_data){
SDL_GLContext* gl_context = user_data;
int err = SDL_GL_MakeCurrent(WINDOW, gl_context);
if(err != 0) SDL_Log(SDL_GetError());
return 0;
}
int main(void){
int err = SDL_Init(SDL_INIT_VIDEO);
assert(err == 0);
SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1);
WINDOW = SDL_CreateWindow("What's in a name", 0, 0, 800, 600, SDL_WINDOW_OPENGL);
assert(WINDOW);
// GL context for the render thread
SDL_GLContext* gl_context = SDL_GL_CreateContext(WINDOW);
// GL context for the main thread to sync to
SDL_GLContext* sync_context = SDL_GL_CreateContext(WINDOW);
assert(gl_context && sync_context);
SDL_CreateThread(render_thread, "render_thread", gl_context);
return 0;
}
Compile with: cc `sdl2-config --cflags --libs` test.c
or whatnot.
The eglMakeCurrent docs page says this:
If
context
is current to some other thread, or if either draw or read are bound to contexts in another thread, anEGL_BAD_ACCESS
error is generated.
If binding
context
would exceed the number of current contexts of that client API type supported by the implementation, anEGL_BAD_ACCESS
error is generated.
So probably one of those two things is happening?
I did try explicitly unbinding the first context from the main thread, and that didn't work. (Unsurprising since creating the second context should unbind it)
On a whim, I just tried unbinding both contexts from the main thread. Then the render thread is able to bind it's context and render a single frame before the main thread tries to sync without a GL context bound and crashes. If I wait and bind the second context on the main thread later it still gets that EGL_BAD_ACCESS
crash though. So it seems whichever thread binds last gets stuck.
Are there any other examples of games using shared contexts that do work?
Looking more closely at that first case and eglMakeCurrent's parameters and SDL's use of them, I don't think SDL's current API allows having two OpenGL/EGL contexts active in different threads at the same time if they reference the same window.
SDL only exposes a context
parameter for its MakeCurrent API, so the backends that use EGL have to pass the surface of the window associated with that context to every eglMakeCurrent call. Doing so with two contexts associated with the same window in different threads would hit this case:
if either
draw
orread
[surface parameters] are bound to contexts in another thread, an EGL_BAD_ACCESS error is generated.
This doesn't really help solve your issue but I'm curious what sort of things you're using two contexts at once for - when I looked into it a long time ago, I got the impression the overall idea wasn't great because it could slow down calls in both contexts due to drivers sometimes needing internal mutex locks (not to mention any possible driver bugs).
In my case I'm really just using it for synchronization. The main thread gets a render queue object, waits for it's fence and fills it up with commands and data. Meanwhile the render thread is submitting GL calls for the previous frame's queue and then a fence for the main thread to wait for so it knows when it's ready. Flip the queues and continue endlessly. Basic double buffering. I've written several renderers in the past 10 years that do that. Works great. I've also read that shared context have some additional cost. It's not bad enough I've ever noticed it when not actively measuring it anyway. It's certainly not worse than the boost you get from decoupling rendering. (shrug)
I've sort of been ignoring this bug for a couple years now assuming that there was just something that wasn't implemented for Wayland yet. The GL3 renderer was kind of just a fallback for the Vulkan renderer so I didn't really invest a lot of thought. SDL on Haiku OS has a mildly similar issue where it can't even create a shared context, and my workaround was to just disable threading. I was surprised to see it fixed the GL on Wayland issue and figured it's probably time to help get it fixed.
I am running into the same issue on Linux Fedora 40 using the "wayland" video driver, even without a render thread
Doing SDL_GL_MakeCurrent(WINDOW, NULL);
before SDL_CreateThread
makes the error go away.
I'm not sure you can create 2 gl contexts for the same window.
To note, this example flat-out segfaults on XWayland for me (Fedora 40/GNOME/Nvidia 550).
I am running into the same issue on Linux Fedora 40 using the "wayland" video driver, even without a render thread
Doing
SDL_GL_MakeCurrent(WINDOW, NULL);
beforeSDL_CreateThread
makes the error go away. I'm not sure you can create 2 gl contexts for the same window.
This hint resolved my issues. Run SDL_GL_MakeCurrent(WINDOW, NULL);
in the main thread before issuing draws in other threads.