Rendering and Storing Output Commands


For a list of related topics, see Programming Interface for Generating Output Commands.

The functions described in this section generate output commands. You can direct these output commands to a renderer for immediate processing or you can store them in a structure resource.

PEXlib does not directly expose the specific protocol requests PEXRCStoreElements and PEXRCRenderOutputCommands. Instead, the request type argument, req_type, determines which of these two requests to use and when to use them to send the output commands to the PEX implementation. This argument must have one of four values: PEXOCRender, PEXOCRenderSingle, PEXOCStore or PEXOCStoreSingle. PEXOCRender and PEXOCRenderSingle cause PEXlib to format the output command into a PEXRCRenderOutputCommands request. This request causes the implementation to immediately render the output commands using the specified renderer when the implementation receives the request. PEXOCStore and PEXOCStoreSingle cause PEXlib to format the output command into a PEXRCStoreElements request. This request causes the implementation to store the output commands into the specified structure when the implementation receives the request. If the request type argument is not one of the values defined above, then the result is implementation-dependent.

To improve performance, PEXlib tries to pack output commands into single PEXRCRenderOutputCommands or PEXRCStoreElements requests. This packing, or buffering, of output commands normally occurs when the request type is PEXOCRender or PEXOCStore. When the request type is PEXOCRenderSingle or PEXOCStoreSingle, PEXlib forces the output command to be formatted into a single request by itself. This is useful, for example, for debugging purposes.

Applications should either use PEXOCStoreSingle when the structure's editing mode is PEXStructureReplace, or delete elements and use PEXStructureInsert to insert the new elements. See Structures for more detail. The result of the PEXRCStoreElements request when the specified structure's editing mode is set to PEXStructureReplace is unpredictable when using the request type PEXOCStore. The PEXRCStoreElements request replaces a range of elements in the structure, depending on how many output commands are in the request.

The number of output commands in a given request depends on too many variables to predict. PEXlib will not pack a single request that is too large to send to the server. However, if a single OC is too large to send, PEXlib causes the PEX server to issue a BadLength error. Invoke the PEXGetSizeOCs and XMaxRequestSize functions beforehand to determine if an OC is too large to send. See Introduction to PEXlib for more detail.

There are also a number of OC manipulation functions defined in Output Command Encode and Decode and in Sending Encoded Output Commands.

For a PHIGS workstation resource to process output commands for rendering, you must store the output commands in structures. The PHIGS workstation subset does not, in itself, support immediate mode rendering. See PHIGS Workstation Overview for more information on PHIGS workstations.


PEXlib Explicit Interface

For a list of related topics, see Programming Interface for Generating Output Commands.

PEXlib offers two major argument interfaces for the output command functions: the explicit interface and the output command context, or OC Context (OCC) interface.

The explicit interface is the interface defined for PEXlib 5.1 and is included in the PEXlib 5.2 specification for backwards compatibility, so PEXlib 5.1 programs compile with PEXlib 5.2 libraries. Although you may still use the explicit interface, you are encouraged to use the OCC interface to take advantage of the performance and functional improvements available in PEXlib 5.2. The explicit interface uses the same first three arguments:

display -
specifies the display connection.

resource_id -
specifies the resource identifier for the targeted renderer (for displaying output commands immediately) or structure (for storing output commands).

req_type -
specifies whether the application renders the output commands immediately (in which case the resource_id argument identifies the renderer resource), or stores the output commands in a structure (in which case the resource_id argument identifies the structure resource).

The explicit interface requires that you specify all arguments on every output command function you invoke.


PEXlib Output Command Context (OCC) Interface

Topics within this section include:

For a list of all the related topics, see Programming Interface for Generating Output Commands.

The output command context interface (introduced with PEXlib 5.2) requires that the first argument always be the OC context, replacing the three arguments listed above. The OC context is an opaque structure that contains many of the arguments that are commonly found in the output command functions. Your application uses a set of special OCC manipulation functions to modify the fields in the opaque OC context. Then your application uses the OC Context in subsequent invocations of output command functions. The OCC interface uses far fewer arguments than the explicit interface, making coding easier and improving performance in some implementations. See Output Command Context (OC Context or OCC) for the complete description of the OCC.

