Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

I've got a JAVA based game with an OpenGL rendering front that is drawing a large amount of sprites every frame (during testing it peaked at 700). Now this game is completely unoptimized. There is no spatial partitioning (so a sprite is drawn even if it isn't on screen) and every sprite is drawn separately like this:

graphics.glPushMatrix();
{                  
    graphics.glTranslated(x, y, 0.0);                           
    graphics.glRotated(degrees, 0, 0, 1);

    graphics.glBegin(GL2.GL_QUADS);

    graphics.glTexCoord2f (1.0f, 0.0f);
    graphics.glVertex2d(half_size , half_size); // upper right

    // same for upper left, lower left, lower right
    graphics.glEnd();                   
}
graphics.glPopMatrix(); 

Currently the game is running at +-25FPS and is CPU bound. I would like to improve performance by adding spatial partitioning (which I know how to do) and sprite batching. Not drawing sprites that aren't on screen will help a lot, however since players can zoom out it won't help enough, hence the need for batching. However sprite batching in OpenGL is a bit of mystery to me. I usually work with XNA where a few classes to do this are built in. But in OpenGL I don't know what to do.

As for further optimization, the game I'm working on as a few interesting characteristics. A lot of sprites have the same texture and all the sprites are square. Maybe these characteristics will help determine an efficient batching technique?

share|improve this question
3  
Check this question. In particular, the first comment, and the accepted answer. –  David Gouveia Oct 12 '12 at 22:00
1  
But the main idea behind batching is that as long as the texture does not change, you can just keep adding the sprite vertices to the same buffer, then submit it all at once at the end (or when the batch size limit is reached, or when the texture does change). –  David Gouveia Oct 12 '12 at 22:06
1  
None of that will work as he needs to break a batch at each sprite in order to do a transform - see my answer below for further discussion of this. –  Jimmy Shelter Oct 13 '12 at 0:00
1  
@mh01 No he doesn't. If you read the implementation of the SpriteBatch in MonoGame for instance, you'll see that the transforms are directly applied to the vertex position right at the moment they're added to the batch. OpenGL does not get a transform for each sprite, just a buffer of vertices that have already been transformed. The portion of the code that does this is here. –  David Gouveia Oct 13 '12 at 9:26
    
@mh01 It basically ends up doing what you say in your answer by applying the transforms on the CPU, except that everything gets placed in a vertex array. –  David Gouveia Oct 13 '12 at 9:34

2 Answers 2

up vote 6 down vote accepted

If you don't wish to make the jump to vertex arrays and a VBO just yet, there are some simple changes you can make to get more efficiency from even immediate mode.

Yes, immediate mode is still the slowest option, but it's not THAT slow - Quake used immediate mode back in 1996 and despite that it will get you well over 25 fps on modern hardware. So bear that in mind when considering the rest of what I have to say. Moving to vertex arrays or VBOs will (eventually) get you more speed, but unless you tackle the root cause of your performance degradation (which is actually the fact that you're doing a separate transform per-sprite) it's not going to be much more speed (it may even be slower).

So let's look at your sprite transformation. This is actually quite a simple transformation matrix that is built on the CPU (even in OpenGL with hardware T&L - it's only the final transform of position by MVP matrix that runs on the GPU) so we can take advantage of that knowledge and make immediate mode go faster.

Before I go into much more detail about how to deal with that, the first thing you need to know is that you have a classic case of an immediate mode anti-pattern at work here. It manifests as this (C/C++ code but it should be obvious what the Java equivalent is):

for (int i = 0; i < NUM_SPRITES; i++)
{
    glBegin (GL_QUADS);
    // glVertex/etc calls here
    glEnd ();
}

The simple solution to this is:

glBegin (GL_QUADS);

for (int i = 0; i < NUM_SPRITES; i++)
{
    // glVertex/etc calls here
}

glEnd ();

If you don't believe me then build it, run it, benchmark it. Ramp up the value of NUM_SPRITES to see how each approach performs as the count increases. Each glBegin/glEnd pair counts as a "batch" in immediate mode, and by putting them outside the loop you get better throughput.

