OpenGL ES: Batch Rendering on the iPhone

This one took me a while, simply because there was a LOT of research to do on my part. This is not going to be a tutorial post, per-se… but more so a “This is what I’ve found” and a request that someone take a look through it and see what I could have done better, and then let me know ! :)

Simply put, I was getting a few FPS problems with the development of my game, and thought I would come up with a way to batch all of my drawing into as few OpenGL calls as I could. I came across a few interesting tidbits, including a Stanford ‘iTunes U’ course (look that up in iTunes, its WELL worth viewing) Especially the one on OpenGL from Tim Omernick (Slide can be found HERE).

A copy of the code can be found at the google code repository, direct link to the TextureController is HERE. Well then, lets have a look at it!

The first thing that I attempted to do, was make a struct with the vertices, color information, and uv information. This is a result from Tim’s lecture and heavily influenced by his code. The hiccup was that I wanted to be able to batch based on the GLuint being drawn so I would only have to bind a texture once and then draw all vertices associated with that texture. I came up with the following solution:

struct Vertex
 short v[2];
 unsigned color;
 float uv[2];

struct VertexInfo
 Vertex vertex[MAX_VERTICES];
 int _vertexCount;

What will happen here, is VertexInfo will be stored in a map, while the Vertex struct houses all of the information that will be thrown at OpenGL to draw. MAX_VERTICES is defined at the top of the header file, and I really have not played around with it much so I am not sure how slow or fast it will be with a full game running. Currently its set for 100,000 but that was just a number I pulled out of nowhere :D I imagine it should be reduced.

The Map containing the vertex information looks like this;

typedef std::map<GLuint, VertexInfo> VertexMap;
VertexMap vertices;

You’ll see how to use this in a minute. My idea for this class was to sort of clone the way the SpriteBatch works from the XNA framework. This is another of my nods to how well this framework is put together, and how much I enjoy working with XNA. I wanted to be able to call a begin function to let the TextureController know if I plan to use blending (or any other parameter I may add, such as sorting), and then throw a bunch of stuff to draw using the Textures->draw functions provided. Each call to the Textures->draw function will take the texture to be drawn and any information needed (such as the location, source rectangle, color, etc) and pack that information into the VertexMap waiting to be thrown to OpenGL. Once all of my draw commands have been executed, a Textures->end() will initiate all drawing for the batch.

Keep in mind, is this the best way to put something like this together? Probably not.. although its my first time doing such an interesting task.. :) Lets see how it works!

NOTE: I will input breaks to make some comments… so if you want the full file, be sure to visit the code repository at

void TextureController::draw( const Texture2D& texture, const Rectangle& destination,
 const Rectangle& source, const Color& color, const GLfloat depth )

As you can see, we’re plugging in a few informational tidbits, but this is only one of the drawing commands that i’ve implemented. The simplest draw command, you only have to provide a texture and a destination rectangle… and “default” values will be put to fill in the blanks when calling this function.

 GLuint glid = texture.getId();

 //	if we don't have any vertices with the texture being drawn, create a
 //	vertex map for it.
 VertexMap::iterator it = vertices.find(glid);
 if (it == vertices.end())
 vertices[glid]._vertexCount = 0;		

 //	find all of the vertices we'll need for this sprite
 float topLeftX = destination.x;
 float topLeftY = destination.y;
 float topRightX = destination.x + destination.width;
 float topRightY = destination.y;
 float bottomLeftX = destination.x;
 float bottomLeftY = destination.y + destination.height;
 float bottomRightX = destination.x + destination.width;
 float bottomRightY = destination.y + destination.height;

The above section is interesting in that it will first find out if we’ve already batched a sprite using the texture. If we have, we’ll just add the vertices into that batch. Otherwise, we’ll create a new batch of vertices and start that new batch. Next, we find all of the vertices! This is done through the destination rectangle passed in. The destination rectangle is going to be the spritebox that the texture is drawn to. In other words, its your canvas and your texture is the paint.

 // Texture atlas
 float minUV[2];
 float maxUV[2];

 //	if the source rectangle of ZERO was passed in, it means the client want to just
 //	draw the texture as is.. otherwise, the client wishes to draw a portion of
 //	the rectangle
 if (source == Rectangle::ZERO())
 float maxS = texture.getMaxS();
 float maxT = texture.getMaxT();
 float minS = 0;
 float minT = 0;

 minUV[0] = minS;
 minUV[1] = minT;
 maxUV[0] = maxS;
 maxUV[1] = maxT;
 float minS = source.x / texture.getWidth();
 float minT = source.y / texture.getHeight();
 float maxS = source.width / texture.getWidth();
 float maxT = source.height / texture.getHeight();

 minUV[0] = minS;
 minUV[1] = minT;
 maxUV[0] = maxS;
 maxUV[1] = maxT;

