WWDC Quick Look 💓 By SwiftGGTeam
Target and optimize GPU binaries with Metal 3

Target and optimize GPU binaries with Metal 3

元の動画を見る

ハイライト

Metal 3 を使用すると、開発者は GPU バイナリ生成をプロジェクトのビルド段階に移すことができ、ランタイム ラグ、初回起動時間、新しいレベルのロード時間、シェーダ ボリューム、およびコンパイル時間を短縮するためのサイズ最適化コンパイル オプションが提供されます。


主要内容

Metal アプリケーションの遅延は、描画呼び出し自体では発生しないことがよくあります。

開発者によって作成されましたMTLRenderPipelineStateMetal は、オンザフライで GPU バイナリを生成する必要がある場合があります。このステップは CPU を消費します。起動ページでこの問題が発生した場合、ユーザーは待ち時間が長くなります。フレームの途中で発生すると、コマンドのエンコードが中断され、画面が落ちます。 (01:11)

以前は、Metal ソースを次のようにプリコンパイルできました。.metallib、Metal のファイル システムをキャッシュさせることもできます。MTLBinaryArchiveすでに生成された GPU バイナリを再利用します。ただし、これらのアーカイブは実行時に生成する必要があります。 Metal 3 はそのギャップを埋めます。パイプライン記述子を JSON 形式で Metal パイプライン スクリプトに書き込み、プロジェクト構築フェーズ中に Metal ソースまたは Metal ライブラリとともにツール チェーンに渡して、バイナリ アーカイブを直接生成します。 (02:20)

結果は簡単です。アプリが起動すると、生成されたアーカイブのみがロードされ、PSO の作成は軽量の操作になります。レベルをロードするときに必要な CPU コンパイルが少なくなります。また、レンダリング プロセス中に安定したフレーム レートと引き換えにウォームアップ フレームを使用する必要はありません。 (02:48)

このセッションの 2 番目のトピックはコンパイル サイズです。 Metal コンパイラは、デフォルトで実行時のパフォーマンスを優先し、関数のインライン化とループの展開を行います。大規模なシェーダでは、これらの変換によってプログラムが大幅に肥大化し、コンパイルに時間がかかる可能性があります。 Xcode 14 の Metal 3 では、サイズを拡張するこのタイプの最適化を制限するために、サイズの最適化が追加されています。 (09:04)

詳細

パイプライン スクリプトを使用して、オフラインで生成される PSO を記述します

(04:47) オフライン コンパイルには、新しい入力、メタル パイプライン スクリプトが必要です。 JSON であり、表現された内容は API のパイプライン記述子に相当します。

まず元の Objective-C コードを見てみましょう。それはから始まりますdefault.metallib関数を取り出して、レンダー パイプライン記述子を詳しく説明します。

// An existing Obj-C render pipeline descriptor
NSError *error = nil;
id<MTLDevice> device = MTLCreateSystemDefaultDevice();

id<MTLLibrary> library = [device newLibraryWithFile:@"default.metallib" error:&error];

MTLRenderPipelineDescriptor *desc = [MTLRenderPipelineDescriptor new];
desc.vertexFunction = [library newFunctionWithName:@"vert_main"];
desc.fragmentFunction = [library newFunctionWithName:@"frag_main"];
desc.rasterSampleCount = 2;
desc.colorAttachments[0].pixelFormat = MTLPixelFormatBGRA8Unorm;
desc.depthAttachmentPixelFormat = MTLPixelFormatDepth32Float;

キーポイント:

  • MTLCreateSystemDefaultDevice()現在のシステムのデフォルトの Metal デバイスを取得します。 -newLibraryWithFile:既存のものから.metallibMetal ライブラリをロードします。 -newFunctionWithName:@"vert_main"頂点関数を指定します。 -newFunctionWithName:@"frag_main"フラグメント関数を指定します。 -rasterSampleCount = 2サンプル数をパイプライン状態に書き込みます。 -colorAttachments[0].pixelFormatそしてdepthAttachmentPixelFormatレンダー ターゲットの形式を文書化します。

同じ記述子を JSON として記述することもできます。ビルド ツールチェーンは、この JSON を読み取り、GPU バイナリを生成する関数と状態の組み合わせを認識します。

{
  "//comment": "Its equivalent new JSON script",
  "libraries": {
    "paths": [
      {
        "path": "default.metallib"
      }
    ]
  },
  "pipelines": {
    "render_pipelines": [
      {
        "vertex_function": "vert_main",
        "fragment_function": "frag_main",
        "raster_sample_count": 2,
        "color_attachments": [
          {
            "pixel_format": "BGRA8Unorm"
          }
        ],
        "depth_attachment_pixel_format": "Depth32Float"
      }
    ]
  }
}

