使用FBO+RBO进行双缓冲和glFinish()

8
我正在使用FBO+RBO技术,并且不使用默认帧缓冲的双缓冲技术,而是将内容绘制到RBO上,然后在单个缓冲的OpenGL环境中直接将其复制到默认FBO(0)的GL_FRONT缓冲区。
这种方式看起来没有问题,也没有出现闪烁,但当场景变得较为复杂时,帧率会突然骤降90%,情况非常奇怪,我知道一定有什么地方不对。这里的问题不是因为跳帧导致的从60fps掉到30fps的那种情况,而是一个突然的大幅度帧率下降。
我尝试了在复制操作之后调用glFlush()函数,但没有任何效果,然后我试了试在复制操作之后调用glFinish()函数,结果帧率提升了10倍。
于是我改成了使用默认帧缓冲的双缓冲技术,通过swapbuffers()函数进行切换,发现帧率也有所提升,与使用glFinish()函数的效果差不多。
我无法弄清楚发生了什么事情,为什么调用glFinish()函数会产生如此大的影响,而且,在双缓冲环境中,是否可以直接在前缓冲区使用RBO进行复制,而不是调用swapbuffers()函数?我知道我忽略了垂直同步,但复合管理器仍会执行同步操作(实际上我没有看到任何撕裂),就像显示器少显示了10帧一样。
顺便问一下,在Windows或Linux上,本地的swapbuffers()函数是否使用了glFinish()函数呢?

“然后我在blit之后尝试了glFinish(),结果我的fps提高了10倍。” 这听起来更像是您的计时方法存在问题,可能与GPU同步不良(当然,使用glFinish可以解决这个问题)。如果能提供更多代码就更好了。 - Christian Rau
我不认为重新实现双缓冲会更好,因为驱动程序中有很多类似三重缓冲、自适应垂直同步等功能。 - Bartek Banachewicz
如果您需要缓冲区进行屏幕内外渲染,那么这是有意义的,但您确实需要花费相当多的工作来重新优化屏幕内部分。 - KillianDS
你真的看到帧率有区别吗?在更复杂的场景中,特别是有运动的情况下,你应该会注意到帧率下降了90%。此外:FPS是性能测量的一个令人困惑的单位,通常更喜欢使用渲染时间(例如每帧X毫秒)。 - KillianDS
2个回答

1

我认为这是一个与同步相关的问题。

当直接渲染到RBO并将其复制到前缓冲区时,根本没有同步。因此,在复杂场景下,GPU命令队列会很快填满,然后CPU驱动程序队列也会很快填满,直到OpenGL命令期间驱动程序强制进行CPU同步。此时,CPU线程将被暂停。

我的意思是,在没有任何形式的同步的情况下,复杂的渲染(其中一个或多个OpenGL命令将被放入队列中)将始终导致CPU线程在某些时候被暂停,因为随着队列填充,CPU将发出越来越多的命令。

为了获得平稳(更加持续)的用户交互,需要同步(使用特定于平台的swapbuffers()或glFinish()),以防止CPU发出越来越多的命令使情况变得更糟(这反过来会使CPU线程稍后停止)

参考文献: OpenGL Synchronization


CPU发出越来越多的指令不应该使实际渲染变得更糟,但它会使CPU和GPU失去同步,CPU可能认为它已经渲染了20帧,而GPU只完成了1帧。但我不认为CPU在队列中发布许多命令会对可见帧率产生太大影响(尽管测量帧率...)。然而,这只是一点直觉,现在无法尝试:). - KillianDS

1
这里有几个不同的问题,它们也有一些联系。
1)自己重新实现双缓冲,虽然在规范上是相同的事情,但对于驱动程序来说却并非如此。驱动程序针对常见情况进行高度优化。例如,许多芯片具有不同的2D和3D单元。swapBuffers中的交换通常由2D单元处理。缓冲区的blitting可能仍然使用3D单元完成。
2)许多驱动程序忽略glFlush(和Finish)。Flush是客户端服务器渲染的遗物。Finish旨在进行分析。但它被滥用为解决驱动程序错误的方法。因此,现在驱动程序经常忽略它,以提高使用Finish作为解决方法的旧代码的性能。
3)不要使用单缓冲。没有性能优势,并且您正在使用驱动程序的“好”路径。窗口管理器已经针对双缓冲OpenGL进行了超级优化。
4)你看到的很像是资源泄漏。您是否分配了未释放的缓冲区?检查的一种快速而简单的方法是任何glGen *函数是否返回逐渐增加的ID。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接