pontifikas's picture

How do VAOs introduce an improvement to VBOs?

I've been trying to figure out VAOs.
From what I read, VAOs store an array of vertex attributes.
What is not clear is how is this done and how is it beneficial?

I will write what I think is happening according to what I've read and please correct me if I'm wrong so I can develop full understanding on this matter.

1)Create the VAO
2)Bind it to make it current
3)Point to some attributes known to reside in the shader, and set some values to them using VertexAttribPointer()
!!--> The moment you do that, the VAO is updated as far as these attributes are concerned.
4) In the function were you do your drawing, berfore binding your VBOs first bind the VAO to make it current and activate
the values of the vertex attributes it contains.
5)Draw your scene using your VBOs.

BENEFIT: You only have to assign values to attributes once. At each drawing the program will refer to the VAO for the values of the attributes.

Thank you in advance.


Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
the Fiddler's picture

That's how I understand it, too:

Without VAOs you have to re-issue VertexAttribPointer calls every time you bind a VBO with a different layout. With VAOs, you encapsulate the layout information in a VAO object (at load time) and you simply bind the correct VBO, VAO and render.

It's cleaner and potentially faster.

zahirtezcan's picture

iirc you don't need to bind vbo if you use vao

the Fiddler's picture

There was large discussion over at opengl.org on how VAOs actually work. The extension spec is somewhat fuzzy on the details, but the 3.2 PDF spec cleans things up: according to table 6.6 (pg 271), the VBO is not part of the VAO state, while the EBO is (table 6.5).

For clarity's sake, by VBO I refer to the current ARRAY_BUFFER_BINDING, while by EBO to the current ELEMENT_ARRAY_BUFFER_BINDING (called ArrayBufferBinding and ElementArrayBufferBinding in OpenTK).

pontifikas's picture

Thank you for the forum link Fiddler. It was very enlightening.
So to sum it up and make the corrections should anyone else bump on this thread searching for info:

1)Create the VAO
2)Bind it to make it current
3) Create some VBO(vertex buffer object)
4) Bind that VBO to make it current
5) Put data into that VBO using BufferData function
6) Point to some attributes known to reside in the shader, and set some values to them using VertexAttribPointer(). This binds the VBO
with some attribute index(0-15). The moment you do that, the VAO is updated, as far as these attributes are concerned.
7) In the function were you do your drawing, Bind the VAO (to make sure it is current, though it may already be).
8)Draw your scene using your DrawElements functions.

I have two last questions though. I OpenTK we have an overload of VertexAttribPointer() that takes as last argument an int (offset).
Unfortunately no documentation is yet provided. How does it work?

Also, is binding vertex buffer object before calling VertexAttribPointer() with IntPtr.Zero as last argument like telling VertexAttribPointer "Associate an attribute index with the data of the currently bound VBO"?
While specifying some array as last argument is like telling VertexAttribPointer "Regardless of the currently bound(current-active) VBO associate the attribute index with THAT set of data"

the Fiddler's picture

The int overload for VertexAttribPointer is there for convenience. Internally, it simply calls VertexAttribPointer(..., new IntPtr(offset)). (Historical note: older versions of OpenTK used object parameters instead of generics, which meant you could pass a plain int even if such an overload did not exist: OpenTK would 'helpfully' take its address and pass that to OpenGL, resulting in crash later on. This bug was common enough that explicit int overloads were added as a workaround).

Filed a bug on the missing documentation as #1623: Convenience overloads are not documented

VertexAttribPointer works differently depending on whether GL.GetInteger(GetPName.ArrayBufferBinding) is zero or not:

  • If it is zero, you are using legacy, client-side Vertex Arrays and the last parameter is a pointer into the client-side array. Since pointers are ugly in .Net, OpenTK allows you to pass a reference to the first element of your array instead and performs the pointer-retrieving voodoo behind the scenes.
  • If it is not zero, you are using modern, server-side Vertex Buffer Objects and the last parameter is a zero-based offset into the server-side buffer data. Note that the compiler won't stop you from passing an array/pointer instead of a zero-based offset, but this will merely get you a nice crash when trying to render the VBO later on (OpenGL is expecting an offset in the range [0, sizeof(Vertex) - 1] and you are passing a pointer that's completely unrelated to that).

