C++ – When is What bound to a VAO

copengl

I've been learning OpenGL for three days and I can get stuff done but I feel like copy pasting without knowing what I'm doing. I seriously think I lack basic understanding about when is exactly what (VBO, attributes, …) bound to a Vertex Array Object (VAO), and haven't found any resources that clarify these aspects in detail.

In particular, these are some of my issues. If I create a VAO:

GLuint vao;
glGenVertexArrays(1, &vao);

can anything get bound to it before I bind the VAO? (if I create a VBO now, is it bound to the VAO?)

glBindVertexArray(vao);

After binding the VAO, if I create a VBO:

GLuint vbo;
glGenBuffers(1, &vbo);

is it bound to the VAO? Or does it happen when I bind it?

glBindVertexArray(vbo);

Or maybe when I copy something to it?

If I get an attribute location:

att = glGetAttribLocation(program_id, "name");

is it bound to the VAO? Or does it happen after enabling it:

glEnableVertexAttribArray(att);

… or after setting it:

glVertexAttribPointer(att, ...);

?

I guess EBOs behave just like VBOs, so I hope the same "rules" apply.

Uniforms should behave like globals, so they shouldn't be affected by VAOs at all.

Now, about unbinding:

If I "bind" a VBO to a VAO, and then unbind the VBO, does it get detached from a VAO?

If I have a VBO that is bound to multiple VAOs, what happens when I unbind that VBO?

And about freeing resources:

What happens when I delete an VBO? Does it get deleted from all the VAOs ? Or do they still have "dangling references" to that VBO?

And about programs:

IIUC I can reuse VBOs between programs. However, if VAOs bind attributes and VBOs, and attributes take a program parameter, can I reuse VAOs between programs? Why do attributes take a program parameter at all?

And about debugging:

Is there a way to pretty print the OpenGL state machine? I would like a way to know the programs that have been linked, with which shaders, which VAOs are there, which VBOs are bound to which VAOs, which attributes are bound to which VAOs and VBOs, have they been set? are they enabled? which uniforms are there…

And about drawing calls:

Suppose someone gives me a VAO, and I have to draw it. Is there a way to know if I should be calling glDrawArrays or glDrawElements? Can I query somehow this information from a VAO? Maybe along with the sizes of my VBOs stored in there?

Best Answer

That's a lot of sub-questions. But since this an area that is often confusing newer OpenGL enthusiasts, let me try and provide some content that will hopefully help more people. I will intentionally skim over some details, like vertex attributes that are not sourced from a buffer, to avoid writing a book here.

The key thing to understand is that a VAO is a collection of state. It does not own any data. It's VBOs that own vertex data. A VAO, on the other hand, contains all the state used to describe where a draw call gets its vertex attributes from. This includes, for each attribute:

  • If it's enabled.
  • Which buffer the attribute is stored in.
  • At which offset in the buffer the data starts.
  • The spacing between subsequent attributes (aka the stride).
  • The type of the data.
  • The number of components.

Plus, once only:

  • Which element array buffer is bound.

Mapping this to API calls, the following calls change state tracked by the currently bound VAO:

  • glEnableVertexAttribArray(...)
  • glDisableVertexAttribArray(...)
  • glVertexAttribPointer(...)
  • glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ...)

Note that this does not include the current binding of GL_ARRAY_BUFFER. The buffer used for each attribute is tracked indirectly, based on which buffer was bound when glVertexAttribPointer() is called for the specific attribute.

This should set the basis for the specific sub-questions:

If I create a VAO, can anything get bound to it before I bind the VAO?

No. The VAO needs to be bound before you can modify any state stored in it.

(if I create a VBO now, is it bound to the VAO?)

No. You can bind a VBO before you bind a VAO, and fill the VBO with data using glBufferData(). A VBO is essentially just a dumb data container. But any kind of vertex attribute setup tracked in the VAO can only be done after the VAO is bound.

If I get an attribute location, is it bound to the VAO?

No, glGetAttribLocation() only does what the name suggests, which is get an attribute location. It does not change any state. You will use the attribute locations for calls like glEnableVertexAttribArray() and glVertexAttribPointer().

Or does it happen after enabling it ... or after setting it

The association of an attribute with a given VBO is established when you call glVertexAttribPointer() while the given VBO is bound.

I guess EBOs behave just like VBOs, so I hope the same "rules" apply.

Mostly, but not entirely. The GL_ELEMENT_ARRAY_BUFFER binding is part of the state stored in the VAO. This makes sense because there is only one element array buffer used for a draw call (while the vertex attributes could come from multiple different array buffers), and there is no separate call that specifies "use the index data from the currently bound element array buffer", like glVertexAttribPointer() specifies "use the vertex data from the currently bound array buffer". Instead that happens implicitly when you call glDrawElements(). Therefore, the element array buffer needs to be bound at the time of the draw call, and this binding is part of the VAO state.

Uniforms should behave like globals, so they shouldn't be affected by VAOs at all.

Correct. Uniforms are associated with shader programs, not VAOs.

If I "bind" a VBO to a VAO, and then unbind the VBO, does it get detached from a VAO?

No. I believe this is already covered by the explanations above.

If I have a VBO that is bound to multiple VAOs, what happens when I unbind that VBO?

Since nothing happens with one VAO, still nothing with multiple VAOs.

What happens when I delete an VBO? Does it get deleted from all the VAOs ? Or do they still have "dangling references" to that VBO?

This is one of the darker corners of OpenGL. If you can recite the exact deletion rules for all object types (they are not all the same), you have reached the advanced level... In this case, the VBO is automatically unbound from the currently bound VAO, but not from other VAOs that are not currently bound. If other VAOs have references to the VBO, the VBO will stay alive until all those bindings are broken, or the VAOs deleted.

However, if VAOs bind attributes and VBOs, and attributes take a program parameter, can I reuse VAOs between programs?

Yes, you can use a VAO for multiple programs. Program state and VAO state are independent. The vertex attribute location in the program specifies which vertex attribute is used to source the values for each attribute/in variable in the vertex shader.

As long as multiple programs use the same locations for the same attributes, you can use the same VAO. To make this possible, you may want to specify the attribute location for each program by using the layout (location=...) directive the vertex shader, or by calling glBindAttribLocation() before linking the program.

Is there a way to pretty print the OpenGL state machine?

There are glGet*() calls that let you retrieve pretty much all of the current OpenGL state. Not convenient, but it's all available. Many platforms/vendors also provide developer tools that allow you to look at OpenGL state at a given point in your program execution.

Suppose someone gives me a VAO, and I have to draw it. Is there a way to know if I should be calling glDrawArrays or glDrawElements?

This is an unusual scenario. Most of the time, you create the VAO, so you know how to draw it. Or if somebody else created it, you would ask them to draw it. But if you really need it, you can get the currently bound element array buffer with glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, ...).