PDA

View Full Version : VBO to VAO



bootstrap
06-09-2009, 03:06 AM
VBO: All our geometry is currently drawn from the bound IBO and VBO with glDraw[Range]Elements(). I never did understand why, but we could not make our code work unless we specified the offsets/types/normalizations of every vertex attribute every time we switched to another IBO/VBO pair. Does anyone understand why? Note: the vertices in all our VBOs are identical (same attributes, same offsets, same types, etc).

VAO: After reading the OpenGL v3.10 specification, I have a few questions about VAOs. The description of VAOs led did not make it clear to me that the VAO remembers and restores the VBO attributes and their offsets/types/normalizations when they are bound. However, table 6.3 and 6.4 seems to clearly state this information is stored with the VAO.

Here are my VAO questions:

#1: Does binding the VAO make all this VBO state active too? And does it also somehow make clear to the GPU whether the indices in the corresponding IBO are 16-bits or 32-bits (I don't see that)?

#2: Why do I not see an IBO or VBO identifier in the VAO state (in tables 6.3 and 6.4)? How can binding the VAO also bind the IBO and VBO if they are not identified in the VAO state?

---------------

context: upgrading cross platform 3D engine from OpenGL210/GLSL120 to OpenGL310/GLSL140 (with 185.85 drivers).

computers: CPU = phenom + 4GB RAM : video = nvidia GTX285 + 2GB VRAM : OS #1 = ubuntu64 v9.04 : OS #2 = winxp64

Xmas
06-09-2009, 04:50 AM
I never did understand why, but we could not make our code work unless we specified the offsets/types/normalizations of every vertex attribute every time we switched to another IBO/VBO pair. Does anyone understand why?
Because GL_ARRAY_BUFFER_BINDING is a latched state. You never bind a buffer for a certain vertex attribute directly, instead you bind a buffer to GL_ARRAY_BUFFER, then call glVertexAttribPointer (or one of the deprecated gl*Pointer functions). Only at that point is GL_ARRAY_BUFFER_BINDING copied to GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING for the specific vertex attribute.

You cannot switch to another buffer by calling glBindBuffer(GL_ARRAY_BUFFER, x) without subsequent calls to gl*Pointer(). GL_ARRAY_BUFFER_BINDING is never used for rendering.


#1: Does binding the VAO make all this VBO state active too? And does it also somehow make clear to the GPU whether the indices in the corresponding IBO are 16-bits or 32-bits (I don't see that)?
Yes, no. The index type remains a parameter of glDrawElements and is not stored in a VAO.


#2: Why do I not see an IBO or VBO identifier in the VAO state (in tables 6.3 and 6.4)? How can binding the VAO also bind the IBO and VBO if they are not identified in the VAO state?
It's right there in table 6.4. GL_ELEMENT_ARRAY_BUFFER_BINDING for the index buffer binding, GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING for each vertex attribute binding.

bootstrap
06-09-2009, 05:03 AM
#2: Why do I not see an IBO or VBO identifier in the VAO state (in tables 6.3 and 6.4)? How can binding the VAO also bind the IBO and VBO if they are not identified in the VAO state?
It's right there in table 6.4. GL_ELEMENT_ARRAY_BUFFER_BINDING for the index buffer binding, GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING for each vertex attribute binding.If that's what GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING means, why are there 16 of them?

Xmas
06-09-2009, 05:16 AM
Because you can use a different buffer for each vertex attribute.

bootstrap
06-09-2009, 05:30 AM
Because you can use a different buffer for each vertex attribute.Wait. So the VAO doesn't know which VBO is attached? It only knows the offsets of the <= 16 attributes in the first vertex of the VBO? I assume you're not saying you can have <= 16 VBOs attached to the VAO, right? After all, a VBO is a VERTEX buffer object, not an ATTRIBUTE buffer object.

This leaves me a bit confused. What does the driver do when we call the glDrawElements() function? How does the driver convert this VAO information into something the GPU understands? I would imagine the GPU needs the 64-bit starting address in *GPU* memory of each attribute, plus the rest of the information in the VAO state. But how does the driver know this if it does not keep track of which VBO is attached?

I'm missing something. Does the VAO know which IBO and VBO are attached to it, or not? If so, how (where is that state)? If not, how can the driver send sufficient information to the GPU when the glDrawElements() is called?

Dark Photon
06-09-2009, 06:11 AM
Because you can use a different buffer for each vertex attribute.Wait. So the VAO doesn't know which VBO is attached? It only knows the offsets of the <= 16 attributes in the first vertex of the VBO?
I think we're in desperate need of a concrete example at this point.

Here are two batches using VAOs and VBOs, supposing 1) you don't need to change any state between the batches except to change a texture, and 2) the VAOs to be used are already built:



glBindTexture
glBindVertexArray
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, ...)
glDrawRangeElements
...
glBindTexture
glBindVertexArray
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, ...)
glDrawRangeElements
...
As you can see, no vertex attribute VBO binds, vertex attribute pointer calls, or vertex attribute enables/disables. All cached in the VAO.

Xmas
06-09-2009, 07:08 AM
I assume you're not saying you can have <= 16 VBOs attached to the VAO, right?
That is indeed exactly what I am saying.


After all, a VBO is a VERTEX buffer object, not an ATTRIBUTE buffer object.
OpenGL only knows a single type of buffer object.

Vertex buffer objects, index buffer objects, pixel buffer objects, uniform buffer objects, or texture buffer objects are just descriptions based on the usage of a particular buffer object. They all are the same type of object: a buffer object. And you can indeed use a single buffer object for all these purposes. I wouldn't recommend it, though.

