Использование памяти продолжает расти на старых устройствах, использующих металл - PullRequest
5 голосов
/ 02 июля 2019

Я использую 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 и никогда не превышает 100 Мб, на устройствах, таких как iPhone 6 или iPhone 6s, потребление памяти продолжает расти, пока в конечном итоге система не убьет приложение.

Я проверил на утечки памяти, используя Instruments, но об утечках не сообщалось. Запуск приложения через Allocations также не показывает никаких проблем, и приложение не будет закрыто системой. Мне также интересно, что на новых устройствах использование памяти стабильно, но на старых оно продолжает расти и расти.

Сложность фильтра не имеет значения, поскольку я пробовал даже самые простые фильтры, и проблема не устранена. Вот пример из моего металлического файла:

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 график постоянно растет:

Xcode

Allocations

Обновление 2: Прикрепленный снимок экрана со списком распределений, отсортированным по размеру, приложение работало 16 минут, применяя фильтр без остановок:

enter image description here

Обновление 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
    }

}

Ответы [ 2 ]

2 голосов
/ 03 июля 2019

Это ошибка в диагностических функциях XCode (проверка металла и / или захват кадра GPU). Если вы их отключите, использование памяти должно быть таким же, как при работе вне Xcode.

2 голосов
/ 03 июля 2019

Вот несколько наблюдений, но я не уверен, действительно ли одно из них вызывает использование памяти, которое вы видите:

  • В applyFilter вы создаете новый ColorMixфильтр каждый кадр .Кроме того, внутри метода экземпляра use(image:, time:) вы создаете еще один при каждом вызове.Это большая нагрузка, тем более что фильтр загружает свое ядро ​​каждый раз на init.Было бы целесообразно просто создать один ColorMix фильтр во время установки и просто обновить его inputImage и inputTime на каждом кадре.
  • outputImage - это не func, ноvar, который вы переопределяете из суперкласса CIFilter:

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

  • Ваше ядро ​​colorMix выполняет какую-либо свертку?В противном случае это может быть CIColorKernel.

  • Если вам нужен размер входных данных внутри вашего ядра, вам не нужно передавать его в качестве дополнительного аргумента.Вы можете просто позвонить .size() на вход sampler.
...