如何在 Metal 中组合使用不同着色器的渲染命令编码器
Posted
技术标签:
【中文标题】如何在 Metal 中组合使用不同着色器的渲染命令编码器【英文标题】:How to combine Render Command Encoders that use a different shader in Metal 【发布时间】:2019-03-31 15:31:22 【问题描述】:我正在用 Metal 编写一个图形引擎,并使用模板缓冲区来掩盖场景中球谐光覆盖的体积。为此我使用了两个着色器,每个灯光需要 3 个绘制调用:一个用于背面,另一个用于正面,最后一个使用不同着色器的绘制调用来实际渲染灯光。
但是,如果我很好理解 Metal 文档,您需要“静态地”定义所有通道,也就是说,您需要为您使用的每个着色器和渲染表面配置使用不同的渲染命令编码器。这是正确的吗?
这意味着我最终为我的灯光创建了这个循环,这感觉很糟糕,因为我创建了很多编码器,
for l in shLights
let descStencil = createLightAccumulationRenderPass()
guard let encoderStencil = commandBuffer.makeRenderCommandEncoder(descriptor: descStencil) else
continue
drawSHLightStencil(l, encoder: encoderStencil)
encoderStencil.endEncoding()
let descColor = createLightAccumulationRenderPass()
guard let encoderColor = commandBuffer.makeRenderCommandEncoder(descriptor: descColor) else
continue
drawSHLight(l, encoder: encoderColor)
encoderColor.endEncoding()
完整代码在这里:https://github.com/endavid/VidEngine/blob/master/VidFramework/VidFramework/sdk/gfx/plugins/DeferredLightingPlugin.swift(drawSHLights
函数)
如果您需要更多关于如何使用它的上下文,请查看这篇博文:http://endavid.com/index.php?entry=85
我也尝试过重用编码器,但如果你不调用endEncoding
,Metal 会在下一次调用makeRenderCommandEncoder
时崩溃。
是否可以以任何方式组合这些编码器?
编辑: 我已经进行了 GPU 捕获,因此更容易看到整个渲染管道。这是截图,
它很小,但我在上面贴了一些标签。白色标签对应于循环中的内容。场景中有 3 盏灯,它们照亮了 3 个球体。
【问题讨论】:
【参考方案1】:但是,如果我很好理解 Metal 文档,您需要“静态地”定义所有通道,也就是说,您需要为您使用的每个着色器和渲染表面配置使用不同的渲染命令编码器。这是正确的吗?
不,这并不完全正确。您会注意到,渲染命令编码器的某些属性是在您创建编码器时通过MTLRenderPassDescriptor
指定的,还有其他属性是在创建编码器后通过访问器设置的。前者在编码器的生命周期内是不可变的。后者可以更改。
因此,如果您更改渲染目标(附件),您确实需要一个新的命令编码器。但是您确实不需要需要新的命令编码器来更改着色器。着色器由渲染管线状态指定,并且可以使用setRenderPipelineState(_:)
在现有命令编码器上进行更改。
如果可能的话,您应该在应用程序的生命周期中创建一次渲染管道状态对象,这是绝对正确的。但之后您可以根据需要随时重复使用它们。
最后,我不会太担心创建多个渲染命令编码器。它们的设计成本相对较低。因此,虽然花费一些精力将可以使用给定编码器完成的所有工作整合在一起很好,但当它与事情的工作方式背道而驰时,不要向后弯腰试图让事情变得“更简单”。
【讨论】:
谢谢!这非常有用。我正在更改渲染目标。我想我可以尝试有相同的目标,但如果编码器像你说的那样便宜,那么也许我太偏执了。在 GPU 帧捕获中,最后我收到了几个警告,但 SH 灯循环中没有性能警告。我将等待有一个真正的应用程序来搜索瓶颈并在必要时进行优化。以上是关于如何在 Metal 中组合使用不同着色器的渲染命令编码器的主要内容,如果未能解决你的问题,请参考以下文章
Metal / SceneKit Fragment Shaders - 如何避免在其他几何体上渲染?