Language:
Page Info
Engine Version:

GPU プロファイリング

GPU では多くのユニットが平行して動いており、フレームの異なるパーツに対して異なるユニットで結合されることが一般的です。 このような事情から、ボトルネックと GPU のボトルネックが何かを探す場合は、GPU の負荷の発生源を見ると分かります。

GPU 負荷の発生源

ProfileGPU コマンドを使って、各種パス、そして時にはドローコールをたどって GPU 負荷をすぐに特定することができます。 マウスベースの UI またはテキストバージョンのいずれかを使用できます。r.ProfileGPU.ShowUI で UI を抑止することができます。データは GPU タイムスタンプに基いており、 通常とても正確です。最適化をすると数字の信頼性が落ちる場合もあるので、数字を厳しく見るのが理想です。ドライバーの中には、シェーダー使用後の数秒でシェーダー負荷を 最適化するものもあることが分かりました。これは注目に値することで、もう少し待つか、別の例で測定して確信を得るのが良いかもしれません。

ProfileGPU.png

CONSOLE:ProfileGPU

Shortcut:Ctrl+Shift+,

...
 1.2% 0.13ms   ClearTranslucentVolumeLighting 1 draws 128 prims 256 verts
42.4% 4.68ms   Lights 0 draws 0 prims 0 verts
   42.4% 4.68ms   DirectLighting 0 draws 0 prims 0 verts
       0.8% 0.09ms   NonShadowedLights 0 draws 0 prims 0 verts
          0.7% 0.08ms   StandardDeferredLighting 3 draws 0 prims 0 verts
          0.1% 0.01ms   InjectNonShadowedTranslucentLighting 6 draws 120 prims 240 verts
      12.3% 1.36ms   RenderTestMap.DirectionalLightImmovable_1 1 draws 0 prims 0 verts
          1.4% 0.15ms   TranslucencyShadowMapGeneration 0 draws 0 prims 0 verts
...

ProfileGPU は、アーティストは正しい光源を最適化しやすいようにライト名を表示します。

フレームごとにおおまかな負荷を調べて、妥当な負荷を検討するのは意味があります (重たいドローコール、複雑なマテリアル、高密度のトライアングル メッシュ、遠い表示距離など)。

  • EarlyZPass:当社ではデフォルトで z パスを使います。DBuffer デカールはフルの Z パスを要求します。これは、r.EarlyZPassr.EarlyZPassMovable. でカスタム化が可能です。

  • 基本のパス:ディファードを使用すると、単純なマテリアルは帯域幅の制約を受ける可能性があります。実際の頂点とピクセル シェーダーは、マテリアル グラムで定義されます。動的オブジェクト上に間接光がある場合、負荷が増加します。

  • シャドウマップのレンダリング:実際の頂点とピクセル シェーダーは、マテリアル グラムで定義されます。ピクセル シェーダーは、マスクされたマテリアル、あるいは透過マテリアルのみに使用されます。

  • シャドウ プロジェクション/ フィルタリング:ほとんどのライトでは、 r.ShadowQuality.Disable シャドウ キャスティングでシェーダー負荷を調整します。Static lisht (静的ライト) か Stationary light (固定ライト) かを考慮します。

  • オクルージョン:HZB オクルージョは常に高負荷ですが、オブジェクト当たりの負荷は小さめです。r.HZBOcclusion をトグルして、オンにしない方が良いかを確認します。

  • ディファード ライティング:接したピクセルに比例し、ライトの機能、 IES プロファイル、シャドウの受け取り、エリアライト、複雑なシェーディング モデルが加わるとさらに負荷が大きくなります。

  • タイル処理されたディファード ライティング:r.TiledDeferredShading をトグルして GPU ライトを無効にするか、r.TiledDeferredShading.MinimumCount を使ってタイル処理された方法またはディファードでない方法をいつ使うか定義します。

  • Environment reflections:r.NoTiledReflections をトグルして、プローブがほとんどない場合以外は通常は遅めの、タイル処理以外の方法を使用します。

  • Ambient Occlusion (アンビエント オクルージョン):品質の調整が可能で、効率的な大きいエフェクトに対して複数のパスを使用できます。

  • ポストプロセス:パスによってはシェアされているので、表示フラグを切り替えて、そのエフェクトがパフォーマンスに価値があるかを確認します。

