When I implemented parallel initialization and update of procedural objects in my OpenGL game, I had to create multiple OpenGL contexts with shared objects, and bind one per thread, so I could create/update the VBOs in parallel.
I got the idea of how to do that from this blog post, and made my first implementation like this (in C++, but this question is also relevant for C):
/* ... */
SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1);
SDL_Window* window = SDL_CreateWindow("Title",
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
Options::width, Options::height, video_flags);
// Create one SDL context per thread
std::vector<SDL_GLContext> threads_glcontexts(globalThreadPool.get_num_threads());
for(auto& t_ctx: threads_glcontexts) {
t_ctx = SDL_GL_CreateContext(window);
}
// Main thread context
SDL_GLContext main_glcontext = SDL_GL_CreateContext(window);
// Setup one context per thread
//
// This function only returns when all threads
// in the pool have executed the given function.
globalThreadPool.run_in_every_pool_thread([&](unsigned thread_idx) {
SDL_GL_MakeCurrent(window, threads_glcontexts[thread_idx]); // ← BROKEN CODE
});
/* ... */
This code worked wonderfully for various months while developing it under Linux, until I ported the game to Windows. Then things went wrong: on Intel and AMD GPUs, is always crashed at startup. On Nvidia, it worked most of the times, but run it a few times and it also crashed on the same place: the first OpenGL call issued by one of the pool threads, which was glGenBuffers()
.
Eventually we found that wglGetCurrentContext()
returned 0
on the offending thread, what lead us to discover that SDL_GL_MakeCurrent()
failed for some threads with errors like:
The question: how to properly setup multiple OpenGL contexts with shared objects on different threads with SDL2?
I don't know if the solution we found will work on every platform supported by SDL, but it at least works on both Windows and Linux/X11.
The issue was that SDL_GL_MakeCurrent()
is not thread-safe in general (it seems to be thread safe on Linux/X11, but that is by chance, since SDL is a multiplatform library (and the problem really is wglMakeCurrent()
, not SDL, because the old code also worked under Wine)).
So we simply had to protect the call with a mutex. In our C++ code, it looks like:
/* ... */
SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1);
SDL_Window* window = SDL_CreateWindow("Title",
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
Options::width, Options::height, video_flags);
// Create one SDL context per thread
std::vector<SDL_GLContext> threads_glcontexts(globalThreadPool.get_num_threads());
for(auto& t_ctx: threads_glcontexts) {
t_ctx = SDL_GL_CreateContext(window);
}
// Main thread context
SDL_GLContext main_glcontext = SDL_GL_CreateContext(window);
// Setup one context per thread
//
// This function only returns when all threads
// in the pool have executed the given function.
std::mutex mtx;
globalThreadPool.run_in_every_pool_thread([&](unsigned thread_idx) {
std::lock_guard<std::mutex> lock(mtx); // ← ↓ WORKINING CODE
SDL_GL_MakeCurrent(window, threads_glcontexts[thread_idx]);
});
/* ... */
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With