多线程的X11应用程序和OpenGL

8

我正在尝试使用libx11创建一个多线程的OpenGL应用程序,每个窗口对应一个单独的线程,再加上一个管理线程。

在管理线程中有一个事件循环:

while(true)
  while(XQLength(mPlatformData->display)){
    XNextEvent(mPlatformData->display, &event);
    std::cout << "event" << std::endl;
  }
}

这是一个适用于单线程应用程序的优秀事件循环,但在这种多线程设置下会发生奇怪的事情。

当我创建一个窗口时,我需要禁用事件队列,否则GLXMakeCurrent将会挂起 - 我的整个线程停止,什么也不做。

我在网上找不到关于多线程X11应用程序的太多信息,我应该以不同的方式处理我的事件吗?


我猜X11和OpenGL是线程不安全的。 - user142019
2
如果我在一个上下文/线程中使用GL,则它是线程安全的。根据X11文档,如果我首先调用XInitThreads(),它应该是安全的,这也是我所做的。 - Dutow
3个回答

3
众所周知,Xlib存在几个无法修复的运行时问题,在并发访问情况下会显现出来。我猜测你遇到了其中一个。
这也是创建Xcb的原因之一:解决Xlib的问题。GLX针对Xlib进行规定,因此在涉及OpenGL时可能会成为阻碍。但是,Xcb周围有一个Xlib包装,可以安全地使用它与GLX进行接口交互,并仍然使用Xcb来进行程序的其余部分: http://xcb.freedesktop.org/opengl/ 我看到两种可能的解决方案:
  1. 在每个XNextEvent和GLX调用周围放置XLockDisplay/Mutex;对于普通OpenGL,您不必锁定,只需锁定以glX...为前缀的函数。

  2. 使用Xcb获取运行时正确的行为,并按照上面链接的指南使其与OpenGL/GLX配合使用。


我已经实现了第一个解决方案作为一个快速修复,但我希望有一个更好的解决方案,我会尝试第二个。 - Dutow

2
如eile所说,您应该检查是否使用了XInitThreads。
当我使用后台线程来绘制动画窗口时,它给我带来了一些好的结果。如果你只是坚持绘图代码,似乎没有真正的问题。
如果你需要更多,因为你正在使用低级别的libX11,最好的方法就是打开多个X11连接,并为每个顶层窗口使用一个连接。我在10年前玩BeOS跨平台工具包时就这样做了,当时情况比现在还要糟糕。
即使是处理事件和顶层窗口的子窗口,您也可以使用此方法。但是这需要一些非常棘手的XEvent掩码代码。

我正在使用XInitThreads,但这不够。而且我不能使用多个Display连接,因为我无法在它们之间共享GLXContext。 - Dutow

1
你在渲染线程中做什么?无论如何,如果你在不同的线程之间共享Display*连接,你必须调用XInitThreads。
我已经在每个线程中使用一个Display连接取得了良好的经验。使用XSelectInput在主线程上获取事件。窗口ID可以在不同的Display*连接之间共享。

1
针对OP的发言:OP已经了解XInitThreads,可以看到他在问题评论中提到了。打开一个额外的Display*连接是处理这个问题的一种方法。不幸的是,它会消耗一种稀缺资源:连接。当前形式的X11服务器不幸地只支持同时连接(客户端)的数量相当低(在256到1024之间);因此,如果每个进程的线程都打开自己的Display连接,你很快就会用完连接插槽。 - datenwolf
1
多个显示连接不能共享GLXContexts - 或者说我无法使它们工作。 - Dutow
@Dutow:只有涉及间接渲染上下文的情况下,您才能在不同连接之间共享GLXContexts,即直接= false调用http://www.opengl.org/sdk/docs/man/xhtml/glXCreateContext.xml。 - datenwolf
我们在Equalizer发行版中有一个示例(eqAsync),它为每个GPU打开一个显示连接,并在第二个线程中为每个GPU上传GL纹理时使用一个额外的共享上下文。据我所知,这是使用glX实现的——因此我想知道您在第二个线程中正在做什么。代码在这里:https://github.com/Eyescale/Equalizer/tree/master/examples/eqAsync - eile

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接