This chapter describes how you can use compounds (or conversely, decomposition) to scale the performance of your graphics application. Decomposition allows you to use multiple pipes to render frames that would normally be rendered by a single pipe.
This chapter has the following sections:
To achieve greater application performance, MPK allows you to decompose a global rendering task into smaller tasks and to assign the smaller tasks to individual pipes. The task division requires a decomposition scheme. In general, a decomposition scheme sends a scene to render to each pipe, gets back rendered images from each pipe for further composition, and then renders the final image. An exception is cull decomposition, where the cull operation is parallelized with the draw operation.
Figure 3-1 illustrates the role of source and destination channels in scalable rendering.
To build a compound, you must create a MPKCompound data structure. The manual SGI OpenGL Multipipe SDK User's Guide describes the syntax of the MPKCompound data structures for your configuration file. This section describes how you build them logically.
Generally, to create a compound, you need to do the following:
Choose a decomposition scheme, which divides the global rendering task into smaller tasks.
Distribute the rendering of the smaller tasks to the source pipes for parallel processing.
Designate a destination channel for the reassembly of the final, coherent image.
The destination channel is usually one of the source channels. To achieve optimal performances, you would usually have one channel per pipe.
This chapter focuses on the three tasks just cited. Optionally, you can also do the following:
The section “Stereo-Selective Compounds” describes how you control whether your compound is used depending on the stereo mode of the application. For more information on the first two optional tasks, see the SGI OpenGL Multipipe SDK User's Guide for the description of the mode and format fields of the MPKCompound data structure. “Hardware Compositing” in Chapter 5 describes the integration of scalable graphics with MPK. The section “Automatic Load Balancing for Compounds” describes how MPK balances the rendering for certain compound modes.
MPK provides several decomposition schemes and the following subsections describe these schemes:
Each decomposition mode improves performance or graphics quality, but the performance gain depends on the application type and the nature of the performance bottleneck. Four factors are important in choosing the decomposition scheme:
In frame decomposition, a frame or view is divided into regions, which are, in turn, assigned to individual source pipes for rendering. Based on the following perspectives, there are several approaches to dividing the frame into regions:
Screen topology (screen decomposition)
Scene graph primitives (database decomposition)
Eye view (eye decomposition)
Each approach yields a different flavor of frame decomposition.
In screen decomposition (also referred to as 2D decomposition), each pipe renders a part of the screen area. Assembling side-to-side each image part constitutes the final rendering. This type of decomposition is used when the intrinsic pixel fill or geometry capacity of each pipe slows down the application. The scalability depends on the balancing of the workloads. The model to display needs to be uniformly distributed across the screen to accommodate a good balancing and, thus, scalability. The graphics I/O is relatively low, because the traveling source images are small.
Figure 3-2 illustrates screen decomposition.
Example 3-1 shows the configuration file specifications for the screen decomposition illustrated in Figure 3-2.
Example 3-1. 2D Compound in a Configuration File
compound {
mode [2D]
channel "destination"
# The top left of "destination" image will be
# rendered on "source0"...
region {
viewport [ 0., .5, .5, .5 ]
channel "source0"
}
# The top right of "destination" image will be
# rendered on "source1"...
region {
viewport [ .5, .5, .5, .5]
channel "source1"
}
# The bottom left of "destination" image will be
# rendered on "source2"...
region {
viewport [ 0., 0., .5, .5 ]
channel "source2"
}
# ... while "destination" itself takes care of
# the bottom right
region {
viewport [ .5, 0., .5, .5 ]
channel "destination"
}
}
|
Figure 3-3 shows how the configuration in Example 3-1 executes.
By adding the mode flag ASYNC, the frame rate can be improved because of the lower consumption of graphics I/O, as shown in Figure 3-4. Note that the ASYNC configuration has a latency of one.
In database (DB) decomposition, the scene is rendered in parallel by dividing it among the different graphics pipes. Each pipe renders its share of the scene to generate partial images. These images are then composited by MPK to generate the final image in the destination channel. During composition, the application can use depth testing and/or alpha blending to achieve the desired effect. Database decomposition allows you to scale both the geometry and the pixel fill performance of the system. For some applications, such as volume rendering, it also scales the texture memory capacity of the system by the number of pipes.
Figure 3-5 demonstrates the use of database decomposition in volume rendering. The volume data is divided equally among the four pipes and the partial images are composited on the destination channel. In this case, the destination channel (top left portion of the figure) is also contributing to the rendering as a source channel.
Example 3-2 shows the configuration file specifications for the database decomposition illustrated in Figure 3-5.
Example 3-2. DB Compound in a Configuration File
compound {
mode [ DB ]
format [ COLOR DEPTH ]
channel “channel”
region {
range [ 0., .25 ]
channel “buffer0”
}
region {
range [ .25, .5 ]
channel “buffer1”
}
region {
range [ .5, .75 ]
channel “buffer3”
}
region {
range [ .75, 1. ]
channel “channel”
}
}
|
Figure 3-6 shows how the confiuration in Example 3-2 executes.
In order to support this decomposition scheme, the application has to use the range field to draw the correct part of the database in the MPKChannel's update draw callback. The range can be acquired using mpkChannelGetRange().
The default DB assembly uses the following pseudo code to assemble the frames using Z-based recomposition:
if ASYNC flag is set
draw depth buffer of first input frame
draw color buffer of first input frame
end if
enable stencil test
for all remaining input frames
enable depth test
draw depth buffer and set stencil bit where depth-test passes
disable depth test
draw color buffer where stencil bit set
end for
|
Some applications, such as volume rendering, may want to use a different recomposition technique than MPK's default procedure. The later section “Compound-Specific Callbacks”explains how to use custom assemble-compound callbacks.
By adding the mode flag ASYNC, the frame rate can be improved because of the lower consumption of graphics I/O, as shown in Figure 3-7. Note that the ASYNC configuration has a latency of one.
Eye decomposition is well-suited for stereo or multiple-view rendering. Each pipe renders a particular view (left, right, mono). The final rendering depends on the type of display. As illustrated in Figure 3-8, if stereo is active, then each pipe view fills in the right or left buffer of the final rendering. This provides good load balancing and scalability, especially for stereo-view rendering, because the scene content remains similar during run time.
An EYE compound has no frame latency, unless the mode qualifier ASYNC has been specified and pixel transfer needs to occur, in which case the latency is 1.
The number of regions of an eye compound is not limited. If more than one region correspond to the same eye view, MPK uses the first specified region (for this eye) as source for the pixel transfer, if needed.
Example 3-3 shows the configuration file specifications for the eye decomposition illustrated in Figure 3-8.
Example 3-3. Eye Compound in a Configuration File
compound {
mode [ EYE STEREO ]
channel “channel”
region {
eye LEFT
channel “buffer”
}
region {
eye RIGHT
channel “channel”
}
}
|
Head-Mounted-Device (HMD) decomposition is very similar to that of eye decomposition, except that the head position actually specifies a new origin for the physical layout of the channels.
Example 3-4 shows a configuration file specification for an HMD decomposition:
Example 3-4. HMD Compound in a Configuration File
compound {
mode [HMD]
channel “destination”
region {
eye left
channel “source::left”
}
region {
eye right
channel “source::right”
}
}
|
If a destination channel is specified, then the frustum is inherited from the destination channel's wall or projection frustum specification; otherwise, the source channel's frustum specification will be used.
Figure 3-9 shows how the configurations for Example 3-3 and Example 3-4 execute.
By adding the mode flag ASYNC, the frame rate can be improved because of the lower consumption of graphics I/O, as shown in Figure 3-10. Note that the ASYNC configuration has a latency of one.
In contrast to frame decomposition, where the focus of load balancing is on dividing the frame into regions, temporal decomposition balances the workload by scheduling the work on each pipe in sync with that of the other pipes to produce a steady stream of rendered frames. The time scheduling rather than the frame division is the focus. There are two types of temporal decomposition: frame multiplexing and data streaming. The work done by each pipe largely distinguishes the two.
Frame multiplexing (also referred to as DPLEX decomposition) distributes entire frames to the source pipes over time for parallel processing. The first pipe begins rendering frame 1; a specified fraction of a frame later the second pipe begins rendering frame 2; another fraction of a frame later the third pipe begins rendering frame 3; and so on for all of the pipes.
Figure 3-11 illustrates frame multiplexing on a four-pipe system.
Frame multiplexing globally scales geometry and pixel fill performance, as the workload balance between pipes is intrinsically maintained. This scheme has an increased transport delay inherent to frame synchronization required across the pipes. It produces a latency of (pipes – 1) frames—that is, there will be a (pipes – 1) frames delay between a user input and the corresponding output frame.
Frame multiplexing can also be accelerated in hardware using the SGI Video Digital Multiplexer (DPLEX), which connects pipes together with a bus, thereby avoiding the image readbacks from the contributing pipes. The pipes are daisy-chained to achieve reduced latency. For more details, see “Hardware Compositing” in Chapter 5.
Example 3-5 shows the configuration file specifications for the screen decomposition illustrated in Figure 3-11.
Example 3-5. DPLEX Compound in a Configuration File
compound {
mode [ DPLEX ]
channel “channel”
region {
channel “dplex::0”
}
region {
channel “dplex::1”
}
region {
channel “dplex::2”
}
}
|
Figure 3-12 shows how the configuration in Example 3-5 executes.
By adding the mode flag ASYNC, the frame rate can be improved because of the lower consumption of graphics I/O, as shown in Figure 3-13. Note that the DPLEX ASYNC configuration has a latency of pipes frames while the DPLEX configuration has a lency of (pipes – 1) frames.
You can achieve full scalability—that is, scale by the number of pipes rather than by (pipes – 1)—using a DPLEX compound. To do so, you must specify the destination channel as a source channel also. To support this feature, the application has to be modified as explained in the following paragraph.
DPLEX operation implies that while one frame is being drawn, another one is copied and displayed on the destination window. In full-scale DPLEX operation, one of the source channels draws a frame while it has to simultaneously display another frame from a different source channel. Drawing into a pbuffer enables this channel to draw its frame into the pbuffer and to display the frame of another source channel in the X window at the same time. MPK needs to know when the drawing into the pbuffer is interruptible in order to composite into the normal source window. Therefore, the application should call mpkChannelSyncDPlex() whenever possible to switch the OpenGL context from the pbuffer to the X window—that is, outside of a glBegin() ... glEnd() sequence. Figure 3-14 shows this decomposition for a two-pipe full-scale DPLEX compound.
Example 3-6 shows a configuration file structured for full scalability using the DPLEX compound.
Example 3-6. DPLEX Compound Structured for Full Scalability
compound {
mode [ DPLEX ]
channel “channel”
region {
channel “channel”
}
region {
channel “buffer”
}
}
|
| Note: Full scalability using the DPLEX compound is supported only on InfiniteReality graphics systems. |
Data streaming (also referred to as 3D decomposition) is similar to database decomposition in that it allows the application to divide the scene among multiple pipes and then composite the partial results to give the final rendering. But, in this case, the composition is done using a series of successive compounds for each frame, as shown in Figure 3-15. For frame N+1, channel stream::1 draws the first quarter of the database, which is copied to channel stream::2 at the beginning of the next frame. During frame N+2, channel stream::2 draws the second quarter of the database on top while channel stream::1 starts a new frame. At frame N+4, the destination channel channel finishes drawing the last quarter and displays the frame started three time steps ago.
Like DPLEX decomposition, this scheme also has a latency of (pipes – 1) frames—that is, there will be a (pipes – 1) frames delay between a user input and the corresponding output frame. As shown in Figure 3-15, this latency is due to successive compounds at each frame. You must wait for (pipes – 1) frame computations before the final rendering is displayed. Each compound needs to read only one source image. Consequently, this keeps graphics I/O consumption low while performance scaling is achieved by pipelining the rendering in parallel across the pipes.
As shown in Example 3-7, the configuration file specification for a data streaming decomposition is similar to that for database decomposition.
Example 3-7. Data Streaming Compound (3D) in a Configuration File
compound {
mode [ 3D ]
format [ COLOR DEPTH ]
channel “channel”
region {
range [ .0 .25 ]
channel “stream::1”
}
region {
range [ .25 .5 ]
channel “stream::2”
}
region {
range [ .5 .75 ]
channel “stream::3”
}
region {
range [ .75 1. ]
channel “channel”
}
}
|
In order to support this decomposition scheme, the application has to use the range field to draw the correct part of the database in the MPKChannel's update draw callback. The range can be acquired using mpkChannelGetRange(). Some applications, such as volume rendering, may want to use a different recomposition technique than MPK's default procedure. The later section “Compound-Specific Callbacks”explains how to use custom assemble-compound callbacks.
Figure 3-16 shows how the configuration for Example 3-7 executes. Data streaming compounds always assemble before drawing; therefore, the ASYNC flag is ignored.
In pixel-based decomposition, a frame is rendered using a multipass approach where single passes are assigned to individual source pipes for rendering. Assembling each frame using accumulation techniques constitutes the final rendering. Accumulation of the frames can be achieved using one of the following techniques:
In order to use OpenGL accumulation, you must use an appropriate visual; otherwise, MPK uses blending.
MPK has implemented one scheme of pixel-based decomposition, a full-scene antialiasing (FSAA) compound. Each pipe renders the full scene from a slightly different viewpoint. The number of rendering passes of a FSAA compound is defined by its number of sources. Furthermore, every channel can thereby be used multiple times. This type of decomposition is used when the the resulting output quality has highest priority. The scalability and final rendering quality depends on the number of available pipes.
Example 3-8 shows an FSAA compound using the SGI Scalable Graphics Compositor:
Example 3-8. Four-Pipe 4x FSAA Compound Using the SGI Scalable Graphics Compositor
compound {
mode [ FSAA HW NOCOPY ]
channel “channel-0”
# The number of sources defines the FSAA mode
region {
channel “channel-0”
}
region {
channel “channel-1”
}
region {
channel “channel-2”
}
region {
channel “channel-3”
}
}
|
Figure 3-17 illustrates the advantage of using a 4x FSAA solution.
Example 3-9 shows how to use the same channel multiple times as a source channel to support multipass rendering in MPK on machines with only a few pipes.
Example 3-9. Multiple Use of a Single Channel in FSAA Decompostion
compound {
mode [ FSAA ]
channel “channel”
# The number of sources defines the FSAA mode
region {
channel “channel”
}
region {
channel “channel”
}
region {
channel “channel”
}
region {
channel “channel”
}
}
|
Cull decomposition is different from the other decomposition modes in that it does not decompose the rendering of a frame. Instead, it decomposes (parallels) the cull and draw operations. Naturally, the use of this feature in the configuration file requires the application also to support the cull decomposition mode (see Chapter 4, “Culling”).
In cull decomposition, the regions specify the operation to be executed by this region. Example 3-10 shows a cull decomposition for a single channel.
Example 3-10. A Simple Cull Decomposition
config
{
name “1-window”
pipe
{
window
{
name “MPK: simple”
viewport [ 0.25, 0.25, 0.5, 0.5 ]
channel
{
name “channel”
wall
{
bottom_left [ -.5, -.4, -1 ]
bottom_right [ .5, -.4, -1 ]
top_left [ -.5, .4, -1 ]
}
}
}
window
{
attributes { hints { drawable none } }
channel { name “cull” }
}
}
compound
{
mode [ CULL ]
channel “channel”
region { cull channel “cull” }
region { draw channel “channel” }
}
}
|
In this example, the channel cull executes the culling concurrently with the draw operation of channel channel. It is defined on a window that has no drawable, because it is not used for any draw operation. You can add the ASYNC flag to force the cull region to process data for the frame N, and the draw region to draw frame N–1. This introduces an additional frame of latency, but potentially increases performance.
The region operation can be the value cull, draw, or cull-draw. The default operation is cull-draw; that is, all source channels first execute the cull operation and then the draw operation.
If multiple regions of a cull compound execute the same operation (cull or draw) for the destination channel, they process the data in parallel and, therefore, may speed up the operation if enough resources are available. As a feature of the MPK implementation, the distribution of the cull and draw tasks is automatically load-balanced. For recomposition of multiple draw or cull-draw regions, MPK uses by default the same algorithm that is used for DB compounds.
| Note: Since culling is application-specific, you must determine (perhaps, from your application vendor) what MPK features and configurations are supported by your application. |
It is possible to further decompose the regions of a cull compound, as described in the following section, “Multilevel Decomposition”
MPK allows you to combine the various decomposition schemes to fix performance bottlenecks that differ in nature. For example, a combined solution can use a database and temporal decomposition scheme for optimizing performance (but it will have a limiting transport delay) or can use an eye and database decomposition scheme for stereo volume rendering.
Figure 3-18 shows a four-pipe solution using an eye and database decomposition scheme.
Example 3-11 shows the configuration file specifications for the multilevel decomposition illustrated in Figure 3-18.
Example 3-11. Multilevel Compound in a Configuration File
compound {
mode [ EYE ]
channel “right-front”
region {
eye LEFT
compound {
mode [ DB ]
channel “left-front”
region {
range [ 0., .5 ]
channel “left-back”
}
region {
range [ .5, 1. ]
channel “left-front”
}
}
}
region {
eye RIGHT
compound {
mode [ DB ]
channel “right-front”
region {
range [ 0., .5 ]
channel “right-back”
}
region {
range [ .5, 1. ]
channel “right-front”
}
}
}
}
|
In many instances, it will be desirable to control which compounds will be used by the application based on whether the application is running in stereo mode. MPK provides a mode parameter for this purpose. For instance, if the application is to run in stereo mode, you may want to use eye decomposition and when in mono mode, to use another type of decomposition. Example 3-12 illustrates this conditional use of compounds.
Example 3-12. Stereo-Selective Compounds
compound {
mode [ EYE STEREO ]
channel “channel”
region {
eye LEFT
channel “buffer”
}
region {
eye RIGHT
channel “channel”
}
}
compound {
mode [ 2D MONO ]
channel “channel”
region {
viewport [ 0., 0., 1., .5 ]
channel “buffer”
}
region {
viewport [ 0., .5, 1., .5 ]
channel “channel”
}
}
|
The MONO and STEREO flags allow you to specify different channel decompositions depending on the current configuration mode. This is especially useful for eye decomposition. In this example, when the destination channel is in stereo mode, MPK uses the eye decomposition. When the destination channel is in mono mode, MPK uses the 2D decomposition.
Achieving an ideal decomposition among the children of a compound can be difficult, since the workload per child often changes on a per-frame basis. To address this problem, MPK provides automatic load balancing for 2D, DB, and 3D compounds.
This section describes the following topics:
Figure 3-19 contrasts dynamic and static load balancing for a 2D compound using volview. Volume rendering is bound by fill rate; therefore, the load balancing can adjust the compound's region so that each pipe has approximately the same amount of volume to rasterize. When using static tiling, one pipe may have to render the whole volume as it is moved around. Since the slowest child dictates overall performance, the frame rate is better, in this case, when using load balancing.
Using the rendering times for each child, MPK computes a new viewport or range each frame. This approach needs the following conditions to work properly:
| Condition | Description | |
| Low latency | A new workload can only be computed after all children have drawn. Therefore, the higher the latency, the higher the difference will be between the frame which is used to compute the new balance and the frame for which the balance is computed. Logically, high latency is counterproductive in achieving proper load balancing. | |
| Frame consistency | Since the new viewport or range is computed based on the last finished frame but applied to the next frame, the two frames should be similar. This is true for most applications. | |
| Scalable compound mode | The chosen decomposition mode has to solve the application's bottleneck. For example, load balancing a 2D compound for a geometry-limited application will fail, unless this application uses view-frustum culling. | |
| Imbalance in decomposition | If the decomposition is already well-balanced—for example, for a DB compound—the static compound may provide a better frame rate. |
In the configuration file, you can use the ADAPTIVE mode flag for a compound to enable load balancing. This mode flag can be used for 2D, DB, and 3D compounds. 2D compounds will use tiles, while DB and 3D compounds will adapt the z-axis range to decompose the rendering. The next section describes how you use a split-axis method to determine tiling schemes and z-axis splits.
| Note: If you do not provide a tiling scheme (or z-axis split) for ADAPTIVE mode, MPK creates one. |
As the name implies, a split-axis method uses splits on the Cartesian coordinate axes to determine tiling schemes (or z-axis range splits). As noted in the preceding section, the tiling schemes are specific to 2D compounds and z-axis splits, to DB and 3D compounds.
You specify a split in the configuration file by using the split field of the compound data structure. The SGI OpenGL Multipipe SDK User's Guide describes the formal syntax for the split field. This section provides several examples of how to use the field. Example 3-13 shows a tiling scheme for a 2D compound with four regions (source channels) defined.
Note the following syntax items and other restrictions:
The split value is a string.
MPK uses the following operators in the string to denote the axis-specific splits:
| | | Splits the x axis. | |
| – | Splits the y axis. | |
| / | Splits the z axis. |
The operators | and – can be used only with 2D compounds and /, only with DB and 3D compounds.
Integers represent the regions in the compound data structure in the order of declaration. All regions declared in the compound must appear in the split string.
A set of brackets must enclose a split, which can be nested in another split (as shown in Example 3-13).
Figure 3-20 illustrates the tiling scheme specified in Example 3-13.
Example 3-14 , like Example 3-13, shows a tiling scheme for a 2D compound with four regions, but this time the primary split is on the x axis.
Figure 3-21 illustrates the tiling scheme specified in Example 3-14.
If you do not specify a split, MPK will automatically create one.
There are no hard and fast rules for choosing the correct decomposition scheme, but the following are some general guidelines to aid you in selecting a reasonable scheme for your environment:
| Mode | Recommended Use | |||
| 2D | Use this scheme if your application is fill-limited.You can also scale geometry performance and texture memory if your application is using view-frustum culling techniques. | |||
| 3D | Use this scheme where you would normally use the DB scheme but where you experience scalability problems caused by a graphics I/O bottleneck on the destination pipe. For 3D decomposition, the graphics I/O per pipe is constant when changing the number of contributing pipes. Unlike the DB scheme, however, adding pipes to a 3D compound increases latency. | |||
| DB | Use this scheme when your application's frame rendering can be sequenced into equally consuming phases. This requires the application to divide your scene into multiple components and then to composite them correctly. Scalability here can be either on fill, geometry, or graphics resources (texture) depending on the application. | |||
| FSAA | Use this scheme if graphics quality is a primary concern. | |||
| EYE | Use this scheme for stereo viewing. | |||
| DPLEX | Use this scheme for general load balancing where the application maintains a reasonably steady frame rate.
|
These are very high-level guidelines that may very well overlap. As noted in the section “Multilevel Decomposition”, you can combine the various decomposition modes to fix different performance bottlenecks.
This section describes how to customize the compound processing using the callbacks provided by the MPKCompound data structure.
This section has the following subsections:
The adaptive readback interface enables the specification of a subviewport of a compound's input channel to be read back, transported, and assembled during compound operations.
This subviewport usually corresponds to the portion of the channel that was drawn during the last update-channel draw callback and, therefore, enables the optimization of pixel transfers for this channel.
The default mpkCompoundReadOutputFrame() callback basically reads the full channel's viewport by using mpkChannelReadFrame() on the handle returned by mpkCompoundGetOutputFrame(). A customized version of this callback, as shown in Example 3-15, typically uses only the channel subportion transported by user data.
Example 3-15. A Custom Read-Output Compound Callback
void readOutputFrame( MPKCompound *compound )
{
MPKChannel *c;
ChannelData *channelData;
MPKFrame *frame;
c = mpkCompoundGetChannel( compound );
frame = mpkCompoundGetOutputFrame( compound );
// get the effectively drawn region computed in updateChannel
channelData = ( ChannelData* )mpkChannelGetUserData( c );
mpkChannelReadFrame( c, frame, channelData->region );
}
|
The example flip.adaptiveRB computes the screen area covered by the scene in the update-channel draw callback in order to optimize the frame transport using the read-output callback. The read-output compound callback for the MPKConfig can be set using mpkConfigSetCompoundReadOutputCB().
The purpose of the custom MPKCompound interface is to allow customization of the MPKCompound pre- and post-assembly passes.
Upon the mpkConfigFrame() invocation, each MPKWindow thread traverses the configuration's compound tree[s] and updates each compound whose channel belongs to the window. If the compound is a leaf node, then MPK invokes the user-specified clear and update callbacks on the associated [source] channel. Otherwise, if the compound is not a leaf node, then MPK assembles the frames output from the compound children into its associated [destination] channel.
This traversal actually occurs in several passes. Figure 3-22 shows the update of one window as in Figure 2-4, but here Figure 3-22 shows the necessary callbacks for compound processing. A complete update for one channel consists of the following actions:
Neither the pre- nor post-assemble callback is invoked if any of the following conditions are true:
The compound channel is NULL.
The compound channel's window is frozen.
The compound latency is greater than current frame number (initial countdown).
A channel can be used as source and destination at the same time, it may even be used more than once as a source channel. In that case, each callback may be invoked more than once during a frame.
The default pre- and post-assemble callback perform the following tests in order to determine if they should perform any assembly:
The compound mode is not NOCOPY.
The compound has input frames—that is, is a destination channel.
If one of these tests fail, they do not assemble. In addition, mpkCompoundPreAssemble() tests that the ASYNC flag is set. Then the images output from the source channels during last frame are assembled in the destination channel prior to any rendering. Likewise, mpkCompoundPostAssemble() tests that the ASYNC flag is not set. Then the images from the source channels are assembled in the destination channel during the same frame as soon as they have been produced.
By overriding the default assemble callbacks, the application can customize the way frames are assembled in the destination channel. This technique is used, for example, in volume rendering, where the input frames are assembled in a fixed order using special blending modes.
Note that if the compound channel is identical to its parent channel, then no output frame is generated for this compound.
The MPKCompound data structure is a container for children of MPKCompound, each associated with an existing MPKChannel. The resulting MPKCompound tree can be traversed using the following three functions:
| Function | Description | |
| mpkCompoundTraverseAll() | Traverses all children of the specified MPKCompound, regardless of any state information. | |
| mpkCompoundTraverseActive() | Traverses the active children of the passed MPKCompound with respect to the current stereo mode (see the previous section “Stereo-Selective Compounds”). | |
| mpkCompoundTraverseCurrent() | Only traverses the active and current children of the passed MPKCompound with respect to the current stereo mode and current DPLEX cycle. |
They provide a general mechanism for traversing a compound hierarchy in a top-to-bottom, left-to-right order. Figure 3-23 shows the traversal for the MPKCompound tree of the used in Example 3-11 in the previous section “Multilevel Decomposition”.
The traversal functions are using three types of callbacks during traversal:
| Callback Type | Description | |
| Leaf callback function | Applied only on the leaf nodes of the compound tree, that is, on compounds without any children (in Figure 3-23, compounds III, IV, VI, and VII). | |
| Pre-callback functions | Applied when traversing parent compounds downwards. In Figure 3-23, they apply to the compounds I, II, and V. | |
| Post-callback functions | Applied when traversing parent compounds upwards. In Figure 3-23, they apply to the compounds I, II, and V. |
The return values of these functions must be either MPK_TRAV_CONT, MPK_TRAV_PRUNE, or MPK_TRAV_TERM to indicate that the traversal should continue, skip this node, or terminate, respectively. The value MPK_TRAV_PRUNE is equivalent to MPK_TRAV_CONT for the post-callback function.