I have an animated background layer that is built on top of OpenGL ES 2.0 and I am using GLKView as graphics container and GLKViewController as controller. For drawing I use GLKBaseEffect.

I introduced a sprite class that can load png-files as textures, manipulate the sprite (SRT) and some additional properties like alpha blending and so on.

I am wondering how I could optimise my program, since the frame-rate drops on my iPhone 4S to about 25 FPS when displaying 50 sprites (all with the same texture/png-file!) with a size of 128x128 px each.

In the following sections I have listed the important parts of the program. Currently I call `glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)` for each of the 50 sprites for each of the frames (target frame rate is 60); which is equally to 3000 calls per second.

**Could that be the bottle neck ? How could I optimise this ?**

This is how I initialise the sprite array (GLKViewController.m):

```
- (void)initParticles {
    if(sprites==nil) {
        sprites = [NSMutableArray array];
        for (int i=0; i<50; i++) {
            Sprite* sprite = [[Sprite alloc] initWithFile:@"bubble" extension:@"png" effect:effect];
            // configure some sprite properties [abbreviated]
            [sprites addObject:sprite];
        }
    }
}

```

This is the rendering function (GLKViewController.m):

```
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect {
    glClearColor(0.0, 0.0, 1.0, 1.0);
    glClear(GL_COLOR_BUFFER_BIT);
    // render the bubbles
    for (Sprite* sprite in sprites) {
        [sprite render];
    }
}

```

Here are some important parts of the sprite class (Sprite.m):

```
- (id)initWithFile:(NSString *)filename extension:(NSString*)extension effect:(GLKBaseEffect *)effect {
    if(self = [self init]) {
        self.effect = effect;
        NSDictionary* options = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], GLKTextureLoaderOriginBottomLeft, nil];
        NSError* error = nil;
        NSString *path = [[NSBundle mainBundle] pathForResource:filename ofType:nil];
        self.textureInfo = [GLKTextureLoader textureWithContentsOfFile:path options:options error:&error];
        if (self.textureInfo == nil) {
            NSLog(@"Error loading file: %@", [error localizedDescription]);
            return nil;
        }
        TexturedQuad newQuad;
        newQuad.bl.geometryVertex = GLKVector2Make(0, 0);
        newQuad.br.geometryVertex = GLKVector2Make(self.textureInfo.width, 0);
        newQuad.tl.geometryVertex = GLKVector2Make(0, self.textureInfo.height);
        newQuad.tr.geometryVertex = GLKVector2Make(self.textureInfo.width, self.textureInfo.height);
        newQuad.bl.textureVertex = GLKVector2Make(0, 0);
        newQuad.br.textureVertex = GLKVector2Make(1, 0);
        newQuad.tl.textureVertex = GLKVector2Make(0, 1);
        newQuad.tr.textureVertex = GLKVector2Make(1, 1);
        self.quad = newQuad;
    }
    return self;
}

- (void)render {
    [self applyBaseEffect];
    long offset = (long)&_quad;
    glEnableVertexAttribArray(GLKVertexAttribPosition);
    glEnableVertexAttribArray(GLKVertexAttribTexCoord0);
    glVertexAttribPointer(GLKVertexAttribPosition, 2, GL_FLOAT, GL_FALSE, sizeof(TexturedVertex), (void*) (offset + offsetof(TexturedVertex, geometryVertex)));
    glVertexAttribPointer(GLKVertexAttribTexCoord0, 2, GL_FLOAT, GL_FALSE, sizeof(TexturedVertex), (void*) (offset + offsetof(TexturedVertex, textureVertex)));
    glBlendColor(1.0, 1.0, 1.0, self.alpha);
    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
    glDisableVertexAttribArray(GLKVertexAttribPosition);
    glDisableVertexAttribArray(GLKVertexAttribTexCoord0);
}

- (void)applyBaseEffect {
    self.effect.texture2d0.name = self.textureInfo.name;
    self.effect.texture2d0.envMode = GLKTextureEnvModeModulate;
    self.effect.texture2d0.target = GLKTextureTarget2D;
    self.effect.texture2d0.enabled = GL_TRUE;
    self.effect.useConstantColor = GL_TRUE;
    self.effect.constantColor = GLKVector4Make(self.tint.r*self.alpha, self.tint.g*self.alpha, self.tint.b*self.alpha, self.alpha);
    self.effect.transform.modelviewMatrix = GLKMatrix4Multiply(GLKMatrix4Identity, [self modelMatrix]);
    [self.effect prepareToDraw];
}

- (GLKMatrix4)modelMatrix {
    GLKMatrix4 modelMatrix = GLKMatrix4Identity;
    modelMatrix = GLKMatrix4Translate(modelMatrix, self.position.x, self.position.y, 0);
    modelMatrix = GLKMatrix4Rotate(modelMatrix, self.rotation, 0, 0, 1);
    modelMatrix = GLKMatrix4Scale(modelMatrix, self.scale, self.scale, 0);
    modelMatrix = GLKMatrix4Translate(modelMatrix, -self.normalSize.width/2, -self.normalSize.height/2, 0);
    return modelMatrix;
}

```

EDIT-1: Here are some performance indicators (seems to be GPU-bound)

[![enter image description here](https://i.stack.imgur.com/83Y1E.png)](https://i.stack.imgur.com/83Y1E.png)

EDIT-2: The frame rate improved from 25 to 45 on my iPhone 4S when I added the `view.drawableMultisample` line regardless of which line I used (none / 4x). Strange - my rendering code seems not to be affected by MSAA, rather on the contrary.

```
GLKView *view = (GLKView*)self.view;
view.context = context;
view.drawableMultisample = GLKViewDrawableMultisampleNone;
//view.drawableMultisample = GLKViewDrawableMultisample4x;

```