关于c ++:opengl:glFlush()与glFinish()

opengl: glFlush() vs. glFinish()

我很难区分调用glFlush()glFinish()的实际区别。

文档说glFlush()glFinish()会将所有缓冲的操作推送到OpenGL,以便可以确保它们都将被执行,不同之处在于glFlush()立即返回,其中glFinish()阻塞,直到所有操作都被执行为止。完成。

阅读了定义之后,我发现如果使用glFlush(),可能会遇到向OpenGL提交比其可执行的操作更多的问题。因此,为了尝试,我将我的glFinish()换成了glFlush(),瞧瞧,我的程序运行了(据我所知),完全一样。帧速率,资源使用情况,一切都一样。

所以我想知道这两个调用之间是否有很大区别,或者我的代码是否使它们运行没有区别。还是应该使用一个与另一个。
我还发现OpenGL会调用glIsDone()之类的命令来检查glFlush()的所有缓冲命令是否完整(因此,向OpenGL发送操作的速度不会比执行它们快)。我找不到这样的功能。

我的代码是典型的游戏循环:

1
2
3
4
while (running) {
    process_stuff();
    render_stuff();
}

请注意,这些命令自OpenGL诞生以来就存在。 glFlush确保先前的OpenGL命令必须在有限时间内完成(OpenGL 2.1规格,第245页)。如果直接绘制到前缓冲区,则应确保OpenGL驱动程序开始绘制时不会有太多延迟。您可能会想到一个复杂的场景,当您在每个对象之后调用glFlush时,它们在屏幕上一个接一个地出现。但是,使用双缓冲时,glFlush实际上几乎没有任何作用,因为直到交换缓冲后更改才可见。

在完全实现先前发出的命令的所有效果之前,glFinish不会返回。这意味着程序的执行将在这里等待,直到绘制完每个最后一个像素,OpenGL不再需要执行其他操作。如果直接渲染到前端缓冲区,则在使用操作系统调用获取屏幕截图之前,将进行glFinish调用。对于双重缓冲,它用处不大,因为您看不到强制完成的更改。

因此,如果您使用双重缓冲,则可能不需要glFlush和glFinish。 SwapBuffers隐式将OpenGL调用定向到正确的缓冲区,无需先调用glFlush。而且不要介意OpenGL驱动程序:glFlush不会阻塞太多命令。不能保证此调用立即返回(无论这意味着什么),因此它可能需要任何时间来处理您的命令。


正如其他答案所暗示的那样,按照规范,确实没有好的答案。 glFlush()的一般意图是,调用它后,主机CPU将无需执行与OpenGL相关的工作-命令将被推送到图形硬件。 glFinish()的一般意图是返回后,不会剩下任何剩余的工作,并且所有适当的非OpenGL API(例如从帧缓冲区,屏幕截图等读取的内容)也应可获得结果。那是否真的发生取决于驱动程序。该规范为合法性提供了很大的自由度。


我也总是对这两个命令感到困惑,但是这张图让我很清楚:
Flush vs Finish
显然,除非累积了一定数量的命令,否则某些GPU驱动程序不会将发出的命令发送到硬件。在此示例中,该数字为5。
该图显示了已发出的各种OpenGL命令(A,B,C,D,E ...)。正如我们在顶部看到的那样,命令尚未发出,因为队列尚未满。

在中间,我们看到glFlush()如何影响排队的命令。它告诉驱动程序将所有排队的命令发送到硬件(即使队列尚未满)。这不会阻塞调用线程。它只是通知驱动程序我们可能不会发送任何其他命令。因此,等待队列填满将浪费时间。

在底部,我们看到了使用glFinish()的示例。它几乎与glFlush()做相同的事情,除了它使调用线程等待,直到所有命令都已被硬件处理为止。

图片摘自《使用OpenGL进行高级图形编程》一书。


如果您没有发现任何性能差异,则说明您做错了什么。正如其他一些人提到的那样,您不需要调用任何一个,但是如果您调用glFinish,那么您将自动失去GPU和CPU可以实现的并行性。让我深入探讨:

