优化iPhone的OpenGL ES填充速率

12

我在iPhone上开发了一个OpenGL ES游戏。我的帧率非常低,只有约20fps。在iPhone 3G上使用Xcode OpenGL ES性能工具,它显示:

渲染器利用率:95%至99%

Tiler利用率:约27%

我正在绘制很多相当大的图片,并进行许多混合操作。如果我减少要绘制的图片数量,帧率可以从约20提高到约40,尽管性能工具的结果保持不变(渲染器仍然达到最大值)。我认为我的iPhone 3G填充率受限制,但我不确定。

我的问题是:如何更精确地确定瓶颈在哪里?这是我的最大问题,我不知道花费所有时间的原因。如果是填充速度的问题,除了减少绘制之外,还有什么方法可以改善呢?

我正在使用纹理集。我尝试尽量减少图像绑定次数,但这并不总是可能的(绘制顺序,不能全部适配到一个1024x1024的纹理上等)。每帧我会做10个图像绑定。这似乎相当合理,但我可能错了。

我正在使用顶点数组和glDrawArrays。我的几何形状并不是很多。如果需要,我可以尝试更加精确地描述它们。每个图像由2个三角形组成,我会尽可能地批处理,但有时候(大约一半的时间)也会单独使用glDrawArrays调用绘制图像。除了图像之外,我还有大约60个三角形的几何形状被渲染在大约6个glDrawArrays中。我经常在调用glDrawArrays之前使用glTranslate。

切换到VBOs会提高帧率吗?我认为我的几何形状并不是很多,但可能出于其他原因而更快?

有哪些需要注意的问题可以降低性能?例如,应该避免glTranslate、glColor4g等操作吗?

我每帧在3个位置使用glScissor。每次使用由2个glScissor调用组成,一个用于设置,另一个用于将其重置为以前的状态。我不知道这里是否会对性能产生很大的影响。

如果我使用PVRTC,它能更快地渲染吗?目前我的所有图像都是GL_RGBA。我没有内存问题。
我的全屏纹理之一是256x256。使用480x320是否更好,以便手机不必进行任何缩放?还有其他关于纹理尺寸的一般性能建议吗?
以下是我绘制的大致想法,按此顺序:
1) 切换到透视矩阵。 2) 绘制全屏背景图像 3) 绘制带半透明的全屏图像(这个有一个滚动纹理)。 4) 绘制一些精灵。 5) 切换到正交矩阵。 6) 绘制一些精灵。 7) 切换到透视矩阵。 8) 绘制精灵和一些其他纹理几何体。 9) 切换到正交矩阵。 10) 绘制一些精灵(如,游戏HUD)。
步骤1-6绘制了一堆背景内容。第8步绘制了大部分游戏内容。步骤10绘制了HUD。
如您所见,有许多层,其中一些是全屏的,而某些精灵相当大(屏幕的1/4)。这些层使用半透明度,因此必须按背面到前面的顺序绘制它们。这进一步复杂化了需要在正交和透视之间绘制各种层。
如果需要,我很乐意提供其他信息。感谢任何有关性能提示或解决问题的一般建议!
编辑:
我添加了一些日志记录以查看我执行了多少个glDrawArrays调用,以及使用了多少数据。我每帧大约执行20个glDrawArray调用。通常,其中1到6个调用每个都有大约40个顶点。其余的调用通常只有2个顶点(一个图像)。我只使用glVertexPointer和glTexCoordPointer。

3
确定您是否填充受限的一个好方法是更改正在绘制的区域的大小。如果减小大小可以提高帧速率,则表示您的填充受到限制。 - Casey Rodarmor
7个回答

7
考虑到渲染器利用率基本上达到了100%,这表明瓶颈正在填充、纹理化和混合像素。旨在优化顶点处理(VBO和顶点格式)或CPU使用率(绘制调用批处理)的技术可能不会有所帮助,因为它们不会加速像素处理。
您最好的选择是减少要填充的像素数量,并查看可以更好地利用第一代设备上非常有限的内存带宽的不同纹理格式。尽可能使用PVRTC纹理,并在其他情况下使用16位未压缩纹理。

1
感谢。将全屏纹理切换到PVR后,帧率提升了约4fps,效果不错。我知道它们使用的内存较少,但不确定是否会有性能提升。此外,我将尽可能更改图像为GL_NEAREST,并再次获得2-3fps的提升。 - NateS
1
更新:使用CADisplayLink也稍微有所帮助。 - NateS

3
请参考苹果公司的《纹理数据处理最佳实践》和《顶点数据处理最佳实践》,以及《iPhone OS OpenGL ES编程指南》。它们强烈建议(其他人也是如此)使用PVRTC来压缩纹理,因为它们可以提供8:1或16:1的压缩比,超过标准未压缩纹理。除了mipmapping外,您似乎正在执行使用纹理集的其他推荐优化。
你似乎没有几何限制,因为(正如我在这个问题中发现的那样),平铺工具利用率统计数据似乎表明几何大小正在造成多大的瓶颈。然而,iPhone 3G S(以及第三代iPod touch和iPad)支持硬件加速的VBOs,所以你可以尝试一下它们,看看它们对性能的影响。它们可能没有压缩纹理那么有效,但是它们很容易实现。

