为啥 glBufferSubData 需要等到 glDrawElements 不使用 VBO?

Posted

技术标签:

【中文标题】为啥 glBufferSubData 需要等到 glDrawElements 不使用 VBO?【英文标题】:Why glBufferSubData need to wait until the VBO is not used by glDrawElements?为什么 glBufferSubData 需要等到 glDrawElements 不使用 VBO? 【发布时间】:2014-06-14 13:48:10 【问题描述】:

在 OpenGL Insights 中,它说“OpenGL 驱动程序必须等待,因为使用了 VBO 通过前一帧的 glDrawElements"。

这让我很困惑。 据我所知,glBufferSubData 会将数据复制到临时内存,然后再传输到 GPU。

那么为什么司机还需要等待呢?它可以只是将 Transfer 命令附加到命令队列中,延迟将数据传输到 GPU 直到 glDrawElements 完成,对吧?

----- 已添加 ----------------------------------------- ----------------------------------

在 OpenGL Insights 中,它说:

http://www.seas.upenn.edu/~pcozzi/OpenGLInsights/OpenGLInsights-AsynchronousBufferTransfers.pdf(第 397 页)

但是,当使用 glBufferSubData 或 glMapBuffer[Range] 时,没有任何内容 API 本身阻止我们修改当前使用的数据 由设备渲染上一帧,如图 28.3. 驱动程序必须通过阻塞函数来避免这个问题,直到不再使用所需的数据:这被称为 隐式同步。

在 Valve 和 NVIDIA 的“Beyond Porting”中,它说:

http://media.steampowered.com/apps/steamdevdays/slides/beyondporting.pdf

MAP_UNSYNCHRONIZED

避免应用程序-GPU 同步点(CPU-GPU 同步点) 但会导致客户端和服务器线程序列化 这个 强制服务器线程中的所有待处理工作完成 相当不错 昂贵(几乎总是需要避免)

他们都指出glBufferSubData/glMapBuffer会阻塞应用线程,而不仅仅是驱动线程。

为什么会这样?

【问题讨论】:

这就是“等待”的意义所在。它会延迟传输,直到旧命令完成。 就是这个意思。驱动程序无法使用新内容开始传输和覆盖 GPU 内存中的缓冲区,因为它当前正在使用中。只是避免这种行为。同一本书应该提出几个替代方案:使用循环缓冲区集、缓冲区孤立、ARB_buffer_storage... 谢谢大家,我已经更新了问题,请再次检查。 我在下面发布了一个解释它的答案。 【参考方案1】:

没有规定司机必须等待。它需要确保在使用旧内容的绘制调用完成执行之前缓冲区内容没有被修改。并且它需要在glBufferSubData()调用返回之前消耗调用者传入的数据。只要结果行为正确,驱动程序中的任何实现都是公平的。

让我们用一个典型的伪调用序列来说明问题,标记调用以便稍后解释:

(1) glBindBuffer(buf)
(2) glBufferSubData(dataA)
(3) glDraw()
(4) glBufferSubData(dataB)
(5) glDraw()

游戏中的限制是:

调用(2)返回后,驱动程序无法访问dataA指向的数据。 OpenGL 规范允许调用者在调用返回后对数据执行任何操作,因此需要在调用返回之前由驱动程序使用。 调用(4)返回后,驱动无法访问dataB指向的数据。 调用(3)产生的draw命令需要在buf的内容为dataA时执行。 在buf的内容为dataB时,需要执行调用(5)产生的绘图命令。

由于 OpenGL 固有的异步特性,有趣的情况是调用 (4)。假设此时dataA 已存储在buf 中,调用(3) 的绘制命令已排队等待GPU 执行。但是我们还不能依赖 GPU 已经执行了该绘制命令。所以我们不能将dataB 存储在buf 中,因为挂起的绘制命令必须由GPU 执行,而dataA 仍然存储在buf 中。但是在我们消费 dataB 之前,我们无法从通话中返回。

有多种方法可以处理这种情况。蛮力解决方案是简单地阻止调用 (4) 的执行,直到 GPU 完成执行调用 (3) 中的绘图命令。这肯定会奏效,但可能会产生非常糟糕的性能影响。因为我们要等到 GPU 完成工作才提交新工作,所以 GPU 可能会暂时空闲。这通常被称为管道中的“泡沫”,是非常不可取的。最重要的是,在调用返回之前,应用程序也会被阻止执行有用的工作。

解决此问题的最简单方法是让驱动程序在调用 (4) 中复制 dataB,然后在 GPU 完成调用 (3) 中的绘制命令后将此数据副本放入 buf ),但在调用 (5) 的绘制命令执行之前。缺点是它涉及额外的数据复制,但防止管道泡沫通常是值得的。

【讨论】:

非常感谢。我已更新问题,请再次检查。 我发现有几篇文章说它会造成管道气泡,这就是为什么我很困惑。 seas.upenn.edu/~pcozzi/OpenGLInsights/…(第397页) 您在问题中添加了glMapBuffer 案例。我对此了解不多,但我认为可以类似地处理。我想我解释了glBufferSubData 的问题,以及如何在有阻塞和无阻塞的情况下在驱动程序中处理它。我的回答中是否有任何具体部分不清楚? 但是它说“当使用 glBufferSubData ...驱动程序必须通过阻塞函数来避免这个问题,直到不再使用所需的数据”,根据你的回答,它不需要阻塞.

以上是关于为啥 glBufferSubData 需要等到 glDrawElements 不使用 VBO?的主要内容,如果未能解决你的问题,请参考以下文章

glBufferSubData 啥时候返回? [复制]

每帧多次调用 glBufferSubData

OpenGL glBufferSubData 偏移问题

glBufferData 和 glBufferSubData 偏移量

带有 glMapBuffer 的 Buffer 会被分配比 glBufferSubData 更慢的 VRAM 吗?

结构的 glBufferSubData 偏移量