opengl:glFlush()与glFinish()


105

我在区分调用glFlush()和之间的实际区别方面遇到困难glFinish()

该文档说glFlush(),并glFinish()将推所有缓存操作的OpenGL,这样一个可以放心,他们都将被执行,不同之处在于glFlush()将立即返回,在那里为glFinish()块,直到所有操作完成。

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

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

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

while (running) {
    process_stuff();
    render_stuff();
}

Answers:


93

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

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

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


1
您是什么意思,glFlush“必须在有限的时间内完成”,然后又说,glFlush不会阻塞,因为“它可能需要任何时间来处理您的命令”?(加盖我的)那些不是互相排斥的吗?
Praxiteles 2014年

1
只要它在某个时间点结束,就符合FINITE时间标准。一些驱动程序完全不操作此调用。
chrisvarnz 2014年

SwapBuffers是特定于上下文的,只需要刷新调用线程上下文。如果呈现多个上下文,则应手动刷新每个上下文,因为在通过另一个上下文交换缓冲区时不会保证发生这种情况。
Andreas

22

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


18

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

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

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

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


我实际上知道该做什么glFlushglFinish但我不知道该图像在说什么。左边和右边是什么?另外,该图像是在公共领域发布还是在某种许可下发布的,允许您将其发布到Internet上?
Nicol Bolas's

我应该详细说明一下。关于许可证:实际上我不太确定。在进行研究时,我偶然发现了Google上的PDF。
塔拉

7

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

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

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

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

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


5
在“现在,为什么要一直调用glFinish”上。如果要在一个线程上加载资源(例如:纹理),然后使用它们通过wglShareLists或类似对象进行共享来在另一个线程上进行渲染,则需要先调用glFinish,然后再向渲染线程发出可以自由渲染异步加载的内容的信号。
Marco Mp

就像我在下面说的,这确实看起来像是驱动程序错误的解决方法。我找不到有关此要求的任何规格说明。在Windows上,驱动程序必须锁定,在Mac上,您必须执行CGLLockContext。但是使用glFinish似乎非常错误。要知道纹理何时有效,在任何情况下都必须执行自己的锁定或事件。
starmole

1
opencl opengl interop docs建议使用glFinish进行同步“在调用clEnqueueAcquireGLObjects之前,应用程序必须确保访问mem_objects中指定的对象的所有待处理的GL操作均已完成。这可以通过在所有对象上发出并等待glFinish命令来完成。 GL上下文以及对这些对象的待定引用”
Mark Essel 2014年

1
您无需“调用” glFinish,就可以使用同步对象。
chrisvarnz 2014年

补充一下,因为原始答案:某些GL实现开始使用flush / finish在不同线程上的共享上下文之间进行同步:developer.apple.com/library/ios/documentation/3DDrawing / ...-最著名的是Apple IOS。我认为从根本上讲,这是对API的另一种滥用。但这是对我的原始答案的警告:在某些实现中,需要完成/刷新。但是定义实现的方式和原因,而不是OpenGL规范。
starmole

7

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

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

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

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


4
错误。就像我在上面的答案中留下的评论一样:如果要在一个线程上加载资源(例如,纹理),然后使用它们通过wglShareLists或类似对象进行共享,从而在另一个线程上进行渲染,则需要先调用glFinish,然后再向渲染线程发出信号可以免费呈现异步加载的内容。而且这不是您所说的禁止操作。
Marco Mp

真?您能否支持在使用带有某些规范链接的共享库之前调用glFinish的需求?我完全可以看到一个需要这个的越野车司机。这又回到了我所说的。人们使用glFinish解决有问题的驱动程序。因此,可以正常工作的驱动程序会忽略它的性能。原始的规范使用情况分析(和单个缓冲渲染)不再起作用。
starmole

2
不得不处理损坏的纹理的个人经验以及以下内容:higherorderfun.com/blog/2011/05/26/… 如果您考虑一下,这是有道理的,因为它与使用互斥锁或其他同步没有太大区别。线程之间的api-上下文可以使用共享资源之前,另一个必须完成该资源的加载,因此需要确保缓冲区已清空。我认为规格更多是具体情况,而不是错误,但我对此声明没有证据:)
Marco Mp

很棒的答案,谢谢。特别是“许多旧程序在其代码中撒满了glFlush和glFinish,而没有理由成为巫毒教徒”。
2014年

真的没有行动吗?glFinish和/或glFlush对于高强度图像处理或基于GPU的数学协处理不是很有价值吗?例如,直到调用glFinish以确保已写入所有像素计算之后,我们才从像素缓冲区向CPU读取GPU计算的结果。我们错过了什么吗?
2014年

5

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

glFinish()与glFlush()具有相同的效果,另外glFinish()会阻塞,直到所有提交的命令都已执行。

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

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

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


是的,但是文档指出两者都具有相同的效果,例如,关于窗户系统的差异很小。但是无论如何,我编辑了我的答案;)
AndiDog 2010年

很高兴在细微差别上大声疾呼。它明确是否有必要调用glFlush(),然后glfinish在() - (我们阅读上面的一切后,有一个问题)
普拉克西特利斯

4

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

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

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

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

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

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

因此,我会说使用flushfinish,因为当调用时缓冲区为空时finish,不会对性能造成影响。而且我正在猜测缓冲区是否已满,finish无论如何您都应该。


0

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

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.