金属:将 MTLRenderCommandEncoder 纹理加载限制为仅部分纹理



我确实有一个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指定的,因为它的 colorAttachmentloadActionstoreAction的相应设置。

我的问题是:
是否可以将加载和存储过程限制在texture的区域?(为了避免在只需要一小部分时浪费计算时间来加载和存储大部分纹理(

为了避免将整个纹理加载并存储到渲染管线中,一种方法可能是以下方法,假设剪刀矩形在绘制调用之间是恒定的:

  1. Blit (MTLBlitCommandEncoder( 从大纹理到较小(例如剪刀矩形的大小(中间纹理的感兴趣区域。

  2. 加载和存储,并仅在较小的中间纹理上绘制/操作。

  3. 完成编码后,将结果拼接回较大纹理的原始源区域。

这样,您可以仅加载和存储管道中的感兴趣区域,而仅增加维护较小中间纹理的常量内存成本(假设感兴趣区域在绘制调用之间是恒定的(。

闪电是一种快速操作,因此上述方法应该可以优化您当前的管道。

最新更新