This section took me the longest to really figure out, because I had not done any research how Texture Atlas’s work. As I said, this was heavily influenced by Tim’s code and I really wanted to understand this section before I moved on. What we’re doing here, is if a blank rectangle is passed into the function (meaning the client didn’t specify a rectangle and therefore wishes to just use the texture ‘as is’) we’ll use the maxS and maxT coordinates for the TextureCoordinates. The way we find out what maxS and maxT are, is after the image goes through its modification to become a power of two texture (see my last article), maxS will become imageWidth / newTextureWidth. In other words
if the texture is passed into your game at 30 pixels by 30 pixels, it was re-sized by the Texture2D class to become 32 by 32. maxS will be 30 / 32, or 0.9375f. This value indicates that when using the default values, 93% of the image is drawn, while the remaining 7% of the image contains nothing more than padding in order to ensure it was a power of two texture.

What if we wanted to use a sub image, only draw a portion? This would be useful if we had several images on one texture. Lets say you have a texture with 2 frames of animation, both frames are 24 by 24. This means your texture is 48 pixels wide, by 24 pixels tall. If you only want to draw the last half of the texture (your “2nd” frame of animation) your source rectangle would be:

x=24, y=0, width=24, height=24
Meaning you’re starting 24 pixels into the picture, at the top pixel of the picture, and you’re going 24 pixels wide and 24 pixels high. This is going to be the sub-image you’re drawing.

