PhotoRealistic RenderMan
Display Driver Guide

Pixar
January, 1996

1. Introduction

The PhotoRealistic RenderMan rendering system produces raster graphics images from 3D shape and shading models. The images are output to image files or to hardware display devices (``frame buffers'') via the PhotoRealistic RenderMan display service. The service may be provided in a variety of ways and may produce output in a variety of forms. The display service may be customized by installing display drivers for specific frame buffer and image file types.

For convenience we define a special meaning for the following terms:

2. Theory of Operation

Display service requests are generated by the rendering system and transmitted to the display service code by means of procedure calls. The service requests are of the following types:

DspyProbe
determines if a device and it's display driver are available.
DspyRes
obtains device defaults for resolution and pixel aspect ratio.
DspyOpen
initializes the selected display driver.
DspySetWindow
writes a rectangular "window" of pixel data to the selected output device.
DspyClose
turns off the selected display driver, cleaning up as necessary.

The generic display service code (the part which is not device-specific) provides the ability to select a particular device from the available device drivers and to dispatch service requests to the device-specific routines.

In the renderer, the display device and file/unit name are selected by the RiDisplay routine of the RenderMan interface, as follows:

	RiDisplay(name, device, mode, RI_NULL);

The device is the name used to select the display device driver. This is normally file or framebuffer (RI_FILE or RI_FRAMEBUFFER). The name is a character string used to specify a unit or file name. Some device drivers may ignore the name, while others will use it to select a hardware unit, a named window on the display, or a particular image file. A null name pointer is converted to the empty string "". The mode is a predefined token used to select the fields of the output pixels; typical values are RI_RGB and RI_RGBA.

3. Display Driver Structure

A display device driver consists of a single C source code file which provides the device-specific display service subroutines. Each of the service routines returns an integer status code: 0 if successful, and -1 if something goes wrong. The exception is the probe routine, which returns 1 if the driver and display hardware are ``available'' and 0 if they are not. In order to create a new display device driver, it is necessary to write a C source code file (the driver) which defines device-specific subroutines for DspyRes, DspyOpen, DspySetWindow, DspyClose, and possibly DspyProbe (see below). In order to install a display device driver, an entry must be added to the display dispatch table which is in the source file This table entry includes the names of the device-specific routines, as well as various optional flags indicating device characteristics. The display service uses device-specific driver subroutines to perform operations requested via the display service protocol.

In the remainder of this section we present the C source code for a skeleton display driver, called d_skel.c.

3.1. Implementing DspyProbe

	/* skeleton code for PhotoRealistic RenderMan display driver */
	#define SERVER 1
	#include <stdio.h>
	#include <sys/types.h>
	#include "dspy.h"

Before attempting to use a display driver, the generic display service code calls the DspyProbe routine (named DspyProbe_devicename by convention) in the driver entry of the dispatch table. If the DspyProbe routine returns a non-zero value, the generic code assumes that the driver is ``available for use.'' If the DspyProbe routine returns zero, the generic code assumes that the driver cannot be used, perhaps because the hardware display device used by the driver is not installed. A dummy probe routine called DspyProbe_dummy is provided in the dispatch table source file. This dummy routine always returns 1, so it indicates that the driver is available, and may be used if this is appropriate. For example, it is usually appropriate for file output drivers. Otherwise, the device driver should supply a custom probe routine. In the DspyProbe routine, the unit or file name is available in DspyState.ds_filename if needed.

	int
	DspyProbe_skel()
	{
		if (hardware is not available)
			return 0;
		return 1;
	}

3.2. Implementing DspyRes

The device-specific DspyRes routine (named DspyRes_devicename by convention) is used by the DspyName generic service code to obtain appropriate default resolutions and device-specific pixel aspect ratio. (In the future, the DspyRes routine will also provide quantization defaults; see RiQuantize in the RenderMan Interface specification.) All drivers must set a reasonable default x and y resolution. For hardware display devices this will normally correspond to the maximum device resolution. Hardware display devices should set the pixel aspect ratio to the actual pixel aspect ratio of the display system. Most image file formats do not specify a pixel aspect ratio.

