Opengl: glFlush() vs glFinish()

我无法区分调用 glFlush()glFinish()之间的实际区别。

文档说,glFlush()glFinish()将把所有缓冲操作推到 OpenGL,这样就可以确保它们都将被执行,不同之处在于 glFlush()会立即返回到 glFinish()阻塞的位置,直到所有操作完成。

在阅读了定义之后,我认为如果使用 glFlush(),我可能会遇到向 OpenGL 提交的操作比它能执行的操作多的问题。所以,仅仅是为了尝试,我把我的 glFinish()换成了 glFlush(),然后你瞧,我的程序运行(据我所知) ,完全一样; 帧速率,资源使用,所有的一切都是一样的。

所以我想知道这两个调用之间是否有很大的区别,或者我的代码使它们运行起来没有什么不同。或者一个应该用在哪里,另一个应该用在哪里。 我还认为 OpenGL 会有一些类似于 glIsDone()的调用来检查 glFlush()的所有缓冲命令是否完成(所以不会比执行操作更快地将操作发送到 OpenGL) ,但是我找不到这样的函数。

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

while (running) {
process_stuff();
render_stuff();
}
77450 次浏览

看看 给你。简而言之,它说:

GlFinish ()具有与 glFlush ()相同的效果,并且 glFinish ()将阻塞该效果,直到所有提交的命令都执行完毕。

另一个 文章描述了其他差异:

  • 交换函数(在双缓冲应用程序中使用)会自动刷新命令,因此不需要调用 glFlush
  • glFinish强制 OpenGL 执行未执行的命令,这是一个坏主意(例如使用 VSync)

总之,这意味着在使用双缓冲时甚至不需要这些函数,除非交换缓冲区实现不自动刷新命令。

似乎没有查询缓冲区状态的方法。有这样的 苹果扩展,可以服务于同样的目的,但它似乎不跨平台(还没有尝试过。)快速浏览一下,似乎在 flush之前您应该输入篱笆命令; 然后您可以在篱笆通过缓冲区时查询它的状态。

我想知道您是否可以在缓冲命令之前使用 flush,但是在开始呈现下一帧之前,您可以调用 finish。这将允许您在 GPU 工作时开始处理下一帧,但是如果在您返回时还没有完成,finish将阻塞以确保所有内容都处于新的状态。

我还没试过,但很快就会试了。

我曾经在一个使用 CPU 和 GPU 的老应用程序上试用过它(它最初使用的是 finish)

当我把它改为 flush在结束和 finish在开始,没有立即的问题。(一切看起来都很好!)程序的响应速度增加了,可能是因为 CPU 没有停止等待 GPU。绝对是个更好的方法。

为了进行比较,我从帧的开始删除了 finished,留下了 flush,它执行了相同的操作。

因此,我建议使用 flushfinish,因为当调用 finish时缓冲区为空时,不会影响性能。我猜如果缓冲区是满的,你应该想要 finish无论如何。

The question is: do you want your code to continue running while the OpenGL commands are being executed, or only to run after your OpenGL commands has been executed.

这在一些情况下很重要,比如通过网络延迟,使得某些控制台输出的图像只有 之后

正如其他答案所暗示的那样,根据规范,确实没有好的答案。glFlush()的总体意图是,在调用它之后,主机 CPU 将没有与 OpenGL 相关的工作要做——命令将被推送到图形硬件。glFinish()的总体意图是,在返回之后,没有剩下的工作就剩下了,所有适当的非 OpenGL API (例如从 framebuffer 读取、屏幕截图等)都应该可以得到结果。.).这种情况是否真的会发生取决于驾驶员。该规范允许在什么是合法的问题上有很大的自由度。

请注意,这些命令从 OpenGL 的早期就存在了。GlFlush 确保以前的 OpenGL 命令 必须在有限的时间内完成(OpenGL 2.1规范,第245页)。如果您直接绘制到前端缓冲区,这将确保 OpenGL 驱动程序在没有太多延迟的情况下开始绘制。您可以想象一个复杂的场景,当您在每个对象后面调用 glFlush 时,在屏幕上出现一个又一个对象。但是,当使用双缓冲时,glFlush 实际上没有任何效果,因为在交换缓冲区之前,更改是不可见的。

