Handling VAOs in GL 4.5 with DSA

Started by
13 comments, last by Xycaleth 9 years, 7 months ago

In GL_EXT_direct_state_access there was a function named glVertexArrayVertexAttribOffsetEXT. I do not see an equivalent in GL_ARB_direct_state_access. I'm trying to accomplish the following using the new core DSA functions:


GLuint vao = 0;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[3]);
glEnableVertexArrayAttribEXT(vao, 0);
glEnableVertexArrayAttribEXT(vao, 1);
glVertexArrayVertexAttribOffsetEXT(vao, buffers[0], 0, 3, GL_FLOAT, GL_FALSE, 0, 0);
glVertexArrayVertexAttribOffsetEXT(vao, buffers[1], 1, 2, GL_FLOAT, GL_FALSE, 0, 0);

I have tried the following, but it does work:

GLuint vao = 0;
glCreateVertexArrays(1, &vao);
glVertexArrayElementBuffer(vao, buffers[3]);
glEnableVertexArrayAttrib(vao, 0);
glEnableVertexArrayAttrib(vao, 1);
glVertexArrayVertexBuffer(vao, 0, buffers[0], 0, 0);
glVertexArrayVertexBuffer(vao, 1, buffers[1], 0, 0);
glVertexArrayAttribFormat(vao, 0, 3, GL_FLOAT, GL_FALSE, 0);
glVertexArrayAttribFormat(vao, 1, 2, GL_FLOAT, GL_FALSE, 0);
Advertisement

This is discussed in Issue 31 in the spec, and the recommendation is to use the GL_ARB_vertex_attrib_binding calls instead:

Do we need VertexArray*Offset? These are present in EXT_direct_state_access.

RESOLVED: No. GL 4.4 intoduced ARB_vertex_attrib_binding which allows separately changing the buffer (via BindVertexBuffer) and setting the format (via VertexAttribFormat). In this version we provide DSA routines to match these: VertexArrayVertexBuffer and VertexArrayAttribFormat.

Futhermore, no need to add VertexArrayAttribBinding, we can use VertexArrayBindingDivisor which is based on ARB_vertex_attrib_binding too.

Direct3D has need of instancing, but we do not. We have plenty of glVertexAttrib calls.

I don't understand why the first version works but the second one does not.

I don't understand why the first version works but the second one does not.

You never bind the VAO in the second version.

You are missing


glVertexArrayAttribBinding (GLuint vaobj, GLuint attribindex, GLuint bindingindex)

to bind an attribute (index) to a vertex buffer binding (index).

Also check all function definitions again so you don't mix them up (e.g. second parameter of glVertexArrayVertexBuffer is also the VBO binding index, not the attribute index).


void glVertexArrayAttribBinding  (GLuint vaobj, GLuint attribindex, GLuint bindingindex);

void glVertexArrayVertexBuffer   (GLuint vaobj, GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride);
void glVertexArrayBindingDivisor (GLuint vaobj, GLuint bindingindex, GLuint divisor);

void glEnableVertexArrayAttrib   (GLuint vaobj, GLuint attribindex);
void glDisableVertexArrayAttrib  (GLuint vaobj, GLuint attribindex);
void glVertexArrayAttribFormat   (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset);
void glVertexArrayAttribIFormat  (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset);
void glVertexArrayAttribLFormat  (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset);
...

Edit: The last sentence of Issue 31 makes no sense (also it's unresolved), how else should the attribute "know" which VBO it refers to.

Ok, I guess I have to admit that I have never fully grasped vertex attributes in OpenGL. I pretty much knew how to get glVertexArrayVertexAttribOffsetEXT working, but I have no clue how to get these other functions to work. The AttribOffset function only has an single index parameter, and with these new functions you have both attribindex and bindingindex. I have no clue what the difference is. Also, I don't understand if the relativeoffset of glVertexArrayAttribFormat is identical to the stride of glVertexArrayVertexBuffer.

I'm a bit rusty on the new vertex attrib binding extension (which these new vertex array DSA functions are an alternative for), but I'll give it a go.

In the beginning, there was nothing glVertexArribPointer would let you describe the format (type, size, etc) of a vertex attribute as well as where to source that data from, albeit this source was implicit from the currently bound vertex buffer object. Let's take your code as an example... so you have two vertex attributes:

Vertex attribute 0:

  • Buffer: buffers[0]
  • Index: 0
  • Num components: 3
  • Type: Float
  • Stride to next element: 12 bytes (implicit from number of components and data type)
  • Buffer offset of first element: 0 bytes

Vertex attribute 1:

  • Buffer: buffers[1]
  • Index: 1
  • Num components: 2
  • Type: Float
  • Stride to next element: 8 bytes (implicit from number of components and data type)
  • Buffer offset of first element: 0 bytes

This might make it clearer that there are two things being described for each attribute. The "layout" of the attribute, and the buffer from which to read the data.

The vertex attrib binding extension was Khronos' solution for separating these two descriptors. Instead of using glVertexAttribPointer, you use glVertexAttribFormat to describe the attribute format, glBindVertexBuffer to describe a buffer which can be read from and how to read from it, and glVertexAttribBinding to associate link formats with buffer sources (or their DSA counterparts).

Taking your code again, it would look something like this (non-DSA, because it's easier for me to write):


glBindVertexArray(vao);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, buffers[0]);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
glBindBuffer(GL_ARRAY_BUFFER, buffers[1]);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, 0);
With the vertex attrib binding function, we get something like this (now in DSA):

