VBO to VAO

VBO: All our geometry is currently drawn from the bound IBO and VBO with glDraw[Range]Elements(). I never did understand why, but we could not make our code work unless we specified the offsets/types/normalizations of every vertex attribute every time we switched to another IBO/VBO pair. Does anyone understand why? Note: the vertices in all our VBOs are identical (same attributes, same offsets, same types, etc).

VAO: After reading the OpenGL v3.10 specification, I have a few questions about VAOs. The description of VAOs led did not make it clear to me that the VAO remembers and restores the VBO attributes and their offsets/types/normalizations when they are bound. However, table 6.3 and 6.4 seems to clearly state this information is stored with the VAO.

Here are my VAO questions:

#1: Does binding the VAO make all this VBO state active too? And does it also somehow make clear to the GPU whether the indices in the corresponding IBO are 16-bits or 32-bits (I don’t see that)?

#2: Why do I not see an IBO or VBO identifier in the VAO state (in tables 6.3 and 6.4)? How can binding the VAO also bind the IBO and VBO if they are not identified in the VAO state?


context: upgrading cross platform 3D engine from OpenGL210/GLSL120 to OpenGL310/GLSL140 (with 185.85 drivers).

computers: CPU = phenom + 4GB RAM : video = nvidia GTX285 + 2GB VRAM : OS #1 = ubuntu64 v9.04 : OS #2 = winxp64

Because GL_ARRAY_BUFFER_BINDING is a latched state. You never bind a buffer for a certain vertex attribute directly, instead you bind a buffer to GL_ARRAY_BUFFER, then call glVertexAttribPointer (or one of the deprecated gl*Pointer functions). Only at that point is GL_ARRAY_BUFFER_BINDING copied to GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING for the specific vertex attribute.

You cannot switch to another buffer by calling glBindBuffer(GL_ARRAY_BUFFER, x) without subsequent calls to gl*Pointer(). GL_ARRAY_BUFFER_BINDING is never used for rendering.

#1: Does binding the VAO make all this VBO state active too? And does it also somehow make clear to the GPU whether the indices in the corresponding IBO are 16-bits or 32-bits (I don’t see that)?

Yes, no. The index type remains a parameter of glDrawElements and is not stored in a VAO.

#2: Why do I not see an IBO or VBO identifier in the VAO state (in tables 6.3 and 6.4)? How can binding the VAO also bind the IBO and VBO if they are not identified in the VAO state?

It’s right there in table 6.4. GL_ELEMENT_ARRAY_BUFFER_BINDING for the index buffer binding, GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING for each vertex attribute binding.

It’s right there in table 6.4. GL_ELEMENT_ARRAY_BUFFER_BINDING for the index buffer binding, GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING for each vertex attribute binding.[/QUOTE]If that’s what GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING means, why are there 16 of them?

Because you can use a different buffer for each vertex attribute.

Wait. So the VAO doesn’t know which VBO is attached? It only knows the offsets of the <= 16 attributes in the first vertex of the VBO? I assume you’re not saying you can have <= 16 VBOs attached to the VAO, right? After all, a VBO is a VERTEX buffer object, not an ATTRIBUTE buffer object.

This leaves me a bit confused. What does the driver do when we call the glDrawElements() function? How does the driver convert this VAO information into something the GPU understands? I would imagine the GPU needs the 64-bit starting address in GPU memory of each attribute, plus the rest of the information in the VAO state. But how does the driver know this if it does not keep track of which VBO is attached?

I’m missing something. Does the VAO know which IBO and VBO are attached to it, or not? If so, how (where is that state)? If not, how can the driver send sufficient information to the GPU when the glDrawElements() is called?

Wait. So the VAO doesn’t know which VBO is attached? It only knows the offsets of the <= 16 attributes in the first vertex of the VBO?[/QUOTE]
I think we’re in desperate need of a concrete example at this point.

Here are two batches using VAOs and VBOs, supposing 1) you don’t need to change any state between the batches except to change a texture, and 2) the VAOs to be used are already built:


  glBindTexture
  glBindVertexArray
  glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, ...)
  glDrawRangeElements
  ...
  glBindTexture
  glBindVertexArray
  glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, ...)
  glDrawRangeElements
  ...

