My brain is too small to understand how VAO binding and EAO (drawElements) interact, please help me understand a D3D->OpenGL scenario

I seem to be finding the opposite of this (c++ - OpenGL 3: glBindVertexArray invalidates GL_ELEMENT_ARRAY_BUFFER - Stack Overflow) case, i.e. things go awry when the element buffer is bound AFTER the vertex-array-buffer, but works when bound before (this example says theirs works when bound after, but not before.)

I’m in the end stage of porting a medium sized D3D7->D3D9 wrapper to D3D7->OpenGL and trying to work out kinks. D3D7 passes elements separately. There’s two paths that push the elements into revolving buffers. The path that does immediate data for both works, whereas the vbuffer path does not, merely because the timing in the calls is slightly different.

In the first case the elements are bound/pushed first. In the fail case second. In both cases a VAO is bound alternatively.

Basically doing the elements after the VAO setup appears to not work. It is drawing something though, that tends to obscure everything in black, somehow.

I can gather that somehow it may be possible to attach elements to VAO, and I wonder if that’s what’s happening here, perhaps even being attached to a previous VAO.

I would be interested in some theory, but mostly I can’t quite figure out how the APIs interact, and how to ensure they don’t get stuck together, and how to possibly use glDrawElements with VAO. Really this seems like an awful design (as usual for OpenGL) since it seems like the elements were attached to the VAO then it would be better to use “glDrawArrays” for that scenario. I may be completely lost here. My brain is too small for OpenGL.

1 Like

It’s kind of impossible to know what to say without actually seeing your code. Yes, you describe your code, but your descriptions are quite nebulous. For example:

What does that mean? What exactly do you mean by “VAO setup”? Do you unbind the VAO after this “setup”? What do you mean by “doing the elements”; what code does that represent? Do you mean creating the buffer you’re going to store indices in? Or putting index data into it? Or attaching it to a VAO for use in rendering? Because these are separate things.

Without seeing your code, it’s really difficult to know what’s going on.

1 Like

I was just about to edit some code in, sorry. The used APIs are glBindVertexArray (vertex description) glBindVertexBuffer (attach VBO to description) and glBindBuffer (bind elements) and some glBufferData/SubData mixed in where uploading is required.

@Alfonse_Reinheart assuming you’re the same as the answer in my stackoverflow link (I’ve always assumed/wondered) I just want to say about that, answer I didn’t find much information in it, so please let’s not rehash that style of an answer.

“doing the elements after the VAO setup appears to not work” means glBindBuffer on the EAO. Either before or after binding the VAO.

EDITED: Before anyone asks, two scenarios are follows:

  1. Doesn’t work.
    glBindVertexArray
    glBindVertexBuffer
    glBindBuffer(EAO)
    glDrawRangeElements

  2. Works.
    glBindBuffer(EAO)
    glBindVertexArray
    glBindVertexBuffer
    glDrawRangeElements

As for what I might be doing outside, please use your imagination if you can. Generally the vbuffer scenario (1) unbinds with glBindVertexBuffer, the others aren’t unbound, since they use internal/implementation buffers.

1 Like

The GL_ELEMENT_ARRAY_BUFFER binding is stored in the VAO. Changing the binding changes the value stored in the currently-bound VAO. Draw calls which use an element array use the binding from the currently-bound VAO. Buffer-related calls which use a target of GL_ELEMENT_ARRAY_BUFFER use the binding from the currently-bound VAO.

In the core profile, any call which modifies or uses the binding has unspecified behaviour if no VAO is bound (in the compatibility profile, a VAO is always bound; VAO 0 is the default VAO which always exists).

All other buffer bindings are context state; i.e. with a single context they behave like global variables. Whereas the element array buffer binding can be thought of as a field within a structure representing a particular VAO.

2 Likes

It looks like the EBO stored in the VAO has the correct data but the one you’re trying to replace it with doesn’t. In scenario 1, glBindBuffer(EAO) affects the draw call; in scenario 2 it doesn’t (but it will replace whatever EBO was stored in the previous VAO, the one which was bound before the glBindVertexArray call).

In general, you shouldn’t be modifying the element array binding when issuing draw calls. You should set it when you create the VAO, along with the attribute arrays, and drawing should just be “bind VAO, issue draw call”. Unless you’re modifying mesh topology on the fly, the element array for a given mesh shouldn’t change.

2 Likes

Okay okay okay, I hate asking for help, but OpenGL is kind of arcane in its ways. So going by glBindBuffer documentation there’s no mention of VAOs, so I guess it makes sense glBindBuffer attaches to a VAO right?

