使用 Metal 的旧设备上的内存使用量不断上升
Memory usage keeps rising on older devices using Metal
我使用 Metal
和 CADisplayLink
实时过滤 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 上稳定,并且在 iPhone 6 或 iPhone 6s 等设备上从未超过 100mb内存使用量不断增长,直到系统最终终止应用程序。
我已使用 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:附上按大小排序的Allocations List截图,应用运行 16分钟,不停地应用过滤器:
更新 3: 关于 applyAnimatedFilter()
中发生的事情的更多信息:
我将过滤后的图像渲染为 metalView
,即 MTKView
。我从 filter.applyFilter(image: ciImage)
收到过滤后的图像,其中 Filter
class 接下来发生:
func applyFilter(image: ciImage) -> CIImage {
...
var colorMix = ColorMix()
return colorMix.use(image: ciImage, time: filterTime)
}
其中 filterTime
只是一个 Double 变量。最后,这是整个 ColorMix class:
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
}
}
以下是一些观察结果,但我不确定其中之一是否真的导致了您所看到的内存使用情况:
- 在
applyFilter
中,您正在创建一个新的 ColorMix
过滤器 每帧 。此外,在实例方法 use(image:, time:)
中,您在每次调用时都会创建 另一个 。这是一个很大的开销,特别是因为过滤器每次都在 init
上加载它的内核。建议在设置期间仅创建一个 ColorMix
过滤器,并在每一帧更新其 inputImage
和 inputTime
。
outputImage
不是 func
,而是您从 CIFilter
super class 覆盖的 var
:
override var outputImage: CIImage? { /* your code here */ }
您的 colorMix
内核是否执行任何类型的卷积?如果不是,则可能是 CIColorKernel
。
- 如果您需要内核中输入的大小,则无需将其作为额外参数传递。您可以在输入
sampler
. 上调用 .size()
这是 Xcode 诊断功能(金属验证 and/or GPU 帧捕获)中的错误。如果你关闭它们,内存使用应该类似于 运行 在 Xcode.
之外
我使用 Metal
和 CADisplayLink
实时过滤 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 上稳定,并且在 iPhone 6 或 iPhone 6s 等设备上从未超过 100mb内存使用量不断增长,直到系统最终终止应用程序。
我已使用 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:附上按大小排序的Allocations List截图,应用运行 16分钟,不停地应用过滤器:
更新 3: 关于 applyAnimatedFilter()
中发生的事情的更多信息:
我将过滤后的图像渲染为 metalView
,即 MTKView
。我从 filter.applyFilter(image: ciImage)
收到过滤后的图像,其中 Filter
class 接下来发生:
func applyFilter(image: ciImage) -> CIImage {
...
var colorMix = ColorMix()
return colorMix.use(image: ciImage, time: filterTime)
}
其中 filterTime
只是一个 Double 变量。最后,这是整个 ColorMix class:
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
}
}
以下是一些观察结果,但我不确定其中之一是否真的导致了您所看到的内存使用情况:
- 在
applyFilter
中,您正在创建一个新的ColorMix
过滤器 每帧 。此外,在实例方法use(image:, time:)
中,您在每次调用时都会创建 另一个 。这是一个很大的开销,特别是因为过滤器每次都在init
上加载它的内核。建议在设置期间仅创建一个ColorMix
过滤器,并在每一帧更新其inputImage
和inputTime
。 outputImage
不是func
,而是您从CIFilter
super class 覆盖的var
:override var outputImage: CIImage? { /* your code here */ }
您的
colorMix
内核是否执行任何类型的卷积?如果不是,则可能是CIColorKernel
。- 如果您需要内核中输入的大小,则无需将其作为额外参数传递。您可以在输入
sampler
. 上调用
.size()
这是 Xcode 诊断功能(金属验证 and/or GPU 帧捕获)中的错误。如果你关闭它们,内存使用应该类似于 运行 在 Xcode.
之外