6

我在学习 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);
    }
  ..
}

每次触摸屏幕或移动屏幕上的手指时都会调用 drawView,并将 tx,ty 设置为发生触摸的 x,y 坐标。

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

===编辑===

同时我已经修改了这个,以便使用更小的四边形(尺寸:34x20)并且更少的重叠。大约 400 个四边形 -> 800 个三角形分布在整个屏幕上。纹理大小为 512x512 atlas 和 RGBA_8888,而纹理坐标为浮点数。该代码在 API 效率方面非常难看:有两个 MatrixMode 更改以及两个加载和两个平移,然后是三角形带(四边形)的绘制数组。现在这会产生 ~45 FPS。

4

5 回答 5

19

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

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

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

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

如果您想尝试一下,只需关闭混合并查看速度加快了多少,即使看起来不正确。glDisable(GL_BLEND);

于 2010-03-28T07:31:20.643 回答
3

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

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

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

如何解决这个问题:

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

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

  • 尝试较小的纹理格式。也许您可以使用 ARGB4444 格式。这将使渲染速度加倍。还要看看压缩的纹理格式。解压缩不会像在硬件中那样导致性能下降。事实上恰恰相反:由于内存中的尺寸更小,图形芯片可以更快地读取纹理数据。

于 2009-01-16T11:04:53.377 回答
2

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

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

于 2009-02-03T10:39:47.310 回答
0

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

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

于 2009-02-03T01:10:36.210 回答
0

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

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

于 2009-02-03T06:23:35.953 回答