キーポイント:

  • libraries.paths関数を提供する Metal ライブラリへのポインタ。 -render_pipelines生成されるレンダー パイプラインのリストです。 -vertex_functionそしてfragment_functionAPIの関数名に相当します。 -raster_sample_count対応するrasterSampleCount
  • color_attachments[0].pixel_format第一のカラー添付形式に対応します。 -depth_attachment_pixel_format深度アタッチメントの形式に対応します。

ランタイム アーカイブから JSON をアンプルする

(05:33) 実行時に PSO を作成するコードがプロジェクトにすでにある場合は、まず開発およびテスト中に記述子を収集し、アーカイブをディスクにシリアル化できます。

// Create pipeline descriptor
MTLRenderPipelineDescriptor *pipeline_desc = [MTLRenderPipelineDescriptor new];
pipeline_desc.vertexFunction = [library newFunctionWithName:@"vert_main"];
pipeline_desc.fragmentFunction = [library newFunctionWithName:@"frag_main"];
pipeline_desc.rasterSampleCount = 2;
pipeline_desc.colorAttachments[0].pixelFormat = MTLPixelFormatBGRA8Unorm;
pipeline_desc.depthAttachmentPixelFormat = MTLPixelFormatDepth32Float;

// Add pipeline descriptor to new archive
MTLBinaryArchiveDescriptor* archive_desc = [MTLBinaryArchiveDescriptor new];
id<MTLBinaryArchive> archive = [device newBinaryArchiveWithDescriptor:archive_desc error:&error];
bool success = [archive addRenderPipelineFunctionsWithDescriptor:pipeline_desc error:&error];

// Serialize archive to file system
NSURL *url = [NSURL fileURLWithPath:@"harvested-binaryArchive.metallib"];
success = [archive serializeToURL:url error:&error];

キーポイント:

  • 作成の前半は前セクションと同じですMTLRenderPipelineDescriptor
  • MTLBinaryArchiveDescriptor新しいバイナリ アーカイブを作成するために使用されます。 -newBinaryArchiveWithDescriptor:error:得るMTLBinaryArchive物体。 -addRenderPipelineFunctionsWithDescriptor:error:記述子をアーカイブに追加し、GPU バイナリを生成します。 -serializeToURL:error:アーカイブをファイルに書き込み、後でパイプライン スクリプトを抽出できるようにします。

(06:01) 抽出ステップでは以下を使用しますmetal-source

metal-source -flatbuffers=json harvested-binaryArchive.metallib -o /tmp/descriptors.mtlp-json

キーポイント:

  • metal-source既存のアーカイブからコンテンツを読み取ります。 --flatbuffers=jsonJSON 形式での出力を必要とするパイプライン スクリプト。 -harvested-binaryArchive.metallib連載したばかりのアーカイブです。 --o /tmp/descriptors.mtlp-json出力ファイルのパスを指定します。

ビルドフェーズ中に GPU バイナリを生成する

(06:24) パイプライン スクリプトを取得したら、Metal ソースから直接アーカイブを生成できます。

metal shaders.metal -N descriptors.mtlp-json -o archive.metallib

キーポイント:

  • metalMetalコンパイルツールです。 -shaders.metal入力メタルソースです。 --N descriptors.mtlp-jsonパイプライン スクリプトをツールチェーンに渡します。 --o archive.metallib出力ライブラリをGPUバイナリで指定します。

(06:48) 入力がすでに Metal ライブラリである場合は、Metal トランスレータ ツールを使用できます。

metal-tt shaders.metallib descriptors.mtlp-json -o archive.metallib

キーポイント:

  • metal-tt既存のものに対処するshaders.metallib
  • descriptors.mtlp-jsonパイプライン記述子情報は引き続き提供されます。 -archive.metallibアプリバンドルにデプロイされた最終出力です。
  • セッションには、生成された Metal ライブラリに GPU バイナリが含まれており、ツール チェーンでサポートされているデバイスにデプロイできることが明確に記載されています。

オフラインで生成されたアーカイブをアプリにロードします

(07:07) ランタイム読み込みアーカイブが短い。ファイルの URL を記述子に入力し、アーカイブを作成します。

MTLBinaryArchiveDescriptor *desc = [MTLBinaryArchiveDescriptor new];
desc.url = [NSURL fileURLWithPath:@"archive.metallib"];
NSError *error = nil;
id<MTLDevice> device = MTLCreateSystemDefaultDevice();
id<MTLBinaryArchive> binaryArchive = [device newBinaryArchiveWithDescriptor:desc error:&error];

キーポイント:

  • MTLBinaryArchiveDescriptorロードするアーカイブについて説明します。 -desc.url生成されたarchive.metallib
  • NSError *errorアーカイブの作成時にエラーが発生します。 -MTLCreateSystemDefaultDevice()メタルデバイスを入手します。 -newBinaryArchiveWithDescriptor:error:ファイルから作成MTLBinaryArchive

Metal は、システムのアップデートまたはアプリのインストール中に、これらのオフラインで生成されたバイナリ アーカイブを非同期かつバックグラウンドでアップグレードし、将来のシステムや製品との互換性を確保します。 (07:18)

