glDrawElements much slower than glDrawArrays

Russell Strong <[email protected]>
Newsgroups gmane.comp.video.mesa3d.user
Message-ID <[email protected]>
Hi,

As a learning exercise I'm rendering a uv sphere with glDrawArrays using buffer
objects, I then change this over to glDrawElements with indices 0,1,2,...n also
in a buffer object. What I found was that the glDrawElements version was
consuming 16% cpu compared with 4% for glDrawArrays.

I've looked around and can't seem to find any reason why it would be slower. 
Both "should" be storing everything in the gpu's ram as far as I can see, but I
suspect the draw elements version is not.

I'm using an older version of mesa 7.11.2 ( Fedora 16 ) intel driver, anyone
remember any problems with glDrawElements then?

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#define GL_GLEXT_PROTOTYPES
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glut.h>

#include "shader.h"

GLuint prog, vbo, ibo;
GLint attrib_vertex, attrib_colour;

#define SLICES 80
#define STACKS 40

#define GL_DRAW_ELEMENTS 0

struct sphere_vertex {
	GLfloat x,y,z;
	GLfloat r,g,b;
};

GLfloat colours[18] = {
	1,0,0,
	0,1,0,
	0,0,1,
	1,1,0,
	1,0,1,
	0,1,1,
};
int colour_idx = 0;

void sphere(float radius, int slices, int stacks)
{
	int i = 0;
	int n = 2 * (slices + 1) * stacks;

	GLuint indices[n];
	struct sphere_vertex points[n];

	float theta, phi;

	for (theta = -M_PI / 2; theta < M_PI / 2 - 0.0001; theta += M_PI / stacks) {
		for (phi = -M_PI; phi <= M_PI + 0.0001; phi += 2 * M_PI / slices) {
			points[i].x = cos(theta) * sin(phi) * radius;
			points[i].y = -sin(theta) * radius;
			points[i].z = cos(theta) * cos(phi) * radius;
			points[i].r = colours[colour_idx];
			points[i].g = colours[colour_idx+1];
			points[i].b = colours[colour_idx+2];

			colour_idx = (colour_idx + 3) % 18;
			i++;

			points[i].x = cos(theta + M_PI / stacks) * sin(phi) * radius;
			points[i].y = -sin(theta + M_PI / stacks) * radius;
			points[i].z = cos(theta + M_PI / stacks) * cos(phi) * radius;
			points[i].r = colours[colour_idx];
			points[i].g = colours[colour_idx+1];
			points[i].b = colours[colour_idx+2];

			colour_idx = (colour_idx + 3) % 18;
			i++;
		}
	}

	glGenBuffers(1, &vbo);
	glBindBuffer(GL_ARRAY_BUFFER, vbo);
	glBufferData(GL_ARRAY_BUFFER, sizeof(points), points, GL_STATIC_DRAW);

	for(i=0;i<n;i++)
		indices[i]=i;
	glGenBuffers(1, &ibo);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
	glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
}

void setup()
{
	prog = create_program("test.v", "test.f", NULL);

	glEnableClientState(GL_VERTEX_ARRAY);

	sphere(0.8, SLICES, STACKS);

	attrib_vertex = glGetAttribLocation(prog, "vertex");
	attrib_colour = glGetAttribLocation(prog, "colour");

	glEnable(GL_DEPTH_TEST);
	glDepthMask(GL_TRUE);

	// Wire frame
	//glPolygonMode(GL_FRONT, GL_LINE);
	//glPolygonMode(GL_BACK, GL_POINT);

	glEnable(GL_CULL_FACE);
	glCullFace(GL_BACK);

	glUseProgram(prog);

	glEnableVertexAttribArray(attrib_vertex);
	glEnableVertexAttribArray(attrib_colour);

	glBindBuffer(GL_ARRAY_BUFFER, vbo);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);

	glVertexAttribPointer(
		attrib_vertex,		// attribute
		3,			// elements per vertex (x,y,z)
		GL_FLOAT,		// type of each element
		GL_FALSE,		// take our value as-is
		6*sizeof(GLfloat),	// next x,y,z appears every 6 floats
		(GLvoid *)0		// offset of first element
	);
	glVertexAttribPointer(
		attrib_colour,		// attribute
		3,			// elements per colour (r,g,b)
		GL_FLOAT,		// type of each element
		GL_FALSE,		// take our value as-is
		6*sizeof(GLfloat),	// next r,g,b appears every 6 floats
		(GLvoid *)(3*sizeof(GLfloat))	// offset of first element
	);
}

void render(void)
{
	int i, num_in_slice = 2*(SLICES+1);

	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	glRotatef(0.4,1,0,0);

	for(i=1;i<STACKS-1;i++)
#if GL_DRAW_ELEMENTS
		glDrawElements(GL_TRIANGLE_STRIP, num_in_slice, GL_UNSIGNED_INT, (GLvoid
*)(i*sizeof(GLuint)*num_in_slice));
#else
		glDrawArrays(GL_TRIANGLE_STRIP, i*num_in_slice, num_in_slice);
#endif

	glutSwapBuffers();
}

void timer(int data)
{
	render();
	glutTimerFunc(10,timer,0);
}

int main(int argc, char* argv[])
{
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA);
	glutInitWindowPosition(100,100);
	glutInitWindowSize(320,320);
	glutCreateWindow("Window");

	glutDisplayFunc(render);
	glutTimerFunc(10, timer, 0);

	setup();

	glutMainLoop();
	return 0;
}
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.