Adding new vertex data to a vao

Hi

I have a single VAO for my scene and the render cycle iterates through some draw commands. There is a vertex buffer bound to the VAO (as well as other buffers) and everything renders fine. I’d now like to add an object/s which means the render cycle needs to be extended and the new vertex data needs to be loaded in to the GPU.

What is the most efficient way of doing this? I know I can’t extend the buffer, I need to create a new one if I need a larger buffer. But i feel like I am missing a good technique of doing this.

An example of what I am asking is as follows. I have a UI and I want to display a list of data (assume each row is a collection of triangles forming quads for font letters, aka text) that is not known and could change at run time. I also might want to add a new row at runtime.

Thanks

Peter

If you can determine an upper bound on the amount of data, allocate the buffer at that size. Even if the upper bound is a significant proportion of your total video memory, it’s better to know that up front than to run out of memory at some arbitrary point in the future.

If you can’t determine a bound, double the size of the buffer each time you need to enlarge it, and copy the data from the previous buffer with glCopyBufferSubData(). Increasing the size by a constant multiple each time ensures that the overall time taken is proportional to the total size (n+n/2+n/4+n/8+…=2n). Increasing by a constant amount results in the time being quadratic in the total size (n+(n-k)+(n-2k)+…+2k+k=n(n+k)/2k). C++ containers such as std::vector and std::string use this approach.

[QUOTE=bobtedbob;1292925]There is a vertex buffer … as well as other buffers …
I’d now like to add an object/s which means the render cycle needs to be extended and the new vertex data needs to be loaded in to the GPU.

What is the most efficient way of doing this? I know I can’t extend the buffer, I need to create a new one if I need a larger buffer.[/QUOTE]

One technique for implementing this is the one you’re onto: that is, let each object that you’re drawing have its own dedicated spot in GPU memory. Typically folks start with thinking they need a separate buffer object for each vertex attribute for each draw call. Then they learn about vertex attribute interleaving, and decide that they can have a single buffer object per draw call. Later you realize that that’s inefficient and doesn’t support dynamic updates very well. So you start grouping things, by having “big” buffer objects that you place the vertices for many draw calls. That helps your efficiency, but it’s still a bit of a pain when you try and figure out how to do dynamic updates. You end up with fragmentation, block usage tracking, hole fitting, possibly compaction, needing to grow your buffer space, etc. In other words, complexity.

That tends to suggest using another technique: just have one big buffer object that you fill and use as a ring buffer (fill/draw/fill/draw/fill/draw/…). Each time you draw, if the vertex data is already in the buffer, you just skip the fill step, point the GPU to it, and issue the draw call (draw/draw/draw/…). Once the buffer fills up, you just start over. This is the Streaming Buffer Object concept. Hi performance, and no memory management/fragmentation issues to deal with. To implement these techniques, read: Buffer Object Streaming in the wiki. There are a number of variations of this technique, but that’s the jist of it. For one variation, see this 2014 presentation: Approaching Zero Driver Overhead. Search down to Dynamic Streaming of Geometry and read on.

One thing you may already realize. There’s nothing about buffer objects that says they can only contain one type of data (e.g. vertex attributes, index data, indirect command buffers, etc.). It’s just a hunk of bytes. You can pack all of that in one buffer object end-to-end. Just point the GPU vertex attributes to the vertex attributes data, the GPU index list to the index data, etc. and everything works just fine with one buffer object. Less buffer objects to have to manage is a good thing.

Thanks for both replies, this has open me up to a whole load of new options. Would I be right in saying the persistent mapped buffer solution with the fencing etc is similar to what vulkan makes you do? I’m going to research those topics more in the links Dark Photon provided

Re fencing and suballocation, yes. Re persistent maps … it doesn’t really make you use it that way, but with Vulkan maps persistent by default (AFAIK) it certainly does encourage it.

While you could have the GPU render from device memory not visible to the host and use a staging buffer to transfer data into it from the CPU (something OpenGL does in some cases but hides from you), that’s extra copies and extra synchronization that’s probably needless in most cases.

Buffer maps in Vulkan aren’t coherent by default though, so you still need to request that if you want it or manage the flushing yourself (similar to OpenGL).

Check it out here:

[ul]
[li]Vulkan Memory Management (NVidia) [/li][li]High Performance Vulkan (Valve) [/li][li]Staging Buffer (vulkan-tutorial) [/li][li]Vulkan Memory Allocator [/li][/ul]