サイズの最適化を使用してシェーダーのボリュームを制御します

09:04optimize for size最初に大規模なシェーダ、特に呼び出しパスとループが深く、デフォルトの最適化コンパイル時間が異常に長いプログラムを試してみるのが適しています。実行時のパフォーマンスが低下する可能性があるため、コンパイル時間と実行時のパフォーマンスを比較する必要があります。 (09:39)

コマンドラインで使用される-Os

xcrun metal -Os large_shader.metal

# or

xcrun metal -c -Os large_shader.metal
xcrun metal -c     more_shaders.metal
xcrun metal large_shader.air more_shaders.air

キーポイント:

  • 最初のコマンドはコンパイル&リンクで有効になります。-Os
  • コマンドの 2 番目のセットは、最初に個別にコンパイルされます。.metalファイル、リンク.air書類。 --Osシェーダの一部のみを最適化するために、特定のコンパイル コマンドにのみ追加できます。
  • セッションは次のことを指摘します-Oslink コマンド以降のコマンドに渡す必要はありません。
  • このオプションは、以前に紹介した GPU バイナリのオフライン生成と併用できます。

(12:44) 実行時にソースからライブラリをコンパイルする場合は、次を使用しますMTLCompileOptions

MTLCompileOptions* options = [MTLCompileOptions new];
options.optimizationLevel = MTLLibraryOptimizationLevelSize;

NSString* source = @"...";
NSError* error = nil;
id<MTLLibrary> lib = [device newLibraryWithSource:source
                                          options:options
                                            error:&error];

キーポイント:

  • MTLCompileOptionsMetal ソース コンパイル オプションをホストします。 -optimizationLevelに設定MTLLibraryOptimizationLevelSize、サイズモードを有効にします。 -source実行時に提供される Metal ソース文字列です。 -newLibraryWithSource:options:error:指定された最適化レベルで作成されましたMTLLibrary

重要ポイント

  • シェーダーのプリコンパイル済みリストを作成: スタートアップ ページと最初のレベルで使用されるレンダー パイプライン記述子をパイプライン スクリプトとして記述します。実行する価値がある理由: これらの PSO は、最初のエントリー エクスペリエンスに影響を与える可能性が最も高くなります。始め方:すでに持っているものから始めるMTLRenderPipelineDescriptorJSON を比較して生成し、metal shaders.metal -N descriptors.mtlp-json -o archive.metallibビルドプロセスに追加されました。

  • アーカイブ収集スイッチをテスト パッケージに追加します: 実際のパイプライン記述子をテスト パッケージに追加しますMTLBinaryArchiveそして連載化。実行する価値がある理由: 実際のプロセスでは、手書きのリストでは見逃されていたパイプラインが明らかになります。開始方法: PSO が作成されたパスの隣にある呼び出しaddRenderPipelineFunctionsWithDescriptor:error:、テスト後に保存しますharvested-binaryArchive.metallib

  • 大きなシェーダー用に 2 セットのコンパイル インジケーターを作成します: 同じシェーダーはデフォルトの最適化を使用し、-Osコンパイルし、コンパイル時間、バイナリ サイズ、およびランタイム フレーム時間を記録します。実行する価値がある理由: セッションでは、サイズ モードは実行時のパフォーマンスを低下させる可能性があり、その利点はプロジェクト データに基づいて判断する必要があると明確に述べています。開始方法: まずコンパイルに最も遅いシェーダーを選択し、使用します。xcrun metal -Osとデフォルトのコマンドはそれぞれ 1 回実行されます。

  • 新しいレベルの読み込みをアーカイブ読み込みの優先順位に変更: 対応するコンテンツをレベル リソース パッケージに入れますarchive.metallib、レベルに入ったときに最初に作成されますMTLBinaryArchive。実行する価値がある理由: セッションでは、オフライン コンパイルの直接の利点として、新しいレベルのロード時間がリストされます。開始方法: レベルごとに独自のパイプライン スクリプトを維持し、構築時に対応するアーカイブを生成し、実行時にそれを使用します。MTLBinaryArchiveDescriptor.url負荷。

関連セッション

  • Discover Metal 3 - Metal 3 のパフォーマンス、ツール チェーン、レンダリング機能の更新についての全体的な理解。このセッションの入り口として適しています。
  • Metal 3 でバインドレス化 — 大規模なレンダラー リソースの構成と CPU/GPU のパフォーマンスに影響を与える Metal 3 のバインドレス ワークフローをさらに詳しく掘り下げます。
  • MetalFX アップスケーリングによるパフォーマンスの向上 — Metal 3 の別のパフォーマンス パスに焦点を当てます。レンダリング解像度を下げ、MetalFX を使用して出力をアップスケールします。
  • Metal による機械学習の高速化 — 同じ Metal トピックに属しており、機械学習トレーニング シナリオでの高速化を実現するための Metal と MPS Graph の使用を紹介しています。

コメント

GitHub Issues · utterances