WWDC Quick Look 💓 By SwiftGGTeam
Discover Metal 4

Discover Metal 4

元の動画を見る

ハイライト

Metal 4 ではコマンドキュー・コマンドバッファ・エンコーダがすべて新しい MTL4 系オブジェクトに刷新されました。コマンドバッファとキューが分離され、マルチスレッドで並列にエンコードできるようになります。さらに MTL4ArgumentTable、residency sets、placement sparse resources、Barrier API、MTL4Compiler といった仕組みが追加され、tensor と機械学習用のエンコーダもファーストクラスのリソースとして組み込まれました。


主要内容

旧来の Metal アプリの最大の悩みは、バインディングモデルが現代のレンダリングが扱うリソース規模に追いつかないことでした。1 つのシーンに数千の materials、ジオメトリバッファ、テクスチャがあり、それらをすべて固定数の bind point に押し込まなければなりません。draw call ごとに CPU 側で再バインドが必要になり、エンコードスレッドがすぐにボトルネック化していました。Apple はこれまで bindless 方式で緩和を試みており、すべてのリソースを argument buffer に格納し、オブジェクトごとに 1 つだけバインドするスタイルでした。しかし、バインディング管理・メモリの常駐管理・shader のコンパイル・ML 演算は依然として各所に分散しており、コードは煩雑なままでした。

Metal 4 のアプローチは、このパイプライン全体を作り直すというものです。コマンドエンコーディング側では MTL4CommandQueue と MTL4CommandBuffer が導入され、command buffer と queue が分離されました。device から buffer を確保し、任意の queue にコミットできるため、マルチスレッドでの並列エンコードが自然にサポートされます(02:22)。エンコーダも統合が進んでおり、新しい unified compute encoder は compute・blit・acceleration structure 構築をまとめて扱います(02:53)。MTL4RenderCommandEncoder には attachment map が用意され、同じ encoder のなかで color attachment を切り替えられるため、無駄な再生成を省けます(03:06)。コマンドバッファのメモリは MTL4CommandAllocator が直接管理し、開発者側が制御できる形になりました(03:44)。

リソース管理の面では、バインディングテーブルが MTL4ArgumentTable に進化しました。サイズを必要に応じて宣言でき、bindless のシーンでは buffer binding は 1 つあれば十分です(05:43)。常駐管理は residency sets に置き換えられます。リソースの集合をまとめて MTL4CommandQueue にアタッチしておけば、そのキューに submit したコマンドからは自動的に参照可能になります(06:55)。Remedy の『Control Ultimate Edition』では residency sets を統合した結果、常駐管理のオーバーヘッドが大きく下がり、レイトレーシングを無効にしたときのメモリ使用量も低減しました(07:22)。物理メモリを超えるサイズが必要な場面では placement sparse resources を使います。リソースを確保する時点ではページを割り当てず、必要なときに placement heap からページを切り出す方式です(08:32)。並行同期は Barrier API に変更され、stage to stage の指定になりました。これは DirectX や Vulkan の barrier 概念に対応する考え方です(09:02)。

なかでも特に興味深いのが、ML がファーストクラス化された点です。Metal 4 では tensor が新しいリソース種別として追加され、任意の次元数をサポートします(14:22)。さらに machine learning command encoder が用意され、CoreML のネットワーク全体を 1 つのコマンドとして command buffer に挿入でき、レンダリング命令と barrier を共有して同期できます(15:05)。小規模なネットワークであれば Metal performance primitives を使って shader のなかで直接推論することも可能です。たとえば neural material evaluation では、latent テクスチャのサンプリング・推論・シェーディングをすべて 1 回の dispatch に融合できます(16:01)。


詳細

コマンドエンコーディング: 新しいオブジェクトと並列エンコード

