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

标签 c++ c multithreading opengl sdl-2

当我在我的 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
});

/* ... */
这段代码在 Linux 下开发时运行了好几个月,直到我将游戏移植到 Windows。然后事情就出了问题:在 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,因为旧代码也有效在葡萄酒下))。
    所以我们只需要使用互斥锁来保护调用。在我们的 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]);
    });
    
    /* ... */
    

    关于c++ - 如何使用 SDL2 为每个线程设置一个共享的 OpenGL 上下文?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64484835/

    相关文章:

    c++ - 如何打开 .a 文件

    c - Do...While 逻辑运算符循环

    无法在数组中拆分和存储十六进制字符串

    multithreading - Delphi 中更好的多线程调试

    java - 在屏幕上重复加载/删除多个图像的最佳方法

    c# - SynchronizationContext 可以不在 WinForms/WPF 应用程序中使用吗?

    c++ - 使用 boost 和标准 C++ 进行 Unicode 安全查找

    C++ WinApi : ReadDirectoryChangesW() Receiving Double Notifications

    c - 在 C 和汇编中移动

    c++ - 像素着色器中的 Sprite 调色板交换