在金属着色器中的缓冲区之间进行选择

Choosing between buffers in a Metal shader

我正在努力将我的 OpenGL 应用程序移植到 Metal。在我的旧应用程序中,我曾经绑定两个缓冲区,一个带有顶点和相应的颜色,一个带有顶点和相应的纹理,并根据一些应用程序逻辑在两者之间切换。现在在 Metal 中,我从 Hello Triangle 示例开始,我尝试 运行 这个顶点着色器

vertex RasterizerData
vertexShader(uint vertexID [[vertex_id]],
             constant AAPLVertex1 *vertices1 [[buffer(AAPLVertexInputIndexVertices1)]],
             constant AAPLVertex2 *vertices2 [[buffer(AAPLVertexInputIndexVertices2)]],
             constant bool &useFirstBuffer [[buffer(AAPLVertexInputIndexUseFirstBuffer)]])
{
    float2 pixelSpacePosition;
    if (useFirstBuffer) {
        pixelSpacePosition = vertices1[vertexID].position.xy;
    } else {
        pixelSpacePosition = vertices2[vertexID].position.xy;
    }
    ...

和这个Objective-C代码

bool useFirstBuffer = true;
[renderEncoder setVertexBytes:&useFirstBuffer
                       length:sizeof(bool)
                      atIndex:AAPLVertexInputIndexUseFirstBuffer];
[renderEncoder setVertexBytes:triangleVertices
                       length:sizeof(triangleVertices)
                      atIndex:AAPLVertexInputIndexVertices1];

(其中 AAPLVertexInputIndexVertices1 = 0AAPLVertexInputIndexVertices2 = 1AAPLVertexInputIndexUseFirstBuffer = 3),这将导致 vertices2 永远无法访问,但我仍然收到错误消息:failed assertion 'Vertex Function(vertexShader): missing buffer binding at index 1 for vertices2[0].'

如果我在 Metal 代码中将 if (useFirstBuffer) 替换为 if (true),一切正常。怎么了?

在 atIndex 参数中,您使用值 AAPLVertexInputIndexUseFirstBuffer 和 AAPLVertexInputIndexVertices1 调用代码,但在 Metal 代码中,值 AAPLVertexInputIndexVertices1 和 AAPLVertexInputIndexVertices2 出现在 buffer() 规范中。看起来您需要在调用代码中使用 AAPLVertexInputIndexVertices1 而不是 AAPLVertexInputIndexUseFirstBuffer。

当您对条件进行硬编码时,编译器足够聪明,可以消除引用不存在缓冲区的分支(通过 dead-code elimination),但是当必须在运行时评估条件时,编译器不知道分支从未被采用。

由于必须绑定所有已声明的缓冲区参数,因此未绑定的未引用缓冲区将通过验证层。当不遵循该路径时,您可以在 Vertices2 插槽(使用 -setVertexBytes:length:atIndex:)绑定几个 "dummy" 字节来解决这个问题。缓冲区的长度是否相同并不重要,因为毕竟永远不会实际访问虚拟缓冲区。