MetalDay 1

Metal入門・・・の前の基礎知識

Metalは入門するのがなかなか大変です1。UIKitやCore Graphicsを使ってグラフィックスを描画するのとはまったく違う手順が多く登場します。なので、その背景にある概念を理解していないままコードに臨むと、「なぜこんなことをやる必要があるのか」と理解しがたく感じることがあるかもしれません(私がそうでした)。

本記事では、そんなMetalへ入門する「以前」の、Metalの基礎知識について解説します。


Metalの概要


Metalとは

Metalは、Appleによって提供されているローレベルなグラフィックスAPIです。別の言い方をすると、GPUへのアクセスを提供するAPIです。GPUというハードウェア層を直接たたくAPI(=ローレベル)であり、GPUは"Graphics Processing Unit"の略であるとおり、そもそもは描画処理を担う演算装置なので、そのインターフェースであるMetalは「ローレベルなグラフィックスAPI」というわけです。

近年ではGPUの並列演算処理能力に着目し、描画以外の汎用的な計算に用いられるケースも増えてきています。たとえばディープラーニングは非常に計算量が多いアルゴリズムですが、その大部分を占める行列演算は並列化しやすくGPUでの処理に向いています。この用途では「汎用」の意味からGPGPU(General Purpose GPU)という呼び方をすることもあります。


OpenGL ESとMetal

Metal登場以前から、iOSではローレベルなグラフィックスAPIとしてOpenGL ESが利用できました。ただOpenGL ESはAppleが独自に策定しているものではなく、様々なプラットフォームにおける様々なハードウェアをサポートしているため、Appleのハードウェアに最適化されているわけではありません。

そこでAppleは新たにMetalというグラフィックスAPIを開発しました。Appleが独自に策定しているものですから、Appleのハードウェアに最適化し、性能を限界まで引き出すことができます。登場時にはOpenGL ESと比較して「最大10倍速い」と謳っていました。


Metalの用途

Metalが発表された、2014年のWWDCの基調講演におけるデモはこのようなものでした。

wwdc14demo.png

日本庭園風の3Dシーンの中で、桜の花びらが舞い散り、鯉の群れが池の波紋を追いかけ、砂紋を動的に描き、蝶の大群を舞わせる、というデモでした。Metalの能力を活かした素晴らしいデモでしたが、逆にこういったデモを見ると、「Metal=重いグラフィックス処理を扱う本格的なゲーム向けのAPI」と考えてしまい、興味の対象外としてしまった開発者も多かったことでしょう。

実際には、Metalは多くの場所で陰ながら活躍しています。WWDC17の"Platforms State of the Union"では次のような図が使われていました。

frameworks-metal.png

画像処理を行うCore Imageや、3D空間の計算・描画を行うSceneKitはもちろんのこと、UIKitやCore AnimationというiOSの超定番フレームワークの下回りでも実はMetalが利用されているのです。

多くのケースではMetalは暗黙的に使用されているため、開発者が実際にMetalのAPIを直接利用する必要があるわけではないのですが、下位レイヤの概念を知っておくことは決して損はありません。

また、従来は重い3D描画や独自の画像処理を行うアプリの開発者でもない限り、OpenGLやMetalといった低レイヤのAPIを直接利用することは稀でしたが、近年ではその用途が拡大してきています。

たとえば今もっともホットなトピックである機械学習の、Convolutional Neural Network(畳み込みニューラルネットワーク)の計算をMetalで行うAPIがMetal Performance Shadersフレームワークで利用できますし、iOS 11ではRNN(Recurrent Neural Network)のAPIも追加されました。同じくホットな分野であるARを扱うARKitでも、より自在なレンダリングを行うためにMetalが利用できるようになっています。


Metalの基礎


Metalの基礎概念

Metalを扱う場合、たとえば「画像を描画する」というもっともシンプルな実装でも、多くのクラスが登場します。「最小実装」がUIKitのように小さくはないのです。GPU周りを扱うプログラミングに慣れていないと、「たったこれだけのことをやるのに、なぜこんなに複雑になるのか」と不可解に思えてしまうかもしれません。

しかし、Metal利用時にCPUとGPUがどう振る舞うか、その処理の流れや概念を理解しておけば、MetalのAPIは各段に理解しやすくなります。

まずはもっとも大まかな構成として、次の図を見てください。

この図は次のことを表現しています。


  • CPUとGPUは両方からアクセスできるメモリ領域を持つ

  • CPUは、GPUへの命令(コマンド)をコマンドバッファという形で作成し、GPUに渡す

  • GPUは渡されたコマンドを実行する

これが大前提となる概念です。これぐらいであればシンプルで直観的ではないでしょうか。このシンプルな概念を踏まえておくだけで、次から解説するMetalの基本クラスの役割がグッと理解しやすくなります。


Metalの基本クラス


MTLDevice

ハードウェアとしてのGPUを抽象化したプロトコルです。

