描画パフォーマンス不足の原因

描画パフォーマンスが不足する場合、つまりはFPSが出ない場合、レンダリングパスの見直し、解像度の変更、シェーダーコードの見直し、レンダリングアセットの見直しなどを行うことが多いと思います。ただし描画パフォーマンスが不足する原因は、必ずしもGPUの性能不足だけではありません。
いろいろな原因が考えられます。場合によってはこれらの性能改善に向けた努力が全くの無駄になっている時もあります。
今回はFPSが出ない様々なケースを、GPUViewを用いて観測してみたいと思います。

まずはじめにGPUの描画性能がボトルネックになっている場合です。(GPU-bound)


GPUへの描画命令がスタックされ、GPUはアイドルすることなく動き続けています。ある意味GPUの性能は正しく使い切られている、良いケースといえるかもしれません。
一方CPU側のGameApp.exeのスレッドは休止している時間が長いので、CPUはもっと性能を出すチャンスがあるとも考えられます。またこの状況下ならば、描画処理の最適化を行うことでFPSの向上が望めます。

 次はCPUがボトルネックになっているケースです。(CPU-bound)


このケースではCPU側のGameApp.exeのスレッドがフル稼働しているにも関わらず、GPUへの描画命令が十分に発行されていません。GPUのアイドル時間が長いです。
このケースではいくら描画処理の最適化を行っても、描画パフォーマンスの向上は望めません。ボトルネックはCPU側にあるので、CPU側の処理を改善する必要があります。

次の例はCPUとGPUの間に同期が必要な処理が存在するケースです。(CPU-GPU sync)


下記のケースではOcclusion queryを発行して、Presentの直後でQueryの結果の取得を行っています。言い換えれば、1フレーム前ののQuery結果の取得を行っている状態です。Presentパケットの発行直後にもうひとつのパケットがすぐに発行されています。これはQueryを問い合わせる際にコマンドのフラッシュを行っている関係で発行されています。(これは必ず発行されるものではありません)
またCPU側のスレッドが長時間アクティブなのは、WhileループでQuery結果を問い合わせ続けているからです。CPUはGPUに発行した描画命令がGPU側で完了するのを待ち、それに伴う結果をQueryで取得するまでCPUサイクルを無駄に消費します。またその間CPU側の処理は滞るので、新たな描画命令が発行されることはありません。そのため、Query取得後から新たな描画命令が発行されるまでの間、GPUはアイドルしてしまいます。
このケースはGPUViewで観測するだけではCPU-boundなのか判別できません。しかしアプリケーションの開発者ならば、フレーム遅延による描画命令のスタックが滞ったり、Query取得の部分でCPU処理に時間をとられるので、そのあたりから総合的にこの問題を見つけることが可能です。

次の例もやはりOcclusion queryを発行しています。


このケースではQuery対象のレンダリングの直後でQueryの結果を取得しています。PresentとPresentの中間で一旦描画命令が全てGPUで処理されるの待つ時間が生じます。この様にGPUとCPU間で同期が必要な処理はパフォーマンスの低下を招きます。これらの例ではOcclusion queryを用いましたが、他にも描画直後にRenderTargetをCPU側で読み出しを行った場合などにも同じような現象が発生します。これらの現象を避けるためには、Occlusion queryやRender targetの読み出しを行う場合は、最低でも2フレーム前の結果を取得するべきです。また、SetMaximumFrameLatencyで設定した分だけの描画コマンドをスタックするためには、その分だけ前のフレームの結果を取得するべきです。
加えて、NVIDIA SLIやAMD Crossfire等でGPUを並列動作させているケースはさらに注意が必要です。
たとえば2つのGPUを並列動作させている場合、一般的にレンダリングをフレーム単位で交互に並列処理しています。つまり2-wayの並列動作の場合は2フレーム分が同時に処理されていますので、
最低でも3フレーム前の結果でないとCPU-GPU syncによるパフォーマンスの問題が起きてしまいます。さらに3-way 4-wayの場合は最低4フレーム,5フレーム前の結果でないとCPU-GPU syncを招いてしまいます。これらのソリューションに対応し、性能スケーリングを維持するためには、アプリケーション側でGPUの並列動作数を確認し、それに対応した動作をする必要があります。

最後はGPU側のメモリ不足のケースです。


意図的に巨大なテクスチャを作成して利用するプログラムを同時に複数起動して、VRAMを使用し尽くしたものです。このケースでは、GPU側に作られているDirectXのオブジェクトの退避処理や転送処理が発生しています。グラフ中の赤い部分がそれに該当します。このケースに関してはGPUViewのMemory Viewerと共に別途詳しく説明したいと思います。

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中