Accelerate with Draw Lists

It is time now to speed up our drawing a little bit. Improving the drawing speed is crucial for applications that draw complicated scenes, especially games.

So let's start by examining what we need and how to get it from the system. In order to draw a cube we have to draw the six faces. Each face consists of four points and a normal vector. This sums up to seven calls into OpenGL.

glBegin(GL_QUADS);		// instruct OpenGL to interpret the following as commands for a quad face
glNormal3fv(nx, ny, nz);	// the normal vector of the face
glVertex3fv(x1,y1,z1); // now the vertices glVertex3fv(x2,y2,z2); glVertex3fv(x3,y3,z3); glVertex3fv(x4,y4,z4); glEnd(); // this terminates the face

This has to be called six times. If only there was a way to store the geometry data somewhere in the display driver or even better in the graphics hardware. Well actually there is. It is called "draw list".

Draw lists store "compiled" geometry inside the video driver memory and if possible in the graphics card memory. The information kept is a lot less than what we need to pass to the driver with the consecutive calls and further more converted to the format the video card expects it. So every time we need to draw our objects we save a lot of time.

Then all we have to do in our drawing function is setup the projection and modeling parameters and ask the driver to draw the cube.

How do we make a draw list?

I selected draw lists as an introduction to OpenGL acceleration because it is one of the easiest things you can do. All you have to do is ask OpenGL to create a list, select it, do the drawing and close it. From this point on when you need to repeat this drawing procedure just call the list. I really tried to make it look hard. A few lines of code will certainly clarify things for you.

GLuint draw_list = glGenLists(1);
glNewList(draw_list, GL_COMPILE);
glColor3f(objects[i].r, objects[i].g, objects[i].b);
glBegin(GL_QUADS);// instruct OpenGL to interpret the following as commands for a quad face
glNormal3fv(nx, ny, nz); // the normal vector of the face
glVertex3fv(x1,y1,z1); // now the vertices
glVertex3fv(x2,y2,z2);
glVertex3fv(x3,y3,z3);
glVertex3fv(x4,y4,z4);
glEnd();// this terminates the face
glEndList();

Now the list is ready to use. The variable 'draw_list' holds the identifier for the generated list, so when you want to draw all you have to do is

glCallList(draw_list);

One of the great advantages I find in this is the simplicity of the resulting code. You usually do a lot more than just drawing one face of a cube and this technique hides all the drawing code away. Your program is a lot shorter and easier to maintain and extend. Only one thing is left to do now. At program end remember to tell the video driver to release any memory allocated to the draw list. This is done with a simple line of code like this:

if (draw_list >  -1) glDeleteLists(draw_list, 1);

In the sample program we draw a big collection of 1000 boxes. By pressing 'L' you toggle between using a draw list and drawing all the boxes manually. The frame rate is displayed on the title bar of the window. The advantage of the draw list is more than obvious.