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

Metal 在金属着色器中的缓冲区之间选择,metal,Metal,我正在努力将OpenGL应用程序移植到Metal。在我的旧应用程序中,我曾经绑定两个缓冲区,一个带有顶点和各自的颜色,另一个带有顶点和各自的纹理,并根据一些应用程序逻辑在这两个缓冲区之间切换。现在在《金属》中,我从我尝试运行这个顶点着色器的示例开始 vertex RasterizerData vertexShader(uint vertexID [[vertex_id]], constant AAPLVertex1 *vertices1 [[buffer(AAPLVer

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

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
永远无法访问,但我仍然得到错误:
断言失败的顶点函数(vertexShader):vertices2[0]的索引1处缺少缓冲区绑定。


如果我将金属代码中的
if(useFirstBuffer)
替换为
if(true)
,一切都会正常工作。怎么了?

在atIndex参数中,使用值AAPLVertexInputIndexUseFirstBuffer和AAPLVertexInputIndexVertices1调用代码,但在金属代码中,值AAPLVertexInputIndexVertices1和AAPLVertexInputIndexVertices2显示在缓冲区()中规范。看起来您需要在调用代码中使用AAPLVertexInputIndexVertices1而不是AAPLVertexInputIndexUseFirstBuffer。

当您硬编码条件时,编译器足够聪明,可以消除引用缺少的缓冲区(via)的分支,但当必须在运行时计算条件时,编译器不知道该分支从未执行过


由于所有声明的缓冲区参数都必须绑定,因此未绑定未引用的缓冲区会导致验证层跳闸。您可以在
Vertices2
插槽(使用
-setVertexBytes:length:atIndex:
)处绑定几个“虚拟”字节,但不遵循该路径来绕过此问题。缓冲区是否具有相同的长度并不重要,因为毕竟,虚拟缓冲区永远不会被实际访问。

我的怀疑是,当您硬编码条件时,编译器足够聪明,可以消除引用缺少缓冲区的分支,但当必须在运行时计算条件时,它就不是了。由于所有声明的缓冲区参数都必须绑定,因此它会使验证层跳闸。您可以在
Vertices2
插槽(使用
setVertexBytes
)绑定几个“虚拟”字节,但不遵循该路径来解决此问题。这也可能是使用函数常量的地方,不过如果您刚刚开始学习基本知识,这可能有点高级。@warrenm是的,为vertices2使用虚拟缓冲区是可行的,但由于这是一种解决方法,我想知道它背后的真正原因。。。无论如何,谢谢你的投入!真正的原因是您有义务为每个缓冲区参数绑定一个缓冲区。当访问被运行时变量选通时,验证层无法知道您不会引用缓冲区。@warrenm我认为不绑定所有缓冲区是有效的,因为当if硬编码为true时,它会工作,并且显然不会访问
vertices2
。因此,如果我真的必须使用虚拟缓冲区,那么它的大小必须与
vertices1
相同,因为我无法确定它的哪个部分将被访问,这是非常低效的。看来肯定还有另一个问题……我更新了问题,使之更清楚。每个变量都有一个不同的值,因此以与金属代码中声明的顺序不同的顺序绑定缓冲区是有效的。您是否可以尝试我建议的修复方法,而不只是重新启动“you is correct”?您只是没有绑定正确的缓冲区,缓冲区中的值并不重要。每次绑定每个缓冲区。如果您尝试执行其他操作,例如仅向着色器传递1个顶点缓冲区,则只需执行该操作,传递单个缓冲区并在执行时选择缓冲区。您实现的方法似乎试图在运行时进行选择,但在着色器中使用了条件逻辑。您的方法不符合金属缓冲区绑定逻辑的期望。对不起,我不明白您的意思。您建议的修复包括不绑定
AAPLVertexInputIndexUseFirstBuffer
,这是不正确的。后来你说我每次都要绑定每个缓冲区,这与你第一次说的是矛盾的
AAPLVertexInputIndexUseFirstBuffer
也被声明为一个缓冲区,您似乎已经对其进行了监督。是的,我试图只传递两个顶点缓冲区中的一个,但问题是它们有不同的类型(我更新了问题以反映这一点),这就是为什么我认为必须声明两个顶点缓冲区,然后只绑定其中一个。