views:

422

answers:

2

Hey guys,

I was wondering how to speed up my iPad application using OpenGLES 2.0. At the moment we have every drawable object draw itself with a call to glDrawArrays(). Blend mode is on, we really need it. Without disabling blendmode, how would we improve performance for this app?

For instances, if we now draw 3 textures (1024x1024, 256x512, 256x512) across the whole screen, the app only gets 15FPS, which is really slow I think? Are we doing something terribly wrong? Our drawing code (for each drawable), is as follows:

- (void) draw {
    GLuint textureAvailable = 0;
    if(texture != nil){
        textureAvailable = 1;
    }

    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, texture.name);

    glVertexAttribPointer(ATTRIB_VERTEX, 2, GL_FLOAT, 0, 0, vertices);
    glEnableVertexAttribArray(ATTRIB_VERTEX);

    glVertexAttribPointer(ATTRIB_COLOR, 4, GL_FLOAT, 1, 0, colorsWithMultipliedAlpha);
    glEnableVertexAttribArray(ATTRIB_COLOR);

    glVertexAttribPointer(ATTRIB_TEXTUREMAP, 2, GL_FLOAT, 1, 0, textureMapping);
    glEnableVertexAttribArray(ATTRIB_TEXTUREMAP);

    //Note that we are NOT using position.z here because that is only used to determine drawing order
    int *jnUniforms = JNOpenGLConstants::getInstance().uniforms;
    glUniform4f(jnUniforms[UNIFORM_TRANSLATE], position.x, position.y, 0.0, 0.0);
    glUniform4f(jnUniforms[UNIFORM_SCALE], scale.x, scale.y, 1.0, 1.0);
    glUniform1f(jnUniforms[UNIFORM_ROTATION], rotation);
    glUniform1i(jnUniforms[UNIFORM_TEXTURE_SAMPLE], 0);
    glUniform2f(jnUniforms[UNIFORM_TEXTURE_REPEAT], textureRepeat.x, textureRepeat.y);
    glUniform1i(jnUniforms[UNIFORM_TEXTURE_AVAILABLE], textureAvailable);

    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}

Possible optimizations I think won't work:

  • Drawing geometry in batches
    • I'm only drawing 3 items and the FPS is 15, I don't think batching the geometry would work here because it's such a small number of calls for drawing that it doesn't matter if we kill 2/3 of those calls.
  • Texture Atlas
    • Again, only drawing 3 textures. What I do wonder if it would matter (a lot) if we were to convert these to PVR? I haven't looked into it, but I must admit we're loading big PNGs at the moment. Is there any way to see if this is indeed the case, or is it easier just to check it out?

But please tell me if I'm wrong, I'm happy to hear any ideas.

Proposed solutions

Mipmapped textures

Loading mipmapped textures, doing it like this:

- (id) initWithUIImage: (UIImage * const) image {
    glGenTextures(1, &name);
    //JNLogString(@"Recieved name(%d), binding texture", name);
    glBindTexture(GL_TEXTURE_2D, name);

    //Set the needed parameters for the texture
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); 
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

    //Load the image data into the texture

    glGenerateMipmap(GL_TEXTURE_2D);

    return self;
}

This doesn't seem to do anything for our FPS, I think this is because our textures are already roughly at the size they are rendered to on the screen, in most cases even 1:1.

Other solutions are welcome! I will try them out and post the results here

+2  A: 

If you are using very large textures, try to create mipmap textures. The cost is basically 1/3 of the original texture memory. I think they can be created with this call when setting up the textures.

glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);

Some calculations: If you have 3 textures 2048x2048 (max size) at 15 Hz you will have a texel throughput (if they are fully shown, ie downscaled to screen resolution) of 2048x2048x3x15 = 188,743,680 / sec which is around the value we see at glbenchmark.com for single fill rate (173 Mtexel/sec). But if you are using mipmap textures the texel throughput should be closer to the screen size resolution (1024x768) which should be something like 1/4 of the previous throughput.

epatel
I can't call glTexParameteri() with GL_GENERATE_MIPMAP, GCC cannot find that constant. I can however call glGenerateMipmap(), <EDIT: Currently checking framerate! Made a mistake before. xD> (code posted in question) I'm currently looking into it, but if our textures are roughly "realsize" as in, they don't need to be scaled down a lot, and will never be (2D game, no distance) do you still think this will speed it up then? Thanks for your help by the way.
Nick
Can't edit that one anymore, haha. Mipmapping did not do anything for our FPS, but thanks for introducing me to it, I'll leave the code with mipmaps since I'm pretty sure it's an improvement in general. :)
Nick
@Nick Yes, at texture rendering close to screen resolution mipmaping will have little effect. Another thing you could look at is how much state modification there are in every frame. Maybe you could look into putting unchanged state into a setup routine that is only called once and then have some smarter logic to handle state changes so they do not affect the rendering more than necessary.
epatel
+1  A: 

I had a branch in my fragment shader. I though that didn't put a lot of strain on it, but it did! Anyhow, that was the whole problem, I removed the branch and now my FPS has almost doubled.

Nick