C++ OpenGL性能:VBOs/顶点着色器vs.glEnableClientState/glVertexPointer和glMultMatrix vs.glUniformMatrix

C++ OpenGL性能:VBOs/顶点着色器vs.glEnableClientState/glVertexPointer和glMultMatrix vs.glUniformMatrix,c++,performance,opengl,graphics,glsl,C++,Performance,Opengl,Graphics,Glsl,我对OpenGL相当陌生。我刚刚开始学习着色器,特别是顶点和片段着色器。我的理解是,当事情通过着色器完成时,您可以获得相当显著的性能提升,因为着色器在GPU上运行 然而,我试着对这个主题做了一些研究,我似乎在这个问题上发现了一些不同的观点,至少在顶点着色器方面 渲染下面这样的对象与使用调用(如glmultMatrix xd进行我的变换)之间的主要区别是什么: glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_

我对OpenGL相当陌生。我刚刚开始学习着色器,特别是顶点和片段着色器。我的理解是,当事情通过着色器完成时,您可以获得相当显著的性能提升,因为着色器在GPU上运行

然而,我试着对这个主题做了一些研究,我似乎在这个问题上发现了一些不同的观点,至少在顶点着色器方面

渲染下面这样的对象与使用调用(如glmultMatrix xd进行我的变换)之间的主要区别是什么:

    glEnableClientState(GL_VERTEX_ARRAY);
    glEnableClientState(GL_NORMAL_ARRAY);

    glVertexPointer(3, GL_FLOAT, 0, &vertices[0]);
    glNormalPointer(GL_FLOAT, 0, &normals[0]);

    glDrawArrays(GL_TRIANGLES, 0, vertices.size() / 3);

    glDisableClientState(GL_VERTEX_ARRAY);
    glDisableClientState(GL_NORMAL_ARRAY);
vs使用VAO/VBO设置,如下所示,我在着色器中将变换矩阵设置为统一变量,并在那里进行变换

glBindVertexArray(vaoHandle);
glBindBuffer(GL_ARRAY_BUFFER, bufferHandle[0]);


glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(float), vertices.data(), GL_STATIC_DRAW);

glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(0);

glBindBuffer(GL_ARRAY_BUFFER, bufferHandle[1]);
glBufferData(GL_ARRAY_BUFFER, normals.size() * sizeof(float), normals.data(), GL_STATIC_DRAW);

glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(1);


只是提醒一下…我不在乎下面的代码有什么问题。再说一次,我只是想知道实际上是否存在性能差异,为什么?这两种方法的引擎盖下都发生了什么?为什么一个比另一个快/慢?转换也是如此。为什么使用统一的顶点着色器比使用glMultMatrix更快?

主要的性能差异不是来自使用着色器,而是来自使用VBOs

在第一个示例中,
顶点
法线
驻留在客户端内存(也称为应用程序内存)中。无论何时绘制,这些阵列都会复制到图形卡中,这可能需要很长时间


与此相反,第二个示例将所有相关值存储在位于图形内存中的VBO中。因此,数据已经存储在最佳位置,绘图不需要复制。

GPU最终执行的操作对于任何至少一半最近的GPU上的两种情况基本相同。我认为在相当长的一段时间内,还没有人构建出真正拥有固定管道专用硬件的GPU。对于桌面GPU,我相信这种转变发生在10多年前(在那之前的几年里,它们已经是可编程的,但仍然有固定功能的硬件)。对于移动GPU,向纯可编程GPU的过渡发生得比较晚,但也早了一段时间

如果使用固定管道,则驱动程序将根据设置的固定函数状态为您生成着色器代码。因此,您真正要比较的是从传递给驱动程序的GLSL编译的着色器,以及驱动程序基于状态值生成的着色器

显然,在这两种情况下,着色器都将在GPU上运行,因此除此之外,实际上没有根本区别

现在,你可能会问:哪一个更有效?总的来说没有办法说。一些考虑因素包括:

  • 由驱动程序为固定函数状态生成的着色器可能具有优势,因为它们经过了大量调优,很可能是在着色器程序集中。这主要是针对工作站级GPU完成的,其中许多软件使用传统的固定功能OpenGL的时间更长

  • 您在GLSL中编写的着色器具有这样的优势:它们完全满足您的需要,而无需其他操作。因此,从这个意义上讲,它们可能更适合您的精确用例。当然,由驱动程序从固定函数状态生成的相应着色器也可以高度简化,但这超出了您的控制范围。特别是如果您关心各种平台上的性能,坦率地说,我不相信所有GPU供应商都能为我生成高效的着色器代码


当然,编写自己的着色器代码还有其他主要优势。它允许您完成固定管道无法完成的事情。即使在固定管道可以完成这项工作的地方,一旦掌握了编写GLSL代码的诀窍,使用着色器通常也会更容易。

谢谢!这正是我想要的答案。因此,着色器(至少是顶点着色器)并不能真正提供任何性能优势?我可以,这取决于您所做的,但总的来说,我会说不。它确实提供了更多的功能。@user3827379:GPU在过去十年中构建的druing没有任何硬连接的顶点处理,因此,从GPU的角度来看,不使用着色器和使用着色器没有多大区别。2015年,使用不推荐使用的固定函数管道和矩阵堆栈没有多大意义。好吧,我听说过很多,但为什么在这种情况下?例如,如果我有一个遗留应用程序使用第一个示例方法glMultMatrixd,而不是使用统一变量的顶点着色器,那么使用着色器/统一方法进行变换会有什么好处?与我的第一种方法相比,使用VAO/VBOs可以提高性能,但是否可以将VAO/VBOs与矩阵堆栈而不是着色器一起使用?
glBindVertexArray(vaoHandle);
glDrawArrays(GL_TRIANGLES, 0, vertices.size() / 3);