XGL Architecture Guide
この本のみを検索
PDF 文書ファイルをダウンロードする

Rendering and Handling State Changes

5

This chapter describes the processes of rendering and handling Context state changes. It includes information on the following topics:
  • Steps in the rendering process
  • Architecture of the mechanisms for storing state changes and passing information about the changes to the device pipeline
  • Architecture of backing store

Goals of the Rendering Architecture

The XGL architecture provides flexibility in the interactions between the device pipeline and the software pipeline at rendering time. The device pipeline can choose to accelerate some rendering tasks and to fall back on the XGL software pipeline for other rendering tasks. This dynamic switching between the device pipeline and the software pipeline allows the device pipeline implementor to tailor a port for a specific device.
The goals for the rendering architecture were to:
  • Allow the device pipeline LI-1 rendering function to make the decision that it cannot render a primitive and call the software pipeline for LI-1 or LI-2 processing.
  • Allow the software pipeline at the LI-1 layer to call the device pipeline at the LI-2 layer.
  • Allow the device pipeline to call the equivalent software pipeline function within a rendering call if the device pipeline cannot completely accelerate the primitive.
  • Allow the software pipeline to call a different rendering function at the same level without knowing whether the function is implemented in the device pipeline. For example, if the device pipeline has not implemented stroke text, it can call the software pipeline's LI-1 stroke text function; the software pipeline's stroke text function may then tesselate the text into lines and call back the device pipeline LI-1 polyline function to see if it can render the partially processed geometry data.
Basic information on the steps of the rendering process is provided in the following section.

How Rendering Works

An application call to render a primitive is mapped by the device-independent code to a call to the device pipeline. This mapping takes place in the Context wrapper. For example, the API multirectangle operator is:
xgl_multirectangle(ctx,rect_list);

The wrapper code maps the C function call to the C++ internal code and forwards the call and the application data to the device pipeline. To do this, the wrapper gets a pointer to the current pipeline and calls the pipeline's version of xgl_multirectangle(). The wrapper locates the pipeline versions of the XGL primitives in an array of function pointers, which is called the opsVec array. The sample wrapper code below shows the currOpsVec pointer pointing to the multirectangle entry of the opsVec array.
void xgl_multirectangle(Xgl_ctx ctx, Xgl_rect_list* rect_list)
{
    // Get pointer to device pipeline
    XglDpCtx* dp = ((XglCtxObject*)ctx)->getDp();

    // Call dp function pointed to by array entry
   (dp->*((void(XglDpCtx::*)(Xgl_rect_list*))
           (dp->currOpsVec[XGLI_LI1_MULTIRECTANGLE])
          ))(rect_list);
}

What Is the opsVec Array?

Rendering calls and information on attribute changes are passed from the device-independent code to the device pipeline through the opsVec array. The opsVec array is a dynamic array of function pointers to device pipeline or software pipeline renderers. It serves as the point of communication between the device-independent code and the device pipeline code. The opsVec array is designed to minimize the overhead in the device-independent code during each graphics primitive call. When the application calls a primitive or makes an attribute change, the pipeline is notified of this immediately through the opsVec array.
The opsVec array is allocated by the base class XglDpCtx and filled in with pointers to default base class functions that point to software pipeline functions. For example, a portion of the default opsVec array in the base XglDpCtx3d class looks like this:

  .....  
  opsVec[XGLI_LI1_MULTIMARKER] = XGLI_OPS(XglDpCtx3d::li1MultiMarker);  
  opsVec[XGLI_LI1_MULTIPOLYLINE] = XGLI_OPS(XglDpCtx3d::li1MultiPolyline);  
  opsVec[XGLI_LI1_NURBS_CURVE] = XGLI_OPS(XglDpCtx3d::li1NurbsCurve);  
  opsVec[XGLI_LI1_MULTIRECTANGLE] = XGLI_OPS(XglDpCtx3d::li1MultiRectangle);  
  opsVec[XGLI_LI1_MULTIARC] = XGLI_OPS(XglDpCtx3d::li1MultiArc);  
  opsVec[XGLI_LI1_MULTICIRCLE] = XGLI_OPS(XglDpCtx3d::li1MultiCircle);  
  opsVec[XGLI_LI1_POLYGON] = XGLI_OPS(XglDpCtx3d::li1Polygon);  
  .....  

