IOSで120fpsでリアルタイムのビデオ処理を行っており、最初にGPUで画像を前処理し(ダウンサンプリング、CPUで十分な速度ではない色の変換など)、後でOpenCVを使用してCPUでフレームを後処理したいと考えています。
Metalを使用してGPUとCPUの間でカメラフィードを共有する最速の方法は何ですか?
言い換えると、パイプは次のようになります。
_CMSampleBufferRef -> MTLTexture or MTLBuffer -> OpenCV Mat
_
CMSampleBufferRef-> MTLTextureを次のように変換しています
_CVPixelBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
// textureRGBA
{
size_t width = CVPixelBufferGetWidth(pixelBuffer);
size_t height = CVPixelBufferGetHeight(pixelBuffer);
MTLPixelFormat pixelFormat = MTLPixelFormatBGRA8Unorm;
CVMetalTextureRef texture = NULL;
CVReturn status = CVMetalTextureCacheCreateTextureFromImage(NULL, _textureCache, pixelBuffer, NULL, pixelFormat, width, height, 0, &texture);
if(status == kCVReturnSuccess) {
textureBGRA = CVMetalTextureGetTexture(texture);
CFRelease(texture);
}
}
_
金属シェーダーが完成したら、MTLTextureをOpenCVに変換します
_cv::Mat image;
...
CGSize imageSize = CGSizeMake(drawable.texture.width, drawable.texture.height);
int imageByteCount = int(imageSize.width * imageSize.height * 4);
int mbytesPerRow = 4 * int(imageSize.width);
MTLRegion region = MTLRegionMake2D(0, 0, int(imageSize.width), int(imageSize.height));
CGSize resSize = CGSizeMake(drawable.texture.width, drawable.texture.height);
[drawable.texture getBytes:image.data bytesPerRow:mbytesPerRow fromRegion:region mipmapLevel:0];
_
いくつかの観察:
1)残念ながら、_MTLTexture.getBytes
_は高価に見え(GPUからCPUにデータをコピーしますか?)、iPhone 5Sでは約5ミリ秒かかります。これは、約100fpsで処理する場合は長すぎます。
2)次のメソッドでMTLTextureの代わりにMTLBufferを使用している人がいることに気付きました:metalDevice.newBufferWithLength(byteCount, options: .StorageModeShared)
(参照: メモリ書き込みパフォーマンス-GPU CPU共有メモリ )
ただし、CMSampleBufferRef
とそれに付随するCVPixelBufferRef
はCoreVideoによって管理されていると推測されます。
これを行う最も速い方法は、MTLBufferに裏打ちされたMTLTextureを使用することです。これは、MTLBufferとメモリを共有する特別な種類のMTLTextureです。ただし、C処理(openCV)は1〜2フレーム遅れて実行されます。これは、コマンドをGPU(エンコード)に送信する必要があり、waitUntilCompletedを使用してGPUを確認する場合は、GPUがレンダリングする必要があるため避けられません。 CPUをかみ砕くだけで無駄な仕上がりになります。
したがって、プロセスは次のようになります。最初にMTLBufferを作成し、次にMTLBufferメソッド "newTextureWithDescriptor:offset:bytesPerRow:"を使用して特別なMTLTextureを作成します。特別なMTLTextureを(インスタンス変数として)事前に作成する必要があります。次に、CMSampleBufferRefから作成されたMTLTextureを取得し、これを特別なMTLTextureに渡す標準のレンダリングパイプラインを(計算シェーダーを使用するよりも高速に)セットアップする必要があります。そのパスでは、1回のパスでダウンスケールし、必要に応じて色変換を行うことができます。次に、コマンドバッファーをGPUに送信します。後続のパスで、[theMTLbufferの内容]を呼び出すだけで、openCVで使用するために特別なMTLTextureをバックアップするバイトへのポインターを取得できます。
CPU/GPUの動作を強制的に停止させる手法は、待機に半分の時間が費やされるため、効率的ではありません。つまり、CPUはGPUが終了するのを待機し、GPUは次のエンコーディングも待機する必要があります(GPUが機能している場合)。 GPUが終了するのを待つのではなく、CPUが次のフレームをエンコードしてopenCV作業を実行するようにします)。
また、人々が通常リアルタイム処理を参照する場合、通常はリアルタイムフィードバック(ビジュアル)を使用した処理を参照しますが、4以降のすべての最新のiOSデバイスは60Hzの画面リフレッシュレートを備えているため、フィードバックはそれよりも速く表示されますは無意味ですが、1(60Hz)にするために2フレーム(120Hz)が必要な場合は、カスタムタイマーを用意するか、CADisplayLinkを変更する必要があります。