我有保存地形 block 网格的顶点缓冲区。每当玩家编辑地形时,相应 block 的网格必须重新生成并上传到顶点缓冲区。由于重新生成网格需要一些时间,我在异步工作线程中进行。
问题是主线程在工作线程上传新数据的同时绘制缓冲区。这意味着,在玩家编辑地形后,损坏的 block 会被渲染一帧。它只是突然爆发一次,然后绘制正确的缓冲区。
这对我来说很有意义,我们当然不应该同时写入和读取相同的数据。因此,我没有更新旧缓冲区,而是创建了一个新缓冲区,填充并交换它们。交换只是更改存储在地形 block 结构中的缓冲区 ID,因此应该是原子的。然而,这并没有帮助。
由于 OpenGL 命令被发送到 GPU 上的队列,因此当 CPU 上的应用程序继续运行时,它们不必执行。所以我可能在新缓冲区真正准备好之前交换了缓冲区。
我还尝试了一种切换缓冲区的替代方法,使用互斥锁进行缓冲区访问。主线程在绘制时锁定互斥体,工作线程在上传新缓冲区数据时锁定它。然而,这也无济于事,这也可能是因为 OpenGL 的异步特性。主线程实际上并没有绘制,只是向 GPU 发送绘制命令。另一方面,当真的只有一个命令队列时,上传缓冲区和绘制缓冲区永远不会同时发生,不是吗?
如何同步来自我的两个线程的顶点缓冲区访问,以防止为一帧绘制未定义的缓冲区?
最佳答案
您必须确保缓冲区更新实际上已完成,然后才能在绘图线程中使用该缓冲区。最简单的解决方案是调用 glFinish
在您发出所有更新 GL 命令后,在您的更新线程中,并且仅在返回后通知绘图线程。
要对同步进行更细粒度的控制,我建议您查看栅栏同步对象(如 GL_ARB_sync 扩展中所述)。您可以在发出更新命令后发出栅栏同步,并实际将同步对象句柄与缓冲区句柄一起存储,以便绘制线程可以检查更新是否实际完成(或等待它)。请注意,同步对象有点特殊,因为它们是唯一不绑定(bind)到 GL 上下文的对象,因此它们可以在多上下文设置中使用。
关于multithreading - 如何同步多线程 OpenGL 缓冲区访问?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22363644/