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



我有顶点缓冲区保存地形块的网格。每当玩家编辑地形时,相应块的网格必须重新生成并上传到顶点缓冲区。由于重新生成网格需要一些时间,所以我在异步工作线程中进行。

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

这对我来说是有意义的,我们当然不应该同时写入和读取相同的数据。因此,我没有更新旧的缓冲区,而是创建了一个新的缓冲区,填充它并交换它们。交换只是改变了存储在地形块结构中的缓冲区id,所以这应该是原子的。然而,这并没有帮助。

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

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

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

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

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

最新更新