编辑:我在下面发布了一个很好的解决整个渲染器分离问题的方法。
我最近在多线程 X11 环境中使用 OpenGL。我找到了以下教程,它可以编译、链接并运行良好。
但是,在尝试根据自己的需要调整代码后,我遇到了一个奇怪的问题。
教程中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 Error of failed request: BadWindow (invalid Window parameter)
Major opcode of failed request: 2 (X_ChangeWindowAttributes)
Resource id in failed request: 0x5000002 Serial number of failed request: 17 Current serial number in output stream: 18
这似乎是由 XSetWMProtocols 引起的。
由于使用了不同的显示连接,如果整个事情一开始就不起作用,我不会感到惊讶。但不知何故,在调用 glXCreateContext 之后,一切似乎都神奇地好起来了。
我对 X11/GLX 编程比较陌生,我错过了什么吗?glXCreateContext 执行了什么样的魔法?还是发生了其他事情?或者也许我应该继续前进,因为 OpenGL 和多线程似乎总是会导致问题。
我的解决方案:
我很懒,只是使用教程中的方法。这一直有效,直到将 freetype 添加到我的项目中,这突然又给了我一个 BadWindow 崩溃。所以,即使一切看起来都很好,当你在不同的线程中工作时,X11 会在你不在的时候认真地处理一些内存。(不是我,我用 valgrind 检查过)
我目前的解决方案是 nm 评论的:我将所有内容都放入一个 GUI 线程(X11 和 GL/GLX 调用)中,其资源永远不可用于其他线程。但是,必须记住两件事,因为它可能会减慢渲染循环:
- 缓慢的消息处理延迟渲染(如下 ilmale 所述)
- 缓慢的渲染延迟消息处理(我担心)
第一个问题很容易解决。创建一个 stl deque 或列表或任何容器,您可以在其中为您的应用程序逻辑排队相关的 XEvent,然后从另一个线程中获取它们。只要确保您的 STL 是线程安全的,并且毫无疑问地实现您自己的队列。通过在容器大小上设置等待条件,您甚至可以模拟 XNextEvent 之类的阻塞调用。
第二个问题很棘手。您可能会争辩说,如果渲染器的速度为 1 fps 或更慢,那么游戏或应用程序无论如何都是无用的。那是真实的。但是,如果您能够处理一些终止信号(例如销毁窗口原子),即使您的速度为 0.1 fps,那将会很整洁。我能想到的唯一解决方案是在渲染每千个左右的精灵后检查新消息。将它们发送到您的容器并继续渲染。当然,在这种情况下,您永远不能让渲染线程随时运行用户脚本或其他未知代码。但我想,无论如何,这将使将渲染与其他线程分开的想法毫无意义。
希望这可以帮助。
: