【问题标题】:Confusion About CIContext, OpenGL and Metal (SWIFT). Does CIContext use CPU or GPU by default?关于 CIContext、OpenGL 和 Metal (SWIFT) 的困惑。 CIContext 默认使用 CPU 还是 GPU?
【发布时间】:2019-01-26 02:27:22
【问题描述】:

所以我正在制作一个应用程序,其中一些主要功能围绕着将 CIFilters 应用于图像。

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

所有这些都在我的 CameraViewController 上为我提供了大约相同的 CPU 使用率 (70%),我将过滤器应用于帧并更新图像视图。所有这些似乎都以完全相同的方式工作,这让我觉得我错过了一些重要的信息。

例如,我使用 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 似乎已完全弃用)?我该如何翻译这个?苹果文档似乎乏善可陈。

【问题讨论】:

    标签: ios swift opengl metal


    【解决方案1】:

    我开始对此发表评论,但我认为自 WWDC'18 以来,这最适合作为答案。我会像其他人一样编辑而不是评论,如果这是正确的做法,我愿意删除整个答案。

    您在正确的轨道上 - 尽可能利用 GPU,它非常适合。 CoreImage 和 Metal,虽然“通常”使用 GPU 的“低级”技术,如果需要,可以使用 CPU。核心图形?它使用 GPU渲染事物。

    图像。 UIImageCGImage 是实际图像。但是,CIImage 不是。考虑它的最佳方式是图像的“配方”。

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

    至于使用UIKitUIImageUIImageView,我只在需要时使用——保存/共享/上传等等。在那之前坚持使用 GPU。

    ....

    这里是开始变得复杂的地方。

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

    直到 WWDC'18,使用 GLKit,包括 GLKView,都很好。但是 OpenGL 的所有东西都被贬低了,苹果正在把东西转移到 Metal 上。虽然(目前)性能提升不是那么好,但您可能最好使用新的东西来使用 MTKView、Metal 和 CIContext`。

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

    【讨论】:

    • 感谢您的资源!我能够成功地实现我想用它做的事情。在我的 iphoneX 上给了我很好的性能,但在我的 iphone6 上运行的 mtkView 无法使用!通过每秒将图像渲染到 imageview 30 次,我获得了更好的用户体验。看起来是 iphone 6 中 A8 芯片的限制。
    • 很高兴我能帮上忙。至于 iPhone 6?在一个月内,我们将落后大约 3 代(3)。是的,我真的希望苹果能保持更多的一致性......但我猜这就是我们付出的代价。
    【解决方案2】:

    一些独立的点:

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

    【讨论】:

    • 是的,我刚刚意识到我没有有效地链接或创建过滤器,谢谢!有趣的是,我实现了 MetalKitView,在我的 iPhone X 上我已经将 CPU 负载减少了约 35%,但在我的 iPhone 6 上,这简直太糟糕了。虽然在它上面渲染到 mtkView 只使用大约 17% 的 cpu,但它非常有问题和跳跃。在纸面上,性能更好,但在实践中无法使用。
    • @RahishMontegomery 我有相反的问题。我的 iPhone 6s Plus 表现出色,电池影响较小,但我的 iPhone XS 表现得更差,电池影响大,即使通过相机并渲染的图像在两部手机之间大小相同。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-01
    • 1970-01-01
    • 2013-07-27
    相关资源
    最近更新 更多