0

OpenGL ES を使用して、iPhone 4S の各フレームに 2560 個の非常にスリムなポリゴンを描画します。問題は、フレームレートが 30 前後になることです。これは、私の好みでは十分に滑らかではありません。それよりも速いほうがいいと思います。

  1. そうですか?

  2. 改善できる点を見つけるのを手伝ってください。

更新: メイン スレッドでレンダリングを行います。レンダリング操作を実行するスレッドに関する推奨事項はありますか?

少し背景: iPhone ビュー座標でサイズ 320x200 のスムーズなスクロール (ターゲットは 60 FPS) の波形を作成しようとしているため、Retina ディスプレイでは 640x400 ピクセルです。テスト デバイスは iPhone 4S です。iOS 6 と 6.1 では、通常の UIKit 描画操作でこれを簡単に実現できました。しかし、デバイスを iOS 7 にアップデートしてからかなり遅くなったので、OpenGL ES を使用することにしました。OpenGL ES を使用すると 2D 描画が高速になるという記述を何度も読んだためです。

OpenGL ES 2.0 で波形の描画を実装しましたが、UIKit よりもデバイス上でわずかに高速です。また、UIKit の場合と同様に、描画されるピクセルの数によって速度が大きく変わるため、何が起こっているのか疑問に思います。

波形はバー/長方形から構成され、それぞれの幅は正確に 1 ピクセルです。ピクセル列ごとに 2 つのバーを描画します。各バーは 2 つのポリゴンで構成されています。つまり、フレームごとに 1280 個のバー、つまり 2560 個のポリゴンを描画します。ポリゴンは非常にスリムです。それぞれの幅は最大で 1 ピクセルです。これは、OpenGL ES で 60FPS で描画するのに問題ないと思います。

私はこのように1本の棒を描きます:

- (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 フレームの実行時間は約 1 ミリ秒です。これは、理論的には 1000 FPS まで上がる可能性があることを意味します。それ以外の時間 (約 33ms) は描画に費やされます。

4

1 に答える 1

2

ダニエルのリクエストにより、質問を締めくくる回答としてこれを投稿しています。

glDrawArrays()上記のコードでは、ボックスごとに呼び出しを使用しているように見えます。これは、多くのボックスでかなりのオーバーヘッドが発生します。

これにアプローチするより効率的な方法は、シーンのすべての頂点、または少なくともより大きなボックスのグループを含む VBO (おそらく動的に更新されるもの) を使用し、それらすべてを 1 回の呼び出しで描画することです。

rickster が指摘しているように、iOS 7 ではインスタンス化の優れたサポートが追加されており、ここでも役立つ可能性があります。

バックグラウンド スレッドでレンダリングするかどうかについては、私の経験では、OpenGL ES シーンをバックグラウンド スレッドでレンダリングすると、パフォーマンスが大幅に向上します (特にマルチコア デバイスでは 10 ~ 40%)。シリアル GCD キューを使用すると、安全な方法でそれを行うのも非常に簡単です。

于 2013-10-25T22:42:24.957 に答える