Sure, absolutely.
Off the top of my head, the tips are (and I haven’t solved everything yet, I still get X BadMatch errors):
[list]Use a separate Display Connection, GLX context etc. per thread
Don’t initialize or access the items created on the thread outside of the thread
[/list]
The main implication to that from Juce is that if you need to do any rendering from a thread you need to take control of all GLX calls on that component. In my case, I took my OpenGLComponent subclass, override the draw/render methods as Jules intended, and never call swap on it or anything else. Then, in the thread, detect whether we’ve initialized, and do something like this:
[code]void ScreenComponent::render(SyncSourceTimeRecord inTime)
{
ScopedLock l(getContextLock());
#if JUCE_LINUX
// on linux, we deal with the context ourselves, so threads are OK
if (threadDisplayConnection == 0)
{
String displayName = T(":0.0"); // force 0.0 for GUI!
threadDisplayConnection = XOpenDisplay (displayName);
}
if (renderContext == 0)
{
needsReshape = true;
// get the top level X Window - we're going to tag onto it
ComponentPeer* const peer = getTopLevelComponent()->getPeer();
if (threadDisplayConnection && isShowing() && peer != 0)
{
XSync (threadDisplayConnection, False);
GLint attribs[] = { GLX_RGBA,
GLX_DOUBLEBUFFER,
GLX_RED_SIZE, 8,
GLX_GREEN_SIZE, 8,
GLX_BLUE_SIZE, 8,
GLX_ALPHA_SIZE, 8,
GLX_DEPTH_SIZE, 16,
None };
int numConfigs = 0;
int screen = DefaultScreen (threadDisplayConnection);
XVisualInfo* const bestVisual = glXChooseVisual (threadDisplayConnection, screen, attribs);
if (bestVisual == 0)
return;
renderContext = glXCreateContext (threadDisplayConnection, bestVisual,
(getShareContext() != 0) ? (GLXContext)(getShareContext()->getRawContext()) : 0,
GL_TRUE);
Window windowH = (Window) peer->getNativeHandle();
Colormap colourMap = XCreateColormap (threadDisplayConnection, windowH, bestVisual->visual, AllocNone);
XSetWindowAttributes swa;
swa.colormap = colourMap;
swa.border_pixel = 0;
swa.event_mask = ExposureMask | StructureNotifyMask;
embeddedWindow = XCreateWindow (threadDisplayConnection, windowH,
0, 0, 1, 1, 0,
bestVisual->depth,
InputOutput,
bestVisual->visual,
CWBorderPixel | CWColormap | CWEventMask,
&swa);
XSaveContext (threadDisplayConnection, (XID) embeddedWindow, improbableNumber, (XPointer) peer);
XMapWindow (threadDisplayConnection, embeddedWindow);
XFreeColormap (threadDisplayConnection, colourMap);
XFree (bestVisual);
XSync (threadDisplayConnection, False);
if (renderContext != 0)
{
//updateContextPosition();
}
}
}
if (needsReshape && threadDisplayConnection && embeddedWindow)
{
int x = getX();
int y = getY();
ComponentPeer* const peer = getTopLevelComponent()->getPeer();
Component::relativePositionToOtherComponent (getTopLevelComponent(), x, y);
XMoveResizeWindow (threadDisplayConnection, embeddedWindow, x, y, jmax (1, width), jmax (1, height));
}
if (renderContext != 0 && glXMakeCurrent (threadDisplayConnection, embeddedWindow, renderContext) && XSync (threadDisplayConnection, False))
#else
if (makeCurrentContextActive()) // will create one if needed
#endif
{
if (needsReshape)
{
initOpenGL();
needsReshape = false;
}
MeshScreen::render(inTime);
#if JUCE_LINUX
glXSwapBuffers (threadDisplayConnection, embeddedWindow);
#else
swapBuffers();
#endif
}
}
[/code]
Note that this makes it self-sufficent, it even does the swap itself (in Linux). You don’t need to make active each time though, since this thread and context stay linked.
That seems to work a lot better. I have a feeling I had all X errors out, and most crashes, until I added a lot of shader and FBO code recently. I can keep you updated if you want.
Oh, and the same applies to other GL screens or views you make: manage their whole X and GLX lifespan yourself, and on the thread as much as possible.
Bruce
PS Just for completeness:
[code]ScreenComponent::~ScreenComponent()
{
removeKeyListener (commandManager->getKeyMappings());
stopThread(2500);
#if JUCE_LINUX
if (threadDisplayConnection)
{
if (renderContext)
glXDestroyContext (threadDisplayConnection, renderContext);
if (embeddedWindow)
{
XUnmapWindow (threadDisplayConnection, embeddedWindow);
XDestroyWindow (threadDisplayConnection, embeddedWindow);
}
XCloseDisplay(threadDisplayConnection);
}
#endif
}
[/code]