我正在用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
,则在下一次调用makeRenderCommandEncoder
时Metal会崩溃。有可能以任何方式组合这些编码器吗?
编辑:
我已经进行了GPU捕获,因此更容易查看整个渲染管道。这是屏幕截图,
它很小,但是我在上面放了一些标签。白色标签与循环中的内容相对应。场景中有3盏灯,它们正在照亮3个球体。
最佳答案
但是,如果我很了解Metal文档,则需要“静态地”定义所有遍历,也就是说,您需要为每个着色器使用不同的“渲染命令编码器”,并使用的渲染表面配置。这样对吗?
不,那不是完全正确的。您会注意到,渲染命令编码器的某些属性是在创建编码器时通过MTLRenderPassDescriptor
指定的,在创建编码器后,还可以通过编码器上的访问器设置其他属性。前者在编码器的使用寿命内是不变的。后者可以更改。
因此,如果更改渲染目标(附件),则确实需要新的命令编码器。但是您要做而不是不需要新的命令编码器来更改着色器。着色器由渲染管线状态指定,可以使用setRenderPipelineState(_:)
在现有命令编码器上进行更改。
绝对正确,如果有可能,应在应用程序的生命周期中创建一次渲染管道状态对象。但是之后,您可以根据需要重复使用它们。
最后,我不必担心创建多个渲染命令编码器。它们的设计成本相对较低。因此,尽管花费一点精力来整合可以使用给定编码器完成的所有工作都是可以的,但是当事态发展时,不要倒着弯腰去尝试使事情“更简单”。