1

I whipped up a simple C program (on github) that uses OpenGL to draw a bunch of triangles from a buffer that was allocated with glBufferStorage like so:

glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
GLbitfield bufferStorageFlags = GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT;
glBufferStorage(GL_ARRAY_BUFFER, vboSize, 0, bufferStorageFlags);
vert *triData = glMapBufferRange(GL_ARRAY_BUFFER, 0, vboSize, bufferStorageFlags);

I am aware that synchronization is my responsibility when using glBufferStorage with MAP_PERSISTENT_BIT, but I'm not sure exactly what I need to protect against. The only time I touch triData is before calling glDrawArrays on it, and after calling SDL_GL_SwapWindow, so I know drawing of the last frame is done, and I haven't called for drawing of this frame to begin yet. This appears to work perfectly, even with vsync disabled.

The wiki says:

Swapping the back and front buffers on the Default Framebuffer may cause some form of synchronization ... if there are still commands affecting the default framebuffer that have not yet completed. Swapping buffers only technically needs to sync to the last command that affects the default framebuffer, but it may perform a full glFinish​.

But every article I've read on the subject makes extensive use of GLsync pointers, though maybe they were just assuming I might want to use the buffer in more complex ways? For now, am I right to believe SDL_GL_SwapWindow is providing sufficient synchronization?

Duovarious
  • 109
  • 7

3 Answers3

2

The previous answers are correct in saying that you do need synchronization even after you use a swap. But I wanted to make even clearer that this is more than just a theoretical concern.

Swap operations are typically not synchronous. It's very common to let the rendering get 1-2 frames ahead of the display. This is done to reduce "bubbles" where the GPU temporarily goes into an idle state. If your swap call were synchronous, the GPU would unavoidably be idle at the time it returns, since all previously submitted work would have completed. Even if you immediately started rendering again, it would take a little time for that work to actually reach the GPU for execution. So you have times where the GPU does nothing, which hurts performance at least as long as your rendering is entirely GPU limited.

Now, you obviously don't want the rendering to get too far ahead of the display. Undesired side effects of that would be increased latency in responding to user input (which is a big deal for games), and excessive memory usage for queued up rendering commands. Therefore, there needs to be throttling before this happens. This throttling is often applied as part of swap operations, but it can potentially happen almost anywere.

So if you measure the wall clock time taken for a swap call to return, it's fairly common for it to be long enough to suggest that it's blocking. But this does not suggest that the call itself is synchronous. It may just be blocking until a previous frame completes, to prevent the rendering to get too far ahead of the display.

Reto Koradi
  • 53,228
  • 8
  • 93
  • 133
  • "Swap operations are typically not synchronous. It's very common to let the rendering get 1-2 frames ahead of the display." But I'm not doing that here, and let's say I don't plan to. I put the swap at the end of my render loop. Doesn't the swap count as explicit synchronization for draw calls? – Duovarious Mar 04 '16 at 05:14
  • Not at all. As I tried to explain, swap calls (which are part of the window system integration, not OpenGL itself) can be asynchronous, pretty much like the OpenGL calls you make. – Reto Koradi Mar 04 '16 at 07:01
  • Ok, I'm beginning to grasp that swapping doesn't quite work the way I thought. I will call glFinish to be sure the draw calls are flushed. – Duovarious Mar 04 '16 at 08:29
1

Here's my favorite advice about any multithreaded/asynchronous code:

If multithreaded code isn't immediately, obviously, provably correct then it is almost certainly wrong.

You cannot prove that OpenGL will not read from a value you are writing to. Therefore, it is wrong, even if no problems are apparent.

Yes, you need to do explicit synchronization. Even though you coherently mapped the buffer, you still cannot change the values in it while OpenGL might be reading from them. You must wait until after the last call that reads from that data before writing to it again. And the only ways that OpenGL has to wait for it to get finished is either glFinish or glClientWaitSync.

Community
  • 1
  • 1
Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
0

I am aware that synchronization is my responsibility when using glBufferStorage,

No, not necessarily. A buffer created with glBufferStorage is no different than a buffer created with glBuffer, except for the fact that you can't re-specify it.

You only need to do manual synchronization when mapping with the MAP_PERSISTENT_BIT (which was included in the same extension that glBufferStorage was, ARB_buffer_storage).

Colonel Thirty Two
  • 23,953
  • 8
  • 45
  • 85