Edited at
MetalDay 12

[iOS]MetalでGPUコンピューティング(8) MTLComputeCommandEncoder

More than 1 year has passed since last update.

この記事は、Metal Advent Calendar2016の12日目です。

これまで、MetalのGPUコンピューティングについて解説記事を書いてきました。

[iOS] MetalでGPUコンピューティング (1) 最小限のコードの記述と特性の把握

[iOS] MetalでGPUコンピューティング (2) 群知能

[iOS] MetalでGPUコンピューティング (3) MTLDevice

[iOS] MetalでGPUコンピューティング (4) MTKView

[iOS] MetalでGPUコンピューティング (5) MTLLibrary

[iOS] MetalでGPUコンピューティング (6) MTLCommandQueue

[iOS] MetalでGPUコンピューティング (7) MTLCommandBuffer


本記事では、前回に引き続きAppleが提供するサンプルコードの解説を行います。

扱うサンプルコードは、前回と同じライフゲームのアプリ、MetalGameOfLifeです。

MetalGameOfLife

IMG_5933.PNG

(実行画面)

今回は、サンプルコード内のMTLComputeCommandEncoderについて解説を行います。

MTLComputeCommandEncoderはGPUによる並列コンピューティング用の設定や関数をエンコーディングするためのメソッドの集合体です。

MTLComputeCommandEncoderはクラスではなくプロトコルです。

MTLComputeCommandEncoderのオブジェクト(エンコーダー)は、MTLCommandBufferのオブジェクト(コマンドバッファ)により生成されます。そして、エンコーダーによってエンコードされたコンピューティング用のコマンドはコマンドバッファに格納されます。

エンコーディングは、MTLCommandBufferオブジェクトの生成の都度行われます。

繰り返しになるのですが、このサンプルコードは、主に以下のファイルで構成されています。

AAPLRender.h

AAPLRender.m

AAPLViewController.h

AAPLViewController.m

Sharder.metal

このうち、AAPLRender.mには並列コンピューティング及び描画のCPU側のロジックが、Shader.metalには頂点シェーダー、フラグメントシェーダー、GPUコンピューティング用のシェーダーが書かれています。

ここからは、サンプルコード内におけるMTLComputeCommandEncoderの使用箇所を解説していきます。

AAPLRender.mに以下の記述があります。MTLCommandBufferのcomputeCommandEncoderメソッドによりエンコーダーが生成されています。この記述は毎フレームごとに呼ばれる箇所にあります。


AAPLRender.m

id<MTLComputeCommandEncoder> commandEncoder = [commandBuffer computeCommandEncoder];


また、以下のコードでは、MTLComputePipelineStateのオブクジェクトをエンコーダーに設定しています。MTLComputePipelineStateのオブジェクトは、シェーダー内に記述されたこれから実行されるコンピューティング用の関数を含んでいます。


AAPLRender.m

[commandEncoder setComputePipelineState:self.simulationPipelineState];


以下のコードでは、エンコーダーに使用するリソースの指定を行なっています。リソースはシェーダー内の関数の引数として渡されます。


AAPLRender.m

[commandEncoder setTexture:readTexture atIndex:0];

[commandEncoder setTexture:writeTexture atIndex:1];
[commandEncoder setSamplerState:self.samplerState atIndex:0];

上記では、関数へのインプット用のテクスチャ、アウトプット用のテクスチャ、サンプラーが渡されています。テクスチャ、サンプラーにはそれぞれ識別用にインデックスを振ります。バッファを用いて数値データを渡したい場合も、同様にインデックスを振ります。

この辺り、詳細は後の記事で紹介したいと思います。

以下のコードでは、GPUのスレッド及びスレッドグループ数の指定を行い、並列コンピューティング用の関数を、スレッドグループ及びスレッドグループ内のスレッド用にエンコードします。


AAPLRender.m

MTLSize threadsPerThreadgroup = MTLSizeMake(16, 16, 1);

MTLSize threadgroupCount = MTLSizeMake(ceil((float)self.gridSize.width / threadsPerThreadgroup.width), ceil((float)self.gridSize.height / threadsPerThreadgroup.height), 1);
...
[commandEncoder dispatchThreadgroups:threadgroupCount threadsPerThreadgroup:threadsPerThreadgroup];

スレッド及びスレッドグループに関しては後の記事で詳細を解説したいと思います。

以下では、endEncodingメソッドによりエンコーダーからのコマンドが完了したことを宣言しています。この宣言があると、これ以上エンコーダーを使用することはできなくなります。


AAPLRender.m

[commandEncoder endEncoding];


サンプルコード内には毎フレーム必ず呼ばれる箇所の記述もありますが、画面をタップした際に実行される箇所もあります。そこでは異なるMTLComputePipelineStateオブジェクトを指定し、異なるコンピューティング用の関数を指定しています。


AAPLRender.m

[commandEncoder setComputePipelineState:self.activationPipelineState];

[commandEncoder setTexture:writeTexture atIndex:0];
[commandEncoder setBytes:cellPositions length:byteCount atIndex:0];
[commandEncoder dispatchThreadgroups:threadgroupCount threadsPerThreadgroup:threadsPerThreadgroup];

このように、MTLComputeCommandEncoderはコマンドバッファに対して各種のコマンドをエンコードする役割を担っています。

今回はライフゲームのサンプルコード内におけるMTLCommandQueueの解説を行いました。

次回以降、さらに他の箇所についての解説を行なっていきます。