glFinish does not return until all effects from previously issued commands [...] are fully realized. This means that the execution of your program waits here until every last pixel is drawn and OpenGL has nothing more to do. If you render directly to the front buffer, glFinish is the call to make before using the operating system calls to take screenshots. It is far less useful for double buffering, because you don't see the changes you forced to complete.

因此,如果使用双缓冲,则可能不需要 glFlush 或 glFinish。SwapBuffers 隐式地将 OpenGL 调用指向正确的缓冲区 没必要先调用 glFlush。并且不要介意强调 OpenGL 驱动程序: glFlush 不会因为太多的命令而窒息。不能保证这个调用返回 马上(不管这是什么意思) ,因此它可能需要任何时间来处理您的命令。

如果您没有看到任何性能差异,这意味着您正在做一些错误的事情。正如其他一些人提到的,您不需要调用任何一个,但是如果您调用 glFinish,那么您将自动失去 GPU 和 CPU 可以实现的并行性。让我深入探讨一下:

实际上,您提交给驱动程序的所有工作都是批处理的,并可能在更晚的时候(例如在 SwapBuffer 时间)发送到硬件。

所以,如果你调用 glFinish,你实际上是强迫驱动程序将命令推送到 GPU (在此之前它一直在批处理,从来没有要求 GPU 工作) ,并拖延 CPU 直到推送的命令完全执行。所以在 GPU 工作的整个过程中,CPU 不工作(至少在这个线程上)。而且在 CPU 工作的所有时间(主要是批处理命令) ,GPU 不做任何事情。所以是的,glFinish 应该会影响你的表现。(这是一个近似值,因为如果很多命令已经批处理了,驱动程序可能会开始让 GPU 处理一些命令。但这并不典型,因为命令缓冲区往往足够大,可以容纳相当多的命令)。

Now, why would you call glFinish at all then ? The only times I've used it were when I had driver bugs. Indeed, if one of the commands you send down to the hardware crashes the GPU, then your simplest option to identify which command is the culprit is to call glFinish after each Draw. That way, you can narrow down what exactly triggers the crash

顺便说一句,Direct3D 这样的 API 根本不支持 Finish 概念。

GlFlush 可以追溯到客户机服务器模型。通过管道将所有 gl 命令发送到 gl 服务器。那根管子可以缓冲一下。就像任何文件或网络输入输出可能会缓冲。GlFlush 只说“现在发送缓冲区,即使它还没有满!”.在本地系统上,几乎不需要这样做,因为本地 OpenGLAPI 不太可能缓冲自身,只是直接发出命令。此外,所有导致实际呈现的命令都将执行隐式刷新。

另一方面,glFinish 是为性能测量而制作的。类似于对 GL 服务器的 PING。它往返于命令之间,并等待服务器响应“我是空闲的”。

现在,当地的司机有很多创造性的想法什么意思是闲着。是“所有像素被绘制”还是“我的命令队列有空间”?还因为许多老程序在他们的代码中无缘无故地使用 glFlush 和 glFinish,就像巫术编码一样,许多现代驱动程序只是把它们当作一种“优化”而忽略了它们。这不能怪他们,真的。

因此,总结一下: 在实践中,除非您正在为一个古老的远程 SGI OpenGL 服务器编码,否则不要将 glFinish 和 glFlush 视为任何操作。

我也一直对这两个命令感到困惑,但这幅图让我明白了一切: Flush vs Finish 显然,一些 GPU 驱动程序不会将发出的命令发送到硬件,除非已经积累了一定数量的命令。在这个例子中,这个数字是 5
The image shows various OpenGL commands (A, B, C, D, E...) that have been issued. As we can see at the top, the commands don't get issued yet, because the queue isn't full yet.

在中间,我们看到 glFlush()如何影响排队的命令。它告诉驱动程序将所有排队的命令发送到硬件(即使队列还没有满)。这不会阻止调用线程。它只是向驱动程序发出信号,表明我们可能不会发送任何额外的命令。因此,等待队列填满将是浪费时间。

在底部,我们看到一个使用 glFinish()的示例。除了让调用线程等到硬件处理完所有命令之外,它几乎与 glFlush()做同样的事情。

图片来自“使用 OpenGL 的高级图形编程”一书。