Note: the attribute and primitive output commands introduced after 5.1 do not offer the explicit argument interface format. This is to encourage the use of the OCC argument interface in newer applications over the older explicit interface.


Flexible Data Formats

Related topics include:

The OCC style functions for primitives that have facet and/or vertex data parameters allow you to supply the graphical data (coordinates, vertex attributes, facet attributes, and floating-point data) in packed, stride, or unpacked form. The packed form is the only form supported in PEXlib 5.1 and requires you to format the data into packed data structures defined by PEXlib. The stride form is more flexible and allows you to supply data formatted in application-defined structured arrays without the need to copy the data into the PEXlib-defined structures before invoking the PEXlib function. (Note: A "structured array", in this context, refers to an array of C language structures (as in struct) which is a common idiom employed in C language data structures, for example:

  struct MyStruct { float x,y; } myData[30];
)

The unpacked form allows you to supply the data in separate lists for each data type.


Data Alignment

Related topics include:

On many machine architectures, data alignment can be very important for performance and correct execution. For example, some machines require that pointers point to word boundaries when accessing word-length data items. Because the OCC interface allows you to control the offset applied to pointers for data accesses, you should be extra careful when specifying OCC pointers and offsets to ensure that you are following your machine's data alignment policies.


Output Command Context (OC Context or OCC)

Topics within this section include:

For a list of all the related topics, see Programming Interface for Generating Output Commands.

The OC Context maintains the state of some common PEXlib arguments across PEXlib function calls. The OC context enables you to set the desired values of these arguments and then reuse them by specifying just the OC Context in several subsequent function calls. This eliminates the need to re-specify these same arguments in every function call, reducing redundancy and improving performance in some environments.

The OC Context itself is an opaque data structure with members that are referred to as values. Programs cannot alter the values of the OC Context directly. Instead, programs must use additional function calls to manipulate the values of the context. This ensures that the PEXlib implementation is informed whenever an OC Context value is changed.


Data Structures

Related topics include:

The primary data structure used with the OC Context is the PEXOCCValues data structure which you use to set the value fields via the bitmask/value mechanism. It is important to realize that you do not alter the OC Context itself. You instead specify your desired changes in the PEXOCCValues data structure and use OCC functions to alter the OC Context with this data structure as input. The default values assigned to the OC Context, when your application creates it, are also given below:

 typedef struct {                          /* DEFAULT Values */
   Display               *display;         /* undefined */
   PEXRenderer           renderer;         /* undefined */
   PEXStructure          structure;        /* undefined */
   PEXOCRequestType      req_type;         /* PEXOCRender */
   int                   shape_hint;       /* PEXShapeUnknown */
   int                   ignore_edges;     /* False */
   int                   contour_hint;     /* PEXContourUnknown */
   int                   contours_all_one; /* False */
   unsigned int          facet_attributes;              /* PEXGANone */
   unsigned int          line_vertex_attributes;        /* PEXGANone */
   unsigned int          marker_vertex_attributes;      /* PEXGANone */
   unsigned int          surface_vertex_attributes;     /* PEXGANone */
   unsigned int          edge_attributes;               /* PEXGANone */
   unsigned int          facet_fp_data_count;           /* 0 */
   unsigned int          line_vertex_fp_data_count;     /* 0 */
   unsigned int          marker_vertex_fp_data_count;   /* 0 */
   unsigned int          surface_vertex_fp_data_count;  /* 0 */
   int                   color_type;        /* PEXColorTypeRGB */
   char                  *encoding_state;   /* NULL Pointer */
   int                   data_model;        /* PEXDataPacked */
   union {
     PEXOCCStrideData           stride;
     PEXOCCUnpackedData         unpacked;
   } data_model_specs;
 } PEXOCCValues;
 
 typedef struct {                           /* all members 0 */
   int                   facet_stride;
   int                   vertex_stride;
   int                   facet_color_offset;
   int                   facet_normal_offset;
   int                   facet_fp_data_offset;
   int                   vertex_coord_offset;
   int                   vertex_color_offset;
   int                   vertex_normal_offset;
   int                   vertex_edge_offset;
   int                   vertex_radius_offset;
   int                   vertex_axes_offset;
   int                   vertex_angle_offset;
   int                   line_vertex_fp_data_offset;
   int                   marker_vertex_fp_data_offset;
   int                   surface_vertex_fp_data_offset;
   } PEXOCCStrideData;
 
 typedef struct {                           /* all members 0 */
   int      facet_color_size;
   int      facet_normal_size;
   int      facet_fp_data_count;
   int      vertex_coord_size;
   int      vertex_color_size;
   int      vertex_normal_size;
   int      vertex_edge_size;
   int      vertex_radius_size;
   int      vertex_axes_size;
   int      vertex_angle_size;
   int      line_vertex_fp_data_count;
   int      marker_vertex_fp_data_count;
   int      surface_vertex_fp_data_count;
   } PEXOCCUnpackedData;