The unit or file name is available in DspyState.ds_filename if needed to determine resolution or pixel aspect ratio. This might be the case if the name selects a pre-defined window on the display or a pre-defined set of display parameters.

	int
	DspyRes_skel(xresp, yresp, aspectp)
		long *xresp, *yresp;
		float *aspectp;
	{
		/* all drivers must set xres and yres defaults */
		*xresp = 512;
		*yresp = 384;
		/* set pixel aspect ratio only if device has one */
		*aspectp = 1.0;
		return 0;
	}

3.3. Implementing DspyOpen

The device-specific DspyOpen routine (named DspyOpen_devicename by convention) is used by the DspyOpen generic service routine to perform device-dependent checks and to initialize the display device (or create/open the image file). The DspyOpen_devicename routine is called only once per image.

The format of the pixels which will be passed to the display driver is specified by the display pixel format, which is available in the global variable DspyState.ds_dpf of type struct display_pixel_fmt. The fields of this structure are

Each channel is stored in an integral number of bytes, so if dpf_alen is (for example) 18, the alpha channel of each pixel will consist of 3 bytes in the data stream. Any of dpf_alen, dpf_zlen, dpf_clen or dpf_ncolors may be zero, indicating that the corresponding channels are not in the data stream. The display pixel format should be checked against the limitations of the device. For example, a device which can display only 8 bits per color channel should check to make sure that dpf_clen is 8. A device which cannot make use of the alpha channel should arrange to ignore the alpha information in the data stream rather than simply reporting an error, since PhotoRealistic RenderMan images will normally include alpha information. The image will lie within a window specified by DspyState.ds_window (the struct window datatype is defined in the C include file dspy.h). This window should be checked against the limitations of the device. DspyOpen_devicename should return -1 if something is wrong with the window, the pixel format, or the device initialization.

The mergeflag parameter of DspyOpen_devicename is non-zero if and only if the rendering system needs to have new image data merged (composited) over existing image data in the file or frame buffer using the alpha channel. Although it is possible to ignore the mergeflag in a device driver, the following are guidelines for the recommended behavior:

	int
	DspyOpen_skel(mergeflag)
		int mergeflag;
	{
		struct display_pixel_fmt *pf;
		struct window *w;
		char *filename;
	
		/* check pixel format for device */
		pf = &DspyState.ds_dpf;
		if (pf->dpf_zlen > 0) {
			DspyError("DspyOpen_skel", "can't accept depth images.\n");
			return -1;
		}
		if (pf->dpf_alen > max || pf->dpf_clen > max
	 	|| pf->dpf_ncolors > max) {
			DspyError("DspyOpen_skel", "bad pixel format\n");
			return -1;
		}
	
		/* check window against device limitations */
		w = &DspyState.ds_window;
		if (w->xmin < minimum x || w->xmax > maximum x
	  	|| w->ymin < minimum y || w->ymax > maximum y) {
			DspyError("DspyOpen_skel", "bad window %d:%d,%d:%d\n",
				w->xmin, w->xmax, w->ymin, w->ymax);
			return -1;
		}
	
		if (DspyState.ds_filename == NULL
	  	|| *DspyState.ds_filename == '\0')
			filename = device-specific default;
		else
			filename = DspyState.ds_filename;
	
		Open the device or file as required.
		if (device open fails) {
			DspyError("DspyOpen_skel", "suitable complaint");
			return -1;
		}
		return 0;
	}

3.3.1. The DspyError Utility Function

The DspyError subroutine is provided by the display library to allow display drivers to output error messages in a standard way. The first argument to DspyError is the name of the subroutine where the problem was detected (or NULL) and the second argument is a printf format string. There may be one to four subsequent arguments corresponding to format items in the format string. The total length of the message including subroutine name and printf arguments should be less than 256 characters, and it is recommended that the message be as short as possible (to facilitate printing the message on one line, for example).

3.3.2. The DspyFindParameter Utility Function

The DspyOpen routine can respond to display options passed to it from the RIB stream by using the DspyFindParameter function. DspyFindParameter takes three arguments: a string with the name of the parameter; a character encoding the type of the parameter values; and an integer specifying the count of values in the parameter. The type must be either:

	'f', meaning floating point values (RtFloats);
	'i', meaning integers (RtInts);
	's', meaning character string values (RtStrings);
	'p', meaning point values (RtPoints); or
	'c', meaning color values (RtColors).

