关于CIContext、OpenGL和金属(SWIFT)的混淆,CIContext默认使用CPU还是GPU?

yr9zkbsy  于 2022-10-18  发布在  Swift
关注(0)|答案(2)|浏览(234)

因此,我正在制作一款应用程序,其中一些主要功能围绕着将CIFilter应用到图像上。

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

所有这些都使我在CameraView控制器上的CPU使用率(70%)与我对帧应用滤镜和更新图像视图的CPU使用率大致相同。所有这些似乎都是以完全相同的方式工作的,这让我认为我遗漏了一些重要的信息。
例如,使用AVFoundation,我从相机获得每一帧,应用滤镜,并用新图像更新图像视图。

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
1.从CgImg创建CiImg
1.使用上下文应用过滤器并转换回UiImg
1.使用新的UiImg更新任何视图
它在我的iPhone X上运行得很好,在我的iPhone 6上也出人意料地运行得很好。由于我的应用程序非常完整,我希望尽可能地优化它。我已经看了很多关于使用OpenGL和金属来做事情的文档,但似乎想不出如何开始。
我一直认为我是在CPU上运行这些进程,但使用OpenGL和Metals创建上下文并没有带来任何改进。我是否需要使用MetalKit视图或GLKit视图(EaglContext似乎已完全弃用)?我该怎么把这个翻译过来?苹果的文档似乎平淡无奇。

4xy9mtcn

4xy9mtcn1#

我开始对此发表评论,但我认为自从WWDC‘18年以来,这是最好的答案。我会以Maven的身份进行编辑,而不是评论,如果这样做是正确的,我愿意删除整个答案。
你在正确的轨道上-当你可以的时候利用图形处理器,它是一个很好的匹配。核心图像和金属,虽然“通常”使用图形处理器的“低级”技术,可以使用CPU,如果需要的话。核心图形?它使用CPU来渲染事物。
图像。UIImageCGImage是实际图像。然而,CIImage并非如此。考虑它的最好方法是为一张图片准备一份“食谱”。
在使用滤镜时,我通常会坚持使用CoreImage、CIFilters、CIImages和GLKViews。对CI Image使用GLKView意味着使用OpenGL以及单个CIContextEAGLContext。它提供的性能几乎与使用MetalKitMTKViews一样好。
至于使用UIKit,它是UIImageUIImageView,我只在需要的时候才使用-保存/分享/上传,无论是什么。在此之前,请坚持使用GPU。
……
这是事情开始变得复杂的地方。
Metals是苹果专有的API。因为他们拥有硬件--包括CPU和GPU--他们已经为他们优化了硬件。它的“管道”与OpenGL略有不同。没什么大不了的,只是不同而已。
直到WWDC‘18年,使用GLKit,包括GLKView,都是很好的。但OpenGL的所有功能都被淘汰了,苹果正在将这些东西转移到金属上。虽然(目前)性能提升不是很大,但您最好还是使用MTKView、Metals和CIConext`的新功能。
看看@Matt给here的答案,这是一个很好的使用MTKview的方法。

lqfhib0f

lqfhib0f2#

一些独立的观点:

  • 分析你的应用程序,找出它把CPU时间花在哪里了。
  • 如果显卡工作实际上不是很难--也就是说,如果你的应用程序不受GPU限制--优化GPU工作可能对整体性能没有帮助。
  • 尽量避免在CPU和GPU之间来回移动数据。不要一直为每个过滤器输出创建CGImage。Core Image的一个主要功能是ability to chain filters,不需要为每个ability to chain filters进行渲染,然后一次渲染所有效果。此外,正如dfd在他的回答中所说,如果您直接渲染到屏幕上,而不是创建一个UIImage来在图像视图中显示,那会更好。
  • 避免重复工作。不要每次都重新创建CIFilter对象。如果参数没有改变,不要每次都重新配置它们。

相关问题