如何将 MetalKit 纹理加载器与金属堆一起使用?
How do I use MetalKit texture loader with Metal heaps?
我有一组金属纹理作为纹理集存储在 Xcode 资产目录中。我正在使用 MTKTextureLoader.newTexture(name:scaleFactor:bundle:options)
.
加载这些
然后我使用 MTLArgumentEncoder
将所有纹理编码到 Metal 2 参数缓冲区中。
效果很好。但是,Introducing Metal 2 WWDC 2017 session recommends combining argument buffers with resource heaps for even better performance, and I'm quite keen to try this. According to the Argument Buffer documentation,不必在参数缓冲区中的每个纹理上调用 MTLRenderCommandEncoder.useResource
,您只需调用useHeap
在分配纹理的堆上。
但是,我还没有找到将 MTKTextureLoader
与 MTLHeap
一起使用的直接方法。它似乎没有从堆中分配纹理的加载选项。
我猜该方法是:
- 使用
MTKTextureLoader
加载纹理
- reverse-engineer每个纹理
MTLTextureDescriptor
objects一组
- 使用纹理描述符创建大小合适的
MTLHeap
- 从
MTLHeap
中分配一组新的纹理
- 使用某种方法复制纹理,也许
replaceBytes
或者甚至 MTLBlitCommandEncoder
- 取消分配使用
MTKTextureLoader
加载的原始纹理
这似乎是一种相当 long-winded 的方法,我还没有看到任何这样的例子,所以我想我会先在这里问一下,以防我遗漏了一些明显的东西。
我应该放弃 MTKTextureLoader
,并搜索一些 pre-MetalKit 从资产目录加载纹理的艺术吗?
我正在使用 Swift,但很高兴接受 Objective-C 个答案。
嗯,我上面概述的方法似乎有效。正如预测的那样,它很漂亮long-winded。我很想知道是否有人有更优雅的东西。
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: [=10=]) }
let heapDescriptor = MTLHeapDescriptor()
heapDescriptor.size = sizeAligns.reduce(0) { [=10=] + .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
}
}
我有一组金属纹理作为纹理集存储在 Xcode 资产目录中。我正在使用 MTKTextureLoader.newTexture(name:scaleFactor:bundle:options)
.
然后我使用 MTLArgumentEncoder
将所有纹理编码到 Metal 2 参数缓冲区中。
效果很好。但是,Introducing Metal 2 WWDC 2017 session recommends combining argument buffers with resource heaps for even better performance, and I'm quite keen to try this. According to the Argument Buffer documentation,不必在参数缓冲区中的每个纹理上调用 MTLRenderCommandEncoder.useResource
,您只需调用useHeap
在分配纹理的堆上。
但是,我还没有找到将 MTKTextureLoader
与 MTLHeap
一起使用的直接方法。它似乎没有从堆中分配纹理的加载选项。
我猜该方法是:
- 使用
MTKTextureLoader
加载纹理
- reverse-engineer每个纹理
MTLTextureDescriptor
objects一组 - 使用纹理描述符创建大小合适的
MTLHeap
- 从
MTLHeap
中分配一组新的纹理
- 使用某种方法复制纹理,也许
replaceBytes
或者甚至MTLBlitCommandEncoder
- 取消分配使用
MTKTextureLoader
加载的原始纹理
这似乎是一种相当 long-winded 的方法,我还没有看到任何这样的例子,所以我想我会先在这里问一下,以防我遗漏了一些明显的东西。
我应该放弃 MTKTextureLoader
,并搜索一些 pre-MetalKit 从资产目录加载纹理的艺术吗?
我正在使用 Swift,但很高兴接受 Objective-C 个答案。
嗯,我上面概述的方法似乎有效。正如预测的那样,它很漂亮long-winded。我很想知道是否有人有更优雅的东西。
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: [=10=]) }
let heapDescriptor = MTLHeapDescriptor()
heapDescriptor.size = sizeAligns.reduce(0) { [=10=] + .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
}
}