Writing Display Drivers for RenderDotC

Contents:
Introduction
Shared Libraries
DspyImageOpen
DspyImageQuery
DspyImageClose
DspyImageData
DspyImageDelayClose
Helper Functions


Introduction

Images created by RenderDotC may be saved to a graphics file, rendered to a display, or even sent to another program for further processing.  Handling image output is the responsibility of a display driver.  The RiDisplay() option selects a display driver by name.  This RIB example selects the TIFF display driver:
Display "ball.tif" "tiff" "rgba"
RenderDotC has an open display driver architecture.  It comes with several standard drivers for TIFF and framebuffer display.  The user can extend this set by writing custom drivers as described in this document.  Source code for a complete example display driver can be found in $RDCROOT/etc/d_zfile.c


Shared Libraries

As with other RenderDotC plug-ins, custom display drivers are compiled as shared libraries (DSO's on Unix, DLL's on Windows).  Each display driver should include this header file:
#include <ndspy.h>
Then it must implement these four entry points:
PtDspyError DspyImageOpen(PtDspyImageHandle *image,
    const char *drivername, const char *filename, int width, int height,
    int paramCount, const UserParameter *parameters, int iFormatCount,
    PtDspyDevFormat *format, PtFlagStuff *flagstuff)
PtDspyError DspyImageQuery(PtDspyImageHandle pvImage, PtDspyQueryType type,
    int size, void *p)
PtDspyError DspyImageClose(PtDspyImageHandle pvImage)
PtDspyError DspyImageData(PtDspyImageHandle image, int xmin, int xmax_plusone,
    int ymin, int ymax_plusone, int entrysize, const unsigned char *data)
It may optionally implement this one also:
PtDspyError DspyImageDelayClose(PtDspyImageHandle)
Each of these functions returns an error code of type enum PtDspyError.  Possible values are:
PkDspyErrorNone            successful completion
PkDspyErrorNoMemory        unable to allocate memory
PkDspyErrorUnsupported     unsupported operation requested
PkDspyErrorBadParams       bad parameters
PkDspyErrorNoResource      no resource available, file not found...
PkDspyErrorUndefined       no other error messages appropriate
Compilation details depend up the platform.

SGI mips3:    CC -I$RDCROOT/include -mips3 -n32 -O -elf -shared d_log10.c -o d_log10.so
SGI mips4:    CC -I$RDCROOT/include -mips4 -n32 -O -elf -shared d_log10.c -o d_log10.so
Visual C++:   cl -I%RDCROOT%/include -LD d_log10.c
Borland C++:  bcc32 -I%RDCROOT%/include -tWD d_log10.c
Linux:        g++ -I$RDCROOT/include -shared d_log10.c -o d_log10.so
BSD/OS:       g++ -I$RDCROOT/include -shared d_log10.c -o d_log10.so


DspyImageOpen

PtDspyError DspyImageOpen(PtDspyImageHandle *image,
    const char *drivername, const char *filename, int width, int height,
    int paramCount, const UserParameter *parameters, int iFormatCount,
    PtDspyDevFormat *format, PtFlagStuff *flagstuff)

The first argument is a pointer to an opaque pointer.  The display driver may create local data of any size and store a pointer to it in *image.  This is blind data as far as the renderer is concerned.  It will blindly pass that pointer as the first argument to subsequent entry points in the display driver.

The name of the display driver, as it was given to RiDisplay(), is passed in the drivername argument.  The file or display name from RiDisplay() is pased as filename.  The width and height arguments usually match the values that were given to RiFormat().  However, if either or both equals 0, the display driver must provide a suitable default.

The parameters array contains paramCount user parameters.  The UserParameter structure is defined in ndspy.h as follows:

typedef struct uparam {
    RtToken name;
    char vtype, vcount;
#ifdef __BORLANDC__
    char pad1, pad2;            /* Waste space for Visual C++ compatibility */
#endif
    RtPointer value;
    int nbytes;
} UserParameter;
where name and value are a token-value pair.  A character, vtype, represents the type of data stored in value and is interpreted as:
'i'    Integer
'f'    Floating point
's'    String
Since value can be an array of values, vcount represents the number of elements in that array.  The total size of the data pointed to by value is nbytes.  When DspyImageOpen() returns, the renderer is free to delete the array pointed to by value.  Therefore, the display driver should make a local copy if necessary.

The following user parameters are predefined by RenderDotC:
 
name vtype vcount purpose
NP 'f' 16 World to NDC transformation matrix
Nl 'f' 16 World to camera transformation matrix
near 'f' 1 Near clipping plane
far 'f' 1 Far clipping plane
origin 'i' 2 Origin of crop window within full image
OriginalSize 'i' 2 Size of full window (not crop window)
PixelAspectRatio 'f' 1 Aspect ratio from RiFormat (or default if -1)
Software 's' 1 Name of renderer
HostComputer 's' 1 Name of computer rendererd on

Additional user parameters may be passed in the token-value list of the Display command.

After user parameters comes the format of the data that will be sent to the display driver.  The number of color channels to be sent is iFormatCount.  The format array contains iFormatCount entries, one for each channel.  The PtDspyDevFormat structure is defined in ndspy.h as follows:

