多线程环境中的XSetWMProtocols和glXCreateContext调用顺序

时间:2012-06-30 06:37:20

标签: c++ multithreading x11 glx

编辑:我在下面发布了一个很好的解决方案来解决整个渲染器分离问题。

我最近在多线程X11环境中使用OpenGL。 我找到了以下tutorial,它编译,链接并运行良好。

然后,在尝试根据自己的需要调整代码后,我遇到了一个奇怪的问题。

在本教程中,XCreateWindow,glXCreateContext,XSelectInput和XSetWMProtocols的调用顺序如下:

param[i].win = XCreateWindow(param[i].d_, root, 200,200, 
                   300,200, 0, visInfo->depth, InputOutput, visInfo->visual,
                   CWColormap,
                   &windowAttr);
param[i].ctx = glXCreateContext(param[i].d_, visInfo,  NULL, True);
XSelectInput(d, param[i].win, StructureNotifyMask);
XSetWMProtocols(d, param[i].win, &(delMsg), 1);

请注意,XCreateWindow和XSelectInput / XSetWMProtocols使用不同的显示连接。

但是,将呼叫顺序更改为

param[i].win = XCreateWindow(param[i].d_, root, 200,200, 
                   300,200, 0, visInfo->depth, InputOutput, visInfo->visual,
                   CWColormap,
                   &windowAttr);
XSelectInput(d, param[i].win, StructureNotifyMask);
XSetWMProtocols(d, param[i].win, &(delMsg), 1);
param[i].ctx = glXCreateContext(param[i].d_, visInfo,  NULL, True);

程序以

失败
  

X请求失败的错误:BadWindow(Window参数无效)
  失败请求的主要操作码:2(X_ChangeWindowAttributes)
  失败请求中的资源ID:0x5000002失败的序列号   请求:17输出流中的当前序列号:18

似乎是由XSetWMProtocols引起的。

由于使用了不同的显示连接,如果整个事情一开始就不起作用,我不会感到惊讶。但不知何故,在调用glXCreateContext之后,一切似乎都神奇地好了。

我对X11 / GLX编程比较陌生,我错过了什么吗? glXCreateContext执行什么样的魔术?还是做了别的事呢? 或许我应该继续前进,因为OpenGL和多线程似乎总是会导致问题。

我的解决方案:

我很懒,只是使用教程中的方法。这一直有效,直到我的项目添加了freetype,这突然让我再次遭遇BadWindow崩溃。所以,即使一切看起来都很好, 当你从不同的线程工作时,X11在你不在身边的时候会非常喜欢一些记忆 。 (这不是我,我和valgrind一起检查过)

我目前的解决方案是n.m.评论:我将所有内容都放入GUI线程(X11和GL / GLX调用),其资源永远不可用于其他线程。但是,必须记住两件事,因为它可能会降低渲染循环的速度:

  • 慢速消息处理延迟渲染(如下面的ilmale所述)
  • 慢渲染延迟消息处理(我的顾虑)

第一个问题很容易解决。创建一个stl deque或列表或任何容器,在那里您为应用程序逻辑排队相关的XEvents,然后从另一个线程中获取它们。只需确保您的STL是线程安全的,并且无疑会实现您自己的队列。 通过在容器大小上设置等待条件,您甚至可以模拟阻塞调用,如XNextEvent。

第二个问题很棘手。您可能会争辩说,如果渲染器的速度为1 fps或更慢,那么游戏或应用程序无论如何都是无用的。那是真实的。但是如果你能够处理一些杀死信号(例如破坏窗口原子),即使你的速度为0.1 fps,它也会很整洁。我能想到的唯一解决方案是在渲染每千个精灵之后检查新消息。将它们发送到您的容器并继续渲染。当然,在这种情况下,您永远不能让渲染线程随时运行用户脚本或其他未知代码。但我想,无论如何,这会使渲染与其他线程分离的想法毫无意义。

希望这有帮助。

2 个答案:

答案 0 :(得分:1)

我在跨平台项目中基本上经历了多线程X11和Win32的相同试验。

我注意到的一件事是X11没有像上面的帖子那样修改内存。当然,各种命令都有一些奇怪的顺序,但一旦你做对了,它似乎相当稳定。

特别是几乎让我丢掉的一件物品是背景GPU处理!这是非常奇怪和难以捕捉运行时竞争条件让我觉得操作系统应该受到责备。

将纹理发送到显示列表中的卡片(咳嗽,同时实现freetype)后,立即绘制资源有时会导致字体显示列表轻微损坏,即使是以后的绘制也是如此。显示列表本身已损坏,我甚至使用全局OpenGL锁来证明线程不应该受到指责。但为什么它被破坏了?操作系统?不,GPU。

我相信共享GLX上下文会对某些卡强行不同的行为,尤其是我系统上的nvidia。不是其他线程导致我的困境,而是在使用资源之前,createContext调用上的共享标志与缺少glFinish()相结合。以及我将在下面解释的一些最佳实践。

在99%的运行中,即使使用多线程,它也可以正常运行而不用 glFinish()。只有在加载时才会出现这种情况,因此不断停止/重新启动应用程序最终会暴露它。如果它没有问题加载所有内容,应用程序将从那里运行良好。如果有问题,图像会一直损坏,直到我重新加载它。

所有问题都是通过遵守这些简单规则来解决的。

  1. 在非main()线程中创建第二个GLContext。 (不要在同一个线程中创建两个上下文并给第二个线程指针,这样就不稳定了)
  2. 在第二个线程中加载资源时,添加glFinish() before 以将结果放在要使用的队列上。 (在使用资源之前简单地说,glFinish())
  3. 在第二个线程内的第二个上下文中调用makeCurrent()后,调用getCurrentContext()函数并等待它为非NULL,然后让 线程执行其他OpenGL资源加载或调用。有时在第二个线程上它(makeCurrent)返回但是getCurrentContext()在某些视频卡上可能仍然是NULL。不确定驱动程序为何或如何发生这种情况,但检查会使应用程序免于崩溃。
  4. 我在我的6-Thread +应用程序中实现了这些实践,奇怪的一次性腐败问题消失了,永远不会回来。

    事实证明X11并不是我的经验......视频卡是,但它真的只是挑剔而不是任何东西。在我的情况下,我甚至使用typedef来编写使用非特定函数的Linux / Windows代码,这会使事情变得更复杂,如果采取适当的预防措施,这仍然是一个可管理的野兽:)。

    如果你问我,这是古怪但不是“不惜一切代价”的问题。我希望这篇文章有所帮助,祝你好运!

答案 1 :(得分:0)

我同意n.m.而且我是编写教程的人。 :d 我试图解决的问题是将事件循环与渲染循环分离,这样我就可以重放事件而不影响渲染,反之亦然。我正在编写一个LUA框架,我的“processMessage(event)”函数可能会调用用户定义的Lua函数。

当我编写事件循环时,我遇到了很多问题,比如你的问题,我也尝试过在Fedora上工作的XCB,但在Ubuntu上崩溃了,经过很多头痛我找到了不同显示的解决方案(对于X服务器就像服务于不同的进程一样,使用共享的GlContext和另一个用于加载的线程(纹理和网格)。

回到你的问题:

XSetWMProtocols(...)

想要创建窗口的相同显示,但只能在某个版本的X上显示。这就是为什么现在我正在使用Qt。