MTLDeviceオブジェクトへの参照を取得するには、MTLCreateSystemDefaultDevice()関数を利用します。

func MTLCreateSystemDefaultDevice() -> MTLDevice?

クラスには属さない関数なので、そのまま利用できます。

let device = MTLCreateSystemDefaultDevice()


MTLCommandBuffer

CPUで作成され、GPUで実行されるコマンドを格納するコンテナです。上で登場した図にあったとおり、このコマンドバッファ単位でGPUに渡され、実行されます。

コマンドバッファへのコマンド追加等が完了したら、コマンドバッファを「コミット」することで、このコマンドバッファがGPUによって実行されます(キューと絡めたより詳細な挙動は次項で解説します)。

いったんコミットしたコマンドバッファに対しては、登録しておいたスケジュールハンドラや処理完了ハンドラが実行されるのを待つか、その実行ステータスをチェックすることしかできません。これはつまり、コマンドバッファのオブジェクトは再利用はできないということでもあります。したがってプロパティ等にオブジェクトを保持するのではなく、都度生成して使います。


MTLCommandQueue

コマンドバッファの実行順を管理するキューです。次のようにMTLDeviceから作成します。

let commandQueue = device.makeCommandQueue()

基本的にアプリに1つだけ作成し、Metalデバイス生存中はオブジェクトを維持しておきます。スレッドセーフです。

コマンドバッファ(MTLCommandBufferオブジェクト)の作成は、このコマンドキューの役割です。

let commandBuffer = commandQueue.makeCommandBuffer()

生成したコマンドバッファを、commit()メソッドを呼びコミットすると、生成元のコマンドキューにコマンドバッファが追加(エンキュー2)されます。

commandBuffer.commit()

コマンドキューはエンキューされた順にGPUにコマンドバッファを送り、GPUはそのコマンドを実行します。こうしてコマンドキューによりコマンドバッファの実行順が保証されるというわけです。


MTLCommandEncoder

コマンドを作成し、コマンドバッファに追加(エンコード)します。

コマンドエンコーダの種類を次の表に示します。

コマンドエンコーダ
概要

MTLRenderCommandEncoder
グラフィックスレンダリングコマンドをエンコードする

MTLComputeCommandEncoder
並列演算処理をエンコードする

MTLBlitCommandEncoder
バッファ・テクスチャ間のコピー処理をエンコードする

MTLParallelRenderCommandEncoder
並列実行する複数のグラフィックスレンダリングコマンドをエンコードする

コマンドエンコーダがコマンドをコマンドバッファに追加し、コマンドバッファがコマンドキューにエンキューされ、GPUで実行される、という関係を図示すると、次のようになります。

command-classes.png


MTLBuffer, MTLTexture

MTLTextureはテクスチャデータを、MTLBufferは頂点データ等、レンダリングに必要なパラメータを保持します。どちらのプロトコルもMTLResourceに準拠し、GPUからアクセス可能なメモリ領域上に確保されます。


つづき: Metalで簡単な描画を行ってみる

本記事は日本語初のMetal解説書Metal入門」から抜粋したものです。書籍では「第2章」でもう少し基礎知識を解説したのち、「第3章」以降でシンプルな実装を行っていきます。

1章 Metal の概要

2章 Metal の基礎


  • 2.1 Metalの「最初のハードル」

  • 2.2 「背景にある概念」を理解する

  • 2.3 Metalの基本クラスとプロトコル

  • 2.4 MetalKit

3章 入門その1 - 画像を描画する


  • 3.1 描画処理のためのセットアップを行う

  • 3.2 画像をテクスチャとしてロードする

  • 3.3 描画処理を実行する

4章 入門その2 - シェーダを利用する


  • 4.1 Metalシェーダの基礎

  • 4.2 「画面全体を一色に塗る」シェーダの実装

  • 4.3 CPUプログラム側の実装

5章 入門その3 - シェーダでテクスチャを描画する


  • 5.1 テクスチャを扱うシェーダの実装

  • 5.2 テクスチャ座標データをシェーダに渡す

  • 5.3 テクスチャをシェーダに渡す

  • 5.4 ピクセルフォーマットを合わせる

6章 Metal のハードウェア要件


  • 6.1 歴代iOSデバイスのMetalサポート状況

  • 6.2 MTLFeatureSet

  • 6.3 Metal非対応デバイスの判定

7章 GPGPU プログラミング入門


  • 7.1 コンピュートシェーダ

  • 7.2 スレッドとスレッドグループ

  • 7.3 GPGPUの実装例

8章 MSL 入門


  • 8.1 Metal のグラフィックスレンダリングパイプライン

  • 8.2 .metalファイルと.metallibファイル

  • 8.3 FunctionQualifier(関数修飾子)

  • 8.4 AttributeQualifier(属性修飾子)

  • 8.5 Address Space Qualifiers(アドレス空間修飾子)

  • 8.6 型

  • 8.7 GLSLをMSLに移植する