The semantics of each member in the PEXOCCValues structure is the same as it is in the explicit interface.

Specify all size and offset members in terms of bytes. The fp_data_count members represent the number of floating-point values.

The encoding_state member is reserved for use by future internationalized text functions to retain character encoding state for languages that require it.

The data_model member indicates the data model used to pass vertex and facet data to OCC primitive functions. You should set this member to PEXDataPacked, PEXDataStride, or PEXDataUnpacked.

The OCC itself is defined as:

 
   typedef struct _PEXOCC
   #ifdef PEXLIB_ILLEGAL_ACCESS
 
   {
       XExtData     *ext_data;       /* hook for extension */
       /* PEXlib private data */
   }
   #endif
   *PEXOCC;
 

You cannot access any members with the OCC directly. You must simply pass the OCC to functions that modify or use the contents of the context structure.


Sample Usage of the OCC

Related topics include:

To invoke the OC functions that use the OC Context, your application must first create an OC Context with the PEXCreateOCC function. This function returns an OC Context initialized with default values for future reference. The application can change the default context values when it creates the OC Context and/or can change values later by invoking the PEXChangeOCC function. These two functions set the value fields via a bitmask/value mechanism and are suitable when you are setting several values at once. There are also OC Context convenience functions that, through a simpler interface, enable you to set only one value per function in the OCC.

Here is a coded example of how to use the OC Context. The last two statements illustrate the difference between an OCC function and a non-OCC function:

 
   PEXOCCValues       ocvalues;
   unsigned int       mask;
   PEXOCC             myocc;
 
   ocvalues.display = my_display;
   PEXSetOCCValueMask(&mask, PEXOCCMDisplay);
   ocvalues.renderer = my_renderer;
   PEXSetOCCValueMask(&mask, PEXOCCMRenderer);
   ocvalues.color_type = PEXColorTypeRGB;
   PEXSetOCCValueMask(&mask, PEXOCCMColorType);
   ocvalues.surface_vertex_attributes = PEXGAColor;
   PEXSetOCCValueMask(&mask, PEXOCCMSurfaceVertexAttributes);
 
   myocc = PEXCreateOCC(&mask, &ocvalues);
 
   PEXOCCTriangleStrip(myocc, NULL, count, vertices);
 
   /* This is the old, explicit interface */
   PEXTriangleStrip(display, resource_id, req_type, PEXGANone,
     PEXGAColor, 0, 0, PEXRGBFloat, NULL, count, vertices);
 

Note that it was not necessary to set the facet attributes member of the OCC to PEXGANone because PEXGANone is the default value in a newly created OCC.

Invoke the PEXFreeOCC function to deallocate the memory associated with the OC Context.


Facet / Vertex Data Formats

Topics within this section include:

For a list of all the related topics, see Programming Interface for Generating Output Commands.

There are three ways to provide facet and vertex data to PEXlib:


Packed Data Interface (PEXlib 5.1 method)

Related topics include:

For a list of all the related topics, see Programming Interface for Generating Output Commands.