2

对于(大多数)3G 用户来说,一个重要的胜利将是您正在使用的纹理过滤。请检查您是否正在使用“三线性”过滤,并将其更改为“双线性”。

确保您按以下方式设置纹理:

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

而不是这样:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

哈利


2
我想补充一个额外的答案,将帧缓冲区支持的格式从32位改为16位。
if ((self = [super initWithCoder:coder])) 
    {
    eaglLayer = (CAEAGLLayer *)self.layer;
    eaglLayer.opaque = YES;
    eaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:
                                            [NSNumber numberWithBool:NO], 
                                            kEAGLDrawablePropertyRetainedBacking, 
                                            kEAGLColorFormatRGB565,  //kEAGLColorFormatRGBA8 = large frame buff 32bit  // kEAGLColorFormatRGB565 = 16bit frame buffer.
                                            kEAGLDrawablePropertyColorFormat, 
                                            nil];        
}

这让我意识到问题的根源是XCode分析器。它一直在抱怨使用了太大的帧缓冲区,最终我在初始化部分找到了它。 http://developer.apple.com/library/ios/#documentation/iPhone/Reference/EAGLDrawable_Ref/EAGLDrawable/EAGLDrawable.html 。 这个简单的变化使我的iPad、Retina和iPod上的游戏达到了60FPS。虽然我三天前才发现这个问题,但我还没有重新发布这些游戏。我不打算以60fps的速度发布,对于休闲游戏来说30fps就足够了。我发现我的音效会降低帧率,所以如果我决定采用60fps,要么重新采样,要么在另一个线程中播放sfx,或者其他解决方案来保持帧率。别忘了丢弃未用于显示的缓冲区。
if (fiOSver >= 4.0f) {
    const GLenum discards[]  = {GL_DEPTH_ATTACHMENT_OES};
    glDiscardFramebufferEXT(GL_FRAMEBUFFER_OES,1,discards);
}

[m_oglContext presentRenderbuffer:GL_RENDERBUFFER_OES];

1
在类似的情况下(将2D冒险游戏移植到iPad上),我的3GS版本以大约60FPS运行,但放到iPad上后,它掉到了20FPS(让我惊呆了)。
结果发现其中一个小问题是PVR卡不支持GL_ALPHA_TEST;在PC上,这实际上有轻微的正面效果(特别是对于旧的英特尔芯片),但它们会影响iPhone的填充率。需要将其更改为

 glDisable(GL_ALPHA_TEST);

给了我立即100%的FPS提升(高达40 FPS)。一行代码不错.. :)

Allan


哇,这真是太有趣了。通过关闭alpha测试,在iPad上将帧率提高一倍。值得一提的是,我们保持了alpha混合,而且我们的游戏非常依赖alpha,所以我们在各个地方都使用混合,你甚至看不到alpha测试的效果。 - Danny Parker
给我一个苹果未记录的错误代码“1280”。不管它是什么意思,谢谢你,苹果。 - Adam

0
iPhone平台上最大的性能杀手是绘制调用和状态更改的数量。如果你进行的绘制调用或状态更改超过20个,那么你将遇到性能瓶颈。
批处理和纹理图集是你的好朋友。

嗨,谢谢。我编辑了我的问题结尾,加入了关于我正在进行的调用数量的附加信息。它似乎大约处于您提到的合理范围之内。我将研究更多单个图片的批处理,但我并不抱太高的希望,认为这可以进一步改善。 - NateS
1
如果你认为填充速率受限,可以尝试使用PVRTC。@NateS - Yann Ramin
在这种情况下,那可能不是最好的建议。减少绘制调用的数量是为了减少在OpenGL实现中消耗的CPU时间量。鉴于渲染器已经充分利用,绘制调用批处理可能不会改善这种情况。可以使用Instruments或Shark来分析CPU使用情况,并确定有多少CPU时间未被使用以及OpenGL驱动程序内部使用了多少CPU时间。 - Frogblast
经过更多的优化以减少绘制调用,我获得了约6帧每秒的提升,这是非常显著的。这让我感到惊讶,因为我并没有期望太大的收益,因为我觉得我的填充速度受限制。 - NateS

0

根据我在旧的Windows Mobile设备上使用openGL ES的经验,处理速度约为600MHz,通常降低渲染窗口分辨率可以提高渲染速度。

最近我的测试表明,在逐帧渲染时需要性能监控,以收集当前性能下可以显示多少fps以及当前应用的分辨率。

我希望在渲染视图中保持一个监控算法,以平衡分辨率和帧率,同时运行游戏渲染引擎是一个好的实践。

根据所需的帧率数量,您应该牺牲渲染视图分辨率,以获得最佳性能,并在大多数具有不同硬件和软件性能的设备上进行优雅的降级。

您可能需要手动控制分辨率,就像本文所解释的那样。

http://www.david-amador.com/2010/09/setting-opengl-view-for-iphone-4-retina-hi-resolution/


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接