复杂的 CoreImage CIFilter 管道以递归方式占用 GB 内存

问题描述

我正在编写一个 macOS 应用,它对图像执行一系列复杂的 CIFilter 操作,以极大地改变高分辨率照片(通常为 24 兆像素或更大)的外观。

其中一些效果包括高斯模糊、不清晰蒙版、泛光、阴暗,以及我使用自定义 CIKernelMetal 中编写的自定义“颗粒”片段着色器。 CIContext 正在使用 Metal 设备来呈现它。本质上,它是一个长链 initialImage -> CIFilter -> outputimage -> CIFilter -> outputimage -> CIFilter -> ...

不仅必须按顺序运行所有这些 CIFilter 以获得最终输出,还必须以全分辨率运行才能正确缩放效果

我面临的问题是执行整个进程会导致大量内存使用。对于 6000x4000 的输入图像,渲染时内存使用量跃升至 6.6GiB。

我使用 Xcode 中的 Metal 工具来诊断问题,看起来 CoreImage 正在为每个过滤器递归分配内存,因此内存只会堆积如山,直到它最终可以最终释放所有资源。

我希望这是顺序的,在下一个操作之前释放每个源缓冲区。我不确定如何帮助这种情况。有没有办法将每个输出图像传递给下一个过滤器,首先强制清理每个输入 CIImage 渲染?

Metal Resource Events

if let device = self.device,let texture = device.makeTexture(descriptor: descriptor),let queue = device.makeCommandQueue(),let buffer = queue.makeCommandBuffer() {
    
    let destination = CIRenderDestination(
        width: descriptor.width,height: descriptor.height,pixelFormat: self.colorPixelFormat,commandBuffer: buffer) {
            return texture
        }
    
    try! MetalImage.context.context.startTask(toRender: MetalImage.image,to: destination)
    self.renderedImage = texture
    
    buffer.commit()
}

解决方法

在没有任何 CIContext 的情况下向 CIImage 显示 24 兆像素的 NSImageView 时,这似乎只占用几百 MB 的内存。

let image = // Create CIImage from complex CIFilter pipeline

let rep = NSBitmapImageRep(ciImage: image)
let previewImage = NSImage(size: rep.size)
previewImage.addRepresentation(rep)
previewImageView?.image = previewImage

在 5K iMac 上,它将非常有效地渲染以进行预览(更新时间不到半秒)。在使用 CIContexts 导出图像之前不需要 CGImageDestination