我正在尝试开发一些非常低延迟的图形应用程序,并且对通过OpenGL绘制屏幕所花费的时间感到非常沮丧。我在网上发现的每一个讨论都解决了优化OpenGL管道的问题,但并没有接近我需要的结果。
检查出来:
https://www.dropbox.com/s/dbz4bq67cxluhs7/MouseLatency.MOV?dl=0
你之前可能已经注意到了这一点:使用c ++ OpenGL应用程序,在屏幕上拖动鼠标,并在OpenGL中绘制鼠标位置,OpenGL落后3或4帧。显然,OSX CAN 以非常低的延迟将[光标]绘制到屏幕,但OpenGL要慢得多。所以,假设我不需要做任何花哨的OpenGL渲染。我只是想以某种方式将像素推向屏幕。有没有办法让我完全绕过OpenGL并更快地绘制屏幕?或者这种功能是否会被锁定在我无法达到的内核内部?
答案 0 :(得分:3)
我知道你在这里询问OS X,但我要讨论的实现细节是实现这些东西最明智的方式,我希望看到其他系统也能这样工作。
您可以在应用程序级别启用的三重缓冲为交换链添加第三个缓冲区,该缓冲区将被同步以进行刷新。这种做三重缓冲的方法确实增加了延迟,因为必须显示第三个缓冲区,并且在发生这种情况之前不允许任何东西触摸它(这是D3D的强制行为 - 行为和功能本身是未定义的在OpenGL中);但桌面窗口管理器(Windows)的工作方式略有不同。
我看到大多数驱动程序为桌面组合实现的行为是帧丢弃。在刷新之间完成多个帧的任何情况下,除了其中一个帧之外的所有帧都被丢弃。实际上,使用窗口而不是全屏+三重缓冲可以获得更低的延迟,因为当第三个缓冲区(由合成器拥有)有一个等待显示的完成帧时,它不会阻止缓冲区交换。
如果帧率不合理,它会创建一组完全不同的视觉问题。从技术上讲,属于丢帧的像素具有无限延迟,因此如果您需要绘制的每一帧都显示在屏幕上,以这种方式减少延迟的好处可能毫无价值。
我相信你可以通过禁用VSYNC并在窗口中绘图来在OS X(如果你想要的话)上获得这种行为。在这种情况下,VSYNC基本上只作为帧起搏(一致性的交易延迟)的一种形式,并且无论你使用什么速率,合成器本身都会消除撕裂。
任何现代窗口系统中的光标始终以最小延迟进行跟踪。图形硬件上有一个名为"硬件光标的功能,"在驱动程序存储光标位置然后每次刷新一次的情况下,硬件将光标覆盖在等待扫描输出的帧缓冲区中的任何位置。因此,即使您的应用程序在60 Hz显示屏上以30 FPS绘图,当使用硬件光标时,光标每16 ms更新一次。
这完全绕过了所有图形API,但是非常有限(例如它使用操作系统定义的游标)。
如果您的问题是输入延迟,那么您可以通过减少预渲染帧的数量并避免三重缓冲来缓解这种情况。 我无法开始告诉你如何减少OS X上驱动程序预渲染帧的数量。
如果您的问题是渲染循环执行之间经过的时间,那么您将采用另一种方式。增加预渲染帧,在窗口中绘制并禁用VSYNC。您可能会遇到大量已绘制但未在此方案中显示的帧。
预渲染帧是一个功能强大的小功能,您无法在OpenGL API级别进行控制。它设置了允许驱动程序管理所有内容的深度,并根据所需的任务,通过摆弄它来交换不同类型的延迟。许多玩家发誓将此值设置为1,以最小化输入延迟,同时降低整体帧率和平滑度。"
预渲染帧是多帧延迟的一个原因。以跨平台方式修复此问题很困难(它是驱动程序设置),但如果您可以访问Fence同步对象,则可以产生与强制执行 1 相同的行为。
如果需要,我可以更详细地解释这一点,一般的想法是你在缓冲区交换后插入一个fence同步,然后在允许下一帧的第一个命令开始之前等待它发出信号。性能可能会下降,但延迟将被最小化,因为CPU不再能够在GPU之前渲染。
答案 1 :(得分:2)
这里有许多延迟。
在典型的交互式应用程序中,您有一个通常会发生的事件循环
使用通常的方式来写入事件更新显示循环,在接下来的迭代的前一步骤和步骤1的步骤5之间几乎没有延迟。这意味着步骤2,3和4使用滞后一个帧周期的数据进行操作。
所以这是延迟的第一个来源。
许多图形管道支持三重缓冲,以实现更流畅的显示更新。它不仅仅保留一个背部和前部缓冲区,而且还有一个第三个缓冲区。绘制这些缓冲区的平均速率是显示刷新周期。缓冲区本身正好在显示刷新周期。因此,这会增加另一个延迟的帧周期。
如果你在带有窗口合成器的系统上运行(这是MacOS X的默认设置),这会有效地增加另一个缓冲阶段,所以如果你有双缓冲模式,它会给你三倍缓冲如果你有一个三重缓冲区,它会给你一个" quad"缓冲区(这里引用,因为四缓冲区是一个通常用于立体渲染的术语)。
你能做些什么:
Windows通过DWM API和MacOS X允许关闭合成或绕过合成器。
尝试尽可能晚地收集和集成用户输入(使用高分辨率睡眠)。如果你只有一个非常简单的场景,你可以将绘图推到V-Sync截止日期附近;实际上,NVidia OpenGL实现具有特定于供应商的扩展,允许在下一个V-Sync之前的特定时间内休眠。
如果您的场景很复杂,但是在需要低延迟用户输入的部分中可以分离,而且它们并不重要,您可以提前绘制更高延迟的内容,并且只在最后时刻将用户输入集成到它。当然,如果使用鼠标来控制观察方向,或者甚至更糟糕的是你为VR头戴式显示器渲染,事情就会变得困难。