Opengl glXSwapBuffers()阻塞直到vblank

Opengl glXSwapBuffers()阻塞直到vblank,opengl,x11,vsync,Opengl,X11,Vsync,在尝试使用OpenGL作为后端为X11创建合成窗口管理器时,我遇到了一个糟糕的情况:glXSwapBuffers()阻塞,直到vblank渲染合成器对X事件不负责任,这会导致窗口拖来拖去,落后于光标大约一帧。我尝试过多线程,但效果不好,所以我决定唯一合适的解决方案是使glXSwapBuffers()异步。最好将绘图命令发送到GPU并立即返回,而不必等待操作实际完成,而且在使用DRI2的现代Linux下,这是可能的。那么我该怎么办呢?实际上glXSwapBuffers应该立即返回。然而,阻碍的是下

在尝试使用OpenGL作为后端为X11创建合成窗口管理器时,我遇到了一个糟糕的情况:glXSwapBuffers()阻塞,直到vblank渲染合成器对X事件不负责任,这会导致窗口拖来拖去,落后于光标大约一帧。我尝试过多线程,但效果不好,所以我决定唯一合适的解决方案是使glXSwapBuffers()异步。最好将绘图命令发送到GPU并立即返回,而不必等待操作实际完成,而且在使用DRI2的现代Linux下,这是可能的。那么我该怎么办呢?

实际上
glXSwapBuffers
应该立即返回。然而,阻碍的是下一个OpenGL命令,它引入了一个所谓的同步点。通常这是调用
glXSwapBuffers
之后的下一个
glClear

请注意,实际上希望以某种方式与V-Blank同步,否则会发生令人讨厌的撕裂伪影。但您是对的,在一个幼稚的实现中,这引入了大约一个显示刷新延迟间隔

这里的大问题是,重定向到屏幕外表面的双缓冲窗口仍可能受到活动交换间隔(即V-Sync设置)的影响;当然,在复合设置中,双缓冲本身没有多大意义

因此,您可以这样做:使用交换间隔扩展将合成器的交换间隔设置为0(无V-Sync);根据您的系统设置,此选项实际上可能不受尊重(用户配置的所有应用程序都强制进行V-Sync)。不幸的是,存在多个交换间隔扩展,一个显示驱动程序的工作方式与另一个不工作。我建议您看看Mesa的交换间隔示例程序和Mesa的glxgears源代码,其中包含的代码几乎可以处理您可能遇到的每种情况

它还希望以某种方式关闭客户端中的V-Sync。我看不到比将共享对象注入到它们中更好的方法了,挂起
glXSwapBuffers
glXCreateContext
和交换间隔扩展来覆盖它们

最后,您必须使用一个可用的视频同步GLX扩展来在合成器中实现定时缓冲区交换(即,在V-Blank出现的适当时刻调用“unsynchronized”
glXSwapBuffers
)。通过直接OpenGL上下文和应用于合成器进程的实时调度策略,您可以做到这一点


注意所有这些问题都是现有X11协议的缺点。但如果你认为韦兰可以解决这些问题,那就再想想。虽然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\u显示
构建为

Display* x11_display = XOpenDisplay(0);
GLXWindow glx_window  = glXCreateWindow(x11_display, fb_config, window, 0);
glx\u窗口
构建为

Display* x11_display = XOpenDisplay(0);
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编程一样,几乎没有什么文档可供您使用。。。祝你好运!:)


奖金。引述:

应用程序对交换间隔的使用可能会被特定于驱动程序的外部配置覆盖。例如,在驱动程序的控制面板中强制关闭Vsync将阻止Vsync,即使在应用程序中swap interval设置为1