实际上,您提交给驱动程序的所有工作都会被分批处理,并在以后可能的方式(例如在SwapBuffer时)发送到硬件。

因此,如果您要调用glFinish,则实际上是在迫使驱动程序将命令推送到GPU(直到那时批处理,并且从未要求GPU继续工作),并暂停CPU,直到完全推送了命令为止被执行。因此,在GPU整个工作期间,CPU不会工作(至少在此线程上)。而且,在CPU一直工作(主要是批处理命令)的所有时间中,GPU不会执行任何操作。是的,glFinish应该会损害您的性能。 (这是一个近似值,因为如果很多命令已经被批处理,驱动程序可能会开始让GPU处理某些命令。不过这并不典型,因为命令缓冲区往往足够大以容纳很多命令)。

现在,您为什么还要调用glFinish?我唯一使用过的时间是当我遇到驱动程序错误时。的确,如果您发送给硬件的命令之一使GPU崩溃,那么确定罪魁祸首的最简单选择就是在每次绘制之后调用glFinish。这样,您可以缩小触发崩溃的确切范围

附带一提,Direct3D之类的API根本不支持Finish概念。


glFlush实际上可以追溯到客户端服务器模型。您通过管道将所有gl命令发送到gl服务器。该管道可能会缓冲。就像任何文件或网络I / O可能会缓冲一样。 glFlush只说"即使缓冲区尚未满,现在也要发送缓冲区!"。在本地系统上,这几乎是不需要的,因为本地OpenGL API不太可能自身缓冲,而是直接发出命令。同样,所有导致实际渲染的命令都将执行隐式刷新。

另一方面,glFinish用于性能测量。对GL服务器的一种PING。它往返执行一条命令,并一直等到服务器响应"我空闲"。

如今,现代的本地驾驶员已经有了非常有创意的想法,尽管闲着意味着什么。是"绘制了所有像素"还是"我的命令队列有空间"?同样是因为许多旧程序无缘无故地在其代码中撒满了glFlush和glFinish,因为伏都教编码使许多现代驱动程序只是将它们视为"优化"。真的不能怪他们。

因此,总而言之:在实践中,不要将glFinish和glFlush视作没有操作,除非您要为古老的远程SGI OpenGL服务器编码。


在这里看看。简而言之,它说:

glFinish() has the same effect as glFlush(), with the addition that glFinish() will block until all commands submitted have been executed.

另一篇文章介绍了其他差异:

  • 交换功能(在双缓冲应用程序中使用)自动刷新命令,因此无需调用glFlush
  • glFinish强制OpenGL执行出色的命令,这不是一个好主意(例如使用VSync)

综上所述,这意味着使用双缓冲时甚至不需要这些功能,除非交换缓冲区实现不会自动刷新命令。


似乎没有一种查询缓冲区状态的方法。有一个Apple扩展程序可以达到相同的目的,但它似乎不是跨平台的(没有尝试过。)乍一看,在flush之前,您应该先推入fence命令。然后,您可以查询该围栏在缓冲区中移动时的状态。

我想知道是否可以在缓冲命令之前使用flush,但在开始渲染下一帧之前调用finish。这样一来,您就可以在GPU工作时开始处理下一帧,但是如果在返回时还没有完成,则finish会阻塞以确保一切都处于新状态。

我还没有尝试过,但是我会尽快的。

我已经在一个甚至可以使用CPU和GPU的旧应用程序上进行过尝试。 (它最初使用finish。)

当我将其更改为结尾处的flush和开始处的finish时,没有立即出现的问题。 (一切看起来都很好!)程序的响应能力增强了,这可能是因为CPU并没有在GPU上等待而停止。绝对是更好的方法。

为了进行比较,我从帧的开头删除了finished,保留了flush,并且执行了相同的操作。

所以我会说使用flushfinish,因为当调用finish的缓冲区为空时,不会影响性能。而且我猜测如果缓冲区已满,您还是应该要finish


问题是:您是希望代码在执行OpenGL命令时继续运行,还是仅在执行OpenGL命令后才运行?

在某些情况下(例如由于网络延迟),只有在绘制图像等之后才具有某些控制台输出,这可能很重要。