2015-08-14 84 views
0

我有一個構建在OpenGL ES 2.0之上的動畫背景層,我使用GLKView作爲圖形容器,並使用GLKViewController作爲控制器。對於繪圖我使用GLKBaseEffect。OpenGL ES 2.0:如何提高繪製精靈的幀率?

我引入了一個sprite類,它可以將png文件加載爲紋理,操縱sprite(SRT)和一些其他屬性,如alpha混合等等。

我想知道如何優化我的程序,因爲在顯示50個sprites(全部具有相同的紋理/ png文件!)時,我的iPhone 4S的幀率下降到大約25 FPS,尺寸爲128x128像素每。

在下面的章節中,我列出了程序的重要部分。目前,我爲每個幀的50個精靈中的每一個調用glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)(目標幀速率爲60);這相當於每秒3000個呼叫。

那可能是瓶頸嗎?我怎麼能優化這個?

這是我如何初始化精靈陣列(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]; 
     } 
    } 
} 

這是渲染功能(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]; 
    } 
} 

下面是一些重要部件精靈類(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; 
} 

編輯-1:這裏有一些性能指標(似乎是GPU結合的)

enter image description here

EDIT-2:當我添加了view.drawableMultisample線無論哪個線的我用(無/ 4X從25提高到45在我的iPhone 4S幀速率)。奇怪 - 我的渲染代碼似乎沒有受到MSAA的影響,恰恰相反。

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

有精靈相同的特徵(頂點,紋理等)? – xcesco

+0

是的,它們都是一樣的(一些氣泡從底部移動到頂部)。我只用'effect.transform.modelviewMatrix'來操作它們。 – salocinx

+1

爲了知道您的應用是CPU還是GPU綁定,您需要首先進行配置。請檢查[this](https://developer.apple.com/library/ios/documentation/3DDrawing/Conceptual/OpenGLES_ProgrammingGuide/ToolsOverview/ToolsOverview.html),然後從XCode運行您的應用程序並在此處添加結果的屏幕截圖。我希望看到「利用率」和「幀時間」的值 –

回答

0

經過兩個小時的深入研究後,我終於找到了造成大規模業績的原因。如評論中所述,性能下降的重要性隨着視圖控制器中包含的按鈕數量的增加而增加。因爲我裝修我的按鈕帶有圓角和陰影,我以後看......這是我對裝飾UI元素的目的UIView類原始類別的一部分:

#import "UIView+Extension.h" 

@implementation UIView (Extension) 

- (void)styleViewWithRoundedEdges:(BOOL)rounded shadowed:(BOOL)shadowed { 
    if (rounded) { 
     self.layer.cornerRadius = 3.0; 
    } 
    if (shadowed) { 
     self.layer.shadowColor = [UIColor blackColor].CGColor; 
     self.layer.shadowOffset = CGSizeMake(2.0, 2.0); 
     self.layer.shadowOpacity = 0.25; 
     self.layer.shadowRadius = 1.0; 
    } 
} 

@end 

只要我關掉了陰影,幀率變成了恆定的60 FPS。

快速搜索引導我到this SO thread

添加這兩行解決了這個問題:

self.layer.shouldRasterize = YES; 
self.layer.rasterizationScale = UIScreen.mainScreen.scale; 

我現在呈現容易精靈100與線性+徑向背景漸變着色器上的iPhone 4S用60 FPS :-D組合。

但是非常感謝你們的耐心和幫助!讓我知道我什麼時候能夠回報你的青睞!

2

爲了提高性能,您必須避免冗餘代碼。可能你爲每個精靈設置了着色器程序。但是總是使用相同的着色器來繪製精靈,所以你可以避免每次都設置它。您可以對頂點和紋理座標使用這種優化。對於每個精靈,只需計算矩陣MVP(模型 - 視圖 - 投影)。

在僞代碼的方法「並條機」:

... 
[set sprite program shader] 
[set vertex coordinates] 
[set texture coordinates] 
[set textures ] 
for each sprite 
    [calculate matrix MVP] 
    [draw sprite] 
end for 
... 

另一種優化方法是使用頂點緩衝對象,但在你的情況下,我認爲它比精靈批量優化較低的優先級。

我在Android上工作,所以我不能幫你用示例代碼。不過,我建議你看看Libgdx來檢查他們如何實現精靈批處理。

+0

非常感謝您的建議。同時我們發現,該應用程序是GPU綁定的。我認爲這意味着,可能VBO/VBA可以幫助提高性能,而不是改變運行在CPU上的代碼。 – salocinx

+1

當然,但如果您減少對OpenGL狀態機的更改,您將節省大量時間。我在android opengl壁紙上遇到了同樣的問題,並且我獲得了很好的性能提升。如果可以的話,嘗試一下。 – xcesco

+0

好的,謝謝你的建議,我會盡快嘗試,並在這裏提供一些反饋。非常感謝你! – salocinx

1

您的性能計數器似乎表明您是GPU填充率限制。您可以檢查您的bubble.png資產是否充分利用其紋理空間,不必要的透支可能是您問題的一部分。

你可以在photoshop/gimp中打開它,並autocrop圖像,如果它從邊緣刪除任何texels,那麼你有一些浪費的空間。如果你能消除浪費的空間,你可以減少透支。

你提到氣泡是128x128,但不清楚它是紋理尺寸還是屏幕尺寸。縮小屏幕尺寸可以提高性能。

除此之外,您可能需要深入研究Sprite類並查看片段着色器實際上在做什麼。選擇更高效的紋理格式並啓用mipmapping也可能有所幫助。