SGI Techpubs Library

IRIX 6.5  »  Books  »  Developer  »  
OpenGL Optimizer Programmer's Guide: An Open API for Large-Model Visualization
(document number: 007-2852-002 / published: 1998-06-09)    table of contents  |  additional info  |  download
find in page

Chapter 3. Sending Efficient Graphics Data to the Hardware

A potential bottleneck in the graphics pipeline is the transfer of rendering commands to the graphics hardware. Generating a compact set of OpenGL commands not only simplifies tasks for the host, it can accelerate later stages in the graphics pipeline.

For a discussion of techniques for developing an optimal set of OpenGL commands, see sections 6.6.2, “Reducing OpenGL Command Overhead,” and section 6.6.3, “Minimize OpenGL Mode Changes,” in Programming OpenGL for the X Window System (see “Recommended Background Reading”). This book is referred to in this chapter as the Green book.

This chapter presents five of the six approaches to optimization mentioned in the Green book sections 6.6.2 and 6.6.3: display lists, vertex arrays, short normals, connected primitives, and avoiding mode switching. The sixth method described in the Green book— using OpenGL evaluators— is a subtler task, addressed by OpenGL Optimizer higher-order geometric primitives, and discussed in Part IV, “Managing and Rendering Higher-Order Geometric Primitives.” OpenGL Optimizer also includes a tool for using multiple processors to create connected primitives.

Also included in this chapter is the discussion of a scene-graph-flattening tool, which simplifies a scene graph.

The chapter has the following sections:

Display Lists

An OpenGL display list is a copy of the scene graph in a form optimized for the graphics pipeline. On some machines, you can accelerate rendering by nearly a factor of 10 by using display lists. The speedup occurs if the graphics hardware can hold display lists in a cache. For graphics hardware of this type, display lists are the most efficient descriptions of objects in a scene. However, because display lists are a copy, they use more memory.

Display lists are useful if you can graphically treat all the elements in the list as a unit. If you have to independently manipulate an element in the group, a display list is not appropriate.

For more information on the advantages of using display lists, see the Green book; the Red book, particularly Chapter 4; and OpenGL on Silicon Graphics Systems, particularly the sections “CPU Tuning: Basics” and “CPU Tuning: Display Lists” in Chapter 12, “Tuning the Pipeline.” These books are all listed in “Recommended Background Reading”.

These two OpenGL Optimizer functions create OpenGL display lists:

opDListCSGeometry(g) 


Compiles a single csGeometry g into an OpenGL display list and returns the modified csGeometry. This is the prototype:

csGeometry *opDListCSGeometry(csGeometry *g)

opDListScene(root) 


traverses the scene graph, beginning at root, compiling each csGeometry into an OpenGL display list. This is the prototype:

void opDListScene(csNode *root)

See the opGFXSpeed(3) manpage for more details.

Vertex Arrays

For more efficient surface descriptions, convert csGeoSet attributes to OpenGL vertex arrays, an alternative to using procedure calls for each piece of vertex data.

For more information on vertex arrays, see the OpenGL Programming Guide, particularly the section “Vertex Arrays” in Chapter 2; and OpenGL on Silicon Graphics Systems, Chapter 14, “Tuning the Pipeline.” These books are listed in “Recommended Background Reading”.

These two OpenGL Optimizer functions make OpenGL vertex arrays (see opGFXSpeed.h):

opGLArrayEXTCSGeoSet() 


Converts the attributes in a csGeoSet to the format appropriate for glDrawArrays() and returns the modified csGeoSet. This is the declaration for the conversion function:

csGeoSet *opGLArrayEXTCSGeoSet(csGeoSet *g)

opGLArrayEXTScene() 


Converts the attributes in all the csGeoSets in a scene graph to the format appropriate for glDrawArrays() and returns the root of modified scene graph. This is the declaration for the conversion function:

void opGLArrayEXTScene(csNode *root)

Shortening Representations of Surface Normal Data

Surface normals, which accurately represent a surface before tessellation, are usually stored in a csGeoSet as floating-point vectors (csVec3fs), one for each vertex.

For all normal vectors in the scene graph below root, the function opShortNormsScene(root) converts the data format from csVec3f to csVec3s, that is, to short-integer vectors. This shortening of the memory segments holding surface normals reduces the amount of data that must be sent from the host to the graphics pipeline by as much as 25%.

Short normals provide faster rendering in situations where host-to-graphics-pipeline bandwidth is the limiting factor. The reduced data volume also enhances performance by allowing more of the scene to reside in the display-list cache.

Avoiding OpenGL Mode Switching

If the OpenGL state (or mode) differs between objects in a scene, rendering speed, particularly the transformation and rasterization stages, can be slowed due to the reconfiguration required.