When the device pipeline is instantiated, it creates a version of the XglDpCtx object for its own pipeline. This object contains a set of opsVec array pointers specific to the device. In its version of the array, the device pipeline will override some or all of the entries in the opsVec array to install function pointers to its own accelerated renderers. If the device pipeline does not support a primitive, it can let the default function call the software pipeline for rendering. For example, the GX pipeline installs the following opsVec entries
in its XglDpCtx3dGx class. For the remaining primitives, the array inherits the default entries pointing to the software pipeline when the object is instantiated.

  opsVec[XGLI_LI1_MULTIMARKER] = XGLI_OPS(XglDpCtx3dGx::li1MultiMarker);  
  opsVec[XGLI_LI1_MULTIPOLYLINE] = XGLI_OPS(XglDpCtx3dGx::li1MultiPolyline);  
  opsVec[XGLI_LI1_MULTI_SIMPLE_POLYGON] =  
                                 XGLI_OPS(XglDpCtx3dGx::li1MultiSimplePolygon);  
  opsVec[XGLI_LI1_TRIANGLE_STRIP] = XGLI_OPS(XglDpCtx3dGx::li1TriangleStrip);  

Figure 5-1 shows a device pipeline that implements polyline and polygon renderers but points to the software pipeline for text rendering.

グラフィック

Figure 5-1

Device Pipeline Options for Rendering

When a device pipeline is loaded, it initializes its XglDpCtx object to assign pointers to its renderers. The opsVec array architecture allows the device pipeline to create a set of renderers that are tuned for performance. For primitives that the pipeline wants to override but that do not need to have optimal performance, the pipeline might create a single function that handles error checking and attribute setting. For performance-critical primitives, the device pipeline may want to create a set of functions including a generic or "slow" function that performs error and attribute checking and one or more functions that are optimized to handle particular sets of attribute values. These "fast" renderers are used when a primitive is called repeatedly without attribute changes. Depending on the sequence of primitive-attribute calls that preceded a particular call, the pipeline can determine which renderer it should set in the opsVec array.
Thus, at rendering time, the device pipeline has several options:
  • If the device pipeline has not implemented a rendering function, it need not reset the opsVec entry but can inherit the pointer to the default function, which calls the software pipeline.
  • The device pipeline can accelerate the primitive by installing a pointer to a single device pipeline renderer that handles error checking and attribute checking.
  • The pipeline can design a set of functions for primitives whose performance is critical. In this case, one renderer may handle generic attribute checking and call another renderer that simply sends data to the hardware given certain attribute values. When attributes change, the pipeline will again call the generic renderer to check attribute changes; when the slow processing of attribute checking is complete, the generic renderer can call the optimized renderer.
Even when the device pipeline has implemented a primitive, it may need to call the software pipeline for assistance at times. The device pipeline calls the software pipeline directly. It can do so in the following cases:
  • If the device pipeline doesn't support the current combination of attribute values, it can call the software pipeline without processing the primitive call.
  • If the device pipeline doesn't support the combination of primitive argument values, it must process the primitive call, but it can call the software pipeline before calling a renderer.
  • In some cases, the device pipeline may begin processing the data and decide that it cannot render all the data. For example, the device pipeline may request the software pipeline to:

    · Render a primitive, as in the case of a clipped polygon that the device pipeline cannot handle in a xgl_polygon() call.

    · Render a subset of the primitive data, as in the case of a polygon that the device pipeline cannot handle in a xgl_multi_simple_polygon() call.

    In this case, the pipeline must call the software pipeline from inside the renderer.

Note that the device pipeline can reset the opsVec array entries whenever it needs to. Although the array is defined in the device-independent code, the DI will never set opsVec entries, and there are no restrictions on when the device pipeline can change its renderers. For information on how the device pipeline sets pointers to its functions in the opsVec array, see the XGL Device Pipeline Porting Guide.

More on the Rendering Architecture

Because the opsVec array was designed to provide the device pipeline with optimal performance, a way for the device-independent code to handle low performance functionality, such as backing store and error checking, was needed. In particular, the device-independent code needed a way to handle rendering to backing store that would not impact rendering to the primary device. To meet this need, an array of wrappers to the opsVec function pointers was designed. This array, called opsVecGen, is a static array that points to a set of general purpose functions that handle error detection and backing store, render through the opsVec array, and then check the deferral mode. Note that the device pipeline cannot change the function pointers in the opsVecGen array as it can in the opsVec array.
The device-independent code maintains a pointer to specify whether it is rendering to the opsVec array or the opsVecGen array. This pointer is called currOpsVec and is defined in DpCtx.h along with the two arrays. If the application sets the backing store on a device using the attribute XGL_WIN_RAS_BACKING_STORE, the device-independent wrapper will set the
currOpsVec pointer to point to the opsVecGen array instead of the opsVec array. Any attribute changes are sent to both the primary device and the backing store device to keep them synchronized.
Figure 5-2 shows the opsVecGen architecture. For more information on backing store, see page 95.

グラフィック

Figure 5-2

Context State Changes

The device pipeline is notified of Context state changes when the changes occur. The application can cause Context state to change in two ways:
  • It can change a Context attribute directly using xgl_object_set().
  • It can change another object, which indirectly causes a Context attribute to change.