Unlike the stride and unpacked interfaces, you can use the packed data interface with either OCC primitive functions or with non-OCC primitive functions. The use of the packed data interface with non-OCC functions, as with PEXlib 5.1 functions, is as follows (this example has no facet data and supplies colors and normals in the vertex data):

 
   PEXVertexRGBNormal        my_vertices[20];
   PEXArrayOfVertex          verts;
 
   /* code to init my_vertices */
 
   verts.rgb_normal = my_vertices;
 
   PEXTriangleStrip(display, rend, PEXOCRender, 0,
       PEXGAColor | PEXGANormal, PEXColorTypeRGB,
       NULL, 20, verts);
 

You can also use packed data format with OCC style functions, which you may find useful when converting older code to use the OCC style functions:

 
   PEXVertexRGBNormal        my_vertices[20];
 
   /* code to init my_vertices */
PEXOCCTriangleStrip(context, NULL, 20, my_vertices);

This usage assumes that you have already initialized the OC Context fields with the correct values. In particular, you need to set the display, renderer (or structure), req_type, surface_vertex_attributes, color_type, and data_model (PEXDataPacked, of course) fields. Unless these fields change, you only need to set these once. You do not need to set any fields in the data_model_specs union.

Also, you do not need the PEXArrayOfVertex union to pass the vertex data in the function. The type of the facet and vertex data parameters is PEXPointer, which allows you to pass any type in this parameter. This is important for the stride interface. However, for both facet and vertex data, you are responsible for making sure that the data you are passing accurately reflects the attributes that the data you are passing accurately reflects the attributes that you set in the facet or vertex attribute fields.

PEXlib does not supply a set of structure type definitions for facet or vertex data that include floating point data because there is no way to determine how many floating point numbers you want to supply with a facet or vertex. Therefore, you may find it convenient to design your own data structure to pass the facet or vertex parameters or some OC functions when using the PEXDataPacked data model. You need to design the data structure with the following ordering rules in mind:

For facet data, the required order is:

  PEXColor*      one of the PEXlib color types, if provided
  PEXVector      normal, if provided
  float[n]      floating point data, if provided

For vertex data, the required order is:

  PEXCoord      (or PEXCoord2D) center
  PEXColor*      one of the PEXlib color types, if provided
  PEXVector      normal, if provided
  unsigned int      edges, if provided
  float[n]      floating point data, if provided

As an example, if you wish to supply vertex data for FillArea using 3D coordinates, RGB floating-point colors and three floating-point numbers (for texture data), you would create a structure like this:

  typedef struct {
    PEXCoord      center;
    PEXColorRGB      rgb;
    float      texture_data[3];
  } MY_PEXRGBTextureVertexData;

Remember to set the correct surface vertex attributes in the OCC. In this case you would make sure that the PEXGA2D flag is off, the PEXGAColor is on and the PEXGAFloatData flag is on. You set the color_type field in the OCC to PEXColorTypeRGB and the number of floats in the OCC to three.

You may then pass the address of an array of these structures directly in the OCC form of a function:

 
   MY_PEXRGBTextureVertexData        my_vertices[20];
 
  /* code to init my_vertices */
PEXOCCTriangleStrip(context, NULL, 20, my_vertices);

Stride Data Interface

Related topics include:

For a list of all the related topics, see Programming Interface for Generating Output Commands.

The stride interface allows you to access the facet and vertex data directly from application data structures if the data is arranged in a "structured array" format, where the application stores facet or vertex data in an array of structures; each structure in the structured array corresponds to data for a single facet or vertex along with other application specific data. You specify the size, or "stride" of each array element and the offsets of each of the facet or vertex data items within the array element. Using this format, you eliminate the need to copy the facet or vertex data from the application data structure to an array of PEXlib packed data structures.

Here is an illustration of the stride model, using vertex data:

In this example, you supply a pointer to the beginning of the structured array in the function call.

 
  PEXOCCTriangleStrip(context, NULL, 20, vertex_data_pointer);
 

