Search code examples
multithreadingopenglwgl

Calling glClientWaitSync() without making GL context current


I have a setup where an scene is rendered in an offscreen OpenGL framebuffer, then a compute shader extracts some data from it, and buts it into a ring buffer allocated on the device. This ring buffer is mapped with glMapBufferRange() to host-readable memory.

On the host side, there should be an interface where a Push() function enqueues the OpenGL operations on the command queue, followed by a glFenceSync(). And a Pull() function uses glClientWaitSync() to wait for a sync object to be finished, and then reads and returns the data from part of the ring buffer.

Ideally it should be possible to call Push() and Pull() from different threads. But there is the problem that an OpenGL context can only be current on one thread at a time, but glClientWaitSync() like all other GL functions needs the proper context to be current.

So with this the Pull() thread would take the OpenGL context, and then call glClientWaitSync() which can be blocking. During that time Push() cannot be called because the context still belongs to the other context while it is waiting.

Is there a way to temporarily release the thread's current OpenGL context while waiting in glClientWaitSync() (in a way similar to how std::condition_variable::wait() unlocks the mutex), or to wait on a GLSync object belonging to another context?

The only solution seems to be to periodically poll glClientWaitSync() with zero timeout instead (and release the context inbetween), or to setup a second OpenGL context with resource sharing.


Solution

  • You cannot change the current context in someone else's thread. Well you can (by making that context current in yours), but that causes a data race if the other thread is currently in, or tries to call, an OpenGL function.

    Instead, you should have two contexts with objects shared between them. Sync objects are shared between contexts, so that's not a problem. However, you need to flush the fence after you create it on the context which created the fence before another thread tries to wait on it.