I'd suggest avoiding legacy VAs and the array-based overloads completely. They have no place in modern OpenGL apps and don't get along well with the .Net GC.

zahirtezcan's picture
the Fiddler wrote:

...according to table 6.6 (pg 271), the VBO is not part of the VAO state, while the EBO is (table 6.5).

I want to comment on this, it is right that "VBO binding" is not the part of VAO state, but how I had understood the VAO is parallel to the nvidia (I have a radeon too but did not test this yet) drivers (I have dx10 capable quadro and mobile) such that when VAO is bound your AttribPointer calls are recorded in a way that you dont need to bind VBOs again. In state tables, it is declared that AttribPointer is held but type is undefined. It is the case that (I have tested this by binding zero buffer to ARRAY_BUFFER before VAO, so it is only emprical not relevant to specs) when I bind VAO I don't need to bind VBO again. So, this gives me the option to hold different attributes on different buffers but I don't need to bind them again and again. One of the posts on the thread on the forum you pointed gives such usage scenario:

//initiate vbo, vao etc.
 
bind textures
bind vao
make draw calls
 
bind textures
bind vao
make draw calls
 
...

Note: before this post I did not know that EBO is part of VAO, it seems good to have that, but it could be of more use if we could store EAO with draw calls similar to IndirectCall mechanism introduced in OpenGL 3.3

pontifikas's picture

This is what I also made out from this thread. It is the call to VertexAttribPointer that binds the VBO to the VAO at a specific index. From that point on, VAO knows from which VBO to fetch data for each vertex attribute index. That is why in my second list of actions I dont include binding the VBO at the drawing function.

pontifikas's picture
zahirtezcan wrote:

Note: before this post I did not know that EBO is part of VAO, it seems good to have that, but it could be of more use if we could store EAO with draw calls similar to IndirectCall mechanism introduced in OpenGL 3.3

When you say that EBO is part of VAO you mean that, in order for the VAO to Draw the EBO must be Created, Bound and Populated after the target VAO is made current? I'm referring to creation in particular.

the Fiddler's picture

As I understand it, it means the last EBO you bind is "latched" into the VAO state. Next time you bind that VAO, it should automatically bind that EBO, too.

Creation itself shouldn't play a role, only the last call to GL.BindBuffer(BufferTarget.ElementArrayBuffer, ...) for any given VAO.

(Haven't tried this myself, though).

golkeeper's picture

True, according to http://www.opengl.org/wiki/Vertex_Array_Objects

"Let us say that you are issuing a glDrawElements command while a particular VAO is bound. In our pseudo-code, let us say that the VAO binding is called pVAO. Here is what will happen, relative to our pseudo-code definition.
The pVAO->pElementArrayBufferObject is the buffer object from which vertex indices will be pulled. If this is NULL (ie: bound to zero), an error is raised: you can use glDrawArrays, but not glDrawElements without an element buffer. "

"The VertexArrayObject::pElementArrayBufferObject is controlled by the binding of GL_ELEMENT_ARRAY_BUFFER. Calling void glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bufferObj) will set this field of the VAO"

"Changing the GL_ARRAY_BUFFER binding does not affect VAO state. This is different from the GL_ELEMENT_ARRAY_BUFFER binding, which is directly part of VAO state. It is only when calling one of these two functions that the GL_ARRAY_BUFFER binding matters to the VAO. "

So, glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bufferObj) changes incapsulated in binded VAO EBO state to current GL_ELEMENT_ARRAY_BUFFER, VertexAttribPointer is the only function that changes VAO's VBO state to current GL_ARRAY_BUFFER.