9章 Metal Performance Shaders


  • 9.1 デバイスがMPSをサポートしているかを確認する

  • 9.2 ガウシアンブラー

  • 9.3 MPSUnaryImageKernelを継承する他の画像処理カーネルクラス

  • 9.4 画像のリサイズ

  • 9.5 画像の転置(行と列の入れ替え)

10章 Metal の最適化


  • 10.1 スレッドグループサイズの最適化

  • 10.2 ArgumentBuffers

11 章 Metal のデバッグ


  • 11.1 GPUフレームキャプチャを有効にする

  • 11.2 Xcode上でGPUの負荷を確認する

  • 11.3 GPUフレームデバッガ

  • 11.4 GPUフレームキャプチャをプログラムから制御

  • 11.5 InstrumentsのMetalSystemTrace

  • 11.6 ラベル

  • 11.7 デバッググループ

  • 11.8 GPUで利用中のメモリサイズを調べる

12 章 Core Image × Metal


  • 12.1 Core ImageとMetalのシームレスな統合

  • 12.2 Core Imageのフィルタ結果をMetalで描画する

  • 12.3 CoreImageのカスタムフィルタをMetalで書く

  • Metalでカーネルを書く利点

  • MSLによるカスタムカーネルの実装

  • Metalカスタムカーネルを利用するためのビルド設定

13 章 SceneKit × Metal


  • 13.1 Metal で SceneKit のマテリアルを描画する

  • 13.2 他のSceneKitとMetalの連携機能

14章 ARKit × Metal


  • 14.1 マテリアルをMetalで描画する

  • 14.2 MetalによるARKitのカスタムレンダリング

15章 Core Video × Metal


  • 15.1 Metalでリアルタイム動画処理

気になった方はぜひ無料サンプルをダウンロードしてみてください。各章のさわりが読めるようになっています。

Screen Shot 2018-06-06 at 12.35.08.png

ニッチな書籍ですが、読んだ方には好評です。


「Metal入門」は入門書とは思えないほどのボリュームと内容です。自分でアプリを作っていて描画をもっとGPUにやらせたい!と思う事が多々あるので勉強してみたいと思っています。まだ読み始めたばかりですが、 ここまで初心者にも分かりやすくMetalを解説する本は世界中探してもなかなかないのでは



1章2章を読むだけでもかなり価値があると思います。 GPUとCPUが、iPhoneの中でどうなっているのかという全体感を掴めただけでも大きな収穫になりました!



iOS 11 Programming

iOS 11 Programming」にもMetalについて基礎から解説している章があります。


  • 13.1 はじめに

  • 13.2 Metalの概要

  • 13.3 Metalの基礎

  • 13.4 MetalKit

  • 13.5 Metal入門その1 - 画像を描画する

  • 13.6 Metal入門その2 - シェーダを利用する

  • 13.7 Metal入門その3 - シェーダでテクスチャを描画する

  • 13.8 ARKit+Metalその1 - マテリアルをMetalで描画する

  • 13.9 ARKit+Metalその2 - MetalによるARKitのカスタムレンダリング

  • 13.10 Metal 2

  • 13.11 Metalを動作させるためのハードウェア要件

書籍のタイトルにはiOS 11とありますが、Metalについては新機能の紹介だけではなくて、基礎からじっくり解説しています。Metalの章だけで37ページもあります。

他の章も他著やネットではなかなか得られない濃い情報が詰まっているので、気になった方はぜひサンプルPDFもあるので見てみてください。


  • 第1章 iOS 11 概要

  • 第2章 ARKit

  • 第3章 Core ML

  • 第4章 Swift 4の新機能とアップデート

  • 第5章 Xcode 9 の新機能

  • 第6章 Drag and Drop

  • 第7章 FilesとDocument Based Application

  • 第8章 レイアウト関連の新機能及び変更点

  • 第9章 Core NFC

  • 第10章 PDF Kit

  • 第11章 SiriKit

  • 第12章 HomeKit入門とiOS 11のアップデート

  • 第13章 Metal

  • 第14章 Audio関連アップデート

PEAKSのサイトにて電子書籍・紙の書籍ともに販売されています。


iOS 11 Programming


iOS 11 Programming



  • 著者:堤 修一,吉田 悠一,池田 翔,坂田 晃一,加藤 尋樹,川邉 雄介,岸川克己,所 友太,永野 哲久,加藤 寛人,

  • 発行日:2017年11月16日

  • 対応フォーマット:製本版,PDF

  • PEAKSで購入する



執筆を担当したARKit、Metalの章の詳細、あるいは全体的なおすすめポイントは以下の記事にも書きました。





  1. OpenGLやDirectX等のグラフィックスAPIをいじった経験のある人達にとっては容易なようです。 



  2. MTLCommandBufferにはenqueue()というメソッドもあります。enqueue()を呼んだタイミングで、当該コマンドバッファのキューにおける実行順が確保されますが、コミット自体はされません(commit()を呼ばない限り実行されません)。