何时使用多个 MTLRenderCommandEncoders 来执行我的 Metal 渲染?

bha*_*ler 3 objective-c metal

我正在学习 Metal,有一个概念性问题让我想解决这个问题:我的代码究竟应该在什么级别处理需要不同管道状态的连续绘图操作?据我了解(从这样的答案:https : //stackoverflow.com/a/43827775/2752221),我可以使用单个MTLRenderCommandEncoder并更改其管道状态,它正在使用的顶点缓冲区等,在调用drawPrimitives:, 和每次调用时当前的编码器状态drawPrimitives:将被保留。那太好了。但似乎 Metal 的设计是这样的,一个人可以创建多个MTLRenderCommandEncoder实例,并使用它们依次将一批命令扔到一个MTLCommandBuffer. 鉴于前者有效 - 使用一个MTLRenderCommandEncoder并改变它的状态——为什么要选择后者?在什么情况下做前者是正确的,在什么情况下需要做后者?后者是必要/适当的情况的示例是什么?

如果重要的话,我正在使用 Objective-C 开发 macOS 应用程序。谢谢。

Ken*_*ses 7

除了 Warren 的回答之外,看待问题的另一种方法是检查 API。许多 Metal 对象是从描述符创建的。创建对象时描述符的属性控制该对象的生命周期。这些是对象在创建后无法更改的方面。

相比之下,对象将具有各种 setter 方法来在其生命周期内修改其他属性。

对于渲染命令编码器,在其生命周期内固定的属性是由MTLRenderPassDescriptor用于创建它的人指定的属性。如果您想为这些属性中的任何一个使用不同的值进行渲染,唯一的方法是从不同的描述符创建一个新的编码器。另一方面,如果您可以使用编码器的 setter 方法完成您需要/想要做的所有事情,那么您不需要新的编码器。


war*_*enm 6

忽略有点高级的多线程编码情况,您想要在一帧期间创建多个渲染命令编码器的主要原因是因为您需要更改要渲染的纹理。

您会注意到在创建渲染命令编码器时需要提供渲染通道描述符。出于这个原因,我们经常说属于特定编码器的命令序列构成了渲染通道。该描述符的附件是指将由编码器编码的命令写入的纹理。

许多不同的技术,包括阴影贴图和后处理效果(如泛光)需要多次通过才能产生。由于您无法在一个过程中更改附件,因此创建新的编码器是在一个帧中对多个过程进行编码的唯一方法。

相关地,您通常应该每帧使用一个命令缓冲区。但是,您有时可以通过将您的传递拆分到多个命令缓冲区来减少帧时间,但这在很大程度上取决于您的工作负载的形状,并且只能与分析一起完成,因为它并不总是一种优化。