How does VAO keep buffer bindings? - opengl

I am struggling to understand how exactly VAO is handling buffer mapping.
What I'm doing could be described in this pseudocode:
SetUp:
BindVAO
BindArrayBuffer
glBufferData(GL_ARRAY_BUFFER, ExpectedMaxCount, NULL, GL_DYNAMIC_DRAW);//Allocate storage
glEnableVertexAttribArray
glVertexAttribPointer
BindElementBuffer
Allocate storage (no data yet)
UnbindVAO
UnbindArrayBuffer
UnbindElementBuffer
Draw:
SubArrayAndElementDataIfNeeded
BindVAO
DrawElements
Is this correct that when DrawElements is called OpenGL uses bound VAO to resolve array and element buffer bindings? After a Draw call the bound array buffer is 0, but element buffer is still the one that was used to Draw.
Is it mandatory to allocate buffer memory during VAO setup? Would VAO be invalidated if BufferData was called after setup?

I am struggling to understand how exactly VAO is handling buffer mapping.
Be very careful when using the word "mapping" around "buffers"; that has a specific meaning when dealing with buffer objects, one that you probably don't intend.
Is this correct that when DrawElements is called OpenGL uses bound VAO to resolve array and element buffer bindings? After a Draw call the bound array buffer is 0, but element buffer is still the one that was used to Draw.
One has nothing to do with the other. A Vertex Array Object, as the name implies, contains all of the state that is necessary to pull vertex data from arrays. When you bind one, all of that state comes back into the context.
The reason the "bound array buffer" is 0 after the call is because it was 0 before the call. Draw calls do not change OpenGL state.
Furthermore, you seem to have fallen for the GL_ARRAY_BUFFER trap. The GL_ARRAY_BUFFER binding only matters to three functions: glVertexAttribPointer, glVertexAttribIPointer, and glVertexAttribLPointer (see a pattern?). These are the only functions that look at that binding. What they do is take the buffer that is bound at the time these functions are called and associates that buffer with the current VAO. GL_ARRAY_BUFFER is not part of the VAO's state. A buffer object becomes associated with a VAO only when you call one of those three functions. Once you make that call, you can bind whatever you want to GL_ARRAY_BUFFER.
GL_ELEMENT_ARRAY_BUFFER is part of the VAO's state.
Is it mandatory to allocate buffer memory during VAO setup? Would VAO be invalidated if BufferData was called after setup?
Technically no, but it's good form to not use a buffer until it has storage. Especially if they're static buffers.

Related

Is there any difference in OpenGL between creating Index Buffers with GL_ARRAY_BUFFER and creating them with GL_ELEMENT_ARRAY_BUFFER?