So yes, it is entirely possible that you use one buffer object for normals, another for vertex positions, a third one for texture coordinates, and so on. Or you can mix those attributes in whatever way you like.

And all the information that is needed for each attribute (buffer binding, size, type, stride, offset, enabled, normalized, and integer flags) plus the binding for a buffer object containing vertex indices (ELEMENT_ARRAY_BUFFER) is stored in a VAO. Thus it's not even necessary to re-bind to GL_ELEMENT_ARRAY_BUFFER, Dark Photon.

bootstrap
06-09-2009, 07:42 AM
I think we're in desperate need of a concrete example at this point.

Here are two batches using VAOs and VBOs, supposing 1) you don't need to change any state between the batches except to change a texture, and 2) the VAOs to be used are already built:



glBindTexture(...)
glBindVertexArray(...)
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, ...)
glDrawRangeElements(...)
...
glBindTexture(...)
glBindVertexArray(...)
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, ...)
glDrawRangeElements(...)
...
As you can see, no vertex attribute VBO binds, vertex attribute pointer calls, or vertex attribute enables/disables. All cached in the VAO.
Sorry, but I'm still missing something here.

When you say "no VBO binds", are you saying there IS no VBO? Are you saying the VAO replaces the VBO... that the VAO is essentially a VBO plus attached attribute information?

If so, please explain the first sentence of the "Vertex Array Objects" section in the OpenGL v3.10 specification, which states:

"The buffer objects that are to be used by the vertex stage of GL are collected together to form a vertex array object. All state related to the definition of data used by the vertex processor is encapsulated in a vertex array object."

When I read the above, I hear them saying "the VBO and any other buffer objects needed by the vertex stage are part of the VAO". It does not say "the VAO ***is*** the buffer object". I am not saying you are wrong. But I am saying that is terribly misleading wording if you are correct.

Also, if the VAO ***is*** a buffer object itself, and is not just a collection of buffer objects (like they say it is above), then why is the VAO not bound with:

glBindBuffer (GL_ARRAY_BUFFER, vaoID); // ???

Instead, according to you *and* the OpenGL v3.10 specification, the VAO is bound not as a buffer object, but as a "vertex array" with:

glBindVertexArray (vaoID);

The above implies to me that the VAO does not contain vertex data, and does not correspond to a buffer object. So I ask again, if we don't bind a VAO, an IBO, ***and*** a VBO, then how does the vertex data get into the VRAM, and how does a given glDrawElements() know where the vertex data is?

-----

Separately, in your example, why must you glBindVertexArray() and glBindBuffer(GL_ELEMENT_ARRAY_BUFFER...) before the second glDrawElements(...) call? Aren't they still bound?

-----

In our current code, we bind an IBO, bind a VBO, call glVertexAttribPointer() 5 times, then call glDrawElements(). Therefore I understand how the GPU knows where to get vertex-indices from (the bound IBO), and how the GPU knows where to get vertex-data from (the bound VBO).

In your code I see how the GPU knows which IBO to fetch from, but I do NOT see how the GPU knows which VBO to fetch from. I'm not saying your code is wrong, I'm just saying I don't see how sufficient information gets to the driver or GPU.

bootstrap
06-09-2009, 08:05 AM
I assume you're not saying you can have <= 16 VBOs attached to the VAO, right?
That is indeed exactly what I am saying.Okay, then how do you attach (say) 5 VBOs to a VAO? Let me guess - you call glVertexAttribPointer() 5 times. But that is not a VBO, that is just a vertex attribute specification. True, this does provide the stride too, so it knows how far to the next instance of the same attribute, but glVertexAttribPointer() does NOT say how many of these attributes follow. It also doesn't transfer these attributes anywhere either. So how do all 5 sets of vertex attributes get into VRAM? And when glDrawElements() gets called, how does the GPU know where those 5 arrays of attributes are?



After all, a VBO is a VERTEX buffer object, not an ATTRIBUTE buffer object.OpenGL only knows a single type of buffer object.

Vertex buffer objects, index buffer objects, pixel buffer objects, uniform buffer objects, or texture buffer objects are just descriptions based on the usage of a particular buffer object. They all are the same type of object: a buffer object. And you can indeed use a single buffer object for all these purposes. I wouldn't recommend it, though.

So yes, it is entirely possible that you use one buffer object for normals, another for vertex positions, a third one for texture coordinates, and so on. Or you can mix those attributes in whatever way you like. And all the information that is needed for each attribute (buffer binding, size, type, stride, offset, enabled, normalized, and integer flags) plus the binding for a buffer object containing vertex indices (ELEMENT_ARRAY_BUFFER) is stored in a VAO. Thus it's not even necessary to re-bind to GL_ELEMENT_ARRAY_BUFFER, Dark Photon. Then what does the setup code look like? Maybe that's what I need to see, to understand how this works?

If I have 5 attributes, do I need to create 5 VBOs? Do I then glBuffer[Sub]Data() the vertex data from CPU memory into these 5 VBOs in VRAM? This is a total disaster, because my vertice are interleaved (per usual practice)! My code would need to call glBufferSubData() once for each attribute in each vertex, which would be thousands to millions of excess function calls!!!!!

What does the code look like to attach 5 VBOs to a VAO?

Yup, I think I need to see what comes first, what comes second, what comes third, etc - including setup code. Until I see how the GPU knows what to do (and where to get the vertex data) when glDrawElements() is called, I am confused. Remember, the data is in VRAM, not in CPU memory.

Xmas
06-09-2009, 08:09 AM
A VAO contains:
- a bind point for a buffer object containing vertex indices
- for each vertex attribute (at least 16):
-- a bind point for a buffer object
-- enable flag
-- integer flag
-- normalize flag
-- size
-- type
-- stride
-- offset

