【问题标题】:How long does it take for OpenGL to actually update the screen?OpenGL 实际更新屏幕需要多长时间?
【发布时间】:2014-02-16 09:15:10
【问题描述】:

我在 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()就在下一次调用之前的时间仅比这少一点,尽管绘制的内容非常简单。交换缓冲区调用所需的时间远低于 1 毫秒。

但是,从应用更改其绘制内容以响应按键按下到更改实际显示在屏幕上的时间是 >150 毫秒(大约 8-9 帧)。这是通过以 60fps 的屏幕和键盘的摄像头记录来测量的。 (注意:确实,我没有办法测量从按键到应用程序获取它需要多长时间。我假设它是

因此,问题:

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

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

  3. 应用程序可以做些什么来使上述立即绘制尽可能快地发生?

  4. 应用程序如何知道当前屏幕上实际显示的是什么? (或者,当前缓冲延迟有多长/多少帧?)

【问题讨论】:

标签: c opengl opengl-es 3d latency


【解决方案1】:
  • 在调用交换缓冲区和实际显示在屏幕上之间,图形在哪里缓冲?为什么延迟?看起来该应用始终在屏幕前绘制许多帧。

如果您设置了swapInterval,则该命令已排队,无论是否绘制到后台缓冲区,都会等到下一个 vsync 并且在下一个 vsync 时,应该显示此缓冲区。

  • OpenGL 应用程序可以做什么来立即在屏幕上绘制? (即:没有缓冲,只是阻塞直到绘制完成;我不需要高吞吐量,我需要低延迟)

使用glFinish 将确保在此 API 返回之前绘制所有内容,但除了 swapInterval 设置之外,无法控制它实际到达屏幕的时间。

  • 应用程序可以做些什么来使上述立即绘制尽可能快地发生? 应用程序如何知道现在屏幕上实际显示的是什么? (或者,当前缓冲延迟有多长/多少帧?)

通常你可以使用同步(类似http://www.khronos.org/registry/egl/extensions/NV/EGL_NV_sync.txt)来找出这个。

您确定测量延迟的方法是正确的吗?如果键输入实际上在您的 PC 中有明显的延迟怎么办?您是否测量了从代码中收到事件到交换缓冲区之后的延迟?

【讨论】:

  • 好信息,谢谢。一些cmets:“无论什么被吸引到后台缓冲区,都等到下一个vsync” - 你的意思是每个发送的命令都会在下一个vsync时(一次)显示?我开始认为会发生什么,直到交换缓冲区的每个命令都在下一次 vsync 时显示,其余的留在队列中。 “使用 glFinish 将确保......” - 听起来正是我需要的,某种限制命令队列大小的方法。 “EGL_NV_sync.txt” - 太好了!
  • “您是否测量了从代码中收到事件到交换缓冲区之后的延迟?” - 是的,它是 16-32 毫秒。传播是因为事件是在画框 1 期间的随机点接收到的,然后影响画框 2。
【解决方案2】:

啊,是的,您已经发现了只有很少人真正理解的 OpenGL 和显示系统交互的特性之一(坦率地说,我直到大约 2 年前才完全理解它)。那么这里发生了什么:

SwapBuffers 做了两件事:

  1. 它将(私有)命令排队到命令队列,该命令队列也用于 OpenGL 绘图调用,本质上是标记到图形系统的缓冲区交换
  2. 它使 OpenGL 刷新所有排队的绘图命令(到后台缓冲区)

除了 SwapBuffers 本身什么也不做。但是这两件事会产生有趣的后果。一种是 SwapBuffers 将立即返回。但是,一旦(通过排队命令)设置了“要交换后台缓冲区”标志,后台缓冲区就会被锁定以进行任何会改变其内容的操作。所以只要没有调用会改变后台缓冲区的内容,事情就不会阻塞。并且会改变后台缓冲区内容的命令将暂停 OpenGL 命令队列,直到后台缓冲区被交换并释放以供进一步的命令使用。

现在 OpenGL 命令队列的长度是一个抽象的东西。但通常的行为是,OpenGL 绘图命令之一会阻塞,等待队列刷新以响应交换缓冲区的发生。

我建议您使用一些高性能、高分辨率的计时器作为时钟源,在您的程序中添加日志语句,以查看延迟发生的确切位置。

【讨论】:

    【解决方案3】:

    您必须了解 GPU 有专门的可用内存(板载)。在最基本的层面上,此内存用于保存您在屏幕上看到的编码像素(它也用于图形硬件加速和其他东西,但现在不重要了)。因为将帧从主 RAM 加载到 GPU RAM 需要时间,所以您可以获得闪烁效果:在短时间内,您会看到背景而不是应该显示的内容。虽然这种复制发生得非常快,但肉眼很明显,而且很烦人。

    为了解决这个问题,我们使用了一种称为双缓冲的技术。基本上双缓冲的工作原理是在您的 GPU RAM 中有一个额外的帧缓冲区(这可以是一个或多个,取决于您正在使用的图形库和 GPU,但两个就足够了)并使用指针来指示应该哪个帧被显示。因此,在显示第一帧时,您已经在代码中使用一些 draw() 函数在主 RAM 中的图像结构上创建下一帧,然后将此图像复制到您的 GPU RAM(同时仍显示前一帧)和然后当调用eglSwapBuffers() 时,指针切换到您的后台缓冲区(我从你的问题中猜到了,我不熟悉OpenGL,但这很普遍)。你可以想象这个指针切换不需要太多时间。我希望您现在看到,直接将图像写入屏幕实际上会导致更多延迟(以及令人讨厌的闪烁)。

    另外,~16.6 毫秒听起来也不算多。我认为大部分时间都浪费在创建/设置所需的数据结构上,而不是真正用于绘图计算本身(您可以通过仅绘制背景来测试这一点)。

    最后我想补充一点,I/O 通常很慢(大多数程序中最慢的部分),而 150 毫秒根本没有那么长(仍然是眨眼速度的两倍)。

    【讨论】:

    • 我确实熟悉 GPU 内存和双缓冲。这并没有解释显示如何落后 八帧。我想了解延迟发生的位置,以及如何将其减少到(理想情况下)一帧或更少。
    • 集成显卡怎么样?
    【解决方案4】:

    延迟将由驱动程序和显示器本身决定。即使你直接写入硬件,你也会受到后者的限制。

    应用程序只能做这么多(即快速绘制,尽可能接近或在绘制期间处理输入,甚至可能在翻转时修改缓冲区)来缓解这种情况。之后,您将受到其他工程师的摆布,包括硬件和软件。

    如果没有外部监控,您无法判断延迟是多少,就像您所做的那样。

    另外,也不要假设您的输入(键盘到应用程序)是低延迟的!

    【讨论】:

    • 确实,显示刷新限制了尽可能低的延迟。我只是想接近那个。后面八帧不接近:)关于输入时间的观点是公平的,我应该研究一下。
    猜你喜欢
    • 1970-01-01
    • 2019-11-18
    • 2014-03-25
    • 2022-01-21
    • 1970-01-01
    • 2016-03-21
    • 2010-12-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多