Skip to main content

Metalの3年間

8月

05, 2020

by coberst


テック

3年前、当社はレンダラーをMetalに移植しました。 時間はさほどかからずに楽しく作業できて、iOS上で本当によく機能しました。 そのため、当社はどうやってその決断をして、どういう結果をもたらしたか(ネタバレ:とても首尾よくいきました!)の記事を書きました。 ほとんどの最初の遡及は今だに適用できますが、今日のMetalはこれまでになくいい状態です。そのため、当社の3年間のアップデートを再公開することにしました。

そこで時計の針を戻して、ちょうど2016年の12月、iOS上の当社のMetalレンダラーバージョンを送信したところだとします。

なぜMetalなのでしょう?

2014年のWWDC(世界開発者会議)でAppleがMetalを発表したとき、私の最初の反応は無視するというものでした。 当社のほとんどのユーザーが持っていなかった最新のハードウエアでしか利用できないものでしたが、AppleはCPUの性能の問題は解決済みで最小の市場のための最適化は最速と最も遅いデバイスの差をさらに拡大させる、と述べました。 その時、当社はOpenGL ES 2をAppleでのみ実行していました。そして、Androidにも移植し始めていました。

2年半先送りして、当社のユーザーのMetal市場占有率はこちらです。

これは、以前よりもさらに魅力的になっています。 Metal導入は、最も古いタイプのデバイスには良くないということはいまだに言えますが、iOS上のGL市場は縮小し続けており、 これらの古いデバイスで実行されるコンテンツは最新のデバイスで実行されるコンテンツとは違うものである場合がよくあります。そのため、速くすることに労力を注ぐことは納得がいきます。 みなさんがお持ちのiOS Metalコードは、Mac上で非常に少ない変更だけで実行されますが、モバイルに焦点を当てていたとしても、Mac上で使用することは納得できます(当社は現在、iOS上で構築されているMetalのみを出荷しています)。

市場占有率を少し詳細に分析することは意義のあることだと思います。 iOSでは、iOS 8.3以上のためにMetalに対応していますが、ユーザーの中にはOSのバージョン制限のためにMetalを実行できない人もいて、いまだにGLを実行している25%のほとんどはただSGXハードウエアを持つ古いデバイスを使用しています。 OpenGL ES 3の機能を全く持っていませんが、当社はそこでローエンドレンダリングパスを実行することで満足しています(すべてのデバイスがMetalになればいいとは思いますが、幸運なことに GL/Metal分離が向上するだけです)。 Mac上では、Metal APIはより新しく、 OSがかなり意義ある役割を果たしています。 Metalを使用するにはOSX 10.11以上を使わなければならず、当社のユーザーの半数は単にもっと古いOSを持っています。ハードウエアよりも、ソフトウエア(95%のMacユーザーがOpenGL 3.2以上を実行)の問題です。

市場占有率を持ってすると、当社にはまだMetalに移植することに携わらないという選択肢もありました。 そのうちの一つは、ただMoltenGLを使用することで、これは当社がすでに持っているOpenGLコードを使用しますが、これはより速いはずのものです。もう一つは、 Vulkanへの移植(パソコン上でより良い性能を得て、最終的にはAndroidでも)とMoltenVKの使用です。 簡単にMoltenGLを精査したところ、結果は芳しくありませんでした。コードをただ実行するのにある程度の努力が必要で在庫のOpenGLと比べて少々性能は良かったのですが、もっと期待がありました。 MoltenVKについては、 一つの低レベルAPI層をもう一つの上に導入するのは間違っていると思います。最適以下の性能につながるインピーダンス不整合となることが確実です。以前に使っていた高レベルAPI よりはいいかもしれませんが、可能な限り速くはならない可能性が高く、そもそも低レベルAPIを選んだのはそれが理由のはずでした。 導入で重要なもう一つの側面は、Vulkanのときよりもさらにシンプルなものです。それについては後述しますが、ある意味、私はVulkan – >- MetalよりMetal -> Vulkanラッパーのほうを好みます。

また、ここで述べておく価値があることは、 最新のiPhone上のiOS 10上にはGLドライバーがないことです。GLは、Metalの上に導入されています。 これは、「一度書いたら、どこでも実行」という前提を考慮すると、OpenGLがモバイル上では実はあまり機能せず、OpenGLを使うことはただ本当に少し(多大ではない)開発の際の努力を省いているという意味です。

移植