typedef struct {
    char *name;
    unsigned type;
} PtDspyDevFormat;
The name field will be one of "r", "g", "b", "a", or "z".  The other field, type, may be any of the following:
PkDspyNone
PkDspyFloat32
PkDspyUnsigned32
PkDspySigned32
PkDspyUnsigned16
PkDspySigned16
PkDspyUnsigned8
PkDspySigned8
The display driver may rearrange the entries in format to receive the data in a more convenient order.  However, the name fields must still point to the original strings as passed in from the renderer.  The display driver may also change type to reflect what type of data it can handle.  For example, a framebuffer display driver generally can't display more than 8 bits per color channel.  Setting type to PkDspyUnsigned8 will cause RenderDotC to rescale the quantization parameters and deliver unsigned 8 bit data to the driver.

The final argument is flagstuff.  The renderer passes in 0 and the display driver can set various bits (using bitwise OR) to indicate how it wants the data.  Available flags include:

PkDspyFlagsWantsScanLineOrder       Requests that data be sent in sequential, not random, order
PkDspyFlagsWantsEmptyBuckets        If a bucket is empty, send it anyway with all black pixels
PkDspyFlagsWantsNullEmptyBuckets    If a bucket is empty, call DspyImageData with data = NULL

DspyImageQuery

PtDspyError DspyImageQuery(PtDspyImageHandle pvImage, PtDspyQueryType type,
    int size, void *p)

The renderer may call DspyImageQuery() to get additional information from the display driver.  The first argument, pvImage, is either a handle returned by DspyImageOpen() or a NULL pointer.  The second argument represents the type of query that the renderer is making.  It can be one of the following:

PkSizeQuery        What are the width, height, and aspect ratio?
PkOverwriteQuery   Does the display driver overwrite the named file?
The renderer passes in a pointer p that addresses at least size bytes.  The display driver then copies the requested data into p.  Each query type has its own data structure, defined in ndspy.h:
typedef struct {
    PtDspyUnsigned32 width;
    PtDspyUnsigned32 height;
    PtDspyFloat32 aspectRatio;
} PtDspySizeInfo;
and:
typedef struct {
    PtDspyUnsigned8 overwrite;     Non-zero if display driver overwrites named file
    PtDspyUnsigned8 interactive;   Not used
} PtDspyOverwriteInfo;
A complete implementation of DspyImageQuery usually looks like this:
PtDspyError DspyImageQuery(PtDspyImageHandle pvImage,
    PtDspyQueryType querytype, int datalen, void *data)
{
    MyImageType image = (MyImageType)pvImage;
    PtDspyOverwriteInfo overwriteInfo;
    PtDspySizeInfo sizeInfo;

    if (datalen <= 0 || !data)
        return PkDspyErrorBadParams;

    switch (querytype) {
    case PkOverwriteQuery:
        if (datalen > sizeof(overwriteInfo))
            datalen = sizeof(overwriteInfo);
        overwriteInfo.overwrite = 1;
        overwriteInfo.interactive = 0;
        memcpy(data, &overwriteInfo, datalen);
        break;
    case PkSizeQuery:
        if (datalen > sizeof(sizeInfo))
            datalen = sizeof(sizeInfo);
        if (image) {
            if (!image->width || !image->height) {
                image->width = 640;
                image->height = 480;
            }
            sizeInfo.width = image->width;
            sizeInfo.height = image->height;
            sizeInfo.aspectRatio = 1.0f;
        }
        else {
            sizeInfo.width = 640;
            sizeInfo.height = 480;
            sizeInfo.aspectRatio = 1.0f;
        }
        memcpy(data, &sizeInfo, datalen);
        break;
    default:
        return PkDspyErrorUnsupported;
    }

    return PkDspyErrorNone;
}


DspyImageClose

PtDspyError DspyImageClose(PtDspyImageHandle pvImage)

This function closes the output and frees the resources pointed to by pvImage.


DspyImageData

PtDspyError DspyImageData(PtDspyImageHandle image, int xmin, int xmax_plusone,
    int ymin, int ymax_plusone, int entrysize, const unsigned char *data)

DspyImageData() should output a rectangle of pixels with upper-left corner at (xmin, ymin), width xmax_plusone - xmax, and height ymax_plusone - ymax.  The pixels to be output are passed in data in row-major order.  Each pixel is entrysize bytes.


DspyImageDelayClose

PtDspyError DspyImageDelayClose(PtDspyImageHandle)

If this optional function is present in a display driver, the renderer will start a separate thread and call it instead of DspyImageClose().  This allows the renderer to exit or continue with another frame while the display driver continues to run.  It is appropriate for drivers that render to a display.


Helper functions

Some useful functions for writing display drivers may be found in $RDCROOT/etc/dspyhlpr.c
They are also included here for convenience:


#include "ndspy.h"