In addition, view model or Transform changes cause changes in a set of items derived from the Context's view model attributes.
Information on attribute changes is passed directly to the device pipelines through the opsVec array. The array has an entry for attribute changes resulting from xgl_object_set() and another entry for attribute changes resulting from messages between objects. The array is designed to minimize
the function call overhead for notifying the device pipeline of attribute changes. It also allows the device pipeline to act only on the attributes that it is concerned with and ignore all other attributes.
The sections that follow discuss how the opsVec array passes information on attribute changes to the device pipeline and how the pipeline can get new attribute values when changes occur.

State Changes From Attribute Setting

Context state changes can result from an application xgl_object_set() call with the obj parameter of ctx. An object set call on the Context:
xgl_object_set(ctx, Xgl_CTX_ATTR_NAME, value, NULL);

maps to the following C++ call:
ctx->setAttrName(value);

When an attribute set operation changes a Context attribute value, the Context wrapper notifies the Context object of the change. The Context updates its cached attribute value and builds an array of attribute changes. This array lists attributes that have changed but does not include the new attribute values.
When the entire list of attributes is processed, the wrapper gets a pointer to the current device pipeline from the Context object, gets the list of changed attribute types from the Context, and sends the list of changes to the pipeline through the object set entry of the opsVec array.
The device pipeline must provide an objectSet() function to handle attribute changes and install a pointer to this function in its opsVec array. The objectSet() function will check the attribute type of the attributes that the pipeline is interested in, retrieve the attribute values from the Context, and update the hardware state. One way of handling changes is to get the new attribute value from the Context object immediately, as in the following example code:
void XglDpCtx2dGx::objectSet(const Xgl_attribute *att_type)
{
    Xgl_usgn32 change_renderer = 0;

    for (;*att_type;att_type++) {
        switch(*att_type) {

         case XGL_CTX_DEVICE:
            // Update all context attributes.
            // (ctx->getAttrTypeListAll()); // DI utility list.
            objectSet((const Xgl_attribute*) attr2dTypeListStatic);
            break;
         case XGL_CTX_BACKGROUND_COLOR:
            // Update backgroundColor (background cached color)
           dpDev->cMap->getColorMapper()(dpDev->cMap,
                              &(Xgl_color&)ctx->getBackgroundColor(),
                              &backgroundColor, TRUE);
            // update hardware
            break;
        case XGL_CTX_SURF_FRONT_FILL_STYLE:
            surfIndex.fld.fstyle = ctx->getSurfFrontFillStyle();
            // update hardware
            break;
        ...
        }
    }
}

As an alternative, the pipeline can choose to simply note that an attribute was changed and wait until a later time to update the hardware context, perhaps updating the hardware context at rendering time. At that time, the device pipeline will check the XGL Context for the new attribute values and update the hardware. The following sample code shows a pipeline objectSet() routine checking for attribute changes.
void XglDpCtx3dSampFb::objectSet(const Xgl_attribute *attr_list)
{
    Xgl_attributeatt_type;

    while (att_type = *attr_list) {
        switch(att_type) {
        case XGL_CTX_ATEXT_CHAR_SLANT_ANGLE:
            update_needed = TRUE;
            break;
        case XGL_CTX_DEVICE:

            objectSet(ctx->getAttrTypeListAll());
            break;
        case XGL_CTX_EDGE_COLOR:
            update_needed = TRUE;
            break;
        ...
        }
   }
}

Note that if the Device is changed, the device pipeline is responsible for updating its entire attribute cache.

State Changes From XGL Object Message Passing

Context state changes can result from changes to objects used by the Context. For example, the Context object registers itself as a user of the Device, the Line Pattern, and the Stroke Font objects, and notes that it wants to be notified when changes to these objects occur. When an object the Context is using changes, the object updates its data and sends a message to the Context. The Context receives the message and reacts accordingly. Objects register interest in other objects through the user list. For more information on the user list, see page 70.
Objects can change in two ways:
  • The application can change the object that it is using; in other words, it can set a different Line Pattern object on the Context. This change is handled by the object set mechanism, and the device pipeline is informed of this change through the object set entry of the opsVec array. Note that a Color Map set on a Raster is handled by message passing; see page 94 for more information on Color Map changes.
  • The object's data can change, as would be the case if the application changed the line pattern data in an existing Line Pattern object. This change is handled by the message receive mechanism.
