如何在 Metal 中组合使用不同着色器的渲染命令编码器
How to combine Render Command Encoders that use a different shader in Metal
我正在用 Metal 编写一个图形引擎,我正在使用模板缓冲区来遮盖场景中球谐光所覆盖的体积。我为此使用了两个着色器,每个灯需要 3 个绘制调用:一个用于背面,另一个用于正面,最后一个绘制调用使用不同的着色器来实际渲染灯光。
但是,如果我很好地理解 Metal 文档,您需要定义所有通道 "statically",也就是说,对于您使用的每个着色器和渲染表面配置,您需要一个不同的渲染命令编码器。这是正确的吗?
这意味着我最终为我的灯创建了这个循环,这感觉很糟糕,因为我正在创建很多编码器,
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个球体。
But, if I understood well Metal documentation, you need to define all your passes "statically", that is, you need a different Render Command Encoder for every shader and render surfaces configurations that you use. Is this correct?
不,这不完全正确。您会注意到渲染命令编码器的一些属性是在您创建编码器时通过 MTLRenderPassDescriptor
指定的,还有其他属性是在创建编码器后通过访问器设置的.前者在编码器的生命周期内是不可变的。后者可以改。
因此,如果您更改渲染目标(附件),您确实需要一个新的命令编码器。但是您不需要需要一个新的命令编码器来更改着色器。着色器由渲染管道状态指定,可以使用 setRenderPipelineState(_:)
.
在现有命令编码器上更改
如果可能的话,您应该在应用程序的生命周期中创建 渲染管道状态对象一次,这是绝对正确的。但之后您可以根据需要经常重复使用它们。
最后,我不会太担心创建多个渲染命令编码器。它们的设计成本相对较低。因此,虽然花费一些努力将所有可以用给定编码器完成的工作整合在一起是很好的,但不要竭尽全力去尝试做一些 "simpler" 当它与事情的工作方式背道而驰时.
我正在用 Metal 编写一个图形引擎,我正在使用模板缓冲区来遮盖场景中球谐光所覆盖的体积。我为此使用了两个着色器,每个灯需要 3 个绘制调用:一个用于背面,另一个用于正面,最后一个绘制调用使用不同的着色器来实际渲染灯光。
但是,如果我很好地理解 Metal 文档,您需要定义所有通道 "statically",也就是说,对于您使用的每个着色器和渲染表面配置,您需要一个不同的渲染命令编码器。这是正确的吗?
这意味着我最终为我的灯创建了这个循环,这感觉很糟糕,因为我正在创建很多编码器,
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个球体。
But, if I understood well Metal documentation, you need to define all your passes "statically", that is, you need a different Render Command Encoder for every shader and render surfaces configurations that you use. Is this correct?
不,这不完全正确。您会注意到渲染命令编码器的一些属性是在您创建编码器时通过 MTLRenderPassDescriptor
指定的,还有其他属性是在创建编码器后通过访问器设置的.前者在编码器的生命周期内是不可变的。后者可以改。
因此,如果您更改渲染目标(附件),您确实需要一个新的命令编码器。但是您不需要需要一个新的命令编码器来更改着色器。着色器由渲染管道状态指定,可以使用 setRenderPipelineState(_:)
.
如果可能的话,您应该在应用程序的生命周期中创建 渲染管道状态对象一次,这是绝对正确的。但之后您可以根据需要经常重复使用它们。
最后,我不会太担心创建多个渲染命令编码器。它们的设计成本相对较低。因此,虽然花费一些努力将所有可以用给定编码器完成的工作整合在一起是很好的,但不要竭尽全力去尝试做一些 "simpler" 当它与事情的工作方式背道而驰时.