As you can see, no vertex attribute VBO binds, vertex attribute pointer calls, or vertex attribute enables/disables. All cached in the VAO.

That is indeed exactly what I am saying.

After all, a VBO is a VERTEX buffer object, not an ATTRIBUTE buffer object.

OpenGL only knows a single type of buffer object.

Vertex buffer objects, index buffer objects, pixel buffer objects, uniform buffer objects, or texture buffer objects are just descriptions based on the usage of a particular buffer object. They all are the same type of object: a buffer object. And you can indeed use a single buffer object for all these purposes. I wouldn’t recommend it, though.

So yes, it is entirely possible that you use one buffer object for normals, another for vertex positions, a third one for texture coordinates, and so on. Or you can mix those attributes in whatever way you like.

And all the information that is needed for each attribute (buffer binding, size, type, stride, offset, enabled, normalized, and integer flags) plus the binding for a buffer object containing vertex indices (ELEMENT_ARRAY_BUFFER) is stored in a VAO. Thus it’s not even necessary to re-bind to GL_ELEMENT_ARRAY_BUFFER, Dark Photon.

Sorry, but I’m still missing something here.

When you say “no VBO binds”, are you saying there IS no VBO? Are you saying the VAO replaces the VBO… that the VAO is essentially a VBO plus attached attribute information?

If so, please explain the first sentence of the “Vertex Array Objects” section in the OpenGL v3.10 specification, which states:

“The buffer objects that are to be used by the vertex stage of GL are collected together to form a vertex array object. All state related to the definition of data used by the vertex processor is encapsulated in a vertex array object.”

When I read the above, I hear them saying “the VBO and any other buffer objects needed by the vertex stage are part of the VAO”. It does not say “the VAO is the buffer object”. I am not saying you are wrong. But I am saying that is terribly misleading wording if you are correct.

Also, if the VAO is a buffer object itself, and is not just a collection of buffer objects (like they say it is above), then why is the VAO not bound with:

glBindBuffer (GL_ARRAY_BUFFER, vaoID); // ???

Instead, according to you and the OpenGL v3.10 specification, the VAO is bound not as a buffer object, but as a “vertex array” with:

glBindVertexArray (vaoID);

The above implies to me that the VAO does not contain vertex data, and does not correspond to a buffer object. So I ask again, if we don’t bind a VAO, an IBO, and a VBO, then how does the vertex data get into the VRAM, and how does a given glDrawElements() know where the vertex data is?


Separately, in your example, why must you glBindVertexArray() and glBindBuffer(GL_ELEMENT_ARRAY_BUFFER…) before the second glDrawElements(…) call? Aren’t they still bound?


In our current code, we bind an IBO, bind a VBO, call glVertexAttribPointer() 5 times, then call glDrawElements(). Therefore I understand how the GPU knows where to get vertex-indices from (the bound IBO), and how the GPU knows where to get vertex-data from (the bound VBO).

In your code I see how the GPU knows which IBO to fetch from, but I do NOT see how the GPU knows which VBO to fetch from. I’m not saying your code is wrong, I’m just saying I don’t see how sufficient information gets to the driver or GPU.

That is indeed exactly what I am saying.[/QUOTE]Okay, then how do you attach (say) 5 VBOs to a VAO? Let me guess - you call glVertexAttribPointer() 5 times. But that is not a VBO, that is just a vertex attribute specification. True, this does provide the stride too, so it knows how far to the next instance of the same attribute, but glVertexAttribPointer() does NOT say how many of these attributes follow. It also doesn’t transfer these attributes anywhere either. So how do all 5 sets of vertex attributes get into VRAM? And when glDrawElements() gets called, how does the GPU know where those 5 arrays of attributes are?

[quote]After all, a VBO is a VERTEX buffer object, not an ATTRIBUTE buffer object.
OpenGL only knows a single type of buffer object.

Vertex buffer objects, index buffer objects, pixel buffer objects, uniform buffer objects, or texture buffer objects are just descriptions based on the usage of a particular buffer object. They all are the same type of object: a buffer object. And you can indeed use a single buffer object for all these purposes. I wouldn’t recommend it, though.

