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

jyztefdp  于 2023-01-22  发布在  其他
关注(0)|答案(1)|浏览(329)

当我在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上下文?

0yycz8jy

0yycz8jy1#

我不知道我们找到的解决方案是否能在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);

std::mutex mtx;
// 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) {
    std::lock_guard<std::mutex> lock(mtx); // ← ↓ WORKINING CODE
    SDL_GL_MakeCurrent(window, threads_glcontexts[thread_idx]);
});

/* ... */

相关问题