Advertisement

OpenGL Check if VBO Upload is Complete

Started by March 26, 2016 09:59 PM
9 comments, last by Matias Goldberg 8 years, 7 months ago

I've learnt that the upload of VBOs is asynchronous.

How can I check that my VBO is uploaded?

I'd like to do this in order not to try to draw it before the upload is complete, as this halts the program (and a lag spike is felt).

Edit: I've read up on fence syncing but supposing that I have my fence, how do I check for whether it's been used by the GPU without using glWaitSync? I'd like to check and get a boolean response, as opposed to waiting.

Because the OpenGL spec doesn't recognize the transfer as asynchronous, per se, there's no way to check. The only option is to force it to wait by issuing a dummy draw call. There are a variety of other things that can also be deferred and lead to hitching in-game, and so it's standard practice to issue dummy draw calls for these things. Getting rid of all this is one of the big improvements in the new APIs (DX12/Vulkan).

SlimDX | Ventspace Blog | Twitter | Diverse teams make better games. I am currently hiring capable C++ engine developers in Baltimore, MD.
Advertisement

Because the OpenGL spec doesn't recognize the transfer as asynchronous, per se, there's no way to check. The only option is to force it to wait by issuing a dummy draw call. There are a variety of other things that can also be deferred and lead to hitching in-game, and so it's standard practice to issue dummy draw calls for these things. Getting rid of all this is one of the big improvements in the new APIs (DX12/Vulkan).

Ah, I see! In that case, I may just set a time-out and only try to render after half a second or something; this won't be noticeable. Problem with DX12 is that one can't support Linux, which for me is a crime if one can avoid it. Vulkan looks promising. Thanks for letting me know though.

Because the OpenGL spec doesn't recognize the transfer as asynchronous, per se, there's no way to check. The only option is to force it to wait by issuing a dummy draw call. There are a variety of other things that can also be deferred and lead to hitching in-game, and so it's standard practice to issue dummy draw calls for these things. Getting rid of all this is one of the big improvements in the new APIs (DX12/Vulkan).

Ah, I see! In that case, I may just set a time-out and only try to render after half a second or something; this won't be noticeable. Problem with DX12 is that one can't support Linux, which for me is a crime if one can avoid it. Vulkan looks promising. Thanks for letting me know though.

It's not necessarily on a timer. Sometimes the driver simply can't be bothered to finish the upload until you actually issue a draw call.

SlimDX | Ventspace Blog | Twitter | Diverse teams make better games. I am currently hiring capable C++ engine developers in Baltimore, MD.
All GL commands are asynchronous, but act like they're immediate. Drawing commands are async too, so if you update a VBO and then draw, both those commands go into the same queue and are processed by the GPU one after the other.

If you want it to he truely async where the GPU is doing two things at once, you need to create another GL context and share the VBO between both contexts. This gives you a second queue that's independent of your main queue, and then you can use fences to track the progress of this secondary queue.
This is tricky though -- multiple contexts can ruin performance, because GPUs only ever have one hardware queue. However, if you only ever use this second queue for buffer updates, some drivers will detect that usage pattern and create a DMA command queue instead of a graphics command queue, which can simultaneously run alongside the main context.

This is getting into very advanced and vendor-specific territory though, so if you're looking for a solution, some more details on your exact situation and problem would help --- in the common situation of small buffer updates, you just update the buffer, then draw immediately after, and let the driver do it's thing.

Going with what Hodgman said. If you are NOT using multiple context from multiple threads then your concerns are unfounded.


All GL commands are asynchronous, but act like they're immediate. Drawing commands are async too, so if you update a VBO and then draw, both those commands go into the same queue and are processed by the GPU one after the other.


Meaning that if you update the VBO then call draw, the result will appear to be sequential/serialized..they WILL happen in the order submitted.
Advertisement

Meaning that if you update the VBO then call draw, the result will appear to be sequential/serialized..they WILL happen in the order submitted.

