渲染命令中着色器调用的频率

Posted

技术标签:

【中文标题】渲染命令中着色器调用的频率【英文标题】:Frequency of shader invocations in rendering commands 【发布时间】:2016-05-16 13:45:50 【问题描述】:

着色器具有调用,每个调用(通常)被赋予一组唯一的输入数据,并且每个(通常)写入自己的单独输出数据。当您发出渲染命令时,每个着色器会被调用多少次?

【问题讨论】:

【参考方案1】:

每个着色器阶段都有自己的调用频率。我将使用 OpenGL 术语,但 D3D 的工作方式相同(因为它们都在建模相同的硬件关系)。

顶点着色器

这些是第二个最复杂的。他们为每个输入顶点执行一次......有点。如果您使用的是非索引渲染,那么该比率正好是 1:1。每个输入顶点都将在单独的顶点着色器实例上执行。

如果您使用索引渲染,那么它会变得复杂。它或多或少是 1:1,每个顶点都有自己的 VS 调用。然而,感谢post-T&L caching,顶点着色器的执行可能少于每个输入顶点一次。

请看,顶点着色器的执行被假定为在输入顶点数据和输出顶点数据之间创建 1:1 映射。这意味着如果您将相同的输入数据传递给顶点着色器(在相同的渲染命令中),您的 VS 应该会生成相同的输出数据。因此,如果硬件可以检测到它即将在之前使用的相同输入数据上执行顶点着色器,它可以跳过该执行并简单地使用之前执行的输出。假设它周围有这些值,例如在缓存中。

硬件通过使用顶点的索引来检测这一点(这就是为什么它不适用于非索引渲染)。如果为顶点着色器提供相同的索引,则假定着色器将获得所有相同的输入值,因此将生成相同的输出值。因此硬件将根据索引缓存输出值。如果索引在 post-T&L 缓存中,则硬件将跳过 VS 的执行并仅使用输出值。

实例化只会使 T&L 后缓存稍微复杂化。它不是仅在顶点索引上缓存,而是根据索引和 instance ID 进行缓存。因此,如果 两个 值相同,它只会使用缓存的数据。

一般来说,VS 对每个顶点执行一次,但是如果您使用索引数据优化几何图形,它可以执行更少的次数。有时很多更少,这取决于你如何做到这一点。

曲面细分控制着色器

或者用 D3D 的说法是外壳着色器。

TCS 在这方面非常简单。对于渲染命令的每个补丁中的每个顶点,它将只执行一次。这里没有进行缓存或其他优化。

细分评估着色器

或 D3D 术语中的域着色器。

TES 在曲面细分图元生成器生成新顶点后执行。因此,它执行的频率显然取决于您的细分参数。

TES 获取由 tessellator 生成的顶点并输出顶点。它以 1:1 的比例进行。

但与顶点着色器类似,每个输出图元中的每个顶点不一定是 1:1。与 VS 一样,假设 TES 在镶嵌图元中的位置和输出参数之间提供直接的 1:1 映射。因此,如果您使用相同的补丁位置多次调用 TES,则预期会输出相同的值。

因此,如果生成的图元共享顶点,则 TES 通常只会为此类共享顶点调用一次。与顶点着色器不同,您无法控制硬件将使用它的程度。您能做的最好的事情就是希望生成算法足够聪明,以尽量减少调用 TES 的频率。

几何着色器

几何着色器将为每个点、线或三角形图元调用一次,由渲染命令直接给出或由 tessellator 生成。因此,如果将 6 个顶点渲染为不连接的线,则您的 GS 将被调用 3 次。

每个 GS 调用可以生成零个或多个原语作为输出。

GS 可以在内部使用实例化(在 OpenGL 4.0 或 Direct3D 11 中)。这意味着,对于到达 GS 的每个原语,GS 将被调用 X 次,其中 X 是 GS 实例的数量。每个这样的调用都将获得相同的输入原始数据(具有用于区分这些实例的特殊输入值)。这对于更有效地将基元引导到分层帧缓冲区的不同层非常有用。

片段着色器

或 D3D 术语中的像素着色器。即使它们还不是像素,也可能不会变成像素,并且它们可以为 same 像素执行多次;)

就调用频率而言,这些是最复杂的。它们执行的频率取决于很多因素。

对于图元光栅化到的每个像素大小的区域,FS 必须至少执行一次。但他们的执行可能不止于此。

为了计算纹理函数的导数,一个 FS 调用通常会从其相邻调用之一借用值。如果没有这样的调用,如果一个邻居落在被光栅化的基元边界之外,这是有问题的。

在这种情况下,仍然会有相邻的 FS 调用。即使它没有产生实际数据,它仍然存在并且仍然有效。好的部分是这些辅助调用不会影响性能。他们基本上用尽了原本不会被使用的着色器资源。此外,此类帮助程序调用实际输出数据的任何尝试都将被系统忽略。

但它们在技术上仍然存在。

一个不太透明的问题围绕着多重采样。看,多重采样实现(特别是在 OpenGL 中)可以自行决定要发出多少 FS 调用。虽然有一些方法可以强制多重采样渲染为每个样本创建一个 FS 调用,但没有保证在这些情况之外,实现将只对每个覆盖的像素执行一次 FS。

例如,如果我没记错的话,如果您在某些 NVIDIA 硬件(8 到 16 或类似的东西)上创建具有高样本数的多样本图像,那么硬件可能会决定多次执行 FS。不一定每个样本一次,而是每 4 个左右样本一次。

那么你得到了多少 FS 调用?被光栅化的图元覆盖的每个像素大小的区域至少有一个。如果您正在进行多重采样渲染,可能会更多。

计算着色器

您指定的确切调用次数。也就是说,您分派的工作组数 * 您的 CS 指定的每个组的调用次数(您的本地组数)。不多也不少。

【讨论】:

以上是关于渲染命令中着色器调用的频率的主要内容,如果未能解决你的问题,请参考以下文章

oc渲染器提示着色器失败

初识OpenGL 链接着色器

初识OpenGL 链接着色器

openGL一些概念02

具有独特着色器的 OpenGL 多次绘制调用会产生空白屏幕

GPU渲染管线概述