In this case, you set the OCC's data_model to PEXDataStride, and in addition to the OC Context values you had to set for the packed case, you also need to set vertex_stride and the offset members in the stride structure used above (in this case, vertex_coord_offset, vertex_normal_offset, vertex_color_offset, and vertex_fp_data_offset.) You do not need to set any offset members in the stride structure for data that you don't use. You tell PEXlib what data items it is to use by the facet/vertex attribute OCC members. With the stride interface, you must supply the offset for every data item that you specify is present in the facet/vertex attributes.

You handle facet data in exactly the same way, except you use different fields in the OC Context.

Some primitives, such as Fill Area Set and Polylines (Polyline Set) are defined by a nested list of vertices. Since you cannot work directly with a nested list of vertices, you use the PEXListOfVertexData, which looks like:

 
  typedef struct {
    unsigned long      count;
    PEXPointer         vertices;
  } PEXListOfVertexData;
 

For each Fill Area Set Contour or Polyline in the set, you allocate one of these structures and fill it in with the number of points in that contour or line and a pointer to the first vertex in the list. Note that the type of the vertices pointer is PEXPointer, so you set this pointer to point to a structured array, just as in the non-nested case. The list of these PEXListOfVertexData structures must be contiguous and you pass the address of the first one in the vertex data argument of the primitive function call.


Unpacked Data Interface

Related topics include:

For a list of all the related topics, see Programming Interface for Generating Output Commands.

You use this data interface style when your data is arranged in lists or arrays of points, colors, normals, etc. Each of these lists can reside anywhere in memory, since you supply a pointer to the start of each list. You also must specify the size of each element in the list by using the unpacked member of the OC Context.

When using this data format, as in this example, you need to set the data_model OCC member to PEXDataUnpacked and to set the vertex_coord_size, vertex_normal_size, vertex_color_size, and vertex_fp_data_size fields of the OCC. In addition, you pass pointers to each of the data arrays, four of them in this case, in the vertex data parameter of the primitive function call. To pass the four pointers, place them in the following structure and pass the address of the structure:

 
  typedef struct {
    PEXPointer    coords;
    PEXPointer    colors;
    PEXPointer    normals;
    PEXPointer    edges;
    PEXPointer    radii;
    PEXPointer    axes;
    PEXPointer    angles;
    PEXPointer    fp_data;
  } PEXUnpackedVertexData;
 
An example of using the unpacked data pointers:

 
  PEXUnpackedVertexData          verts;
 
  verts.coords = my_coords;
  verts.colors = my_colors;
  verts.normals = my_normals;
  verts.fp_data = my_fp_data;
 
  PEXOCCTriangleStrip(context, NULL, count, &verts);
 

When working with facet data, use the PEXUnpackedFacetData data structure. Some primitives, such as Fill Area Set and Polylines (Polyline Set) are defined by a nested list of vertices. Since you cannot work directly with a nested list of vertices, you use the PEXListOfVertexData data structure, which looks like:

 
  typedef struct {
    unsigned long      count;
    PEXPointer         vertices;
  } PEXListOfVertexData;
 

For each Fill Area Set Contour or Polyline in the set, you allocate one of these structures and fill it in with the number of points in that contour or line and a pointer to the first vertex in the list. Note that the type of the vertices pointer is PEXPointer, so you set this pointer to point to a PEXUnpackedVertex data structure, just as in the non-nested case. The list of these PEXListOfVertexData structures must be contiguous and you pass the address of the first one in the vertex data argument of the primitive function call.


Errors and Output Command Errors

For a list of related topics, see Programming Interface for Generating Output Commands.

The PEX output commands are designed so that the PEX implementation can often continue to process an OC with an error in it by using defaults or fall-back values. However, in some cases, it is not practical to define a reasonable fall-back value and so the PEX implementation stops processing OC's and generates an error, usually a BadPEXOutputCommand error.

Many BadPEXOutputCommand errors are very specific to the output command and are listed with the function. There are another set of errors that are common to a number of output commands and are not generally listed with each OC function.

BadPEXOutputCommand: Most cases are listed with each OC. Some of the causes common to many OC's are:

BadPEXStructurePermission: If you attempt to store an output command in a locked structure, the implementation generates this error.