The Relationship between VBOs and VAOs


I have a question about VBOs and VAOs.

Let’s say I have an interleaved VBO that stores some vertex positions, colours etc.

Then I create a VAO, and start setting up the state needed to supply vertex data. The VBO would be bound at this point, and thus, becomes associated with this VAO.

I’m seeking to understand the thinking behind this relationship between VAOs and VBOs. Why associate each VAO with an individual VBO (or a single set of seperate VBOs if non-interleaving).

Surely, it would be more beneficial to decouple the data of the VBO from the vertex formatting state of the VAO. That way, a single VAO could be bound once and then used across multiple sets of VBOs, each of which, although may contain different combinations of data, are formatted identically in terms of vertex layout, and thus compatible with the bound VAO.

Possibly I’m not understanding these two ideas very well, and was hoping if someone could help illuminate me a little on this.

Well, you can already do that. Yes, the state set by glBindVertexBuffer will still technically be part of the VAO, but you can quickly change buffer bindings without changing the vertex format. And thus, you can have one VAO that gets used by many buffer bindings. The DSA version of the API doesn’t even give you the option of glVertexAttribPointer-like functionality; it always separates them.

As for why this was not always the case, it’s basically the same reason it always is when something in OpenGL looks stupid: backwards compatibility.

When vertex arrays were first introduced to OpenGL, they could only use client side arrays. That’s why all of those functions have “Pointer” in the name: because they took actual pointers to actual memory, rather than byte offsets. Also, VAOs weren’t actually a thing at the time; they came later.

Then, the concept of using GPU memory came along. NVIDIA’s attempt to do this was too low-level (they let you directly allocate memory). AMD’s attempt was better, but still had some problems. See, their idea required that the user call a different function to set the format + buffer object used by it. Which meant that, if a user wanted to sometimes use GPU memory and sometimes use CPU memory, they had to have two entirely different code paths. Which was deemed to be bad because reasons.

ARB_VBO fixed this “problem” by making the “pointer” part of the “Pointer” functions change into a byte offset based on whether something was bound to GL_ARRAY_BUFFER. That way, people could bind to that binding point, and then call their old functions, passing in “pointers” that weren’t actually pointers.

I’m sure someone, somewhere was made happy by this.

Fast-forward to OpenGL 3.0 and Vertex Array Objects. They wanted to extract the vertex state into an object. But separating vertex format state from buffer binding state was impossible because glVertexAttribPointer and such set both kinds of state. So if you tried that, you would have a function that set both context state and object state. That would be decidedly weird.

And of course, making a new set of functions was considered unacceptable because reasons. Reasons which must not have been that good, since they eventually did.

Thank you for the swift response. I seem to have a knack of thinking up features that already exist, and completely missing them in the documentation pages… Whoops! Good that you’ve been able to direct me toward the right resource.

Very interesting what you say about the history behind it as well. It’s always useful to have that sort of insight. I agree that certain portions of the API look a bit “stupid” and could be better implemented. One example, the last parameter of glVertexAttribPointer, that being a const GLvoid * pointer. This gave me hours of headaches as a beginner, before I realised that it was simply the offset into the buffer. I’d often wonder why that particular parameter was implemented so badly, but like you say, it is most likely a case of backward compatibility. In fact, I think this particular example links in closely with what you were saying about client-side VAOs. May be worth me looking into a bit more.

I’ll let you know if I require any further assistance interpreting the link you’ve sent me. It seems clear enough though.

Thank you.