PtDspyError DspyFindStringInParamList(const char *string, char **result,
    int n, const UserParameter *p)
{
    int i;

    for (i = 0; i < n; i++, p++)
        if (p->vtype == 's' &&
            p->name[0] == string[0] &&
            strcmp(p->name, string) == 0) {
            *result = *(char **)p->value;
            return PkDspyErrorNone;
        }

    return PkDspyErrorNoResource;
}

PtDspyError DspyFindMatrixInParamList(const char *string, float *result,
    int n, const UserParameter *p)
{
    int i;

    for (i = 0; i < n; i++, p++) {
        if (p->vtype == 'f' &&
            p->vcount == 16 &&
            p->name[0] == string[0] &&
            strcmp(p->name, string) == 0) {
            memcpy(result, (float *)p->value, 16 * sizeof(float));
            return PkDspyErrorNone;
        }
    }

    return PkDspyErrorNoResource;
}

PtDspyError DspyFindFloatInParamList(const char *string, float *result,
    int n, const UserParameter *p)
{
    int i;

    for (i = 0; i < n; i++, p++) {
        if ((p->vtype == 'f' || p->vtype == 'i') &&
            p->name[0] == string[0] &&
            strcmp(p->name, string) == 0) {
            if (p->vtype == 'f') {
                *result = *(float *)p->value;
                return PkDspyErrorNone;
            }
            else {
                *result = (float)(*(int *)p->value);
                return PkDspyErrorNone;
            }
        }
    }

    return PkDspyErrorNoResource;
}

PtDspyError DspyFindFloatsInParamList(const char *string, int *resultCount,
    float *result, int n, const UserParameter *p)
{
    int i, j, *ip;

    for (i = 0; i < n; i++, p++) {
        if ((p->vtype == 'f' || p->vtype == 'i') &&
            p->name[0] == string[0] &&
            strcmp(p->name, string) == 0) {
            if (p->vcount < *resultCount)
                *resultCount = p->vcount;
            if (p->vtype == 'f') {
                memcpy(result, (float *)p->value, *resultCount * sizeof(float));
                return PkDspyErrorNone;
            }
            else {
                for (j = 0, ip = (int *)p->value; j < *resultCount; j++)
                    *result++ = (float)*ip++;
                return PkDspyErrorNone;
            }
        }
    }

    return PkDspyErrorNoResource;
}

PtDspyError DspyFindIntInParamList(const char *string, int *result,
    int n, const UserParameter *p)
{
    int i;

    for (i = 0; i < n; i++, p++) {
        if ((p->vtype == 'i' || p->vtype == 'f') &&
            p->name[0] == string[0] &&
            strcmp(p->name, string) == 0) {
            if (p->vtype == 'i') {
                *result = *(int *)p->value;
                return PkDspyErrorNone;
            }
            else {
                *result = (int)(*(float *)p->value);
                return PkDspyErrorNone;
            }
        }
    }

    return PkDspyErrorNoResource;
}

PtDspyError DspyFindIntsInParamList(const char *string, int *resultCount,
    int *result, int n, const UserParameter *p)
{
    int i, j;
    float *fp;

    for (i = 0; i < n; i++, p++) {
        if ((p->vtype == 'i' || p->vtype == 'f') &&
            p->name[0] == string[0] &&
            strcmp(p->name, string) == 0) {
            if (p->vcount < *resultCount)
                *resultCount = p->vcount;
            if (p->vtype == 'i') {
                memcpy(result, (int *)p->value, *resultCount * sizeof(int));
                return PkDspyErrorNone;
            }
            else {
                for (j = 0, fp = (float *)p->value; j < *resultCount; j++)
                    *result++ = (int)*fp++;
                return PkDspyErrorNone;
            }
        }
    }

    return PkDspyErrorNoResource;
}

PtDspyError DspyReorderFormatting(int formatCount, PtDspyDevFormat *format,
    int outFormatCount, const PtDspyDevFormat *outFormat)
{
    PtDspyError ret = PkDspyErrorNone;
    int i, j;

    if (formatCount < outFormatCount)
        outFormatCount = formatCount;

    for (i = 0; i < outFormatCount; i++) {
        for (j = i; j < formatCount; j++) {
            if (format[j].name[0] == outFormat[i].name[0] &&
                strcmp(format[j].name, outFormat[i].name) == 0) {
                if (i != j) {
                    PtDspyDevFormat tmpFormat;
                    tmpFormat = format[i];
                    format[i] = format[j];
                    format[j] = tmpFormat;
                }
                if (outFormat[i].type)
                    format[i].type = outFormat[i].type;
                break;
            }
        }
        if (j >= formatCount)
            ret = PkDspyErrorBadParams;
    }

    return ret;
}

void DspyMemReverseCopy(unsigned char *t, const unsigned char *s, int n)
{
    int i;

    s += n;
    for (i = 0; i < n; i++)
        *t++ = *--s;
}


Copyright © 2001-2006 Dot C Software, Inc. All rights reserved.
Dot C Software, Inc., 182 Kuuhale St., Kailua, HI 96734
(808) 262-6715 (voice) (808) 261-2039 (fax)
The RenderMan® Interface Procedure and RIB Protocol are:
Copyright © 1988, 1989, Pixar. All rights reserved.
RenderMan® is a registered trademark of Pixar.