opengl:glFlush()与glFinish()
我很难区分调用glFlush()
和glFinish()
之间的实际区别。
文档说glFlush()
和glFinish()
会把所有缓冲的操作都推送到OpenGL,这样可以确保所有的操作都会被执行,区别在于glFlush()
立即返回glFinish()
阻塞,直到所有的操作完成。
读过这些定义之后,我发现如果我使用glFlush()
,那么我可能会遇到向OpenGL提交更多操作的问题。 所以,试试看,我把glFinish()
换成了glFlush()
,然后看,我的程序跑了(据我所知),完全一样; 帧率,资源使用情况,一切都是一样的。
所以我想知道这两个调用之间是否有很大差别,或者如果我的代码使它们运行没有什么不同。 或者应该使用哪一个而不是另一个。 我还想到OpenGL会有一些像glIsDone()
这样的调用来检查glIsDone()
所有缓冲命令是否完成(所以不会比OpenGL更快地发送操作),但是我找不到这样的function。
我的代码是典型的游戏循环:
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不会窒息太多的命令。 不保证这个调用立即返回(不pipe这意味着什么),所以它可以花费任何时间来处理你的命令。
正如其他答案所暗示的那样,根据规范确实没有好的答案。 glFlush()
的一般意图是在调用它之后,主机CPU将没有与OpenGL相关的工作 – 命令将被推送到graphics硬件。 glFinish()
的一般意图是在返回之后, 不剩下剩余的工作,并且结果也应该可用于所有适当的非OpenGL API(例如从帧缓冲区读取,截图等)。 这是否真的是驾驶者依赖的。 该规范允许一个纬度,什么是合法的。
如果你没有看到任何性能差异,这意味着你做错了什么。 正如其他人提到的,你不需要调用,但如果你打电话glFinish,那么你会自动失去了GPU和CPU可以实现的并行性。 让我进一步深入:
实际上,您提交给驱动程序的所有工作都是批处理的,并可能在稍后(例如SwapBuffer时间)发送到硬件。
所以,如果你打电话给glFinish,你基本上是强迫驱动程序将命令推送到GPU(它的批处理,直到那时,并没有要求GPU工作),并停止CPU,直到推送的命令完全执行。 所以在GPU工作的整个过程中,CPU不会(至less在这个线程上)。 而且CPU一直在做它的工作(主要是批处理命令),GPU不会做任何事情。 所以是的,glFinish会伤害你的performance。 (这是一个近似值,因为驱动程序可能会启动GPU在某些命令上工作,如果它们中的很多已经被批处理的话,这并不典型,因为命令缓冲区往往足够大以容纳相当多的命令)。
现在,你为什么要叫glFinish呢? 我曾经使用过的唯一一次是当我有驱动程序错误。 事实上,如果发送给硬件的命令之一使GPU崩溃,那么确定哪个命令是罪魁祸首的最简单的方法是在每次Draw之后调用glFinish。 这样,你可以缩小究竟是什么触发了崩溃
作为一个侧面说明,像Direct3D这样的API根本不支持Finish概念。
我总是对这两个命令感到困惑,但是这个形象让我清楚: 显然有些GPU驱动程序不会将已发布的命令发送到硬件,除非已经累积了一定数量的命令。 在这个例子中,这个数字是5 。
该图显示了已经发布的各种OpenGL命令(A,B,C,D,E …)。 正如我们在顶部看到的那样,这些命令还没有被发布,因为队列还没有满。
在中间,我们看到glFlush()
如何影响排队的命令。 它告诉驱动程序将所有排队的命令发送到硬件(即使队列还没有满)。 这不会阻塞调用线程。 它只是向驾驶员表明我们可能不会发送任何其他命令。 因此等待排队填满将是浪费时间。
在底部,我们看到一个使用glFinish()
的例子。 它几乎与glFlush()
完全相同,除了它使调用线程等待,直到所有的命令都被硬件处理。
从“使用OpenGL的高级graphics编程”一书中获得的图像。
glFlush的确可以追溯到客户端服务器模型。 您通过pipe道将所有的gl命令发送到gl服务器。 那个pipe道可能会缓冲。 就像任何文件或networkingI / O可能缓冲。 glFlush只说:“现在发送缓冲区,即使它还没有满!”。 在本地系统上,这几乎是不需要的,因为本地OpenGL API不太可能自我缓冲,只是直接发出命令。 而且所有导致实际渲染的命令都会执行隐式刷新。
在另一方面glFinish是为了性能测量。 GL服务器的一种PING。 它往返一个命令,并等待,直到服务器响应“我闲置”。
如今的现代,当地的司机们有着相当有创意的想法。 是“所有像素被绘制”还是“我的命令队列有空间”? 也因为许多旧的程序在无代码的情况下将glFlush和glFinish作为巫术编码散布在许多现代驱动程序中,而忽略它们作为“优化”。 不能怪他们,真的。
所以总结:在实践中将glFinish和glFlush都视为无操作,除非您正在编写古老的远程SGI OpenGL服务器。
看看这里 。 总之,它说:
glFinish()与glFlush()具有相同的效果,另外glFinish()将会阻塞,直到所有提交的命令都被执行完毕。
另一篇文章介绍了其他区别
- 交换函数(用于双缓冲的应用程序)自动刷新命令,所以不需要调用
glFlush
-
glFinish
强制OpenGL执行优秀的命令,这是一个坏主意(例如,与VSync)
总而言之,这意味着在使用双缓冲时,甚至不需要这些函数,除非您的swap-buffers实现不会自动刷新命令。
似乎没有办法查询缓冲区的状态。 有这个苹果扩展可以用于相同的目的,但它似乎并不是跨平台的(没有尝试过)。快速一瞥,似乎在flush
之前,你会推入围栏命令; 然后您可以查询该篱笆在缓冲区中的状态。
我想知道在缓冲命令之前是否可以使用flush
,但是在开始渲染下一帧之前,你可以调用finish
。 这将允许您在GPU工作时开始处理下一帧,但是如果在返回时没有完成,则会阻止finish
,以确保一切都处于最新状态。
我没有尝试过,但我很快就会。
我已经尝试了一个旧的应用程序,有相当的CPU和GPU使用。 (它最初使用finish
。)
当我改变它flush
结束并开始finish
时,没有立即的问题。 (一切看起来都不错!)程序的响应性增加了,可能是因为CPU没有在GPU上等待。 绝对是一个更好的方法。
为了比较,我从框架开始删除finished
,留下flush
,并执行相同的。
所以我会说使用flush
和finish
,因为当调用finish
时缓冲区为空时,没有性能影响。 而且我猜如果缓冲区已满,你应该想finish
。
问题是:你想让你的代码在OpenGL命令执行的时候继续运行,或者只在你的OpenGL命令执行完毕后才能运行。
这在像networking延迟那样的情况下可能很重要,只有在图像被绘制之后才具有特定的控制台输出。