Two OpenGL Optimizer classes allow you to inhibit mode changes during rendering. You can inhibit a change to the color associated with a csShape or you can disable the entire csAppearance associated with the shape. In either case the first values of states that are encountered during the draw traversal are used for the entire scene.

Removing Color Bindings

You can accelerate the transform stage by disabling the current-color tests, which are controlled by glColorMaterial(). Naturally this alters the color of objects. See the OpenGL Programming Guide for more details.

The function opRemoveColorBindings() traverses a scene graph and sets the color binding of each csGeoSet to NO_COLOR. This is the declaration of the function, which appears in the file opGFXSpeed.h:

void opRemoveColorBindings(csNode *root)

Removing csAppearance Effects: opCollapseAppearances

You can force all csShape nodes in a scene graph to have the same csAppearance, and thus prevent mode switching by the OpenGL machine during rendering. To do so, use the class opCollapseAppearances, which is a csAction that traverses the scene graph and sets all csAppearances to be the same as the first appearance encountered by the traversal. Be aware, however, that existing csAppearances are lost.

Methods in opCollapseAppearances

apply() 

Is inherited from csAction. When you call apply() on a node, all csShapes below it are set to have the same csAppearance as the first csShape encountered by a traversal starting at that node.

Creating OpenGL Connected Primitives

OpenGL defines two useful geometric primitives to minimize the redundancy of vertex information, and thus increase rendering performance: triangle fans (trifans) and triangle strips (tristrips), as shown in Figure 3-1.

Figure 3-1. Construction of Triangle Fan (left) and Triangle Strip (right)

Figure 3-1 Construction of Triangle Fan (left) and Triangle Strip (right)

Trifans and tristrips take advantage of adjacency to eliminate vertex data duplication along shared edges. A tristrip or trifan with n triangles is specified by n+2 vertices, which is typically significantly less than the 3n vertices required to encode n triangles independently.

Tristrips and trifans used in conjunction with display lists form a powerful combination on machines with a display-list cache. Because of their compact representations, tristrips and trifans allow the cache to hold more triangles.

The following sections discuss OpenGL Optimizer classes for creating trifans and tristrips:


Note: You can read more about trifans and tristrips in the OpenGL Programming Guide.


Features of Trifans and Tristrips

Reducing the number of vertices by collecting triangles into strips or fans mainly reduces transform time— fewer vertices means fewer vertex transformations. Secondary benefits of “tristripping” and “trifanning” are reductions in OpenGL function call overhead, bandwidth requirements, memory consumption, and caching. Another benefit is fewer glVertex*() calls and proportionally less bandwidth to the graphics hardware. Because tristrips and trifans encode fewer vertices, they also require less memory than independent triangles. On the host side, this translates into better locality of reference. Fill-limited applications receive no benefit from using tristrips or trifans.

How OpenGL Optimizer Constructs Trifans and Tristrips

During construction of a trifan, a new triangle is defined by a new vertex, the previous vertex, and the first vertex, which is common to all the triangles in the fan (see Figure 3-1).

During the construction of a tristrip, a new triangle is defined by a new vertex the previous two vertices that were added to the tristrip (see Figure 3-1).

How OpenGL Optimizer Manages Attributes of Shared Vertices

Each vertex has attributes, such as color. When a vertex defines a new triangle in a tristrip or trifan, it retains the attributes it had as a member of the original triangle. When the vertex is subsequently shared with another triangle, it has two sets of attributes. To resolve the ambiguity, the vertex's attributes that are associated with the most recently added triangle are lost.

If normals and colors associated with shared vertices of two adjacent triangles are too different, you may see an unacceptable distortion of appearance. You can therefore control the maximum acceptable difference between the attributes of the vertex in the two triangles in which it participates.

To illustrate the problem, consider the case of two adjacent triangles that lie on different faces of a cube. The original normals associated with the shared vertices on the edge of the cube are at right angles to each other. If these triangles are grouped into a tristrip, one of the faces is lit as if it were a curved surface, because its original normal at the shared vertex no longer controls the lighting calculation. Similarly, if you created a trifan with a central vertex at the corner of a cube and triangles on all three adjacent faces, two of the faces would appear curved.

Strategies for Using Trifans, Tristrips, or a Combination of Both

Trifanning algorithms often work well where tristripping algorithms work poorly, and vice versa. Generating trifans is typically easier than generating good tristrips because a good candidate for the first vertex in a fan is any vertex adjacent to a large number of edges. Determining starting triangles for tristrips is more complicated. OpenGL Optimizer provides classes for three ways to create trifans and tristrips:

  • a trifan generator

  • a tristrip generator

  • an automatic combination of the two

To tune your scene graph, try each technique, and use the one that results in the minimum number of vertices (see “Gathering Triangle Statistics”).