I've been delving into OpenGL a while, and now, working on a project, I found that, when I'm creating an index buffer, if I bind it to a GL_ARRAY_BUFFER instead to a GL_ELEMENT_ARRAY_BUFFER has (apparently) the same result.
I mean, the vertex buffers are always bound to GL_ARRAY_BUFFER, but if I create an index buffers like this:
glCreateBuffers(1, &m_BufferID);
glBindBuffer(GL_ARRAY_BUFFER, m_BufferID);
glBufferData(GL_ARRAY_BUFFER, count * sizeof(uint), vertices, GL_STATIC_DRAW);
And then, when drawing geometry for instance, I bind it to a GL_ELEMENT_ARRAY_BUFFER, works just fine, and I don't know why I thought that index buffers had to be created with GL_ELEMENT_ARRAY_BUFFER too, but... Is there any "inner" difference actually?
In terms of the nature of the buffer object itself? No. All buffer objects are the same and can be used for any task appropriate for a buffer object. A buffer object does not take on special properties by which binding it was initially used with.
However, the GL_ELEMENT_ARRAY_BUFFER binding point is itself a bit unusual. It's not part of global context state; it's part of VAO state. So if you have no VAO bound (under a core profile context), then you can't bind anything to that binding point. And when you bind to that binding point, you are affecting the state of the currently bound VAO. And if you change the currently bound VAO, you will be changing which buffer is bound to the element array binding point.
So generally, you should only bind to that point if what you intend to do is attach the buffer to the currently bound VAO.
Yes it ist. While the ARRAY_BUFFER binding is a global state, the ELEMENT_ARRAY_BUFFER binding is stated in the Vertex Array Object. See Index buffers.
Hence, glBindBuffer(GL_ELEMENT_ARRAY_BUFFER m_BufferID) changes a state in the currently bound Vertex Array Object.
Note that compared to the index buffer (ELEMENT_ARRAY_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. This reference is stored when glVertexAttribPointer is called. Then the buffer which is currently bound to the target ARRAY_BUFFER, is associated to the specified attribute index and the name (value) of the object is stored in the state vector of the currently bound VAO.
However the index buffer is a state of the VAO. If a buffer is bound to the target ELEMENT_ARRAY_BUFFER, this buffer is assigned to the currently bound Vertex Array Object.

Why must you use both glBindBuffer and glBindBufferRange to create a uniform buffer in OpenGL?

To create a uniform buffer object in OpenGL, why must I call both glBindBuffer and glBindBufferRange? According to documentation,
Think of glBindBufferRange as binding the buffer to two places: the particular index and the target​. glBindBuffer only binds to the target​, not the index.
So then glBindBuffer seems superfluous to me if you are already binding a buffer to that same target using glBindBufferRange.
I am reading Learning Modern 3D Graphics Programming and chapter 7 shows how to make uniform buffer objects. The code used in the book:
glGenBuffers(1, &g_GlobalMatricesUBO);
glBindBuffer(GL_UNIFORM_BUFFER, g_GlobalMatricesUBO);
glBufferData(GL_UNIFORM_BUFFER, sizeof(glm::mat4) * 2, NULL, GL_STREAM_DRAW);
glBindBuffer(GL_UNIFORM_BUFFER, 0);
glBindBufferRange(GL_UNIFORM_BUFFER, g_iGlobalMatricesBindingIndex, g_GlobalMatricesUBO, 0, sizeof(glm::mat4) * 2);
This code works until I comment out the call to glBufferData, in which case I get a black screen. This is surprising to me because afterwards we are binding g_GlobalMatricesUBO to the GL_UNIFORM_BUFFER binding target anyways. The documentation linked to earlier also says:
Do note that this does not replace standard buffer binding with glBindBuffer. It does in fact bind the buffer to the target​ parameter, thus unbinding whatever was bound to that target. But usually when you use glBindBufferRange, you are serious about wanting to use the buffer rather than just modify it.
Which seems to touch on what I'm confused about, but I do not understand this explanation. It says it unbinds whatever was previously bound to the target, which seems to reinforce my (wrong) idea that binding a buffer to the target beforehand is unnecessary.
glBufferData does not only initialize the buffer, it creates the buffer object's data store with the specified size. You can think about it like memory allocation in c++.
glBufferData creates a data store for the buffer object which is currently bound to the specified target. In this case it creates a data store for the named buffer object g_GlobalMatricesUBO, because it is bound to the specified target GL_UNIFORM_BUFFER.
glBindBufferRange doesn't create any data store, it use the data store which has to exist. The buffer object with the data store is specified by the 3rd parameter.

VAO and GL_ELEMENT_ARRAY_BUFFER association

I have a problem understanding association between VAO and buffer bound to GL_ELEMENT_ARRAY_BUFFER (let's call it EBO). I got to know that it is part of VAO state, but differ from buffers used along with glVertexAttribPointer calls (those buffers are simply remembered by VAO as attributes storage omitting need to bind them - if I understood correctly).
In this discussion it is claimed:
Unlike GL_ARRAY_BUFFER, a VAO store the current binding for GL_ELEMENT_ARRAY_BUFFER. Calling glBindBuffer(GL_ELEMENT_ARRAY_BUFFER) stores a reference to the specified buffer in the currently-bound VAO. glDrawElements() etc take the vertex indices from the buffer stored in the currently-bound VAO. So switching between VAOs switches between element arrays.
But do I need to call glBindBuffer(GL_ELEMENT_ARRAY_BUFFER) between bind and unbind of VAO to make sure it will 'save' it in its state or VAO will just take currently bound EBO when VAO gets created?
Also the answer quoted above isn't clear to me whether VAO just 'knows' (as attribute storage buffers) which buffer indices (glDrawElements) have to be taken from or does VAO binds proper EBO when VAO gets bound?
edit: First of my questions has been answered here, however I believe the second one was answered by #Rabbid76.
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 commandvoid 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
If you set things up correctly, at draw time you should only be binding VAOs. That is sort of the whole point, avoid the CPU overhead of calling into the GL driver repeatedly for each piece of geometry.
During your initial setup you should create and bind a VAO, then bind all attribute buffers and describe them with glVertexAttribPointer, enable them with glEnableVertexAttribArray and lastly bind the GL_ELEMENT_ARRAY_BUFFER. After that, I would bind the VAO to null so you don't accidentally bind something else in the following lines.
(Note that the glBindBuffer of the attribute buffer itself is not directly recorded into the VAO, but rather inferred at the call site of glVertexAttribPointer. That is, whatever is currently bound to GL_ARRAY_BUFFER when you call glVertexAttribPointer will be what the VAO sources for vertex pulling.)
During your render loop you should bind the different VAOs you created earlier.
It's a little bit confusing because in practice sometimes binding a VAO means "I want to start recording a little macro" (at initial setup time) and sometimes binding a VAO means "I want you to play back all the bindings in that macro I recorded earlier" (at render time). It is described in the original RFC as:
The currently bound vertex array object is used for all commands
which modify vertex array state, such as VertexAttribPointer and
EnableVertexAttribArray; all commands which draw from vertex arrays,
such as DrawArrays and DrawElements; and all queries of vertex
array state.
https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_vertex_array_object.txt
See also tables 6.4 and 6.5 in the GL standard to see exactly what fields are stored in the VAO struct:
https://www.khronos.org/registry/OpenGL/specs/gl/glspec32.core.pdf

OpenGL - Vertex Array inside a VBO and Index & Texture Array outside?

Is it possible to place a set of Vertices into a VBO, but take the Index and Texture coord. Arrays from regular memory? If yes, which syntax to use?
Yes, it is possible to do this. But you shouldn't.
The reason to use buffer objects is to improve performance. Doing what you suggest simply reduces the performance you would have gained by properly using buffer objects.
Also, it's a driver path that most drivers don't see very often. Either people use buffer objects for vertex data, or they use client memory arrays. Since it's a road less traveled, you're more likely to encounter driver bugs.
The syntax is just the regular syntax. The gl*Pointer calls use buffer objects or not based on whether a buffer object is bound to GL_ARRAY_BUFFER at the time the gl*Pointer call is made. As such, you can bind a buffer to GL_ARRAY_BUFFER, make a gl*Pointer call with an offset, then bind 0 to GL_ARRAY_BUFFER and make a gl*Pointer call with an actual pointer.
Similarly, the glDraw*Elements* calls use a buffer object if a buffer is bound to GL_ELEMENT_ARRAY_BUFFER. So if you want to use client memory for these functions, bind 0 to that.

OpenGL vertex buffer confusion

Would someone care to explain the difference to be between a VertexBuffer, a VertexArray, a VertexBufferObject, and a VertexArrayObject? I'm not even sure if these are all terms for different things, but I've seen all of them appear in the OpenGL spec.
I know that a VertexBuffer simply contains vertices and nothing else, once bound, and once I've set the vertex pointers, I can use DrawArrays to draw it. I've done it this way many times.
I am using what I think is a VertexArray, which stores the state of any vertex buffers that are set, and also any vertex pointers. Binding a VertexArray automatically binds the vertex buffer and sets the vertex pointers. I have used this (mostly) successfully too.
But what is a VertexBufferObject, and a VertexArrayObject? Are they better? Doesn't VertexArray give me everything I need?
A vertex array is simply some data in your program (inside your address space) that you tell OpenGL about by providing a pointer to it.
While more efficient than specifying every single vertex individually, they still have performance issues. The GL must make a copy at the time you call DrawElements (or a similar function), because that is the only time it can be certain that the data is valid (after all, nothing prevents you from overwriting the data right away). This means that there is a significant hindrance to parallelism, and thus a performance issue.
Vertex buffer objects ("vertex buffers") are raw blocks of data that you do not own, i.e. they are not in your address space. You can either copy data into the buffer object with Copy(Sub)Data or by temporarily mapping it to your address space. Once you unmap the buffer, it does no longer belong to you. The huge advantage is that now the GL can decide what to do with it, and when to upload it. It knows that the data will be valid, because you cannot access it. This makes CPU/GPU parallelism a lot easier.
Vertex array abjects are a bit of a misnomer. There are no vertices or arrays in them. They are merely a kind of "state description block" which encapsulate the bindings of one or several vertex buffer objects (including any VertexAttribPointer calls). As such, they are both a convenience function and somewhat more efficient (fewer function calls), but not strictly necessary. You could do anything that a VAO does by hand, too.
BufferObject: a GPU allocated memory buffer
Vertex Buffer Object: a BufferObject containing vertices informations (colors, position, custom data used by a shader, ...)
Pixel Buffer Object: a BufferObject containing pixel or texel informations. Mainly used to upload textures.
Element Buffer Object: a BufferObject containing indices (used by glDrawElements).
Vertex Array: memory used by gl*Pointer call. Might be host memory or a Vertex Buffer Object if it is bound using glBindBuffer command with GL_ARRAY_BUFFER.
Element Array: memory used by glDrawElements call. Might be host memory or an Element Buffer Object if it is bound using glBindBuffer command with GL_ELEMENT_ARRAY_BUFFER.