We're not talking about logical ordering or things appearing serialized. There's a ton of operations in GL that are deferred internally, which in turn will cause runtime hitches when the driver decides to actually commit operations. Buffer uploads, texture uploads, and especially shader (re)compiles are the prime suspects here. This is not an imaginary problem for anyone who has actually shipped a game on a GL pipeline.

Issuing a dummy draw call - and potentially a fence or glFlush/glFinish/SwapBuffers - is the only way to combat this problem of runtime hitches.

SlimDX | Ventspace Blog | Twitter | Diverse teams make better games. I am currently hiring capable C++ engine developers in Baltimore, MD.

Hold on guys. There is a way to check if the copy has been performed the way OP asked.

apitest shows how to issue a fence and wait for it. The first time it checks if the fence has been signaled. The second time it tries again but flushing the queue since the driver may not have processed the copy yet (thus the GPU hasn't even started the copy, or whatever you're waiting for. If we don't flush, we'll be waiting forever. aka deadlock)

Of course if you want to just check if the copy has finished, and if not finished then do something else: you just need to do the 'wait' like the first time (i.e. without flushing), but using waiting period of zero (so that you don't wait, and get a boolean-like response like OP wants). We do this in Ogre to check for async transfer's status.

As with all APIs that offer fences (D3D12, Vulkan, OpenGL), the more fences you add, the worse it becomes for performance (due to the added hardware and driver overhead of communicating results, synchronizing, and keeping atomicity). Use them wisely. Don't add fences "just in case" you think you'll want to query for the transfer status. If you have multiple copies to perform, batch them together and then issue the fence.

I'd like to do this in order not to try to draw it before the upload is complete, as this halts the program (and a lag spike is felt).

Calling glDraw* family of functions won't stall because it's also asynchronous. I can't think of a scenario where the API will stall because an upload isn't complete yet. You usually need to check if a download (i.e. GPU->CPU) is completed before you map the buffer to avoid stalling (unless you use unsynchronized or persistent mapping flags; in such case it won't stall but you still need to check if the copy is complete to avoid a hazard)

Thank you! I've literally learnt so much from your responses. This made me think about where the problem actually lies, since I was not mapping any buffers or performing any sort of expensive GPU downloads. After running my profiler in "Telemetry" mode for the first time, I've discovered (within two minutes) that actually, the garbage collector usage correlates perfectly with lag spikes and I happen to be wasting a lot of memory in a certain place.


apitest shows how to issue a fence and wait for it. The first time it checks if the fence has been signaled. The second time it tries again but flushing the queue since the driver may not have processed the copy yet (thus the GPU hasn't even started the copy, or whatever you're waiting for. If we don't flush, we'll be waiting forever. aka deadlock)

Of course if you want to just check if the copy has finished, and if not finished then do something else: you just need to do the 'wait' like the first time (i.e. without flushing), but using waiting period of zero (so that you don't wait, and get a boolean-like response like OP wants). We do this in Ogre to check for async transfer's status.

So can you use a fence to test for the actual status of a BufferSubData call uploading to server? And that works consistently across platforms without issuing a draw call against that buffer? After all the driver must do a full copy of the buffer to somewhere in-core at the point of call, but what the rube goldberg machine does after that is anyone's guess.


Calling glDraw* family of functions won't stall because it's also asynchronous. I can't think of a scenario where the API will stall because an upload isn't complete yet.

It'll stall if it gets caught in a situation where it can't continue dispatching frames without finishing a pending operation. I don't remember seeing this happen with buffer uploads, but I've seen it many, many times with shader recompiles. Whether this is because shader recompiles are a long goddamn process, or they have to happen in the main thread, or it just runs up against the limit of buffered frames, or some combination thereof, I'm not sure. It seems conceivable that a large upload followed by lots of dispatched frames could conceivably trigger the same effect.

SlimDX | Ventspace Blog | Twitter | Diverse teams make better games. I am currently hiring capable C++ engine developers in Baltimore, MD.

This topic is closed to new replies.

Advertisement