batch opengl calls

Member
Posts: 306
Joined: 2009.03
Post: #1
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.
Quote this message in a reply
Moderator
Posts: 1,560
Joined: 2003.10
Post: #2
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.
Quote this message in a reply
Member
Posts: 25
Joined: 2009.05
Post: #3
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.
Quote this message in a reply
Member
Posts: 166
Joined: 2009.04
Post: #4
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.
Quote this message in a reply
Member
Posts: 25
Joined: 2009.05
Post: #5
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.
Quote this message in a reply
Member
Posts: 166
Joined: 2009.04
Post: #6
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.
Quote this message in a reply
Member
Posts: 306
Joined: 2009.03
Post: #7
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.
Quote this message in a reply
Member
Posts: 306
Joined: 2009.03
Post: #8
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;
}
Quote this message in a reply
Member
Posts: 306
Joined: 2009.03
Post: #9
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.
Quote this message in a reply
Member
Posts: 283
Joined: 2006.05
Post: #10
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.
Quote this message in a reply
Member
Posts: 306
Joined: 2009.03
Post: #11
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.
Quote this message in a reply
Member
Posts: 166
Joined: 2009.04
Post: #12
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.
Quote this message in a reply
Member
Posts: 306
Joined: 2009.03
Post: #13
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.
Quote this message in a reply
Member
Posts: 166
Joined: 2009.04
Post: #14
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.
Quote this message in a reply
Member
Posts: 306
Joined: 2009.03
Post: #15
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?
Quote this message in a reply
Post Reply 

Possibly Related Threads...
Thread: Author Replies: Views: Last Post
  Trying to save draw-calls for framerate (iphone app) player___1 0 2,661 Jul 6, 2009 12:39 PM
Last Post: player___1