全体的にMetalへの移植は楽なものでした。 当社では、Direct3D 9/11のような高レベルなAPIからPS4 GNMのような低レベルのAPIまで、違うグラフィックAPIの作業経験がたくさんあります。 これは、同時に妥当に高レベルでありながらMetalをAPIのように楽に使うことができるユニークな利点を与えてくれますが、CPU-GPU同期のような一部のタスクはアプリ開発者がするように残してくれます。

実質的な唯一の障害はシェーダーの集積で、それが終わってコードを書く段階になったとき、APIが非常にシンプルで一目瞭然でほとんどコードが自動的に書けることが明らかになりました。 ほとんどのものをレンダリングした移植を最適以下の状態でたった1日で10時間以内に実行できるようにしましたが、コードの整理や認証の問題の修正、プロファイリングと最適化、そして全体的な仕上げにあと2週間かかりました。 この期間内でAPI導入をすることは、APIとツールセットの質について多くを語ってくれます。 これに役立つ側面がいくつかあると思います。

  • それぞれの段階において、いいフィードバックをもらいながら、コードを徐々に開発することができます。 当社のコードは、すべてのCPU-GPU同期を無視することから始まり、特定のパーツの状況設定が本当に最適以下であり、内臓されたリソースのリファレンス追跡を使用し、問題に出くわすことを避けるためにCPUとGPUを並行して実行することはありませんでした。最適化と仕上げ段階から、出荷できるものに変換して、その過程でレンダリングする能力を決して失わないようにしました。
  • ツールはあなたのためにあり、機能していて、しかもよく機能してくれています。 Direct3D 11に慣れている人たちにとっては、これはあまり驚くようなことではなかったでしょう。しかし、これはコードの最適化を助け、開発中にほとんどの問題を検知するのに連携してよく機能するCPUプロファイラ、GPUプロファイラ、GPUデバッガー、そしてGPU API検証レイヤーを備えたモバイル上では初めてのことでした。
  • APIはDirect3D 11よりもいくらか低レベルなので、カギとなる低レベルの決定を開発者に任せていますが(レンダリングパスの環境設定や同期など)、それぞれのリソースに作成時の特定の「使用フラグ」があっても、パイプライン障壁やレイアウト変遷、それぞれのシェーダー段階にリソースを自由に割り当てられるいくつものスロットがある伝統的なバインディングモデルを必要としない伝統的なリソースモデルをいまだに使っています。 どちらも馴染みのあるものですが、理解しやすく、速くするのに非常に限定された数のコードを必要とします。

もう一つ助けになったことは、当社のAPIインターフェースはMetalのようなAPIに対応する準備ができていて、非常に限られたものではありますが、高性能の導入を簡単に書くことができるように十分な詳細(レンダリングパスのような)を明らかにしています。 導入のどの時点でも、状態を保存したり復元(特にレンダリングターゲット設定を状態変更やその間ずっと続くリソースや状態のバインディングとして扱うことによって、APIインターフェイスの数多くがこれに苦しんでいます)したり、リソースの存続期間や同期についての複雑な決定をする必要はありませんでした。 レンダリングする必要があった唯一の「複雑な」一連のコードについては、一つ作り出すのに必要なハッシュビットによってレンダリングのパイプライン状態を作り出しますが、パイプライン状態のオブジェクトは当社のAPI抽象化の一部ではありません。 これさえも、かなり直球で速いものでしょう。 当社のAPIインターフェースについては、別の記事でさらに書きます。

そして、シェーダー集積に1週間かかり、洗練された最適化済み導入(1)に2週間かかりました。結果はどうだったでしょうか? 結果は素晴らしいもので、Metalは完璧に約束した性能を提供します。 一つには、シングルスレッドのディスパッチ性能は、OpenGL(当社のレンダリングフレームのドローディスパッチ部分をワークロードによっては2〜3倍縮小する)よりも顕著に良く、冗長な状況設定を減らすという意味において、これは当社のOpenGL導入の微調整がよくできていて、速いパスを使うことによってドライバーとの相性がいいです。 しかし、そこでは終わりません。Metalでのマルチスレッドはレンダリングコードの準備ができている場合の活用にはとるに足らないものです。 スレッドされたドローディスパッチに変更しませんでしたが、それでもレンダリングスレッド外でリソースを準備するその他のパーツをすでに変更しましたが、それはOpenGLとは違って、かなり努力を要さないものです。

