OpenGL 性能:VBOs/Vertex 着色器与 glEnableClientState/glVertexPointer 以及 glMultMatrix 与 glUniformMatrix
OpenGL performance: VBOs/Vertex shader vs. glEnableClientState/glVertexPointer and glMultMatrix vs glUniformMatrix
我对 OpenGL 还很陌生。我刚刚开始学习着色器,尤其是顶点和片段着色器。我的理解是,当通过着色器完成任务时,您可以获得相当显着的性能提升,因为着色器在 GPU 上运行。
但是,我已经尝试对这个主题进行一些研究,并且我似乎发现了一些关于这个问题的不同意见,至少在顶点着色器方面是这样。
像下面这样渲染对象和使用像 glMultMatrixd 这样的调用进行转换之间的主要区别是什么:
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);
.....
glBindVertexArray(vaoHandle);
glDrawArrays(GL_TRIANGLES, 0, vertices.size() / 3);
请注意...我不关心下面的代码有什么问题。同样,我只想知道是否确实存在性能差异,为什么?这两种方法的底层原理是什么?为什么一个 faster/slower 然后另一个?转换也是如此。为什么在顶点着色器中使用 uniform 比使用 glMultMatrix 更快?
主要的性能差异不是来自使用着色器,而是来自使用 VBO。
在第一个示例中,vertices
和 normals
驻留在客户端内存(也称为应用程序内存)中。每当绘制它们时,这些数组都会被复制到显卡,这可能会花费大量时间。
与此相反,第二个示例将所有相关值存储在位于图形内存中的 VBO 中。因此数据已经存储在最佳位置,不需要复制来绘制。
对于至少一半最近的任何 GPU 上的两种情况,GPU 最终执行的内容基本相同。我认为在相当长的一段时间内,没有人构建出真正具有固定管道专用硬件的 GPU。对于桌面 GPU,我相信这种转变发生在大约 10 多年前(在此之前的几年,它们已经是可编程的,但仍然具有固定功能的硬件)。对于移动 GPU,向纯可编程 GPU 的过渡发生得较晚,但也发生在很久以前。
如果您使用固定管道,驱动程序会根据您设置的固定功能状态为您生成着色器代码。因此,您真正比较的是从传递给驱动程序的 GLSL 编译而来的着色器,以及驱动程序根据状态值生成的着色器。
在这两种情况下,着色器显然会在 GPU 上 运行,因此除此之外真的没有根本区别。
现在,您可能会问:哪个效率更高?一般没法说。一些注意事项包括:
由驱动程序为固定功能状态生成的着色器可能具有优势,因为它们经过大量调整,很可能在着色器程序集中。这主要是针对工作站 class GPU 完成的,其中许多软件使用传统固定功能 OpenGL 的时间更长。
您用 GLSL 编写的着色器的优势在于它们完全您需要的,没有别的。因此,从这个意义上说,它们可能会更适合您的精确用例。当然,驱动程序从固定功能状态生成的相应着色器也可以高度精简,但这不在您的控制范围内。特别是如果您关心各种平台上的性能,坦率地说,我不会相信所有 GPU 供应商都能为我生成高效的着色器代码。
当然,编写自己的着色器代码还有其他主要优势。它允许你做一些用固定管道根本不可能做的事情。即使在固定管道可以完成工作的地方,一旦掌握了编写 GLSL 代码的窍门,使用着色器通常也会更容易。
我对 OpenGL 还很陌生。我刚刚开始学习着色器,尤其是顶点和片段着色器。我的理解是,当通过着色器完成任务时,您可以获得相当显着的性能提升,因为着色器在 GPU 上运行。
但是,我已经尝试对这个主题进行一些研究,并且我似乎发现了一些关于这个问题的不同意见,至少在顶点着色器方面是这样。
像下面这样渲染对象和使用像 glMultMatrixd 这样的调用进行转换之间的主要区别是什么:
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);
.....
glBindVertexArray(vaoHandle);
glDrawArrays(GL_TRIANGLES, 0, vertices.size() / 3);
请注意...我不关心下面的代码有什么问题。同样,我只想知道是否确实存在性能差异,为什么?这两种方法的底层原理是什么?为什么一个 faster/slower 然后另一个?转换也是如此。为什么在顶点着色器中使用 uniform 比使用 glMultMatrix 更快?
主要的性能差异不是来自使用着色器,而是来自使用 VBO。
在第一个示例中,vertices
和 normals
驻留在客户端内存(也称为应用程序内存)中。每当绘制它们时,这些数组都会被复制到显卡,这可能会花费大量时间。
与此相反,第二个示例将所有相关值存储在位于图形内存中的 VBO 中。因此数据已经存储在最佳位置,不需要复制来绘制。
对于至少一半最近的任何 GPU 上的两种情况,GPU 最终执行的内容基本相同。我认为在相当长的一段时间内,没有人构建出真正具有固定管道专用硬件的 GPU。对于桌面 GPU,我相信这种转变发生在大约 10 多年前(在此之前的几年,它们已经是可编程的,但仍然具有固定功能的硬件)。对于移动 GPU,向纯可编程 GPU 的过渡发生得较晚,但也发生在很久以前。
如果您使用固定管道,驱动程序会根据您设置的固定功能状态为您生成着色器代码。因此,您真正比较的是从传递给驱动程序的 GLSL 编译而来的着色器,以及驱动程序根据状态值生成的着色器。
在这两种情况下,着色器显然会在 GPU 上 运行,因此除此之外真的没有根本区别。
现在,您可能会问:哪个效率更高?一般没法说。一些注意事项包括:
由驱动程序为固定功能状态生成的着色器可能具有优势,因为它们经过大量调整,很可能在着色器程序集中。这主要是针对工作站 class GPU 完成的,其中许多软件使用传统固定功能 OpenGL 的时间更长。
您用 GLSL 编写的着色器的优势在于它们完全您需要的,没有别的。因此,从这个意义上说,它们可能会更适合您的精确用例。当然,驱动程序从固定功能状态生成的相应着色器也可以高度精简,但这不在您的控制范围内。特别是如果您关心各种平台上的性能,坦率地说,我不会相信所有 GPU 供应商都能为我生成高效的着色器代码。
当然,编写自己的着色器代码还有其他主要优势。它允许你做一些用固定管道根本不可能做的事情。即使在固定管道可以完成工作的地方,一旦掌握了编写 GLSL 代码的窍门,使用着色器通常也会更容易。