【问题标题】:Metal: limit MTLRenderCommandEncoder texture loading to only part of textureMetal:将 MTLRenderCommandEncoder 纹理加载限制为仅部分纹理
【发布时间】:2019-11-29 21:10:57
【问题描述】:

我确实有一个 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 和@987654328 的相应设置@。

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

【问题讨论】:

    标签: swift textures metal


    【解决方案1】:

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

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

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

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

    这样,您只需在管道中加载和存储感兴趣的区域,只需增加保持较小中间纹理的恒定内存成本(假设感兴趣区域在两次绘制调用之间是恒定的)。

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

    【讨论】:

    • 我想过这个,这实际上是我当前的实现。这种方法对我来说的一个问题是,由于内存分配,每次创建一个小纹理似乎效率很低。所以我目前使用一个大的纹理来复制到,它对于所有可能的 scissorRect 大小都足够大,并使用这个带有 .dontCare 的 loadAction 的纹理。关于如何在不需要分配新内存的情况下获得小纹理的任何想法? (我想到了类似原来大texture的另一种看法,但这样的东西似乎不存在)。
    • 您预先分配较小的纹理/缓冲区(仅一次),而不是为每一帧分配。但这是基于假设你有一个恒定的剪式矩形,而不是可变的矩形,正如你所说。如果您的较大纹理仅在渲染通道中使用而无需存储,那么建议使用 MTLStorageMode.memoryless(只能由 GPU 访问)。
    猜你喜欢
    • 2018-06-09
    • 2023-03-07
    • 1970-01-01
    • 2013-06-03
    • 2020-11-17
    • 2011-03-16
    • 2019-06-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多