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

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

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

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

## 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](https://qiita-image-store.s3.amazonaws.com/0/3180/655226e9-3b4d-a919-d3c0-f16e077fe7e9.png)

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

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

<img width="1471" alt="frameworks-metal.png" src="https://qiita-image-store.s3.amazonaws.com/0/3180/13a2465a-b9f9-527e-f183-9a8dd2081cce.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は各段に理解しやすくなります。

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

<img src="https://qiita-image-store.s3.amazonaws.com/0/3180/f4d71912-3cab-1372-4d19-1dee14bbfe10.png" width="500">

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

- CPUとGPUは**両方からアクセスできるメモリ領域を持つ**
- **CPUは、GPUへの命令（コマンド）をコマンドバッファという形で作成し、GPUに渡す**
- GPUは渡されたコマンドを実行する

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


### Metalの基本クラス

#### MTLDevice

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

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

```swift
func MTLCreateSystemDefaultDevice() -> MTLDevice?
```

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

```swift
let device = MTLCreateSystemDefaultDevice()
```


#### MTLCommandBuffer

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

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

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


#### MTLCommandQueue

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

```swift
let commandQueue = device.makeCommandQueue()
```

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

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

```swift
let commandBuffer = commandQueue.makeCommandBuffer()
```

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

```swift
commandBuffer.commit()
```

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

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



#### MTLCommandEncoder

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

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

| コマンドエンコーダ | 概要 |
| ---- | ---- |
| MTLRenderCommandEncoder | グラフィックスレンダリングコマンドをエンコードする |
| MTLComputeCommandEncoder | 並列演算処理をエンコードする |
| MTLBlitCommandEncoder | バッファ・テクスチャ間のコピー処理をエンコードする |
| MTLParallelRenderCommandEncoder | 並列実行する複数のグラフィックスレンダリングコマンドをエンコードする |

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

<img width="1478" alt="command-classes.png" src="https://qiita-image-store.s3.amazonaws.com/0/3180/1b01aa93-c523-ee1d-ccf9-a73b8e10aa7a.png">


#### MTLBuffer, MTLTexture

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


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

本記事は**日本語初のMetal解説書**「[Metal入門](https://booth.pm/ja/items/826318)」から抜粋したものです。書籍では「第２章」でもう少し基礎知識を解説したのち、「第３章」以降でシンプルな実装を行っていきます。


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でリアルタイム動画処理

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

- https://booth.pm/ja/items/826318

<img height="400" alt="Screen Shot 2018-06-06 at 12.35.08.png" src="https://qiita-image-store.s3.amazonaws.com/0/3180/207a8165-b144-e5e8-f8f4-1c41ac54343f.png">


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

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

- http://tamablog.firstseed.io/entry/2018/04/22/175626

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

- http://kboy-engineering.hateblo.jp/entry/2018/05/10/001759

<blockquote class="twitter-tweet" data-cards="hidden" data-lang="ja"><p lang="ja" dir="ltr">このボリュームとわかりやすさで1500円！ さらに今日までセールで1200円…！！Metalに興味ある人全員必読では <a href="https://t.co/D2XxoSHeX1">https://t.co/D2XxoSHeX1</a></p>&mdash; Shinichiro Oba (@ooba) <a href="https://twitter.com/ooba/status/988708315017437184?ref_src=twsrc%5Etfw">2018年4月24日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">Metal入門ざっと読了。<br>まだSceneKitとMetalの連携の部分など理解しきれてない部分もあるけど、このあたりはサンプルコード弄りながら理解していこう。<br>同人誌ながらMetalに関して一通り網羅されてる良書。はっきりいって安い。</p>&mdash; nsʇ‾ɐʞ‾ɐʇ (@ta_ka_tsu) <a href="https://twitter.com/ta_ka_tsu/status/990516004810715136?ref_src=twsrc%5Etfw">2018年4月29日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>



## iOS 11 Programming

「[iOS 11 Programming](https://peaks.cc/shu223/iOS11)」にもMetalについて基礎から解説している章があります。

- 13.1	はじめに
- 13.2	Metalの概要
- 13.3	Metalの基礎
- 13.4	MetalKit
- 13.5	Metal入門その１ - 画像を描画する
- 13.6	Metal入門その２ - シェーダを利用する
- 13.7	Metal入門その３ - シェーダでテクスチャを描画する
- 13.8	ARKit+Metalその１ - マテリアルをMetalで描画する
- 13.9	ARKit+Metalその２ - MetalによるARKitのカスタムレンダリング
- 13.10	Metal 2
- 13.11	Metalを動作させるためのハードウェア要件

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

他の章も他著やネットではなかなか得られない濃い情報が詰まっているので、気になった方はぜひ[サンプルPDF](http://peaks.cc/shu223/iOS11)もあるので見てみてください。

- 第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のサイトにて電子書籍・紙の書籍ともに販売されています。

<div class="peaks_widget" style="overflow:hidden; padding:20px; border:1px solid #dedede;"><div class="peaks_widget__image" style="float:left; margin-right:15px; line-height:0;"><a target="_blank" id="purchase" href="https://peaks.cc/shu223/iOS11"><img alt="iOS 11 Programming" style="border:none; max-width:140px;" src="https://s3-ap-northeast-1.amazonaws.com/peaks-images/project002_cover.jpg" width="500"></a></div><div class="peaks_widget__info"><p style="margin:0 0 3px 0; font-size:110%; font-weight:bold;"><a target="_blank" id="purchase" href="http://peaks.cc/shu223/iOS11">iOS 11 Programming</a></p><ul style="margin:0; padding:0;"><li style="font-size:90%; list-style:none;"><span>著者：</span><span>堤 修一,</span><span>吉田 悠一,</span><span>池田 翔,</span><span>坂田 晃一,</span><span>加藤 尋樹,</span><span>川邉 雄介,</span><span>岸川克己,</span><span>所 友太,</span><span>永野 哲久,</span><span>加藤&#160;寛人,</span></li><li style="font-size:90%; list-style:none;">発行日：2017年11月16日</li><li style="font-size:90%; list-style:none;">対応フォーマット：製本版,PDF</li><li style="font-size:90%; list-style:none;"><a target="_blank" id="purchase" style="text-decoration:underline; color:#1DA1F2;" href="http://peaks.cc/shu223/iOS11">PEAKSで購入する</a></li></ul></div></div>

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

- [「iOS 11 Programming」を共著で執筆しました／本書のおすすめポイント - Over&Out その後](http://d.hatena.ne.jp/shu223/20171128/1511827476)