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

Swift 金属:将MTLRenderCommandEncoder纹理加载限制为仅部分纹理,swift,textures,metal,Swift,Textures,Metal,我有一个金属渲染管道设置,它对渲染命令进行编码,并对纹理:MTLTexture对象进行操作,以加载和存储输出。这个纹理相当大,而且每个渲染命令只对整个纹理的一小部分进行操作。基本设置大致如下所示: // texture: MTLTexture, pipelineState: MTLRenderPipelineState, commandBuffer: MTLCommandBuffer // create and setup MTLRenderPassDescriptor with loadAc

我有一个金属渲染管道设置,它对渲染命令进行编码,并对
纹理:MTLTexture
对象进行操作,以加载和存储输出。这个
纹理
相当大,而且每个渲染命令只对整个纹理的一小部分进行操作。基本设置大致如下所示:

// 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
的相应设置

我的问题是:

是否可以将加载和存储过程限制在
纹理的某个区域?(以避免在只需要一小部分纹理的情况下,为加载和存储大量纹理而浪费计算时间)

一种避免将整个纹理加载和存储到渲染管道中的方法如下:,假设您的剪切矩形在两次调用之间保持不变:

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

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

  • 编码完成后,将结果返回到较大纹理的原始源区域

  • 通过这种方式,您可以在管道中只加载和存储感兴趣的区域,只需增加保持较小中间纹理的恒定内存成本(假设在调用之间感兴趣的区域是恒定的)


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

    我考虑过这一点,这实际上是我当前的实现。对于我来说,这种方法的一个问题是,由于内存分配,每次创建一个小纹理似乎效率很低。因此,我目前使用一个大的纹理进行复制,这个纹理足够大,可以容纳所有可能的scissorRect大小,并将此纹理与loadAction.dontCare一起使用。如何在不需要分配新内存的情况下获得小纹理?(我想到了一些类似于原始大
    纹理的不同视图,但这种情况似乎并不存在)。您可以预先分配较小的纹理/缓冲区(仅一次),而不是为每一帧分配。但这是基于一个假设,你有一个恒定的剪式矩形,而不是像你说的可变矩形。如果较大的纹理仅在渲染过程中使用,而不需要存储,那么建议使用MTLStorageMode.memoryless(仅可由GPU访问)。