So how can we use this to acelerate your case? Quite easy. Instead of using the matrix stack to do transforms, let's build the matrix and apply it to each sprite in software ourselves. This seems a mite counter-intuitive, but stick with me - it's still going to be faster than breaking batches by having to do a separate transform per-sprite.

Our loop now looks like this:

glBegin (GL_QUADS);

for (int i = 0; i < NUM_SPRITES; i++)
{
    // evaluate transformation matrix for this sprite
    // apply transformation matrix to sprite positions
    // glVertex/etc calls here
}

glEnd ();

So how do we do those two new steps? Just by using the OpenGL documentation to build the matrix; e.g. the matrix for glRotate is given here: http://www.opengl.org/sdk/docs/man/xhtml/glRotate.xml and for glTranslate here: http://www.opengl.org/sdk/docs/man/xhtml/glTranslate.xml. Another, better option is to grab a matrix library; I'm not familiar with Java so I can't speak as to availability of such but a quick Google search should turn up results.

It's also worth noting that you have a potential shortcut here too - if for a given sprite the rotation angle is 0, then instead of doing a full matrix transform you can just do:

 glVertex2f (x + half_size, y + half_size); // and etc...

Once you have that part resolved, THEN is the time to consider porting to vertex arrays or VBOs, not before. Because, like I said, your real bottleneck here is not use of immediate mode, it's doing a separate transform per sprite. Porting the code before addressing that will still leave you in a position where you have to break batches at every sprite, and any performance gains will be quite marginal (a bad VBO implementation may even run much slower).

IMO people are often too trigger-happy about recommending VBOs as a solution as soon as they see any immediate mode code, and can sometimes fail to analyze the code properly and identify that the real bottleneck may be elsewhere. Of course a VBO will be the optimal solution longer-term, but address that real bottleneck first.

share|improve this answer
    
It's quite an interesting notion that transforming on the CPU will eliminate the CPU bottleneck. I suppose it could be the case if the transformations are cheaper than the draw call overhead. This should be easy to test. I'll give it a try. –  Roy T. Oct 13 '12 at 6:35
    
+1 Yeah, this seems like the logical first step towards batching. –  David Gouveia Oct 13 '12 at 10:45

If your interest is in making games and not game engines, I suggest you use a game engine. Game engines are libraries which solve some of the most frequently encountered problems, like the one you are having. There are many, but if I may suggest:

Slick2D at http://slick.cokeandcode.com/. It is a very simple engine, but it lets you get started quickly and easily.

If you wish little more try http://libgdx.badlogicgames.com/. It is in active development, and an astounding number of games has already been published using it. In addition, you can port your game to Android with almost no additional work.

As for the answer behind your actual question... If you can believe it, such a simple question has a complicated answer. You would think that drawing a rectangle is easy, but in order to optimize it you need to know several advanced techniques. The shortest answer is that you need to minimize number of times the CPU is interrupting the GPU, and in addition you should maximize the number of 'things' GPU has to do with a single interruption from the CPU. In your case, every glBegin/End is a trip from the CPU to the GPU. Every time you change a texture it is a trip from the CPU to the GPU. Etc. So the idea is to pack as many sprites into a single texture, then create a buffer with coordinates for as many sprites (ten thousand is a good number), and then render them with single call. One set of functions you should look at are glVertexPointer/glDrawElements. When you start reading about them, you will see there are more ways to do this.

However, once more, I suggest you go with the 3rd party engine, as they usually have nicely wrapped this for you so you can just draw your rectangles.

share|improve this answer
    
+1, I agree that usually using an existing game engine is better but in this case there already exists an engine I'm just adding a hardware renderer to it while trying to minimize the impact (pipeline changes etc..) using an existing front-end would involve changing the entire pipeline too much, breaking the software renderer etc... I indeed expected that this simple thing would be quite complicated. Thanks for the few pointers I'll read up on them. –  Roy T. Oct 13 '12 at 6:31

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.