在尝试使用OpenGL作为后端为X11创建合成窗口管理器时,我陷入了一个令人讨厌的情况,其中glXSwapBuffers()阻塞,直到vblank渲染合成器对X事件不响应,导致窗口被拖动到落后于光标大约一帧。我尝试过多线程,但是效果不好,所以我认为唯一合适的解决办法是使glXSwapBuffers()异步。希望将绘图命令发送到GPU并立即返回,而无需等待操作实际完成,并且在现代Linux下使用DRI2可以实现AFAIK。那我该怎么办?
答案 0 :(得分:4)
实际上glXSwapBuffers
应立即返回。然而,阻塞的是下一个引入所谓同步点的OpenGL命令。通常,这是跟glClear
。
glXSwapBuffers
请注意,实际上需要以某种方式与V-Blank同步,否则会发生令人讨厌的撕裂伪影。但是你是对的,在一个天真的实现中,这引入了一个显示刷新间隔的延迟。
这里的一个大问题是,重定向到离屏表面的双缓冲窗口仍然可能受到主动交换间隔(即V-Sync设置);当然,双重缓冲本身在合成设置中没有多大意义。
所以这是你可以做的:使用交换间隔扩展将你的合成器的交换间隔设置为0(没有V-Sync);根据您的系统设置,实际上可能无法满足此选择(用户配置所有应用程序都被强制为V-Sync)。不幸的是,有几个交换间隔扩展和一个显示驱动程序的功能不适用于另一个。我建议你看一下Mesa的交换间隔示例程序和Mesa的 glxgears 的来源,其中包含的代码可以处理你可能遇到的几乎所有情况。
也希望以某种方式关闭客户端中的V-Sync。我没有看到比将共享对象注入其中更好的方法,挂钩glXSwapBuffers
,glXCreateContext
和交换间隔扩展来覆盖它们。
最后,您必须使用一个可用的视频同步GLX扩展来在您的合成器中实现定时缓冲区交换(即在V-Blank发生的恰当时刻调用“未同步”glXSwapBuffers
)。通过直接的OpenGL上下文和应用于合成器进程的实时调度策略,您可以做到这一点。
注意所有这些问题都是现有X11协议的缺点。但如果你认为Wayland会摆脱这些问题,那就再想一想。虽然Wayland最初的目的是使“每一帧完美”并消除同步问题,但在实践中我再次遇到了许多问题。在this blog post中,Wayland的创建者讨论了往返和开销,但他完全避免了管道同步和缓冲区交换延迟的问题。这些问题是基于堆叠组合和基于缓冲交换的V-Sync概念所固有的。要真正解决问题,必须存在某种与屏幕相关的V-Sync事件,该事件独立于图形操作并且可以应用任意相位偏移,以便应用程序可以将其渲染循环与显示刷新同步。并且应该有一个额外的“framebuffer commit”函数,它使整个组合链考虑新到达的帧。这将允许合成器在V-Blank发生之前将应用程序同步到几个100μs,这样组合就可以在帧缓冲提交和V-Blank之间的边界内发生。
答案 1 :(得分:1)
正如@datenwork所说,我不认为它是glxSwapBuffers
阻塞。但是。我解决了受this blog post启发的问题。
具体来说,在我的平台上(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 documentation:
您的应用程序使用交换间隔可能会被外部的特定于驱动程序的配置所覆盖。例如,即使在应用程序中将交换间隔设置为1,在驱动程序的控制面板中强制关闭Vsync也会阻止Vsync。