When Is What Bound to a Vao

When is What bound to a VAO?

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, ...).

how does a VBO get attached to a VAO

how does a VBO get attached to a VAO

It is attached when glVertexAttribPointer is called.

Each attribute which is stated in the Vertex Array Objects state vector may refer to a different Vertex Buffer Object. This reference is stored when glVertexAttribPointer is called.
Then the buffer which is currently bound to the target ARRAY_BUFFER is associated to the attribute and the name (value) of the object is stored in the state vector of the VAO. The ARRAY_BUFFER binding is a global state.

This behaves different to the Index Buffer (ELEMENT_ARRAY_BUFFER). The index buffer binding is stated within the Vertex Array Object. When a buffer is bound to the target ELEMENT_ARRAY_BUFFER, then this buffer is associated to the vertex array object which is currently bound.


That means when you call glBindBuffer(GL_ARRAY_BUFFER, VBO);, then a global state is set that stores VBO. glVertexAttribPointer retrieves VBO from the global state and assoicates it to VAO.
When glBindBuffer(GL_ARRAY_BUFFER, ...) is called again, then the previous binding is lost, but of course it does not change any state of the VAO.

glBindVertexArray(VAO);              # bind VAO (global state)

glBindBuffer(GL_ARRAY_BUFFER, VBO1); # bind VBO1 (global state)
glVertexAttribPointer(0, ...); # associate VBO1 to attribute 0 in VAO

glBindBuffer(GL_ARRAY_BUFFER, VBO2); # bind VBO2 (change global state, VBO1 binding is lost)
glVertexAttribPointer(1, ...); # associate VBO2 to attribute 1 in VAO

What happens when binding a VAO without unbinding another bound VAO?

Override.

If the bind is successful no change is made to the state of the vertex array object, and any previous vertex array object binding is broken.

https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glBindVertexArray.xhtml

VBO must be bound after VAO

I don't know why I have to bind vbo after binding vao in render function"

Because you try to create and initialize the buffer object 's data store by glBufferData and for this the buffer has to be bound.

I recommend to move the code, which creates and initializes the buffer to set_buffers. Then it will be sufficient to bind the vertex array object only, in the function render:

void render()
{
glClear(GL_COLOR_BUFFER_BIT);

glUseProgram(shadptr->progGraphics);
glBindVertexArray(vao_g);
glDrawArrays(GL_TRIANGLES, 0, 6);

glUseProgram(shadptr->progText);
glBindVertexArray(vao_t);
glDrawArrays(GL_TRIANGLES, 0, 6);

SDL_GL_SwapWindow(winptr->window);
}

void set_buffers()
{
....

GLfloat vertices[6][4] = {
{ 1.0f, 1.0f, 1.0f, 1.0f },
{ 1.0f, 1.0f, 1.0f, 1.0f },
{ 1.0f, 1.0f, 1.0f, 1.0f },

{ 1.0f, 1.0f, 1.0f, 1.0f },
{ 1.0f, 1.0f, 1.0f, 1.0f },
{ 1.0f, 1.0f, 1.0f, 1.0f }
};

glBindBuffer(GL_ARRAY_BUFFER, vbo_g);
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 6 * 4, vertices, GL_DYNAMIC_DRAW);

glBindBuffer(GL_ARRAY_BUFFER, vbo_t);
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 6 * 4, vertices, GL_DYNAMIC_DRAW);

glBindBuffer(GL_ARRAY_BUFFER, 0);
}

I recommend to use glBufferSubData, to change the content of a buffer. This will update the buffer, but doesn't creates and initializes a completely new data store and thus would be much more efficient.

Why should I bind the index buffer to draw elements with a VAO?

Why this line is necessary ?

It is not necessary. However, the element buffer reference is stored in the VAO. Just remove glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);. This line breaks the binding of the index buffer and to the VAO.

The Index Buffer (ELEMENT_ARRAY_BUFFER) binding is stored within the Vertex Array Object. When glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO) is called the element buffer object ID is stored in the currently bound Vertex Array Object. Therefore the VAO must be bound before the element buffer with glBindVertexArray(VAO).

Unlike to the Index Buffer, the Vertex Buffer binding (ARRAY_BUFFER) is a global state.

Each attribute which is stated in the VAOs state vector may refer to a different ARRAY_BUFFER. When glVertexAttribPointer is called the buffer which is currently bound to the target ARRAY_BUFFER, is associated to the specified attribute index and the ID of the object is stored in the state vector of the currently bound VAO.

Call to glBindVertexArray(vao): what does it do if 'vao' is already bound, and how to design shape classes accordingly?

but my guess would be that it doesn't actually do anything if vao is
already bound currently. Is that guess correct?

Yes. As far as correctness is concerned, re-binding something that already is bound is not a problem.

It might add a small performance penalty, though. A reasonable GL implementation would probably not do much in this case, but you still have a call into the GL lib, and it has to find the current context of the thread before it has a chance to do nothing. So it might be possible that you are still better off if you cache the currently VAO on your side and only call into the GL if it actually changes. But that is something that only profiling/benchmarking can tell you. If you have many thousands or even millions of instances, that might become a real problem - OTOH, you are screwed than anyway if you have a separate draw call per instance - you should look into instanced rendering in that case.

Also, I saw a tutorial some time ago that used to call
glBindVertexArray(0) at the end of render(). Wouldn't that be a
potential performance loss when there are many shapes?

Yes. Most of the tutorials do this for some "cleanness", but it is really a waste of time in most situations. Unbinding some GL object is only a useful operation if you can (and actually intent to) do something in the "unbound" state - like FBO 0 which is just the default framebuffer. For VAOs, in the core profile, VAO 0 is utterly useless, and you have to bind one in any case before you can draw - so you win nothing by unbinding any other inbetween.

VAO and GL_ELEMENT_ARRAY_BUFFER association

Do I need to call glBindBuffer(GL_ELEMENT_ARRAY_BUFFER) between bind and unbind of VAO?

The OpenGL specification (chapter 10.3. VERTEX ARRAYS) clearly says:

A vertex array object is created by binding a name returned by GenVertexArrays
with the command
void BindVertexArray( uint array ); array is the vertex array object name.
The resulting vertex array object is a new
state vector
, comprising all the state and with the same initial values listed in tables 23.3 and 23.4.

BindVertexArray may also be used to bind an existing vertex array object.
If the bind is successful no change is made to the state of the bound vertex array
object, and any previous binding is broken.

The Tables 23.3 and 23.4 contain ELEMENT_ARRAY_BUFFER_BINDING.

This means, that the GL_ELEMENT_ARRAY_BUFFER has to be bound after the vertex array object has been bound (glBindVertexArray).

The "name" of the GL_ELEMENT_ARRAY_BUFFER object is stored in the vertex array objects state vector.
If the vertex array object has been unbound and is bound again, then the GL_ELEMENT_ARRAY_BUFFER is known and bound again, too.


Note, glBufferData creates a new data store for a buffer object. glBufferData delete any existing data store, and set the values of the buffer object’s state variables. This means, that the data are associated to the buffer object until you associate new data. - see Khronos reference page glBufferData and OpenGL 4.6 API Compatibility Profile Specification; 6.1 Creating and Binding Buffer Objects



Related Topics



Leave a reply



Submit