The routine returns a void *, pointing to a buffer which contains the data if it exists, or NULL if the parameter was not in the RIB stream.

In order to receive the parameter data, three things must happen. First, the parameter must be declared in the RIB stream using a Declare command. The full syntax of Declare can be used, as described on page 14 of the RenderMan Interface Version 3.1 Specification, except that the values must be declared to be uniform. This explains why type and count are defined as they are. Second, the parameter must be passed as parameters to the Display command which names this display device. Third, DspyFindParameter must be called with a type and count which exactly matches the RIB declaration. If this is done correctly, then DspyFindParameter will return a pointer to the data which can be cast as a pointer to an array of the correct data type, and the data can be retrieved.

For example, consider the following RIB fragment:

	Declare "compression" "string"
	Declare "compresscoeffs" "float[4]"
	...
	Display "foo.pic" "mydevice" "rgb"
		"compression" ["on"] "compresscoeffs" [2.0 0.0 4.5 -3.2]
	...

In DspyOpen_mydevice, the following code can be used:

	RtString *compression;
	RtFloat  *coeffs;
	...
	compression = DspyFindParameter( "compression", 's', 1 );
	if (compression != NULL && strcmp(compression[0], "on")==0) {
		coeffs = DspyFindParameter( "compresscoeffs", 'f', 4 );
		if (coeffs != NULL)
			mydevice_set_compression(coeffs);
	}

Remember in all cases that DspyFindParameter is returning a pointer to an array of the type requested, which is why compression is dereferenced above even though we know it is returning exactly one string.

3.4. Implementing DspyClose

The device-specific DspyClose routine (named DspyClose_devicename by convention) is used by the generic display service code to close the device driver when all pixel output has been completed. Note that the DspyClose_devicename routine is called only once per image.

	int
	DspyClose_skel()
	{
		Do device-dependent close/cleanup as necessary.
		if (Problems arise or close is not valid)
			return -1;
		return 0;
	}

3.5. Implementing DspySetWindow

The display-specific DspySetWindow routine (named DspySetW_devicename by convention) is used by the generic display service code to display a rectangular group of pixels. The arguments of the DspySetWindow routine are a pointer to a window structure which describes the position and size of the rectangular group of pixels, and a data pointer which points to a buffer which contains the actual pixel data values in ``little-endian'' order (least-significant byte first). The alpha channel is first, followed by the Z channel, and then the color channels. Any channel whose length in bits is 0 is not in the data stream. Recall that each channel is stored in an integral number of bits in the data stream. For example, if the display pixel format indicates no alpha channel, no Z channel, and three colors with 2 bits per color channel, the data stream would contain three bytes per pixel, one for each color channel. Each byte would have a value of 0, 1, 2, or 3, based on a value of 0 for minimum brightness and 3 for maximum brightness.

	int
	DspySetW_skel(w, data)
		struct window *w;
		u_char *data;
	{
		int n;	/* number of pixels in window if needed */

		n = (w->xmax - w->xmin + 1)
			* (w->ymax - w->ymin + 1);
		for (y = w->ymin; y <= w->ymax; y++) {
			for (x = w->xmin; x <= w->xmax; x++) {
				alpha channel begins at *data
				Z (depth) channel begins after alpha
				color channels begin after Z
				Remember: least significant byte first!!!

				/* increment to data for next pixel */
				data += DspyState.ds_pixbytes;
			}
		}
		return 0;
	}

4. Display Driver Installation

Recall from the earlier client/server discussion that display device drivers exist only in the dspyserver program in the UNIX implementation of the PhotoRealistic RenderMan display service. The PhotoRealistic RenderMan software distribution contains the source code and object code software modules required to install one or more display device drivers in the dspyserver program. This section presents step-by-step instructions for installing a device driver assuming that the source code file for the driver has already been written as described in the previous section. The d_skel.c source file presented in the previous section is used as an example. (Note that the source file d_skel.c provided with the PhotoRealistic RenderMan distribution is slightly different from the code presented in the previous section.)

