ハイライト
Apple は、バイナリ アーカイブ、ダイナミック ライブラリ、および新しいオフライン ツール チェーンを Metal に追加しました。これにより、開発者はアプリで GPU バイナリを収集、シリアル化、配布できるようになり、パイプライン状態オブジェクトを初めて作成する際のバックエンドのコンパイル コストが削減されます。
主要内容
大規模な Metal ゲームを初めて開始すると、多くの場合、大量のパイプライン ステート オブジェクト (PSO) が作成されます。これによりゲーム中の滞りは軽減されますが、ロード画面にコストが集中します。シェーダーがオフラインで AIR (Apple Intermediate Representation、中間表現) にコンパイルされている場合でも、デバイスは各 PSO の作成時に現在の GPU で実行可能なマシン コードを生成します。
システムレベルのメタル シェーダ キャッシュにより、後続の操作が高速化されます。開発者は、最初のインストール時、デバイスの再起動後のコールド スタート時、またはキャッシュがまだいっぱいになっていないときに、バックエンド コンパイルを待つ必要があります。ユーティリティ シェーダ コードの共有にも同様の問題があります。複数の計算パイプラインが関数の同じバッチを繰り返しコンパイルし、重複したマシン コードをメモリに保存する可能性があります。
このセッションでは、問題を 3 つのワークフローに分割します。 Metal Binary Archive を使用すると、アプリケーションはパイプラインのコンパイル結果を明示的に収集、保存、配布できます。 Metal Dynamic Libraries を使用すると、計算ユーティリティ関数を動的ライブラリの形式で再利用できます。 Metal Developer Tools は、AIR、スタティック ライブラリ、ダイナミック ライブラリ、シンボル インスペクション、バイナリ スライス管理をコマンド ラインに組み込み、Windows ビルド環境もサポートします。
詳細
バイナリ アーカイブ: パイプライン キャッシュを明示的に管理する
(03:03) バイナリ アーカイブにより、アプリケーションはパイプライン キャッシュを直接制御できるようになります。開発者は、コンパイルされたパイプライン関数をアーカイブに収集し、レベル、シーン、または一般的に使用されるパイプラインごとにグループ化し、デバイスから収集して、同じ GPU とシステム バージョンを備えた互換性のあるデバイスに配布できます。デバイスに互換性がない場合、Metal はランタイム コンパイルに戻ります。
空のアーカイブを作成するMTLBinaryArchiveDescriptor始める。
let descriptor = MTLBinaryArchiveDescriptor()
descriptor.url = nil
let binaryArchive = try device.makeBinaryArchive(descriptor:descriptor)
キーポイント:
MTLBinaryArchiveDescriptor新しいアーカイブを作成するか、既存のアーカイブをディスクからロードするかを決定します。descriptor.url = nil新しい空のアーカイブを作成することを示します。device.makeBinaryArchive現在からMTLDeviceアーカイブを作成すると、後で収集されるバイナリが GPU の互換性に関連します。
(06:47) アーカイブはすべてのパイプラインを自動的に収集しません。アプリケーションは、対象となる記述子を追加する必要があります。その後、Metal がバックエンド コンパイルを実行し、マシン コードをアーカイブに入れます。
// Render pipelines
try binaryArchive.addRenderPipelineFunctions(with: renderPipelineDescriptor)
// Compute pipelines
try binaryArchive.addComputePipelineFunctions(with: computePipelineDescriptor)
// Tile render pipelines
try binaryArchive.addTileRenderPipelineFunctions(with: tileRenderPipelineDescriptor)
キーポイント:
addRenderPipelineFunctionsレンダー パイプライン関数を収集します。addComputePipelineFunctionsコンピューティング パイプラインの関数を収集します。addTileRenderPipelineFunctionsタイル レンダリング パイプラインの関数を収集します。- 記述子が追加されると、バックエンドのコンパイルがトリガーされ、目的はマシンコードをアーカイブに書き込むことです。
(06:56) アーカイブを使用する場合は、パイプライン記述子にフックします。binaryArchivesアレイ上で。メタルは配列順に検索します。ヒットすると、PSO の作成はバックエンドのコンパイルをスキップし、システム シェーダー キャッシュに書き込めなくなります。
// Reusing compiled functions to build a pipeline state object from a file
let renderPipelineDescriptor = MTLRenderPipelineDescriptor()
// ...
renderPipelineDescriptor.binaryArchives = [ binaryArchive ]
let renderPipeline = try device.makeRenderPipelineState(descriptor:
renderPipelineDescriptor)
キーポイント:
binaryArchivesパイプラインの作成時に、どのアーカイブを検索するかを宣言します。- 配列の順序は検索順です。
makeRenderPipelineStateレンダー PSO を作成するのは依然として呼び出しサイトですが、関数バイナリはアーカイブから直接取得される場合があります。
(07:15) 収集が完了すると、アプリケーションはアーカイブを書き込み可能な場所にシリアル化します。セッションはまた、アーカイブ ファイルがプロセスにメモリ マップされることを思い出させます。使用されなくなったアーカイブを解放すると、仮想アドレス空間を解放できます。
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let archiveURL = documentsURL.appendingPathComponent("binaryArchive.metallib")
try binaryArchive.serialize(to: NSURL.fileURL(withPath: archiveURL))
キーポイント:
serialize収集したアーカイブを次の場所に書き込みます.metallib書類。- このファイルは次回の起動時に再利用することも、GPU バイナリ アセットとして配布することもできます。
- よく使用されるパイプラインとサブレベルのパイプラインをグループに保存すると、未使用のキャッシュを適時に解放するのに役立ちます。
(07:26) 次回起動時に、記述子url既存のファイルをポイントすると、Metal はアーカイブをロードし、それを後続のパイプライン作成にすぐに使用します。
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let serializeURL = documentsURL.appendingPathComponent("binaryArchive.metallib")
let descriptor = MTLBinaryArchiveDescriptor()
descriptor.url = NSURL.fileURL(withPath: serializeURL)
let binaryArchive = try device.makeBinaryArchive(descriptor: descriptor)
キーポイント:
descriptor.url既存のアーカイブを指定すると、呼び出しによってバイナリがディスクにロードされます。- ロードされたアーカイブをパイプライン記述子に入れることができます
binaryArchives。 - アーカイブが完了したかどうかをデバッグしたい場合は、前述のトランスクリプトを使用できます。
failOnBinaryArchiveMiss、ミス時に nil を直接返すことができます。
(10:35) Apple は Epic Games の Fortnite ワークロードでこれを行いました。このワークロードには 11,000 を超える PSO があります。 1,700 の PSO 関数バリアントを収集したアーカイブをプリセットした後、パイプラインのビルド時間は 1 分 26 秒から 3 秒に短縮され、約 28 倍速くなりました。
ダイナミック ライブラリ: 計算ユーティリティ マシン コードを再利用する
(13:52)MTLDynamicLibrary共有コンピューティング ユーティリティ コード用。古いモデルでは、複数のパイプラインがユーティリティ関数の同じバッチに依存している場合、バックエンドのコンパイルとマシンコードの保存が繰り返し発生する可能性がありました。ダイナミック ライブラリは、これらの関数をリンク可能、ロード可能、シリアル化可能なマシン コード コレクションにします。
実行時に動的ライブラリを作成する場合、最初に使用しますMTLCompileOptionsダイナミック ライブラリの種類とインストール名を指定して、MTLLibrary生成するMTLDynamicLibrary。
let options = MTLCompileOptions();
options.libraryType = .dynamic;
options.installName = "@executable_path/myDynamicLibrary.metallib"
let utilityLib = try device.makeLibrary(source: dylibSrc, options: options)
let utilityDylib = try device.makeDynamicLibrary(library: utilityLib)
キーポイント:
libraryType = .dynamicこのライブラリが動的ライブラリとして使用されることを示します。installNameこれは、パイプラインの作成時にリンカーが検索するダイナミック ライブラリの名前です。makeDynamicLibraryMetal コード バックエンドをマシン コードにコンパイルします。 session は、このコンパイルは動的ライブラリの作成時にのみ行う必要があると述べています。
(17:59) 動的ライブラリに依存するカーネルがコンパイル中に渡されるMTLCompileOptions.libraries依存関係を宣言します。コンパイル フェーズでは、一致する関数シグネチャが少なくとも 1 つあることを確認し、パイプラインの作成時に実際のバインディングの実装が行われます。
let options = MTLCompileOptions()
options.libraries = [ utilityDylib ]
let library = try device.makeLibrary(source: kernelStr, options: options)
キーポイント:
options.librariesこのカーネルがどのダイナミック ライブラリにリンクするかを Metal に指示します。- コンパイルはシンボルの欠落がチェックされ、一致する署名がない場合は失敗します。
- パイプラインの作成時に特定の実装が選択されるため、動的ライブラリは代替のコンピューティング動作をサポートできます。
(19:37) セッションも紹介insertLibraries。それは似ていますDYLD_INSERT_LIBRARIES注: コンピューティング パイプラインを作成するとき、リンカーは最初に挿入されたライブラリを検索し、次に最初にリンクされたライブラリを検索します。この機能により、ミドルウェアがフックを公開できるようになり、ユーザーはエントリ関数が配置されている場所を再構築することなく、カスタマイズされたカーネル動作を提供できるようになります。MTLLibrary。
(20:36) 動的ライブラリは、アプリでシリアル化して配布できます。シリアル化の結果には、プリコンパイルされたバイナリと汎用 AIR が含まれます。ターゲットデバイスがプリコンパイルされたスライスを使用できない場合、Metal は AIR から再コンパイルされます。このコンパイル結果はメタル シェーダー キャッシュには入らないため、セッションを再度シリアル化し、次回ロードすることをお勧めします。
Metal 開発者ツール: シェーダー ビルドを自動化可能なステップに分割する
(23:55) Ravi が新しい Metal Developer Tools をデモします。ツール チェーンは CPU を利用してモデルを構築します。metalソースファイルをAIRにコンパイルし、metal-libtool静的ライブラリを作成し、リンカは実行可能ファイルまたは動的 MetalLib を生成できます。静的リンクでは、ユーティリティ コードが各実行可能 MetalLib にコピーされるため、展開は簡単になりますが、バンドルのサイズが増加する可能性があります。
(26:05) ダイナミック ライブラリのコマンド ライン エントリはリンカーです-dynamiclibそして-install_name。install_nameこれはロード コマンドに書き込まれ、ローダーが実行時にダイナミック ライブラリを見つけるために使用します。金属サポート@loader_pathそして@executable_path、前者はロード コマンドを含む MetalLib パスに解決され、後者はエントリ関数を含む実行可能 MetalLib パスに解決されます。
(29:53) 動的リンクでは、開発者がシンボルの可視性を管理することも必要です。デフォルトでは、Metal はライブラリ内のすべてのシンボルをエクスポートします。 2 つの動的ライブラリが誤って同じ名前の関数をエクスポートすると、実行時にローダーが予期しない実装にバインドされる可能性があります。セッションがおすすめですmetal-nmエクスポートシンボルを確認して使用してくださいstatic、匿名名前空間、可視性属性、名前空間、およびexported-symbols-listインターフェイスの境界を制御します。
(31:02)metal-lipo収集された MetalLib スライスを表示およびマージするために使用されます。セッションの例では、A13 デバイスから収集された MetalLib には、A13 バックエンド コンパイル スライスと一般的な AIR スライスが含まれています。 A13 デバイスはバックエンド コンパイルをスキップできますが、他のデバイスは引き続き AIR からフォールバックできます。 A12、A13、およびその他のスライスを結合すると、より多くのデバイスの起動エクスペリエンスが向上しますが、App バンドルのサイズが増加します。
(34:40) 最後に、Windows 上の Metal Developer Tools を使用すると、開発者は macOS、Windows、またはハイブリッド環境で Apple プラットフォーム用の MetalLib を構築できます。既存の Windows アセット ファームを持つゲーム チームやグラフィックス チームにとって、これはシェーダー ビルドを既存のインフラストラクチャにプラグインできることを意味します。
重要ポイント
-
やるべきこと: 大規模なゲーム用の最初のビルド パイプライン アセット パッケージを作成します。 実行する価値がある理由: バイナリ アーカイブは、主要な PSO のバックエンド コンパイル結果をアプリで配布できるため、セッション内のフォートナイト ワークロードのパイプライン ビルド時間が 1 分 26 秒から 3 秒に短縮されました。 開始方法: メイン メニュー、キャラクター選択、コア レベルを含むシーンをビルド デバイスで実行し、呼び出します。
addRenderPipelineFunctions、addComputePipelineFunctionsアーカイブを収集し、シリアル化し、バンドルにまとめます。 -
何をすべきか: 再利用された計算後処理関数を Metal 動的ライブラリに分割します。 実行する価値がある理由:
MTLDynamicLibrary複数のコンピューティング パイプラインで同じユーティリティ マシン コードを共有できるため、バックエンドのコンパイルとストレージの繰り返しが削減されます。 開始方法: ユーティリティ シェーダの安定した関数シグネチャをセットアップし、.dynamic図書館とinstallName、カーネルのコンパイル中に渡されますMTLCompileOptions.libraries依存関係を宣言します。 -
やるべきこと: 画像処理アプリ用の交換可能なフィルター フックを作成します。 実行する価値がある理由:
insertLibrariesパイプライン作成フェーズで最初に挿入されたダイナミック ライブラリを検索させることができ、セッション サンプルはそれを使用して全画面後処理のピクセル カラー関数を変更します。 開始方法: デフォルトのダイナミック ライブラリと置換ダイナミック ライブラリの両方によってエクスポートされる関数インターフェイスを定義し、コンピューティング パイプラインの作成時にユーザーの選択に従って対応するライブラリを挿入します。 -
何をすべきか: シェーダー ビルドを CI 監査に組み込みます。 実行する価値がある理由:
metal-libtool、metal-nm、metal-lipoライブラリ構築、シンボルチェック、スライス管理をそれぞれカバーしており、反復可能なリリースプロセスに慣れるのに適しています。 開始方法: CI は最初に AIR を生成し、次に静的または動的 MetalLib をビルドし、エクスポートされたシンボルとリリース パッケージ内の GPU スライス マニフェストを確認します。
関連セッション
- Metal で Apple GPU を利用する - Apple GPU の TBDR、タイル メモリ、帯域幅モデルを理解すると、どのパイプラインが事前にコンパイルする価値があるかを判断するのに役立ちます。
- GPU カウンターを使用して Metal アプリとゲームを最適化する — GPU カウンターを使用して、フレームや読み込みを実際に遅くしている Metal ワークロードを見つけます。
- Xcode 12 を使用して Metal アプリについての洞察を得る — Xcode 12 の Metal キャプチャ、デバッガー、およびパフォーマンス洞察ツールについて引き続き学習します。
- Metal で GPU 側のエラーをデバッグする — より複雑なシェーダー ビルド プロセスを導入した後、Xcode を使用して GPU 側のエラーを特定します。
- Metal 関数ポインターについて知る - 関数ポインターとダイナミック ライブラリは両方とも Metal 2020 のシェーダー拡張性アップデートの一部です。
コメント
GitHub Issues · utterances