PDA

View Full Version : OpenGL fast texture drawing with VBOs



Matthew4693434
05-31-2010, 12:45 PM
Hello. I am making a 2D game with OpenGL. I would like to speed up my texture drawing by using VBOs.

Currently I am using the immediate mode. I am generating my own coordinates when I rotate and scale a texture. I also have the functionality of rounding the corners of a texture, using the polygon primitive to draw those.

I was thinking, would it be fastest to make a VBO with vertices for the sides of the texture with no offset included so I can then use glTranslate, glScale and glRotate to move the drawing position for my texture. Then I can use the same VBO with no changes to draw the texture each time. I could only change the VBO when I need to add coordinates for the rounded corners.

Is that the best way to do this? What things should I look out for while doing it? Is it really fastest to use GL_TRIANGLES instead of GL_QUADS in modern graphics cards?

Thank you for any answer.

Also I want the drawing to be as fast as possible while maintaining quality. Is there anything else I can do to improve the speed?

mhagain
05-31-2010, 01:47 PM
Firstly, it depends on whether your bottleneck is in bandwidth or not. For most non-trivial scenes there will be some benefit to be had, but it may not be as much as you might think (especially if your driver is doing it's own behind the scenes optimisation on submitted vertexes).

What I'd suggest doing is move initially to vertex arrays (possibly in 2 stages, firstly non-indexed, secondly indexed) - you're going to need them anyway for a move to VBOs, and if you get any perf increase there then you will likely get more from VBOs. The exception to this is if you need to update a VBO multiple times per frame.

Yes, use triangles in preference to quads if performance is really really critical. 3D hardware draws everything as triangles anyway, so a quad will need to be decomposed into it's two component triangles before it can be drawn. Again though this can heavily depend on whether or not this is even a bottleneck for what you're doing.

Try to use as few draw calls as possible (with as many primitives as possible in each call), group items with common state together, within each group draw from front to back if you can, and you should be a long way towards getting optimal performance.

Other bottlenecks you might encounter are fillrate and shader performance. Fillrate can be alleviated by avoiding overdraw (e.g. by drawing front to back) but it's not always practical or convenient to do this for every object in the scene. For shaders (including fixed func texenvs), as well as optimising any vertex or fragment shaders you might use (reduce instructions, move per-fragment ops to per-vertex if you can get away with it, etc) you should be doing backface culling and frustum culling, if you're not already, but be aware that frustum culling can sometimes be slower than just drawing everything, e.g. if you just have an unstructured triangle soup.

Matthew4693434
05-31-2010, 02:29 PM
Thank you very much for the answer.

Surely it isn't very hard to use VBOs on top of vertex arrays? I already got them working for lines so I have some idea what to do.

I will use triangles because I might as well.

"Try to use as few draw calls as possible (with as many primitives as possible in each call), group items with common state together, within each group draw from front to back if you can, and you should be a long way towards getting optimal performance."

What do you mean by this, sorry?

"Fillrate can be alleviated by avoiding overdraw (e.g. by drawing front to back) but it's not always practical or convenient to do this for every object in the scene."

Are you referring to 3D graphics again because this is only in 2D. You'd think performance would be easy to get with 2D but that's easier said than done. My game is actually giving fine fps some of the time but will occasionally go very slow before going fast again. There seems to be some memory problem. Yet overall it could still be made faster even if any suggestions don't fix the odd performance issue.

Frustum culling is only used in 3D graphics, right? And back face culling is done by default anyway, apparently?

mhagain
05-31-2010, 03:40 PM
Thank you very much for the answer.

Surely it isn't very hard to use VBOs on top of vertex arrays? I already got them working for lines so I have some idea what to do.

I will use triangles because I might as well.

"Try to use as few draw calls as possible (with as many primitives as possible in each call), group items with common state together, within each group draw from front to back if you can, and you should be a long way towards getting optimal performance."

What do you mean by this, sorry?

"Fillrate can be alleviated by avoiding overdraw (e.g. by drawing front to back) but it's not always practical or convenient to do this for every object in the scene."

Are you referring to 3D graphics again because this is only in 2D. You'd think performance would be easy to get with 2D but that's easier said than done. My game is actually giving fine fps some of the time but will occasionally go very slow before going fast again. There seems to be some memory problem. Yet overall it could still be made faster even if any suggestions don't fix the odd performance issue.

Frustum culling is only used in 3D graphics, right? And back face culling is done by default anyway, apparently?


It's not very difficult to use VBOs on top of vertex arrays at all, but they might not give you the performance you want depending on how your drawing needs to be set up. It's also easier to identify potential causes of crashes and/or bugs (and roll back code to previous versions without upsetting things) when you make smaller, more incremental changes.

Regarding draw calls, I mean batch your primitives as much as possible. If you have 300 triangles that share the same state, putting them all into a single vertex array (or VBO) and drawing them with a single glDrawElements call will be faster than drawing 300 separate triangles.

I missed the 2D specification in your OP, but a lot of what I said can still apply. An orthographic projection is really just a special case of a perspective projection after all, so you can potentially gain performance by rejecting primitives outside of your window rectangle before they even get sent to the GPU.

Another thing for 2D - I would assume that you're not mipmapping textures, so you should read up on texture atlases. They can really help a lot with reducing the number of texture changes you need to make.

Matthew4693434
05-31-2010, 04:01 PM
I might use texture atlases when it comes to characters and things like that, thank you.

I'll take a look into Vertex arrays now but since I use the same textures, over and over again, I'm thinking sending the vertex data once must be beneficial?

I already have code which ignores parts off-screen. I'm not sure if I've done it for everything though.

I was looking into the OSX OpenGL profiler but it didn't work. It basically crashed the game.

For some reason when I've tried the Vertex Arrays (With GL_QUADS to begin with) the textures wont work:

glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL _CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL _CLAMP_TO_EDGE);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glColor4d(1,1,1,a/255);
glBindTexture(GL_TEXTURE_2D, texture);
glTranslated(offset[0],offset[1],0);
glEnableClientState(GL_VERTEX_ARRAY);
GLdouble vertices[] = {0,0,size[0],0,size[0],size[1],0,size[1]};
GLint texcoords[] = {0,0,1,0,1,1,0,1};
glTexCoordPointer (2, GL_INT, 0, texcoords);
glVertexPointer(2, GL_DOUBLE, 0, vertices);
glDrawArrays(GL_QUADS, 0, 4);
glDisableClientState(GL_VERTEX_ARRAY);

