为什么这个OpenGL ES代码在iPhone上速度慢?

时间:2009-01-16 10:46:03

标签: iphone opengl-es

我在学习OpenGL ES的过程中略微修改了iPhone SDK的GLSprite示例,但结果却很慢。即使在模拟器中(最糟糕的是),所以我必须做错事,因为它只有400个纹理三角形。

const GLfloat spriteVertices[] = {
  0.0f, 0.0f, 
  100.0f, 0.0f,  
  0.0f, 100.0f,
  100.0f, 100.0f
};

const GLshort spriteTexcoords[] = {
  0,0,
  1,0,
  0,1,
  1,1
};

- (void)setupView {
    glViewport(0, 0, backingWidth, backingHeight);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrthof(0.0f, backingWidth, backingHeight,0.0f, -10.0f, 10.0f);
    glMatrixMode(GL_MODELVIEW);

    glClearColor(0.3f, 0.0f, 0.0f, 1.0f);

    glVertexPointer(2, GL_FLOAT, 0, spriteVertices);
    glEnableClientState(GL_VERTEX_ARRAY);
    glTexCoordPointer(2, GL_SHORT, 0, spriteTexcoords);
    glEnableClientState(GL_TEXTURE_COORD_ARRAY);

    // sprite data is preloaded. 512x512 rgba8888   
    glGenTextures(1, &spriteTexture);
    glBindTexture(GL_TEXTURE_2D, spriteTexture);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, spriteData);
    free(spriteData);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

    glEnable(GL_TEXTURE_2D);
    glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
    glEnable(GL_BLEND);
} 

- (void)drawView {
  ..
    glClear(GL_COLOR_BUFFER_BIT);
    glLoadIdentity();
    glTranslatef(tx-100, ty-100,10);
    for (int i=0; i<200; i++) { 
        glTranslatef(1, 1, 0);
        glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
    }
  ..
}
每次触摸屏幕或移动屏幕上的手指并将tx,ty设置为触摸发生的x,y坐标时,都会调用 drawView。

我也尝试过使用GLBuffer,当预先生成翻译时,只有一个DrawArray,但性能相同(~4 FPS)。

=== EDIT ===

与此同时,我对此进行了修改,以便使用更小的四边形(尺寸:34x20),并且完成的重叠要少得多。在整个屏幕上有~400个四边形 - > 800个三角形。纹理大小为512x512 atlas和RGBA_8888,而纹理坐标为float。 代码在API效率方面非常难看:有两个MatrixMode更改以及两个加载和两个转换然后是三角形条带(quad)的drawarrays。 现在这产生~45 FPS。

5 个答案:

答案 0 :(得分:19)

(我知道这已经很晚了,但是我无法抗拒。无论如何我会发帖,以防其他人来这里寻求建议。)

这与纹理大小无关。我不知道为什么人们评价尼尔斯。他似乎对OpenGL管道存在根本性的误解。他似乎认为对于给定的三角形,整个纹理被加载并映射到该三角形上。反之亦然。

三角形映射到视口后,会进行栅格化。对于三角形覆盖的每个屏幕上像素,将调用片段着色器。默认的片段着色器(您正在使用的OpenGL ES 1.1)将查找最接近地映射((GL_NEAREST)的纹素到您正在绘制的像素。它可能会查找4个纹素,因为您使用更高质量的GL_LINEAR方法来平均最佳纹素。尽管如此,如果你的三角形中的像素数是100,那么你必须读取的最多纹理字节是4(查找)* 100(像素)* 4(每种颜色的字节。远远小于Nils所说的。令人惊讶的是,他听起来就像他实际上知道他在说什么一样。

WRT平铺架构,这在嵌入式OpenGL设备中很常见,以保留引用的局部性。我相信每个瓷砖都会暴露在每个绘图操作中,很快就会剔除它们中的大部分。然后瓷砖决定在自己上绘制什么。当你打开混合时,这会慢得多。因为您使用的是可能重叠并与其他图块混合的大三角形,所以GPU必须做很多额外的工作。如果不是渲染具有alpha边缘的示例正方形,而是渲染实际形状(而不是形状的方形图片),那么你可以关闭场景的这一部分的混合,我打赌这会加快速度极大。

如果你想尝试它,只需关闭混合,看看有多少东西加速,即使看起来不对。 glDisable(GL_BLEND);

答案 1 :(得分:3)

您的纹理是每像素512 * 512 * 4字节。这是一兆字节的数据。如果每帧渲染200次,则会产生每帧200兆字节的带宽负载。

大约4 fps,你只消耗800mb /秒的纹理读取。帧和Zbuffer写入也需要带宽。然后是CPU,也不要低估显示器的带宽要求。

嵌入式系统(例如您的iphone)上的RAM不如桌面PC那么快。你在这里看到的是带宽饥饿效应。 RAM无法更快地处理数据。

如何解决这个问题:

  • 选择一个理智的纹理大小。平均而言,每个像素应该有1个纹素。这样可以提供清晰的纹理。我知道 - 这并不总是可行的。使用常识。

  • 使用mipmap。这占用了额外空间的33%,但允许图形芯片选择使用较低分辨率的mipmap(如果可能)。

  • 尝试较小的纹理格式。也许你可以使用ARGB4444格式。这会使渲染速度加倍。另请参阅压缩纹理格式。解压缩不会导致性能下降,因为它是在硬件中完成的。事实恰恰相反:由于内存尺寸较小,图形芯片可以更快地读取纹理数据。

答案 2 :(得分:2)

我想我的第一次尝试只是一次糟糕(或非常好)的测试。 iPhone有一个PowerVR MBX Lite,它有一个基于图块的图形处理器。它将屏幕细分为较小的图块并使它们平行。现在在上面的第一种情况下,由于非常高的重叠,细分可能会有点疲惫。此外,由于距离相同,它们无法被剪裁,因此必须计算所有纹理坐标(这可以通过更改循环中的平移来轻松测试)。 同样由于重叠,并行性无法被利用,一些瓷砖无所事事,其余的(1/3)工作很多。

所以我认为,虽然内存带宽可能是一个瓶颈,但在本例中并非如此。问题更多是因为图形硬件的工作原理和测试设置。

答案 3 :(得分:0)

我不熟悉iPhone,但是如果它没有用于处理浮点数的专用硬件(我怀疑它没有),那么尽可能使用整数会更快。

我目前正在为Android开发(也使用OpenGL ES),例如我的顶点数组是int而不是float。我不能说它有多大差别,但我想这值得一试。

答案 4 :(得分:0)

  Apple对于iPhone的具体硬件规格非常守口如瓶,这对于我们这些来自控制台背景的人来说似乎很奇怪。但人们已经能够确定CPU是32位RISC ARM1176JZF。好消息是它有一个完整的浮点单元,所以我们可以像在大多数平台上那样继续编写数学和物理代码。

http://gamesfromwithin.com/?p=239