glDrawArraysInstanced的性能问题

时间:2014-04-08 10:17:58

标签: opengl nvidia opengl-4 gldrawarrays

我试图实现一个OpenGL4实例绘制算法,其中每个实例由一个三角形组成。 我想要实现这种算法的主要原因是:

  • 在基于每个三角形而不是基于每个顶点的颜色给定的频繁场景中使用较少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);
}

片段着色器是这一个:

#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);

如您所见,我使用单个VBO作为位置,但每个位置属性都连接到VBO 使用不同的偏移量。

对于颜色,我使用单个VBO,并且使用相同的步幅和偏移连接所有颜色属性(从而实现每个三角形颜色而不是每个顶点颜色)。

渲染循环如下:

glUseProgram(render_program);

glUniformMatrix4fv(uniforms.mvp_matrix, 1, GL_FALSE, proj_matrix * view_matrix);

glDrawArraysInstanced(GL_TRIANGLES, 0, 3, numTris);

我在集成的Intel HD 4400卡和Nvidia GeForce GT 750M卡上测试了该应用程序。 令人惊讶的是,在英特尔卡上的表现要比在Nvidia卡上表现得更好。以下是一些fps统计数据:

800000个三角形:

  • 英特尔:140 fps
  • Nvidia:31fps

1600000个三角形:

  • 英特尔:74 fps
  • Nvidia:16 fps

有人对如何提高Nvidia卡的性能有任何建议吗?你认为将TBO用于位置和颜色会给我带来性能提升吗?

更新:

为了更好地理解这个问题,我使用GPUView在windows下分析了应用程序。我注意到英特尔和Nvidia之间的行为完全不同。

英特尔每帧生成一个大的DMA数据包(8 kB),执行速度非常快。相反,Nvidia在每个排队的队列中产生了更大数量的小数据包(4-8个字节),因此,他们必须等待很长时间才能执行。

这些信息让我想知道这是否是一个Nvidia驱动程序错误。 你认为这是可能的吗?

0 个答案:

没有答案