我有包含地形块网格的顶点缓冲区。每当玩家编辑地形时,相应块的网格必须重新生成并上传到顶点缓冲区。由于重新生成网格需要一些时间,我在异步工作线程中执行此操作。
问题在于主线程在同一时刻绘制缓冲区,而工作线程上传新数据。这意味着,在玩家编辑地形后,一个损坏的块会渲染一帧。它只会突然出现一次,之后就会正确地绘制缓冲区。
这对我来说有点合理,我们当然不应该同时写入和读取同一数据。因此,我创建了一个新的缓冲区,填充它并交换它们,而不是更新旧缓冲区。交换只是更改存储在地形块结构中的缓冲区ID,因此应该是原子的。但是,这并没有帮助到我。
由于OpenGL命令被发送到GPU上的队列中,它们不必在CPU上的应用程序继续执行时执行。因此,在新缓冲区实际准备好之前,我可能已经交换了缓冲区。
我还尝试了另一种切换缓冲区的替代方法,使用互斥锁来访问缓冲区。主线程在绘制时锁定互斥锁,而工作线程在上传新缓冲区数据时锁定它。然而,这也没有帮助,可能是由于OpenGL的异步性质。主线程实际上没有绘制,只是将绘制命令发送到GPU。另一方面,当真正只有一个命令队列时,上传缓冲区和绘制它们永远不会同时发生,对吗?
我该如何同步我的两个线程对顶点缓冲区的访问,以防止未定义的缓冲区被绘制一帧?
问题在于主线程在同一时刻绘制缓冲区,而工作线程上传新数据。这意味着,在玩家编辑地形后,一个损坏的块会渲染一帧。它只会突然出现一次,之后就会正确地绘制缓冲区。
这对我来说有点合理,我们当然不应该同时写入和读取同一数据。因此,我创建了一个新的缓冲区,填充它并交换它们,而不是更新旧缓冲区。交换只是更改存储在地形块结构中的缓冲区ID,因此应该是原子的。但是,这并没有帮助到我。
由于OpenGL命令被发送到GPU上的队列中,它们不必在CPU上的应用程序继续执行时执行。因此,在新缓冲区实际准备好之前,我可能已经交换了缓冲区。
我还尝试了另一种切换缓冲区的替代方法,使用互斥锁来访问缓冲区。主线程在绘制时锁定互斥锁,而工作线程在上传新缓冲区数据时锁定它。然而,这也没有帮助,可能是由于OpenGL的异步性质。主线程实际上没有绘制,只是将绘制命令发送到GPU。另一方面,当真正只有一个命令队列时,上传缓冲区和绘制它们永远不会同时发生,对吗?
我该如何同步我的两个线程对顶点缓冲区的访问,以防止未定义的缓冲区被绘制一帧?
glBufferSubData(...)
的函数实际上是原子性的。如果实现正确,您不必担心部分写入会显示在其他线程中。话虽如此,在尝试以这种方式执行时,存在大量的开销-您是否考虑过循环缓冲区,您的工作线程写入其中(例如后缓冲区),而渲染线程从另一个缓冲区读取(例如前缓冲区)? - Andon M. ColemanglBufferData
来更改数据。 - danijarglBufferData(...)
不会更改数据,它只是分配一个全新的数据存储并填充数据。你所做的是通过调用它来进行缓冲区孤立。任何在管道中使用旧数据存储的挂起命令都不会受到影响。 - Andon M. Coleman