Okay so I have spent I don't even know how many hours now trying to read about and experiment with VAOs/VBOs and I just can't wrap my head around it.

Are you supposed to have multiple VAOs/VBOs?

Say I want to load 5 different 3D models. Do I store all of vertices in the same VBO, or do I create a VBO for each of the objects I want to draw? Do I need a VAO for each VBO or do I use the same one throughout my program?

I'm sorry if these are dumb questions, but I just don't get it.

john_connor

06-01-2017, 05:33 PM

Okay so I have spent I don't even know how many hours now trying to read about and experiment with VAOs/VBOs and I just can't wrap my head around it.

putting 5 different (static) model vertices into the same buffer is a good idea if they share the same verex layout

GClements

06-01-2017, 07:15 PM

Are you supposed to have multiple VAOs/VBOs?

Say I want to load 5 different 3D models. Do I store all of vertices in the same VBO, or do I create a VBO for each of the objects I want to draw? Do I need a VAO for each VBO or do I use the same one throughout my program?

It's up to you. In practical terms, it's dictated by how rendering will be organised.

While you could put all of your vertex data in a single VBO, you can only set usage hints for an entire VBO, not for regions of it. So if different attributes warrant different usage hints, they should probably be in different VBOs. And you can orphan an entire VBO (essentially allowing it to be garbage-collected once it's no longer used), but not a region of it. So replacing a region of a VBO that's being used by pending commands will block until those commands have completed.

As for VAOs: you can just create a single VAO and change all of the attribute array state before each draw call (prior to 3.x, this was the only option). But if you're going to be using a particular combination of state repeatedly, it's more efficient to capture that in a VAO then just switch VAOs. Particularly if you have a lot of attributes.

As for handling multiple "objects": if those objects are static, you should consider whether it's feasible to merge them into a single mesh for rendering, to minimise the number of draw calls. You can use glMultiDrawElements() to draw a subset of the geometry.

Dark Photon

06-02-2017, 06:36 AM

Okay so I have spent I don't even know how many hours now trying to read about and experiment with VAOs/VBOs and I just can't wrap my head around it.

Are you supposed to have multiple VAOs/VBOs?

Say I want to load 5 different 3D models. Do I store all of vertices in the same VBO, or do I create a VBO for each of the objects I want to draw? Do I need a VAO for each VBO or do I use the same one throughout my program?

Good questions. You've already gotten some good responses, but it may help your understanding to talk about why each exists.

One of the speed-ups driver implementers came up with for this was vertex array objects (VAOs). This allows the driver to cache information on reused vertex attribute and index list bindings, which can save some of that cost.

Another speed-up one driver implementer came up with for this was bindless (https://www.nvidia.com/object/bindless_graphics.html). This allows the app to perform some of that per-draw-call validation work up-front once so that your VBOs are hot and ready to render with every time you reference them. And because your VBOs are hot and ready to render with, you can provide GPU addresses for your vertex attribute and index lists to the driver directly, which bypasses needless lookup logic in the driver saving further per-draw-call cost.

So to your question...

If you use bindless (available on NVidia drivers only) as a performance optimization, then for static vertex attribute and index list data, in practice it doesn't make much difference rendering performance-wise whether you split your data up into a bunch of VBOs or try to merge your vertex attribute/index lists data into a smaller subset of VBOs (e.g. using a Streaming Buffer Object (https://www.khronos.org/opengl/wiki/Buffer_Object_Streaming) approach).

However, if you don't use bindless but instead use VAOs as a performance optimization (or if you use neither!), then there is a measurable cost you pay for bouncing around to different buffer objects for your rendering. So you should consider reducing the total number of VBOs accessed in a frame (e.g. by merging VBOs) to minimize this cost.

Here I'm assuming a fixed set of batches (draw calls) in both of these cases. However, it's worth pointing out that reducing your VBO count by storing more batches per VBO opens up the possibility for you to reduce the number of draw calls you make. Reducing the number of draw calls you make can improve your performance as well.

So what's the take-away from this?

I'd encourage you to think about ways to combine your batch data into a small set of shared VBOs, rather than creating a VBO per batch. One option to consider which works well for dynamic vertex attribute data (and static for that matter) is a Streaming Buffer Object (https://www.khronos.org/opengl/wiki/Buffer_Object_Streaming) approach. This effectively uses VBOs as a fast on-GPU cache of the subset of the vertex attribute data you've needed to render with lately. It also makes it trivial to bound the amount of GPU memory that you allocate and use for vertex attribute and index lists data.

And if you're not using bindless (https://www.nvidia.com/object/bindless_graphics.html) to accelerate your draw calls, use VAOs where it makes sense.