それらの次のパス上にエフェクトを持つことができるパスもあります。幾つか紹介します。

  • EarlyZ のフルパスでは、ドローコールが増え GPU 負荷もかかりますが、ベース パスでのピクセル処理を避けることで負荷を大幅に削減することができます。

  • HZB を最適化すると、カリングがより控えめになります。

  • スクリーンの大部分がシャドウの場合、シャドウを有効化するとライトのライティング負荷を減らすことができます。

GPU のボトルネックについて

パフォーマンス負荷は、ピクセル数に比例する場合が多いです。r.SetRes を使ってレンダリングの解像度を変えたり、エディタのビューポートをスケールしてみると、そのことが分かります。 r.ScreenPercentage を使うと遥かに便利ですが、この機能を使用した分だけアップサンプリング負荷がかかることに留意してください。

かなり大きなパフォーマンスの変更があった場合は、ピクセル関連の何かの制約を受けています。通常、メモリ帯域 (読み書き) か演算 (ALU) のいずれかですが、 特定のユニットがサチュレートしている稀なケースもあります (MRT エクスポートなど)。関連するパスでメモリ (または演算) を下げてパフォーマンスに違いが出れば、それはメモリ帯域の制約を受けていることがわかります (または ALU ユニット)。 これは単なるテストなので、違いが同じに見える必要はありません。これで、パフォーマンス改善のために減らす必要のある負荷の正体が分かりました。

シャドウマップの解像度は画面の解像度でスケールしませんが (r.Shadow.MaxResolution を使用)、シャドウ キャスティングのかなり領域をマスクしているか透過マテリアルでない限りは ピクセル シェーダーの制約は受けません。シャドウマップ レンダリングは、頂点処理あるいはトライアングル化 (高密度のメッシュ、LOD なし、テセレーション、WorldPositionOffset の使用) の制約を受ける場合が多いです。 シャドウマップ レンダリングは、ライト数、カスケード / キューブマップの側面、ライト錐台でシャドウキャスティングしているオブジェクト数に比例します。これはよくあるボトルネックで、 負荷を減らす方法は大きいコンテンツを変更するのみです。

ワイヤーフレームがソリッドなカラーで表示される場合、クワッドを使用しないと細分化が進んだメッシュに支障ができます。これは、 GPU がトライアングルを 2x2 ピクセルブロックに処理し、トライアングルの外のピクセルを少し後で拒否するためです。これは、ミップマップの演算処理に必要です。大きめのトライアングルでは特に問題になりませんが、 トライアングルが小さかったり冗長な場合、かなりの数のピクセルが処理されるのに実際にはほとんど画像に影響しないため、パフォーマンスが下がります。ライティングで非常にうまくクワッドを使用しているので、 ディファード・シェーディングがこの状況を改善します。問題はベース パスに残るので、複雑なマテリアルのレンダリングはかなり遅くなります。 これを解決するには、密度の低いメッシュを使用します。詳細度メッシュで、問題がある場合のみ行うことができます。

r.EarlyZPass を調整して、早期 Z パスを利用する価値があるか確認します (ベースパス中にドローコールが増えてオーバードローが減る)。