There is always a VAO bound to the context. The default VAO has the name 0 and is created along with the context, similar to textures 0 and framebuffer 0. When you bind a VAO, all the state stored in it will become active. Calls to glVertexAttribPointer, or glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, ...), modify the currently bound VAO.

Xmas
06-09-2009, 08:32 AM
// BEGIN INITIALIZATION
// Define some vertex data
struct Vertex {
GLfloat position[3];
GLfloat texcoord[2];
};
Vertex vertexdata[NUM_VERTS] = { ... };
GLubyte indexdata[NUM_INDICES] = { 0, 1, 2, ... };

// Create and bind a VAO
GLuint vao;
glGenVertexArrays(1, &amp;vao);
glBindVertexArray(vao);

// Create and bind a BO for vertex data
GLuint vbuffer;
glGenBuffers(1, &amp;vbuffer);
glBindBuffer(GL_ARRAY_BUFFER, vbuffer);

// copy data into the buffer object
glBufferData(GL_ARRAY_BUFFER, NUM_VERTS * sizeof(Vertex), vertexdata, GL_STATIC_DRAW);

// set up vertex attributes
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, position));
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, texcoord));

// Create and bind a BO for index data
GLuint ibuffer;
glGenBuffers(1, &amp;ibuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibuffer);

// copy data into the buffer object
glBufferData(GL_ELEMENT_ARRAY_BUFFER, NUM_INDICES * sizeof(GLubyte), indexdata, GL_STATIC_DRAW);

// At this point the VAO is set up with two vertex attributes
// referencing the same buffer object, and another buffer object
// as source for index data. We can now unbind the VAO, go do
// something else, and bind it again later when we want to render
// with it.

glBindVertexArray(0);

// END INITIALIZATION

// BEGIN RENDER LOOP

// This is it. Binding the VAO again restores all buffer
// bindings and attribute settings that were previously set up
glBindVertexArray(vao);
glDrawElements(GL_TRIANGLES, NUM_INDICES, GL_UNSIGNED_BYTE, (void*)0);

// END RENDER LOOP

bootstrap
06-12-2009, 02:11 AM
Xmas, thanks much for your example. I am still a bit fuzzy about the details and implementation of the VAO, but your code got me closer to understanding. A few follow-up questions.

The last two entries in table 6.4 in the OpenGL v3.10 specification appear to be 1 IBO identifier and 16 VBO identifiers. Does this mean up to 16 VBOs can be attached to one VAO, in which case glDrawElements() calls would grab vertex elements from several VBOs simultaneously?

If so, how can this be specified? Would you just repeat the three sections of code you comment with...

// create and bind a BO for vertex data
// copy data into the buffer object
// set up vertex attributes

... as many times as you want VBOs attached to the VAO?

If this is true, then I assume every time an application binds a VAO and binds a VBO, that VBO is appended to the VAO state? If this is the case, how would an application ever selective detach one or more of the VBOs attached to a VAO? It seems like the application would need to destroy the VAO and create a new one. Or is my whole notion just completely misguided?

Thanks for helping me slowly figure out these VAOs.

dletozeun
06-12-2009, 04:45 AM
16 VBO identifiers


If I read correctly there is only one: ARRAY_BUFFER_BINDING

Did you talk about this state: VERTEX_ATTRIB_ARRAY_BUFFER_BINDING

There are 16 identifiers but it is for vertex attributes arrays if I am correct.

I may be wrong here since I never had the opportunity to play with this extension yet. For the ones who did, is this mean that you need one vao for each vbo used (vertex, normals, texture coordinates,...) ? In this case, it may be better to pack all this in one big vbo, in order to bind only one vao per rendering batch, isn't it?

bakura
06-12-2009, 06:05 AM
I thought it was not possible to attach an index buffer to a VAO but in fact it looks like it is the case. That's really cool because in my design in which I have render job, now instead of creating a render job that would contain an index buffer as well as a vertex buffer, it would only contain a VAO ^^.

Xmas
06-12-2009, 06:21 AM
The last two entries in table 6.4 in the OpenGL v3.10 specification appear to be 1 IBO identifier and 16 VBO identifiers. Does this mean up to 16 VBOs can be attached to one VAO, in which case glDrawElements() calls would grab vertex elements from several VBOs simultaneously?

If so, how can this be specified? Would you just repeat the three sections of code you comment with...

// create and bind a BO for vertex data
// copy data into the buffer object
// set up vertex attributes

... as many times as you want VBOs attached to the VAO?
Basically, yes. However you should have a real reason to separate your vertex attributes into several BOs, otherwise keep them in a single BO.


If this is true, then I assume every time an application binds a VAO and binds a VBO, that VBO is appended to the VAO state?
Not quite.

Binding a BO to GL_ARRAY_BUFFER does not modify the bound VAO. Only when you call glVertexAttribPointer(n, ...) is the binding to GL_ARRAY_BUFFER copied over into the binding point of attribute n in the VAO.


glBindBuffer(GL_ARRAY_BUFFER, 1);
glVertexAttribPointer(0, ...); // at this point buffer 1 becomes bound to attrib 0
glVertexAttribPointer(1, ...); // buffer 1 also bound to attrib 1
glBindBuffer(GL_ARRAY_BUFFER, 2); // does NOT modify any binding of a specific attribute
glVertexAttribPointer(1, ...); // buffer 2 is now bound to attrib 1. Attrib 0 still uses buffer 1

Note that the GL_ELEMENT_ARRAY_BUFFER bind point is different and is actually part of the VAO.