Metal 4 のエンコーディングモデルは、新たなオブジェクト群を中心に構成されます。MTL4CommandQueue、MTL4CommandBuffer、MTL4CommandAllocator、MTL4RenderCommandEncoder、新しい unified compute encoder です。MTLDevice はそのまま継続して利用でき、device オブジェクトを置き換える必要はありません(02:08)。

// 擬似コード例:transcript で明示された API 名のみを使用
let queue = device.makeMTL4CommandQueue()
let allocator = device.makeMTL4CommandAllocator()
let cmdBuffer = device.makeMTL4CommandBuffer(allocator: allocator)

let renderEncoder = cmdBuffer.makeMTL4RenderCommandEncoder(descriptor: rpDesc)
// attachment map により、同じ encoder 内で color attachment を切り替えられる
renderEncoder.setAttachmentMap(mapA)
renderEncoder.drawPrimitives(...)
renderEncoder.setAttachmentMap(mapB)
renderEncoder.drawPrimitives(...)
renderEncoder.endEncoding()

queue.commit([cmdBuffer])

ポイントは次のとおりです。

  • MTL4CommandQueue: 新しいキューオブジェクトです。command buffer と分離されており、複数スレッドから同時に利用できます。
  • MTL4CommandAllocator: command buffer のメモリ使用を直接管理し、フレームワーク内部での暗黙的な確保を避けられます。
  • makeMTL4CommandBuffer(allocator:): command buffer は queue ではなく device が生成するため、並列にエンコードしてからどの queue に提出するかを後で決められます(02:29)。
  • MTL4RenderCommandEncoder + attachment map: 1 つの render encoder のなかで color attachment を動的に切り替えられるので、出力先ごとに encoder を作り直す必要がありません(03:16)。

バインディング: MTL4ArgumentTable

argument table は stage ごとに宣言し、stage をまたいで共有することもできます。bindless のシーンであれば buffer binding は 1 つで十分です(06:00)。

常駐: residency sets

residency set は一度設定すれば長期間使い回せます。常駐内容がほとんど変化しないなら、アプリ起動時にまとめて登録できます。ストリーミング読み込みのシーンでは別スレッドに更新を任せ、エンコード処理と並行させるのが向いています(07:09)。

スパースリソース: placement sparse

placement sparse なリソースは宣言時にページを持ちません。実行時に必要に応じて placement heap からページを割り当てる仕組みです(08:43)。注意点として、placement sparse のマッピング操作には MTL4CommandQueue が必要ですが、既存のレンダリングキューは依然として MTLCommandQueue です。両者は MTLEvent で同期させる必要があります(21:26)。

同期: Barrier API

Barrier は stage to stage で指定します。たとえば、compute encoder でグレースケール変換 shader を dispatch し、続く render encoder の fragment ステージでその結果を読む場合は、dispatch から fragment への barrier が必要になります(10:01)。具体例は公式の sample「processing a texture in a compute function」を参照してください。

コンパイル: MTL4Compiler と flexible render pipeline states

MTL4Compiler は device から独立しており、CPU 上のコンパイル実行タイミングを明確に制御できます。呼び出し元スレッドの QoS を継承するので、マルチスレッドで並行コンパイルする場合、優先度の高いスレッドからの要求が優先的に処理されます(11:45)。flexible render pipeline states を使うと、color state だけが異なる複数の pipeline で同じコンパイル済み Metal IR を共有できます。先に未特化の pipeline を作っておき、必要に応じて特化する流れになります(12:41)。

機械学習: tensor、ML encoder、shader ML

大規模なネットワークは ML command encoder 経由で扱います。CoreML パッケージを Metal のツールチェーンで Metal package に変換し、encoder に渡せばレンダリング命令と barrier を共有できます(15:32)。小規模なネットワークは shader ML が向いています。Metal performance primitives を使い、shader 内で直接行列演算や畳み込み演算を行う方式で、OS のコンパイラがデバイスに合わせてコードを inline 最適化してくれます(17:00)。

MetalFX: フレーム補間とデノイズの追加