In the message receive mechanism, the object sends a message to its users informing them of the change. For example, if a Line Pattern object changes, it sends the Context a message informing it that the Line Pattern object has changed. The Context receives this message in its receive() function and updates its attribute data. The Context then forwards the message to the device pipeline through the message receive entry of the opsVec array.
To handle forwarded messages, the device pipeline must provide a messageReceive() function and install a pointer to this function in the XGLI_LI_MSG_RCV entry of its opsVec array. The pipeline's messageReceive() function will check the object type and respond to the message, as is shown in the following code fragment.
void XglDpCtx{2,3}dGx::messageReceive(XglObject* obj,
                                         const XglMsg& msg)
{
    switch (obj->getObjType()) {
      case XGL_2D_CTX:
      case XGL_3D_CTX:
        if (msg.flag & (XGLI_MSG_VIEW_COORD_SYS |
                          XGLI_MSG_VIEW_CTX_ATTR)) {
            transformsChanged = TRUE;
            // Set generic renderers.
        }
        break;

      case XGL_WIN_RAS:
        if (obj == device) {
            if (msg.flag & XGLI_MSG_DEV_COLOR) {
            // Update cached colors and plane mask changes.
        }
         break;

      case XGL_LPAT:
        if (obj == ctx->getLinePattern() ||
                obj == ctx->getEdgePattern()) {
            objectSet((const Xgl_attribute*) attrTypeList);
        }
        break;
      case XGL_TRANS:
        if (obj == (XglObject*)ctx->getGlobalModelTrans() ||
            obj == (XglObject*)ctx->getLocalModelTrans() ||
            obj == (XglObject*)ctx->getViewTrans()) {

            transformsChanged = TRUE;
            // Set generic renderers.
        }
       break;
        ...
   }
}

For more specific information on the messages the device pipeline needs to respond to, see the XGL Device Pipeline Porting Guide.

View Model Derived Data

XGL defines a conceptual view model consisting of a number of coordinate systems where an application can specify certain operations. XGL provides a facility named view model derived data to assist pipelines with implementation of the view model operations.
Derived data maintains a cache of items derived from a Context's view model attributes. The derived items include Transforms for mapping geometry between coordinate systems as well as items in various coordinate systems such as the view clip bounds, lights, eye positions or eye vectors, model clip planes, and depth cue reference planes. For example, derived data calculates the VDC-to-DC Transform from the Context attributes for the VDC map, VDC orientation, VDC window, DC viewport, and jitter offset, and the Device attribute for DC orientation. Thus, a derived item can depend on only API attributes, on only derived items, or on a combination of both.
The design goals of derived data are to:
  1. Support geometry entering LI-1 from other coordinate systems (in addition to Model Coordinates) with a simple interface for pipelines. The pipeline can set the current coordinate system when one primitive invokes another. For example, li1AnnotationText() can decompose the text into polylines in VDC and then invoke li1MultiPolyline(). Derived data keeps track of the current coordinate system for the pipeline.

  2. Provide a fast test to inform a pipeline of changes to derived items of concern so that the pipeline can minimize data transfer to devices that retain state. A pipeline can express concern about changes to a specified set of items. This allows pipelines to filter out irrelevant changes. This is useful because derived data consists of a large number of items and pipelines are typically interested in only a few of the items.

  3. Defer calculation of a derived item until a pipeline requests that item. The pipelines are not notified of derived data changes immediately but when they need them at rendering time.

Components of the Derived Data Mechanism

The central object of the derived data mechanism is the view cache object. The view cache consists of derived items and functions for deferred evaluation of the items. Each Context has a pointer to its own view cache, which maintains the derived items specific to that Context. A view cache object is created at Context creation time by a constructor in the Context object.
The Context constructor creates a set of Transform objects that represent its default transformations (Local Model Transform, etc.) and creates the view cache object. The view cache constructor creates internal Transforms that are needed by derived data. The view cache constructor also creates a set of view group configuration objects, each of which represents a coordinate system from which geometry can enter an LI-1 primitive.
The view group interface object is the pipeline's interface to the view model derived data. It informs a pipeline when derived items have changed as a result of either the application changing a view model attribute or a pipeline changing the coordinate system from which geometry enters the next LI-1 primitive. The view group interface constructor is called in the XglPipeCtx{2,3}d classes, and the object is created when the pipeline XglDpCtx object is created.
Each pipeline has a pointer to a view group interface object. The view group interface has functions for creating and destroying view concern objects. A view concern object is a description of all the derived items about which a pipeline is concerned. A pipeline may create as many view concern objects as it needs. For example, it can create one view concern object for stroke primitives and another for surface primitives.
Figure 5-3 on page 90 illustrates the set of objects in the derived data mechamism. This example shows a 3D Context with one view concern object for stroke primitives and another for surface primitives. Note that for 3D Contexts, there are five view group configuration objects.

グラフィック

Figure 5-3

How Derived Data Gets Information From the Context

As mentioned on page 83, there are two ways that Context state can change: the application can directly set a Context attribute, or the application can change an object used by the Context, causing the object to send a message to the Context about the change. Either of these two types of changes can cause derived data to change.
The Context has a number of attributes that the application can set, among which are the view model attributes. For example, if the application changes the VDC window using xgl_object_set() to set XGL_CTX_VDC_WINDOW, a number of derived items that are dependent on the value of the VDC window become invalid. But, because the changes are evaluated lazily, the derived data items are not recalculated until the pipeline requests the items.
If the member datum in the Context for the VDC window is changed, the Context informs the view cache object that the application changed the VDC window by calling a function in the view cache object. This function invalidates the derived items that depend on the VDC window. A set of bits records which derived items changed.
Some Context attributes that are objects, such as Lights, Transforms, or the Device, can change, causing derived data items to change. For example, if an application gets the Local Model Transform and changes it, the Transform sends a message to each Context that uses the Transform. If the Context is interested in the change to the Transform, it will call a view cache function to invalidate derived items that depend on the Local Model Transform. This process is similar for changes to Lights and for Device resizes.

Handling Derived Data Changes

Derived items can change when the application changes a view model attribute or a pipeline changes the current coordinate system. Each type of event causes a message to be sent to the device pipeline at the time of the event; notification is not deferred. The message types are XGLI_MSG_VIEW_CTX_ATTR for Context attribute changes, and XGLI_MSG_VIEW_COORD_SYS for current coordinate system changes.
Messages of the two types above give advance warning that the next primitive may need to get derived items. A pipeline may choose to deal with the messages simply by setting its own flag at the time of the notification, then deferring action until the next primitive when it would need to interrogate the composite at the next level.
A pipeline determines that a Context attribute or derived item has changed by checking the flag that the pipeline sets upon receiving a message of the types XGLI_MSG_VIEW_CTX_ATTR or XGLI_MSG_VIEW_COORD_SYS. If the flag is set, the pipeline determines whether any derived items have changed by calling the changedComposite() function in the view group interface object, as in viewGrpItf->changedComposite(surfConcern). The composite is a record of the state changes for all derived items. If the changedComposite() function returns TRUE, the pipeline must check the change functions for individual items to determine which ones changed. For more information on using the view group interface object's change functions, see the XGL Device Pipeline Porting Guide.

Changes in Context Stroke Attributes

The Context object includes a set of objects that contain attribute information used when the software pipeline draws primitives as lines. For example, if a device pipeline does not implement text, the software pipeline will render the text. The software pipeline will convert the text to polylines and call the device pipeline polyline function to render the lines. However, in this case, the device pipeline polyline function will want to use the text attributes rather than the line attributes when rendering the lines. This is handled transparently for the device pipeline by the stroke group objects.
Lines, markers, text, edges, and front and back hollow polygons are considered to be stroke types. Since the same set of attributes applies to each of the stroke types, the Context object maintains a stroke group object for each of the stroke types. Multiplexing primitives on the MultiPolyline() primitive is shown in Figure 5-4.

グラフィック

Figure 5-4


Note - Stroke groups are used only by the MultiPolyline() primitive. Other stroke primitives, such as MultiMarker(), do not use stroke groups.

Stroke Group Objects

The stroke group object for each stroke type contains the values for the multipolyline-specific attributes (line color, line width, etc.). When an attribute set occurs on a Context, the Context updates the attribute's value, and, if the set affects a stroke group, the Context also updates the stroke group attribute for that stroke type.
The XglStrokeGroup object provides interfaces for the pipeline to retrieve the value of stroke attributes. For example, if a pipeline needs to know the value of the line color, it invokes the stroke groups's getColor() function. The XglStrokeGroup3d object adds 3D-specific attributes to the stroke object. The stroke group objects are associated with the Context object and are created at Context creation time. A conceptual sketch of the relationship between the Context object and the stroke group objects is shown in Figure 5-5.

グラフィック

Figure 5-5

Rendering Using Stroke Attributes

Each stroke group object contains the attribute values for a particular stroke type. To indicate which stroke group will be used for rendering, the Context object provides a current stroke pointer, which points to one of the stroke group objects. When geometry is rendered as lines, the stroke pointer is set to the appropriate stroke group and MultiPolyline() renders using that stroke group.
For example, if a device pipeline does not implement text, the software pipeline will tesselate the text into polylines and call the device pipeline's MultiPolyline() function. However, before this happens, the software pipeline assigns the current stroke group to the text stroke group. This generates an objectSet() call to the device pipeline indicating which attributes need to be loaded from the current stroke group. By the time the device pipeline MultiPolyline() function is called, all the correct stroke attributes will already have been processed.

Device State Changes

The state of a Device object can change in the following general ways:
  1. Device changes can be color or pixel mapping related. The application can modify the Color Map object associated with the Device, or it can set a new Color Map on the Device.

  2. Device changes can result from changes to the dimensions of the raster. In this case, Context state is affected since Window Raster size changes cause derived data changes.

  3. Device changes can result from switching buffers during multibuffering.

The pipeline XglDpCtx object and XglDpDev object are each informed of Device state changes, as follows:
  • The device pipeline XglDpCtx object is notified of Device changes via the Context object through message passing. This enables the XglDpCtx to update hardware state. For example, the device-independent Device object sends a message to the Context about a color map change in these lines:
   XglMsg msg;
   msg.flag = XGLI_MSG_DEV_COLOR;
   send(msg);

The Context receives the message and forwards it to the XglDpCtx through the opsVec array.
  • The device pipeline XglDpDev object is notified of a change by a direct call from the device-independent code. This allows the pipeline to make device-specific changes. For example, this call informs XglDpDev of a color map change:
   ((XglDpDevWinRas*)dpDev)->setCmap(cmap);

See the XGL Device Pipeline Porting Guide for more information on the message passing mechanism and on the XglDpDev functions that the device-independent Device object uses to inform the pipeline of device state changes.

Rendering Into Backing Store

A backing store consists of a piece of off-screen memory that reflects the content of the display buffer and that is used by the server to restore previously obscured areas of the display during an expose event. When backing store is enabled, XGL renders a primitive to both the visible and covered portions of the window when the window is clipped. Normally, XGL renders the covered area to backing store using the inverse clip list and does not render the exposed area to backing store. However, if the inverse clip list becomes complex, it is more efficient to render to the entire backing store, and XGL will, at its option, delete the inverse clip list.
In general, the XGL core is responsible for handling backing store operations. The device pipeline need only implement a small set of device-dependent functions in the pipeline to provide support in certain cases. However, the device pipeline has the option of handling backing store itself. For example, the PEXlib pipeline can rely on the server to handle backing store operations, so it does not need the XGL core to provide backing store support.

Architecture of Backing Store

An application that wants to use backing store first requests it through the server and then through XGL. When the server is asked to provide backing store, it tries to allocate backing store memory from either system memory or from a hardware cache. It is up to the server to decide whether to provide the backing store support and from which area the backing store is allocated.
When XGL is asked to enable backing store, it determines whether the server granted the request for backing store and whether the window is single buffered (since XGL cannot render to backing store if double buffering is enabled). If backing store is enabled, and the window is single buffered, XGL then requests the handle to the allocated memory through the XglDrawable.
To render into the backing store area, XGL creates a set of shadow objects that reflect the contents of the parent objects. For example, each Context and Device pair is associated by a device pipeline-context object. Thus, if there is a shadow device, there is a parent XglDpCtx, which renders the visible part of the screen, and a shadow XglDpCtx, which renders to the backing store. The shadow objects are:
  • Shadow device pipeline-context object
  • Shadow raster object, which can be either a Memory Raster or a Window Raster
  • Shadow device pipeline device object
Figure 5-6 shows the shadow devices that are created when backing store is enabled.

グラフィック

Figure 5-6

The parent XglDpCtx and the shadow XglDpCtx have the same structure. Each has an opsVec pointer, called currOpsVec, and two arrays: the opsVec array and the opsVecGen array. Figure 5-7 illustrates the parent device pipeline-context and the backing store device pipeline-context.

グラフィック

Figure 5-7

Creating a Shadow Device

A shadow device is created the first time that backing store is requested. An application sets the XGL_WIN_RAS_BACKING_STORE attribute to request backing store. When backing store is requested, the Window Raster calls the device pipeline to inquire if it wants the XGL core to create a backing store device and handle backing store operations. The Window Raster does this with the pipeline interface dpDev->needRtnDevice(). If
dpDev->needRtnDevice() returns TRUE, the XGL core will:
  1. Get a handle to the backing store memory from the server and create a shadow device, which can either be a Window Raster or a Memory Raster. It then attaches the shadow device to the parent device. The shadow device includes its device-independent and device-dependent parts.

  2. Create a shadow device pipeline-context object.

  3. Synchronize the raster attributes between the base device and the shadow device.

  4. Set the doRetained flag in the parent device to TRUE to indicate that backing store mode is on and that the server is granting the backing store.

The example code below shows the shadow device being created.

  void XglRasterWin::setBackingStore(Xgl_boolean  
                                           backingstore_mode)  
  {  
   .....  
       if (backingstore_mode) {        // user set to TRUE  
          if (backingStore) return;   // backing store aleady set  
          // no backing store in server mode or w/ double buffering  
          // return if window system is not X  
           if (buffersAllocated > 1 || XglXServer::isXServerMode()  
                || type != XGL_WIN_X)  
                return;  
  
           if (((XglDpDevWinRas*)dpDev)->needRtnDevice())  
                backingStore = createRtnDevice();  
           else {  
                backingStore = TRUE;  
           }  


       }  
       else if {                  // user set to FALSE  
          if (shadowDevice) {    // supporting backing store  
                ....  
              // free backing store device components,  
                // reset pointer(s) to NULL  
                destroyRtnDevice();  
           }  
            if (doRetained) {  
                drawable->unGrabRetainedWindow();  
                doRetained - FALSE;  
           }  
           backingStore = FALSE;  
       }  
  }  

Note that there are two possible scenarios for the creation of the shadow interface manager when the Context and Device are associated:
  • If the Context and Device have not previously been associated, the pointer to the device pipeline-context object for the Context and Device pair cannot be retrieved from the parent Device. In this case, the parent Device will create the parent device pipeline-context object. If backing store is set, the parent Device will ask the shadow device to create a shadow device pipeline-context for the Context object and then attach it to the parent device pipeline-context.
  • If the Context and Device have previously been associated, the pointer to the device pipeline-context object for the Context and Device pair is retrieved from the parent Device.
If a device pipeline can handle backing store itself, it can return FALSE to dpDev->needRtnDevice(), in which case all backing store operations become the responsibility of the device pipeline or its underlying framework. For example, because the PEXlib pipeline can rely on the server to handle backing store operations, it returns FALSE to needRtnDevice().

Tracking Backing Store Changes in the Server

Since the server can potentially drop backing store support or switch the allocation of backing store memory to another device, the XGL core must track the changes in backing store on the server side for every WIN_LOCK() call.
Whenever a change is detected, the XGL core destroys the existing shadow device and shadow device pipeline-context object for that window (device). New shadow device and shadow device pipeline-context objects are created if new backing store memory is allocated by the server. This operation can be rather time-consuming if it happens in the middle of a rendering call, though it rarely occurs.

Rendering Into the Backing Store Device

When a rendering call occurs and backing store is enabled, XGL first renders into the parent device and then renders into the shadow device. The rendering call is duplicated in order to render into both devices. To render into backing store, the device independent code sets the currOpsVec pointer (used by the API wrapper to call a device pipeline function) to point to the opsVecGen array instead of the the opsVec array. The currOpsVec points to the opsVecGen only in the parent device. The shadow device's opsVec is called directly from the routine pointed to by the opsVecGen, and its currOpsVec pointer is not used. Figure 5-8 illustrates rendering into backing store.

グラフィック

Figure 5-8

Since both the parent XglDpCtx and shadow XglDpCtx derive from the base class XglDpCtx, a backing store device pipeline doesn't know it is being used for backing store. In exactly the same way as the parent device, the shadow device will either try to render through hardware, or will call the software pipeline if it doesn't know how to render a given primitive-attribute combination.
As a result, any attribute changes are sent to both the parent device and the shadow device to keep them synchronized. Since the object set function pointer is also in both opsVec arrays, when backing store is on, attribute setting is handled through the opsVecGen array, which calls the backing store version of the object set function. This function calls object set for both the front and backing store pipelines, keeping them synchronized.
The Context do_retained parameter is used only by the device independent code and the software pipeline. By default, do_retained is FALSE; the Context sets it to TRUE when backing store is enabled, and it remains TRUE until the processing first gets into parent device's pipeline. Once the rendering call is sent to the parent device, do_retained is set to FALSE.
If a device pipeline calls the software pipeline, for example at LI-1, the software pipeline processes only once for both the parent device and the shadow device at each LI layer until the parent device successfully renders. That is, if the parent device can only render the primitive at LI-2, then the LI-1 software pipeline processing is done only once for both the parent device and the shadow device.
The code example below shows the opsVecGen li1MultiPolylineGen() function rendering into both the parent device and the shadow device if backing store is enabled and rendering only once to the software pipeline if the device pipeline calls the software pipeline.
void XglDpCtx3d::li1MultiPolylineGen(Xgl_bbox*    bounding_box,
                                     Xgl_usgn32   num_pt_lists,
                                     Xgl_pt_list* pl)
{
    if (error_checking) {
        do_error_checking()
    }

   drawBackingStore = FALSE;
   if (backing_store_on && !picking) {
        WIN_LOCK(drawable);

        drawBackingStore = TRUE;
    }

    // Call the parent device to render to
    // the visible part of the screen.
    //
    // NOTE: Add an additional Xgl_boolean (TRUE) parameter in case
    //       the parent device pipeline calls swp and uses the DI
    //       default function.
    //
    (this->*((void(XglDpCtx::*)
             (Xgl_bbox*,Xgl_usgn32,Xgl_pt_list*,Xgl_boolean))
             (opsVec[XGLI_LI1_MULTIPOLYLINE]))
        )(bounding_box,num_pt_lists,pl,TRUE);

    if (backing_store_on && !picking && drawBackingStore) {
        if (dpBs && <other optimization testing>)) {
                // shadow device exists
            swp->setDpCtx(dpBs);  // switch dp pointer in swp to
                                  // dp backing store device

            (dpBs->*((void(XglDpCtx::*)
                    (Xgl_bbox*,Xgl_usgn32,Xgl_pt_list*))
                    (dpBs->opsVec[XGLI_LI1_MULTIPOLYLINE])
                    ) //call opsVec entry in shadow dpCtx
                   )(bounding_box,num_pt_lists,pl);

            swp->setDpCtx(this); // reset dp pointer in swp to parent
        }

        WIN_UNLOCK(drawable);
    }

    if (asap_mode_on)
        // do flush stuff
    }
}

