Opengl glDrawArraysInstanced的性能问题
我试图实现一个OpenGL4实例绘制算法,其中每个实例由一个三角形组成。 我想实现这种算法的主要原因是:Opengl glDrawArraysInstanced的性能问题,opengl,nvidia,opengl-4,gldrawarrays,Opengl,Nvidia,Opengl 4,Gldrawarrays,我试图实现一个OpenGL4实例绘制算法,其中每个实例由一个三角形组成。 我想实现这种算法的主要原因是: 在经常出现的情况下使用较少的GPU内存的能力,在这种情况下,颜色是基于每个三角形而不是基于每个顶点的 在不使用几何体着色器的情况下执行每个三角形的计算的能力,从我的实验来看,几何体着色器大大降低了整个管道的速度 我的渲染程序由顶点着色器和片段着色器组成。顶点着色器如下所示: #version 400 core layout (location = 0) in vec3 tri_p0;
- 在经常出现的情况下使用较少的GPU内存的能力,在这种情况下,颜色是基于每个三角形而不是基于每个顶点的
- 在不使用几何体着色器的情况下执行每个三角形的计算的能力,从我的实验来看,几何体着色器大大降低了整个管道的速度
#version 400 core
layout (location = 0) in vec3 tri_p0;
layout (location = 1) in vec3 tri_p1;
layout (location = 2) in vec3 tri_p2;
layout (location = 3) in vec4 tri_colorP0;
layout (location = 4) in vec4 tri_colorP1;
layout (location = 5) in vec4 tri_colorP2;
out FRAGMENT {
vec4 color;
} vs_out;
uniform mat4 mvp_matrix;
void main(void) {
vec3 position;
vec4 color;
if(gl_VertexID == 0) {
position = tri_p0;
color = tri_colorP0;
}
else if(gl_VertexID == 1) {
position = tri_p1;
color = tri_colorP1;
}
else if(gl_VertexID == 2) {
position = tri_p2;
color = tri_colorP2;
}
vs_out.color = color;
gl_Position = mvp_matrix * vec4(position, 1.0);
}
glUseProgram(render_program);
glUniformMatrix4fv(uniforms.mvp_matrix, 1, GL_FALSE, proj_matrix * view_matrix);
glDrawArraysInstanced(GL_TRIANGLES, 0, 3, numTris);
片段着色器改为此着色器:
#version 400 core
layout (location = 0) out vec4 color;
in FRAGMENT {
vec4 color;
} fs_in;
void main(void) {
color = fs_in.color;
}
如您所见,在我的顶点着色器中,我为顶点位置声明了三个顶点属性,为颜色声明了三个顶点属性。所有这些属性都是实例化的,其除数设置为1
我之所以有三种颜色属性,是因为有时我希望能够为三个三角形顶点提供不同的颜色,而更常见的情况是,我为整个三角形提供单一颜色。在最后一个场景中,我只需将三个颜色属性附加到相同的VBO,指定相同的步幅和偏移
我编写了一个测试应用程序,它绘制一个四边形矩阵,每个四边形由两个三角形组成。
这是我用来初始化顶点数据的代码:
int numQuadsPerRowCol = sqrtl(NUM_TRIANGLES / 2);
numTris = numQuadsPerRowCol * numQuadsPerRowCol * 2;
float stepX = (maxX - minX) / numQuadsPerRowCol;
float stepY = (maxY - minY) / numQuadsPerRowCol;
GLfloat* positions = new GLfloat[3 * 3 * numTris];
GLfloat* colors = new GLfloat[4 * numTris];
int k = 0;
int l = 0;
for (int i = 0; i < numQuadsPerRowCol; i++) {
for (int j = 0; j < numQuadsPerRowCol; j++) {
GLfloat color[4];
int id = i * numQuadsPerRowCol + j;
color[0] = ((id & 0x00ff0000) >> 16) / 255.0;
color[1] = ((id & 0x0000ff00) >> 8) / 255.0;
color[2] = (id & 0x000000ff) / 255.0;
color[3] = 1.0;
for (int t = 0; t < 2; t++) {
for (int c = 0; c < 4; c++) {
colors[l + c] = color[c];
}
l += 4;
}
GLfloat xLeft = minX + j * stepX;
GLfloat xRight = minX + (j + 1) * stepX;
GLfloat yBottom = minY + i * stepY;
GLfloat yTop = minY + (i + 1) * stepY;
//first triangle positions
positions[k++] = xLeft;
positions[k++] = yTop;
positions[k++] = 0;
positions[k++] = xLeft;
positions[k++] = yBottom;
positions[k++] = 0;
positions[k++] = xRight;
positions[k++] = yBottom;
positions[k++] = 0;
//second triangle positions
positions[k++] = xLeft;
positions[k++] = yTop;
positions[k++] = 0;
positions[k++] = xRight;
positions[k++] = yBottom;
positions[k++] = 0;
positions[k++] = xRight;
positions[k++] = yTop;
positions[k++] = 0;
}
}
glGenBuffers(1, &positionVbo);
glBindBuffer(GL_ARRAY_BUFFER, positionVbo);
glBufferData(GL_ARRAY_BUFFER, numTris * 3 * 3 * sizeof(float), positions, GL_STATIC_DRAW);
glVertexAttribPointer(TRI_P0, 3, GL_FLOAT, GL_FALSE, 9 * sizeof(GLfloat), NULL);
glVertexAttribDivisor(TRI_P0, 1);
glEnableVertexAttribArray(TRI_P0);
glVertexAttribPointer(TRI_P1, 3, GL_FLOAT, GL_FALSE, 9 * sizeof(GLfloat), (void *)(3 * sizeof(GLfloat)));
glVertexAttribDivisor(TRI_P1, 1);
glEnableVertexAttribArray(TRI_P1);
glVertexAttribPointer(TRI_P2, 3, GL_FLOAT, GL_FALSE, 9 * sizeof(GLfloat), (void *)(6 * sizeof(GLfloat)));
glVertexAttribDivisor(TRI_P2, 1);
glEnableVertexAttribArray(TRI_P2);
glGenBuffers(1, &colorVbo);
glBindBuffer(GL_ARRAY_BUFFER, colorVbo);
glBufferData(GL_ARRAY_BUFFER, numTris * 4 * sizeof(float), colors, GL_STATIC_DRAW);
//All color attributes are attached to the same VBO with the same stride and offset --> per-triangle colors
glVertexAttribPointer(TRI_COLOR_P0, 4, GL_FLOAT, GL_FALSE, 0, NULL);
glVertexAttribDivisor(TRI_COLOR_P0, 1);
glEnableVertexAttribArray(TRI_COLOR_P0);
glVertexAttribPointer(TRI_COLOR_P1, 4, GL_FLOAT, GL_FALSE, 0, NULL);
glVertexAttribDivisor(TRI_COLOR_P1, 1);
glEnableVertexAttribArray(TRI_COLOR_P1);
glVertexAttribPointer(TRI_COLOR_P2, 4, GL_FLOAT, GL_FALSE, 0, NULL);
glVertexAttribDivisor(TRI_COLOR_P2, 1);
glEnableVertexAttribArray(TRI_COLOR_P2);
glBindBuffer(GL_ARRAY_BUFFER, 0);
我在集成Intel HD 4400卡和Nvidia GeForce GT 750M卡上测试了该应用程序。
令人惊讶的是,英特尔牌上的表现比英伟达的表现要好。以下是一些fps统计数据:
800000个三角形:
- 英特尔:每秒140帧
- 英伟达:31帧/秒
- 英特尔:每秒74帧
- 英伟达:每秒16帧
你认为这是可能的吗?尽管“带位置和颜色的三角形”听起来就像是地理着色器应该很好地处理的东西。这可能会更快,因为它可以“一次完成”。实例化的问题在于它不是免费的,它需要对每个实例的GPU进行一些模糊的篡改。在Intel HD上,CPU=GPU,这没什么大不了的,但在nv卡上,你可以看到你自己。通常建议对至少有几十个(或大约100个)顶点的模型使用实例化。嗨,Damon,谢谢你的回答。事实上,我认为这可能是Nvidia特有的问题。事实上,我也在ATI卡上试用了该应用程序,正如我所料,FPS甚至比Intel卡上的更好。我还使用GPUView分析了这个应用程序,我想我发现了一些有趣的东西。有关详细信息,请参阅原始帖子中的更新。如果更新,它实际上不应该执行这两项操作。根据您的描述,Intel似乎通过从对象生成一个大的顶点缓冲区来模拟软件中的实例,乘以实例。而nVidia似乎会为每个实例重新上传三角形。理想情况下,两者都应该做的是上传一次对象,并绘制它,如果GPU不能自己完成这项工作,那么在每个实例之后只需在GPU上调整一些指针即可(实际上,您希望它已经能够完成这项工作了!)。实例化毕竟只是一个类似于
for()
循环的循环。实例化中的所有内容都是相同的,除了实例ID。因此,实际上,这应该通过执行一次DMA传输并告诉GPU“为1000个实例执行此操作”,或者如果GPU无法执行此操作,则告诉它1000次:“执行此操作”,并在两者之间增加实例ID。是的,我同意你的看法。你所描述的正是我所期望的行为。我真的很惊讶地看到现实是完全不同的。