PDA

View Full Version : VAO problem



ugluk
12-04-2009, 02:18 AM
I'd like to store both vertex array data and vertex array indices into the same VBO, but apparently it's impossible:

"When a buffer object is bound to a target, the previous binding for that target is automatically broken."

So I can't use the same VBO for both vertex array data and indices? Maybe there is a way?

Also, buffer object 0 is not allowed anymore in OpenGL 3.2, which means I am not able to feed GL from client memory anymore. What if one runs out of server memory? Tough luck?

ZbuffeR
12-04-2009, 03:26 AM
Array data and array indices are different targets, so it should work.

ugluk
12-04-2009, 06:05 AM
It doesnt, maybe you know of a compilable and working tutorial program? GL wiki includes ones, but with source code omitted.

ugluk
12-04-2009, 08:05 AM
You gave me wrong info, I tried and it seems each glBufferData() call overwrites old buffer data.

Now please, does OpenGL 3.2 really disable rendering from system memory (since buffer 0 disabled)?

Dark Photon
12-04-2009, 09:15 AM
You gave me wrong info, I tried and it seems each glBufferData() call overwrites old buffer data.
I didn't see anybody say that. I think you misunderstood.


Now please, does OpenGL 3.2 really disable rendering from system memory (since buffer 0 disabled)?
Not if you use the compatibility profile. Then it all works as before, assuming your vendor/driver offers the compatibility profile (NVidia does).

Dark Photon
12-04-2009, 09:26 AM
Also, buffer object 0 is not allowed anymore in OpenGL 3.2, which means I am not able to feed GL from client memory anymore. What if one runs out of server memory? Tough luck?
Kind of a grey area, isn't it?

I don't know for sure, but GL drivers probably handle VBOs like they do textures and keep "backups" in CPU memory managed by the driver. When it needs a texture/VBO that's not on the GPU, it likely has to free up some space and DMA it to GPU mem. When your working set gets too large for GPU memory (with textures), you start breaking frame and performance suffers miserably.

Aleksandar
12-04-2009, 10:38 AM
I think that it is possible to mix-up vertices and indices, but it is totally meaningless. Why would you do that? The only way that mixture can work is to store indices at the beginning or at the end of the buffer, because you cannon define stride in the index-array. But why would you do that? There are many disadvantages of that approach.

Brolingstanz
12-04-2009, 11:41 AM
What does any of this have to do with VAOs?

:puzzled:

Alfonse Reinheart
12-04-2009, 11:57 AM
So I can't use the same VBO for both vertex array data and indices?

You generally shouldn't use the same buffer object for both array and index data. This was an old limitation from NVIDIA way back when VBO first came out. I don't know if it is still legitimate today, but it's probably a good idea to still do so.

However, you can use the same buffer object if you want to. But before I can explain how, you need to understand what happens when you build a VAO. First, read this (http://www.opengl.org/wiki/Vertex_Array_Objects). Yes, I know it doesn't have code examples; it's not a tutorial. It's an explanation of what happens when you use buffer objects.

Specifically, pay attention to the bottom section. The use of glVertexAttribPointer (or any similar function, like glNormalPointer, etc) causes the buffer object currently attached to the GL_ARRAY_BINDING binding to become associated with the VAO. This means that, after this call, you can bind any other buffer object to that binding point without changing a thing.

If you want to use the same buffer object for vertex arrays and indices, do this:

1: Create your VAO and bind it.
2: Bind your buffer to GL_ARRAY_BINDING.
3: Make all of the gl*Pointer calls to associate this buffer with the vertex arrays.
4: Unbind the buffer from GL_ARRAY_BINDING.
5: Bind the buffer to GL_ELEMENT_ARRAY_BINDING.

You're done. You can unbind the VAO now, then bind it later whenever you want to render with it.

ugluk
12-05-2009, 02:00 AM
This info is more helpful, but on my GeForce 9300M, glBufferData() overwrites vertex data with indices if invoked on the same buffer object twice. Whatever was filled in last stays in even though I bind buffers correctly.

BTW: This is not a VAO issue per se, but more of a buffer issue. In my program I've used a VAO to store state and thought at first the problem was somehow VAO-related.

Why would using the same buffer for both vertices and indices be inefficient/wrong?

glBindBuffer(GL_ARRAY_BUFFER, buffer_object);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer_object);

This goes just fine, no errors

glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size(), &indices.front(),
GL_STATIC_DRAW);
glBufferData(GL_ARRAY_BUFFER, vertices.size(), &vertices.front(),
GL_STATIC_DRAW);

No errors here too, but second call will overwrite indices with vertices (I checked by reversing the order of glBufferData() calls).

Using 2 buffers solved the problems, but is this a NVIDIA bug or am I doing something wrong?

skynet
12-05-2009, 07:46 AM
Why would using the same buffer for both vertices and indices be inefficient/wrong?

It has been inefficient in times of GF1/2, when indices had to be stored in system memory. But today, its all the same memory anyway. I'm doing it without problems.
The _only_ pitfall I experienced with this approach (on an ATI card), was when I accidently placed vertex attribute data directly behind unsigned short indices. The vertex data became unaligned and it ended up in slow, broken rendering. So be aware to align your (float-)vertex data to at least 4 bytes.