glEnableVertexAttribArray(vao, 0);
glEnableVertexAttribArray(vao, 1);
 
// Setup the formats
glVertexArrayAttribFormat(vao, 0, 3, GL_FLOAT, GL_FALSE, 0);
glVertexArrayAttribFormat(vao, 1, 2, GL_FLOAT, GL_FALSE, 0);
 
// Setup the buffer sources
glVertexArrayVertexBuffer(vao, 0, buffers[0], 0, 0); // Note the 2nd argument here is a 'binding index', not the attribute index
glVertexArrayVertexBuffer(vao, 1, buffers[1], 0, 0);
 
// Link them up
glVertexArrayAttribBinding(vao, 0, 0); // Associate attrib 0 (first 0) with binding 0 (second 0).
glVertexArrayAttribBinding(vao, 1, 1);

I've not tested this, but I'm pretty sure it works :)

The remaining question you probably have is why do both the vertex buffer and the vertex attribute have an offset argument? Let's imagine that you store multiple models in a single vertex buffer, and not all of them have the same format. You can set up the formats ahead of time, for example:

  • Attrib 0: vec3, offset 0
  • Attrib 1: vec2, offset 16

Your buffer sources will also have its own offset, which is the base from where to start reading. So you might have a model's vertex data stored in a vertex buffer, but it's at offset 320 bytes from the start. You can set this offset on the vertex buffer and the format doesn't have to take this into account as it and the buffer source are separated! This also lets you swap buffer sources and formats a whole lot more easily, instead of having to rebind a load of buffers and respecifying the attribute types every time, as you would need to with the original glVertexAttribPointer

Tried it and still I get nothing on screen.

    GLuint buffers[3] = {0};

    glCreateBuffers(3, buffers);

    glNamedBufferData(buffers[0], sizeof(position), &position, GL_STATIC_DRAW);
    glNamedBufferData(buffers[1], sizeof(texcoord), &texcoord, GL_STATIC_DRAW);
    glNamedBufferData(buffers[2], sizeof(indices), &indices, GL_STATIC_DRAW);

    /*GLuint vao = 0;
    glGenVertexArrays(1, &vao);

    glBindVertexArray(vao);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[2]);

    glEnableVertexArrayAttribEXT(vao, 0);
    glEnableVertexArrayAttribEXT(vao, 1);

    glVertexArrayVertexAttribOffsetEXT(vao, buffers[0], 0, 3, GL_FLOAT, GL_FALSE, 0, 0);
    glVertexArrayVertexAttribOffsetEXT(vao, buffers[1], 1, 2, GL_FLOAT, GL_FALSE, 0, 0);*/

    GLuint vao = 0;
    glCreateVertexArrays(1, &vao);
    glVertexArrayElementBuffer(vao, buffers[2]);

    glEnableVertexArrayAttrib(vao, 0);
    glEnableVertexArrayAttrib(vao, 1);

    glVertexArrayAttribFormat(vao, 0, 3, GL_FLOAT, GL_FALSE, 0);
    glVertexArrayAttribFormat(vao, 1, 2, GL_FLOAT, GL_FALSE, 0);

    glVertexArrayVertexBuffer(vao, 0, buffers[0], 0, 0);
    glVertexArrayVertexBuffer(vao, 1, buffers[1], 0, 0);

    glVertexArrayAttribBinding(vao, 0, 0);
    glVertexArrayAttribBinding(vao, 1, 1);

    glBindVertexArray(vao);

You've probably already checked, but are you getting any GL errors? Either at draw time or after you've created the VAO?

You've probably already checked, but are you getting any GL errors? Either at draw time or after you've created the VAO?

No errors, no debug output warnings, just a blank screen.

This topic is closed to new replies.

Advertisement