使用Metal中的function_constants创建UberShader的正确方法是什么?

时间:2016-10-13 19:14:20

标签: ios shader metal

我刚从WWDC 2016的“金属中有什么新东西”视频中了解到function_constants,并且很多时候提到过UberShaders。我想创建一个片段超级着色器,可以用于不同类型的传递,如simplePassThrough,defferred等。以下是我想要使用它的方法。

constant int passType [[function_constant(0)]];
constant bool simplePassThrough = (passType == 0);
constant bool forwardShading = (passType == 1);
constant bool deferredShading = (passType == 2);

fragment FragmentOutStruct UberFragmentShader()
{
FragmentOutputStruct frgOut;
if (simplePassThrough) {
    // Update frgOut
} else if (forwardShading) {
    // Update frgOut
} else if (deferredShading) {
    // Update frgOut
}
return frgOut;
}

这是正确的方法吗?如果我使用这种方法,我最终编译的MTLFunction会看到太多分支吗?

2 个答案:

答案 0 :(得分:6)

这是函数常量的合法用例,在运行时没有分支成本。这是因为编译器将消除它确定永远不能执行的代码(例如,因为它等同于if(false) { ... })。

答案 1 :(得分:6)

是的,你走在正确的轨道上。 (As @warrenm noted already.但是要稍微扩展他的答案......)

您的示例与Apple在WWDC16 session中引入函数常量的示例基本相同:您的“分支”都直接从函数常量值派生,这意味着着色器编译器可以(当您构建app)通过代码生成依赖于函数常量值的每个可能路径的IR变体。

在这里,您将int传递给着色器,但这并不意味着它必须编译2个 32 着色器变体 - 编译器可以进行一些静态分析并查看基于该值有四种可能的代码路径(0,1,2和任何其他,其中最后一个完全忽略if语句并返回frgOut)。

在运行时,Metal框架根据您为常量传递的值确定要发送到GPU的四个着色器中的哪一个,因此在着色器/ GPU上没有分支。例如,如果传递值1,则表示您运行的着色器基本上如下所示:

fragment FragmentOutStruct UberFragmentShader() {
    FragmentOutputStruct frgOut;
    // Update frgOut per `if (forwardShading)` chunk of original shader source
    return frgOut;
}

正如您所看到的,该着色器中没有分支。