So I'm clearly doing something wrong.

Matthew4693434
06-01-2010, 08:24 AM
I actually managed to get some output from VBOs but it not the sort of output I wanted. It seems my first call to glDrawArrays does nothing and the second give some malformed triangle-shaped graphics.

I have setup my VBO with the data with a function:

extern "C" GLuint create_box_vbo(GLdouble size[2]){
GLuint vbo;
glGenBuffers(1,&vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
GLsizeiptr data_size = 8*sizeof(GLdouble);
GLdouble vertices[] = {0,0, 0,size[1], size[0],0, size[0],size[1]};
glBufferData(GL_ARRAY_BUFFER, data_size, vertices, GL_STATIC_DRAW);
data_size = 8*sizeof(GLint);
GLint textcoords[] = {0,0, 0,1, 1,0, 1,1};
glBufferData(GL_ARRAY_BUFFER, data_size, textcoords, GL_STATIC_DRAW);
return vbo;
}

I have then drawn the texture in another function with the code:


glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL _CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL _CLAMP_TO_EDGE);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glColor4d(1,1,1,a/255);
glBindTexture(GL_TEXTURE_2D, texture);
glTranslated(offset[0],offset[1],0);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glVertexPointer(2, GL_DOUBLE, 0, 0);
glEnableClientState(GL_VERTEX_ARRAY);
glTexCoordPointer (2, GL_INT, 0, 0);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glDrawArrays(GL_TRIANGLES, 0, 3);
glDrawArrays(GL_TRIANGLES, 1, 3);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
glBindBuffer(GL_ARRAY_BUFFER, 0);

Here's the odd result - http://godofgod.co.uk/my_files/wrong.png
And here's what it's supposed to look like - http://godofgod.co.uk/my_files/right.png