在尝试使用 OpenGL 作为后端为 X11 制作合成窗口管理器时,我陷入了一种令人讨厌的情况,即 glXSwapBuffers() 阻塞,直到 vblank 渲染合成器对 X 事件无响应,这导致窗口被拖动到大致落后于光标一帧。我尝试过多线程,但效果不佳,所以我决定唯一合适的解决方案是使 glXSwapBuffers() 异步。最好将绘图命令发送到 GPU 并立即返回,而无需等待操作实际完成,而 AFAIK 这在具有 DRI2 的现代 Linux 下是可能的。所以我该怎么做?
2 回答
其实glXSwapBuffers
应该马上回来。然而,阻塞的是下一个 OpenGL 命令,它引入了所谓的同步点。通常这是glClear
调用glXSwapBuffers
.
请注意,实际上希望以某种方式与 V-Blank 同步,否则会发生令人讨厌的撕裂伪影。但你是对的,在一个幼稚的实现中,这引入了一个显示刷新间隔的延迟。
这里的大问题是,重定向到屏幕外表面的双缓冲窗口可能仍会受到活动交换间隔(即 V-Sync 设置)的影响;当然,双缓冲本身在复合设置中没有多大意义。
因此,您可以执行以下操作:使用交换间隔扩展将合成器的交换间隔设置为 0(无 V-Sync);根据您的系统设置,这个选择实际上可能不会被兑现(用户配置的所有应用程序都被强制为垂直同步)。不幸的是,有几个交换间隔扩展,适用于一个显示驱动程序的内容不适用于另一个。我建议您查看 Mesa 的交换间隔示例程序和 Mesa 的glxgears的源代码,其中包含处理您可能遇到的几乎所有情况的代码。
也希望以某种方式关闭客户端中的 V-Sync。我没有看到比将共享对象注入它们、挂钩glXSwapBuffers
和glXCreateContext
交换间隔扩展来覆盖它们更好的方法。
glXSwapBuffers
最后,您必须使用可用的视频同步 GLX 扩展之一在您的合成器中实现定时缓冲区交换(即在 V-Blank 发生的正确时刻调用“非同步” )。使用直接的 OpenGL 上下文和应用于合成器进程的实时调度策略,您可以做到这一点。
请注意,所有这些问题都是现有 X11 协议的缺点。但是,如果您认为 Wayland 会摆脱这些问题,请再想一想。虽然 Wayland 最初的目的是让“每一帧都完美”并消除同步问题,但在实践中我又遇到了许多问题。在这篇博文中Wayland 的创建者谈到了往返和开销,但他完全避免了管道同步和缓冲区交换延迟的问题。这些问题是基于堆叠合成和缓冲区交换的 V-Sync 概念所固有的。要真正解决这个问题,必须有某种与屏幕相关的 V-Sync 事件,它独立于图形操作并且可以应用任意相位偏移,以便应用程序可以将其渲染循环与显示刷新同步。并且应该有一个额外的“帧缓冲提交”功能,使整个合成链考虑新到达的帧。这将允许合成器在 V-Blank 发生之前将应用程序同步到几个 100µs,这样合成就可以在帧缓冲区提交和 V-Blank 之间的空白处发生。
正如@datenwork 所说,我不认为这glxSwapBuffers
是阻碍。但有些东西是。我解决了受这篇博文启发的问题。
具体来说,在我的平台(Ubuntu 14 + Nvidia 驱动程序 + Nvidia OpenGL 实现)上,以下代码有效:
PFNGLXSWAPINTERVALEXTPROC glXSwapIntervalEXT = (PFNGLXSWAPINTERVALEXTPROC)glXGetProcAddress((const GLubyte*)"glXSwapIntervalEXT"); // Set the glxSwapInterval to 0, ie. disable vsync! khronos.org/opengl/wiki/Swap_Interval
glXSwapIntervalEXT(x11_display, glx_window, 0); // glXSwapIntervalEXT(0);
x11_display
建在哪里
Display* x11_display = XOpenDisplay(0);
并glx_window
构建为
GLXWindow glx_window = glXCreateWindow(x11_display, fb_config, window, 0);
并且fb_config
是一个合适GLXFBConfig
的 ,具体来说,我得到如下:
int visual, n_fb_configs;
GLXFBConfig* fb_configs = glXGetFBConfigs(x11_display, screen_number, &n_fb_configs);
GLXFBConfig fb_config = fb_configs[2]; // Select 3rd FB config! Many others work!
glXGetFBConfigAttrib(x11_display, fb_config, GLX_VISUAL_ID, &visual); // Query visual?
printf("screen %x fb_configs %d fb_config %llx visual %x\n", screen_number, n_fb_configs, (ull)fb_config, visual);
请注意,在启用(基本上没用的,imo)vsync 的情况下,我没有出现任何屏幕撕裂。
此外,根据您的 GPU 驱动程序和 OpenGL 实现(是 Nvidia 的吗?是 Mesa 的吗?),glxSwapInteral*(...)
您可能还需要使用其他功能。例如。有一个glXSwapIntervalMESA(...)
和一个glXSwapIntervalSGI(...)
。
Rant:与 X11 编程一样,几乎没有文档可供您使用……祝您好运!:)
奖金。引用OpenGL 文档:
您的应用程序对交换间隔的使用可能会被特定于驱动程序的外部配置覆盖。例如,在驱动程序的控制面板中强制关闭 Vsync 将阻止 Vsync,即使您的应用程序中的交换间隔设置为 1。