5

考虑典型的“朴素”顶点着色器:

in vec3 aPos;

uniform mat4 uMatCam;
uniform mat4 uMatModelView;
uniform mat4 uMatProj;

void main () {
    gl_Position = uMatProj * uMatCam * uMatModelView * vec4(aPos, 1.0);
}

当然,传统观点会建议“每个顶点乘以三个 mat4,其中两个即使在当前着色器程序中的多个后续 glDrawX() 调用中也是一致的,至少这两个应该在 CPU 端进行预乘,可能甚至三个。”

我想知道现代 GPU 是否已将此用例优化到 CPU 端预乘不再是性能优势的程度。当然,纯粹主义者可能会说“这取决于最终用户的 OpenGL 实现”,但对于这个用例,我们可以放心地假设它将是提供该实现的当前一代支持 OpenGL 4.2 的 nVidia 或 ATI 驱动程序。

根据您的经验,考虑到我们可能会在每个 UseProgram() 过程中“绘制”一百万个左右的顶点——每个 UseProgram() 至少会预乘前两个(透视投影和相机变换矩阵),从而将性能提升到任何显着程度?每个 Draw() 调用的所有三个呢?

当然,这都是关于基准测试......但我希望有人拥有我错过的基于当前一代硬件实现的基本见解,这可能表明“甚至不值得一试,不要浪费你的时间”“无论如何都要这样做,因为您当前没有预乘的着色器将是纯粹的精神错乱” ......想法?

4

1 回答 1

4

我想知道现代 GPU 是否已将此用例优化到 CPU 端预乘不再是性能优势的程度。

GPU 在并行操作中工作得最好。“GPU”可以像这样优化三个顺序向量/矩阵乘法的唯一方法是,如果着色器编译器检测到它们是统一的,并在您发出绘图调用时自行在某处进行乘法运算,并将结果传递给着色器。

因此,在任何一种情况下,3 个矩阵相乘在着色器中都变为 1。你可以自己做这些乘法,也可以不做。驱动程序可以实现或不实现这种优化。这是可能性的图表:

            | GPU optimizes  | GPU doesn't optimize
------------|----------------|---------------------
You send 3  |   Case A       |        Case B
matrices    |                |
---------------------------------------------------
You multiply|   Case C       |        Case D
on the CPU  |                |
------------|----------------|---------------------

在案例 A 中,您获得的性能比您的代码建议的要好。在情况 B 中,您不会获得更好的性能。

案例 C 和 D 都可以保证为您提供与案例 A 相同的性能。

问题不在于驱动程序是否会实施这种优化。问题是,“这种表现对你有什么价值?” 如果您想要那种性能,那么您应该自己做;这是可靠地实现该性能的唯一方法。如果你不关心性能......那有什么关系?

简而言之,如果您关心此优化,请自己做。

根据您的经验,考虑到我们可能会在每次 UseProgram() 过程中“绘制”一百万个左右的顶点——每个 UseProgram() 将至少预乘前两个(透视投影和相机变换矩阵)将性能提升到任何显着程度?每个 Draw() 调用的所有三个呢?

它可能; 它可能不会。这完全取决于顶点变换如何成为渲染系统的瓶颈。没有在实际渲染环境中做测试是无法知道的。

此外,结合投影和相机矩阵并不是最好的主意,因为这意味着在世界空间而不是相机空间中进行照明。它还使延迟渲染变得更加困难,因为您没有纯投影矩阵来提取值。

于 2012-10-17T18:14:27.107 回答