So yes, it is entirely possible that you use one buffer object for normals, another for vertex positions, a third one for texture coordinates, and so on. Or you can mix those attributes in whatever way you like. And all the information that is needed for each attribute (buffer binding, size, type, stride, offset, enabled, normalized, and integer flags) plus the binding for a buffer object containing vertex indices (ELEMENT_ARRAY_BUFFER) is stored in a VAO. Thus it’s not even necessary to re-bind to GL_ELEMENT_ARRAY_BUFFER, Dark Photon. [/QUOTE]Then what does the setup code look like? Maybe that’s what I need to see, to understand how this works?

If I have 5 attributes, do I need to create 5 VBOs? Do I then glBuffer[Sub]Data() the vertex data from CPU memory into these 5 VBOs in VRAM? This is a total disaster, because my vertice are interleaved (per usual practice)! My code would need to call glBufferSubData() once for each attribute in each vertex, which would be thousands to millions of excess function calls!!!

What does the code look like to attach 5 VBOs to a VAO?

Yup, I think I need to see what comes first, what comes second, what comes third, etc - including setup code. Until I see how the GPU knows what to do (and where to get the vertex data) when glDrawElements() is called, I am confused. Remember, the data is in VRAM, not in CPU memory.

A VAO contains:

  • a bind point for a buffer object containing vertex indices
  • for each vertex attribute (at least 16):
    – a bind point for a buffer object
    – enable flag
    – integer flag
    – normalize flag
    – size
    – type
    – stride
    – offset

There is always a VAO bound to the context. The default VAO has the name 0 and is created along with the context, similar to textures 0 and framebuffer 0. When you bind a VAO, all the state stored in it will become active. Calls to glVertexAttribPointer, or glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, …), modify the currently bound VAO.

// BEGIN INITIALIZATION
// Define some vertex data 
struct Vertex {
  GLfloat position[3];
  GLfloat texcoord[2];
};
Vertex vertexdata[NUM_VERTS] = { ... };
GLubyte indexdata[NUM_INDICES] = { 0, 1, 2, ... };

// Create and bind a VAO
GLuint vao;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);

// Create and bind a BO for vertex data
GLuint vbuffer;
glGenBuffers(1, &vbuffer);
glBindBuffer(GL_ARRAY_BUFFER, vbuffer);

// copy data into the buffer object
glBufferData(GL_ARRAY_BUFFER, NUM_VERTS * sizeof(Vertex), vertexdata, GL_STATIC_DRAW);

// set up vertex attributes
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, position));
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, texcoord));

// Create and bind a BO for index data
GLuint ibuffer;
glGenBuffers(1, &ibuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibuffer);

// copy data into the buffer object
glBufferData(GL_ELEMENT_ARRAY_BUFFER, NUM_INDICES * sizeof(GLubyte), indexdata, GL_STATIC_DRAW);

// At this point the VAO is set up with two vertex attributes
// referencing the same buffer object, and another buffer object
// as source for index data. We can now unbind the VAO, go do
// something else, and bind it again later when we want to render
// with it.

glBindVertexArray(0);

// END INITIALIZATION

// BEGIN RENDER LOOP

// This is it. Binding the VAO again restores all buffer 
// bindings and attribute settings that were previously set up
glBindVertexArray(vao);
glDrawElements(GL_TRIANGLES, NUM_INDICES, GL_UNSIGNED_BYTE, (void*)0);

// END RENDER LOOP

Xmas, thanks much for your example. I am still a bit fuzzy about the details and implementation of the VAO, but your code got me closer to understanding. A few follow-up questions.

The last two entries in table 6.4 in the OpenGL v3.10 specification appear to be 1 IBO identifier and 16 VBO identifiers. Does this mean up to 16 VBOs can be attached to one VAO, in which case glDrawElements() calls would grab vertex elements from several VBOs simultaneously?

If so, how can this be specified? Would you just repeat the three sections of code you comment with…

// create and bind a BO for vertex data
// copy data into the buffer object
// set up vertex attributes

… as many times as you want VBOs attached to the VAO?

If this is true, then I assume every time an application binds a VAO and binds a VBO, that VBO is appended to the VAO state? If this is the case, how would an application ever selective detach one or more of the VBOs attached to a VAO? It seems like the application would need to destroy the VAO and create a new one. Or is my whole notion just completely misguided?

Thanks for helping me slowly figure out these VAOs.

16 VBO identifiers

If I read correctly there is only one: ARRAY_BUFFER_BINDING