The default opsVec li1MultiPolyline() function of the base class XglDpCtx is shown below. The if statement should only be true if the device's doRetained flag is TRUE, and the function is called directly from the corresponding LI opsVecGen function from the parent device as a result of a default call to the software pipeline (and not picking).
In order to distinguish the full LI opsVecGen software pipeline call from the multiple calling of the default device pipeline functions from the software pipeline as a result of partial calls to the software pipeline, an additional Xgl_boolean gen_punt parameter is added to the LI-1 and LI-2 default functions:
// Default function pointed to by opsVec array
// if device pipeline wants to call the software pipeline.
void XglDpCtx3d::li1MultiPolyline(Xgl_bbox*    bounding_box,
                                  Xgl_usgn32   num_pt_lists,
                                  Xgl_pt_list* pl,
                                  Xgl_boolean  gen_punt)
{
    // The doRetained field corresponds to XGL API attribute for
    // XGL_WIN_RAS_BACKING_STORE && the server granting backing
    // store.
    // Also, no picking for backing store case.
    // gen_punt has to be tested *after* doRetained

    if (device->getDoRetained() && gen_punt
                                && !ctx->getPickEnable()) {
        // release the lock in li1MultiPolylineGen
        WIN_UNLOCK(drawable);

        swp->li1MultiPolyline(bounding_box, num_pt_lists, pl, TRUE);
         drawBackingStore = FALSE;
    }
    else
        swp->li1MultiPolyline(bounding_box, num_pt_lists, pl);
}

