金属:将 MTLRenderCommandEncoder 纹理加载限制为仅部分纹理
Metal: limit MTLRenderCommandEncoder texture loading to only part of texture
我有一个 Metal 渲染管道设置,它对渲染命令进行编码并在 texture: MTLTexture
对象上运行以加载和存储输出。这个 texture
相当大,并且 每个渲染命令只在整个纹理的一小部分上运行 。基本设置大致如下:
// texture: MTLTexture, pipelineState: MTLRenderPipelineState, commandBuffer: MTLCommandBuffer
// create and setup MTLRenderPassDescriptor with loadAction = .load
let renderPassDescriptor = MTLRenderPassDescriptor()
if let attachment = self.renderPassDescriptor?.colorAttachments[0] {
attachment.clearColor = MTLClearColorMake(0.0, 0.0, 0.0, 0.0)
attachment.texture = texture // texture size is rather large
attachment.loadAction = .load
attachment.storeAction = .store
}
// create MTLRenderCommandEncoder
guard let renderCommandEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: renderPassDescriptor) else { return }
// limit rendering to small fraction of texture
let scissorRect = CGRect(origin: CGPoint.zero, size: 0.1 * CGSize(width: CGFloat(texture.width), height: CGFloat(texture.height))) // create rect begin small fraction of texture rect
let metalScissorRect = MTLScissorRect(x: Int(scissorRect.origin.x), y: Int(scissorRect.origin.y), width: Int(scissorRect.width), height: Int(scissorRect.height))
renderCommandEncoder.setScissorRect(metalScissorRect)
renderCommandEncoder.setRenderPipelineState(pipelineState)
renderCommandEncoder.setScissorRect(metalScissorRect)
// encode some commands here
renderCommandEncoder.endEncoding()
实际上创建了许多 renderCommandEncoder
个对象,每次只对纹理的一小部分进行操作。 不幸的是,每次提交renderCommandEncoder
时,整个纹理都会被加载并存储在最后,这是由renderPassDescriptor
指定的,因为它的相应设置colorAttachment loadAction
和 storeAction
.
我的问题是:
是否可以将加载和存储过程限制在 texture
的区域?(以避免在只有需要一小部分)
一种方法可以避免将整个纹理加载和存储到渲染管道中,假设您的剪刀矩形在绘制调用之间保持不变:
Blit (MTLBlitCommandEncoder) 从大纹理到较小(例如剪刀矩形的大小)中间纹理的感兴趣区域。
加载和存储,draw/operate仅在较小的中间纹理上。
完成编码后,将结果 blit 回较大纹理的原始源区域。
通过这种方式,您可以仅在管道中加载和存储感兴趣的区域,并且只会增加维护较小中间纹理的恒定内存成本(假设感兴趣区域在绘制调用之间保持不变)。
Blitting 是一种快速操作,因此上述方法应该优化您当前的管道。
我有一个 Metal 渲染管道设置,它对渲染命令进行编码并在 texture: MTLTexture
对象上运行以加载和存储输出。这个 texture
相当大,并且 每个渲染命令只在整个纹理的一小部分上运行 。基本设置大致如下:
// texture: MTLTexture, pipelineState: MTLRenderPipelineState, commandBuffer: MTLCommandBuffer
// create and setup MTLRenderPassDescriptor with loadAction = .load
let renderPassDescriptor = MTLRenderPassDescriptor()
if let attachment = self.renderPassDescriptor?.colorAttachments[0] {
attachment.clearColor = MTLClearColorMake(0.0, 0.0, 0.0, 0.0)
attachment.texture = texture // texture size is rather large
attachment.loadAction = .load
attachment.storeAction = .store
}
// create MTLRenderCommandEncoder
guard let renderCommandEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: renderPassDescriptor) else { return }
// limit rendering to small fraction of texture
let scissorRect = CGRect(origin: CGPoint.zero, size: 0.1 * CGSize(width: CGFloat(texture.width), height: CGFloat(texture.height))) // create rect begin small fraction of texture rect
let metalScissorRect = MTLScissorRect(x: Int(scissorRect.origin.x), y: Int(scissorRect.origin.y), width: Int(scissorRect.width), height: Int(scissorRect.height))
renderCommandEncoder.setScissorRect(metalScissorRect)
renderCommandEncoder.setRenderPipelineState(pipelineState)
renderCommandEncoder.setScissorRect(metalScissorRect)
// encode some commands here
renderCommandEncoder.endEncoding()
实际上创建了许多 renderCommandEncoder
个对象,每次只对纹理的一小部分进行操作。 不幸的是,每次提交renderCommandEncoder
时,整个纹理都会被加载并存储在最后,这是由renderPassDescriptor
指定的,因为它的相应设置colorAttachment loadAction
和 storeAction
.
我的问题是:
是否可以将加载和存储过程限制在 texture
的区域?(以避免在只有需要一小部分)
一种方法可以避免将整个纹理加载和存储到渲染管道中,假设您的剪刀矩形在绘制调用之间保持不变:
Blit (MTLBlitCommandEncoder) 从大纹理到较小(例如剪刀矩形的大小)中间纹理的感兴趣区域。
加载和存储,draw/operate仅在较小的中间纹理上。
完成编码后,将结果 blit 回较大纹理的原始源区域。
通过这种方式,您可以仅在管道中加载和存储感兴趣的区域,并且只会增加维护较小中间纹理的恒定内存成本(假设感兴趣区域在绘制调用之间保持不变)。
Blitting 是一种快速操作,因此上述方法应该优化您当前的管道。