Did you talk about this state: VERTEX_ATTRIB_ARRAY_BUFFER_BINDING

There are 16 identifiers but it is for vertex attributes arrays if I am correct.

I may be wrong here since I never had the opportunity to play with this extension yet. For the ones who did, is this mean that you need one vao for each vbo used (vertex, normals, texture coordinates,…) ? In this case, it may be better to pack all this in one big vbo, in order to bind only one vao per rendering batch, isn’t it?

I thought it was not possible to attach an index buffer to a VAO but in fact it looks like it is the case. That’s really cool because in my design in which I have render job, now instead of creating a render job that would contain an index buffer as well as a vertex buffer, it would only contain a VAO ^^.

Basically, yes. However you should have a real reason to separate your vertex attributes into several BOs, otherwise keep them in a single BO.

If this is true, then I assume every time an application binds a VAO and binds a VBO, that VBO is appended to the VAO state?

Not quite.

Binding a BO to GL_ARRAY_BUFFER does not modify the bound VAO. Only when you call glVertexAttribPointer(n, …) is the binding to GL_ARRAY_BUFFER copied over into the binding point of attribute n in the VAO.

glBindBuffer(GL_ARRAY_BUFFER, 1);
glVertexAttribPointer(0, ...); // at this point buffer 1 becomes bound to attrib 0
glVertexAttribPointer(1, ...); // buffer 1 also bound to attrib 1
glBindBuffer(GL_ARRAY_BUFFER, 2); // does NOT modify any binding of a specific attribute
glVertexAttribPointer(1, ...); // buffer 2 is now bound to attrib 1. Attrib 0 still uses buffer 1

Note that the GL_ELEMENT_ARRAY_BUFFER bind point is different and is actually part of the VAO.

So it appears that a VAO does contain the identity of an IBO, but does not contain the identity of any VBO — BUT — the VAO does contain some kind of references to the several interleaved “attribute arrays” in a typical VBO (though these “attribute arrays” could in fact be in separate VBOs and/or non-interleaved (say, one attribute array after another in a single VBO).

I hate to think what happens if an application deletes a VBO that contains one (or more) of these attribute arrays! The VAO will apparently have no idea, since it knows nothing whatsoever about VBOs (only those attribute arrays, which aren’t really buffers at all, since they can be interleaved). This seems rather strange to me, but maybe I don’t understand subtle/obscure aspect of the architecture. On second thought, remove the “maybe”.

Maybe the strangeness stems from the “latching” business, which took me a bit of getting used to, too.

I really can’t believe how much disinformation is going on in this thread.

A little about how buffer objects work (GL 2.1 and before):

When you bind a buffer object to the GL_ARRAY_BUFFER, you have not done anything. The buffer that is bound to GL_ARRAY_BUFFER only matters when you call glVertexAttribPointer or similar functions like glVertexPointer, etc.

Upon making such a call, the OpenGL context stores the information provided in the call. So the stride, offset, and other parameters for that vertex attribute are stored. Also, if a non-zero buffer object is bound to GL_ARRAY_BUFFER, it then interprets the pointer argument as an offset into this buffer object. Both the offset and the buffer are stored in the context data for that vertex attribute.

Changing GL_ARRAY_BUFFER doesn’t mean anything. What matters is what buffer object is bound when you call glVertexAttribPointer. Only then is the association between a vertex attribute and a buffer object made.

How vertex array objects work (ARB_Vertex_Array_Object and GL 3.0+):

If a non-zero Vertex Array Object (VAO) is bound to the context at the time of calling glVertexAttribPointer or similar functions, then the vertex attribute data that was previously stated to be stored in the context is instead stored in the VAO currently bound to the context.

It is not complicated at all.

I hate to think what happens if an application deletes a VBO that contains one (or more) of these attribute arrays!

The specification clearly states what should happen:

Interesting. Are you sure about that? In the extension spec, I don’t find mention of an ELEMENT_ARRAY bind point being stored in the VAO. Did I miss it?

All these speculations, doubts, the reported neglible (if at all) performance advantage, the limited application possibilities and weird usage make me wonder, why VAO has been promoted to core so quickly and un-asked for.

Why hasn’t it been availble as extension before to prove its usefulness? FBO has shown that this is a way which works. Now we have something in the core that nobody wanted this way, but will stay for a long time.