Triangle fans are particularly useful when used with tessellations of trimmed NURBS because the tessellation process often generates large sets of triangles that can be represented by fans. See Part IV, “Managing and Rendering Higher-Order Geometric Primitives” for more information on NURBS.

Counting Vertices to Assess Graphic Pipeline Load

To assess the benefits of tristrips or trifans when tuning your database, use the average number of vertices per triangle as a metric. The vertex number is preferable to the average number of triangles per trifan or tristrip because it is proportional to the real computational load on the transformation stage of the pipeline. To obtain triangle and vertex statistics, see “Gathering Triangle Statistics”.

Merging Triangles Into Fans: opTriFanner

The main feature of the opTriFanner class is an overloaded method, convert(), which generates csTriFanSets from triangle sets. A set of triangles can come from a csGeometry, from a singly linked list of trifans that you create, or from an opGeoConverter, discussed in “Decomposing csGeoSets Into Constituent Triangles: opGeoConverter”. In anticipation of possible derivations, the member function convert() is declared to accept the parent class of csGeoSet, csGeometry.

Class Declaration for opTriFanner

The class has the following main methods:

class opTriFanner : public opTriFanSetBuilder
{
public:
opTriFanner(const opGeoConverter *gc);
~opTriFanner();

static csGeometry *convert( 
                    const opGeoConverter *gc,
                    opColorGenerator *cg=opColorGenerator::noColors());

static csGeometry *convert( 
                    csGeometry *geom,
                    opColorGenerator *cg=opColorGenerator::noColors());
};

The TriFanner::convert() Method

The convert() method can be invoked with two different set of arguments. The method can have one of the following effects:

  • Returns a new csGeometry containing csTriFanSets made by rearranging the triangles from gc. The optional opColorGenerator specifies a new color scheme for the triangle fans.

  • Returns a csGeometry containing triangle fans made by rearranging the triangles from geom. The following csGeometrys are triangulated and trifanned: csTriSet, csTriStripSet, or csTriFanSet, csQuadSet, csPolySet. The optional opColorGenerator specifies a new color scheme for the triangle fans.

Merging Triangles Into Strips: opTriStripper

The second approach to control redundant vertex information is to organize triangles into strips of adjacent triangles.

Class Declaration for opTriStripper

The class has the following main methods:

class opTriStripper : public opTriStripSetBuilder
{
public:
opTriStripper(const opGeoConverter *gc);
~opTriStripper();

static csGeometry *convert( 
                  const opGeoConverter *gc,
                  opColorGenerator *cg = opColorGenerator::noColors());

static csGeometry *convert( 
                  csGeometry *geom,
                  opColorGenerator *cg = opColorGenerator::noColors());

static csShape    *convert( 
                  csShape *s,
                  opColorGenerator *cg = opColorGenerator::noColors());
};

The TriStripper::convert() Method

The convert() method can be invoked with three different sets of arguments. The method can have one of the following effects:

  • Returns a new csShape containing csTriStripSets made by rearranging the triangles from shape. The following csGeometrys will be triangulated and tristripped: csTriSet, csTriStripSet, or csTriFanSet, csQuadSet, csPolySet. The optional opColorGenerator specifies a new color scheme for the triangle strips.

  • Returns a csGeometry containing triangle strips made by rearranging the triangles from geom. The following csGeometrys will be triangulated and tristripped: csTriSet, csTriStripSet, or csTriFanSet, csQuadSet, csPolySet. The optional opColorGenerator specifies a new color scheme for the triangle strips.

  • Returns a csGeometry containing triangle strips made by rearranging the triangles in an opGeoConverter. The optional opColorGenerator specifies a new color scheme for the triangle strips.

Tuning Triangle Strips: Fixing Tristrips that are too Short

The effectiveness of triangle strips depends on the length of the strips: many short strips are less efficient than the same number of triangles in one long tristrip.

Typically, models cannot be grouped into long strips using OpenGL Optimizer tristripping algorithms. In general, the more uniform the tessellation, the longer the strips will be. When you see too many vertices per triangle (see “Gathering Triangle Statistics”), check for the following:

  • The triangles may not actually be adjacent because of cracks. If the triangles have been generated by an OpenGL Optimizer tessellator, you may be able to eliminate the cracks using the opTopo class, which aligns the higher order representations before tessellation. For more information on opTopo, see Chapter 10, “Creating and Maintaining Surface Topology.”

  • Normals, colors, or texture coordinates may be too different to allow grouping. Try relaxing tolerances if possible.

  • The number of triangles available for creating tristrips may be too small for developing effective tristrips. Try combining triangles from several csGeoSets (see “Merging csGeoSets in a Scene Graph: opCombineGeoSets”).

  • Some models cannot be grouped into long strips using the OpenGL Optimizer algorithm. Try the trifanning algorithm, a different tristripping algorithm, a mix of tristrip and trifans, or see if you can generate a more uniform tessellation (see Chapter 11, “Rendering Higher-Order Primitives: Tessellators”).

Merging Triangles Into Both Strips and Fans: opTriFanAndStrip

The class opTriFanAndStrip is a csAction that uses a hybrid approach to traversing a scene graph and merging the triangles in each csGeoSet into trifans or tristrips.

The merging operation begins by making trifans. If a trifan has fewer than a minimum number of triangles, the fan is not kept and the triangles are passed to the tristripper.

Class Declaration for opTriFanAndStrip

The class has the following main methods:

class OP_DLLEXPORT opTriFanAndStrip : public csAction
{
public:
// Input:  csShape
//           csGeometry, csGeoSet0, . . . csGeoSetN
// Output: csShape
//           csGeometry, csTriStripSet, csTriFanSet
opTriFanAndStrip(int minFanSize,
    opColorGenerator *cg=opColorGenerator::noColors());
virtual ~opTriFanAndStrip();

static csShape *convert(
                    csShape *,
                    int minFanSize=5,
                    opColorGenerator *cg=opColorGenerator::noColors());
};

Methods in opTriFanAndStrip

apply(csNode *node) 


Is inherited from csAction. It initiates the conversion traversal and applies convert() to each csShape in the scene graph below node.

convert() 

Collects the csGeoSets in a csShape node and creates from all the triangles a new csTriFanSet containing fans with at least minFanSize triangles, and a csTriStripSet containing the remaining triangles. convert() then places these new objects in the csShape. The remaining csGeometrys are placed in a new csShape.

To control whether individual trifans and tristrips created by the apply() and convert() functions are distinguished by color, use an opColorGenerator as for opTriFanner and opTriStripper (see “The TriFanner::convert() Method” and “Specifying Coloring of New csGeoSets: opColorGenerator”).

Merging Triangles Using Multiple Processors: opMPTriFanAndStrip

If your application runs on a machine with multiple processors, you can use the OpenGL Optimizer tool opMPTriFanAndStrip to accelerate generation of trifans and tristrips.

The method apply(), which is inherited from csAction, performs the same conversion as opTriFanAndStrip::apply(), but runs the procedure in parallel. The algorithm checks the number of processors and reserves one for the thread manager; the remaining processors manipulate the scene graph. For more information about OpenGL Optimizer multiprocessing tools, see Chapter 14, “Managing Multiple Processors.”

Class Declaration for opMPTriFanAndStrip

The class has the following main methods:

class opMPTriFanAndStrip : public csAction
{
public:
opMPTriFanAndStrip(int minFanSize, opColorGenerator 
                   *cg=opColorGenerator::noColors());
virtual ~opMPTriFanAndStrip();

void             begin(csNode *node); // count shapes, allocate memory

csTravDirective  preVisit(csNode *node); // collect shapes in list

void             end(csNode *node); // convert shapes in parallel, 
                                   // replace in tree
};

Methods in opMPTriFanAndStrip

apply(csNode *node) 


Is inherited from csAction and initiates the conversion traversal, which uses all but one of the available processors.

opMPTriFanAndStrip() 


Sets the minimum allowable trifan size. Triangles from smaller fans become parts of tristrips. To evaluate the effect of the trifan size, see “Gathering Triangle Statistics”.

To control scene graph traversal, the class defines the virtual functions inherited from csAction: begin(), preVisit(), and end().

To control whether individual trifans and tristrips created by the apply() and convert() functions are distinguished by color, use an opColorGenerator as you do for opTriFanner and opTriStripper (see “The TriFanner::convert() Method” and “Specifying Coloring of New csGeoSets: opColorGenerator”).

Observing Trifans and Tristrips: opColorizeStrips()

The convenience function opColorizeStrips() traverses a scene graph and applies random colors to csTriStripSets, csTriFanSets, and csTriSets, allowing you to visualize the effects of opTriFanner, opTriStripper, opTriFanAndStrip, or opMPTriFanAndStrip algorithms. Notice that the convert() method for each of these classes also allows you to apply random color to the output.

The function, which is declared in opGFXSpeed.h, has the following prototype:

void opColorizeStrips(csNode *root)

OpenGL Optimizer Programmer's Guide: An Open API for Large-Model Visualization
(document number: 007-2852-002 / published: 1998-06-09)    table of contents  |  additional info  |  download

    Front Matter
    About This Guide
    Part I. Getting Started
    Part II. High-Level Strategic Tools for Fast Rendering
    Part III. Specific Tools for Fast Rendering
    Part IV. Managing and Rendering Higher-Order Geometric Primitives
    Part V. Traversers, Low-Level Geometry Processing, and Multiprocessing
    Part VI. Utilities and Troubleshooting
    Part VII. Appendices
    Glossary
    Index


home/search | what's new | help