6

アップデート

OpenGL ですべてを描画することで、CG の制限を回避しました。まだいくつかの不具合がありますが、これまでのところ、はるかに高速に動作しています。

いくつかの興味深い点:

  • GLKView: これは iOS 固有のビューであり、OpenGL コンテキストとレンダリング ループの設定に非常に役立ちます。iOS を使用していない場合は、ご自身で行ってください。
  • シェーダーの精度: OpenGL ES (2.0) の現在のバージョンのシェーダー変数の精度は16 ビットです。これは私の目的には少し低かったので、16 ビット変数のペアを使用して 32 ビット演算をエミュレートしました。
  • GL_LINES: OpenGL ES は単純な線をネイティブに描画できます。あまりよくありません(ジョイントもキャップもありません。下のスクリーンショットの上部にある紫/灰色の線を参照してください)。これを改善するには、カスタム シェーダーを作成し、各線を三角形のストリップに変換して、動作します!(おそらく、Canvas2D が GPU で高速化されているとブラウザが言うとき、それがブラウザのやり方です)

                                                      レンダリング例

  • できるだけ描きません。それは理にかなっていると思いますが、たとえばビューポートの外側にあるものをレンダリングすることを避けることができることがよくあります。
  • OpenGL ES は塗りつぶされたポリゴンをサポートしていないため、自分でテッセレートする必要があります。iPhone-GLUの使用を検討してください。これは MESA コードのポートであり、かなり優れていますが、少し使いにくいです (標準の Objective-C インターフェイスはありません)。

元の質問

drawRectユーザーが指でパンすると更新されるスクロール ビューのメソッドで、多くの CGPaths (通常は 1000 以上) を描画しようとしています。ブラウザー用の JavaScript で同じアプリケーションを使用しており、それを iOS ネイティブ アプリに移植しようとしています。

iOS テスト コードは次のとおりです (100 行の操作でpath、事前に作成されていCGMutablePathRefます)。

- (void) drawRect:(CGRect)rect {
    // Start the timer
    BSInitClass(@"Renderer");
    BSStartTimedOp(@"Rendering");

    // Get the context
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSetLineWidth(context, 2.0);
    CGContextSetFillColorWithColor(context, [[UIColor redColor] CGColor]);
    CGContextSetStrokeColorWithColor(context, [[UIColor blueColor] CGColor]);
    CGContextTranslateCTM(context, 800, 800);

    // Draw the points
    CGContextAddPath(context, path);
    CGContextStrokePath(context);

    // Display the elapsed time
    BSEndTimedOp(@"Rendering");
}

JavaScript では、参考までに、コードは (10000 行の操作で) です。

window.onload = function() {
  canvas = document.getElementById("test");
  ctx = canvas.getContext("2d");

  // Prepare the points before drawing
  var data = [];
  for (var i = 0; i < 100; i++) data.push ({x: Math.random()*canvas.width, y: Math.random()*canvas.height});

  // Draw those points, and write the elapsed time
  var __start = new Date().getTime();
  for (var i = 0; i < 100; i++) {
    for (var j = 0; j < data.length; j++) {
      var d = data[j];
      if (j == 0) ctx.moveTo (d.x, d.y);
      else ctx.lineTo(d.x,d.y)
    }
  }
  ctx.stroke();
  document.write ("Finished in " + (new Date().getTime() - __start) + "ms");
};

現在、私は iOS よりも JavaScript の最適化に長けていますが、プロファイリングを行った結果、CGPath のオーバーヘッドは JavaScript に比べて絶対に信じられないほど悪いようです。両方のスニペットは、実際の iOS デバイス上でほぼ同じ速度で実行され、JavaScript コードの行操作の数は Quartz2D コードの 100 倍です!

編集: これは Instruments の時間プロファイラーの上部です:

Running Time   Self             Symbol Name
6487.0ms       77.8%  6487.0    aa_render
449.0ms        5.3%   449.0     aa_intersection_event
112.0ms        1.3%   112.0     CGSColorMaskCopyARGB8888
73.0ms         0.8%   73.0      objc::DenseMap<objc_object*, unsigned long, true, objc::DenseMapInfo<objc_object*>, objc::DenseMapInfo<unsigned long> >::LookupBucketFor(objc_object* const&, std::pair<objc_object*, unsigned long>*&) const
69.0ms         0.8%   69.0      CGSFillDRAM8by1
66.0ms         0.7%   66.0      ml_set_interrupts_enabled
46.0ms         0.5%   46.0      objc_msgSend
42.0ms         0.5%   42.0      floor
29.0ms         0.3%   29.0      aa_ael_insert

コードがネイティブであるため、iOSではこれがはるかに高速になるはずだと私は理解しています...だから、知っていますか:

  • ...ここで何が間違っていますか?
  • ...そして、リアルタイムで多くの線を描画するための別のより良い解決策があれば?

どうもありがとう!

4

2 に答える 2

0

質問で説明したように、OpenGL を使用することが正しい解決策です。理論的には、OpenGL を使用してあらゆる種類のグラフィック描画をエミュレートできますが、すべての形状アルゴリズムを自分で実装する必要があります。たとえば、線のエッジ コーナーを自分で拡張する必要があります。OpenGLにはの概念はありません。線画はユーティリティ機能のようなもので、ほとんどデバッグ専用です。すべてを三角形の集合として扱う必要があります。

ほとんどの描画には 16 ビットの float で十分だと思います。大きな数値の座標を使用している場合は、スペースを複数のセクターに分割して、座標の数値を小さくすることを検討してください。フロートの精度は、非常に大きくなったり非常に小さくなったりすると悪くなります。

アップデート

UIKit を OpenGL ディスプレイで表示しようとすると、すぐにこの問題に遭遇すると思います。残念ながら、私もまだ解決策を見つけることができませんでした。

于 2012-10-01T18:44:39.067 に答える