There is no need to add this extra parameter to LI-3 because calling the software pipeline is not allowed in LI-3, and an error will be issued in that case. As noted on page page 82, the opsVecGen array is static; in other words, a device pipeline cannot change function pointer entries in this array.
The Xgl_boolean argument is FALSE by default, so any calls directly from one XglDpCtx function to another XglDpCtx function do not need to pass the extra parameter.

Propagation of API Changes to the Backing Store Device

The parent device is responsible for propagating any relevant API changes to the shadow device. These operations are done in the XGL core without the device pipeline's intervention, unless set functions are explicitly called. The parent Window Raster device will reflect all relevant changes to the shadow device if the shadow device exists. For example, a window resize event could trigger a change to the memory allocation by the server. The XGL core will handle the corresponding change to the shadow XGL device by destroying the old XGL shadow device and creating another one if needed.
These operations involve explicit create and set calls to the device pipeline. However, the device pipeline does not need to be aware of the internal backing store operations.

Backing Store Support for the Z-Buffer and Accumulation Buffer

For devices that use a software Z-buffer or accumulation buffer, the server only provides backing store during an expose event for the image buffer, not for the Z-buffer or accumulation buffer. XGL enables the sharing of software Z-buffers and/or accumulation buffers with the backing store device. This allows the software Z-buffer and accumulation buffer to stay synchronized with the backing store device.
Devices that may be used as backing store devices (for example, a Memory Raster) and that use software Z-buffer and/or accumulation buffer must implement setSwZBuffer() and setSwAccumBuffer() so that the XGL core can call these functions and pass in the parent's software buffer addresses during backing store device creation.
For devices that use software Z-buffer and accumulation buffer, setting HLHSR mode on or calling the accumulation function for the first time may trigger the creation of the software buffer in the parent device. The XGL core is responsible for passing the addresses of the buffer memory from the parent device to the backing store device for sharing.
The backing store device should cache the parent device pointer that is passed in during backing store device creation time. Before creating any Z-buffer or accumulation buffer, these device pipelines should ask the parent device for
any software buffer address through parent_dev->getSwZBuffer() or parent_dev->getSwAccumBuffer() to make sure it is sharing those buffers as much as possible.
Note that devices with hardware Z-buffer or accumulation buffer cannot share these buffers with backing store. Thus, these devices may not want to enable backing store when HLHSR mode is on or when accumulation is needed. See the XGL_WIN_RAS_BACKING_STORE reference page in the XGL Reference Manual for more information.