I am getting jpeg data streamed via peer iOS device. I process and display it using CoreImage and Metal + MTKView in the following way. I receive jpeg data and convert it to CIImage. Next I apply appropriate transform on CIImage and render it to CVPixelBuffer. I then display this CVPixelBuffer to MTKView via passthrough shaders (Metal processing & MTKView does not use ciContext that is used by CIImage part of code). Then I convert the pixel buffer to YUV, do some image processing, and display that data in an MTKView as well. Problem is it sometimes gets too laggy and slow. Are there any bottlenecks in the code below that are caused by using CoreImage and Metal? How to make this pipeline better?
private func handleImageData(_ data:Data) {
DispatchQueue.main.async {
if let image = CIImage(data: data) {
self.displayImage(image)
}
}
}
private func displayImage(_ image:CIImage) {
let transformFilter = CIFilter(name: "CIAffineTransform")!
transformFilter.setValue(image, forKey: "inputImage")
transformFilter.setValue(NSValue(cgAffineTransform: self.framesTransform), forKey: "inputTransform")
let sourceImage = transformFilter.value(forKey: "outputImage") as! CIImage
//Render CIImage to pixel buffer
var pixelBuffer:CVPixelBuffer? = nil
CVPixelBufferPoolCreatePixelBuffer(kCFAllocatorDefault, self.ciPixelBufferPool!, &pixelBuffer)
if let pixBuf = pixelBuffer {
self.ciContext.render(sourceImage, to: pixBuf)
self.displayPixelBufferOnMTKView(pixBuf)
//Convert the pixel buffer to YUV as some filters operate only on YUV
if let yuvPixelBuffer = self.rgb2yuvFrameRenderer?.copyRenderedPixelBuffer(pixBuf) {
self.processYUVPixelBuffer(yuvPixelBuffer)
}
}
}
Here is Metal shader code:
// Compute kernel
kernel void kernelRGBtoYUV(texture2d<half, access::read> inputTexture [[ texture(0) ]],
texture2d<half, access::write> textureY [[ texture(1) ]],
texture2d<half, access::write> textureCbCr [[ texture(2) ]],
constant ColorConversion &colorConv [[ buffer(0) ]],
uint2 gid [[thread_position_in_grid]])
{
// Make sure we don't read or write outside of the texture
if ((gid.x >= inputTexture.get_width()) || (gid.y >= inputTexture.get_height())) {
return;
}
float3 inputColor = float3(inputTexture.read(gid).rgb);
float3 yuv = colorConv.matrix*inputColor + colorConv.offset;
half4 uv = half4(yuv.gbrr);
textureY.write(half(yuv.x), gid);
if (gid.x % 2 == 0 && gid.y % 2 == 0) {
textureCbCr.write(uv, uint2(gid.x / 2, gid.y / 2));
}
}