【问题标题】:Memory usage keeps rising on older devices using Metal使用 Metal 的旧设备上的内存使用量不断上升
【发布时间】:2019-11-13 05:55:46
【问题描述】:

我使用MetalCADisplayLink 实时过滤CIImage 并将其渲染为MTKView

// Starting display link 
displayLink = CADisplayLink(target: self, selector: #selector(applyAnimatedFilter))
displayLink.preferredFramesPerSecond = 30
displayLink.add(to: .current, forMode: .default)

@objc func applyAnimatedFilter() {
    ...
    metalView.image = filter.applyFilter(image: ciImage)
}

根据 Xcode 中的内存监视器,内存使用量在 iPhone X 上是稳定的,不会超过 100mb,在 iPhone 6 或 iPhone 6s 等设备上,内存使用量一直在增长,直到最终系统终止应用程序。

我已经使用Instruments 检查了内存泄漏,但没有报告泄漏。通过分配运行应用程序也不会显示任何问题,并且应用程序不会被系统关闭。我还发现有趣的是,在较新的设备上,内存使用是稳定的,但在较旧的设备上,它只会不断增长。

过滤器的复杂性并不重要,因为我尝试了最简单的过滤器,但问题仍然存在。这是我的金属文件中的一个示例:

extern "C" { namespace coreimage {

    float4 applyColorFilter(sample_t s, float red, float green, float blue) {

        float4 newPixel = s.rgba;
        newPixel[0] = newPixel[0] + red;
        newPixel[1] = newPixel[1] + green;
        newPixel[2] = newPixel[2] + blue;

        return newPixel;
    }
}

我想知道是什么原因导致旧设备出现问题,以及我应该朝哪个方向努力。

更新 1:这里有两张 1 分钟图,一张来自Xcode,一张来自Allocations,两者都使用相同的过滤器。 Allocations 图表稳定,而Xcode 图表一直在增长:

更新2:附上按大小排序的分配列表截图,应用运行了16分钟,不停地应用过滤器:

更新 3:更多关于 applyAnimatedFilter() 中发生的事情的信息:

我将过滤后的图像渲染为metalView,即MTKView。我从filter.applyFilter(image: ciImage) 收到过滤后的图像,接下来在Filter 中发生:

 func applyFilter(image: ciImage) -> CIImage {
    ...
    var colorMix = ColorMix()
    return colorMix.use(image: ciImage, time: filterTime)
 }

filterTime 只是一个 Double 变量。最后,这是整个 ColorMix 类:

import UIKit

class ColorMix: CIFilter {

    private let kernel: CIKernel

    @objc dynamic var inputImage: CIImage?
    @objc dynamic var inputTime: CGFloat = 0

    override init() {

        let url = Bundle.main.url(forResource: "default", withExtension: "metallib")!
        let data = try! Data(contentsOf: url)
        kernel = try! CIKernel(functionName: "colorMix", fromMetalLibraryData: data)
        super.init()
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    func outputImage() -> CIImage? {

        guard let inputImage = inputImage else {return nil}

        return kernel.apply(extent: inputImage.extent, roiCallback: {
            (index, rect) in
            return rect.insetBy(dx: -1, dy: -1)
        }, arguments: [inputImage, CIVector(x: inputImage.extent.width, y: inputImage.extent.height), inputTime])
    }

    func use(image: CIImage, time: Double) -> CIImage {

        var resultImage = image

        // 1. Apply filter
        let filter = ColorMix()
        filter.setValue(resultImage, forKey: "inputImage")
        filter.setValue(NSNumber(floatLiteral: time), forKey: "inputTime")

        resultImage = filter.outputImage()!

        return resultImage
    }

}

【问题讨论】:

  • 您是否尝试添加自动释放池?
  • @matt 我没有,我认为它在仅 Swift 的项目中作用不大?
  • 好吧,你想错了。 :) 我不保证它会解决这个特殊问题,但至少让我们尝试一下。将applyAnimatedFilter 的整个内部包裹在autoreleasepool 块中,让我们看看是否有什么不同。
  • @matt 谢谢,我现在就试试 :) 另外我注意到,当通过 Instruments 在 Allocations 上运行应用程序时,它不会被杀死并且内存图是稳定的。 Xcode 和 Instruments 在内存管理上会不会有什么不同?
  • @matt 根据 Xcode 内存图添加 autireleasepool 没有影响

标签: ios swift metal core-image cadisplaylink


【解决方案1】:

这是 Xcode 的诊断功能(金属验证和/或 GPU 帧捕获)中的一个错误。如果你关闭这些,内存使用应该与在 Xcode 之外运行时相似。

【讨论】:

  • 这对我也有用!我有一个视频游戏引擎,我已经预先分配了所有游戏的内存,令我惊讶的是内存可能会像这样不断增加。谢谢!
  • SDL 导致了内存泄漏,并且看起来它并不是真正的 SDL,而是我在 Scheme 编辑器中打开了 Metal API 验证这一事实。非常感谢!
【解决方案2】:

以下是一些观察结果,但我不确定其中一个是否真的会导致您所看到的内存使用:

  • applyFilter 中,您正在创建一个新的ColorMix 过滤器每一帧。此外,在实例方法use(image:, time:) 中,您将在每次调用时创建另一个。这是很多开销,特别是因为过滤器每次都在init 上加载它的内核。建议在设置期间仅创建一个 ColorMix 过滤器,并在每一帧上更新其 inputImageinputTime
  • outputImage 不是 func,而是您从 CIFilter 超类覆盖的 var

    override var outputImage: CIImage? { /* your code here */ }

  • 您的colorMix 内核是否执行任何类型的卷积?如果不是,则可能是CIColorKernel

  • 如果您需要内核中输入的大小,则无需将其作为额外参数传递。您可以在输入 sampler 上调用 .size()

【讨论】:

  • 嘿,弗兰克,感谢您提供的出色优化提示!我将在我的代码中调整它们。每帧创建一个新的过滤器真的很不合理。 Ken Thomases 关于在方案中关闭 Metal 验证和 GPU 捕获的建议对我有用,现在内存图在 Xcode 中是稳定的。
  • 不错!很高兴我能帮助你。 ?
猜你喜欢
  • 2012-11-01
  • 1970-01-01
  • 2019-10-08
  • 2014-02-25
  • 2014-04-12
  • 2017-12-15
  • 1970-01-01
  • 2012-12-23
  • 1970-01-01
相关资源
最近更新 更多