解像度を変更しても関係ない場合、頂点処理 (バーテックス シェーダー、テセレーション) 負荷の制約を受けている可能性が高いです。 多くの場合、確認するためにはコンテンツを変更しなければなりません。よくあるケースを紹介します。

  • 頂点が多すぎる (詳細度メッシュを使用)

  • ミップマップが少ないテクスチャを使った複雑な World Position Offset / Displacement マテリアル (マテリアルの調節)

  • テセレーション (テセレーション係数 fastest way: show Tessellation で調整します。テセレーション レベルが粗いとうまくスケールできないハードウェアもあるので、できるだけ避けてください)

  • UV あるいは法線シームは頂点が増える場合が多いです (アンラップされた UV を見ると UV 島 (island) はほとんど失敗で、フラット化されたメッシュはトライアングルごとに頂点が 3 つあります)。

  • 頂点属性が多すぎる (余分な UV チャネル)

  • 頂点数が適切か確認する。インポータ コードには頂点を結合 (同じ位置、UV、法線を持つ頂点をまとめる) しないものもある。

それほど頻繁ではありませんが、何かの制約を受けることがあります。例えば以下のものです。

  • オブジェクトの負荷 (CPU 負荷の場合が多いが、GPU 負荷も若干含まれる)

  • トライアングル設定負荷 (シャドウマップ スタティックメッシュなどの、負荷が小さい頂点シェーダーでポリゴン数の多いメッシュはほとんど問題にはなりません)

  • 詳細度 (LOD) メッシュの使用

  • 表示負荷 (HZB オクルージョン カリングなど)

  • シーン負荷 (GPU パーティクル シミュレーションなど)

リアルタイム GPU プロファイラ

主要なレンダリング カテゴリに対してリアルタイムでフレーム単位の統計を表示するリアルタイム GPU プロファイラが追加されました。リアルタイム GPU プロファイラを使用するには、Backtick (`) キーを押してコンソールを開き、「stat GPU」 と入力して Enter キーを押します。または、[Viewport Options] ドロップダウンの [Stat] サブメニューからでもリアルタイム GPU プロファイラを開くことができます。

GPU_Stats.png

統計は階層ではなく累積なので、イベント ツリーの深い階層まで探さずに主要カテゴリを見ることができます。例えば、Shadow Projection はビュー全体のすべてのライトに対すプロジェクションの合計です。画面上の GPU 統計情報は、GPU ロードの詳細をタイトル実行中に表示したものです。例えば、コンソール変数を変更したり、エディタでマテリアルを修正したり、(recompileshaders を変更して) シェーダーをオンザフライで修正および再コンパイルした場合など、変更の影響を瞬時に測定できるので便利です。後に、分析用にタイトルを動かした時に、GPU 統計情報をファイルに記録することができます。

既存の統計情報と同様に、コンソール コマンドの stat startfilestat stopfile を使って統計情報を ue4stats ファイルに記録し、Unreal Frontend tool でファイルを開いてそれらを表示することができます。

Saved_Profile.png

UnrealFrontend で GPU をプロファイルした様子。合計時間、ポストプロセス時間、ベースパス時間が表示される。

統計情報は float 型のカウンタとしてコードで宣言されます。例えば、

DECLARE_FLOAT_COUNTER_STAT(TEXT("Postprocessing"), Stat_GPU_Postprocessing, STATGROUP_GPU);

レンダリング スレッド上のコード ブロックは、これらの統計名を参照する SCOPED_GPU_STAT マクロで測定することができます。SCOPED_DRAW_EVENT と似た動きをします。例:

SCOPED_GPU_STAT(RHICmdList, Stat_GPU_Postprocessing);

明示的に測定できない GPU はキャッチオール [考慮されない] 統計に分類されます。値が高くなりすぎると、SCOPED_GPU_STAT イベントが追加で必要であることを表示して、不足作業の原因を説明します。ドロー イベントとは異なり、GPU 統計は累積です。同じ統計に対して複数のエントリを追加でき、それらはフレーム全体で合計されます。CPU が結合されいるケースでは、CPU の処理が GPU に追いつかない CPU ボトルネック (バブル) によって GPU の速度が落ちる場合があります。予想以上に描画スレッドに時間がかかってしまった場合、そのことを考慮してください。PlayStation 4 では、コマンドリスト サブミッション間の時間をタイミングから除外することで、これらのバブルを修正します。今後のリリースで、最新のレンダリング API にこの機能性を拡張していきます。