bootstrap
06-12-2009, 11:37 AM
So it appears that a VAO does contain the identity of an IBO, but does not contain the identity of any VBO --- BUT --- the VAO does contain some kind of references to the several interleaved "attribute arrays" in a typical VBO (though these "attribute arrays" could in fact be in separate VBOs and/or non-interleaved (say, one attribute array after another in a single VBO).

I hate to think what happens if an application deletes a VBO that contains one (or more) of these attribute arrays! The VAO will apparently have no idea, since it knows nothing whatsoever about VBOs (only those attribute arrays, which aren't really buffers at all, since they can be interleaved). This seems rather strange to me, but maybe I don't understand subtle/obscure aspect of the architecture. On second thought, remove the "maybe".

Brolingstanz
06-12-2009, 12:23 PM
Maybe the strangeness stems from the "latching" business, which took me a bit of getting used to, too.

Alfonse Reinheart
06-12-2009, 12:37 PM
I really can't believe how much disinformation is going on in this thread.

A little about how buffer objects work (GL 2.1 and before):

When you bind a buffer object to the GL_ARRAY_BUFFER, you have not done anything. The buffer that is bound to GL_ARRAY_BUFFER only matters when you call glVertexAttribPointer or similar functions like glVertexPointer, etc.

Upon making such a call, the OpenGL context stores the information provided in the call. So the stride, offset, and other parameters for that vertex attribute are stored. Also, if a non-zero buffer object is bound to GL_ARRAY_BUFFER, it then interprets the pointer argument as an offset into this buffer object. Both the offset and the buffer are stored in the context data for that vertex attribute.

Changing GL_ARRAY_BUFFER doesn't mean anything. What matters is what buffer object is bound when you call glVertexAttribPointer. Only then is the association between a vertex attribute and a buffer object made.

How vertex array objects work (ARB_Vertex_Array_Object and GL 3.0+):

If a non-zero Vertex Array Object (VAO) is bound to the context at the time of calling glVertexAttribPointer or similar functions, then the vertex attribute data that was previously stated to be stored in the context is instead stored in the VAO currently bound to the context.

It is not complicated at all.


I hate to think what happens if an application deletes a VBO that contains one (or more) of these attribute arrays!

The specification clearly states what should happen:


(2) What happens when a buffer object that is attached to a non-current
VAO is deleted?

RESOLUTION: Nothing (though a reference count may be decremented).
A buffer object that is deleted while attached to a non-current VAO
is treated just like a buffer object bound to another context (or to
a current VAO in another context).

Dark Photon
06-15-2009, 06:12 AM
Note that the GL_ELEMENT_ARRAY_BUFFER bind point is different and is actually part of the VAO.
Interesting. Are you sure about that? In the extension spec (http://www.opengl.org/registry/spec/ARB/vertex_array_object.txt), I don't find mention of an ELEMENT_ARRAY bind point being stored in the VAO. Did I miss it?

skynet
06-15-2009, 06:37 AM
All these speculations, doubts, the reported neglible (if at all) performance advantage, the limited application possibilities and weird usage make me wonder, why VAO has been promoted to core so quickly and un-asked for.

Why hasn't it been availble as extension before to prove its usefulness? FBO has shown that this is a way which works. Now we have something in the core that nobody wanted this way, but will stay for a long time.

Xmas
06-15-2009, 07:40 AM
Interesting. Are you sure about that? In the extension spec (http://www.opengl.org/registry/specs/ARB/vertex_array_object.txt), I don't find mention of an ELEMENT_ARRAY bind point being stored in the VAO. Did I miss it?
(Fixed the link) The extension spec says that a VAO contains all state in tables 6.6, 6.7, and 6.8 of the OpenGL 2.1 spec, minus GL_CLIENT_ACTIVE_TEXTURE. This does include GL_ELEMENT_ARRAY_BUFFER_BINDING.

Curiously, it also includes GL_ARRAY_BUFFER_BINDING, which the 3.1 spec explicitly excludes from VAOs (see table 6.5). So there is a slight difference between the extension and 3.1 core.



All these speculations, doubts, the reported neglible (if at all) performance advantage, the limited application possibilities and weird usage make me wonder, why VAO has been promoted to core so quickly and un-asked for.

Why hasn't it been availble as extension before to prove its usefulness? FBO has shown that this is a way which works. Now we have something in the core that nobody wanted this way, but will stay for a long time.
The speculations and doubts could be reduced a lot if people tried to actually read the spec. VAOs are really not complicated at all, and I don't know what you think is weird about simply taking a bunch of context state and putting it into a separate state object. It's been done before with texture objects.

What makes you think nobody wanted it this way?

Alfonse Reinheart
06-15-2009, 11:16 AM
All these speculations, doubts, the reported neglible (if at all) performance advantage, the limited application possibilities and weird usage make me wonder, why VAO has been promoted to core so quickly and un-asked for.

Nothing you have stated here is true, save perhaps for the performance advantage. Performance is naturally the pervue of the hardware makers. They will optimize VAO when they feel like devoting resources towards doing so.

GL 3.0 is only 9 months old.

Any of the "speculation" and "doubts" you are hearing is due to unfamiliarity with the extension. Its function is explicitly laid down in the specification. VBOs had a similar journey; it took a long time before people understood how binding a buffer object for rendering worked.


Why hasn't it been availble as extension before to prove its usefulness?

VAO was adapted from an Apple extensions. Coincidentally called APPLE_Vertex_Array_Object.

Dark Photon
06-15-2009, 11:51 AM
(Fixed the link) The extension spec says that a VAO contains all state in tables 6.6, 6.7, and 6.8 of the OpenGL 2.1 spec, minus GL_CLIENT_ACTIVE_TEXTURE. This does include GL_ELEMENT_ARRAY_BUFFER_BINDING.
Thanks for the correction. Sorry I missed that.

So (to correct myself) it seems that a minimalist example for how VAOs would be used if you only needed to swap a texture and change vertex array and index array bindings between batches would be:


glBindTexture
glBindVertexArray
glDrawRangeElements
...
glBindTexture
glBindVertexArray
glDrawRangeElements
...

(after the VAOs have been built of course).

...which might explain why I didn't see a perf boost when I tried this before. Similar to how you often don't see a perf boost with VBOs unless everything's a VBO. I'll have to go back and retest that later...

Xmas
06-16-2009, 02:26 AM
You probably shouldn't expect a huge performance boost, as you mostly save a few state-changing calls. Unlike using VBOs, where you actually avoid a copy of all vertex data used in a draw call.

But VAOs (and state objects in general) make working with OpenGL more convenient and less error-prone IMO.

skynet
06-16-2009, 03:40 PM
Nothing you have stated here is true, save perhaps for the performance advantage.

I'll try to explain.
1. The extension spec language for VAO is not very clear. The _main_ purpose is 'hidden' in the one small sentence: "BindVertexArray may also be used to bind an existing vertex array object." There would be less discussion going on here, if the spec was more explicit and elaborate at this point.

2. I see the limitation in application mainly in the fact that it only supports static attribute/index data. There is no way to change a VAO after it has been created (err, first bound). I don't know if constantly creating/destroying VAOs is recommended performance wise (in the sense of streaming geometry from disk and dynamically placing them in larger VBOs). I would have liked to see a separation of vertex attribute format/layout and position inside a VBO.

3. The 'weird usage' refers to the fact that a single function does both, storing and restoring state _depending on how often it gets called_ I would have liked to see two separate functions here. The current method resembles GL_COMPILE_AND_EXECUTE for display lists, which has been discouraged to use for a very long time now. Separation of store/restore would also enable to change a VAO several times.

4. It might be true that VAO had a predecessor as APPLE extension. I don't have access to Macs, so Mac-only extensions are practically non-existent to me. Neither I heard any "success stories" about APPLE_vao in these forums.

I don't criticize the fact that there are efforts to make GL faster and better. I just wonder, how quick and unreflected some extensions went straight into core, while others like EXT_texture_filter_anisotropic seem to starve to death.

Alfonse Reinheart
06-16-2009, 08:27 PM
There would be less discussion going on here, if the spec was more explicit and elaborate at this point.

Specifications are meant for those persons implementing the specification. They are not meant for users of the specification.

While specifications are the only form of real documentation that OpenGL users get, this is not the intended situation.


There is no way to change a VAO after it has been created (err, first bound).

There is no such thing as an immutable OpenGL object. You may modify a VAO as much as you wish.


The 'weird usage' refers to the fact that a single function does both, storing and restoring state _depending on how often it gets called_

Your statement suggests that you do not know how VAOs work. Or that you do not understand how OpenGL objects in general work.

Every OpenGL object is defined in terms of a set of context state data. Binding an object is the equivalent of copying the state stored in the object to the context. This is true for every OpenGL object and every binding action.

The behavior of buffer objects and VAO are not dependent on how often either one gets called.


It might be true that VAO had a predecessor as APPLE extension.

It is true; your awareness of its existence being irrelevant to that truth. The VAO specification was even designed to use the same enumerators, so that there would be compatibility at some level between the two.


Neither I heard any "success stories" about APPLE_vao in these forums.

APPLE_Vertex_Array_Object is 7 years old. That you have neither heard of it nor heard of any "success stories" is again irrelevant.


I just wonder, how quick and unreflected some extensions went straight into core, while others like EXT_texture_filter_anisotropic seem to starve to death.

I have never understood this issue with what happens to be core vs. what is an extension. EXT_texture_filter_anisotropic is more widely implemented than GL 3.0. For practical purposes, you can assume it exists. For comparison, you have to ask for GL 3.0.

A feature being an extension does not mean it is not widely supported, used, or tested. Similarly, a feature being core does not mean that implementations of it are mature or as fast as they could be. VAO being the best example of this.

skynet
06-17-2009, 12:45 AM
While specifications are the only form of real documentation that OpenGL users get, this is not the intended situation.
Intended or not, most of the time it is the only source of information.

Your statement suggests that you do not know how VAOs work.
Indeed. I just realized minutes ago :-) Thanks!

A feature being an extension does not mean it is not widely supported, used, or tested. Similarly, a feature being core does not mean that implementations of it are mature or as fast as they could be. VAO being the best example of this.
In the strict sense, extensions are optional. You can leave them out and still have a compliant implementation. Why do you think that core features do not need maturity? IMHO maturity and usefulness should be the number one reasons why a feature is _promoted to core_ at all.

Xmas
06-17-2009, 06:23 AM
Do you think VAOs are not mature or not useful, and if so, why?

skynet
06-17-2009, 04:06 PM
The comment on maturity was not especially directed at VAO.
A good example of "how its supposed to be" is EXT_fbo. When it came out, the community was asked if it should go to core. The community said no, leave us some time to evaluate. With time, a set of new extensions came out, enhancing FBOs and lifting many restrictions. FBOs matured and culminated into ARB_fbo, which is now in core.
The whole process probably should have taken 2 years less, of course ;-)

Alfonse Reinheart
06-17-2009, 04:34 PM
The community said no, leave us some time to evaluate.

The community said "no," because EXT_FBO was not finished. There was missing functionality in the extension as written. It did not go into the core because it was not ready to be core.

The version that was eventually adopted into the core was much more complete.

V-man
06-17-2009, 08:00 PM
For VAO, the way it was meant to be used is that you need to create a VAO for every object you need to render with one exception. If you have 2 objects who's offsets into the VBO are identical, then 1 VAO for both is enough.
I'm not using VAO for the moment. Is there a performance benefit?

And also, the specification isn't very clear. Are the glEnableClientState part of the VAO or not?

Alfonse Reinheart
06-17-2009, 09:16 PM
Are the glEnableClientState part of the VAO or not?

glEnableClientState sets the state that says whether a non-generic vertex array is active. That state is part of tables 6.6-6.8. Thus, this state is stored in a VAO.

bootstrap
06-18-2009, 09:11 PM
Maybe the strangeness stems from the "latching" business, which took me a bit of getting used to, too.Eh, I'm not sure what this means, so probably this is one bit (or byte) of understanding that might help me understand how OpenGL works more easily. Please explain, or give links or references to what I should read. Thanks.

Xmas
06-19-2009, 02:13 AM
Eh, I'm not sure what this means, so probably this is one bit (or byte) of understanding that might help me understand how OpenGL works more easily. Please explain, or give links or references to what I should read. Thanks.
It means that binding a buffer object to a specific vertex attribute is a two-step process. You don't bind the buffer object directly to the attribute. Instead, you bind it to the context bind point GL_ARRAY_BUFFER, then call glVertexAttribPointer to copy that binding to the vertex attribute bind point.

bertgp
06-19-2009, 09:38 AM
Maybe the strangeness stems from the "latching" business, which took me a bit of getting used to, too.Eh, I'm not sure what this means, so probably this is one bit (or byte) of understanding that might help me understand how OpenGL works more easily. Please explain, or give links or references to what I should read. Thanks.

To complement what Xmas said, I believe the term latched state was chosen because of "latches" in electronic circuit design. Take a look at this (http://en.wikipedia.org/wiki/Latch_(electronics)) for more info.

Alfonse Reinheart
06-19-2009, 11:55 AM
I believe the term latched state was chosen because of "latches" in electronic circuit design.

It was chosen because it was the only way of implementing VBO without making a new set of entrypoints for binding vertex attributes. At the time, there were a lot of vertex attribute binding functions. Also, the ARB felt that it was important that code could easily switch back and forth between VBO and non-VBO functionality.

bootstrap
06-19-2009, 06:07 PM
I'm not sure what this means, so probably this is one bit (or byte) of understanding that might help me better understand how OpenGL works. Please explain, or give links or references to what I should read. Thanks.
It means that binding a buffer object to a specific vertex attribute is a two-step process. You don't bind the buffer object directly to the attribute. Instead, you bind it to the context bind point GL_ARRAY_BUFFER, then call glVertexAttribPointer to copy that binding to the vertex attribute bind point.All this would be easier to understand if the terminology was precise. Perhaps the terminology is precise, but I never found, read and internalized the clear definitions.

What exactly is "bind"? I adopted this term too, without being 100% clear what it means in the context of OpenGL. In my mind, it means we call an OpenGL function that assigns one or more values to an OpenGL structure (that structure being part of what we call "OpenGL state", I believe).

For example, when my code calls glVertexAttribPointer(), I would say this "creates an OpenGL data structure (state) that contains several values (attributeID, datatype, stride, offset, normalization) adds the address of that structure into whichever VBO was most recently made active by glBindBuffer(GL_ARRAY_BUFFER). Presumably the internal VBO structure contains a 16+ element array of attribute pointers which the "attributeID" argument in glVertexAttribPointer() specifies. However, you seem to say calling glVertexAttribPointer() binds the active VBO to the attribute, not binding the attribute to the active VBO as I imagine it. How confusing.

That's how I currently think about the term "bind", but some of what I'm reading makes me wonder if I understand this term correctly... or even whether the term has a fully consistent meaning in the context of OpenGL.

Though many of the answers in this thread are helpful, somehow I still am not fully clear about the full functional relationship of VAOs, VBOs, IBOs, and vertex "attributes". What might be the best way to clear this up for me is to state in pseudocode or word-description of exactly what the glDrawElements() function refers-to and does when it is called. What does it look for first? What in that entities structure/state does it look for, and what does it do with it? Knowing all this should make clear what programs need to do to prepare to call glDrawElements(). This might be the best vehicle to explain the logic behind this wonderful but complex mess (VAO/VBO/IBO/attributes) that makes our programs so fast and convenient, but my mind so confused. Any takers?

Alfonse Reinheart
06-19-2009, 10:46 PM
All this would be easier to understand if the terminology was precise. Perhaps the terminology is precise, but I never found, read and internalized the clear definitions.

The terminology is precise. People on this forum are not.

Binding only ever happens between an object and the OpenGL context. The term for associating one object with with another object is "attachment", not binding. Shader objects are attached to program objects. Texture objects are attached to program objects. Buffer Objects are attached to VAOs.


What might be the best way to clear this up for me is to state in pseudocode or word-description of exactly what the glDrawElements() function refers-to and does when it is called.

Very well.

OpenGL is a state machine. Most OpenGL function calls simply set various pieces of that state.

Objects in OpenGL are defined (in most cases) as a specific subset of the full OpenGL state. For example, here is the OpenGL context state for a texture object:



struct TextureObjectState
{
void *pTextureData;
InternalFormat eFormat;
TextureType eType; //1D, 3D, Cube, etc.
int iWidth;
int iHeight;
int iDepth;
TextureFilter eFilter;
int iMaxAnisotropy;
};

static TextureObjectState *pContextTextureState = new TextureObjectState();


Conceptually, the implementation of glTexture1D would look like this:



void glTextureImage1D()
{
DeleteTextureData(pContextTextureState->pTextureData);
pContextTextureState->pTextureData = AllocateTextureData(iWidth, eFormat);
pContextTextureState->iWidth = iWidth;
pContextTextureState->eFormat = eFormat;
pContextTextureState->eType = GL_TEXTURE_1D;
if(pData)
{
CopyTextureData(pContextTextureState->pTextureData, pData);
}
}


So, if you call "glBindTexture" with a texture object, what you do is this:



void glBindTexture(TextureObjectState *pNewObj)
{
pContextTextureState = pNewObj;
}


This leaves out the fact that you the user don't directly have TextureObjectState objects. You have texture names that map to TextureObjectState objects. If you pass texture name 0, that automatically maps to the default TextureObjectState that was originally stored in pContextTextureState.

It should be obvious that calling any function that modifies texture object state after binding will cause these functions to modify the texture object you provided.

You asked about vertex state. Here's what that looks like:



struct VertexAttributeState
{
bool bIsEnabled = false;
int iSize = 4; //This is the number of elements in each attrib, 1-4.
unsigned int iStride = 0;
VertexAttribType eType = GL_FLOAT;
bool bIsNormalized = false;
bool bIsIntegral = false;
void * pPtrOrBufferObjectOffset = 0;
BufferObject * pBufferObj = 0;
};

struct VertexArrayObjectState
{
BufferObject *pElementArrayBufferObject = NULL;
VertexAttributeState attributes[MAX_VERTEX_ATTRIB];
}

static VertexArrayObjectState *pContextVAOState = new VertexArrayObjectState();
static BufferObject *pCurrentArrayBuffer = NULL;


Coincidentally, VertexArrayObjectState is the sum total of state that gets stored in a Vertex Array Object. You can find the definition of all of these in the state tables section (6.2) of the OpenGL specification versions 3.1. Older versions have more state in them, but still retain this basic structure.

The remaining question is this: how do you set this state?



void glBindBuffer(enum target, uint buffer)
{
BufferObject *pBuffer = ConvNameToBufferObj(buffer);

switch(target)
{
case GL_ARRAY_BUFFER:
pCurrentArrayBuffer = pBuffer;
break;
case GL_ELEMENT_ARRAY_BUFFER:
pContextVAOState->pElementArrayBufferObject = pBuffer;
break;
...
}
}

void glEnableVertexAttribArray(uint index)
{
pContextVAOState->attributes[index].bIsEnabled = true;
}

void glDisableVertexAttribArray(uint index)
{
pContextVAOState->attributes[index].bIsEnabled = false;
}

void glVertexAttribPointer(uint index, int size, enum type, boolean normalized, sizei stride, const void *pointer)
{
VertexAttributeState &amp;currAttrib = pContextVAOState->attributes[index];

bool bIsEnabled = false;
int iSize = 4; //This is the number of elements in each attrib, 1-4.
unsigned int iStride = 0;
VertexAttribType eType = GL_FLOAT;
bool bIsNormalized = false;
bool bIsIntegral = false;
void * pPtrOrBufferObjectOffset = 0;
BufferObject * pBufferObj = 0;

currAttrib.iSize = size;
currAttrib.eType = type;
currAttrib.iStride = stride;
currAttrib.bIsNormalized = normalized;
currAttrib.bIsIntegral = true;
currAttrib.pPtrOrBufferObjectOffset = pointer;
currAttrib.pBufferObj = pCurrentArrayBuffer;
}


This works just like any other state object. Allocated Vertex Array Objects will have their state set by these functions.

The last line of glVertexAttribPointer is the critical association between the Buffer Object and the Vertex Array Object. It uses whatever object is in pCurrentArrayBuffer (part of the OpenGL context). We saw above that this is set by glBindBuffer(GL_ARRAY_BUFFER).

Aside: you can see how stressed and strained the OpenGL object model is when you look at things like FBO and shader/program objects. When you bind an FBO to the context, it actually changes the state vector for the framebuffer. Shader and program objects don't even use the context, except to be rendered. You set other objects directly into them.

glDrawElements is one of those functions that does not change OpenGL state. It is a rendering command. How it renders is dependent on the current state of OpenGL. All you need to do is set up the current OpenGL state to be what you need it to be and call glDrawElements.

Xmas
06-22-2009, 03:36 AM
The terminology is precise. People on this forum are not.

Binding only ever happens between an object and the OpenGL context. The term for associating one object with with another object is "attachment", not binding. Shader objects are attached to program objects. Texture objects are attached to program objects. Buffer Objects are attached to VAOs.
And pre-VAO, buffer objects are bound to the context. That's still reflected in the fact that the get values for the VAO attachment points are called GL_ELEMENT_ARRAY_BUFFER_BINDING and GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING, despite having turned into attachment points.

tamlin
06-28-2009, 10:31 AM
"That's still reflected in the fact" ... "despite having turned into attachment points."

Alfonse has already written words to enlighten mortals, illuminate complexity, and display how much we all really want a different paradigm (whether we yet know it or not), why I can but copy them:

"you can see how stressed and strained the OpenGL object model is"...

Indeed parts in the API, and the model, are overloaded from historical meanings - often in attempts to not increase the number of identifiers or perceived complexity even further.

<mode=rant?>
Judging by 3.0, I can't say I'm too optimistic about the future. But still, I hope for the day when 1.x/2.x is but an open source layer you can d/l and compile should you want it - using a completely new (modularized) API - an OpenGL TNG.

Where at least software people from competing companies were allowed to work together, and openly, to design the best damn 3D API we could. No NDA's, no bullshit, just interface designers/architects/hackers/<whatever_you_like> doing what they do best.

With version nine MS actually managed to create something both useful and reasonably long-lived (for MS) - D3D9. The ARB with its many competent members should be able to create something better, if it managed to make its members collaborate. Not only because such a group would be greater than the sum of its parts, but to take the future of their lives in their own hands - or are the gfx vendors of today happy to be in the hands of a single software vendor from Seattle?

Sorry this became way off-topic.
</mode>

Alfonse Reinheart
06-28-2009, 02:35 PM
Indeed parts in the API, and the model, are overloaded from historical meanings - often in attempts to not increase the number of identifiers or perceived complexity even further.

You have the effects right, but your reason for them is wrong.

VBOs are a perfect example. The ARB wanted transitioning to VBOs to be easy. Thus they implemented it in a way that required as few changes to existing code as possible. VAO are done similarly.

The problem is the ARB's continued insistence on this kind of backwards compatibility. Of reusing old APIs regardless of how convoluted the semantics get.

Panajev
12-17-2010, 07:16 AM
Thank you Xmas, you post helped me greatly to get VAO's set-up... and the VAO-less iOS 3.2 path was really simple to get once the app was configured to use VAO's (moved to an interleaved vertex format too) :)!

VAO, VBO, and IBO set-up:

//PRAGMA MARK: VAO and VBO creation and set-up...
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0);
{
if(iOS4Path) {
glBindVertexArrayOES(0);
glGenVertexArraysOES(1, &amp;pageTrianglesFrontVAO);
glBindVertexArrayOES(pageTrianglesFrontVAO);
}
glGenBuffers(1, &amp;pageTrianglesVBO);
glBindBuffer(GL_ARRAY_BUFFER, pageTrianglesVBO);
glBufferData(GL_ARRAY_BUFFER, numVertices_ * sizeof(VertexBufferElement2D), inputMesh_, GL_STATIC_DRAW);
glVertexAttribPointer(ATTRIB_VERTEX, 2, GL_FLOAT, GL_FALSE, sizeof(VertexBufferElement2D), (void*)offsetof(VertexBufferElement2D, position));
glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_FLOAT, GL_FALSE, sizeof(VertexBufferElement2D), (void*)offsetof(VertexBufferElement2D, texcoord));

glGenBuffers(1, &amp;pageTrianglesFrontIBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, pageTrianglesFrontIBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, stripLength_*sizeof(GLushort), frontStrip_, GL_STATIC_DRAW);

// At this point the VAO is set up with two vertex attributes
// referencing the same buffer object, and another buffer object
// as source for index data. We can now unbind the VAO, go do
// something else, and bind it again later when we want to render
// with it.

if(iOS4Path) {
glGenVertexArraysOES(1, &amp;pageTrianglesBackVAO);
glBindVertexArrayOES(pageTrianglesBackVAO);
}
glBindBuffer(GL_ARRAY_BUFFER, pageTrianglesVBO);
glVertexAttribPointer(ATTRIB_VERTEX, 2, GL_FLOAT, GL_FALSE, sizeof(VertexBufferElement2D), (void*)offsetof(VertexBufferElement2D, position));
glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_FLOAT, GL_FALSE, sizeof(VertexBufferElement2D), (void*)offsetof(VertexBufferElement2D, texcoord));

