0

我使用 OpenGL ES 在 iPhone 4S 上为每一帧绘制了 2560 个非常纤细的多边形。问题是我的帧率在 30 左右,这对我的口味来说不够流畅。我认为它应该比这更快。

  1. 那正确吗?

  2. 请帮助我找出可以改进的地方。

更新:我在主线程上进行渲染。关于执行渲染操作的线程是否有任何建议?

一点背景:我正在尝试在 iPhone 视图坐标中制作大小为 320x200 的平滑滚动(目标是 60 FPS)波形,因此在视网膜显示器上为 640x400 像素。我的测试设备是 iPhone 4S。在 iOS 6 和 6.1 中,我可以通过正常的 UIKit 绘图操作轻松实现这一点。然而,由于我将设备更新到 iOS 7,它变得慢了很多,所以我决定使用 OpenGL ES,因为我读了很多次,它可以更快地进行 2D 绘图。

我使用 OpenGL ES 2.0 实现了绘制波形,但现在它在设备上比使用 UIKit 快一点。和 UIKit 一样,速度很大程度上取决于被绘制的像素数,这让我想知道发生了什么。

波形由条形/矩形组成,每个矩形的宽度正好是 1 个像素。我每像素列绘制两个条,每个条由两个多边形组成,这意味着我为每帧绘制 1280 个条或 2560 个多边形。多边形非常纤细。它们中的每一个的宽度最多为 1 个像素。我认为使用 OpenGL ES 以 60FPS 绘制应该没问题。

我画一个这样的条:

- (void) glFillRect: (Float32)x0 : (Float32)y0 : (Float32)x1 : (Float32)y1 {
    glEnableVertexAttribArray(GLKVertexAttribPosition);

    GLfloat vertices[8];
    glVertexAttribPointer(GLKVertexAttribPosition, 2, GL_FLOAT, GL_FALSE, 0, vertices);

    GLfloat* vp = vertices;
    *vp++ = x0; *vp++ = y0;
    *vp++ = x1; *vp++ = y0;
    *vp++ = x0; *vp++ = y1;
    *vp++ = x1; *vp++ = y1;
    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

    glDisableVertexAttribArray(GLKVertexAttribPosition);
}

调用上述方法的代码如下。_maxDrawing和 _avgDrawing是我的效果,在应用启动时是这样组成的:

_maxDrawing = [[GLKBaseEffect alloc] init];
_maxDrawing.useConstantColor = GL_TRUE;
_maxDrawing.constantColor = GLKVector4Make(0.075f, 0.1f, 0.25f, 1.0f);

我后来调整了投影矩阵,使我的 OpenGL ES 绘图坐标与我的视图的视图坐标对齐,这是 2D 绘图的标准方法。

[_maxDrawing prepareToDraw];
x_Cu = [self transformViewXToWaveformX:rect.origin.x];
for (Float32 x_Vu = rect.origin.x; x_Vu < viewEndX_Vu; x_Vu += onePixelInViewUnits) {
    x_Cu += onePixelInContentUnits;
    if (x_Cu < 0 || x_Cu >= waveformEndX_Cu) {
        continue;
    }

    SInt64 frameIdx = (SInt64) x_Cu;
    CBWaveformElement element;
    element = [self.dataSource getElementContainingFrame:frameIdx];

    prevMax = curMax;
    curMax = futureMax;
    futureMax = element.max;
    smoothMax = prevMax * 0.25 + curMax * 0.5 + futureMax * 0.25;
    if (smoothMax < curMax)
        smoothMax = curMax;

    Float32 barHeightHalf = smoothMax * heightScaleHalf;
    Float32 barY0 = viewHeightHalf - barHeightHalf;
    Float32 barY1 = viewHeightHalf + barHeightHalf;
    [self glFillRect: x_Vu : barY0 : x_Vu + onePixelInViewUnits : barY1];
}

[_avgDrawing prepareToDraw];
x_Cu = [self transformViewXToWaveformX:rect.origin.x];
for (Float32 x_Vu = rect.origin.x; x_Vu < viewEndX_Vu; x_Vu += onePixelInViewUnits) {
    x_Cu += onePixelInContentUnits;
    if (x_Cu < 0 || x_Cu >= waveformEndX_Cu) {
        continue;
    }

    SInt64 frameIdx = (SInt64) x_Cu;
    CBWaveformElement element;
    element = [self.dataSource getElementContainingFrame:frameIdx];

    Float32 barHeightHalf = element.avg * heightScaleHalf;
    Float32 barY0 = viewHeightHalf - barHeightHalf;
    Float32 barY1 = viewHeightHalf + barHeightHalf;
    [self glFillRect: x_Vu : barY0 : x_Vu + onePixelInViewUnits : barY1];
}

当我取出所有的 OpenGL 调用时,一帧的执行时间约为 1 毫秒,这意味着它理论上可以达到 1000 FPS。所有其他时间(大约 33 毫秒)都用于绘图。

4

1 回答 1

2

根据丹尼尔的要求,我将其发布为结束问题的答案。

在上面的代码中,您似乎正在glDrawArrays()为每个框使用一个调用。这会导致大量盒子的大量开销。

解决此问题的一种更有效的方法是使用包含场景的所有顶点的 VBO(可能是动态更新的),或者至少包含更大的一组框,并通过一次调用来绘制所有这些顶点。

正如 rickster 所指出的,iOS 7 为实例化添加了一些很好的支持,这在这里也可以提供帮助。

关于是否在后台线程上渲染,根据我的经验,在后台线程上渲染我的 OpenGL ES 场景时,我通常会看到显着的性能提升(10-40%,尤其是在多核设备上)。使用串行 GCD 队列,以安全的方式也很容易做到这一点。

于 2013-10-25T22:42:24.957 回答