【问题标题】:Multithreaded object loading while rendering with OpenGL使用 OpenGL 渲染时的多线程对象加载
【发布时间】:2020-05-31 08:05:37
【问题描述】:

我想在主程序显示加载屏幕时在单独的线程中加载一些纹理和网格,因为加载所有资源需要几秒钟。我正在使用 OpenGL 和 GLFW。我尝试使用以下代码来完成此操作:

    void *status;

    if(pthread_create(&loader, NULL, &loader_func, NULL))
    {
        fprintf(stderr, "Error creating loader thread\n");
        return 1;
    }

    while(_flags & FLAG_LOADING)
    {
        vec3 color = { 0.1, 0.3, 1.0 };
        if(glfwWindowShouldClose(window))
        {
            resource_destroy();
            glfwTerminate();
            return 0;
        }

        GL_CHECK(glClearColor(0.1, 0.1, 0.1, 1.0));
        GL_CHECK(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT));

        font_renderer_activate();
        render_string(&_font_menu, "Loading...", _width / 2, _height / 2, 64,
                color, ALIGN_V_CENTER | ALIGN_H_CENTER);

        glfwSwapBuffers(window);
        glfwPollEvents();
    }

    if(pthread_join(loader, &status))
    {
        fprintf(stderr, "Error joining loader and main thread\n");
        return 1;
    }

    if(*(int *)status)
    {
        fprintf(stderr, "Error loading resources\n");
        return 1;
    }

loader_func() 不渲染到屏幕上,仅使用 OpenGL 函数来创建 VAO、VBO 等并将数据加载到其中。

问题是,在加载文本显示在屏幕上并且加载完成后,屏幕上没有显示任何内容(编辑:除了文本 HUD)并且我在日志中收到很多调试错误消息(I' m 将所有 OpenGL 调用包装在一个宏中,该宏使用 glGetError 来检查错误):

main.c:588
glBindTexture(GL_TEXTURE_CUBE_MAP, cubemap_texture);
GL_Error 0x502: GL_INVALID_OPERATION
main.c:589
glDrawArrays(GL_TRIANGLES, OFFSET_SKYBOX, VERTICES_SKYBOX);
GL_Error 0x502: GL_INVALID_OPERATION
main.c:629
glDrawArrays(GL_TRIANGLES, OFFSET_SELECTOR, VERTICES_SELECTOR);
GL_Error 0x502: GL_INVALID_OPERATION

当我直接调用 loader_func 时,没有错误并且主渲染循环正常工作。

我读到要在另一个线程中使用 OpenGL 函数,需要调用 glfwMakeContextCurrent 但这在我的情况下不起作用,因为这样就不会渲染加载屏幕。我唯一的想法是利用像 SDL 这样的第二个库在加载时创建一个窗口,然后销毁它并使用 GLFW 创建一个新窗口以与 OpenGL 一起使用。这就是我想用 OpenGL 实现的吗?

【问题讨论】:

  • 我认为,如果您在主线程中执行所有操作(除了几秒钟无响应),您的 OpenGL 代码就可以正常工作?
  • 是的,就是这样。
  • 我的页边空白处显示了一些相关文章,例如stackoverflow.com/q/16203224/2740650stackoverflow.com/q/6172020/2740650。我认为它的要点是您 DO 需要在使用多个线程时使用glfwMakeContextCurrent。麻烦的是,你不能有两个线程拥有窗口。不知道为什么你的主循环如图所示必须忙等待,因为它实际上只是呈现一条消息。它是否需要使用 OpenGL 来呈现消息?如果是这样,请尝试使用线程锁定和glfwMakeContextCurrent(来回切换)。
  • 据我所知,glfw 仅支持通过 OpenGL 进行渲染。我想我会使用线程锁定的方法,因为我想绘制一个进度条,以便主线程将被告知进度更新。

标签: c multithreading opengl glfw


【解决方案1】:

处理这个问题的最简单方法是让主线程创建和管理所有 OpenGL 对象,而加载线程执行文件 IO(很容易成为加载中最慢的部分)。一旦加载线程完成加载特定资产,它可以通过将加载的数据传递到主线程,这可以完成最终的 OpenGL 上传部分。

毕竟,渲染加载屏幕并不是一个巨大的性能消耗或其他东西,因此在主线程上上传的成本将是最小的。这也允许你做加载条的事情,因为你的主线程会经常得到加载过程的结果,所以它随时知道加载完成了多少。

如果出于某种原因您绝对必须有两个线程都进行 OpenGL 调用,那么您还应该有两个 OpenGL 上下文,每个上下文都在不同的线程中,并且这两个上下文彼此共享对象。 GLFW 非常乐意提供此if you ask it nicely。正常创建主窗口,然后将GLFW_VISIBLE 提示设置为GLFW_FALSE,并创建第二个窗口(具有任意分辨率)。您应该将主窗口作为最后一个参数传递给glfwCreateWindow,以便两个上下文可以共享对象。然后,您可以在不同的上下文中将每个窗口设置为当前,就可以了。

请注意。在它们之间共享对象的上下文only share certain objects。不能共享引用其他对象的对象(查询对象也由于某种原因不可共享)。 VAO 引用缓冲区对象,因此它们不能被共享。因此,尝试在屏幕外上下文中创建它们是没有意义的。

【讨论】:

    猜你喜欢
    • 2012-06-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-19
    • 1970-01-01
    相关资源
    最近更新 更多