MetalFX は従来の時間方向アップスケーラーに加えて、今年は frame interpolation(中間フレームを合成してリフレッシュレートを引き上げる機能)と denoising upscaler(低サンプル数のレイトレーシング結果をデノイズしながらアップサンプルする機能)が追加されました(18:2418:53)。

デバッグツールと最初の一歩

Xcode 26 には Metal 4 のプロジェクトテンプレートが同梱されており、新規 Game プロジェクト作成時に Metal 4 を選ぶだけで始められます(22:50)。API/Shader Validation、Metal Debugger、Metal Performance HUD、Metal System Trace はいずれも Metal 4 に対応しています。


重要ポイント

  • 何をするか: 既存の Metal アプリの shader コンパイル経路を MTL4Compiler に移行する

    • 価値の理由: リスクが最も低く、効果が直接的に表れます。MTL4Compiler は device から独立しており、呼び出し元スレッドの QoS を継承するため、並行コンパイル時には優先度の高いリクエストから処理されます。起動時の hitch 改善に効きやすいテーマです。
    • 始め方: device から MTL4Compiler を 1 つ確保し、これまで makePipelineState を呼んでいた箇所を新しい API に切り替えます。shader や pipeline descriptor には手を入れず、コンパイル入口だけを差し替えるところから始めます。
  • 何をするか: 散在している useResource / makeResident 呼び出しを residency sets に置き換える

    • 価値の理由: Remedy の『Control Ultimate Edition』での実測では、レイトレーシングを無効化した場合のメモリ使用量が顕著に下がり、常駐管理の CPU オーバーヘッドも同時に低下しました。導入コストも低めです。
    • 始め方: リソースのライフサイクルに沿ってグルーピングします(永続・ステージ単位・ストリーミング)。グループごとに residency set を作り、永続グループはアプリ起動時に流し込んで MTL4CommandQueue にアタッチし、ストリーミンググループはロードスレッドで更新します。
  • 何をするか: 物理メモリを超過するテクスチャを placement sparse に作り直す

    • 価値の理由: 同一のアセットでより広いデバイスレンジをカバーできるようになります。低スペック機では低 LOD のページだけ常駐させ、ハイエンド機では全ページを常駐させる、といった使い分けが可能になり、複数バージョンのアセットを用意する必要がなくなります。
    • 始め方: 大きめのテクスチャ(地形の virtual texture、UI atlas など)から着手します。placement sparse として宣言し、placement heap を作成し、可視性スクリプトでページの確保と解放を制御します。マッピング操作は MTL4CommandQueue で行い、既存の MTLCommandQueue とは MTLEvent で同期します。
  • 何をするか: MetalFX の時間方向アップスケール + フレーム補間で高リフレッシュレートを実現する

    • 価値の理由: 1/4 ピクセルでレンダリングして 1 フレームを補間すれば、理論上はレンダリング負荷の 7/8 を削減できる計算になります。120Hz を狙うゲームにとって、コストパフォーマンスが最も高い一歩です。
    • 始め方: まずは MetalFX の時間方向アップスケール(2x 倍率からスタート)を組み込み、目立つアーティファクトがないことを確認してから frame interpolation を追加します。Metal Performance HUD で実フレームレートとレイテンシをリアルタイムに観測しながら進めます。
  • 何をするか: Shader ML + Metal performance primitives で neural material を実現する

    • 価値の理由: latent テクスチャは従来の PBR テクスチャセットよりも体積が大幅に小さく、テクスチャ帯域が下がります。推論・サンプリング・シェーディングを 1 回の dispatch に融合できるため、device memory 上でデータを行き来させる必要もなくなります。
    • 始め方: PyTorch / CoreMLtools で小型の material decoder を学習し、CoreML パッケージとして書き出します。shader 内で Metal performance primitives 経由で呼び出し、サンプリング済みの latent を tensor として入力に渡し、albedo / normal / roughness を出力させます。

関連セッション

コメント

GitHub Issues · utterances