当我在OpenGL游戏中实现并行初始化和过程对象的更新时,我必须使用共享对象创建多个OpenGL上下文,并为每个线程绑定(bind)一个,因此可以并行创建/更新VBO。
我从this blog post获得了如何执行此操作的想法,并进行了第一个这样的实现(在C++中,但是这个问题也与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
});
/* ... */
在将代码移植到Windows之前,该代码在Linux上进行开发的各个月表现出色。然后事情就出了问题:在Intel和AMD GPU上,总是在启动时崩溃。在Nvidia上,它大多数时候都可以工作,但是又运行了几次,并且也崩溃在同一位置:一个池线程发出的第一个OpenGL调用是glGenBuffers()
。最终,我们发现
wglGetCurrentContext()
在有问题的线程上返回了0
,这导致我们发现SDL_GL_MakeCurrent()
在某些线程中失败,并出现以下错误:问题:如何使用SDL2在不同线程上通过共享对象正确设置多个OpenGL上下文?
最佳答案
我不知道我们找到的解决方案是否可以在SDL支持的每个平台上使用,但是至少在Windows和Linux / X11上都可以使用。
问题是SDL_GL_MakeCurrent()
通常不是线程安全的(在Linux / X11上它似乎是线程安全的,但这是偶然的,因为SDL是一个多平台库(问题实际上是wglMakeCurrent()
,而不是SDL,因为旧的代码也可以在Wine下使用))。
因此,我们只需使用互斥锁来保护通话。在我们的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.
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]);
});
/* ... */