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

Multithreading 如何同步多线程OpenGL缓冲区访问?,multithreading,opengl,gpu,undefined-behavior,vbo,Multithreading,Opengl,Gpu,Undefined Behavior,Vbo,我有保存地形块网格的顶点缓冲区。每当玩家编辑地形时,必须重新生成相应块的网格并上传到顶点缓冲区。因为重新生成网格需要一些时间,所以我在异步工作线程中进行 问题在于,主线程在工作线程上载新数据的同时绘制缓冲区。这意味着,在玩家编辑地形后,一个损坏的块将被渲染一帧。它只会爆发一次,然后,正确的缓冲区就会被绘制出来 这对我来说很有意义,我们当然不应该同时写和读相同的数据。因此,我没有更新旧的缓冲区,而是创建了一个新的缓冲区,填充并交换它们。交换只是更改存储在terrain chunk结构中的缓冲区id

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

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

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

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

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


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

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


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

如果我正确理解您的情况,我必须说。。。它不应该那样工作。缓冲区更新与它们进行隐式同步,每个对
glBufferSubData(…)
之类的调用实际上都是原子的。如果实现符合要求,则不必担心部分写入会出现在另一个线程中。也就是说,一开始尝试这样做会产生大量的开销-您是否考虑过工作线程写入的循环缓冲区(例如后台缓冲区),而渲染线程从另一个(例如前台)读取?顺便问一下,您使用API的哪一部分来更新这些缓冲区?您是在映射和取消映射缓冲区,还是在使用
glBufferSubData(…)
?@AndonM.Coleman:hmm.我只是假设daijar正在使用多个上下文,否则这个问题就没有意义了。@danijar:
glBufferData(…)
不会更改数据,它会分配一个全新的数据存储并用数据填充它。您通过调用所做的操作称为缓冲区孤立。管道中使用旧数据存储的任何挂起命令都不受此影响。如果涉及两个单独的上下文,则为“是”。它们具有独立的命令队列、状态机等。实际上,上下文共享所做的只是允许您跨多个上下文引用同一对象。如果每个命令都是从单个上下文发出的,那么您不会从相同的同步保证中获益。这听起来很有趣。“更新线程”是指更新顶点缓冲区的工作线程,是吗?@danijar:是的,没错。很好。我的目标是使用旧的缓冲区,直到新的缓冲区上传到GPU,我不能让渲染器无所事事地等待。您将如何处理此问题?@danijar:使用循环缓冲区,您将始终拥有一个完整的
前端
缓冲区,当后台线程在
后端
缓冲区上工作时,该缓冲区将被显示。背景线程完成后,可以交换正面和背面。更新可能需要一个额外的帧才能显示,但至少你不会再出现部分更新的问题。@danijar:你可以使用我在回答中概述的ARB_同步方法。工作线程将更新的缓冲区排入队列,绘制线程检查队列的头是否实际完成了更新。如果是这样,它可以使用它。否则,它可以继续使用旧缓冲区进行渲染,并在下一帧再次检查。