在 Metal 中使用 function_constants 创建 UberShader 的正确方法是什么?
What is the right approach to creating an UberShader using function_constants in Metal?
我刚刚在 WWDC 2016 的 "What's new in Metal" 视频中了解到 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 会看到太多分支吗?
这是函数常量的合法用例,在运行时不会产生分支成本。这是因为编译器将删除它确定永远无法执行的代码(例如,因为它等同于 if(false) { ... }
)。
是的,您走对了。 ( 但要稍微扩展一下他的回答...)
您的示例与 Apple 在 WWDC16 session 介绍函数常量中展示的示例基本相同:您的 "branches" 都是直接从函数常量值派生的,这意味着着色器编译器可以(当你构建你的应用程序时)为通过你的代码的每个可能路径生成 IR 变体,这些路径取决于函数常量值。
在这里,您将 int
传递给着色器,但这并不意味着它必须编译 232 个着色器变体 — 编译器可以做到一些静态分析,发现有四种可能的代码路径基于该值(0、1、2 和其他任何东西,其中最后一个只是完全省略了 if
语句和 returns frgOut
).
在 运行 时,Metal 框架根据您为常量传递的值确定将四个着色器中的哪一个发送到 GPU,因此在着色器/GPU 上没有分支。例如,如果您传递 1
的值,则您 运行 正在使用一个基本上如下所示的着色器:
fragment FragmentOutStruct UberFragmentShader() {
FragmentOutputStruct frgOut;
// Update frgOut per `if (forwardShading)` chunk of original shader source
return frgOut;
}
如您所见,该着色器中没有分支。
我刚刚在 WWDC 2016 的 "What's new in Metal" 视频中了解到 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 会看到太多分支吗?
这是函数常量的合法用例,在运行时不会产生分支成本。这是因为编译器将删除它确定永远无法执行的代码(例如,因为它等同于 if(false) { ... }
)。
是的,您走对了。 (
您的示例与 Apple 在 WWDC16 session 介绍函数常量中展示的示例基本相同:您的 "branches" 都是直接从函数常量值派生的,这意味着着色器编译器可以(当你构建你的应用程序时)为通过你的代码的每个可能路径生成 IR 变体,这些路径取决于函数常量值。
在这里,您将 int
传递给着色器,但这并不意味着它必须编译 232 个着色器变体 — 编译器可以做到一些静态分析,发现有四种可能的代码路径基于该值(0、1、2 和其他任何东西,其中最后一个只是完全省略了 if
语句和 returns frgOut
).
在 运行 时,Metal 框架根据您为常量传递的值确定将四个着色器中的哪一个发送到 GPU,因此在着色器/GPU 上没有分支。例如,如果您传递 1
的值,则您 运行 正在使用一个基本上如下所示的着色器:
fragment FragmentOutStruct UberFragmentShader() {
FragmentOutputStruct frgOut;
// Update frgOut per `if (forwardShading)` chunk of original shader source
return frgOut;
}
如您所见,该着色器中没有分支。