Twitter @HigenekoTwitter #XNA
前回はCPUのボトルネックを割り出す方法を紹介しましたが、今回はGPUのボトルネック部分を割り出す方法を紹介します。
今日、取調室に呼び出されたのはGPUです。CPUと違ってGPUは決して自供することはありません。どんな質問を投げかけてもかたくなに黙秘権を行使します。ただし、いくつかの質問をすると微妙に表情が変わることが判りました。
グラフィクスプログラマーはこのGPUの微妙な表情の違いからGPUのボトルネック部分を探し出す必要があります。
GPUのボトルネックを見つけ出すにはGPUのパイプラインがどのようになっているのかを知る必要があります。実際には様々なグラフィクスカードがあり、それぞれに多少の違いはありますが、共通して以下の重要なステージがあります。
これらのいずれもがボトルネックになる可能性があり、このボトルネックを知ることは非常に役立ちます。例えば頂点シェーダーがボトルネックになっていると判っているのなら、テクスチャフェッチ数を減らすよりも頂点シェーダー内の命令数を減らすほうが効果的です。また、ピクセルシェーダーがボトルネックとなっている状態であれば、フレームレートを減らすことなくモデルのポリゴン数を増やすこともできると判断できます。
では、それぞれのパイプライン・ステージでどんな要素がパフォーマンスに影響を与えるのでしょうか?
GPUのボトルネックを見つけるには、これらの要素をCPUで処理される部分に大きな変化を起こすことなく変える必要があります(CPUパフォーマンスが変わればGPUにも影響を与えることになるので、正確な判断ができなくなる)。
まずは、思いっきり小さな解像度、例えば100x50くらいの解像度にしてゲームを実行してみます。小さな解像度にしてもCPUの処理、頂点フェッチ、頂点シェーダーのパフォーマンスには変化がありません。
もし、低解像度にしてもフレームレートが変わらない場合(GPUバウンドであることが前提)、頂点処理がボトルネックになっている証拠です。
頂点処理を軽くするには、少ないポリゴン数のモデルに変更するか、頂点シェーダーを簡略化します。もし、頂点フェッチが問題だと思う場合はカスタムモデルプロセッサーを作り、VertexChannelCollection.ConvertChannelContentを使ってPackedVectorフォーマットを使って頂点データを小さくします。例えばNormalized101010は法線データには向いていますし、HalfVector2はテクスチャ座標データの圧縮によく使われます。
もし、低解像度にしてフレームレートが上がった場合、ピクセル処理がボトルネックになっている証拠です。
ミップマップを使っている場合(使っていない場合はミップマップを使うようにするだけでパフォーマンスアップになります)、SamplerStates[n].MipMapLevelofDetailBiasに4や5を設定してみましょう。これでテクスチャがぼやけた感じになります。フレームレートがあがっていればテクスチャの読み込みがボトルネックになっているということです。この場合、DXT圧縮にしたり、テクスチャサイズを小さくしたり、テクスチャの数を減らすことでパフォーマンスアップになります。
次にピクセルシェーダーを単色を返すだけの単純なものにする。これはピクセルシェーダーとテクスチャフェッチに影響しますが、テクスチャフェッチについては既にテスト済みなので、このテストはミップマップバイアスを変更してもフレームレートが上がらなかった場合にします。この変更でフレームレートが上がった場合は、ピクセルシェーダーがボトルネックになっています。
まだ、ボトルネックが見つからない?と、いうことは残った可能性は#3、#6、もしくは#7になります。
マルチサンプルを有効にしてみて、フレームレートが変わらなければラスタライザがボトルネックになっています。
フレームバッファのピクセルフォーマットをSurfaceFormat.Bgr565などの小さいものに変更してみます。これでフレームレートが上がればフレームバッファがボトルネックになっています。
これでもフレームレートが上がらないということは消去法で深度/ステンシルバッファがボトルネックになっているということです。
元ネタ:
http://blogs.msdn.com/shawnhar/archive/2008/04/11/santa-s-production-line.aspx