I have vertex buffers holding meshes of terrain chunks. Whenever the player edits terrain, the mesh of the corresponding chunk must be regenerated and uploaded to the vertex buffer. Since regenerating the mesh takes some time, I do it in an asynchronous worker thread.
The issue is that the main threads draws the buffer in the same moment the worker thread uploads new data. That means, after the player editing the terrain, a corrupted chunk gets rendered for one frame. It just flares up once and after that, the correct buffers gets drawn.
This kind of made sense to me, we shouldn't write and read the same data at the same time of course. So instead of updating the old buffer, I created a new one, filled it and swapped them. The swapping was just changing the buffer id stored within the terrain chunk struct, so that should be atomic. Hoever, that didn't help.
Due to the fact that OpenGL commands are sent to a queue on GPU, they don't have to be executed when the application on the CPU continues. So I may have swapped the buffers before the new one was actually ready.
I also tried an alternative to switching the buffers, using a mutex for buffer access. The main thread locks the mutex while drawing and the worker thread locks it while uploading new buffer data. However, this didn't help either and it may be because of OpenGL's asynchronous nature, too. The main thread didn't actually draw, but just send draw commands to the GPU. On the other hand, when there really is only one command queue, uploading buffers and drawing them could never occur at the same time, does it?
How can I synchronize the vertex buffer access from my two threads to prevent that an undefined buffer gets drawn for one frame?
For example, you can use a separate thread to pass OpenGL rendering calls to dedicated 3-D hardware. Symmetric multiprocessing (SMP) systems can greatly benefit from using multiple threads. An obvious strategy is to use a separate thread for each processor to handle OpenGL rendering in separate windows.
Need to do explicit synchronization. In OpenGL 4.4, so only latest GPU can support it. In the next post I’ll share my results from the Demo application. I’ve compared glMapBuffer approach with glBuffer*Data and persistent mapping.
Fortunately, OpenGL (since version 4.4) gives us a new technique to fight this problem. It’s called persistent mapped buffers that comes from the ARB_buffer_storage extension. Let us revisit this extension. Can it boost your rendering code?
It is recommended that you call wglMakeCurrent (NULL, NULL) if GL context is current on another thread, then call wglMakeCurrent (dc, glrc) in the other thread. Another solution is to have multiple GL rendering contexts.
You must make sure that the buffer update is actually completed before you can use that buffer in your draw thread. The easieast solution would be to call glFinish
in your update thread after you issued all the update GL commands, and only notify the the draw thread after that returned.
To have a more fine grained control over the synchronization, I would advice you to have a look at fence sync objects (as described in the GL_ARB_sync extension). You can issue a fence sync after you issued your update commands and actually store the sync object handle with your buffer handle so that the draw thread can check if the update actually completed (or wait for it). Note that sync objects are kind of special since they are the only objects not tied to the GL context, so that they can be used in multi-context setups.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With