是否应该在 CPU 上预乘(顶点着色器、mat4)统一矩阵?

met*_*eap 5 glsl matrix vertex-shader

考虑典型的“天真”顶点着色器:

in vec3 aPos;

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

void main () {
    gl_Position = uMatProj * uMatCam * uMatModelView * vec4(aPos, 1.0);
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

Nic*_*las 5

我想知道现代 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  |                |
------------|----------------|---------------------
Run Code Online (Sandbox Code Playgroud)

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

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

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

简而言之,如果你关心这个优化,那就自己做吧。

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

它可能; 可能不会。这完全取决于顶点变换对渲染系统的瓶颈程度。如果没有在实际的渲染环境中进行测试,就无法得知。

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