在金属着色器中的缓冲区之间进行选择
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 = 0
、AAPLVertexInputIndexVertices2 = 1
和 AAPLVertexInputIndexUseFirstBuffer = 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" 字节来解决这个问题。缓冲区的长度是否相同并不重要,因为毕竟永远不会实际访问虚拟缓冲区。
我正在努力将我的 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 = 0
、AAPLVertexInputIndexVertices2 = 1
和 AAPLVertexInputIndexUseFirstBuffer = 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" 字节来解决这个问题。缓冲区的长度是否相同并不重要,因为毕竟永远不会实际访问虚拟缓冲区。