Metal API を使用して、Apple A7 GPU で多数の非常に小さな 2D クワッドをできるだけ高速にレンダリングしようとしています。GPU のトライアングル スループットの数値を調べて (たとえば、こちら)、Apple がキーノート デモ中に画面上に 1M を超えるトライアングルを引用していることから、60 fps でフレームあたり 500,000 のようなクワッドをレンダリングできると期待しています。それらのすべてが (z バッファーによって隠されずに画面上で) 表示され、小さい (ラスタライザーにとってはトリッキー) ことを考えると、おそらく少し少ないので、これは GPU が非常によく最適化されているユースケースではない可能性があります。おそらく、Apple のデモは 30fps で実行されていたので、最大 200,000 が実行可能であるとしましょう。確か10万…ですよね?
ただし、私のテスト アプリでは、最大値はわずか 20,000 です。それ以上であり、iPad Air ではフレームレートが 60 を下回りました。100,000 クワッドの場合、14 fps、つまり 2.8M 三角形/秒のスループットで実行されます (AnandTech の記事で引用されている 68.1M の画面上の三角形と比較してください!)。
単純なフラグメント シェーダーを使用してクワッドを 1 ピクセル小さくしても、パフォーマンスは向上しません。したがって、これは頂点バウンドであると想定でき、Xcode の GPU レポートは一致します ("Tiler" は 100% です)。頂点シェーダーも簡単で、わずかなスケーリングと変換計算を行うだけなので、ボトルネックは固定機能ステージであると想定しています...?
背景情報として、インスタンスごとに 1 つのクワッド、つまりインスタンスごとに 4 つの頂点を使用して、単一のインスタンス化された描画呼び出しを使用してすべてのジオメトリをレンダリングしています。クワッドの位置は、頂点シェーダーのインスタンス ID によってインデックス付けされた別のバッファーから適用されます。他のいくつかの方法も試しました (すべての頂点が事前に変換されたインスタンス化されていない、インスタンス化された + インデックスが作成されたなど) が、役に立ちませんでした。複雑な頂点属性、バッファー/サーフェス形式、またはドライバー/GPU で遅いパスにヒットする可能性が高いと思われる他のものはありません (もちろん確信はありませんが)。ブレンディングはオフです。他のほとんどすべてはデフォルトの状態です (ビューポート、シザー、ztest、カリングなど)。
アプリケーションは Swift で書かれていますが、問題にならないことを願っています ;)
私が理解しようとしているのは、(「適切な」3D シーンとは対照的に) このようなクワッドをレンダリングするときに私が見ているパフォーマンスが期待されるかどうか、または宣伝されている三角形に近づくためにいくつかのより高度な技術が必要かどうかです。スループット。ここでの制限のボトルネックは何だと思われますか?
また、Metal よりも OpenGL の方が高速である理由を誰かが知っている場合 (私は試したことがなく、理由も思いつきません)、それも聞いてみたいと思います。
ありがとう
編集: シェーダー コードを追加します。
vertex float4 vertex_shader(
const constant float2* vertex_array [[ buffer(0) ]],
const device QuadState* quads [[ buffer(1) ]],
constant const Parms& parms [[ buffer(2) ]],
unsigned int vid [[ vertex_id ]],
unsigned int iid [[ instance_id ]] )
{
float2 v = vertex_array[vid]*0.5f;
v += quads[iid].position;
// ortho cam and projection transform
v += parms.cam.position;
v *= parms.cam.zoom * parms.proj.scaling;
return float4(v, 0, 1.0);
}
fragment half4 fragment_shader()
{
return half4(0.773,0.439,0.278,0.4);
}