Search code examples
linuxmultithreadingopengltexturesglut

shared context with GLUT on Linux


In my current setup, I have two displays that are being driven by two GPUs. Using GLUT, I create two windows (one per display) and render each one from the main thread by calling glutSetWindow() in the draw call, for each window.

The draw calls renders a Texture2D as a sphere (using gluSphere()) but the Texture2D is swapped for another image every few seconds. I have set up an array of 2 Texture2D so I can load the next image while the current Texture2D is shown. This works well as long as everything runs in the main thread.

The problem is that the call to glTexImage2D(), to load the next image, hangs my draw call, so I need to call glTexImage2D() on a different thread. Calling glTexImage2D() on a different thread crashes, as it seems the OpenGL context is not shared. GLUT does not seem to provide a way to share the context, but I should be able to get the context on Linux via glXGetCurrentContext().

My question is if I get the context via this call, how can I make it a shared context? And would this even work with GLUT? Another option has been to switch to different library to replace GLUT, like GLFW but in that case I will loose some handy function such as gluSphere(). Any recommendation if the context cannot be shared with GLUT please?


Solution

  • With GLX context sharing is established at context creation; unlike WGL you can't establish that sharing as an afterthought. Since GLUT doesn't have a context sharing feature (FreeGLUT may have one, but I'm not sure about that) this is not going to be straightforward.

    I have two displays that are being driven by two GPUs.

    Unless those GPUs are SLi-ed or CrossFire-ed you can't establish context sharing between them.

    The problem is that the call to glTexImage2D(), to load the next image, hangs my draw call, so I need to call glTexImage2D() on a different thread.

    If the images are of the same size, use glTexSubImage2D to replace it. Also image data can be loaded asynchronously using pixel buffer objects, using a secondary thread that doesn't even need a OpenGL context!

    Outlining the steps:

    In the OpenGL context thread:

    initiating transfer

    • glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pboID)
    • void *p = glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY);
    • signal transfer thread
    • continue with normal drawing operations

    In the transfer thread

    on signal to start transfer

    • copy data to the mapped buffer
    • signal OpenGL context thread

    In the OpenGL context thread:

    on signal to complete transfer

    • glUnmapBuffer
    • glTex[Sub]Image
    • sync = glFenceSync
    • keep on drawing with the old texture

    on further iterations of the drawing loop

    • poll sync with glClientWaitSync using a timeout of 0
    • if the wait sync returns signalled switch to the new texture and delete the old one
    • else keep on drawing with the old texture