在尝试使用OpenGL作为后端为X11制作合成窗口管理器时,我陷入了一个令人讨厌的情况,其中glXSwapBuffers()阻塞,直到vblank渲染合成器对X事件无响应为止,这导致窗口被拖动到光标后方大约滞后于光标一帧。我已经尝试了多线程,但是效果不佳,所以我决定唯一合适的解决方案是使glXSwapBuffers()异步。希望将绘制命令发送到GPU并立即返回,而不必等待操作实际完成,而AFAIK在具有DRI2的现代Linux中是可能的。所以我该怎么做?
最佳答案
实际上glXSwapBuffers
应该立即返回。但是,阻塞的是下一个引入了所谓的同步点的OpenGL命令。通常,这是glClear
调用之后的下一个glXSwapBuffers
。
请注意,实际上希望以某种方式与V-Blank同步,否则会发生令人讨厌的撕裂痕迹。但是您是对的,在一个简单的实现中,这引入了大约一个显示刷新间隔的延迟。
这里的最大问题是,重定向到屏幕外表面的双缓冲窗口可能仍会受到有效的交换间隔(即V-Sync设置)的影响;当然,在复合设置中,双缓冲本身并没有多大意义。
因此,您可以执行以下操作:使用交换间隔扩展名将合成器的交换间隔设置为0(无垂直同步)。根据系统的设置,实际上可能不接受该选择(用户配置的所有应用程序都将强制进行V同步)。不幸的是,有几个交换间隔扩展,并且与一种显示驱动程序一起使用的不适用于另一种。我建议您看一下Mesa的交换间隔示例程序以及Mesa的glxgears的源代码,其中包含的代码几乎可以处理您可能遇到的每种情况。
还希望以某种方式关闭客户端中的V-Sync。我没有比将共享对象注入(inject)到它们中更好的方法了,钩住glXSwapBuffers
,glXCreateContext
和交换间隔扩展来覆盖它们。
最后,您必须使用可用的视频同步GLX扩展之一在合成器中实现定时缓冲区交换(即,在V空白发生的恰好时机,调用“未同步的” glXSwapBuffers
)。通过将直接OpenGL上下文和实时调度策略应用于合成器过程,您可以做到这一点。
请注意,所有这些问题都是现有X11协议(protocol)的缺点。但是,如果您认为Wayland将摆脱这些问题,请再考虑一下。 Wayland原本打算使“每个框架都完美”并消除同步问题,但实际上,我再次遇到了许多问题。 Wayland的创建者在this blog post中谈到了往返和开销,但他完全避免了管道同步和缓冲区交换延迟的问题。这些问题是基于堆栈组成和基于缓冲区交换的V-Sync概念的固有问题。为了真正解决该问题,必须有某种与屏幕相关的垂直同步事件,该事件与图形操作无关,并且可以应用任意相位偏移,以便应用程序可以将其渲染循环与显示刷新同步。并且应该有一个附加的“framebuffer commit”功能,该功能使整个合成链考虑新到达的帧。这样可以使合成器在V-Blank发生之前将应用程序同步到几百微秒,这样就可以在帧缓冲区提交和V-Blank之间的余量中进行合成。