用金属调用顶点着色器多少次?

How many times is the vertex shader called with metal?

我一直在学习一些基本的金属渲染,但我被一些基本概念所困:

我知道我们可以使用以下方法将顶点数据发送到着色器:

renderEncoder.setVertexBuffer(vertexBuffer, offset: 0, index: 0)

然后我们可以在着色器中检索它:

vertex float4 basic_vertex(const device VertexIn* vertexIn [[ buffer(0) ]], unsigned int vid [[ vertex_id ]])

据我了解,每个顶点都会调用一次顶点函数,vertex_id 会在每次调用时更新以包含顶点索引。

问题是,vertex_id从何而来?

我可以向着色器发送更多不同大小的数据:

renderEncoder.setVertexBuffer(vertexBuffer, offset: 0, index: 0)
renderEncoder.setVertexBuffer(vertexBuffer2, offset: 0, index: 1)

如果 vertexBuffer 有 3 个元素,而 vertexBuffer2 有 10 个元素...顶点函数被调用了多少次? 10?

谢谢!

这取决于您在渲染命令编码器上进行的绘图调用。采取最简单的画法:

drawPrimitives(type:vertexStart:vertexCount:)

vertexCount决定了你的顶点函数被调用的次数。传递给顶点函数的顶点 ID 在 vertexStartvertexStart + vertexCount - 1.

范围内

如果考虑另一种绘制方法:

drawPrimitives(type:vertexStart:vertexCount:instanceCount:)

这涵盖了相同范围的顶点 ID。但是,它会调用您的顶点函数 vertexCount * instanceCount 次。将有 instanceCount 个顶点 ID 为 vertexStart 的调用。对于这些调用,实例 ID 的范围从 0 到 instanceCount - 1。同样,将有 instanceCount 个顶点 ID 为 vertexStart + 1 的调用(假设 vertexCount >= 2),每个实例 ID 在 [0..instanceCount-1] 中。等等

其他绘制方法有各种其他选项,但它们大多不影响调用顶点函数的次数。例如,baseInstance 会改变实例 ID 的范围,但不会改变其大小。

各种 drawIndexedPrimitives() 方法从缓冲区获取特定的顶点 ID,而不是枚举一个范围内的所有顶点 ID。该缓冲区可能在多个位置包含给定的顶点 ID。对于那种情况,我不认为它定义了是否可以为相同的顶点 ID 和实例 ID 多次调用顶点函数。 Metal 可能会尽量避免重复工作,但实际上可能会更快地为索引缓冲区中的每个索引调用顶点函数,即使多个此类索引最终成为相同的顶点 ID。

传递给顶点处理阶段的缓冲区中的顶点和数据之间的关系完全取决于您。您根本不必传递任何缓冲区。例如,一个顶点函数可以完全通过计算从顶点 ID 和实例 ID 生成顶点信息。

当然,至少某些缓冲区包含使用顶点 ID 索引的每顶点数据数组是很常见的。其他缓冲区可能是所有顶点都相同的统一数据(也就是说,您不使用顶点 ID 索引到该缓冲区)。不过,Metal 本身并不知道这一点。