その先は、Metalは簡単にアクセスできる信頼性の高いツールを与えることによって性能の一部を修理することができます。 当社のレンダリングコードの中心的なパーツの一つは、世界スペースにあるCPU上にある照明データを計算し、3Dテクスチャのリージョンにアップロードするシステム(これは、OpenGL ES 2ハードウエア上で模倣しなければなりません)です。 アップデートは偏っているため、全体のテクスチャを複製することはできず、ドライバーがどのようにglTexSubImage3Dを導入するかに依存しなければなりません。 ある時点では、アップデート性能を向上させるのに当社はPBOを使おうとしましたが、Android上でもiOS上でも全体的に重要な安定性の問題に直面しました。 Metal上では、リージョンをアップロードするのに2つの組み込み済みの方法があります。GPUが現在、テクスチャを読んでいない場合に使用できる MTLTexture.replaceRegion、またはテクスチャを使用し始めるのにGPUに間に合うように非同期でリージョンをアップロードできる MTLBlitCommandEncoder(copyFromBufferToTexture か copyFromTextureToTexture)です。

これらの両方の手法は私が望むよりも遅いものでした。最初のものは効率的な一部のアップデートに対応しなければならなかったために利用できず、非常に遅いアドレス変換導入のように見えるものを使用して、純粋にCPU上でのみ機能しました。 2つ目が成功しましたが、CPUの方でもコマンドを設定するのにかなりコストがかかり、GPUのほうでもなぜかかなり高い経費がかかり、3Dテクスチャを埋めるのに一連の2Dブリットを使用しているように見えました。 もし、これがOpenGLだったら、終わっていました。実は、これらの2つの手法の性能は、OpenGLの似たようなアップデートで観察された費用とほぼ同等のものでした。 幸いなことに、これはMetalなので、シェーダー計算に簡単にアクセスでき、非常にシンプルなシェーダー計算がよりよいバッファーをする能力をくれました。> CPUとGPU上で3Dテクスチャアップロードが非常に速く、基本的にこのコードの部分の性能の問題を永遠に解決したのです。(2

最後の一般的なコメントとして、Metalのコード管理はまったく骨が折れないものでもありました。 これまで導入しなければならなかったすべての追加機能は、当社が対応するどのAPI上よりも簡単に追加でき、この傾向が続くことが予測されます。 もう一つAPIを追加するときのちょっとした懸念は、常にメンテナンスが必要なことでしたが、 OpenGLと比べてこれにはあまり労力を必要としませんでした。実際、iOS上ではもうOpenGL ES 3に対応しなくてよくなり、これは当社のOpenGLコードの一部も簡素化できるという意味です。

安定性

今日のiOSではMetalは非常に安定していると感じられます。 2014年の始動の時にどのような状況だったのか、今日のMacのようなものだったのかはよく分かりませんが、iOSのためのドライバーもツールもどちらもかなり信頼できるように感じます。

iOS 10上では、Xcode 7で集積したシェーダーの読み込みに関係したドライバーの問題が一件(Xcode 8に乗り換えることによって修正しました)と、nextDrawable APIの誤用の結果起きたiOS 9上のドライバークラッシュが一件ありました。 動作バグが一つも見つからず、一度もクラッシュしなかったこと以外、比較的新しいAPIにしてはMetalはかなり満遍なく確固たるものでした。

加えて、Metalで得られるツールは多様で豊富なものです。具体的に使えるのは以下のものです。

  • APIを使うときに、一般的な問題を明らかにするかなり包括的な認証レイヤー。 それは、基本的にはDirect3D debugのようなもので、Direct3Dでは知られていますが、OpenGLの領域(理論上、 ARB_debug_callbackがこれを解決し、実際はほとんど利用できるものではなく、利用できる場合でもあまり役に立たない) では聞いたことのないものでした。
  • 状態と一緒にディスパッチしたすべてのコマンドを表示する機能しているGPUデバッガー、レンダーターゲットコンテンツ、テクスチャコンテンツ、その他。これまでは必要としていなかったので機能しているシェーダーデバッガーがあるか分かりませんが、バッファー検査はちょっと簡単になるかもしれませんが、それでも大体は機能します。
  • 各パスごとの性能統計(時間、帯域幅)と各シェーダーの実行時間も表示する機能しているGPUプロファイラ。 GPUは、はドローコール(描画が呼ばれた回数)のタイミングごとでは実は予測できないタイラーだからです。もちろんのこと。 このレベルの可視性があるということは、特にiOS上のグラフィックスAPIでGPUのタイミング情報が全くないということを考慮すると、素晴らしいことです。
  • GPUViewに類似していて、一部のUIの特異性を調整し、実際に使うのが簡単でCPUとGPUのレンダリングワークロードのスケジュールを表示する機能しているCPUかGPUタイムライン追跡(Metalシステム追跡)。
  • お持ちのシェーダー構文を認証するオフラインのシェーダー集積はしばしば有用な警告を与え、 シェーダー構文をバイナリ・ブロブに変換し、それは実行時間ではかなり速く読み込み、加えて事前にそれなりに最適化されており、ドライバー集積は速くなるように読み込み時間を短縮しています。

Direct3Dかコンソールの世界から来た人は、これらすべてのことを当然だと思うでしょう。信じてください。OpenGLでは、これらすべてが変わっていて、興奮を持って迎えられます。特にモバイル上では、しばしば壊れたドライバーや認証の欠如、GPUデバッガーの欠如、助力となるGPUプロファイラの欠如、それぞれのメーカーが少しずつ違うパーサがあるテキストベースのシェーダー言語で作業することを余儀なくされ、GPUスケジューリングデータを集める能力の欠如に対応するのに慣れています。

Metalは、コードを書くのにも、アプリケーションに搭載するのにも素晴らしいAPIです。 簡単に使える上に性能が予測可能で頑強なドライバーと確固たるツールセットを備えています。 移植性以外は、すべての側面においてOpenGLに勝りますが、OpenGLの現実は、3つのプラットフォーム(iOS、AndroidそしてMac)上でのみ実際は使用されるべきもので、そのうちの2つは現在ではMetalに対応しています。加えて、OpenGLの移植の保証は、一つのプラットフォーム上で書いたコードが他のプラットフォーム上では違った理由で結局は機能しないことになることが非常に多いため、概して遂行されません。

もし、UnityやUE4のようなサードパーティのエンジンを使っている場合、Metal はすでにそこでサポートされています。もし、そうでない場合でグラフィックスのプログラミングを楽しんでいるか、性能を非常に気していて、iOSかMacを深刻に捉えているなら、 Metalを試してみることをおすすめします。 がっかりさせられることはありません。

現在のMetal

過去3年間での当社の視点で起きた最大の変化は、大規模なスケールでの採用です。

3年前、デバイスの4分の1はOpenGLを使用しなければなりませんでした。 今日、当社の聴衆にとってこの数値は最大2%で、これは当社のOpenGLバックエンドはもうほとんど関係ないという意味です。 当社はまだこれを保っていますが、これは長くは続きません。

ドライバーは、今までで最高です。一般的にiOS上でドライバーの問題は見られませんが、問題がある場合、初期のプロトタイプでよく起きますが、 プロトタイプがプロダクションに移行する時までには、問題はだいたい解決しています。

当社は、Metalバックエンドを改善するために3つの分野に焦点を当てて、ある程度の時間をかけました。

シェーダー集積ツールチェーンの再作業

過去3年間に起きたもう一つのことは、Vulkanの発売と開発です。 APIは完全に違っているように見えるかもしれません(実際そうです)が、Vulkanのエコシステムは合わさったときに簡単に使えるプロダクション品質集積ツールセットという結果をもたらし、レンダリングコミュニティにとって素晴らしい一連のオープンソースツールを与えてくれました。

HLSLソースコード(シェーダー計算を含む様々なDX11機能を使用)を取れる集積ツールチェーンを作るライブラリを使い、SPIRVに集積し、述べたSPIRVを最適化し、結果となるSPIRVをMSLに変換(Metal シェーディング言語)に変換します。 DX9 HLSLソースのみを入力に使える当社の以前のツールチェーンに取って代わり、複雑なシェーダーでは様々な正確さの問題がありました。

Appleがこれと何の関係もなかったことは、少々象徴的なことですが、 こちらです。 glslang (https://github.com/KhronosGroup/glslang)、 spirv-opt (https://github.com/KhronosGroup/SPIRV-Tools) と SPIRV-Cross (https://github.com/KhronosGroup/SPIRV-Cross)の貢献者と管理者に多大な感謝を捧げます。 新しいツールチェーンも出荷できるように、これらのライブラリに一連の修繕を施し、それを当社のシェーダーをVulkan、MetalそしてOpenGL APIに再ターゲットするために使いました。

macOSサポート

macOSポートは、常に可能性としてありましたが、当社にとっては機能のいくつかがなくて困り始めるまでは大きな焦点は当ててはいませんでした。 そのため、macOS上でより速いレンダリングをして未来への可能性を解放するためにMetalに投資すべきだと決めました。

導入の観点からは、これはまったく難しくありませんでした。 ウィンドウ管理以外は、ほとんどのAPIはまったく同じです。大きな変更が必要だった唯一の分野は、メモリ確保でした。 モバイル上では、バッファとテクスチャのための共有メモリスペースがありますが、デスクトップ上ではAPIが固有のビデオメモリのついたGPUがあることを前提とします。

Metalの実行時間はデータのコピーにも対応するため、管理されたリソースを使ってその問題を避けて素早く対応することは可能です。 これが、当社が最初のバージョンを出荷した方法ですが、システムメモリの使いすぎを最小化するためにスクラッチバッファを使用してリソースデータをさらに明瞭にコピーするために、後で導入の作業をやり直しました。

macOSとiOSとの最大の違いは、安定性でした。 iOSでは、1つの構造に対して1種類のドライバーメーカーにだけ対応していたのですが、macOSでは3つのすべてのメーカー(インテル、AMD、NVidia)をサポートしなければなりませんでした。 加えて、iOS上での当社(ラッキーなことに!) はMetalが利用できた「最初」のiOSバージョンであるiOS 8を飛ばしましたが、当時はMetalを使用するユーザーが非常に少なかったので、macOS上ではこれは実用的ではありませんでした。 これらの問題の組み合わせによって、さらに数多くの比較的に無害でmacOS上のAPIで比較的に目立たない部分の両方でドライバーの問題に直面しました。

一部のバージョンに関して、すでに知られた避けて通るのが難しいシェーダーコンパイラーのバグ(例えば、10.11ではMetalが機能するにはmacOS 10.11.6が現在は必要)があるので、サポートから除外してレガシーOpenGLバックエンドに変更し始めましたが、当社はmacOS Metal (10.11+)のすべてのバージョンに現在でも対応しています。

性能の利点は、当社の期待に沿ったものでした。市場占有率に関しては、現在 macOSプラットフォーム上ではOpenGLユーザーが~25%でMetalユーザーが~75%で、これはかなり健全な割合です。 これは、当社が使用をサポートする他のプラットフォームが他にはなく、これはサポートがより簡単で性能の良いAPIに焦点を当てることができるという面では素晴らしいもので、将来のある時点でOpenGLのサポートをまったくやめることが現実的であるかもしれないという意味です。

性能とメモリ消費のおさらい

当社は歴史的に使用するグラフィックスAPI機能に関してはかなり保守的で、Metalも例外ではありませんでした。 Metalには、明示ヒープのあるリソース割り当てAPIの向上、Metal 2でのタイルシェーダー、引数バッファとGPU側のコマンド生成、その他を含む数々の大きな機能アップデートが過去何年間でありました。

私たちはほとんど新しい機能のどれも使うことはありません。 今のところ、性能は納得できるもので、全体的に適用できる改善に焦点を当てたいとおもいます。ですから、タイルシェイダーのようにレンダラーを通じて非常に特殊なサポートを必要として、より新しいハードウエアでのみアクセスできるものは、面白みがあまりないのです。

それを述べた上で、当社は完全に非同期なテクスチャアップロードを使用してレベルの読み込みでつかえることを減少させるためにただ「速く」実行するためだけにバックエンドのさまざまなパーツを微調整する時間をかけました。これは完全に痛みを伴わないものでしたが、 前述のmacOS上のメモリ最適化、キャッシュのミスを減少させることによる様々な場所のバックエンドでのCPUディスパッチの最適化などです。当社の新しいシャドーシステムに必要なメモリを大きく削減するために利用できるときはメモリのないテクスチャストレージを使用するのに明らかなサポートがある唯一のより新しい機能の一つです。

未来

全体的に、Metal改良にあまり時間を費やさなくてよかったという事実は、実際にはいいことです。3年前に書かれたコードが大まかに言って機能していて、速く安定していますが、これは成熟したAPIの大きな特徴です。 かかった時間と当社とユーザーへの継続した利点を考慮すると、Metalへの移植は、大きな投資でした。

当社は、異なるAPIのための作業量のバランスを常に再評価しています。将来的なレンダリングプロジェクトの一部では、さらに現代的なMetal APIのパーツを深く探索する必要が生じる可能性が高いですが、もしこれが起きるとしたら、それについての記事を必ずまた書きます!


  1. まあ、そうですね。テスト中に発見されたバグを修正するのに1週間はかかりますね
  2. 数値は、A10でのフレームにつき128 KBに値するアップデートされたデータ(2つの 32x16x32 RGBA8 リージョン)

Robloxコーポレーションとこのブログは、いかなる企業もサービスも推奨も支持もしません。 また、このブログに含まれる情報の正確さ、または完全性について、いかなる保証または約束もしません。

このブログ記事は、元は Robloxテックブログ に掲載されたものです。