如何使用SDL2设置每个线程共享一个OpenGL上下文?

8
当我在我的OpenGL游戏中实现过程对象的并行初始化和更新时,我必须创建多个共享对象的OpenGL上下文,并为每个线程绑定一个,以便我可以并行地创建/更新VBO。
我从这篇博客文章中得到了如何做的想法,并且像这样进行了第一次实现(用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后出现了问题。 在Intel和AMD的GPU上,它总是在启动时崩溃。在Nvidia上,它大多数时候都可以工作,但是运行几次之后也会在相同的地方崩溃:由其中一个池线程发出的第一个OpenGL调用,它是glGenBuffers()
最终,我们发现wglGetCurrentContext()在有问题的线程上返回0,这导致我们发现对于一些线程,SDL_GL_MakeCurrent()失败,并显示以下错误:
  • wglMakeCurrent():句柄无效
  • wglMakeCurrent():不支持所请求的转换操作
问题是如何使用SDL2在不同的线程上正确设置共享对象的多个OpenGL上下文?
1个回答

7
我不知道我们找到的解决方案是否适用于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); // ← ↓ WORKING CODE
    SDL_GL_MakeCurrent(window, threads_glcontexts[thread_idx]);
});

/* ... */

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接