Как использовать загрузчик текстур MetalKit с кучей металла?

У меня есть набор текстур металла, которые хранятся в каталоге активов Xcode как наборы текстур. Я загружаю их с помощью MTKTextureLoader.newTexture(name:scaleFactor:bundle:options).

Затем я использую MTLArgumentEncoder для кодирования всех текстур в буфер аргументов Metal 2.

Это прекрасно работает. Однако сессия Знакомство с Metal 2 WWDC 2017 рекомендует комбинировать буферы аргументов с кучей ресурсов для еще большей производительности, и я очень хочу попробовать это. Согласно документации по буферу аргументов вместо вызова MTLRenderCommandEncoder.useResource on для каждой текстуры в буфере аргументов вы просто вызываете useHeap в куче, из которой были выделены текстуры.

Однако я не нашел простого способа использовать MTKTextureLoader вместе с MTLHeap. Кажется, у него нет возможности загрузки для выделения текстуры из кучи.

Я предполагаю, что подход будет следующим:

  • загрузить текстуры с MTKTextureLoader
  • реконструировать набор из MTLTextureDescriptor объектов для каждой текстуры
  • используйте дескрипторы текстуры для создания MTLHeap подходящего размера
  • назначить новый набор текстур из MTLHeap
  • используйте какой-нибудь метод для копирования текстур, возможно replaceBytes или даже MTLBlitCommandEncoder
  • освободить исходные текстуры, загруженные с MTKTextureLoader

Это кажется довольно длинным подходом, и я не видел никаких примеров этого, поэтому я подумал, что сначала спрошу здесь, если мне не хватает чего-то очевидного.

Должен ли я отказаться от MTKTextureLoader и поискать искусство, существовавшее до MetalKit, при загрузке текстур из каталогов ресурсов?

Я использую Swift, но с радостью принимаю ответы Objective-C.


person OliverD    schedule 05.03.2018    source источник


Ответы (1)


Что ж, метод, который я описал выше, похоже, работает. Как и предполагалось, это довольно многословно. Мне было бы очень интересно узнать, есть ли у кого-нибудь что-нибудь более элегантное.

enum MetalError: Error {
    case anErrorOccured
}

extension MTLTexture {
    var descriptor: MTLTextureDescriptor {
        let descriptor = MTLTextureDescriptor()
        descriptor.width = width
        descriptor.height = height
        descriptor.depth = depth
        descriptor.textureType = textureType
        descriptor.cpuCacheMode = cpuCacheMode
        descriptor.storageMode = storageMode
        descriptor.pixelFormat = pixelFormat
        descriptor.arrayLength = arrayLength
        descriptor.mipmapLevelCount = mipmapLevelCount
        descriptor.sampleCount = sampleCount
        descriptor.usage = usage
        return descriptor
    }

    var size: MTLSize {
        return MTLSize(width: width, height: height, depth: depth)
    }
}

extension MTKTextureLoader {
    func newHeap(withTexturesNamed names: [String], queue: MTLCommandQueue, scaleFactor: CGFloat, bundle: Bundle?, options: [MTKTextureLoader.Option : Any]?, onCompletion: (([MTLTexture]) -> Void)?) throws -> MTLHeap {
        let device = queue.device
        let sourceTextures = try names.map { name in
            return try newTexture(name: name, scaleFactor: scaleFactor, bundle: bundle, options: options)
        }
        let storageMode: MTLStorageMode = .private
        let descriptors: [MTLTextureDescriptor] = sourceTextures.map { source in
            let desc = source.descriptor
            desc.storageMode = storageMode
            return desc
        }
        let sizeAligns = descriptors.map { device.heapTextureSizeAndAlign(descriptor: $0) }
        let heapDescriptor = MTLHeapDescriptor()
        heapDescriptor.size = sizeAligns.reduce(0) { $0 + $1.size }
        heapDescriptor.cpuCacheMode = descriptors[0].cpuCacheMode
        heapDescriptor.storageMode = storageMode
        guard let heap = device.makeHeap(descriptor: heapDescriptor),
            let buffer = queue.makeCommandBuffer(),
            let blit = buffer.makeBlitCommandEncoder()
            else {
            throw MetalError.anErrorOccured
        }
        let destTextures = descriptors.map { descriptor in
            return heap.makeTexture(descriptor: descriptor)
        }
        let origin = MTLOrigin()
        zip(sourceTextures, destTextures).forEach {(source, dest) in
            blit.copy(from: source, sourceSlice: 0, sourceLevel: 0, sourceOrigin: origin, sourceSize: source.size, to: dest, destinationSlice: 0, destinationLevel: 0, destinationOrigin: origin)
            blit.generateMipmaps(for: dest)
        }
        blit.endEncoding()
        buffer.addCompletedHandler { _ in
            onCompletion?(destTextures)
        }
        buffer.commit()
        return heap
    }
}
person OliverD    schedule 06.03.2018