ストリーミングはほとんどのブラウザと
Developerアプリで視聴できます。
-
OpenGL AppをMetalに移行する
MetalはAppleプラットフォームでGPUによる高速なグラフィックス処理と演算を行うための最新の基盤であり、OpenGL、OpenGL ES、OpenCLに代わるものです。このセッションでは、Metalのアーキテクチャと機能、およびOpenGLベースのAppをMetal APIに移行するための詳しいアプローチを紹介します。
リソース
関連ビデオ
WWDC21
WWDC19
-
ダウンロード
(音楽)
(拍手) Metalのセッションへようこそ GPU ソフトウェアパフォーマンス チームのライオネルです 私とサラとマックスで OpenGL AppをMetalへ移行する 方法を紹介します
昨年 OpenGL OpenGL ES そしてOpenCLが 非推奨になると発表しました iOS 13とmacOS Catalinaでの サポートは継続されますが 先を見据えましょう 新しいプロジェクトは Metalで始めましょう 既存のOpenGL Appを Metalへ移行したいなら このセッションが役立ちます
2014年に初めて発表されたMetalは 低オーバーヘッドで高効率 高性能な GPUのプログラミングAPIです
この5年で Appleの主要なフレームワークは Metalに対応しました よい成果も出ています もしアプリケーションが SpriteKitやSceneKit Core ImageやCore Animationを 使っているなら すでにMetalを使っています
エンジンのベンダとも協力しています UnityやUnreal Engine 4 Lumberyardなど Metalをうまく生かせます これらのエンジンを使っているなら よくご存じでしょう
もし独自のレンダラーを 作成している場合 Metalはとても有効です MetalはOpenGLとOpenCLの 機能を兼ね備えた― Unified APIです アプリケーションにマルチスレッド レンダリングを可能にします CPUのオペレーションに 大きな負荷がかかる場合 アプリケーションの実行中 オーバーヘッドを減らす努力をしてます
Metalのシェーディング言語は C++言語です アプリケーション内のシェーダは すべてプリコンパイルされます 例えば 幅広い種類のシェーダが 簡単に使えます
そして大事なのは デバッグツールと最適化ツールが Xcodeに組み込まれていること Metalへ移植すれば フルサポートを受けられます このセッションでは いくつかのステップをご紹介します OpenGLからMetalへの移行について― アプリケーションを比較して 説明します 概説として 簡単に OpenGLのアプリケーションを見ましょう まずレンダリングに使う ウィンドウをセットアップします 次にバッファやテクスチャなどの リソースを作ります GLSLで書かれたシェーダを 実装します GLでレンダリングする前に オブジェクトのステートが必要です 例えばGLプログラム フレームバッファオブジェクト 頂点配列オブジェクトなどです
リソースを初期化したら レンダーループが動き フレームが書けます
それぞれのフレームでは リソースのアップデート フレームバッファの結びつけ グラフィックステートの設定 ドローコールを決めます これをフレームバッファごとに 行います シャドウマップやライティングパス ポストプロセスなどがあるでしょう
そして最後に 最終出力イメージが現れます 簡単ですね Metalの順序も似ています オリジナル部分の更新情報と 新たな仕様も紹介します 順序に大きな違いはありません 同じような方法で 使うことができます
私たちは新しいコンセプトを 再導入しました OpenGLとMetalの並行性を保ち 2つのAPIを上手く移行させるためです
グラフィックのチュートリアルで 最初に学ぶのは ウィンドウの作成方法です ウィンドウ サブシステムから 始めましょう GLもMetalも同じコンセプトですが 実行方法は少し違います
アプリケーションに必要なのは ドローイングサーフェスの構成です ビューとビューデリゲートは APIとウィンドウシステムの下層間の インターフェイスを対処します
GLに対応するため これらの フレームワークを使っているなら 同等のフレームワークが Metalにあります NSOpenGLViewやGLKViewは MTKViewへマップできます EAGLLayerと一緒に Core Animationを使用してる場合 CAMetalLayerを使用できます
例としてGLKViewを使います エントリポイントは1つです 最後のフレームから解像度が 変わっていないか確認し 必要ならレンダーループで サイズの修正ができます Metalkitでは少し違います drawableを変更したい場合 別の関数があります スクリーンの回転や ウィンドウのサイズ変更ができます ドロウファンクション内で リソースの再配置が必要か チェックしなくても コードに組み込まれています
さらに順応性を求めるなら CAMetalLayerが提供されています 表示のバッキングレイヤとして 使えます CAEAGLLayerは属性が定義されています カラーフォーマットなどです CAMetalLayerで構成できるのは drawableのサイズ ピクセルフォーマット 色空間などです CAMetalLayerは膨大なテクスチャを 保持してます 次のdrawableをコールし フレームに表示します 重要なコンセプトなので 後ほど詳しく話します
ウィンドウの完了です 次はMetalでの 新しいコンセプトについて コマンドキュー コマンドバッファ コマンドエンコーダです これらのオブジェクトはMetal内で 同時にGPUへ働きかけます openGLContextの基礎は サブミッションを行います
openGLは潜在的なAPIです いつ作動するかを示す コードはありません デベロッパはグラフィックの働きを あまり管理できません シェーダはコンパイルされ リソースストレージが割り当てられ 妥当性確認が行われ GPUが実際に実行します openGLContextは 大きなステートマシーンで 典型的なワークフローは こちらです アプリケーションが openGLContextを生成 スレッドにセットし任意の OpenGLコメントをコールします コメントはコンテキストで記録され ある時点で実行されます これを詳しく見てみましょう アプリケーションが これらのコールを送りました ステートの変更とdrawのコールです 完璧なシナリオはコンテキストが GPUコメントに変換され 内部バッファへ蓄積されます これが溜まるとGPUへ送られます 実行のためにglFlushを書き込んだ場合 その時点で実行されます GPUはその前でも 実行することが可能です 例えば― このdrawコールに 依存関係を持たせます この時点でプログラムは実行されます ひどい機能停止に陥ります プログラムが提出されるかは 場合によります OpenGLの欠点の1つですね パフォーマンスに一貫性がありません 小さな修正でスムーズになります
Metalは明示的なAPIです どの作業をいつ実行するか アプリケーションで決められます
MetalはopenGLContextを 内部のワーキングオブジェクトに 分割するコンセプトです アプリケーションが最初に生成する オブジェクトは MTLDevice オブジェクトです GPUの抽象的表現です 次はオブジェクトのキーの MTLCommandQueueです コマンドの順番を そのままGPUへ送ります コマンドバッファの形で送られます コマンドバッファは GPUが実行するコマンドのリストです これがOpenGLでの コマンドバッファの概念です もう少し詳しく見てみましょう
アプリケーションは 直接 コマンドバッファへ指令を渡さず MTLCommandEncoderを 作成します 3つの主なエンコーダを紹介します まず MTLBlitCommandEncoder リソース周辺のコピー処理を行います
コマンドエンコーダはAPIコードを GPUの命令に変換します そしてコマンドバッファへ 書き換えられます 例えばリソースのコピー処理の ブリットなど 一連のコマンドが エンコードされます するとエンコードが終了し エンコーダオブジェクトが放出されます
また MTLComputeCommandEncoderは OpenCLと同様の 並列演算処理を行います いくつかのカーネルをエンキューし コマンドバッファへ書き換え エンコーダを解放します
3つ目は MTLRenderCommandEncoderです ステートの変更やdrawのコールを エンキューしエンコードします
残ったのはコマンドバッファです ワークロードは溜まっていますが GPUはどの作業も実行していません Metalはオブジェクトと エンコードされたコマンドを CPUで作成します アプリケーションが コメントのエンコードを終えると コマンドバッファが 明確な指示を出します GPUはそれらのコマンドを実行します エンコードされたコマンドです OpenGLとMetalを比べてみましょう OpenGLはGPUに対して 直接のコントロール力がありません glFlushやglFinishなどの コードに頼るのみです glFlushはコマンドの実行を 予定した時に強制させます glFinishはGPUの処理が 完全に履行されるまで待ちます これらのコマンドの前に 処理が行われる場合もあり 機能停止や失速につながります Metalは同等のファンクションを提供し 明確に実行のコントロール力を 得られます コマンドを待機させることは 必要ない限り お勧めしません 代わりにコマンドバッファを利用し コールバックを追加してください コマンドバッファの実行の完了後 アプリケーションに通知されます その間 CPUは他の動作ができます
コマンドキュー コマンドバッファ コマンドエンコーダを紹介しました 次はリソースの説明です
グラフィックアプリケーションには 主に3つのリソースがあります バッファ テクスチャ サンプラーです まずバッファを説明します バッファオブジェクトにはメモリが 関連付けされています APIのコードで オブジェクトステートとメモリを 変更することが可能です
例えばglBufferDataを使うと オブジェクトとメモリの修正ができます バッファの寸法もglBufferDataで 変更できます 古いオブジェクトや そのコンテンツも OpenGLによって内部的に破棄されます
MetalではAPIは似たバッファを作ります しかし一番の違いは 作られるサブジェクトが 変更不可なのです バッファをリサイズする場合 新規に作成し 古いものを破棄します
OpenGLもMetalも オブジェトを使う方法を 指し示す方法があります しかしOpenGLのusage enumは バッファオブジェクトのデータが どうアクセスされたかのヒントです ドライバはヒントを基に メモリの最適な置き場を決めます しかしストレージを直接 コントロールできません OpenGLが最終的な決断をします
MetalではAPIによる ストレージモードが提供され 特定のメモリがパターン化され 分配されています Metalはあなたに コントロール権を与えます オブジェクトクリエーションにおいて 重要な考えです テクスチャを説明したあと こちらを詳しく説明します
OpenGLのテクスチャには 内部サンプラーオブジェクトがあります サンプリングモードは サンプラーを通してセットします また テクスチャの外に サンプラーオブジェクトを作れます テクスチャの作成と結合の例です サンプラーをセットし 最後にデータを書き込みます
OpenGLには多くのAPIコールがあります データと一緒に 初期化テクスチャを作ります リソースを指定する バージョンもあります サンプラーの管理になると さらに増えます 果てしない量です
Metalのデザインのゴールは サンプラーAPIがすべての柔軟性に 対応することです Metalのテクスチャとサンプラー オブジェクトはバラバラで 作成後は変更できません
テクスチャの作成には ディスクリプタを作成します pixelFormat など テクスチャの大きさを定める― プロパティを書き込みます 重要なプロパティは ストレージモードです テクスチャの保存場所を定めます そしてディスクリプタを使い 変更不可のオブジェクトを作成します
まずサンプラーディスクリプタから プロパティを書き込み 変更不可のサンプラーオブジェクトを 作る方法もあります
テクスチャの画像の大きさは bytesPerRowで設定します OpenGLと同じように ロードするための領域を設定します テクスチャのreplaceRegionを使い 指定したポインタから データをテクスチャへコピーします
最初のテクスチャをロードすると 上下が逆になります Metalではテクスチャの座標が Y軸になるのが原因です さらにMetal APIはシステム内部で pixelFormatの変形は 遂行しません 必要なテクスチャフォーマットの アップロードが必要です
ストレージモードに戻ります OpenGLではドライバの選択で リソースの使い方が決められます レンダーバッファオブジェクトなどを 作成し ヒントを与えることは可能です しかし それはヒントにすぎず 実装の詳細は知ることができません
数分前にMetalのストレージモードを 紹介しました テクスチャディスクリプタを設定し バッファを作成することもできます 実際の例を見てみましょう 一番簡単なのは 共有ストレージモードです CPUとGPUの両方が リソースにアクセスできます バッファはオブジェクトの メモリバックを ここへ合せます iOSのテクスチャは 便利なファンクションをコールし イメージデータを設定し取り出せます
プライベートストレージモードも 使えますが GPUのみのアクセスになります 普段CPUからの アクセスが不要なものなら Metalの最適化が期待されます
データのコンテンツを埋められるのは GPUだけなので blitEncoderを使えばCPUからも データが埋められます 共有ストレージを使い 第2中間リソースにアクセスできます
音声はビデオメモリ専用です プライベートストレージに 保存されたデータは ビデオメモリのみに 割り当てられます
macOSには マネージドストレージモードがあり CPUとGPUからアクセスできます ビデオメモリ専用のシステムでは 効率のよいアクセスのため ミラーメモリの作成が必要になります データを同期させるため 明示的なコードが必要になります 例えばdidModifyRangeなどです
それぞれのストレージモードの 説明をしました macOSでは 静的アセットと レンダーターゲットのため プライベートストレージを使います 小さい動的なバッファは 共有ストレージモード 容量が大きく 更新が少ない場合は マネージドストレージモード
iOSでの静的アセットと レンダーターゲットは プライベートストレージ ディバイスが統合メモリや 様々なサイズの動的データの場合 共有ストレージが有効でしょう
次はグラフィックアプリケーションの シェーダの向上と 該当するAPIの紹介です
OpenGLでのシェーダのコンパイルには シェーダオブジェクトを作成します そしてglShaderSourceの置き換えと compilationのJITと 検査が必要になります このワークフローには 利点がありますが シェーダをコンパイルするたびに パフォーマンスコストがかかります
Metalでそれを効率よく進めるには 手早く 少ない頻度で行うことです ビルド時 Xcodeはすべての MetalのshaderSourceを デフォルトMetalライブラリファイルに コンパイルします ランタイム時の回復のため アプリケーションのバンドルへ加えます そのためランタイム時の コンパイルが避けられ コンパイルの時間を削り アプリケーションの負担も減ります 手順はアプリケーションの バンドルから Metalライブラリを作成 そこからシェーダファンクションを 取り込みます
OpenGLはC言語をベースにした GLSLを使います Metalのシェーディング言語は C++がベースです OpenGLデベロッパにも なじみがあるでしょう C++の構成はクラス テンプレート 構造体などです enumやnamespaceも定義できます GLSLではvectorとmatrix型が 埋め込まれています グラフィックに使えるオペレーションが 多数組み込まれ テクスチャやサンプラーのクラスも 埋め込まれています
MSLもグラフィックや計算に 向いています シェーダはコンパイル済みならば Xcodeがエラーや警告 ガイドを知らせてくれます
MSLの実際のコードを見て GLSLと比べてみましょう シンプルな頂点シェーダで行います GLSLが上でMSLが下です
まずプロトタイプのシェーダです GLSLはvoid main シェーダステージを 指定するものはありません glCreateShaderのコールで シェーダを確定します MSLではシェーダコードで シェーダステージを確定 vertex qualifierは それぞれのvertexでの 完璧なアウトプット実行を 示しています
GLSLでは どのシェーダの 入り口点もmainでコールします コマンドを受け取りvoidへ戻ります MSLの各入り口点には 異なる名前があります Xcodeでシェーダを組み立てる場合 コンパイラは前処理でインクルードし 分割できます C++のコードと同じです ランタイム時にはコンパイルされた Metalライブラリから 名前で関数の照会が可能です
次はインプットです GLSLはどの入り口点にも main関数があります すべてのインプットは 広範囲の実引数として渡されます Vertex属性と一様の変数に 当てはまります
Metalでは シェーダステージの すべての入力は エントリ関数の実引数です 二重の角かっこは C++の属性です
インプットの1つは 射影行列のモデルビューです
OpenGLでは データを変数に結合するために C++のコードに含まれるGLSLの名前に 注意が必要です 間違いが起こりやすい場所です
MSLでは均一統合のため 指標はデベロッパによって 明確にコントロールされます アプリケーションは 特定のスロットと結合します サンプルではスロット1です ここで重要なのは モデルビューのプロジェクションが すべてのvertexへ一定になるよう 意図していることです
シェーダのための別のインプットは vertex属性です GLSLでは別々の属性の インプットを使います MSLは独自にデザインされた構造が 使われます シェーダの どの呼び出しにも 独自の変数を受け取ります
すべてのインプットを シェーダに書き込めば すべての計算を遂行できます
GLSLのアウトプットは glTexCoordなどの可変の属性と 既定の変数に分かれます この場合gl Positionです
MSLでvertexシェーダのアウトプットは 独自の構造になります
vertexとvertexアウトプット構造です スクロールしてMSLがどうなったか 見てみましょう
GLSLではインプットのvertex属性が 別に定義されています Metalはそれらを 構造内で確定しています
MSLではvertexシェーダのインプットで 各ストラチャの要素に キーワードの属性があります 各属性に振り分けられます GLSLと同じように これらの指標は Metal APIで使われ vertexバッファストリームから vertex属性に割り当てられます
GLSLには特別なキーワードの 定義があります gl Positionはモデルの 射影に変換される― 頂点座標の変数を表します MSLのvertexアウトプットも同じように vertexシェーダアウトプットの 信号を送る― 特別なキーワードの位置は 構造体メンバの中です
GLSLのvectorタイプと同じで MSLはSIMDタイプを定義します simd.hヘッダはCPUとGPUの コードを共有できます しかし注意が必要です バッファのvectorとmatrixタイプは 16バイトです 半精度では8バイト あまり詰め込まれていません float3は12バイトあり アラインされると16バイトです データがCPUとGPUに最適に アクセスできるよう注意が必要です 必要ならBackedFormatもあります 使用する前にシェーダ内で アンパックする必要があります
GLSLとMSLの主な違いを 説明しました この移行をスムーズに 簡単に行うために マックスが すばらしいツールを 紹介します どうも (拍手)
こんばんは
Metalは 単なるAPIや シェーディング言語ではありません ツールの強力な集合体です 私はマックス Metalへの移植の手助けをします
スクリーンを見てください 古いOpenGLで作成された ドローコールです Metalへ移植しました 神殿と木をドローイングしたもので 全体が光で照らされています フラグメントシェーダを移行しましょう
まずOpenGLコードをコピーし Metalシェーダファイルへ ペーストしました すでにインプット構造体が できています 関数の原型もです 始めましょう まずはコンテンツの メインの関数をコピーし Metalの関数へ直接 貼ります ここでMetalの有能さが表れます シェーダはコンパイルされているため すぐエラーが起きます 詳しく見てみましょう
vectorタイプの名前が違っています vec2をfloat2に修正します vec3をfloat3へ vec4はfloat4です 瞬時に修正できました
次のエラーはインプット構造体です グローバル変数は 移行元の構造のままです 似ている命名規則を使うので簡単です
uniformでも同じ修正が必要です
次はもう少し複雑です Metalのsampleも異なっています 1から始めましょう sample関数を colormapから直接 選びます ご覧のとおり すべて選ぶだけです この関数ではsamplerとtextureを 選ぶよう指示されます textureは入力済みです 関数の変数としてsamplerを 渡すことも可能です またはコードで宣言することも可能です こうします
normalMapでも同様に作業します
最後のエラーは OpenGLで定義した多くの変数です 最終的な計算された色に戻します
他にも色々な関数があります normalizeやdot product 私のお気に入りのmaxは まったく同じです シェーダの移植が完了しました 表示します
何かが変ですね
OpenGLでシェーダの エラーがあった場合 ソースコードを確認するのが 一般的でしょう アウトプットを見て 懸命に考えるでしょう ここではシェーダの デバッガーを使います
カメラのアイコンをクリックします GPUの痕跡を入手します
Metal APIがコールした すべての記録です ドローコールを確認していきます こちらが木の画像 こちらは神殿です
神殿の階段で長押しします ピクセルインスペクタが シェーダをデバッガーします
こちらをご覧ください 移行したコードのラインごとの値と 選択したピクセルの値です colorMapを見てみましょう
適当なtextureと言えます 階段も上半分のtextureは よさそうです しかしtextureの座標を見てみると 下半分を選んでいるようです 検証してみましょう textureのY座標をひっくり返します
そしてシェーダを更新します よさそうです 修正を続けます さらに よくなりましたね
今のはOpenGLから Metalへ移植する際の 典型的なエラーです テクスチャローディングコードを確認し 座標の原点が正しいか確かめましょう 1つ1つ修正する手間が省けます 機能に富んだエディターと 強力なデバッギングが ゲームをMetalへ移行する 手助けになるでしょう
ありがとう 残りのスライドはサラが説明します (拍手) マックス どうも サラ・クラウソンです Metalへの移植方法の 続きを説明します
グラフィックアプリケーションでは 多くの設定がありました ウィンドウやコマンドの準備 リソースとシェーダの設定もありました 次はステートを設定するための レンダーループです
OpenGLはステート管理において いくつか重要な概念がありました 頂点配列オブジェクトはvertex属性の レイアウトとバッファを定義 プログラムはvertexと フラグメントシェーダを連結 フレームバッファは アプリケーションがレンダリングする 色と深度ステンシルの組み合わせです これらのステートオブジェクトは 初期設定時に作られ フレームをとおして使われます
OpenGLのステートの 管理方法を話します
レンダーループです OpenGLがフレームバッファを結合させ プログラムや ステートの修正を行っています Enabling DepthやFace Culling ドローコール前のcolorMap の 変更などです
OpenGLの視点から APIトレースを見てみると APIコールのすべての変更を たどる必要があります ドローコールが起こると 停止してしまいます Primitive AssemblyやDepth Stateなど 前の修正が正当か 検査する必要があります 確認には大きな負荷がかかります OpenGLが衝撃を 最小限にしようとするため 制限が大きくなります
OpenGLのステートオブジェクトは 発表当初は画期的でした
フレームバッファオブジェクトは レンダーターゲットと統合され プログラムはvertexと フラグメントシェーダを連結 頂点配列オブジェクトは vertex属性APIやvertexバッファなど 大きなオブジェクトを結合します これらの変更で ポジティブな結果を生み出しても OpenGLはドローコールで 多くの検査が必要となります
ColorMaskがフラグメントシェーダを 最適化できるか フラグメントシェーダのアウトプットと フレームバッファの互換性は?
vertexレイアウトと 連結されたプログラムの互換性は? 付属されたレンダーターゲットは 混合可能なのか
Metal用に再設計された グラフィックステート管理では 頂点配列オブジェクトから 頂点レイアウトと結合された― プログラムシェーダを取り出します pixelFormatやBlendStateの 情報と一緒に MTLRenderPipelineDescriptorという 1つのオブジェクトにします 構造体はGraphicsPipelineに関わる すべてのステートを記述しています ディスクリプタを構成するには 初期化します 今説明したステートを設置します vertexとフラグメントシェーダ vertex情報やpixelFormat BlendStateなどです
その後 ディスクリプタを使って PSOを作成します この不変オブジェクトは レンダーステートを説明しています 1度作成すれば 正当性の検査も1度で終わり プログラムをとおして使えます
またはDepthとStencil関連の セッティングをまとめて MTLDepthStencilDescriptorに 統合します これらはすべて 深度ステンシル ステートです このディスクリプタで深度ステンシル ステートオブジェクトを作ります これも不変オブジェクトで プログラムをとおして使えます
OpenGLでのレンダーループは Metalでは こうなります 正当性が立証されたオブジェクトは この先 検査や追跡が不要です 比べてみましょう
Metalではレンダーパスの最初は MTLRenderCommandEncoderです フレームバッファと似ています
depthステートは オブジェクトに焼き付けられ renderEncoderに 置けばいいだけです
PipelineStateオブジェクトは プログラムシェーダと VertexArrayProperty pixelFormatの結合体です renderEncoderも設定します
renderEncoderは ラスタライザステートを直接管理します パイプラインはまだ柔軟性があります PipelineStateオブジェクトに すべて焼き付けてはいません
PSOに事前に記録されているものには フラグメント関数や PixelFormatなどがあります こちらはドロー中に 設定できるものです Cull ModeやFill Modeなど Scissor やViewportも 自由に設定できます
ドローコールも同様に残っています 一番の違いは 新しいステートを有効にすると 隠れた検証の負荷が発生します 新しいPipelineStateに 交換すればいいのです
OpenGLでは高負荷だった作業を 最適化する方法をお伝えします
OpenGLのデベロッパなら ご存じですね レンダーループの 多くのステートを変更したあと 最初のドローコールで 中断が起こります 最適化を利用し そのコールを隠すでしょう Pre-Warmingはダミーコールを流し OpenGLで必要なステートを 事前に行っていました エンジンが準備万端なら 一番簡単な方法は PSOへの差し替えです
MetalのシェーダPre-Warmingは 異なるステートとPSOオブジェクトの 生成によりできます
まずディスクリプタを作成します 最初のドローコールまでの 書き込みを終えます 最初のPipelineStateオブジェクトを 作成します 同じディスクリプタで ステートを少し変更します ここではblendingを有効化しています 2つ目のPipelineStateオブジェクトを 作成します これらは事前に検査されているので ドロー中でも問題は起こりません OpenGLからMetalへの 簡単な移行方法でした
アプリケーションの セットアップステージで Metalへ移行する利点について 説明します 負荷の大きいオペレーションが 減ります
OpenGLではシェーダのコンパイルや ステートの検査には ドロー時間まで待つ必要がありました 負荷の大きい作業が フレームごとにあったのです
Metalへ移行すれば これらのオペレーションは 違うステージへ移動します コンパイルされたシェーダや Shader Compilationは ビルド時に行われるので 1度で済みます ステートの定義は ロード時へ動きました ドロー時はドローコールだけの 負荷になります
セットアップステージの説明でした 次はリソースやシェ-ダなど レンダーフレームでの作業です
1つのフレームをドローするには アプリケーションは テクスチャとバッファを更新します レンダリングする レンダーターゲットを固定します 作業が終わるまで いくつかのレンダーパスが起こります まずはリソースのアップデートです リソースによっては レンダーループで更新を続けます
例えばshader constantや Vertexやindexバッファ テクスチャです
これらの修正はフレーム間で 終了します GPUとCPUを同期させています 典型的なOpenGLのリソース更新は 以下のどの組み合わせでも行えます
バッファはCPUで更新されます またはGPUを介した バッファ同士のコピーで可能です テクスチャもCPUで更新されます またはGPUを介した テクスチャ同士のコピーでも可能です
Metalも似た機能を提供しています ライオネルが説明したとおり バッファとテクスチャのコンテナは 変更できません そして初期設定時に生成されます しかしコンテンツは修正が可能です
共有やマネージドストレージにある バッファの更新には CPUのコンテンツプロパティを 使います GPUではblitEncoderが データコピーを行います blitEncoderのcopyFromBufferを使い GPUからバッファを更新できます
同じようにテクスチャも ストレージモードから replaceRegionを使い CPUを介して更新可能です GPUからのテクスチャの更新は copyFromTextureを使います
更新の可否は ストレージモードによります 共有やマネージドストレージにある テクスチャやバッファは更新可能です
OpenGLはGPUとCPUの 同期を図っていました それにより アプリケーションに 負荷をかけていました Metalでのメモリ管理のおかげで データの同期の場所と時間を選べます バッファ テクスチャ どちらも可能です
OpenGLをMetalへ移行した場合 フローはこうなります
CPUがレンダーパスを設定する間に リソースを更新します それが終われば バッファはレンダーパスの実行時に GPUで使用が可能です GPUがバッファから読み込む間は CPUがレンダーパスを設定し 同じバッファを更新します 競合状態です 問題の解決方法を説明します
簡単な方法はGPUに リソースを預けることです コマンドバッファに対して waitUntilCompleted()を使います 前半で話したglFinishに似ています バッファを使ったレンダーパスの実行が GPUで終わるまで CPUの作業も制限されます
実行が終わると GPUからコールバックを受け取ります これにより どのバッファも CPUやGPUによって 足止めを食らうことはありません
しかしCPUは GPUの実行中 アイドリング状態です GPUはCPUが仕事を終えるまで 待ちぼうけです レース状態を解消するには 有効かもしれません しかしプログラムの遅延が発生するので waitUntilCompleted()の使用は お勧めしません
必要性に応じて 2つかそれ以上のバッファを更新し 同期させるのが効率的な方法です CPUもGPUも それぞれの作業を行えます トリプルバッファリングを 見てみましょう
最初のリソースです GPUによって消費されます completionHandlerを使ってみます GPUによって 該当のフレームが終了すると CPUも終了を認識します
しかし終わるのを待つ必要はありません トリプルバッファリングでは 異なるバッファなので CPUは2つ先まで更新を進めます GPUのフレームは完了しています ここでcompletionHandlerを投入します GPUの作業は終わり バッファは バッファプールへ戻っています CPUとGPUはバッファごとに ずれて作業を行います 多くのデベロッパは この方法を使うでしょう 最適なパフォーマンスが実現できます
作業の遂行においては トリプルバッファリングなら キューも3つ必要です
frameBoundarySemaphoreの 初期化が必要です GPUが実行を終えるたびにバッファを オーバーライドできると CPUに伝えます
最後にバッファインデックスを 初期化します
レンダーループの内部は バッファを書く前に 該当のルレームのGPUの作業が 終わっている必要があります レンダーパスの最初に frameBoundarySemaphoreの 完了を待ちます 完了の信号を受け取ったら バッファを新しいフレームデータへ 再利用できます
コマンドをエンコードし GPUへリソースを結合し 次のフレームに使います
その前に CommandBufferへ CompletionHandlerを追加し 完遂します
GPUの作業が終われば フレームに信号を送り CPUは実行の完了を知り バッファを次のフレームに使います
これはシンプルなトリプルバッファの 実装です もっと大規模なリソース更新にも 使えます
リソースが更新されました 次はレンダーターゲットです
OpenGLでは フレームバッファオブジェクトは レンダリングコマンドの 最終地点でした フレームバッファオブジェクトは テクスチャとレンダーバッファを集め レンダリングを円滑に進めました フレームバッファは可変であり レンダーパスは漠然としています 最終的にはディスプレイ表示のため 交換します
OpenGLのフレームバッファの典型的な ワークフローです アプリケーションの初期化処理では フレームバッファはすでにあります 結合することで最新に保ちます テクスチャなどのリソースも加え フレームバッファのステータスも 確認します
ドロー時はフレームバッファを 現行の状態にします レンダーパスにとっては 暗黙のスタートです ドローコールの前に すべてを消去します 最後に特定の信号をOpenGLへ送って メモリへ保存する必要がないと 伝達します レンダーパスの終わりの ヒントでもありますが 保証するものではありません
Metalでは MTLRenderCommandEncoderが レンダリングコマンドの最終ゴールです FBOと似て レンダリングの行き先を集めて 指令を届きやすくします MTLRenderCommandEncoderは直接 GPUへハードウェアコマンドを出します レンダーパスは始まりと終わりが 明確に分かります
Metalのレンダーパスです renderPassDescriptorを作ります 付随するリソースを記述します オペレーションの始めと終わりを 指定します ロード&ストアアクションと言います OpenGLとは違い Metalはリソースを直接 削除しません ロードアクションと色を指定します ここでは黒です
ストアアクションはDontCareにします OpenGLのフレームバッファと 似ているところです 結果を保存する場合は ストアアクションを実行します
レンダータイムでは ディスクリプタを使って エンコードを生成します 自らドローコールを実行し エンコードを終えます
フレームバッファを処分する前に 何か書いてみましょう
レンダーコマンドはレンダーパスにも 参照されます テクスチャやバッファのドローコール インプットやステートを設定 ドローコマンドを発令します
これは典型的なOpenGLの ドローの流れです よいOpenGL appはすべてを 事前にセットしておきます ターゲットを統合し
シェーダとのリンクをプログラムします
VertexBufferやUniformなどの リソースとを 違うステージから結合します そしてドローが完成します
少し前に話したとおり OpenGLの変更は 隠れた負担が発生します 負担を避けるためにステージの 変更をまとめていた場合 Metalのステートオブジェクトの 事前検査も有効に活用できます Metalの検証はPipelineState オブジェクト作成時のみです シェーダがコンパイルされていれば ループは小さくなります
プログラマーにとっては 違いはわずかです 先ほどのコードをMetalで 開いています
まずは MTLRenderCommandEncoderからです OpenGLのフレームバッファに相当します
あらかじめ設計された PipelineStateオブジェクトを設定 OpenGLプログラムでも 同等のものがあります
VertexBufferとUniformの リソースを割り当てます Uniformはシェーダステージごとの 配置が必要です OpenGLとは違うところです OpenGLから引用している Uniformですが 違う物を使っても大丈夫です
最後にテクスチャとドローコールです すべてのドローコールを設定したら レンダーパスを終えます
まだ表示の問題があります
GPUはフレームバッファ用に 書かれています OpenGLはレンダーフレームを表示します drawInRectから戻るとコンテキストは presetRenderBufferをコールします MetalはCore Animationから ドローアブルへ直接 実行します ドローアブルはディスプレイの テクスチャです
レンダーバスを エンコードすることが可能です 現在のドローアブルを引き出し レンダーループのあとに コマンドバッファに指令を出します
最初のコードを覚えていますか? ウィンドウサブシステムの時です GLKViewとdrawInMTKViewを 紹介します レンダリングを見ていただけます
こちらです GLKViewではフレームバッファを結合し レンダーコマンドで drawInRectによりイメージが表われます Metalではコマンドバッファを作成し 終わりのエンコーダを作成し レンダーコマンドを行います 1つ追加で行うことは presentDrawableをコールすることです 最終のコマンドバッファ前に行います シングルエンコーダの 単純なレンダーループでは これを行うだけです もっと複雑なアプリケーションの場合 別のセッションも見てください “Delivering Optimized Metal Apps and Games”です
フレームが完成しました ウィンドウサブシステムの 移行方法を説明しました リソースの作成をしましたね すばらしいツールで 問題点を簡単に見つけられます
レンダーコマンドキュー コマンドバッファ コマンドエンコードを作成 レンダーパスも設けました 正当性が事前に検査された オブジェクトの作成 トリプルバッファリングのリソースの 更新方法を紹介 レンダーパスのための MTLRenderCommandEncoder レンダーフレームの最終的な配置
グラフィックアプリケーションで 実演もしました OpenGLとMetalは共通点が多く 移植もスムーズに行えます グラフィックにおける問題解決に 新たな技術も取り入れました このセッションで伝えたいことは OpenGLからMetalへの移行は 難しくないということです 既存のアプリケーションは 向上するでしょう もう1つ伝えたいことは Metalはすばらしいツールを 提供しています 開発の強い味方になるでしょう マックスがデモで紹介したのも その1つです コードの問題を解決してくれます Xcodeは新しいGPUの Memory Viewerを提供しています アプリケーションのメモリ面で 役立つでしょう GamePerformance Templateの Metal System Traceは ドロップフレームなどの サブミッション問題を解決します 今年 初めて発表したのは Metal Appの Supporting Simulatorです (拍手) 興奮しますよね (笑い声)
macOS Catalinaに搭載の Xcode 11では フルハードウェアアクセラレーションで iOSやtvOSのアプリケーションに対応 Metal シミュレータを搭載 MTLGPUFamilyApple2の機能により ゲームやアプリケーションが 高性能なスクリーンに対応しています
シミュレータやハードウェアについては シミュレータ関連のセッションを ご覧ください Metalに関するセッションも オンラインでいくつも公開しています
さらなる情報は ウェブサイトの資料をご覧ください ラボにもお越しください ご清聴 ありがとうございました (拍手)
-
-
特定のトピックをお探しの場合は、上にトピックを入力すると、関連するトピックにすばやく移動できます。
クエリの送信中にエラーが発生しました。インターネット接続を確認して、もう一度お試しください。