All installation work takes place in a special dspyinst directory. The first step is to move to that directory:

	% cd ${RMANTREE}/etc/dspyinst

The next step is to edit the dispatch table to add an entry for the new display driver. The new entry should be placed at the beginning of the dispatch table if you want it to be the default file or framebuffer driver; otherwise place it after the entry which you wish to be the default. Edit dispatch.c with your favorite text editor and add the following (example for the ``skel'' driver):

	struct display_dispatch display_dispatch[] = { this line exists
	#ifdef	DISPLAY_SKEL
		{
			/* driver routines for the "skel" device */
			"skel", 0,
			DspyProbe_skel,
			DspyRes_skel, DspyOpen_skel, DspyClose_skel,
			DspySetW_skel
		},
	#endif	/* DISPLAY_SKEL */
		remainder of table

Note that inclusion of the driver in the dispatch table is controlled by conditional compilation based on the symbol DISPLAY_devicename.

The 0 value in the example table entry is a flag value indicating a framebuffer device which can accept DspySetW commands in any arbitrary sequence (a randomly addressable device). Some devices and most file formats require that pixels be sent to them in scanline order. The flag bit DSPY_SCANLINE causes the generic display service code to buffer the pixel data in a temporary file and output it to the device driver in scanline order, one DspySetW call per scanline. The flag bit DSPY_FILE indicates that the driver is to be considered a file driver rather than a framebuffer driver. This information is used only to select the first available driver of the appropriate type when file or framebuffer is requested by the client. A typical flag value for a file driver is (DSPY_SCANLINE | DSPY_FILE) while a typical flag value for a frame buffer driver is 0.

The dispatch table will not compile properly unless the display driver subroutines are declared prior to the dispatch table initialization. Insert the subroutine declarations in dispatch.h:

	#ifdef	DISPLAY_SKEL
	extern int DspyProbe_skel();
	extern int DspyRes_skel();
	extern int DspyOpen_skel();
	extern int DspyClose_skel();
	extern int DspySetW_skel();
	#endif	/* DISPLAY_SKEL */

5. Testing and Troubleshooting

If you have followed the procedure described in the previous sections, a new dspyserver containing your device driver should now be installed. This can be tested most easily with the command:

	% txdspy -dspy skel ${RMANTREE}/prman/etc/setup/logo_t

If the ``skel'' driver is properly installed, this command should output a small texture image of the logo texture from the PhotoRealistic RenderMan installation confidence test using the ``skel'' driver (of course, the ``skel'' driver shipped with the distribution will simply report each call to the driver routines; no image will be produced). If the driver is not installed properly, you will see an error message from dspyserver indicating that it couldn't find the ``skel'' display driver:

	D04001 DspyName: couldn't find requested driver: "skel" (WARNING)

dspyserver will then attempt to find an alternate file display driver (e.g., the tiff driver). The first step in troubleshooting is to make sure that the new dspyserver has been installed in its proper location by using the command:

	% ls -l ${RMANTREE}/etc/dspyserver

The modification time of the installed dspyserver should be the time that you installed your driver. If the modification time is wrong, try the make install command again, watching to make sure that the new dspyserver is successfully copied to the correct destination.

If the modification time seems correct, check whether the new driver was actually included in dspyserver:

	% strings ${RMANTREE}/etc/dspyserver | grep skel

If nothing is printed, the driver was not actually included when dspyserver was rebuilt. Make sure that the dispatch table entry was made correctly, that dispatch.c was compiled correctly, and that Makefile includes the driver object code file name in the DRIVERDEFS list.

If anything goes wrong, remember that you can copy the file dspyserver.std to ${RMANTREE}/etc/dspyserver to restore the original dspyserver which was shipped with the software distribution.


RenderMan Artist Tools Home Page
RenderMan Toolkit | ATOR | Combiner | Alfred | Looks | Textures
Pixar Home Page

Copyright © 1996 Pixar. All rights reserved. RenderMan® is a registered trademark of Pixar.
Pixar Animation Studios, 1001 West Cutting Blvd., Richmond, CA 94804
510-236-4000 (voice) 510-236-0388 (fax)