3

我有一组金属纹理,它们作为纹理集存储在 Xcode 资产目录中。我正在使用MTKTextureLoader.newTexture(name:scaleFactor:bundle:options).

然后我使用 aMTLArgumentEncoder将所有纹理编码到 Metal 2 参数缓冲区中。

这很好用。但是,介绍 Metal 2 WWDC 2017 会议建议将参数缓冲区与资源堆结合起来以获得更好的性能,我非常想尝试一下。根据参数缓冲区文档,您不必调用MTLRenderCommandEncoder.useResource参数缓冲区中的每个纹理,只需调用useHeap分配纹理的堆。

但是,我还没有找到MTKTextureLoaderMTLHeap. 它似乎没有从堆中分配纹理的加载选项。

我猜这种方法是:

  • 加载纹理MTKTextureLoader
  • MTLTextureDescriptor对每个纹理的一组对象进行逆向工程
  • 使用纹理描述符创建适当大小的MTLHeap
  • MTLHeap
  • 使用某种方法来复制纹理,replaceBytes也许甚至是MTLBlitCommandEncoder
  • 释放加载的原始纹理MTKTextureLoader

这似乎是一个相当冗长的方法,我还没有看到任何这样的例子,所以我想我会先在这里问一下,以防我遗漏了一些明显的东西。

我应该放弃MTKTextureLoader并搜索一些关于从资产目录加载纹理的 pre-MetalKit 艺术吗?

我正在使用 Swift,但很高兴接受 Objective-C 的答案。

4

1 回答 1

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
    }
}
于 2018-03-06T00:02:27.940 回答