2.4 Performance Issues
Buffer objects and vertex arrays should result in acceptable performance in most rendering situations.
Visit OpenGL vendor Web sites to become informed about the limits of specific OpenGL implementations. If you suspect that your performance is significantly less than typical, add debugging code to obtain an accurate vertex count from your application. Compare your vertex count with vendor specifications, and calculate an expected performance rate. Ensure that you are not specifying more vertices than necessary.
The following sections provide some additional performance tips.
2.4.1 Display Lists
Display lists store a sequence of OpenGL commands in server memory for later execution. Applications execute the entire display list command sequence with a single command, glCallList (). Executing commands from display lists is often more efficient than issuing the same sequence of commands individually.
To use display lists, your application must obtain, store, and eventually dispose of display list identifiers. For each display list your application uses, you must store a sequence of OpenGL commands in it. Finally, when you want to execute the stored commands, you'll need to issue the glCallList () command.
To obtain unused display list identifiers, use glGenLists (). To dispose of display list identifiers when they are no longer needed, call glDeleteLists ().
Use these commands to manage display list identifiers. glGenLists () creates a sequence of s unused display list identifiers and marks them as used. It returns the first identifier in the sequence. glDeleteLists () deletes the sequence of range identifiers starting at list. glIsList () returns GL_TRUE if list is an existing display list.
OpenGL version: 1.0 and later.
To obtain a single display list identifier, use the following code:
GLuint listID; listID = glGenLists( 1 );
Your application can obtain multiple display list identifiers by passing in an s parameter greater than 1. In this case, the returned identifier is the base, and additional identifiers follow sequentially.
When you call glDeleteLists (), OpenGL marks the sequence of identifiers from list through list+range-1 as unused.
To store OpenGL commands in a display list, issue the glNewList () command followed by the sequence of commands you want to store. Stop storing commands in a display list with the glEndList () command.
Use these commands to specify OpenGL commands to store in a display list. n is the display list identifier. When mode is GL_COMPILE, subsequent OpenGL commands are stored in the display list. Specifying a mode of GL_COMPILE_AND_EXECUTE also stores commands but additionally executes them. GL_COMPILE_AND_EXECUTE is less efficient than GL_COMPILE, so avoid GL_COMPILE_AND_EXECUTE.
OpenGL version: 1.0 and later.
As an example, consider the glBegin ()/ glEnd () paradigm code for drawing a triangle. Because this method of specifying geometry uses so many function calls, it's an excellent candidate for storing in a display list. The following code stores in a display list the commands for drawing a triangle:
glNewList( listID, GL_COMPILE ); glBegin( GL_TRIANGLES ); glColor3f( 1.f, 0.f, 0.f ); glVertex3f( 0.f, 0.f, 0.f ); glVertex3f( 1.f, 0.f, 0.f ); glVertex3f( 0.f, 1.f, 0.f ); glEnd(); glEndList();
OpenGL doesn't store all commands in a display list. In particular, commands that affect client state aren't stored in display lists, such as glPushClientAttrib (), glPopClientAttrib (), glEnableClientState (), glBindBuffer (), glVertexPointer (), glNormalPointer (), and glTexCoordPointer (). If these commands are executed between glNewList () and glEndList (), OpenGL executes them immediately. For a complete list of commands that are not stored in a display list, see Section 5.4, "Display Lists," of The OpenGL Graphics System.
Applications can store the vertex array rendering commands glDrawElements(), glDrawRangeElements (), and glMultiDrawElements () in display lists. When an application stores a vertex array rendering command in a display list, OpenGL copies the necessary vertex array data into the display list at the same time.
You shouldn't store vertex array rendering commands in display lists if you're using buffer objects, because buffer objects usually store vertex data in high-performance server memory. In this situation, storing the vertex array rendering commands in a display list would cause OpenGL to make a second copy of the data in high-performance server memory. This could adversely affect application performance by unnecessarily consuming additional memory. On the other hand, if your application is running on an OpenGL implementation that doesn't support buffer objects, storing vertex array rendering commands in display lists could boost application performance.
As stated previously, storing a vertex array rendering command in a display list causes OpenGL to copy all necessary vertex array data into the list at the same time. For this reason, your application must enable the appropriate arrays and issue vertex array pointer commands as though it is actually rendering the arrays.
To execute the commands stored in a display list, call glCallList ().
Executes stored OpenGL commands from a display list. n is the display list identifier to execute.
OpenGL version: 1.0 and later.
An added benefit of display lists is the elimination of application branches and loops. A for loop, for example, at the machine level typically incurs the overhead of an increment, a compare, and a branch per loop iteration. Highly tuned applications often partially unwind loops to minimize this overhead. When creating a display list, the application executes the loop only while creating the list. OpenGL stores only the resulting OpenGL commands, which are executed with a glCallList () command as a fully unwound loop.
The drawback to display lists should be obvious. When creating a display list, OpenGL makes a copy of both the commands and their data, possibly creating a burden on memory. If memory is a scarce resource for your application, you should use display lists only for the most performance-critical portions of your code.
2.4.2 Face Culling
The OpenGL face-culling feature discards filled primitives based on the direction they face. Applications typically use face culling to discard faces from the back of models. This boosts performance by not bothering to rasterize primitives that won't be visible in the final image.
To use face culling, your application needs to enable it. You might also need to specify the faces that OpenGL should cull.
OpenGL effectively uses the right-hand rule to determine whether a primitive is front- or back-facing. If the vertices are ordered counterclockwise, the primitive is front facing. Note that a front face can become a back face due to a modeling transformation or change in viewing angle. For this reason, OpenGL performs face culling in window-coordinate space. (Chapter 3, "Transformation and Viewing," describes all OpenGL coordinate spaces. For now, think of window coordinates as pixels onscreen.)
To enable face culling, call glEnable ( GL_CULL_FACE ). By default, this culls back faces, or faces with clockwise-ordered window-coordinate vertices. You can tell OpenGL to cull front faces instead by calling glCullFace ( GL_FRONT ).
Typically, applications enable face culling when the application renders solid geometry with a complete hull. Many applications create geometry using the right-hand rule so that counterclockwise vertices define front faces. Not all applications behave this way, however, and it's common for 3D model files to store faces with a vertex ordering that doesn't match the OpenGL default. You can change this default with the glFrontFace () command. See "glFrontFace" in OpenGL® Reference Manual.
For more information on face culling, see Chapter 2, "State Management and Drawing Geometric Objects," of OpenGL® Programming Guide.
2.4.3 Vertex Array Size
Most OpenGL implementations can provide maximum performance only if the number of vertices stored in a vertex array is below an implementation-specific threshold. An application can store as many vertices in a vertex array as necessary, but if the number exceeds this threshold, performance could suffer.
Applications can query this implementation-specific threshold with the following code:
GLint maxVerts; glGetIntegerv( GL_MAX_ELEMENTS_VERTICES, &maxVerts );
When using glDrawRangeElements (), the minimum and maximum index parameters, start and end, should specify a range smaller than GL_MAX_ELEMENTS_VERTICES to obtain maximum performance.
OpenGL implementations have a similar limit on the number of vertex array indexes. Applications can query this value by calling glGetIntegerv () and passing in GL_MAX_ELEMENTS_INDICES. The count parameter to both glDrawElements () and glDrawRangeElements () should be less than GL_MAX_ELEMENTS_INDICES to obtain maximum performance.
Many applications disregard these upper limits and simply specify as many vertices and indices as necessary. If performance is a concern, however, modify your application to respect these limits where feasible.