OpenGL:用于加载资源的次要线程?

11

我正在使用C语言和Win32 API,想要知道如何实现一个用于加载资源(纹理和VBO)的OpenGL次线程。

根据我找到的信息,应该使用wglShareLists()实现,但是我不确定该如何设置次线程:

我需要一个新的设备上下文还是只需要一个新的渲染上下文?

我需要调用哪些wgl函数?

3个回答

9
您不需要新的上下文,因为您可以重用第一个设备上下文。顺便说一下,您可以指定另一个设备上下文,但是根据您的平台,您应该注意它(在Windows上,设备上下文必须具有相同的像素格式),否则您可能无法在两个上下文之间共享对象。
在主线程中创建两个上下文,第二个上下文与第一个上下文共享。然后,在主线程上将第一个上下文设置为当前上下文,同时将另一个上下文设置为次要线程上的当前上下文。 请注意,您可以与任何渲染上下文共享:所有共享上下文都通过其名称“看到”相同的对象,实际上它们共享一个对象名称空间。两个不同的对象名称空间(即两个非共享上下文)可以定义相同的对象(即纹理对象名称为1),但是相同的名称实际上取决于当前上下文而指向不同的对象。
由次要线程创建的对象同时且一致地可见。但是,并非所有对象都可以在上下文之间共享。请记住,有时驱动程序支持意外的对象,而其他时候驱动程序无法正确支持预期的对象。
OpenGL正在成为面向对象的语言。您可以看到创建对象的某种模式:
生成名称(GenTextures,GenBuffers) 定义对象(BindTexture,BindBuffer) 对象存在(IsTexture,IsShader,IsFramebuffer) 删除名称(和对象)
(请注意,使用Gen例程创建的对象仅在绑定时存在)
对象类可能是
显示列表 纹理对象 缓冲区对象 着色器对象和程序对象 渲染缓冲区对象 帧缓冲区对象 查询对象 同步对象 变换反馈对象
我建议使用“运行时”测试,例如以下内容:
private bool TestSharingObject(RenderContext rContextShare)
{
    uint texture = 0;

    // rContextShader is a context sharing with this RenderCOntext

    this.MakeCurrent(true);

    if (Caps.TextureObject.Supported == true) {
        // Generate texture name
        Gl.GenTextures(1, out texture);
        // Ensure existing texture object
        Gl.BindTexture(Gl.TEXTURE_2D, texture);
        Gl.BindTexture(Gl.TEXTURE_2D, 0);
        // Self test
        if (Gl.IsTexture(texture) == false)
            throw new NotSupportedException();
    }

    // Make current sharing context
    rContextShare.MakeCurrent(true);

    return ((texture != 0) && (Gl.IsTexture(texture) == true));
}

另一个建议是在次要线程上运行CPU密集型操作,而不直接影响绘图系统窗口缓冲区。一个很好的例子是着色器编译,因为编译在CPU端运行;还要记住驱动程序可以异步执行您的操作,并且OpenGL实现可以管道化不同的操作...

1

在你的次要线程中以任何你需要的方式加载资源,然后将它们的所有权传回到主线程进行GL调用。

在GL上下文之间共享“列表”应该可以工作。在主线程上执行所有GL调用确实有效 :)


3
在之前的一个项目中,我们非常希望找到一种将资源在另一个线程中进行子加载的方法,尽管我们花费了几周时间尝试不同的方法,但它从未能可靠地工作。嗯...虽然“可以”工作,但是驱动程序在第二个线程中上传内容时会获取全局锁定,阻止第一个线程的操作,这就使得整个目的毫无意义。+1 - bernie
谢谢,现在我不会浪费时间尝试制作第二个OpenGL线程了(我考虑过按照你说的做,但我认为如果第二个线程也可以上传到OpenGL,那会更有效率)。 - Jonathan
@bernie 如果这些操作如此频繁,为什么要在不同的线程上运行?相反,长时间运行的任务可以在不同的线程上运行(着色器编译、资源数据加载、顶点处理)。 - Luca
1
@Luca Piccioni 我们有接近实时的限制(即从不低于60hz),并且有巨大的环境(数百GB),随着视点移动逐步分页。纹理为4kx4k texels,因此上传整个纹理需要约2ms。 16ms中的2ms太多了。这就是第二个OpenGL上传线程会简化问题的地方,但由于它实际上会导致更多的故障,因此我们每帧以小块上传纹理。您提到的长时间运行的任务如果没有通过这种方式获得故障,也将是很好的选择。 - bernie

0

只有在主线程上通过wglShareLists完成所有工作线程时,它才能正常工作。 使用消息映射,传递渲染上下文的引用,并让主线程创建渲染上下文,仅由主线程使用wglShareLists。然后,在工作线程上使用由主线程创建的渲染上下文调用wglMakeCurrent。在对渲染上下文进行任何gl操作之前,必须先调用wglShareLists。这是因为参数hglrc2(与hglrc1共享显示列表的OpenGL渲染上下文)要求在调用wglShareLists时不包含任何现有的显示列表。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接