My impression was reading “Vertex” in VertexArray and VertexBuffer these were narrowly defined to deal with vertex data. In D3D you have/had FVF or VertexDeclaration, which in my head is how I think of a VAO even though I understand it’s only after glBindVertexBuffer arrived on the scene you can actually share them (by rebinding instead of the other way around.)

VAO is more like a display list then, oddly named. It takes so much to set up the vertex declaration it seems like a mistake to also use them for passing indices. Much of the design of OpenGL in latter days looks like trying to separate things that it packaged together. In order to implement this project I had to rely on many late stage additions to get OpenGL to act more like D3D9. Anyway, for some reason I hadn’t even suspected glBindBuffer. I was thinking maybe glBindVertexBuffer bound the (old) EAO along with the VBO. Just a big blind spot.

1 Like

Oh shoot, my mental model is still not right. If scenario 1 “Doesn’t work” and the goal is to bind the EAO to the VAO, why isn’t glBindVertexArray+glBindBuffer(EAO) effectively achieving that? By “affects the draw call” do you mean it’s successfully passing its indices? If so why is that version the one that doesn’t get the picture (I can understand if scenario 2 only happens to work because the VAO/VBO happens to be the same between calls.) Sorry again! I’m embarrassed to ask.

Only the GL_ELEMENT_ARRAY_BUFFER binding is stored in a VAO. All other targets are context state.

A VAO a state container. It packages the various state required to define a “mesh”: both vertex attributes and the indices defining the topology. The intention is to minimise the effort required to draw a mesh.

glBindVertexBuffer lets you change the buffer separately from the other attribute state. The original glVertexAttribPointer interface combined everything. The new interface splits this into three: glVertexAttribFormat sets the format, glVertexAttribBinding sets the binding point, and glBindVertexBuffer sets the associating between binding points and buffers.

None of these are related to the element array binding, which is also stored in the VAO.

1 Like

It is. glBindBuffer(GL_ELEMENT_ARRAY_BUFFER) will change the binding for the VAO you’ve just bound.

If it isn’t “working”, it’s likely because you have another error elsewhere. E.g. when you intialise the EBO, if you bind the buffer without thinking about which VAO is bound you may end up overwriting an existing binding by accident. Or if you try to modify the EBO’s contents without paying attention to VAOs, you may end up modifying the wrong EBO.

In the standard usage pattern, you create a VAO and immediately set its state (attribute data and EBO binding). Thereafter, its state doesn’t change. Drawing a mesh comes down to “bind VAO, issue draw call”. If you have multiple meshes using the same vertex data, you create multiple VAOs. The only case where you’d change the EBO binding after VAO creation is if you’re modifying topology on the fly, which isn’t particularly common.

1 Like

Thanks so much, after understanding this what works was to just swap the AEO and VAO/VBO filling order. I’m breaking my brain to understand how in mixed operation (mix of scenarios 1 and 2) they failed to get a picture combined. I suppose it’s a blessing that it keyed me in to something being wrong, but it really seems like it should have worked for the most part since scenario 1 was dominant. I can’t think of an explanation, even if there’s some rule like an AEO can only be bound to one at a time.

Well it seems to be working in a test case, I still have a ways to go to find all the kinks in this leg of this project. TBH I’m surprised I couldn’t find better sources on this subject with search terms. Hopefully this post will help to improve that. Thanks everyone :heart:

(Edited: I swear without you guys manning the trenches OpenGL might be impossible to figure out. At the end of the day it just demands asking for help at one point or another!)

1 Like

Really? This is described in OpenGL books. For an on-line tutorial (that’s also available as an excellent print book), check out:

For instance, this section I believe describes what you were confused about. Search down to the Vertex Array Objects and Element Buffer Objects sections:

There’s also the very useful OpenGL Wiki which describes this too: Vertex_Array_Object, Index Buffers.

And of course, the OpenGL Spec.

2 Likes

I think I probably used EAO this way with webGL, possibly without realizing it, since I think it comes down to the order of the calls, and I think usually I would bind the index buffer second since indices usually come later in draw call parameter lists. Other than this I’ve used older style OpenGL more in my life, and had not had a reason until now to use the core profile. It may be something people take for granted if they learn it early on in tutorials, I’m experienced with OpenGL so I tend not to read tutorials, and familiar with graphics, so the idea of reading a book on OpenGL would not occur to me. Anyway, because it may taken for granted might explain why there aren’t good search results for the terms. I tend to be bad at finding creative places to look for solutions to problems I don’t realize I have. It may be a mental deficit.