VBO / IBO Woes

Started by
12 comments, last by PaladinOfKaos 13 years, 7 months ago
Hi guys. This is my first post on gamedev in years (literally -- my last 3 posts were in 2007, one post in 2006, then a bunch in 2005), so it's great to be back in 'the game' after going through many many life altering experiences

So, my question today is about opengl's vertex buffer objects. I've read every tutorial on the internet for every different platform that exists, but I couldn't resolve this issue.

I have a custom vertex class which include some (but not much) extra garbage in it, in addition to the VNT data:

class Vertex {  int stuffA;  float pos[3];  float tc[2];  float n[3];  int stuffB;};class Tri {  unsigned short v[3];};


Since there isn't too much extra data in the 'stuffs' and since i am not loading up huge models, i don't mind just dumping all of the data into my VBO:

  Vertex verts[3]; Tri tris[1]; /* initialize both with data.. */  GLuint vertexBuffer = 0;  GLuint indexBuffer = 0;  glGenBuffersARB(1, &vertexBuffer);  glBindBufferARB(GL_ARRAY_BUFFER_ARB, vertexBuffer);  glBufferDataARB(GL_ARRAY_BUFFER_ARB, 3*sizeof(Vertex), verts[0].pos, GL_STATIC_DRAW_ARB);  glGenBuffersARB(1, &indexBuffer);  glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, indexBuffer);  glBufferDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, sizeof(unsigned short)*3, tris[0].v, GL_STATIC_DRAW);


Finally, in my main loop, I render everything. Now, here is the issue -- the structure of my Vertex class may (and most likely will) be changing in the near future. In order to save me much headache, I've been using the offsetof() function defined in stddef.h to get my byte offsets. I assumed this would work, but my project keeps crashing. Now here is there interesting part --- if I move member variable 'stuffA' to the end of the class, the program doesn't crash! What am I doing wrong??

glBindBufferARB(GL_ARRAY_BUFFER, vertexBuffer);glEnableVertexAttribArrayARB(0);glVertexAttribPointerARB(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex,pos) );glEnableVertexAttribArrayARB(1);glVertexAttribPointerARB(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex,n) );glEnableVertexAttribArrayARB(2);glVertexAttribPointerARB(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex,tc) );glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_SHORT, (void*)offsetof(Vertex,pos) );


As always, thank you for any assistance provided ahead of time, and your help is greatly appreciated =)
"a low level aho master like you couldn't kill me even if I let you"
Advertisement
Why are you trying to feed your custom struct into a VBO? You'd be much better off piping your vertex data to tightly packed arrays (at least temporarily).

GLfloat* vertices = new GLfloat[numVertices * 3];


Do you plan on holding all that vertex data in memory the whole time?

Don't push your structs into VBOs. That's just asking for trouble.
Amateurs practice until they do it right.Professionals practice until they never do it wrong.
In the above example, static_draw was used; however in production, stream_draw will be used for the vertices and static_draw for the indicies. Position and normals will be updated, and textures will most likely remain static.

I am aware that the best practice would be to create a tightly packed VBO of (position and normals) and create that using stream_draw, and then create a texture VBO which would be static_draw and create the IBO which would also be static (3 VBO's total).

But for this specific implementation, my primary concern is that I cannot figure out what is causing the code to break and GL to explode =/.
"a low level aho master like you couldn't kill me even if I let you"
Quote:Original post by Uthman
But for this specific implementation, my primary concern is that I cannot figure out what is causing the code to break and GL to explode =/.


But that is my primary concern. I guess I just have a tunnel-vision biased way of approaching VBOs given my background, but looking at your code, I just struggle to see how it ever would work, much less why it is failing specifically.

removed -- I misinterpreted part of the problem.
Amateurs practice until they do it right.Professionals practice until they never do it wrong.
In fact optimial performance for vertex shader comes from interleaving your custom struct into the VBO, as it will be a cache hit for all data members on each shader pass.

If you pack all "positions" into a big array, and normal in another big array and pass them to your VBO, cache will have a miss for each vertex attribute data, thus fetching will be slower, resulting in a worse performance for the VBO.

As for your question, what do you mean by it explodes?
Quote:Original post by kilah
In fact optimial performance for vertex shader comes from interleaving your custom struct into the VBO, as it will be a cache hit for all data members on each shader pass.

If you pack all "positions" into a big array, and normal in another big array and pass them to your VBO, cache will have a miss for each vertex attribute data, thus fetching will be slower, resulting in a worse performance for the VBO.


This might be true for when the data is stored in memory outside the GPU, but are you sure the data stays that way once it is piped over to the GPU itself?

Plus, correctness is more important than speed. Optimization comes later. ;)
Amateurs practice until they do it right.Professionals practice until they never do it wrong.
Sure he needs to get his things done before trying more complex behavior I agree with you there, testing non interleaved data is less error prone than doing specific optimizations.

As for interleaving VBOs and driver behavior: OpenGL drivers have full freedom on how they handle inner data of their VBOs. Yet that doesn't mean that they do optimize on language allowed behaviors.

As for my personal experience, data seems to raw streamed into driver selected memory and then copied to cache as a block, as cache optimizations for memory alignment seems to increase performance of the VBOs a LOT. Looks like drivers take a DMA approach on VBO data mapping, yet I might be wrong.

In short: if language allows interleaving data, driver will not bother to do so, as it is allowed by the language itself.
By explode, I meant the call stack crashes right into opengl assembly code.

I think the above example is simple enough that it should work without causing any errors. There are other examples on the net that have verbatim duplicate code, just without two ints packed into the Vertex structure that work.. I'm unable to see what is causing the code to break

Check out this program on opengl.org for example; the originator has his vertex class padded to 64 bytes using 4 floats.

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

So I've made some progress. The Drawelements call needed to point to the start of my ibo, not to the byte offset of my position vector. That helped stop the program from crashing:

glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_SHORT, (void*)0 );


However, now, when stuffA is in the beginning of the Vertex class, nothing is drawn. When it is at the end of the class, the triangle is drawn as usual.
"a low level aho master like you couldn't kill me even if I let you"
to me what you just mentioned looks like 3 possible errors:

- Wrong element copy into your struct.
- Wrong vertex shader attribute read
- Wrong offset on interleaved data.

Check this 3 possibilities, which I am pretty sure they are the reason of your troubles right now
You were right:
- Wrong element copy into your struct.
- Wrong offset on interleaved data.

These two were correct. The problem was here:
- Wrong vertex shader attribute read

I was mixing GLES1.1 with GLES2.0. I don't have any vertex or fragment shader programs, because this is a gles1.1 application. Instead of using glEnableVertexAttribArrayARB / glVertexAttribPointerARB, I should have been using the deprecated glEnableClientState / glVertexPointer. Once I made the appropriate switch, everything seemed to work, and I could put my class member variables in any order and pack all sorts of ridiculous data in there that shouldn't even be on the gpu to begin with without breaking my program.

Cheers !
"a low level aho master like you couldn't kill me even if I let you"

This topic is closed to new replies.

Advertisement