Metalプログラミング

こんにちは、IOSフリーランスエンジニアの永田です。
最近は、レンダリングのObjective-C,Mac開発でMetal研究をしていました、レンダリング設定するコードがステップ数も多く、結構複雑で毎日解析をしていたせいか、Appleリファレンスの今回の規模数ですと認識することが容易にできました。
こちらはApple参考記事になります。
MetalBasicTessellation_Objective-C
そして自分が作成中のSwiftソースはこちらになります。現在は仕事、自宅でコーディングを繰り返しています。
現在は仕事、自宅でコーディングを繰り返すことによりコーディングが出来ることも事実ですので、
もう少し技術レベルを上げていきたい部分が本音であります。
様々なプログラミングでもっとお役に立てるようになったら、行動パターンに変化をもたらしたいと思います。
class ViewController: UIViewController
override func viewDidLoad()
@IBOutlet weak var mtkView: MTKView!はStorryBoard上でViewClassをMTKViewに設定してIBOutletで紐付けしています。
その他の@IBOutletはUIを表示する部品です。デファルトのClassを使用して、IBOutletで紐付けしています。
var tessellationPipeline = AAPLTessellationPipeline()はViewController内でインスタンスを生成してします。
self.mtkView.enableSetNeedsDisplay = true 、ViewがsetNeedsDisplayに応答するかどうかを制御します。
multisampleColorTextureを作成するために使用したサンプル数で、デフォルトは1です
sampleCountが1より大きい場合は、マルチサンプリングされたカラーテクスチャが作成され、
currentDrawableのテクスチャはcurrentRenderPassDescriptorのresolve textureとして設定され、
ストアアクションはMTLStoreActionMultisampleResolveに設定されます。
override func viewDidAppear(_ animated: Bool)
self.tessellationPipeline = tessellationPipeline.initWithMTKView(mtkView: self.mtkView )メソッドは、
AAPLTessellationPipelineクラスのfunc initWithMTKView(mtkView: MTKView )->Selfメソッドを呼び、
AAPLTessellationPipeline自身のクラスをViewController内のvar tessellationPipelineに代入しています。
self.mtkView.draw()でMetal画面生成を実施しています。
@IBAction func triagleQuad(_ sender: UISegmentedControl)
上記のメソッドでtessellationPipelineがデリゲードされたので、AAPLTessellationPipelineのMetalデータセットを共有でき、
Metalの描写タイプを選択できます。
@IBAction func segument(_ sender: UISwitch)
self.tessellationPipelineのsenderを設定することにより、AAPLTessellationPipelineクラスのMetalデータセット計算ロジックを可変しています。
@IBAction func sliderHorizon(_ sender: UISlider), @IBAction func insideHorizon(_ sender: UISlider)
スライダーの設定値を self.tessellationPipeline.insideFactor = sender.valueで代入して、
AAPLTessellationPipelineクラスのsetBufferメソッドに代入しています。
class AAPLTessellationPipeline :NSObject,MTKViewDelegate
func initWithMTKView(mtkView: MTKView )->Self
このクラスはMetalセットアップ、パイプライン設定,レンダリング設定のメソッド呼び出しを行なっています。
wireframe = true,patchType = MTLPatchType.triangle,edgeFactor = 2.0,insideFactor = 2.0初回設定です。(ViewController内のUIと関連)
mtkView.device = device,mtkView.delegate = self -> AAPLTessellationPipelineクラスとViewControllerクラスのインスタンスと共有しています。
func didSetupMetal()->Bool
device = MTLCreateSystemDefaultDevice()はMetalDeviceへの参照を返します。
メタルデバイスを取得するこのAPIは、システムを高電力に切り替えるGPU。
複数のGPUをサポートする他のシステムでは、メインディスプレイに関連付けられています。
commandQueue = device.makeCommandQueue() では新しいコマンドキューを作成して返します。
この方法で作成されたコマンドキューは、完了していないコマンドバッファを最大64個まで許可します。
library = device.newDefaultLibrary()ではメインバンドルのデフォルトライブラリを返します。
func didSetupComputePipelines()->Bool
let kernelFunctionTriangle = library.makeFunction(name: "tessellation_kernel_triangle")メソッドで
TessellationFunctions.metalクラスのTessellationFunctions.metalメソッドを呼び出しています。
MTLComputePipelineState(計算パスで使用される計算機能と構成状態を含むオブジェクト。)である
computePipelineTriangle変数に computePipelineTriangle = try device.makeComputePipelineState(function: kernelFunctionTriangle!)で
パイプラインを設定しています。
computePipelineQuadも同様な処理です。
func didSetupRenderPipelinesWithMTKView(view:MTKView)->Bool
ディスクリプター(構成情報の保持)の設定とレンダリングパイプライン(レンダリングパイプラインとはモデルデータの入力から出力のデータ内設計書)の設定を実施して、
didSetupComputePipelines()と同様な処理を実施しています。
renderPipelineDescriptor.maxTessellationFactor = 16は パッチをテッセレーションする際にテッセレータが使用する最大テッセレーション係数を指定します。
テッセレーションとは
↑募金しました。(300円)
テッセレーション (Tessellation) は、コンピュータグラフィックスの画像演算手法の1つである。
2次元画像上で3次元の複雑な立体を表現するのために多数のポリゴン (polygon) が用いられるが、
テッセレーションはこのポリゴンメッシュをさらに分割して表現することで、画像をより詳細かつ滑らかで現実感のあるものにする技術である。
func setUpBuffers()
MTLBufferのtessellationFactorsBuffer変数に、新しいメモリを割り当ててバッファを作成します。
let controlPointPositionsTriangle : Array
makeBufferのLeangthを設定いる箇所でObjective-cと同様な処理を実装していたところ、最後の方で処理drawPatchesメソッドでエクセプションが起きました。
エラー内容は、。
validateFunctionArguments:2800: failed assertion `(length - offset)(8) must be >= 16 at buffer binding at index 0 for vertexBuffer.0[0].'。
bufferに対してlengthが足りない状態でした。
そこでこのクラスで設定している。makeBufferメソッドのlength設定を変更したところ、描写に成功しました。
func computeTessellationFactorsWithCommandBuffer(commandBuffer:MTLCommandBuffer)
let computeCommandEncoder = commandBuffer.makeComputeCommandEncoder()は、
エンコードを実施しています。このコマンドバッファにエンコードする計算コマンドエンコーダを返します。
使用される計算パイプラインの状態を設定します。
setBytesメソッドは、
指定されたバッファー・バインディング・ポイントのデータをコピーで設定します。 これにより、既存のMTLBufferがバインディングポイントから削除されます。
setBufferメソッドは、
指定されたバインドポイントインデックスにあるすべての計算カーネルのグローバルバッファを設定します。
dispatchThreadgroupsメソッドは、
計算関数ディスパッチをエンキューします。
エンキューは、データベース・リソースへのアクセスをシリアライズする共有メモリーの構造(ロック)です。
エンキューは、セッションまたはトランザクションと対応付けることが可能です。
endEncoding()メソッドは、
このエンコーダからのすべてのコマンド生成が完了したことを宣言し、MTLCommandBufferから切り離します。
func tessellateAndRenderInMTKView(view:MTKView,withCommandBuffer:MTLCommandBuffer)
let renderPassDescriptor = view.currentRenderPassDescriptorのcurrentRenderPassDescriptorは、
テクスチャとビューの深度、ステンシル、およびサンプルバッファとクリア値から生成されたレンダーパス記述子。
これはコンビニエンスプロパティです。 Viewはこの記述子を使用せず、アプリケーションがこの記述子を使用する必要はありません。
let renderCommandEncoder = withCommandBuffer.makeRenderCommandEncoder(descriptor: renderPassDescriptor!)は、
コマンドバッファにエンコードするレンダーコマンドエンコーダを返します。
setVertexBufferメソッドは、
指定されたバインドポイントインデックスにあるすべての頂点シェーダのグローバルバッファを設定します。
drawPatchesメソッドは、
指定されたステージでイベントが発生するまで、さらなるGPU作業を防止します。
とは異なり、このメソッドは指定されたステージに関連付けられたコマンドのみをブロックし、コマンドが実行時に重なり合うようにします。
iOSでは、レンダーコマンドエンコーダのフェンス待機は常にエンコーダの先頭に発生します。
presentメソッドは、
このコマンドバッファが実行のためにスケジューリングされたときに呼び出されるdrawableを追加します。
サブミッションスレッドはロックされ、現在の呼び出しはウィンドウサーバーによって処理されます。
func draw(in view: MTKView)
computeTessellationFactorsWithCommandBufferメソッドと、tessellateAndRenderInMTKViewメソッドを実施しています。
このクラスでMTKViewDelegateの必須メソッドです。
func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) {}も同様です。
簡単ではございますが、Metalを描写しているFlowを記載しました。これからもMetalの研究していきます。
AppStore リリースしているアプリの紹介

こちらは個人として5月にリリースしたアプリケーションで製作期間は約1ヶ月です。
機能は二つのカメラとアルバム機能、画像を他のアプリケーションと共有機能です。
一つ目のカメラは輪郭抽出です。二つ目のカメラは底辺、高さを計測します。
アルバム機能では画面のサイズと角度を計測できます。情報を他のアプリケーションと共有できます。
今後の予定
次回は、TessellationFunctions.metalを解析していきます。
理由は徹底的にやり込み、原理原則を体と脳に代入するためです。
モバイルパワーと通信パワーが上がるにつれて、3Dプログラミングが増えてくるのはないかと考えています。
次回も、動かせるMetalプログラミングを解説していきます。
貴重な時間に、お読み下さいましてありがとうございました。