OpenGL实际上需要多长时间来更新屏幕?


9

我在C中有一个简单的OpenGL测试应用程序,它根据按键输入绘制不同的内容。(Mesa 8.0.4,在配备NVIDIA GTX650的PC上与Mesa-EGL和GLFW,Ubuntu 12.04LTS一起尝试过)。绘制非常简单/快速(旋转三角形类型的东西)。我的测试代码没有以任何方式故意限制帧速率,它看起来像这样:

while (true)
{
    draw();
    swap_buffers();
}

我对此进行了非常仔细的计时,发现从一个eglSwapBuffers()(或glfwSwapBuffers相同的东西)调用到下一个调用的时间约为16.6毫秒。eglSwapBuffers()即使画出的内容非常简单,从通话后到下次通话前的时间也仅比该时间少一点。交换缓冲区调用花费的时间不到1ms。

但是,从应用程序响应按键更改其绘画到实际在屏幕上显示更改的时间> 150ms(价值约8-9帧)。这是通过以60fps的屏幕和键盘的摄像机记录来测量的。

因此,问题是:

  1. 在调用交换缓冲区与实际显示在屏幕之间的绘制在哪里缓冲?为什么要延迟?看起来该应用始终都在屏幕前绘制许多帧。

  2. OpenGL应用程序可以做什么以立即绘制屏幕?(即:无缓冲,仅阻塞直到绘制完成;我不需要高吞吐量,我需要低延迟)

  3. 应用程序可以做些什么来使上述立即抽奖尽快发生?

  4. 应用程序如何知道当前屏幕上实际显示的内容?(或者,当前缓冲延迟是几帧/几帧?)


您是否可以将键盘通知应用程序所需的时间与实际呈现所需的时间分开?
ThorinII 2014年

@ThorinII:公平点。我可能可以使用并口等上的LED设置一些骇人听闻的内容,以指示应用程序何时真正按下按键。仍然只是fgetc(stdin),那有多慢?:/
Alex I

我给人的印象是glFlush用于立即刷新所有命令。
Programmdude

1
@AlexI检查这个gamedev.stackexchange.com/questions/66543/…可能会帮助您
concept3d 2014年

1
@ concept3d:是的,基本上已经回答了,只是以为您想要一些额外的代表:)显然,我必须等待一天才能将其授予。
Alex I

Answers:


6

从CPU调用的所有绘图API函数都将提交到GPU 命令环缓冲区,以供GPU稍后执行。这意味着OpenGL函数主要是非阻塞函数。因此,CPU和GPU将并行工作。

需要注意的最重要的事情是您的应用程序可以绑定CPU或GPU。一旦调用glFinish,CPU应该等待GPU完成绘制命令,如果GPU花费更多时间并且可能/导致CPU停止运行,则您的应用程序受GPU约束。如果GPU完成绘制命令,而CPU花费的时间太长,无法完成glFinish,则您的应用程序受CPU限制。

请注意,glFlush和之间有区别glFinish

glFlush:表示先前已发送到GL的所有命令必须在有限时间内完成。

glFinish:强制所有先前的GL命令完成。在完全实现以前发出的命令对GL客户端和服务器状态以及帧缓冲区的所有影响之前,Finish不会返回。”

glXSwapBuffers在返回之前执行隐式的glFlush。后续的OpenGL命令可以在调用glXSwapBuffers之后立即发出,但是直到缓冲区交换完成后才执行。

实际帧时间很可能由两个CPU / GPU中的哪个花费更多时间来完成工作来确定。


这非常有帮助。可能的更正:opengl.org/sdk/docs/man2/xhtml/glXSwapBuffers.xml “ glXSwapBuffers在返回之前执行隐式的glFlush”,看来这实际上并没有做glFinish,因此命令缓冲区可以包含很多交换缓冲区返回时将其填充。
Alex I

...另外,我认为最有趣的一点是,我非常简单的测试应用程序既不受CPU的限制,又不受GPU的限制-在这里也没有太多工作要做-但是,在某种程度上,它最终都以低帧率(与显示器完全相同)刷新率?)和高延迟(由于命令缓冲区,实际上您对此部分的解释很好)。
Alex I

@AlexI both low framerate (exactly same as monitor refresh rate??否,除非您明确使用VSync。
concept3d

@AlexI我还想指出帧时间不同于FPS,请使用帧时间来分析您的应用程序,因为它是线性的。另一方面,FPS的测量效果不好,不是线性的。
concept3d

我没有明确使用vsync。我没有做任何更改默认vsync设置的操作,甚至不知道在OpenGL中更改这些设置的位置。我刚好得到60fps(不超过一个百分点)。
Alex I

4

从技术上讲,OpenGL从不更新屏幕。

有一个与GL(例如GLX,WGL,CGL,EGL)分开的窗口系统API可以执行此操作。使用这些API的缓冲区交换通常会隐式调用,glFlush (...)但在某些实现中(例如Windows上的GDI光栅化器),它会执行完整操作glFinish (...)

 

    *左侧是穿过的ICD(硬件)路径SwapBuffers (...)。在右侧,是GDI(软件)路径。

如果启用了VSYNC和双缓冲,则任何在挂起交换发生之前修改后备缓冲区的命令都必须停滞直到交换。命令队列的深度有限,因此该停止的命令最终将导致管道阻塞。这可能意味着在SwapBuffers (...)VBLANK出现之前,不是阻塞应用程序而是阻塞一些无关的GL命令。真正归结为交换链中有多少个后备缓冲区。

只要所有的后缓冲区都充满了尚未完成的帧,但仍要移到前,交换缓冲区将隐式导致阻塞。可悲的是,没有办法显式控制大多数GL窗口系统API使用的反向缓冲区的数量(除了0个单缓冲或1个双缓冲)。如果需要,驱动程序可以自由使用2个后备缓冲区(三重缓冲),但是您不能在应用程序级别使用GLX或WGL之类的请求。


安东:好消息,谢谢。我认为我看到的部分是双缓冲,但是:“必须在交换发生前尝试修改后台缓冲区,否则必须阻止”-为什么?听起来一切都可以发送到命令缓冲区,包括交换:)
Alex I

“明确控制大多数GL窗口系统API使用的后备缓冲区的数量(除了0个单缓冲或1个双缓冲)”-如何控制一个单缓冲/双缓冲?
Alex I

@AlexI:我澄清了有关命令为什么会导致阻塞的语言。在其他API中,有一个称为“预先渲染”的概念,实际上是命令队列的深度。至于控制单/双缓冲,这由创建渲染上下文时选择的像素格式控制。在WGL中,可以选择单缓冲或双缓冲,但是如果用户选择驱动程序,驱动程序实际上可能会创建2个后备缓冲区(因此,双缓冲变为三重缓冲)。
Andon M. Coleman

您实际上不希望在前面渲染太多帧,因为虽然这将减少阻塞,但同时也会增加延迟。最小化延迟的最佳方法实际上是glFinish (...)在交换缓冲区后立即调用。这将清除命令队列,但这也意味着GPU和CPU将被同步(如果您想一直保持GPU正常工作,那就不好了)。
Andon M. Coleman

1

我以为您熟悉这个实验

本质上,约翰·卡马克(John Carmack)在做类似的事情,记录屏幕并计时发送到屏幕的像素。他发现很多延迟都来自屏幕。其他因素是键盘,视频驱动程序的输入延迟和/或程序本身的执行过程。

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.