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)
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;