【问题标题】:How to setup one shared OpenGL contexts per thread with SDL2?如何使用 SDL2 为每个线程设置一个共享的 OpenGL 上下文?
【发布时间】:2021-02-05 14:57:46
【问题描述】:

当我在我的 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。然后事情就出了问题:在 Intel 和 AMD GPU 上,总是在启动时崩溃。在 Nvidia 上,它大部分时间都可以工作,但运行几次后,它也在同一个地方崩溃:其中一个池线程发出的第一个 OpenGL 调用,它是glGenBuffers()

最终我们发现wglGetCurrentContext() 在有问题的线程上返回了0,这导致我们发现SDL_GL_MakeCurrent() 在某些线程中失败,并出现以下错误:

  • wglMakeCurrent():句柄无效
  • wglMakeCurrent():不支持请求的转换操作

问题:如何使用 SDL2 在不同线程上正确设置多个具有共享对象的 OpenGL 上下文?

【问题讨论】:

    标签: c++ c multithreading opengl sdl-2


    【解决方案1】:

    我不知道我们找到的解决方案是否适用于 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]);
    });
    
    /* ... */
    

    【讨论】:

      猜你喜欢
      • 2014-01-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-07-26
      • 1970-01-01
      • 1970-01-01
      • 2013-06-03
      • 2018-10-01
      相关资源
      最近更新 更多