Using 2 buffers solved the problems, but is this a NVIDIA bug or am I doing something wrong?
Its your fault. Think about it, you bound the same VBO to both
binding points. This means through both binding points you speak to the same block of memory - and the last glBufferData "wins"!

Alfonse Reinheart
12-05-2009, 01:25 PM
but on my GeForce 9300M, glBufferData() overwrites vertex data with indices if invoked on the same buffer object twice. Whatever was filled in last stays in even though I bind buffers correctly.

Of course it does. It's a buffer object.

Buffer object memory is not broken into parts based on attachment points. It's just an array of memory, just like you would get if you called malloc or new. Calling glBufferData is like calling malloc.

Here is a condensed version of what you're doing in GL terms.



GLuint SetupMyVertexAndIndexData(...)
{
GLuint buffer = 0;
glGenBuffers(1, &buffer);
glBindBuffer(GL_ARRAY_BINDING, buffer);
glBufferData(GL_ARRAY_BINDING, numVertices * vertexSize, <vertex data>);
glBindBuffer(GL_ELEMENT_ARRAY_BINDING, buffer);
glBufferData(GL_ELEMENT_ARRAY_BINDING, numIndices * indexSize, <index data>);
return buffer;
}


And here's the equivalent in C:



BufferObject *vertexBuffer = NULL;
BufferObject *indexBuffer = NULL;

BufferObject SetupMyVertexAndIndexData(...)
{
BufferObject myBuffer;
vertexBuffer = &amp;myBuffer;
vertexBuffer->data = malloc(numVertices * vertexSize);
//Fill vertexBuffer->data with vertex data.
vertexBuffer = NULL;
indexBuffer = &amp;myBuffer;
free(indexBuffer->data);
indexBuffer->data = malloc(numIndices * indexSize);
//Fill indexBuffer->data with index data.
return myBuffer;
}


Do you see the problem?

If you want to put vertex and index data in the same buffer object, they have to go into different parts of the buffer object:



GLuint SetupMyVertexAndIndexData(...)
{
GLuint buffer = 0;
glGenBuffers(1, &amp;buffer);
glBindBuffer(GL_ARRAY_BINDING, buffer);
glBufferData(GL_ARRAY_BINDING, (numVertices * vertexSize) + (numIndices * indexSize), NULL);
glBufferSubData(GL_ARRAY_BINDING, (numVertices * vertexSize), 0, <vertex data>);
glBufferSubData(GL_ARRAY_BINDING, (numIndices * indexSize), (numVertices * vertexSize), <index data>);
return buffer;
}


Whenever you want to use the index data, in a glDrawElements call, you must provide an offset of (numVertices * vertexSize).

Binding a buffer to a different binding point does not change the contents of the buffer. It does not cause the buffer to have both a "vertex portion" and an "index portion". The buffer is always a single 1-dimensional array of bytes.

So if you want two things in a single buffer object, you must put them in different places within that 1D array of bytes.

ugluk
12-06-2009, 12:53 AM
Thanks for your experienced answers. Now everything works, I did it like this, so the VAO would remember the correct buffer to use for indices:



GLuint buffer = 0;
glGenBuffers(1, &amp;buffer);
glBindBuffer(GL_ARRAY_BINDING, buffer);
glBindBuffer(GL_ELEMENT_ARRAY_BINDING, buffer);

glBufferData(GL_ARRAY_BINDING, (numVertices * vertexSize) + (numIndices * indexSize), <vertex data>, GL_STATIC_DRAW);
glBufferSubData(GL_ELEMENT_ARRAY_BINDING, (numVertices * vertexSize), (numIndices * indexSize), <index data>);
return buffer;


Still, I am worried. How do I know what particular requirements a particular GL has about data alignment? Maybe indices also need a particular alignment, not just vertices? I suppose, it's less of a problem, since most vertices are either floats or doubles... I wanted to use a single buffer, because it made sense to me for reasons skynet gave: vertices and indices use the same memory these days.

Currently I bunch all vertex attributes together at the start of the buffer, placing indices to them behind.

Also, a wild idea (I'm not taking it seriously), but: is it possible to put textures along with the vertices and indices? I suppose here alignment is even more important. But if vertices come first and are floats, followed by 4 byte ints, textures could also fit in, but maybe only a single texture...

The one buffer for one thing approach looks more robust.

Alfonse Reinheart
12-06-2009, 01:39 AM
glBufferData(GL_ARRAY_BINDING, (numVertices * vertexSize) + (numIndices * indexSize), <vertex data>, GL_STATIC_DRAW);

Is your <vertex data> array actually (numVertices * vertexSize) + (numIndices * indexSize) in size? I highly doubt it. If it isn't, then you're causing reads from memory you don't own. This is not a good thing; it can cause your program to crash. Just use the code as I gave it, where you pass NULL and do two BufferSubData calls.


How do I know what particular requirements a particular GL has about data alignment?

You don't. Basically, all you can do is follow a few rules of thumb and profile where possible.


is it possible to put textures along with the vertices and indices?

With one exception, buffer objects do not store texture data; texture objects do. And in the case of texture buffer objects, it's better to use a specific buffer for them, where appropriate. Your usage pattern for texture buffers tends to be very different from your usage pattern for vertex data. Texture buffers tend to be streamed buffers, so STATIC_DRAW is not appropriate.

ugluk
12-06-2009, 08:00 AM
Well, it did work. Apparently, linux is more forgiving than windows. I've fixed my code since, as you suggested.