当我在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()在某些线程中失败,并出现以下错误:
  • wglMakeCurrent():句柄无效
  • wglMakeCurrent():不支持请求的转换操作

  • 问题:如何使用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]);
    });
    
    /* ... */
    

    07-24 09:45
    查看更多