この記事は、インテル® デベロッパー・ゾーンに掲載されている「Find game bottlenecks with Intel GPA」(https://software.intel.com/en-us/blogs/2014/06/29/find-game-bottlenecks-with-intel-gpa) の日本語参考訳です。
この記事では、一般的なゲーム解析について説明します。ゲーム解析では、適切な場所を最適化するため、ゲームの主なボトルネックを特定します。インテル® グラフィックス・パフォーマンス・アナライザー (インテル® GPA) は、そのためのツール群を提供します。
最初に、テストシステムにゲームとインテル® GPA を、解析用システムにインテル® GPA をインストールし、両方のシステムでインテル® GPA Monitor を起動します。インテル® GPA の基本操作については、簡単なセルフテストに関するこちらのブログ (https://software.intel.com/en-us/blogs/2013/06/28/starting-out-with-gpa-with-troubleshooting-tips) をお読みください。
両方のシステムでインテル® GPA Monitor が起動したら、テストシステムでゲームを起動し、解析用システムでインテル® GPA System Analyzer を起動します。ゲームが一般的な設定で動作していることを確認し、設定を記録します。オプション、品質設定、解像度は、ゲームの動作とボトルネックの場所に影響します。
設定画面のスクリーンショットを撮っておくと良いでしょう。インテル® GPA System Analyzer からゲームに接続し、前述のブログで説明されているいくつかの基本的なメトリックを確認します。
最初は、何もせずにゲームの動作を見てみましょう。ゲームプレイが安定してきたら、フレームレートと CPU/GPU 使用率を確認します。場合によっては、ほかの人にこの作業を頼むと良いでしょう。インテル® GPA System Analyzer では、さまざまなメトリックをリアルタイムに確認できます。
1 つだけ急激に変化しているメトリックがあるか? フレームレートは一定か? それとも、ときどき (あるいは頻繁に) 遅くなっているか? など、ゲーム全体の動作を把握します。
プレイを続行し、インテル® GPA System Analyzer の CSV ボタンを使って、現在表示されているメトリックを CSV ファイルに出力します。30 秒ほど実行して、CSV キャプチャーを停止し、ゲームプレイを続行します。これにより、リアルタイムのメトリックを後で確認し、画面上では見落としたパターンなどを見つけることができます。インテル® GPA System Analyzer であと 2 回キャプチャーを行います。キャプチャー処理が終了するまで数秒かかるため、続けてキャプチャーする場合は、システムが通常の状態に戻るまで待ちます。カメラボタンを使ってフレームをキャプチャーし、少し間をおいてから、赤い録画ボタンを使ってトレースをキャプチャーします。
インテル® GPA System Analyzer を利用してリアルタイムのテストを行うこともできますが、それはまた別の機会に触れましょう。
必要なすべてのデータ収集が完了したので、ゲームを終了します。
これで実行中のゲームの影響を受けることなく、解析ツールを利用して、テストシステムですべての解析を実行することができます。どちらのシステムでもすべてのアナライザーを実行できますが、多量のデータをコピーしなければならないため、テストシステムで解析ツールを実行したほうが簡単です。キャプチャーしたファイルはテストシステムに保存されています。(CSV ファイルは解析用システムに保存されています。)
最初に、インテル® GPA Platform Analyzer で、トレース・キャプチャー・ファイルを開きます。このツールを使って、ゲームが CPU 依存か GPU 依存かを確認できます。GPU Engine (Render と GPGPU) セクションに キャプチャー時の GPU アクティビティーが表示されます。GPU アクティビティーでクロスハッチの呼び出しを確認すると、GPU キュー内でフレームがどのように処理されたかが分かります。GPU アクティビティーの下の Thread Lifetime は CPU アクティビティーを示します。GPU アクティビティーにギャップがある場合は、GPU 依存ではありません。同様に、CPU アクティビティーにギャップがある場合は、GPU 依存ではありません。
ゲームが CPU 依存の場合は、このプラットフォーム・トレースを詳しく検証します。最近では、CPU 依存は低性能のシステムでよく見られ、一般的なラップトップは GPU 依存の傾向にあります。
ゲームが GPU 依存の場合は、Frame Analyzer でフレーム・キャプチャーを開きます。まず、画面上部に表示されるグラフで作業単位 (“erg”) にパターンがあるかどうか確認します。一部の作業が長時間実行しているか? 多数の作業が短時間実行しているか? 呼び出しが多数 (1500 以上) あるか? などに注目します。呼び出しが多い場合は、呼び出しの期間に関係なく、ゲームまたはドライバーが CPU 依存である可能性が高いです。一方、一部の作業が長時間実行している場合は、ゲームが GPU 依存である可能性が高いと考えることができます。ここで得た情報とプラットフォーム・トレースで得た情報を比較してみてください。
次に、非常に大きな “erg” に注目します。必要に応じて、Frame Analyzer の上部にあるドロップダウンを使って、時間単位の表示に切り替えます。実行時間の長いいくつかの “erg” をクリックして、その実行時間を控えておきます。この情報は後で使用します。画面下部の [Experiments] タブで [Disable Erg(s)] をオンにします。すると、これらの “erg” が一時的にフレームワークロードから除外され、残りのフレームを検証しやすくなります。また、期間がリセットされ、ほかの “erg” の実行時間を正確に把握することができます。これにより、一部の “erg” に集中すべきか、多数の “erg” に集中すべきかが明らかになります。
元の表示に戻るには、画面上部のバーにある [Revert All Changes] ボタンをクリックします。
[Experiments] タブを使って、実行時間が最も長い “erg” を検証してみましょう。設定の変更はただちに適用され、フレームが再レンダリングされ、左側に新しい実行時間が表示されるので 各変更の結果を素早く確認できます。まず、[2×2 Textures] をオンにして、テクスチャー・ルックアップに費やされる時間を見てみます。これは、すべてのテクスチャーを単純な 2×2 テクスチャーに置換するだけなので、ルックアップは非常に高速です。次に、[Simple Pixel Shader] をオンにした場合を見てみます。これは、テクスチャー・ルックアップとシェーダーで計算処理に費やされた時間を示します。2 つの結果を比較し、[Simple Pixel Shader] をオンにしたほうが著しく高速な場合は、シェーダーで行われる計算処理の量と種類を確認し、単純化します。ほかのオプションをオフにして、ゲームのパイプラインで 1 ピクセルの処理を実行する [1×1 Scissor Rect] をオンにします。これにより、1 ピクセルのレンダリングに必要な処理が明らかになり、ピクセルごとの作業量が多すぎるかどうか判断できます。
一般に [2×2 Textures] によりゲームが大幅にスピードアップすることはありません。これらのケースでは、特にシェーダーで計算処理を排除できる場合、テクスチャー・ルックアップを増やすことができます。シェーダーで計算処理に時間がかかりすぎている場合は、計算処理を減らす方法を考えます。例えば、CPU 側で計算し定数として渡したり、テクスチャーとして渡したり、フレームごとに行われる作業のうち以降のフレームで再利用できるものを探してみたり、オフロードによりワークロード全体を減らす方法を考えてみると良いでしょう。
データ収集時にゲームで使用されていた解像度が、そのジョブに適切であるかどうか検討してみるのも 1 つの方法です。解像度を落とすことでピクセル数が減り、ピクセルにかかる時間が軽減されるでしょう。
画面上部で最も長いバーをいくつかクリックして、[Shaders] タブでそれぞれの GPU 時間を確認し、実行時間の最も長いシェーダーを選択します。そして、そのシェーダーによって使用された実行スロットの数を確認します。使用された命令スロットの数は、[Assembly] または [HLSL] ビューの下のほうにあります。この値が大きくない場合は、シェーダー全体に対する各操作の相対コストを明らかにするため、[Profile] を選択してシェーダーを複数回実行し、各行の実行時間を取得します。そして、そのデータを基に、シェーダーをより速く実行する方法について考えてみます。
シェーダーに含まれるすべての [const] ロードを確認します。[const] ロード はメモリーにアクセスするため時間がかかります。
確認が終わったら、[Shaders] タブを右クリックして、[Select All Ergs That Use This Shader] を選択します。これにより、シェーダーの相対的なインパクトが示され、このシェーダーを変更することで得られるフレームのスピードアップが分かります。
各シェーダーを確認し、そのシェーダーが何を行っているのか不明な場合は、その “erg” を無効にします。無効にした “erg” の出力がなくなることで、次の “erg” に影響がある場合、無効にした “erg” は次の “erg” で入力として使用されている可能性があります。このことから、無効にした “erg” が何を行っているのか推測することができるでしょう。例えば、次の “erg” は無効にしたフレームの出力を基に SSAO 計算を行っているかもしれません。
フレームを調査する別の方法は、画面上部のバーでレンダリング・ターゲット表示に切り替えます。現在 “erg graph” が表示されている場合は、ドロップダウンで “render targets” に切り替えます。いくつかのレンダリング・ターゲットでパフォーマンス・オーバヘッドが示されるかもしれません。すべてのレンダリング・ターゲットが個別のものであることを確かめるには、[View] メニューから [API details] を選択します。そして、フィルターボックスに “SetRenderTarget” と入力して、結果を確認します。
1 行に複数のレンダリング・ターゲットのセットが表示されている場合、冗長なレンダリング・ターゲットの可能性があります。メイン画面の左側に使用されたレンダリング・ターゲットが表示されているので、”erg” で使用されたレンダリング・ターゲットとその [API details] を比較してみてください。最初のほうのレンダリング・ターゲットが何を行っているのか確認します。ひょっとしたら、シャドウマップを構築しているかもしれません。メイン画面でレンダリング・ターゲット・リストを上にスクロールすると、レンダリング・ターゲットの [Color] ビューを選択できます。レンダリング・ターゲットに何が含まれているのか不明な場合は、ビューアーのヒストグラムを展開し、スライダーでグレースケールを調整して、パターンが見られるかどうか確認します。
[API details] ビューに戻り、同じレンダリング・ターゲット番号が複数回使用されていないかチェックし、使用されている場合はいつどのように使用されているかを確認します。[API view] をスクロールして冗長がないか調べます。最初のほうの “erg” がシャドウマップへの書き込みを行っている場合は、その “erg” を選択してからメイン画面で [State] タブを選択し、その呼び出しで使用されたすべてのステート設定を表示します。Z 書き込みが有効な場合は、カラー書き込みも有効かどうか確認します。これらの書き込みは通常、デプス値のみ書き込みます。ただし、DirectX* 9 では、デプスバッファーから直接読み取ることができないため、デプス値はカラーに書き込まれます。DirectX* 11 では、このような制限はないため、カラー書き込みは有効にされません。
大きな “erg” が R と B へ書き込みを行っている場合、ターゲットの残り 2 つのチャネルは使用されていないため排除できる可能性があります。レンダリング・ターゲットを変更して、検証してみると良いでしょう。
[State] タブでは、カリングが行われているかどうかも確認します。一貫性がない場合は、カリングが行われていないものに注目し、[Texture] タブでテクスチャーを確認して、それが何であるかを把握します。多くの場合はツリーです。ターゲットにアルファがあるかどうか、それが使用されているかどうかをチェックします。それらの “erg” の PS コードを調査し、プロファイルして、時間のかかっている操作を特定します。ハードウェアによっては、texkill 操作に時間がかかることがあります。Z 書き込みが有効かどうか、シェーダーに texkill が含まれているかどうか、ステンシルが有効かどうかをチェックします。
カリングが行われる “erg” に対して、デプスプリパスでカリングモードが設定されているか、あるいはシャドウモードが設定されていることを確認します。
シャドウ処理を行うコードで、カスケード・シャドウマップまたは別の光源によるシャドウを探します。カスケードの場合は、特定のデプスまで複雑な形状を含む詳細なフロントセクションから分かります。遠距離シャドウが粗く、近距離シャドウは問題ない場合はカスケードです。場合によっては、両方が生成され、実行時にどちらを使用するか決定されることがあります。品質設定と解像度に対してシャドウが詳細すぎる場合もあります。
Frame Analyzer のメイン画面にある Frame Overview をスクロールして、異常値を探します。最初のほうの Z の失敗数が多い場合、ビューの錐体カリングを追加してみると良かもしれません。形状が削除されたり、多くの形状が失われている個所がある場合は、カリングを追加するべきです。
最初のほうの Z が強制終了されてもサンプルが書き込まれるオブジェクトは、シーンの一部です。
送信されているのに書き込まれていない項目はありませんか?
PS が頻繁に呼び出されている場合、描画が過剰に行われているか、多数のレンダリング・ターゲットを使用している可能性があります。よく理解するためには、画面上部でレンダリング・ターゲット・ビューに切り替えて、Y 軸にレンダリングされたピクセルを表示します。1 つまたはいくつかのレンダリング・ターゲットが多数のピクセルをレンダリングしている場合、オーバードローが多すぎる可能性があります。
フレームの描画時にその領域でそれぞれのパスが何を行っているのかを検証します。[Shaders] タブで、デプスパスの頂点シェーダーと シグネチャーを確認します。”decl position v0″ のようなコードに対し、その値がシェーダーで使用されているのかどうかを調べます。特に、カラー値を受け取っているデプスパスのコードに注目します。このカラー値は通常不要です。この作業は、シグネチャーが大きすぎる場合の発見にも役立ちます。位置のみが使用される場合、シグネチャーの不要なバイトを排除することができます。
ツリー “erg” ではアルファを使用するため、テクスチャー座標が必要です。シグネチャーの排除できる部分を探ります。
書き込まれるサンプル数が大きい場合、MSAA が有効になっている可能性があります。MSAA を有効/無効にした場合のサンプルを比較し、その影響を明らかにします。MSAA が有効で実行に時間がかかる場合は、代わりに PP AA アプローチを利用できるかどうか検討してみると良いでしょう。
この記事では、ゲームのパフォーマンスについてさまざまな角度から取り上げました。この記事が、パフォーマンス・ボトルネックの特定と修正に役立つことを願っています。
コンパイラーの最適化に関する詳細は、最適化に関する注意事項を参照してください。