当我在OpenGL游戏中实现程序对象的并行初始化和更新时,我必须创建多个具有共享对象的OpenGL上下文,并为每个线程绑定一个上下文,这样我就可以并行创建/更新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
});
/* ... */
这段代码在Linux下开发的几个月里运行得非常好,直到我把游戏移植到Windows上。在英特尔和AMD GPU上,总是在启动时崩溃。在Nvidia上,它大部分时间都能正常工作,但运行几次,它也在同一个地方崩溃:其中一个池线程发出的第一个OpenGL调用,即glGenBuffers()
。
最终我们发现wglGetCurrentContext()
在出错线程上返回了0
,这使我们发现SDL_GL_MakeCurrent()
在某些线程上失败,错误如下:
- wglMakeCurrent(接通电流):句柄无效
- wglMakeCurrent(接通电流):不支持请求的转换操作
问题是:如何使用SDL2在不同线程上正确设置多个具有共享对象的OpenGL上下文?
1条答案
按热度按时间0yycz8jy1#
我不知道我们找到的解决方案是否能在SDL支持的每个平台上工作,但它至少能在Windows和Linux/X11上工作。
问题是
SDL_GL_MakeCurrent()
通常不是线程安全的(它在Linux/X11上似乎是线程安全的,但这是偶然的,因为SDL是一个多平台库(问题实际上是wglMakeCurrent()
,而不是SDL,因为旧代码也在Wine下工作))。所以我们只需要用互斥锁来保护调用。在我们的C++代码中,它看起来像: