Chapter 14. Using Scalable Graphics Hardware
Prev |  | Next

## Chapter 14. Using Scalable Graphics Hardware

Scalable graphics hardware provides nearly perfect scaling of both geometry rate and fill rate on some applications. This chapter describes how you use OpenGL Performer in conjunction with an SGI Video Digital Multiplexer (DPLEX), an SGI Scalable Graphics Compositor, and graphics processing units ( GPUs). The corresponding sections are the following:

- “Using OpenGL Performer with a DPLEX”

- “Using OpenGL Performer with an SGI Scalable Graphics Compositor”

- “Using OpenGL Performer with GPUs”

## Using OpenGL Performer with a DPLEX

A DPLEX is an optional daughtercard that permits multiple graphics hardware pipelines to work simultaneiously on a single visual application. DPLEX hardware is available on Silicon Graphics Onyx2, SGI Onyx 3000, and SGI Onyx 300 systems. For an overview of the DPLEX hardware, see the document Onyx2 DPLEX Option Hardware User's Guide.

OpenGL Performer taps the power of a DPLEX by using hyperpipes. The following sections describe how to use hyperpipes:

- “Hyperpipe Concepts”

- “Configuring Hyperpipes ”

- “Configuring pfPipeWindows and pfChannels”

- “Programming with Hyperpipes”

### Hyperpipe Concepts

A pfH yperpipe is a combination of pfPipes or pfMultipipes; there is one pfPipe for each graphics pipe in a DPLEX ring or chain. A DPLEX ring or chain is a collection of interconnected graphic boards.

A key concept with hyperpipes is that of temporal decomposition. Think of a rendered sequence as a 3D data set with time being the third axis. With temporal decomposition, the dataset is subdivided along the time axis and distributed across, in this case, each of the graphic pipes in the hyperpipe group.

Temporal decomposition is different from spatial decomposition, in which the dataset is subdivided along the X axis, Y axis, or both X and Y axes.

### Configuring Hyperpipes

It is the responsibility of the application to establish the hyperpipe group configuration for OpenGL Performer. There are two steps in the configuration process:

1. Establish the number of graphic pipes (or pfPipes because there is a one-to-one correspondence) in each hyperpipe group.

2. Map the pfPipes to specific graphic pipes.

#### Establishing the Number of Graphic Pipes

Use the argument in the pfHyperpipe() function to establish the number of graphic pipes in the hyperpipe group, for example:

    pfHyperpipe(2);
    pfConfig();

In this example, two pfPipes combine to create the pfHyperpipe, as shown in Figure 14-1.

Figure 14-1. pfPipes Creating pfHyperpipes

Like the pfMultipipe() function, pfHyperpipe() must be invoked prior to configuring the pfPipes using pfConfig() and after the call to pfInit().

The number of pipes is used by pfConfig() to associate the configured pfPipes. The pfHyperpipe() function can be invoked multiple times to construct multiple hyperpipe groups, as shown in Figure 14-2.

Figure 14-2. Multiple H yperpipes

Additionally, the pfHyperpipe() function can be combined with the pfMultipipe() call to configure pfPipes that are not associated with a hyperpipe group. The num argument to the pfMultipipe() function defines the total number of pfPipes to configure (including those in hyperpipe groups).

Example 14-1, diagrammed in Figure 14-2, shows the configuration of a system with three hyperpipe groups. The first hyperpipe group consists of three graphic pipes. The remaining two hyperpipe groups have two graphic pipes each. This example also configures one non-hyperpipe group graphic pipe.

Example 14-1. Configuring a System with Three Hyperpipe Groups

    pfInit();
    pfMultipipe(8);    /* need eight pfPipes 3-2-2-1 */
    pfHyperpipe(3);    /* pfPipes 0, 1, 2 are the first group */
    pfHyperpipe(2);    /* pfPipes 3, 4 are the second group */
    pfHyperpipe(2);    /* pfPipes 5, 6 are the third group */
    pfConfig();        /* construct the pfPipes */

If the target configuration includes only hyperpipe groups, it is not necessary to invoke pfMultipipe(). OpenGL Performer correctly determines the number of pfPipes from the pfHyperpipe() calls.

#### Using the Default Hyperpipe Mapping to Graphic Pipes

The pfPipes constructed by pfConfig() are ordered into a linear array and are selected with the pfGetPipe() function. The pfPipes that are part of a hyperpipe group always appear in this array before any non-hyperpipe group pfPipes.

The pfHyperpipe() function groups pfPipes together starting, by default, with pfPipe number 0. In the following example, there are four pfPipes; the first two are combined into a hyperpipe group:

    pfMultipipe(4);
    pfHyperpipe(2);
    pfConfig();

OpenGL Performer maps each pfPipe to a graphic pipe, which is associated with a specific X display, as shown in Figure 14-3:

Figure 14-3. Default Hyperpipe Mapping to Graphic Pipes

#### Using Nondefault Hyperpipe Mappings to Graphics Pipes

Each graphics pipe is associated with only one X screen. By default, OpenGL Performer assigns each pfPipe to the screen of the default X display that matches the pfPipe index in the pfPipe array; in other words, pfPipe(0) in the hyperpipe is mapped to X screen 0.

In most configurations, this default mapping is not sufficient. The second phase, therefore, involves associating the configured pfPipes with the graphic pipes. This is achieved through the pfPipeScreen() or pfPipeWSConnectionName() function on the pfPipes of the hyperpipe group.

Example 14-2 shows, given the configuration in Example 14-1, how to map the pfPipes to the appropriate screens. In this example, all of the graphic pipes are managed under the same X display, that is, a different screen on the same display.

Example 14-2. Mapping Hyperpipes to Graphic Pipes

    /* assign the single pfPipe to screen 0 */
    pfPipeScreen(pfGetPipe(7), 0);

    /* assign the pfPipes of hyperpipe group 0 to screens 1,2,3 */
    for (i=0; i < 3; i++)
    pfPipeScreen(pfGetPipe(i), i+1);

    /* assign the pfPipes of hyperpipe group 1 to screens 4,5 */
    for (i=3; i<5; i++)
        pfPipeScreen(pfGetPipe(i), i+1);

    /* assign the pfPipes of hyperpipe group 2 to screens 6,7 */
    for (i=5; i<7; i++)
        pfPipeScreen(pfGetPipe(i), i+1);

The following is a more complex example that uses GLXHyperpipeNetworkSGIX returned from glXQueryHyperpipeNetworkSGIX() to configure the pfPipes. This example is much more complete and is referred to in the following sections.

Example 14-3. More Complete Example: Mapping Hyperpipes to Graphic Pipes

    int hasHyperpipe;
    	GLXHyperpipeNetworkSGIX* hyperNet;
    	int numHyperNet;
    	int i;
    	Display* dsp;
    	int numNet;
    	int pipeIdx;
    	pfChannel* masterChan;

    	/* initialize Performer */
    	pfInit();

    	/* does this configuration support hyperpipe */
    	pfQueryFeature(PFQFTR_HYPERPIPE, &hasHyperpipe);
    	if (!hasHyperpipe) {
    	    pfNotify(PFNFY_FATAL, PFNFY_RESOURCE, "no hyperpipe support");
    	    exit(1);
    	}

    	/* query the network */
    	dsp = pfGetCurWSConnection();
    	hyperNet = glXQueryHyperpipeNetworkSGIX(dsp, &numHyperNet);
    	if (numHyperNet == 0) {
    	    pfNotify(PFNFY_FATAL, PFNFY_RESOURCE, "no hyperpipes");
    	    exit(1);
    	}

    	/*
    	 * determine the number of distinct hyperpipe networks. network
    	 * ids are monotonically increasing from zero. a value < 0
    	 * is used to indicate pipes that are not members of any hyperpipe.
    	 */
    	for (i=0, numNet=-1; i<numHyperNet; i++)
    	    if (numNet < hyperNet[i].networkId)
    		numNet = hyperNet[i].networkId;
    	numNet += 1;

    	/*
    	 * configure all of the hyperpipes in the net
    	 *
    	 * NOTE -
    	 * while it is possible to be selective about which hyperpipe(s)
    	 * to configure, that is left as an exercise.
    	 */
    	for (i=0; i<numNet; i++) {
    	    int count = 0;
    	    int j;
    	    for (j=0; j<numHyperNet; j++)
        		if (hyperNet[i].networkId == i) count++;
    	    pfHyperpipe(count);
    	}

    	pfConfig();

    	/* associate pfPipes with screens */
    	for (i=0, pipeIdx=0; i<numNet; i++) {
    	    int j;
    	    for (j=0; j<numHyperNet; j++)
        		if (hyperNet[i].networkId == i)
    		    pfPipeWSConnectionName(pfGetPipe(pipeIdx++),
    			    hyperNet[i].pipeName);
    	}

    	/* construct the pfPipeWindows for each hyperpipe */
    	masterChan = NULL;
    	for (i=0, pipeIdx=0; i<numNet; i++) {
    	    pfPipe* pipe;
    	    pfPipeWindow* pwin;
    	    pfChannel* chan;
    	    PFVEC3 xyz, hpr;

    	    pipe = pfGetPipe(pipeIdx);
    	    pwin = pfNewPWin(pipe);
    	    pfPWinName(pwin, "Hyperpipe Window");

    	    /*
    	     * void
    	     * openPipeWindow(pfPipeWindow* pwin)
    	     * {
    	     *     pfPWinOpen(pwin);
    	     * }
    	     */
    	    pfPWinConfigFunc(pwin, openPipeWindow);
    	    pfPWinFullScreen(pwin);
    	    pfPWinMode(pwin, PFWIN_NOBORDER, 1);
    	    pfPWinConfig(pwin);

    	    chan = pfNewChan(pipe);
    	    pfPWinAddChan(pwin, chan);

    	    /*
    	     * layout channels left to right in hyperpipe order. this
    	     * ordering is arbitrary and should be redefined for the
    	     * specific application.
    	     */
    	    pfChanShare(chan,
    		    pfGetChanShare() | PFCHAN_VIEWPORT |
    		    PFCHAN_SWAPBUFFERS | PFCHAN_SWAPBUFFERS_HW);
    	    pfMakeSimpleChan(chan, 45);
    	    pfChanAutoAspect(chan, PFFRUST_CALC_VERT);

    	    xyz[0] = xyz[1] = xyz[2] = 0;
    	    hpr[0] = (((numNet-1)*.5f)-i)*45.f;
    	    hpr[1] = hpr[2] = 0;
    	    pfChanViewOffsets(chan, xyz, hpr);
    	    pfChanNearFar(.000001, 100000);

    	    /*
    	     * void
    	     * drawFunc(pfChannel* chan, void* notUsed)
    	     * {
    	     *     pfClearChan(chan);
    	     *     pfDraw();
    	     * }
    	     */
    	    pfChanTravFunc(PFTRAV_DRAW, drawFunc);
    	    if (i == 0)
    		       masterChan = chan;
    	    else
           		pfAttachChan(masterChan, chan);

    	    /* bump to the first pipe of the next hyperpipe */
    	    pipeIdx += pfGetHyperpipe(pipe);
    	}

    	/*
    	 * the next step is to construct the scene, attach it to
    	 * masterChan and start the main loop. this bit of code
    	 * is not included here since it follows other demonstration
    	 * applications included elsewhere in the Programmer's Guide.
    	 */

### Configuring pfPipeWindows and pfChannels

The pfPipes grouped into a pfHyperpipe are indexed; the first pfPipe is pfPipe(0) and it is referred to as the master pfPipe. Most actions taken on the hyperpipe group are effected through this pfPipe; for example, all objects, such as pfPipeWindows and pfC hannels, are attached to the master pfPipe. OpenGL Performer automatically clones all objects, except pfChannels, across all of the pfPipes in the pfHyperpipe, as shown in Figure 14-4.

Figure 14-4. Attaching Objects to the Master pfPipe

When constructing pfPipeWindows or pfChannels, the pfPipe argument should be the master pfPipe. OpenGL Performer ensures that the constructed objects are cloned (pfPipeWindows) or attached (pfChannels) as needed to the other pfPipes in the hyperpipe group.

With the exception of certain attributes, detailed in Table 14-1, OpenGL Performer propagates attribute updates to the cloned pfPipeWindows when they occur. The following is a list of pfPipeWindow functions for which the attributes do not propagate.

Table 14-1. pfPipeWindow Functions That Do Not Propagate

C Function | C++ Member Function
--- | ---
pfPWinSwapBarrier() | setSwapBarrier()
pfPWinWSConnectionName() | setWSConnectionName()
pfPWinOverlayWin() | setOverlayWin()
pfPWinStatsWin() | setStatsWin()
pfPWinScreen() | setScreen()
pfPWinWSWindow() | setWSWindow()
pfPWinWSDrawable() | setWSDrawable()
pfPWinFBConfigData() | setFBConfigData()
pfPWinFBConfigAttrs() | setFBConfigAttrs()
pfPWinFBConfig() | setFBConfig()
pfPWinFBConfigId() | setFBConfigId()
pfPWinGLCxt() | setGLCxt()
pfPWinList() | setWinList()
pfPWinPVChan() | setPVChan()
pfPWinAddPVChan() | addPVChan()
pfPWinRemovePVChan() | removePVChan()
pfPWinRemovePVChanIndex() | removePVChanIndex()
pfBindPWinPVChans() | bindPVChans()
pfUnbindPWinPVChans() | unbindPVChans()
pfSelectPWin() | select()
pfAttachPWinWin() | attachWin()
pfDetachPWinWin() | detachWin()
pfAttachPWin() | attach()
pfAttachPWinSwapGroup() | attachSwapGroup()
pfAttachPWinWinSwapGroup() | attachWinSwapGroup()
pfDetachPWinSwapGroup() | detachSwapGroup()
pfChoosePWinFBConfig() | chooseFBConfig()

When using any of the preceding interfaces within an application, set the appropriate attribute in the cloned pfPipeWindow.

#### Clones

Clones are identified by an index value. The index of a clone matches that of the master pfPipeWindow. This index is used to retrieve the clone pfPipeWindow from the other pfPipes in the hyperpipe group. Example 14-4 sets the FBConfigAttrs for each of the pfPipeWindows in the first hyperpipe group.

Example 14-4. Set FBConfigAttrs for Each pfPipeWindow

    static int attr[] = {
    	    GLX_RGBA,
    	    GLX_DOUBLEBUFFER,
    	    GLX_LEVEL, 0,
    	    GLX_RED_SIZE, 8,
    	    GLX_GREEN_SIZE, 8,
    	    GLX_BLUE_SIZE, 8,
    	    GLX_ALPHA_SIZE, 8,
    	    GLX_DEPTH_SIZE, 16,
    	    GLX_STENCIL_SIZE, 0,
    	    GLX_ACCUM_RED_SIZE, 0,
    	    GLX_SAMPLE_BUFFERS_SGIS, 1,
    	    GLX_SAMPLES_SGIS, 4,
    	    None
    	};

    	int numHyper = pfGetHyperpipe(pfGetPipe(0));
    	for (i=0; i<numHyper; i++) {
    	    /* get the first pfPipeWindow on pfPipe */
    	    pfPipeWindow* pwin = pfGetPipePWin(pfGetPipe(i), 0);
    	    pfPipeFBConfigAttrs(pwin, attr);
    }

The current API has no support for directly querying the pfPipeWindow index within the pfPipe. The only mechanism to determine an index value is to track it in the application or search the pfPipeWindow list of the pfPipe. Example 14-5 performs such a search.

Example 14-5. Search the pfPipeWindow List of the pfPipe

    /* search the master pfPipe pipe for the pfPipeWindow in pwin */
    	int pwinIdx;
    	int numPWins = pfGetPipeNumPWins(pipe);
    	for (i=0; i<numPWins; i++)
    	    if (pfGetPipePWin(pipe) == pwin) break;
    	if (i == numPWins)
    	    pfNotify(PFNFY_FATAL, PFNFY_PRINT, "oops!");
    	pwinIdx = i;

#### Synchronization

When working with pfPipeWindows, it is possible for some updates to occur within the DRAW process. For this release (and possibly future releases) of OpenGL Performer, these updates are not automatically propagated to the clone pfPipeWindows. It is the responsibility of the application to ensure that the appropriate attributes are propagated or that similar actions occur on the clones.

The CULL and DRAW stages of different pfPipes within a hyperpipe group can run in parallel. For this reason, applications that assume a fixed pfChannel to pfPipe relationship or maintain global configuration data associated with a pfChannel that is updated in either the CULL or DRAW stages may fail. It is currently impossible (or at least very difficult) to transmit information from the CULL or DRAW stages of one pfPipe to another CULL or DRAW stage of another pfPipe within a hyperpipe group. All changes should be affected by the APP stage.

### Programming with Hyperpipes

Programming with hyperpipes, as described in the preceding sections, generally involves the following steps:

1. Configure the hyperpipe either on the fly or using a configuration file.

2. Map screens to hyperpipes, if necessary.

3. Allocate pfPipeWindow and pfChannels:

   - Create one pfPipeWindow for each pfHyperpipe.

   - Attach a pfPipeWindow to the master pfPipe.

   - Create a pfChannel for each pfHyperpipe.

4. Start the main loop (pfFrame()...pfSync()).

There are two additional requirements for DPLEX:

- You cannot use single buffer visuals.

  The DPLEX option uses the glXSwapBuffers() call as an indication to switch the multiplexer. This logic is bypassed for single buffered visuals.

- glXSwapBuffers() and pfSwapWinBuffers() functions must not be invoked outside of the internal draw synchronization logic.

  Because the pfuDownloadTexList() function with the style parameter set to PFUTEX_SHOW calls glXSwapBuffers(), this feature must be disabled. (Simply set the style parameter to PFUTEX_APPLY).

  Also, the Perfly application displays a message at startup which also swaps the buffers. Again, this function must be disabled when using hyperpipe groups. The version of Perfly that ships with `performer_demo` correctly disables these features.

Each pfPipe software rendering pipeline runs at a fraction of the target frame rate as defined by pfFrameRate(). The fraction is 1/(number of pipes in hyperpipe group). For example, if there are two pfPipes in the pfHyperpipe, each pfPipe runs at one half of the pfFrameRate(). Although the CULL and DRAW stages run at a slower rate, the APP stage must run at the target frame rate.

## Using OpenGL Performer with an SGI Scalable Graphics Compositor

This section gives a brief overview of the SGI Scalable Graphics Compositor and how to use it with OpenGL Performer. For more information on the compositor, including the details of the hardware setup, see the document SGI InfinitePerformance: Scalable Graphics Compositor User's Guide.

| Note: The compositor is currently supported by InfinitePerformance graphics systems only.

This section contains the following subsections:

- “How the Compositor Functions”

- “Using the pfCompositor Class”

- “Load Balancing”

### How the Compositor Functions

The compositor receives two to four input signals and outputs a single signal either in analog or digital format. Hence, it can handle spatial composition of four inputs which enables multiple pipes to contribute to a single output. Four different composition schemes are available:

- Vertical stripes

- Horizontal stripes

- 2D tiles

- Cut-ins

Figure 14-5 illustrates the various hardware composition schemes.

Figure 14-5. Hardware Composition Schemes

The following items are noteworthy regarding the compositor's capabilities:

- For every output pixel, the compositor averages all values from all the pipes. Among other things, this provides applications with the means to do full-scene antialiasing (FSAA) in hardware.

- Stereo through the compositor and cut-ins are not supported in OpenGL Performer 3.0.

  | Note: For more information on the current limitations and anomalies associated with the use of the SGI Scalable Graphics Compositor, refer to the hardware documentation.

### Using the pfCompositor Class

The pfCompositor class provides methods for controlling a single compositor or a heirarchy of compositors. The class provides methods for setting the subdivision of the inputs for the compositor to use. The subdivision is set by specifying the viewports and frusta for each input.

The pfCompositor class also allows for automatically setting the subdivision based on the load of each pfPipe providing an input. The subdivision pfCompositor performs can be controlled by setting the subdivision mode. By default, automatic load balancing is enabled and the subdivision mode is a 2x2 arrangement. Figure 14-6 illustrates some decomposition modes available for pfCompositor.

Figure 14-6. Decomposition Modes for pfCompositor

A pfCompositor requires a pfLoadBalance class for doing the load balancing of each of its children. The pfLoadBalance class determines the resulting workload for each pipe. The behavior can be customized by subclassing pfLoadBalance and overriding the appropriate methods. A pfCompositor can use a customized pfLoadBalance class specified using the pfCompositorLoadBalancer() function. If a load balancer is not specified, one will automatically be created and used. See the `pfLoadBalance` man page for more information on pfLoadBalance.

Antialiasing is supported as another type of subdivision mode and is enabled with the function pfCompositorMode().

Table 14-2 describes the functions for managing compositors.

Table 14-2. Functions for Managing Compositors

Function | Description
--- | ---
pfCompositorAddChild() | Adds a pipe (specified by pipe number) to the heirarchy.
pfCompositorReconfig() | Tells the compositor to look at its child pipes and their channels and configure itself to manage them. This function is required and should be called once all the pipes and channels have been created.
pfCompositorPerspective() pfCompositorOrtho() | Sets the frustum of the indexed channel that this pfCompositor manages. The default is a perspective frustum with `left`, `right`, `bottom`, and `top` set to `–1`, `1`, `–1`, and `1`, respectively.
pfGetCompositorFrustum() | Returns the `left`, `right`,`bottom`, and `top` values of the frustum that was set by pfCompositorPerspective() or pfCompositorOrtho().
pfCompositorNearFar() | Sets the near and far planes for the indexed channel that the compositor manages.
pfGetCompositorNearFar() | Returns the near and far planes for the indexed channel that the compositor manages.
pfGetCompositorFrustType() | Returns the type of frustum currently in use. The return value can be PF_PERSPECTIVE or PF_ORTHO.
pfCompositorViewport() | Sets the viewport (`left`,`right`, `bottom`, `top`) that this pfCompositor will encompass. The default viewport is (`0`, `1`, `0`, `1`).
pfGetCompositorViewport() | Returns the `left`, `right`, `bottom`, and `top` values that were set by pfCompositorViewport().
pfCompositorChannelClipped() | Enables or disables clipping for a specific channel. This can be useful when you want clipping enabled on all channels except one; for example, the graphical user interface (GUI) channel of Perfly has clipping disabled while all others are subjected to clipping.
pfGetCompositorChannelClipped() | Returns the value set by pfCompositorChannelClipped().
pfCompositorVal() | Accepts the values PFLOAD_COEFF and PFCOMP_CLIPPING. PFLOAD_COEFF passes the value to the pfLoadBalance class. This coefficient determines how quickly the balancer transitions from the current state to the desired balanced state. PFCOMP_CLIPPING enables and disables channel viewport and frustum clipping to the composited area. Clipping is enabled by default.
pfGetCompositorVal() | Returns the values set by pfCompositorVal().
pfCompositorMode() | Accepts PFCOMP_TYPE and PFLOAD_BALANCE as arguments. PFCOMP_TYPE can be one of the following: PFCOMP_2x1HORIZ PFCOMP_2x1VERT PFCOMP_2x2 PFCOMP_4x1HORIZ PFCOMP_4x1VERT PFCOMP_4xAA PFCOMP_TYPE specifies the type of load balancing and subdivision the compositor should be using. PFLOAD_BALANCE can be either PF_ON or PF_OFF. PFLOAD_BALANCE specifies whether the pfCompositor should be performing automatic load balancing. If it is PF_OFF, no load balancing computations are made and no glXHyperpipe calls are made unless specified otherwise with pfCompositorChildViewport() or pfCompositorChildSubdivision().
pfGetCompositorMode() | Returns the values set by pfCompositorMode().
pfCompositorLoadBalancer() | Allows the pfLoadBalance class to be used by the pfCompositor. The pfCompositorLoadBalancer() call must happen before pfConfig().
pfGetCompositorLoadBalancer() | Returns the value set by pfCompositorLoadBalancer().
pfCompositorChildViewport() | Allows you to manually set the viewport that the indexed child of this pfCompositor should use. This method will also calculate the necessary subdivision information from the viewport information and send the subdivision information to the compositor hardware.
pfGetCompositorChildViewport() | Returns the viewport information for the indexed child of this pfCompositor.
pfCompositorChildFrustum() | Allows you to manually set the frustum that the indexed child of this pfCompositor should use.
pfGetCompositorChildFrustum() | Returns the frustum information for the indexed child of this pfCompositor.
pfCompositorChildSubdivision() | Allows you to manually set the subdivision that the indexed child of this pfCompositor should use. This method will also calculate the necessary channel viewport information from the subdivision information and send the viewport information to the appropriate pfChannel.
pfGetCompositorChildSubdivision() | Returns the subdivision information for the indexed child of this pfCompositor.
pfCompositorMasterPipe() | Specifies a pipe as the master pipe. All changes to the channels on the master pipe will be appropriately propagated to the other channels that pfCompositor manages. If a master pipe is not specified, pfCompositor uses the pipe specified as its first child.

### Load Balancing

The pfLoadBalance class encapsulates an algorithm for distributing any incoming workload among multiple servants. The class maintains history information about the following:

- Workload distribution among servants

- The time it takes each servant to complete its workload allocation

Based on this information, pfLoadBalance computes a new workload distribution to minimize the time needed for completing the input workload.

The default implementation keeps track of a single frame of history. Subclasses of pfLoadBalance could track history information through multiple frames. Using multiple frames, the subclasses can compute the rate of change in the work allocation to workload ratio.

The default implementation tries to smooth the transition as workload allocation changes for each servant. Subclasses of pfLoadBalance could also change the smoothing factors to match the rate of change in workload between consecutive frames.

The following psuedo code illustrates a typical use of pfLoadBalance from pfCompositor:

    // Set the number of servants to balance this frame.

    pfLoadBalanceNumActive()

    foreach child

    // Tell the load balancer what the current load is on the child.

       pfLoadBalanceLoad()

    end

    balance()

    foreach child

    // Get the percentage of the work this child should perform.

        pfGetLoadBalanceWork()

    end

Table 14-3 describes the functions you can use to manage load balancing.

Table 14-3. Load Balancing Functions

Function | Description
--- | ---
pfLoadBalanceLoad() | Sets the load on a specified servant. This is the load to be used during the load balancing calculations in pfLoadBalanceBalance().
pfGetLoadBalanceLoad() | Returns the load that was set by pfLoadBalanceLoad() for a specified servant.
pfLoadBalanceBalance() | Takes the new load values specified by pfLoadBalanceLoad() and figures out the desired workload for each servant. Then pfLoadBalanceBalance() takes the load coefficient (PFLOAD_COEFF) into account while calculating the percentage of the workload this servant should perform.
pfGetLoadBalanceWork() | Returns the percentage of work (a value from `0` to `1`) to be done by the specified servant.
pfLoadBalanceVal() | Accepts PFLOAD_COEFF as an argument, the value of which determines how fast or how slow the balancer transitions from the current workload to the desired workload.
pfGetLoadBalanceVal() | Returns the value set by pfLoadBalanceVal().
pfLoadBalanceNumActive() | Sets the number of servants to balance.
pfGetLoadBalanceNumActive() | Returns the value set by pfLoadBalanceNumActive().

## Using OpenGL Performer with GPUs

GPUs are used widely in commodity graphics hardware and also in graphics platforms like SGI Onyx4 systems. OpenGL Performer supports GPU programming through the use vertex programs and fragment programs.

Vertex programs are used by the GPU to modify various parameters of each vertex. Similarly, fragment programs are used to modify the color and depth value of each fragment (pixel) as it is being rendered. A description of vertex and fragment program instruction sets is beyond the scope of this guide. You can find a description of these instruction sets in the OpenGL extension registry at http://oss.sgi.com/projects/ogl-sample/registry/ under `GL_ARB_vertex_program` and `GL_ARB_fragment_program`.

This chapter describes how you can use GPU programs in OpenGL Performer in the following sections:

- “The pfGProgram Class”

- “The pfGProgramParms Class”

- “The pfVertexProgram and pfFragmentProgram Classes”

### The pfGProgram Class

OpenGL Performer implements GPU programming through the general class pfGProgram. This class allows you to set GPU programs, vertex programs and fragment programs.

The function pfNewGProgram() creates and returns a handle to a pfGProgram. The parameter `arena` specifies a malloc() arena out of which the pfGProgram is allocated or the value `NULL` specifies allocation off the process heap. pfGPrograms can be deleted with pfDelete().

The call new(`arena`) allocates a pfGProgram from the specified memory arena or from the heap if arena is NULL. The new() call allocates a pfGProgram from the default memory arena (see function pfGetSharedArena() in the `pfSharedMem`(3pf) man page). Like other pfObjects, pfGPrograms cannot be automatically created statically on the stack or in arrays. pfGPrograms should be deleted with pfDelete() rather than the delete operator.

The function pfGetGProgramClassType() returns the `pfType*` for the class pfGProgram. The `pfType*` returned by pfGetGProgramClassType() is the same as the `pfType*` returned by invoking pfGetType(), the virtual function getType() on any instance of class pfGProgram. Because OpenGL Performer allows subclassing of built-in types, when decisions are made based on the type of an object, it is usually better to use pfIsOfType(), the member function isOfType(), to test if an object is of a type derived from an OpenGL Performer type rather than to test for strict equality of the types.

A pfGProgram is a sequence of assembly-like instructions. You can specify the instructions in two ways:

- In a string with new line characters separating instructions

- In a text file

If the program is specified in a string, you use the function pfGProgramProgram() or pfGProgramProgramLen(). The first parameter of each is the string defining the program. In the second function, you can specify the length when you want to load only part of the string.

If the program is loaded from a text file, you use the function pfGProgramLoadProgram().

Using the function pfGProgramApplypfGProgram(), you can apply the pfGProgram but only in the draw process. Once the pfGProgram has been applied, you can query its state using the following functions:

pfGetGProgramProgramLength() |  | Returns the number of instructions of the program.
pfGetGProgramNativeProgramLength() |  | Returns the number of instructions used by the specific GPU.
pfGProgramIsValidpfGProgram() |  | Returns `1` if the program has been successfully loaded into the GPU.

You should not use a pfGProgram directly but one of its subclasses. There are two classes of specific GPU programs subclassed from pfGProgram: pfVertexProgram and pfFragmentProgram. A pfVertexProgram or a pfFragmentProgram is set in a pfGeoState and is enabled in a pfGeoState. The user parameters for the vertex and fragment programs can be defined using the class pfGProgramParams, which is described in the following section.

For sample code, see the following file:

`/usr/share/Performer/src/pguide/libpf/C++/gprogram.C
` (IRIX and Linux)
`%PFROOT\Src\pguide\libpf\C++\gprogram.cxx
` (Microsoft Windows)

### The pfGProgramParms Class

The pfGProgramParms is a class that is used to store parameters of GPU programs, specifically of pfVertexPrograms and pfFragmentPrograms. The function pfNewGProgramParms() creates and returns a handle to a pfGProgramParms. The parameter `arena` specifies a malloc() arena out of which the pfGProgram is allocated or NULL for allocation off the process heap. You can delete pfGPrograms with pfDelete().

A pfGProgramParms is a set of indexed quadruples of floating point values that are used as parameters for vertex and fragment programs. You can specify the values using the function pfGPParamsParameters() which has the following syntax:

    pfGPParamsParameters(pfGProgramParms* gpparams, int index, int type, int count, void* ptr);

The parameter index specifies the first index of the specified parameters (the index by which the parameters are accessed in the GPU program) and the the parameter count specifies how many indices will be set.

The parameter type may be one of the following:

`PFGP_FRAGMENT_LOCAL` |  | Local parameters of a single fragment program.
`PFGP_FRAGMENT_ENV` |  | Environment parameters. Shared between all fragment programs.
`PFGP_VERTEX_LOCAL` |  | Environment parameters of a single vertex program.
`PFGP_VERTEX_ENV` |  | Environment parameters. Shared between all vertex programs.

The pointer `ptr` points to the parameter data.

Using the following functions, you can query the existing parameters in a pfGProgramParms:

pfGetGPParamsNumParameters() |  | Returns the number of parameters.
pfGetGPParamsParameters() |  | Returns the parameters in the order of their specification.
pfGetGPParamsParametersByIndex() |  | Returns the parameters by the index by which the parameters are accessed.

You can apply the pfGProgramParms using pfGProgramParamsApply() but only in the draw process. If you modify the pfGProgramParms after they have been applied, you must call pfGProgramParamsUpdate() for the change to take effect.

A pfGProgramParms is set in a pfGeoState. Each pfGeoState can have one pfGProgramParms of each of the four types, two for the pfVertexProgram associated with the pfGeoState and two for the pfFragmentProgram.

### The pfVertexProgram and pfFragmentProgram Classes

The pfVertexProgram and pfFragmentProgram classes are derived from the class pfGProgram. These subclasses do not add any new methods. A vertex program or a fragment program is used by the GPU to modify various parameters of each vertex or fragment (pixel), respectively. The GPU allows you to specify a sequence of floating-point 4-component operations that are executed for each vertex or fragment. These operations transform an input set of per-vertex or per-fragment parameters to another set of per-vertex or per-fragment parameters.

A vertex program replaces the standard OpenGL set of lighting and texture coordinate generation modes. Consequently, the vertex program must take care of the basic transformation of vertex coordinates to the screen coordinates, the generation of texture coordinates, and the application of the lighting equation. This programming model allows you to modify the position of each vertex, producing, for example, a displacement mapping.

Similar to a vertex program, a fragment program replaces the standard OpenGL set of texture and fog application modes. The fragment program has to access the textures and to modulate the resulting color according to the fog equation, if necessary. This programming model allows you to modify the resulting color and depth of each pixel, making it possible, for example, to apply a complex per-pixel shading.

You can find the instruction sets for vertex and fragment programs in the OpenGL extension registry at http://oss.sgi.com/projects/ogl-sample/registry/ under the `GL_ARB_vertex_program`.

As subclasses of pfGProgram, pfVertexPrograms and pfFragmentPrograms can use the management methods of a pfGProgram to set, load, and apply programs. Section “The pfGProgram Class” describe these methods.

You set and enable pfVertexPrograms and pfFragmentPrograms in a pfGeoState. As described in section “The pfGProgramParms Class”, the user parameters for GPU programs can be defined using the pfGProgramParms class.

For sample code, see the following file:

`/usr/share/Performer/src/pguide/libpf/C++/gprogram.C
` (IRIX and Linux)
`%PFROOT\Src\pguide\libpf\C++\gprogram.cxx
` (Microsoft Windows)

Prev | Table of Contents | Next
Chapter 13. Shaders |  | Chapter 15. ClipTextures