In order to change these numbers into texture coordinates, we need to divide by the image width or height. For example, 24 / 48 is .5, so your texture coordinate for x is .5, then 0, then .5, and .5.

 //	Convert the colors into bytes
 unsigned char red = * 255.0f;
 unsigned char green = * 255.0f;
 unsigned char blue = * 255.0f;
 unsigned char shortAlpha = color.alpha * 255.0f;

 //	pack all of the color data bytes into an unsigned int
 unsigned _color = (shortAlpha << 24) | (blue << 16) | (green << 8) | (red << 0);

 // Triangle #1
 addVertex(glid, topLeftX, topLeftY, minUV&#91;0&#93;, minUV&#91;1&#93;, _color);
 addVertex(glid, topRightX, topRightY, maxUV&#91;0&#93;, minUV&#91;1&#93;, _color);
 addVertex(glid, bottomLeftX, bottomLeftY, minUV&#91;0&#93;, maxUV&#91;1&#93;, _color);

 // Triangle #2
 addVertex(glid, topRightX, topRightY, maxUV&#91;0&#93;, minUV&#91;1&#93;, _color);
 addVertex(glid, bottomLeftX, bottomLeftY, minUV&#91;0&#93;, maxUV&#91;1&#93;, _color);
 addVertex(glid, bottomRightX, bottomRightY, maxUV&#91;0&#93;, maxUV&#91;1&#93;, _color);

Aah, bit packing... how do I love thee, let me count the ways! When I first learned how to manipulate bits of data, I asked myself "Why would this ever be useful?" ... guess I learned the hard way, that its a very necessary skill to have. I don't fully understand it as much as I would like to, but I do a little research on it every day.

What we're doing in the above code is taking the red / green / blue / alpha color value (which is passed in as a 'between 0 and 1' value), multiplying that by 255, and packing the bits into a single value called _color. Apparently, its a lot faster for OpenGL to use this data in this way, so hi ho, hi ho.. its off to pack we go!

void TextureController::addVertex(GLuint glid, float x, float y, float uvx, float uvy, unsigned color)
 VertexInfo *vertexInfo = &vertices&#91;glid&#93;;
 Vertex *vert = &vertexInfo->vertex[vertexInfo->_vertexCount];
 vert->v[0] = x;
 vert->v[1] = y;
 vert->uv[0] = uvx;
 vert->uv[1] = uvy;
 vert->color = color;

And packing we do! This is where the batch work is done. What we’re doing is passing in a single Vertex data (the x location, y location, texture coordinates, color, and the texture used) and packing it into the struct I showed you above. Once we do that, we increase the _vertexCount, so we’re keeping tabs on how many vertices we’re batching.

So.. thats how we batch, how do we render? Simple!

NOTE: Keep in mind, this is only a portion of the code. This function is called from the “end()” function I talked about eariler. end() will ensure that beginning was called, and then pass the buck to the render function.

void TextureController::renderToScreen()

 //	Texture Blending fuctions
 if ( blendAdditive )

 //	needed to draw textures using Texture2D

 //	enables alpha for transparent textures
 //	I forget where I got these commands, I think
 glAlphaFunc(GL_GREATER, 0.1f);

 //	Enable the various arrays used to draw texture to screen


The above is just standard stuff to get ready for rendering. In fact, I think this was just cut / pasted from my Texture2D class. The real interesting part happens next!

 //	loop through all of the elements of the map and draw the vertices
 VertexMap::iterator it = vertices.begin();
 for (/* none */; it != vertices.end(); it++)
 //	easy access to our data
 VertexInfo *vertexInfo = &it->second;
 Vertex *vert = vertexInfo->vertex;

 //	bind the texture for the following vertices
 bindTexture( (*it).first );

 //	throw everything to OpenGL
 glVertexPointer(2, GL_SHORT, sizeof(Vertex), &vert->v);
 glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), &vert->uv);
 glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(Vertex), &vert->color);
 glDrawArrays(GL_TRIANGLES, 0, vertexInfo->_vertexCount);

 //	reset this batches vertex count
 vertexInfo->_vertexCount = 0;

What we’re doing here is looping through every key (which is the GLuit of the texture, if you recall) binding that texture so that OpenGL knows which texture to draw with, and then throws all of the vertices and texture coordinates and all other info needed to render the batch. A few notes that some people might not know (I didn’t when I was researching this)

glVertexPointer(2, GL_SHORT, sizeof(Vertex), &vert->v);
the 2 means that there will be 2 vertices, and x and a y. This number could be a 3, if you’re also storing a z value. We will be using GL_SHORT (remember in the struct, all vertices are declared as short), and the interesting portion of this is the sizeof(Vertex). This is called a STRIDE, and basically tells OpenGL the distance between the data in memory. (I hope I explained that correctly!) The last value is the array of vertices!

glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), &vert->uv);
This is very similar to the above.. except we’re using different values. The 2 is referring to the fact that we have 2 points per vertex for the texture coordinates (an x and a y) but if you’re going to be using a z value, this number will be changed to 3. Remember, we are using floats for this (check the struct), and the STRIDE value stays the same. Then just pass it in the array of information.

glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(Vertex), &vert->color);
Again, similar concept, except we did a few things differently with this. Remember our color scheme is 4 colors (red, green, blue, and alpha) but we packed all that information into a single value? We specify that via the GL_UNSIGNED_BYTE. otherwise, everything else remains similar.

glDrawArrays(GL_TRIANGLES, 0, vertexInfo->_vertexCount);
This is the reason we’re keeping track of how many vertices we have! What we’re doing here is telling OpenGL to draw the arrays using triangles, the 0 is referring to the starting index (obviously we’re going to be starting at index 0, just as if we were looping through the index ourselves), and how many elements are in the arrays.

What this will do, is put together the information in a single stream of information.. something like:

This is much more efficient for OpenGL to render, and therefore it will chew through the data and at much greater speeds than if you render your data once per texture. (ala, my old method)

now all thats left to do is clean up by disabling any states we enabled

 //	disable all the stuff we enabled eariler
 glDisable( GL_BLEND );
 glDisable( GL_TEXTURE_2D );
 glDisable( GL_ALPHA_TEST );


and voila!

