27
votes

I am writing a little graphics engine using OpenGL ( via OpenTK with C# ).

To define vertex attributes, I have a VertexDeclaration class with an array of VertexElement structures that are mapped to glEnableVertexAttribArray/glVertexAttribPointer calls.

Also, to support multiple vertex streams, I have a special structure holding a vertex buffer, vertex declaration, vertex offset and instance frequency (like the XNA's VertexBufferBinding structure).

Currently, whenever a drawing call is invoked, I iterate over all the set vertex streams and bind their vertex buffers, apply vertex declarations, disable unused vertex attributes and draw the primitives.

I would like to use VAOs to cache the glEnableVertexAttribArray calls into them, and whenever a vertex stream is applied, bind the VAO and change its array buffer binding.

Is that a correct usage of VAOs?

1
it seems this question is a useful link here. Not sure why the answer says "No" to "caching" the glEnableVertexAttribArray calls. The answer to the linked question "When to call glVertexAttribPointer?" says that's exactly what VAOs are for and that's the recommended approach nowadays. - xealits

1 Answers

54
votes

Is that a correct usage of VAOs?

No1.

glVertexAttribPointer uses the buffer object that was bound to GL_ARRAY_BUFFER at the moment the function was called. So you can't do this:

glVertexAttribPointer(...);
glBindBuffer(GL_ARRAY_BUFFER, bufferObject);
glDrawArrays(...);

This will not use bufferObject; it will use whatever was bound to GL_ARRAY_BUFFER when glVertexAttribPointer was originally called.

VAOs capture this state. So the VAO will, for each vertex attribute, store whatever buffer object was bound to GL_ARRAY_BUFFER when it was called. This allows you to do things like this:

glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, buffer1);
glVertexAttribPointer(0, ...);
glVertexAttribPointer(1, ...);
glBindBuffer(GL_ARRAY_BUFFER, buffer2);
glVertexAttribPointer(2, ...);

Attributes 0 and 1 will come from buffer1, and attribute 2 will come from buffer2. VAO now captures all of that state. To render, you just do this:

glBindVertexArray(VAO);
glDraw*();

In short, if you want to change where an attribute's storage comes from in OpenGL, you must also change it's format. Even if it's the same format, you must call glVertexAttribPointer again.

1: This discussion assumes you're not using the new ARB_vertex_attrib_binding. Or, as it is otherwise known, "Exactly how Direct3D does vertex attribute binding." If you happen to be using an implementation that offers this extension, you can effectively do what you're talking about, because the attribute format is not tied with the buffer object's storage. Also, the tortured logic of glVertexAttribPointer is gone.

In general, the way we solve this in the OpenGL world is to put as many things as possible in the same buffer object. Failing that, just use one VAO for each object.