glGenBuffers(1, &amp;pageTrianglesBackIBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, pageTrianglesBackIBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, stripLength_*sizeof(GLushort), backStrip_, GL_STATIC_DRAW);

if(iOS4Path) glBindVertexArrayOES(0);
}
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0);

that code, coming after the mesh was created, deals with a co-planar mesh (used to render a page with two sides).

Rendering objects:

glUniform1i(u_frontSideLoc, YES);
[objDataGL validateProgramDebug]; //only used in Debug mode
{
if (iOS4Path) {
CMLog(@"iOS 4.2 path...");
glBindVertexArrayOES(0);
glBindVertexArrayOES([obj pageTrianglesFrontVAO]);
GL_ENABLE_VERTEX_ATTRIBUTES();
glBindTexture(GL_TEXTURE_2D, objDataGL.textureId[0]);
glDrawElements(GL_TRIANGLE_STRIP, stripLength, GL_UNSIGNED_SHORT, (void*)0);
GL_DISABLE_VERTEX_ATTRIBUTES();

glBindVertexArrayOES([obj pageTrianglesBackVAO]);
GL_ENABLE_VERTEX_ATTRIBUTES();
glBindTexture(GL_TEXTURE_2D, objDataGL.textureId[1]);
glUniform1i(u_frontSideLoc, NO);
glDrawElements(GL_TRIANGLE_STRIP, stripLength, GL_UNSIGNED_SHORT, (void*)0);
GL_DISABLE_VERTEX_ATTRIBUTES();
glBindVertexArrayOES(0);
}
else {
CMLog(@"iOS 3.2 path...");
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

glBindBuffer(GL_ARRAY_BUFFER, [obj pageTrianglesVBO]);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, [obj pageTrianglesFrontIBO]);
GL_ENABLE_VERTEX_ATTRIBUTES();
glBindTexture(GL_TEXTURE_2D, objDataGL.textureId[0]);
glDrawElements(GL_TRIANGLE_STRIP, stripLength, GL_UNSIGNED_SHORT, (void*)0);
GL_DISABLE_VERTEX_ATTRIBUTES();

glBindBuffer(GL_ARRAY_BUFFER, [obj pageTrianglesVBO]);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, [obj pageTrianglesBackIBO]);
GL_ENABLE_VERTEX_ATTRIBUTES();
glUniform1i(u_frontSideLoc, NO);
glBindTexture(GL_TEXTURE_2D, objDataGL.textureId[1]);
glDrawElements(GL_TRIANGLE_STRIP, stripLength, GL_UNSIGNED_SHORT, (void*)0);
GL_DISABLE_VERTEX_ATTRIBUTES();

glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
}