Again, this is my first pass at writing such a system.. and I am still learning the form of graphics programming. I urge you to pick up the code from the svn repository ( and play around with it. If you find anything that I could do better, or things that may help me understand this process a little better, feel free to post for everyone to learn from!

Happy Coding everyone!


16 comments so far

  1. nick on

    Pretty cool post. I just came by your blog and wanted to say that I have really enjoyed browsing your posts.

    Any way I’ll be subscribing to your feed and I hope you post

    again soon!

    • Craig on

      Thanks Nick. I post when I have something interesting to post on… I usually don’t do random posts unless its something that can be useful for someone. This latest post took me a while to put together because I did a little more research than the other topics.. but so far everything has been working well to use all of these systems in a single game engine :)

  2. sammy on

    I was fallowing your post lately as i am learning have put a great resources for beginners like me.I was also tring to put some of my classes of c++ in my code and got huge list of errors. I changed my obj-C .m to .mm but do we need to setup something to compile cpp file like compiler flag or something

    • Craig on

      I’m going to guess somewhere in the range of 10,000 errors? :) I’ve found that when working with C++ files, (meaning, *.cpp) you pretty much have to have practically no .m or .mm files, and none of your files can #import / #include from things like foundation/foundation.h (this was the big one for me, soon as I #included them into my project anywhere, i got a HUGE list of errors)

      This is most likely the source of your errors.

  3. sammy on

    and one more thing I forgot to ask.Can I use objective c methods in cpp files.if so do I need to change anything like types etc.and if we change .m file .mm file some of the code gives error.sorry to ask so many question but i searched a lot but didn’t find any solution to these questions.

    • Craig on

      I don’t think you can make obj-c calls in cpp files, but if the exact same class is renamed to then you’re able to do so. A good example is my ( , look under ‘Utilities’ ) Remember, .mm files are nothing more than obj-c++ files, so you can use C++ syntax and obj-c syntax.

  4. sammy on


  5. Tim Omernick on

    Hey, I’m the Tim Omernick who gave that talk… just stumbled across your post…glad my talk was so helpful to you!

    You are actually onto some pretty deep stuff here. This is the way many high-end 3D engines work and you’ve almost got a “deferred renderer” setup going. This approach allows you to be efficient about reducing draw call count and switching GL state and may lead you into some interesting visual effects possibilities (especially if you start using GL2 shaders)

    If you want even more optimal performance, you should try this — instead of mapping the plain texture # to vertex list, try to also include the relevant GL state inside your bucket structure (“VertexInfo”). You can use this “bit packing” you are so fond of to create a uint32 that is a combination of GL state (in the high bits) and the texture number (in the low bits), or anything else you want to “batch” by.

    Then — instead of rendering each batch after your Textures->end(), save *all* the batches you’re going to draw that frame into a big list of VertexInfos. Sort that list by the uint32 that represents the GL state + texture. Now if you render this list in order, you can be very clever about only changing GL state when necessary, and you can even sort opaque/alpha objects correctly (draw opaque first then alpha).

    If you don’t do this, you’ll often run into patterns like this:






    when the following is far more efficient:



    make sense? sorry if not :)

    • Craig on

      I had to read through that a few times… but yeah it makes total sense after I wrapped my head around it.. and very helpful and gives me quite a few ideas too! :)

      I didn’t realize the stuff I was doing was getting into that level of detail, but I guess with a few tweaks, you could really do some interesting things with this line of thinking. And yes, your talk helped a great deal with getting me onto the right track!

  6. Hjalti Jakobsson on

    Thanks for the article. Interesting stuff.

    I’m just wondering how you plan on keeping certain objects on top. Let’s say you have one texture for your character and you’re drawing two characters but with another texture in between. So you might have a character, then a weapon and then the same kind of character on top of that. I hope you get what I mean.

    – H

  7. Craig on

    yeah you mean depth right? Like take a side scrolling 2d game.. sprite 1 in the “back”, sprite 2 in the middle, and sprite 3 in the front? That way when they overlap, sprite 3 will be on top, and sprite 2 will be obstructed somewhat, while sprite1 will be obstructed by the other two…

    It was a problem I was thinking about, and I actually have some comments in the code as a “Todo, need to figure out a way to do depth” :) I have a few ideas, but nothing that I have really played around with yet.

  8. Hjalti Jakobsson on

    Yeah exactly. I’m also wondering if you plan to alter the vertices each frame when rotating or alter the world transform.

    – H

  9. Hjalti Jakobsson on

    Ah, nevermind. You’ll have to alter the vertices if you want to rotate two items using the same texture individually.

  10. Hjalti Jakobsson on

    Hi again.

    Have you tried running it on an actual device? Seems like std:map crashes on the device but not in the simulator.

    • Craig on

      I have not, but I have helped someone debug this issue. Lower the # of vertices, remember that number I pretty much pulled out of my ass? :)

      I don’t know the optimum number for that, mostly because I don’t have a device to test it on.. but it would require some tweaking.

  11. Hjalti Jakobsson on

    Yeah I found out that number of vertices seems to overflow the stack. Found out after getting strange crashes at random places.

    – H

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: