ハイライト
Xcode 12 と Instruments は、Apple GPU のパフォーマンス リミッター、メモリ帯域幅、占有、HSR、その他のカウンターをエンコーダーと描画呼び出しレベルに公開し、Metal 開発者が最長パスから特定のリソースとシェーダーをトレースできるようにします。
主要内容
Metal ゲームでフレームがドロップすると、開発者は最初に「GPU ビジー」を認識するのは簡単です。これでは十分ではありません。本当の答えは、どのコマンド エンコーダが最も長いかということです。 ALU、テクスチャ サンプラー、バッファ、タイル メモリ、またはピクセル バックエンドでスタックしていますか? GPU カウンターがなければ、最適化はエフェクトをオフにし、シェーダーを変更し、再実行し、再度推測することになります。
このセッションでは、トラブルシューティングの手順を非常にわかりやすく説明します。まず、Instruments のゲーム パフォーマンス テンプレートを使用して実デバイス上でフレームを記録し、パフォーマンス リミッターとシェーダー タイムラインを開いて、最も時間がかかるエンコーダーを見つけます。次に、同じフレームを Xcode Metal Debugger に渡し、エンコーダーまたは描画呼び出しの粒度でカウンターを表示し、「遅さ」を特定のハードウェア単位に分解します。
これらの数値は、Apple GPU の TBDR (Tile Based Deferred Renderer、タイルベースの遅延レンダリング) アーキテクチャに読み戻す必要があります。 Apple GPU には独立したビデオ メモリがありません。 CPU と GPU はシステム メモリを共有します。 GPU コアは、オンチップのタイル メモリ、ALU、テクスチャ ユニット、ピクセル バックエンド、および共有の最終レベル キャッシュに依存します。テクスチャ フォーマット、ストレージ モード、ロード/ストア アクション、描画順序はすべて、フレームが効率的かどうかに影響します。
スピーチの最後のデモは非常に代表的です。チームは Xcode のメモリ グループから開始し、メイン メモリから読み取られたバイト数でソートし、描画ごとのカウンター モードに切り替え、最終的にバインド リソースで共有ストレージの RGBA16 浮動小数点キューブ マップを見つけました。プライベート ストレージに変更した後、可逆テクスチャ圧縮を有効にすることができます。ブロック圧縮されたテクスチャ アセットと組み合わせることで、ゲームは iPad Pro 上で 120 FPS まで安定して実行できます。
詳細
まず、ツールチェーンから最長のエンコーダーを見つけます。
(04:33) Apple には 2 つの入り口があります。 Metal System Trace は Instruments に属しており、CPU/GPU タイムライン、システム ステータス、フレーム内のさまざまなエンコーダーの時間消費を確認するのに適しています。 Metal Debugger は Xcode に属しており、エンコーダー粒度ですべての GPU カウンターをリストでき、描画呼び出し粒度で多数のカウンターを表示できるため、より詳細なパフォーマンス調査に適しています。
(06:58) デモの最初のステップは、インストゥルメントでゲーム パフォーマンス テンプレートを選択し、デバイスとアプリを確認し、メタル アプリケーション記録オプションを入力し、GPU カウンター セットをパフォーマンス リミッターに設定し、新しいシェーダー タイムラインを有効にすることです。記録が完了すると、開発者はフレームにズームして、コマンド バッファー、エンコーダー、およびシェーダーのタイムラインを表示できます。
キーポイント:
- 最初に実際のデバイスのワークロードを見てみましょう。セッションでは、メタル システム トレースが熱や動的システムの変化の影響を受けると明確に述べられていたためです。
- シェーダー タイムラインでは、エンコーダーの実行中にどのシェーダーが実行されているか、対応するサンプル数とおおよその GPU 時間を表示できます。
- デモで最も掘り下げる価値があるのは Deferred Phase Encoder です。フレーム内で最も長い時間がかかるため、フラグメント シェーダーは約 1.29 ミリ秒です。
- パフォーマンス リミッターの一番上のトラックには、現在最も顕著なリミッターが表示され、その下には ALU やテクスチャ サンプラーなどの別のトラックがあります。
パフォーマンス リミッター ソートのトラブルシューティング
(05:58) GPU は、算術演算、メモリ アクセス、ラスタライズなどのタスクを同時に実行します。 Limiter カウンターの目的は、複数のサブシステムのアクティビティとストールを定量化し、「最も遅い部分」を示すことです。講演者は、どのカウンターのセットに入るかを決定する前に、常にパフォーマンスのリミッターに注目することを推奨しました。
(10:00) このセッションでカバーされるリミッターには、算術演算、テクスチャの読み取りと書き込み、タイル メモリのロードとストア、バッファーの読み取りと書き込み、GPU 最終レベル キャッシュ、およびフラグメント入力補間が含まれます。 Xcode では、グループごとに表示したり、フィルターを使用して ALU、テクスチャ、バッファ、イメージブロック、または最終レベル キャッシュを入力してリストを絞り込むことができます。
キーポイント:
- トップ リミッターは最終的な答えではなく、調査の次のステップへの入り口にすぎません。
- Instruments は「どの段落が一番長いか」を調べるのに適しており、Xcode は「なぜこの段落が長いのか」を調べるのに適しています。
- Xcode のカウンター テーブルは並べ替えることができ、デモではそれを使用して、最も高価なエンコーダーと最も ALU に制限されたエンコーダーを見つけます。
- エンコーダーの平均的な判断が十分ではない場合、描画ごとのカウンター モードに切り替えて、問題を特定の描画呼び出しに減らすことができます。
ALU リミッター: 精度、複雑な演算、分岐の一貫性をチェック
(10:22) ALU はシェーダー コアの一部であり、浮動小数点演算、整数演算、ビット単位の演算、および関係演算を処理します。 Apple GPU では、16 ビット浮動小数点はダブル レートで実行され、32 ビット浮動小数点はフル レートで実行され、32 ビット整数および複雑な演算は通常、ハーフ レート以下で実行されます。
(11:19) シェーダー コアの SIMD レーンには 32 スレッドがありますが、プログラム カウンターを共有します。すべてのスレッドが同じ命令を実行する場合、それはコヒーレント実行に属します。条件付き分岐によって一部のスレッドが別のパスを取ることが許可される場合、それは分岐実行となります。デモ図の同じプログラムは、分岐に入っていないスレッドが依然としてサイクルを消費するため、40 サイクルから 70 サイクルに変化します。
キーポイント:
- ALU リミッターが高い場合は、まずそれが予想されるボトルネックかどうかを確認します。 GPU の高速処理自体は悪いことではありません。
- ALU の負荷を軽減したい場合、セッションは複雑な計算を近似またはルックアップ テーブルに置き換えることを推奨します。
- 暗黙的な変換と FP32 テクスチャ/バッファ入力を回避するために、半精度を使用する場合は完全精度浮動小数点数を減らします。
- プロジェクトで許可されている場合にメタル シェーダが使用されていることを確認します。
-ffast-mathコンパイルオプション。 - ALU リミッターが高いことは、シェーダーが効率的であることを意味するものではありません。講演者は、すべて FP32 で動作している場合、ALU は 100% 制限されている可能性があるが、使用率はわずか 50% であるという例を示しました。
テクスチャと帯域幅: フォーマット、圧縮、ストレージ モードからチェック
(14:25) テクスチャ処理ユニット (TPU、テクスチャ処理ユニット) は、テクスチャ データの読み取りを担当します。これはレンダー パスの LoadActionLoad に使用され、シェーダーでテクスチャを明示的に読み込んだりサンプリングしたりするためにも使用されます。ピクセル形式はサンプリング レートに直接影響します。講演では、RGBA32Float などの 128 ビット形式は 4 分の 1 レートでサンプリングされるため、特にその名前が付けられました。
(15:10) Apple GPU は、PVRTC や ASTC などのブロック圧縮ピクセル フォーマットをサポートしており、従来のピクセル フォーマットの可逆圧縮もサポートしています。 A13 GPU は ASTC HDR をサポートします。講演内の HDR 環境マップの例では、非圧縮の小さな立方体マップには 3 MB が必要で、ASTC HDR によってメモリ フットプリントと帯域幅が大幅に削減できることが示されています。
(29:50) Xcode デモには、完全なトラブルシューティング リンクが表示されます。 [メモリ] グループに切り替え、メイン メモリから読み取られたバイト数で並べ替え、遅延位相エンコーダが最も多くのメモリを読み取っていることを確認します。 Per-Draw Counters モードに切り替えて、エンコーダ内の描画呼び出しのみを確認します。次にテクスチャ L1 バイト転送を押して [並べ替え] をクリックし、[バインドされたリソース] を入力すると、描画呼び出しが共有ストレージの RGBA16 浮動小数点キューブ マップをバインドして読み取っていることがわかります。
キーポイント:
- テクスチャ サンプル リミッタが高い場合、ミップマップ、フィルタリング オプション、異方性サンプル数、ピクセル サイズ、およびテクスチャ圧縮が最初にチェックされます。
- テクスチャ書き込みリミッターが高い場合は、ピクセル サイズ、ピクセルごとの一意の MSAA サンプル、および書き込みがコヒーレントであるかどうかを確認します。
- 共有ストレージのサンプリングされたテクスチャは、自動的に可逆テクスチャ圧縮を使用できません。デモではプライベート ストレージに変更することを推奨しています。
- プライベート ストレージが利用できない場合、Blit Command Encoder を使用して GPU のリソースを明示的に最適化できます。
- メモリ帯域幅カウンターは、システム メモリから GPU へのデータ転送を測定します。テクスチャまたはバッファ リミッターが両方とも高い場合は、対応するリソースを最初に最適化する必要があります。
タイル メモリ、バッファ、GPU LLC: データを適切なレベルに配置する
(17:34) タイル メモリは、Threadgroup および Imageblock データの保存に使用される高性能メモリです。これは、タイル シェーダ、スレッドグループ メモリ、レンダー パス カラー アタッチメント、プログラム可能なブレンディング、通常のブレンディングなどのシナリオに現れます。タイル メモリは、ツールやドキュメントではイメージブロック メモリまたはスレッドグループ メモリとして示されることがよくあります。
キーポイント:
- タイル メモリ リミッターが高い場合、スレッドグループ アトミックを減らすためにセッションを推奨します。
- アルゴリズムが許可する場合は、スレッドグループの並列削減または SIMD レーン操作を検討してください。
- スレッドグループのメモリ割り当てとアクセスは 16 バイトに揃える必要があります。
- メモリのアクセスパターン自体も効率に影響を与えるため、アクセス順序を再配置することで改善できます。
(18:52) メタル バッファもデバイス メモリによってサポートされていますが、シェーダー コアによってのみアクセスされます。バッファ データには、デバイスと定数の 2 つのアドレス空間があります。デバイスは、フラグメントまたは頂点インデックスによってアクセスされるデータに適しています。 constant は、多くの頂点またはフラグメントによって共同で使用される読み取り専用データに適しています。
キーポイント:
- バッファー リミッターが高い場合は、まずデータをより密に詰めることができるかどうかを確認します。
- 不要なデータ幅を減らすには、より小さい型を使用します。
- ロードとストアをベクトル化してみます。
- デバイスのアトミックおよびレジスタのスピルを回避します。
- 一部のシナリオでは、ALU と TPU の異なるキャッシュを利用するために、データをテクスチャに変更できます。
(20:08) GPU 最終レベル キャッシュ (GPU LLC) はすべての GPU コアによって共有され、テクスチャ データとバッファ データをキャッシュし、デバイス アトミックも保存します。空間的局所性と時間的局所性が最適化されています。 GPU LLC に過度の負荷をかけるのではなく、最初にタイル メモリを使用することをお勧めします。テクスチャまたはバッファ リミッタの両方が同時に高い場合は、テクスチャまたはバッファを最初に処理する必要があります。
占有率と HSR は他のカウンターと一緒に読み取る必要があります
(23:24) 占有率は、GPU が現在使用しているスレッド容量を測定します。内部リソースが十分であり、スケジュールするコマンドがある場合、GPU は使用可能なスレッドを切り替えることでレイテンシーを隠します。講演者らは、占有率が高いことも低いこと自体は問題ではないと強調した。
キーポイント:
- 開発者は、スレッドグループごとの最大スレッド数、SIMD レーンの実行幅、静的スレッドグループのメモリ長など、コンピューティング パイプラインまたはレンダリング パイプラインの静的プロパティをクエリする必要があります。
- 十分なフラグメント占有がある限り、低い頂点占有は許容されます。
- 全体的な占有率が低いのは、タイル/スレッドグループ メモリなどの内部リソースが枯渇していることが原因である可能性があります。あるいは、単純にレンダリング領域が小さすぎるか、計算グリッドが小さすぎることが原因である可能性があります。
- 占有率は、ALU、テクスチャ、バッファ、帯域幅などのカウンタと合わせて判断する必要があります。
(25:54) HSR (Hidden Surface Removal、隠れたサーフェスの削除) は、オーバードローを減らすために使用される早期可視化パスです。不透明なメッシュに対してピクセルパーフェクトであり、送信順序に依存しません。セッションは、保存されたピクセルに対するフラグメント シェーダーの呼び出しの比率という測定可能なメトリックを使用してオーバードローを定義します。
キーポイント:
- HSR の効率は、ラスタライズされたピクセル、フラグメント シェーダーの呼び出し、保存されたピクセル、Pre-Z テストの失敗などのカウンターによって測定できます。
- オーバードローを減らすときに、全画面パスとブレンディングを減らします。
- 描画順序は可視性の状態によって並べ替える必要があります。最初に不透明なメッシュ、次にアルファ テスト、破棄、深度フィードバック、そして最後に半透明のメッシュです。
- 不透明なメッシュと不透明でないメッシュを交互に配置しないようにします。
- カラー アタッチメント書き込みマスクを異なる不透明メッシュとインターリーブすることは避けてください。
重要ポイント
-
何をすべきか: ゲームに「パフォーマンス シナリオ」メニューを組み込み、戦闘、主要都市、後処理ストレス テストなどの再現可能なシーンに固定的にジャンプします。
-
実行する価値がある理由: セッション ワークフローは実際のフレームから始まり、固定されたシーンにより、Instruments と Xcode が同等のカウンターをキャプチャできるようになります。
-
開始方法: 各シーンにデバッグ入口を追加し、メタル システム トレースを記録し、最初に最高のパフォーマンス リミッターと最長のエンコーダーを比較します。
-
何をすべきか: 「テクスチャ帯域幅監査」レポートをマテリアル システムに追加します。
-
実行する価値がある理由: このデモでは、描画ごとのカウンターを通じて共有ストレージの RGBA16 浮動小数点キューブ マップを検出し、それをプライベート ストレージまたはブロック圧縮に変更することを推奨します。
-
開始方法: テクスチャのピクセル フォーマット、ミップマップ、ストレージ モード、および圧縮ステータスをスキャンし、RGBA32Float、ミップマップなし、および共有サンプル テクスチャを高優先チェック項目としてマークします。
-
内容: 半精度および分岐分岐チェックをシェーダー レビューに追加します。
-
実行する価値がある理由: セッションでは、F16 は 2 倍のレートであり、発散実行には同じ SIMD レーンの追加サイクルがかかることが明確に述べられています。
-
開始方法: ALU リミッターが最も高いエンコーダーから始めて、フラグメント シェーダーの FP32 入力、複雑な関数、暗黙的な変換、およびデータ依存の分岐を確認します。
-
やるべきこと: 透明なオブジェクト、アルファ テスト、および不透明なメッシュに対する安定した描画順序戦略を確立します。
-
実行する価値がある理由: HSR カウンターはオーバードローを測定できます。セッションでは、最初に不透明、次にアルファ/破棄/深度フィードバック、最後に半透明を使用することをお勧めします。
-
開始方法: レンダラーのレンダー キュー内の可視性状態ごとにバケットを作成し、保存されたピクセルに対するフラグメント シェーダーの呼び出しの比率を使用して変更を確認します。
関連セッション
- Apple GPU を Metal で利用する — TBDR、タイル メモリ、レンダー パスなどの完全なアーキテクチャの背景。
- Metal アプリを Apple シリコン Mac に導入 — Apple シリコン Mac での GPU アーキテクチャ移行の重要なポイントについて説明します。
- Apple シリコン Mac の Metal パフォーマンスの最適化 — Apple シリコン Mac での Metal ワークロードのパフォーマンス チューニングについて引き続き説明します。
- Xcode 12 で Metal アプリについての洞察を得る — Metal アプリの動作を理解するための Xcode 12 のデバッグ ツールを紹介します。
コメント
GitHub Issues · utterances