如何同步多线程OpenGL缓冲区访问?

时间:2014-03-12 20:57:59

标签: multithreading opengl gpu undefined-behavior vbo

我有顶点缓冲区,包含地形块的网格物体。每当玩家编辑地形时,必须重新生成相应块的网格并将其上载到顶点缓冲区。由于重新生成网格需要一些时间,因此我在异步工作线程中执行此操作。

问题是主线程在工作线程上传新数据的同一时刻绘制缓冲区。这意味着,在玩家编辑地形之后,一个损坏的块会被渲染一帧。它只会爆发一次,之后会得到正确的缓冲区。

这对我来说很有意义,我们不应该在同一时间写入和读取相同的数据。因此,我没有更新旧缓冲区,而是创建了一个新缓冲区,填充它并交换它们。交换只是改变了存储在terrain chunk结构中的缓冲区id,因此应该是原子的。不过,这没有用。

由于OpenGL命令被发送到GPU上的队列,因此当CPU上的应用程序继续时,不必执行它们。所以我可能在新缓冲区准备就绪之前交换了缓冲区。

我还尝试了使用互斥锁进行缓冲区访问来切换缓冲区的替代方法。主线程在绘制时锁定互斥锁,工作线程在上载新缓冲区数据时将其锁定。然而,这也没有帮助,也可能是因为OpenGL的异步性质。主线程实际上没有绘制,只是将绘图命令发送到GPU。另一方面,当真的只有一个命令队列时,上传缓冲区和绘制它们可能永远不会同时发生,是吗?

如何从两个线程同步顶点缓冲区访问以防止为一帧绘制未定义的缓冲区?

1 个答案:

答案 0 :(得分:5)

在绘制线程中使用该缓冲区之前,必须确保实际完成缓冲区更新。最简单的解决方案是在发出所有更新GL命令后在更新线程中调用glFinish,并且只在返回后通知绘制线程。

为了对同步进行更细粒度的控制,我建议您查看 fence sync 对象(如GL_ARB_sync扩展名中所述)。您可以在发出更新命令后发出fence同步,并实际将同步对象句柄与缓冲区句柄一起存储,以便绘制线程可以检查更新是否实际完成(或等待它)。请注意,同步对象有点特殊,因为它们是绑定到GL上下文的唯一对象,因此它们可以在多上下文设置中使用。