关于CIContext,OpenGL和Metal(SWIFT)的困惑。 CIContext默认使用CPU还是GPU?

时间:2018-08-19 23:27:06

标签: ios swift opengl metal

因此,我正在开发一个应用程序,其中一些主要功能围绕将CIFilter应用于图像。

let context = CIContext()
let context = CIContext(eaglContext: EAGLContext(api: .openGLES3)!)
let context = CIContext(mtlDevice: MTLCreateSystemDefaultDevice()!)

所有这些在我的CameraViewController上给我大约相同的CPU使用率(70%),在这里我将滤镜应用于帧并更新imageview。所有这些似乎都以完全相同的方式工作,这使我觉得我缺少一些重要的信息。

例如,使用AVFoundation,我从相机获取每个帧,然后应用滤镜并使用新图像更新imageview。

let context = CIContext()

func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
    connection.videoOrientation = orientation
    connection.isVideoMirrored = !cameraModeIsBack
    let videoOutput = AVCaptureVideoDataOutput()
    videoOutput.setSampleBufferDelegate(self, queue: DispatchQueue.main)

    let sharpenFilter = CIFilter(name: "CISharpenLuminance")
    let saturationFilter = CIFilter(name: "CIColorControls")
    let contrastFilter = CIFilter(name: "CIColorControls")
    let pixellateFilter = CIFilter(name: "CIPixellate")

    let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer)
    var cameraImage = CIImage(cvImageBuffer: pixelBuffer!)

    saturationFilter?.setValue(cameraImage, forKey: kCIInputImageKey)
    saturationFilter?.setValue(saturationValue, forKey: "inputSaturation")
    var cgImage = context.createCGImage((saturationFilter?.outputImage!)!, from: cameraImage.extent)!
    cameraImage = CIImage(cgImage: cgImage)

    sharpenFilter?.setValue(cameraImage, forKey: kCIInputImageKey)
    sharpenFilter?.setValue(sharpnessValue, forKey: kCIInputSharpnessKey)
    cgImage = context.createCGImage((sharpenFilter?.outputImage!)!, from: (cameraImage.extent))!
    cameraImage = CIImage(cgImage: cgImage)

    contrastFilter?.setValue(cameraImage, forKey: "inputImage")
    contrastFilter?.setValue(contrastValue, forKey: "inputContrast")
    cgImage = context.createCGImage((contrastFilter?.outputImage!)!, from: (cameraImage.extent))!
    cameraImage = CIImage(cgImage: cgImage)

    pixellateFilter?.setValue(cameraImage, forKey: kCIInputImageKey)
    pixellateFilter?.setValue(pixelateValue, forKey: kCIInputScaleKey)
    cgImage = context.createCGImage((pixellateFilter?.outputImage!)!, from: (cameraImage.extent))!
    applyChanges(image: cgImage)

}

另一个示例是我如何仅对普通图像应用更改(我使用滑块进行所有操作)

   func imagePixelate(sliderValue: CGFloat){
    let cgImg = image?.cgImage
    let ciImg = CIImage(cgImage: cgImg!)
    let pixellateFilter = CIFilter(name: "CIPixellate")
    pixellateFilter?.setValue(ciImg, forKey: kCIInputImageKey)
    pixellateFilter?.setValue(sliderValue, forKey: kCIInputScaleKey)
    let outputCIImg = pixellateFilter?.outputImage!
    let outputCGImg = context.createCGImage(outputCIImg!, from: (outputCIImg?.extent)!)
    let outputUIImg = UIImage(cgImage:outputCGImg!, scale:(originalImage?.scale)!, orientation: originalOrientation!)
    imageSource[0] = ImageSource(image: outputUIImg)
    slideshow.setImageInputs(imageSource)
    currentFilteredImage = outputUIImg
}

非常好:

  1. 从UiImg创建CgImg
  2. 从CgImg创建CiImg
  3. 使用上下文应用过滤器并将其转换回UiImg
  4. 使用新的UiImg更新任何视图

这在我的iPhone X上运行良好,并且在我的iPhone 6上也运行良好。由于我的应用程序非常完整,因此我希望尽可能地对其进行优化。我浏览了很多有关使用OpenGL和Metal进行处理的文档,但似乎无法弄清楚如何开始。

我一直以为我在CPU上运行这些进程,但是使用OpenGL和Metal创建上下文并没有改善。我是否需要使用MetalKit视图或GLKit视图(eaglContext似乎已完全弃用)?我该如何翻译呢?苹果文件似乎没什么意思。

2 个答案:

答案 0 :(得分:1)

我开始对此发表评论,但是我认为自WWDC'18以来,这是最好的答案。我会像其他人一样编辑比我评论的要多的专家,如果愿意的话,我愿意删除整个答案。

您处在正确的轨道上-尽可能使用GPU,这非常合适。 CoreImage和Metal,而“通常”使用GPU的“低级”技术,如果需要,可以使用CPU。 CoreGraphics?它使用GPU 渲染东西。

图像。 UIImageCGImage是实际图像。 CIImage不是。想到它的最好方法是图像的“配方”。

我通常-现在,我将稍后解释-使用过滤器时,请坚持使用CoreImage,CIFilters,CIImages和GLKViews。对CIImage使用GLKView意味着使用OpenGL以及单个CIContextEAGLContext。与使用MetalKitMTKViews一样,它提供的几乎性能都很好。

对于使用UIKit以及UIImageUIImageView来说,我只在需要时才做-保存/共享/上传。一直坚持到那时。

....

在这里开始变得复杂。

Metal是Apple专有的API。由于他们拥有硬件(包括CPU和GPU),因此已经对其进行了优化。它的“管道”与OpenGL有所不同。没什么大不了的,只是有所不同。

直到WWDC'18使用GLKit(包括GLKView)都可以。但是OpenGL的所有东西都被贬低了,苹果公司正在将它们转移到Metal。尽管目前的性能提升不是那么好,但最好还是使用MTKView,Metal和CIContext`。

看看答案,@ matt给了here一个使用MTKViews的好方法。

答案 1 :(得分:0)

一些独立点:

  • 为您的应用程序配置文件以弄清楚它在哪里花费了CPU时间。
  • 如果图形工作实际上不是很困难(也就是说,如果您的应用程序不受GPU约束),则优化GPU工作可能无助于整体性能。
  • 尝试避免在CPU和GPU之间来回移动数据。不要继续为每个过滤器输出创建CGImage。 Core Image的主要功能是ability to chain filters,无需为每个效果进行渲染,然后立即渲染所有效果。另外,正如dfd在他的回答中所说,如果您直接在屏幕上渲染而不是创建UIImage以在图像视图中显示,那会更好。
  • 避免重复工作。不要每次都重新创建CIFilter对象。如果参数没有更改,请不要每次都重新配置它们。