iDevGames Forums
batch opengl calls - Printable Version

+- iDevGames Forums (http://www.idevgames.com/forums)
+-- Forum: Development Zone (/forum-3.html)
+--- Forum: iPhone, iPad & iPod Game Development (/forum-11.html)
+--- Thread: batch opengl calls (/thread-1212.html)

Pages: 1 2


batch opengl calls - kendric - Jun 1, 2009 07:00 AM

So people have talked about some performance gains(though the amount varies) by having just one batch of vertex renders per texture instead of many. My question is, can this only be done with quads? I use a triangle strip of size 2 to render quads as I heard it was faster slightly. But if I tried to specify more verticies it would connect my triangles together. I haven't used the quads functionality yet but I assume you specify all 4 corners so perhaps this is how people are doing it. There might be a trade off between quads and triangle strips that could cancel the bonus you get from a single call but thats beyond my knowledge level.


batch opengl calls - ThemsAllTook - Jun 1, 2009 07:20 AM

I'm not entirely sure about the relative performance gains (it's unlikely you'll be able to get a reliable answer other than by trying it yourself), but you'll most likely want to be looking at GL_TRIANGLES instead of GL_QUADS. GL_QUADS doesn't exist in OpenGL ES, and wouldn't be a very natural choice for replacing GL_TRIANGLE_STRIP anyway.


batch opengl calls - ShiftZ - Jun 1, 2009 11:51 AM

kendric Wrote:So people have talked about some performance gains(though the amount varies) by having just one batch of vertex renders per texture instead of many.

If you can do it by minor efforts, then you pobably should do that (GL_TRIANGLES see above)
As for me, i use sprites with animated textures, and to have one render call per texture i have to reconstruct vertex array for each texture every frame, and i see that unacceptable.


batch opengl calls - warmi - Jun 1, 2009 02:57 PM

ShiftZ Wrote:If you can do it by minor efforts, then you pobably should do that (GL_TRIANGLES see above)
As for me, i use sprites with animated textures, and to have one render call per texture i have to reconstruct vertex array for each texture every frame, and i see that unacceptable.

You don't have to do that if your texture contains all animation frames.

All you need to to is to set correctly the current texture matrix (to scale and translate your texture coordinates ) and the GPU will do it for you.


batch opengl calls - ShiftZ - Jun 2, 2009 03:06 AM

warmi Wrote:You don't have to do that if your texture contains all animation frames.

All you need to to is to set correctly the current texture matrix (to scale and translate your texture coordinates ) and the GPU will do it for you.

I cannot see any way to do that.
Sprites could be anywhere, and have any anim frame id, and to render all of them in one draw call i should reconstruct vertex array and also tex coords array every new frame, and texture \ model \ view matrix could be set up only once before actuall draw call.


batch opengl calls - warmi - Jun 2, 2009 07:36 AM

ShiftZ Wrote:I cannot see any way to do that.
Sprites could be anywhere, and have any anim frame id, and to render all of them in one draw call i should reconstruct vertex array and also tex coords array every new frame, and texture \ model \ view matrix could be set up only once before actuall draw call.

Yeah, if you are rendering multiple sprites within the same batch then yeah.


batch opengl calls - kendric - Jun 3, 2009 08:41 AM

Thanks for the answers. I can move from triangle strips to triangles and check the performance. My drawing s all done through a few functions so I should be able to just cache vertex's until I hit a new texture and render it all.


batch opengl calls - kendric - Jul 11, 2009 06:29 PM

Well, I finally got around to trying this. It works for some stuff and others its all wonky. I think its most likely related to when there are more then one atlas's being used on the screen at the same time as the one screen that works fine has images from only one atlas.
Anyhow I thought I would post this code as if we can get it working it might be a nice library item for other people to use. If you see anything wrong here that might be causing the problems let me know. also may have to do with rotation as my images are drawing at the wrong rotations in some cases(but not others). I used to a seperate triangle strip of size 2 for each render and this code attempts to render it all in one go per texture.
Code:
typedef enum _Atlas
{
    Atlas_Units,
    Atlas_Misc,
    Atlas_FrontEnd,
    Atlas_Background,
    
    Atlas_Last
}Atlas;

//for the below struct, I may have the comment for the 2 wrong
//it might be top left and bottom right
typedef struct _AtlasRenderDetails
{
    GLuint textureID;//gl texture id
    CGPoint textureLocationsX1Y1;//pre computed bottom left of the texture
    CGPoint textureLocationsX2Y2;//pre computed top right of the picture
    short width;//width to draw it
    short height;//height to draw it
    Atlas atlas;//this is more specific to my engine so you can ignore
}AtlasRenderDetails;

typedef struct _GGLOptions
{
    bool textureEnabled;
    GLuint currentTexture;
    GLfloat *vertexArray;
    GLfloat *textureArray;
    GLfloat arraySize;
    int arrayPosition;
    GLfloat r;
    GLfloat g;
    GLfloat b;
    GLfloat a;
}GGLOptions;

GGLOptions _GGLOptions;




static void GGLinitOpenGL()
{
    _GGLOptions.textureEnabled=false;
    _GGLOptions.currentTexture=99999;
    _GGLOptions.arrayPosition=0;
    _GGLOptions.arraySize=100;
    _GGLOptions.vertexArray=malloc(sizeof(GLfloat)*_GGLOptions.arraySize);
    _GGLOptions.textureArray=malloc(sizeof(GLfloat)*_GGLOptions.arraySize);
    _GGLOptions.r=999;//bogus values so that it will be different from whatever the first thing that is set
    _GGLOptions.g=999;
    _GGLOptions.b=999;
    _GGLOptions.a=999;
    glEnableClientState(GL_VERTEX_ARRAY);
    glDisable(GL_TEXTURE_2D);
    glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
    //glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    //glAlphaFunc(GL_GREATER,0.3f);
    //glEnable(GL_ALPHA_TEST);
    glEnable(GL_BLEND);
}

static void _GGLgrowArray()
{
    GLfloat *newVertexArray=malloc(sizeof(GLfloat)*_GGLOptions.arraySize*2);
    GLfloat *newTextureArray=malloc(sizeof(GLfloat)*_GGLOptions.arraySize*2);
    memcpy(newVertexArray, _GGLOptions.vertexArray, _GGLOptions.arraySize);
    memcpy(newTextureArray, _GGLOptions.vertexArray, _GGLOptions.arraySize);
    free(_GGLOptions.vertexArray);
    free(_GGLOptions.textureArray);
    _GGLOptions.vertexArray=newVertexArray;
    _GGLOptions.textureArray=newTextureArray;
    printf("Doubling size");
}

static void GGLrenderData()
{
    //do render
    //setup params
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexCoordPointer(2, GL_FLOAT, 0, _GGLOptions.textureArray);
    glVertexPointer(2, GL_FLOAT, 0, _GGLOptions.vertexArray);
    //Draw the texture
    glDrawArrays(GL_TRIANGLES, 0, _GGLOptions.arrayPosition/2);//Array position is in x and y terms, and this function is in vertex terms so its half
    //reset
    _GGLOptions.arrayPosition=0;
}

static void GGLsetColor(GLfloat r,GLfloat g,GLfloat b,GLfloat a)
{
    if(_GGLOptions.r!=r||_GGLOptions.g!=g||_GGLOptions.b!=b||_GGLOptions.a!=a)
    {
        if(_GGLOptions.arrayPosition)
        {
            GGLrenderData();
        }
        _GGLOptions.r=r;
        _GGLOptions.g=g;
        _GGLOptions.b=b;
        _GGLOptions.a=a;
        //This is because UIComponent preblends the alpha into the image
        glColor4f(r*a,g*a,b*a,a);
    }
    
}


static void GGLsetTexture(GLuint texture)
{
    if(!_GGLOptions.textureEnabled)
    {
        _GGLOptions.textureEnabled=true;
        glEnable(GL_TEXTURE_2D);
        glEnableClientState(GL_TEXTURE_COORD_ARRAY);
    }
    if(_GGLOptions.currentTexture!=texture)
    {
        if(_GGLOptions.arrayPosition)
        {
            GGLrenderData();
        }
        _GGLOptions.currentTexture=texture;
        glBindTexture(GL_TEXTURE_2D, texture);
    }
}

static void GGLclearTexture()
{
    if(_GGLOptions.arrayPosition)
    {
        GGLrenderData();
    }
    if(_GGLOptions.textureEnabled)
    {
        _GGLOptions.textureEnabled=false;
        glDisable(GL_TEXTURE_2D);
        glDisableClientState(GL_TEXTURE_COORD_ARRAY);
    }
}

static void GGLdrawTexture(GLfloat x,GLfloat y,GLfloat width,GLfloat height,AtlasRenderDetails renderDetails)
{
    GGLsetTexture(renderDetails.textureID);
    if(_GGLOptions.arrayPosition+12>=_GGLOptions.arraySize)
    {
        _GGLgrowArray();
    }
    //make temporary pointers that point to where the next one will be put in our big array
    GLfloat* textureVertices=_GGLOptions.textureArray+_GGLOptions.arrayPosition;
    GLfloat* spriteVertices=_GGLOptions.vertexArray+_GGLOptions.arrayPosition;
    //triangle 1
    textureVertices[0]=renderDetails.textureLocationsX1Y1.x;
    textureVertices[1]=renderDetails.textureLocationsX1Y1.y;
    textureVertices[2]=renderDetails.textureLocationsX1Y1.x;
    textureVertices[3]=renderDetails.textureLocationsX2Y2.y;
    textureVertices[4]=renderDetails.textureLocationsX2Y2.x;
    textureVertices[5]=renderDetails.textureLocationsX2Y2.y;
    //triangle 2
    textureVertices[6]=renderDetails.textureLocationsX2Y2.x;
    textureVertices[7]=renderDetails.textureLocationsX2Y2.y;
    textureVertices[8]=renderDetails.textureLocationsX1Y1.x;
    textureVertices[9]=renderDetails.textureLocationsX1Y1.y;
    textureVertices[10]=renderDetails.textureLocationsX2Y2.x;
    textureVertices[11]=renderDetails.textureLocationsX1Y1.y;
    //triangle 1
    spriteVertices[0]=x;
    spriteVertices[1]=y+height;
    spriteVertices[2]=x;
    spriteVertices[3]=y;
    spriteVertices[4]=x+width;
    spriteVertices[5]=y;
    //triangle 2
    spriteVertices[6]=spriteVertices[4];//x+width
    spriteVertices[7]=y;
    spriteVertices[8]=x;
    spriteVertices[9]=spriteVertices[1];//y+height
    spriteVertices[10]=spriteVertices[4];//x+width
    spriteVertices[11]=spriteVertices[1];//y+height
    _GGLOptions.arrayPosition+=12;
}



batch opengl calls - kendric - Jul 11, 2009 06:43 PM

I think it is rotations. If I am building a big vertex array but then a rotation is performed, a render has to happen before the rotation and then right before you pop the stack on your model. Ill put this in later and post if it did the trick.


batch opengl calls - maximile - Jul 12, 2009 09:28 AM

Check the Apple docs on optimising OpenGL ES. They suggest using triangle strips with degenerate triangles connecting them. Sounds pretty inelegant to me, so I just use triangles and get adequate performance. Batching certainly made a big difference for me.


batch opengl calls - kendric - Jul 13, 2009 05:22 PM

I scrapped this whole thing because it makes doing rotations and translations a big mess. You have to render after and before each one. I dont think its worth it for my code. I do a translation on a large # of my thigns that are drawing.

One thing I did notice is that my bitmaped texture font rendering is eating up a lot of time. I may need to move to prerendered textures and 1 render rather then drawing each frame.


batch opengl calls - warmi - Jul 14, 2009 12:02 AM

The only way you can get separate parts of batched geometry to rotate independently is by rotating them with your own code.
It certainly makes a lot of sense for 2d rendering when you are displaying independently rotated/scaled/positioned quads (sprites).
It is much faster to rotate/scale 4 vertices than issuing a batch call for just 4 vertices.


batch opengl calls - kendric - Jul 15, 2009 06:36 AM

Rotating the verticies won't rotate the image though right? My game supports 360 degree rotation of the guys. From my profiling, I got the load down from 40% game loop 40% render to like 20% game loop 60% render. The only thing left I can think of is batching my opengl calls. I do about 70-100 renders per frame, each being its own call to this function. I'm very interested to see if there is a way around this without giving up my arbitrarily rotated images.


batch opengl calls - warmi - Jul 15, 2009 07:49 AM

kendric Wrote:Rotating the verticies won't rotate the image though right? My game supports 360 degree rotation of the guys.

Of course it will - otherwise the whole thing would be pointless.


batch opengl calls - kendric - Jul 15, 2009 08:09 AM

So if i render a square(2 triangles) and i specify the texture co-ordinates unrotated, and I specify the vertex's each rotated around its center by 45 degrees i will get the exact same thing as if i rotated normally 45 degrees and drew it with un rotated vertexes?