9.2 Writing Display Drivers
3Delight comes with a set of standard display drivers that are suitable for most applications (see section Display Drivers). However, it is possible to write custom display drivers if some specific functionality is needed. Basically, a display driver is a DSO (DLL under Windows) which implements an interface that 3Delight understands. This interface, along with all the data types used, is described in the `$DELIGHT/include/ndspy.h' header file and is further investigated in the following sections.
| 9.2.1 Required Entry Points | ||
| 9.2.2 Utility Functions | ||
| 9.2.3 A Complete Example | ||
| 9.2.4 Compilation Directives |
9.2.1 Required Entry Points
A display driver must implement four mandatory entry points:
- PtDspyError DspyImageQuery ( PtDspyImageHandle image, PtDspyQueryType type, size_t size, void *data )
Queries the display driver about format information.
- PtDspyError DspyImageOpen ( PtDspyImageHandle * image, const char *drivername, const char *filename, int width, int height, int paramcount, const UserParameter *parameters, int formatcount, PtDspyDevFormat *format, PtFlagStuff *flags)
Opens a display driver.
- PtDspyError DspyImageData ( PtDspyImageHandle image, int xmin, int xmax_plus_one, int ymin, int ymax_plus_one, int entrysize, const unsigned char *data )
Sends data to display driver.
An optional entry point is also defined:
- PtDspyError DspyImageDelayClose ( PtDspyImageHandle image )
Closes the display driver in a separate process. This means that renderer will exit leaving the display driver running. This is particularly useful for framebuffer-like display driver(68).
Every function is detailed in the following sections.
DspyImageQuery
This function is called for two reasons:
- 3Delight needs to know the default resolution of the display driver. This may happen if the user did not call
Format. - 3Delight needs to know whether the display driver overwrites or not the specified file (not used at the moment).
Parameters are:
- type
- Takes two possible values:
PkOverwriteQueryandPkSizeQuery. For each query, a different data structure needs to be filled. The structures are declared in `$DELIGHT/include/ndspy.h'. - size
- Maximum size of the structure to fill.
- data
- A pointer to the data to fill. Copy the appropriate structure here.
See section A Complete Example.
DspyImageOpen
Called before rendering starts. It is time for the display driver to initialize data, open file(s), .... Here is a description of all the parameters passed to this function.
- image
- This opaque pointer is not used in any way by 3Delight. It should be allocated and used by the display driver to pass information to
DspyImageDataandDspyImageClose. For instance, a TIFF display driver would put some useful information about the TIFF duringDspyImageOpenso thatDspyImageDatacould access the opened file. - drivername
- Gives the device driver name as specified by
Display. For example:Display "super_render" "framebuffer" "rgb"
provides `framebuffer' in drivername. - filename
- Gives the filename provided in the
Displaycommand. For example:Display "render.tif" "tiff" "rgb"
provides `render.tif' in filename. - width
- height
- Gives the resolution of the image, in pixels. If the image is cropped, width and height reflect the size of the cropped window.
- paramcount
- Indicates total number of user parameters provided in this call.
- UserParameter
- An array of user parameters, of size paramcount.
UserParameteris defined as:typedef struct { const char *name; char valueType, valueCount; const void *value; int nbytes; } UserParameter;name is the name of the parameter, valueType is its type, which can be one of the following: `i' for an integer type, `f' for an IEEE floating point type, `s' for a string type and `p' for a pointer type. valueCount is used for parameters that have more than one value, such as matrices and arrays. value is the pointer to the actual data, except for the `p' type where it is the data itself. A set of standard parameters is always provided; those are described in Table 9.1. - formatcount
- Number of output channels.
- formats
- An array of channel descriptions of size formatcount. A channel description contains a name and a type:
typedef struct { char *name; unsigned type; } PtDspyDevFormat;The type can take one of the following values and is modifiable by the display driver:PkDspyFloat32PkDspyUnsigned32PkDspySigned32PkDspyUnsigned16PkDspySigned16PkDspyUnsigned8PkDspySigned8
PkDspyByteOrderHiLoPkDspyByteOrderLoHi
- flags
- Modifiable flags to control the way data is sent to the display driver. This flag can be set to
PkDspyFlagsWantsScanLineOrderto receive the data per scanline instead of per bucket.
Parameters can be passed to a display driver when issuing the Display command:
Display "render" "my_display" "rgb" "string compression" "zip"
In this case, `my_display' driver receives the parameter "compression".
DspyImageOpen(). |
DspyImageData
3Delight calls this function when enough pixels are available for output.
Most of the time, this happens after each rendered bucket. However, if DspyImageOpen asks for scanline data ordering, a call to this function is issued when a row of buckets is rendered.
- image
- Opaque data allocated in
DspyImageOpen - xmin
- xmax_plus_one
- ymin
- ymax_plus_one
- Screen coordinates containing provided pixels data.
- entrysize
- Size, in bytes, of one pixel. For example, if eight bit RGBA data was asked, entrysize is set to four.
- data
- Pointer to the actual data, organized in row major order.
DspyImageClose
Called at the end of each rendered frame. It is time to free all resources that were used and free image (which was allocated by DspyImageOpen).
DspyImageDelayClose
If this entry point is defined in the display driver, it is called instead of DspyImageClose() with the difference being that the call occurs in a separate process so that 3Delight can exit without waiting for the display driver. The `framebuffer' display driver uses this functionality (see section The framebuffer display driver). This is not supported on Windows so it should be avoided whenever possible.
9.2.2 Utility Functions
IMPORTANTThe following functions are useful to search through the parameters list and to perform some other low-level tasks. Note that there is a slightly annoying side-effect: it becomes mandatory to link with `3delight.dll' on the Windows operating system see section Linking with 3Delight. This means that display drivers that use these functions are not directly usable with other RenderMan-compliant renderers and will have to be re-compiled for them (although with no other modfications).
- PtDspyError DspyFindStringInParamList ( const char *name, char **result, int parameters_count, const UserParameter *parameters )
- PtDspyError DspyFindFloatInParamList ( const char *name, float *result, int parameters_count, const UserParameter *parameters )
- PtDspyError DspyFindFloatsInParamList ( const char *name, int *result_count, float *result, int parameters_count, const UserParameter *parameters )
- PtDspyError DspyFindIntInParamList ( const char *name, float *result, int parameters_count, const UserParameter *parameters )
- PtDspyError DspyFindIntsInParamList ( const char *name, int *result_count, float *result, int parameters_count, const UserParameter *parameters )
- PtDspyError DspyFindMatrixInParamList ( const char *name, float *result, int parameters_count, const UserParameter *pthe arameters )
All these functions search a parameter of a specific type and size in the provided parameters list (as specified by
DspyImageOpen, refer to DspyImageOpen). If the desired parameter is found and the type matches, these functions will fill result with the found values and returnPkDspyErrorNone. So for example, to find afloat gamma[3]parameter, one could issue:float display_gamma[3]; float array_size; PtDspyError result = DspyFindFloatInParamList( "gamma", &array_size, &display_gamme, /* the ouputs */ paramCount, parameters); if( result == PkDspyErrorNone ) { /* can read 'array_size' floats in 'display_gamme'. */ ... }Notes:
- Colors, points, normals and vectors are considered as arrays of floats.
- Integers are converted to floating points.
- Both user provided parameters and standard parameters (see Table 9.1) are accesible with these functions.
- PtDspyError DspyReorderFormatting ( int format_count, PtDspyDevFormat *format, int out_format_count, const PtDspyDevFormat *out_format )
Reorders the format array in a specific order. Parameters:
- format_count
- format
- This is the format channels description as provided by DspyImageOpen. It contains all the channels provided to this display driver.
- out_format_count
- The size of the out_format character array.
- out_format
- The desired ordering. For example: "bgra".
If a display driver wants the channels in an "bgra" ordering it can call this function as follows:
/* the formats will be re-ordered in-place. */ DspyReorderFormatting( format_count, formats, 4, "bgra" );
- void DspyMemReverseCopy ( unsigned char *target, const unsigned char *source, int len )
Reverse len bytes, from source to target.
- void DspyError ( const char *module, const char *fmt )
Output an error message using the specified formatting. For example:
DspyError( "dspy_tiff", "cannot open file '%s'\n", file_name );
9.2.3 A Complete Example
/*
Copyright (c)The 3Delight Team.
All Rights Reserved.
*/
//
// = LIBRARY
// 3delight
// = AUTHOR(S)
// Aghiles Kheffache
// = VERSION
// $Revision$
// = DATE RELEASED
// $Date: 2009-04-06 21:43:34 -0400 (Mon, 06 Apr 2009) $
// = RCSID
// $Id: zfile.cpp 12168 2009-04-07 01:43:34Z aghiles $
//
#include <ndspy.h>
#include <uparam.h>
#include <ri.h>
#include <assert.h>
#include <stdio.h>
#include <float.h>
#include <string.h>
#include <limits.h>
/* ZFile Display Driver Implementation */
const unsigned kDefaultZFileSize = 512;
static void stderr_error( int type, int severity, char *msg )
{
/* just ignore type and severity. */
fprintf( stderr, "%s", msg );
}
/*
zfile format:
zFile format is (matrices and image are row-major):
magic # (0x2f0867ab) (4 bytes)
width (short) (2 bytes)
height (short) (2 bytes)
shadow matrices (32 floats, 16 for NP and 16 for Nl) (128 bytes)
image data (floats) (width*height*4 bytes)
NOTE
Matrices are stored in row major format.
*/
class zFile
{
public:
zFile(
const char* fileName,
const float* np, const float* nl,
unsigned short width, unsigned short height )
: m_file(0x0), m_width(width), m_height(height),
m_currentLine(0), m_pixelsLeftOnLine(width)
{
m_file = fopen( fileName, "wb" );
if( m_file )
{
unsigned long magic = 0x2f0867ab;
assert( sizeof(long) == 4 );
fwrite( &magic, 4, 1, m_file );
fwrite( &m_width, sizeof(m_width), 1, m_file );
fwrite( &m_height, sizeof(m_height), 1, m_file );
fwrite( np, sizeof(float), 16, m_file );
fwrite( nl, sizeof(float), 16, m_file );
}
}
~zFile()
{
if( m_file )
{
fclose(m_file);
}
}
bool Valid() const { return m_file != 0x0; }
unsigned GetWidth() const {return m_width;}
unsigned GetHeight() const {return m_height;}
bool WriteScanline(
unsigned short y, unsigned short size, const float* data )
{
if( y != m_currentLine || size > m_pixelsLeftOnLine )
{
return false;
}
m_pixelsLeftOnLine -= size;
if( m_pixelsLeftOnLine == 0 )
{
++m_currentLine;
m_pixelsLeftOnLine = m_width;
}
return fwrite( data, sizeof(float), size, m_file ) == size;
}
private:
FILE* m_file;
unsigned short m_width;
unsigned short m_height;
unsigned short m_currentLine;
unsigned short m_pixelsLeftOnLine;
};
/*
A utility function to get user parameters ...
*/
const void* GetParameter(
const char *name,
unsigned n,
const UserParameter parms[] )
{
for( unsigned i=0; i<n; i++ )
{
if(0 == strcmp(name, parms[i].name))
{
return parms[i].value;
}
}
return 0x0;
}
/*
Open
*/
PtDspyError DspyImageOpen(
PtDspyImageHandle *i_phImage,
const char *i_drivername,
const char *i_filename,
int i_width, int i_height,
int i_parametercount,
const UserParameter i_parameters[],
int i_numFormat,
PtDspyDevFormat i_format[],
PtFlagStuff *flagstuff )
{
int i;
bool zfound = false;
const float* nl =
(float*)GetParameter( "Nl", i_parametercount, i_parameters );
const float* np =
(float*)GetParameter( "NP", i_parametercount, i_parameters );
RtErrorHandler error_handler =
(RtErrorHandler)GetParameter(
"errorhandler", i_parametercount, i_parameters );
if( !error_handler )
{
/* could happen if display driver is not run from 3Delight. */
error_handler = &stderr_error;
}
/* Loop through all provided data channels and only ask for the 'z'
channel. */
for( i=0; i<i_numFormat; i++ )
{
if( strcmp(i_format[i].name, "z") != 0 )
{
i_format[i].type = PkDspyNone;
}
else
{
i_format[i].type = PkDspyFloat32;
zfound = true;
}
}
if( !zfound )
{
/* An example on how to call the error message handler. */
(*error_handler)(
RIE_CONSISTENCY, RIE_ERROR, "dspy_z : need 'z' in order to proceed.\n" );
return PkDspyErrorUnsupported;
}
if( !nl || !np )
{
(*error_handler)( RIE_CONSISTENCY, RIE_ERROR,
"dspy_z : need Nl & Np matrices in order to proceed. bug.\n" );
return PkDspyErrorBadParams;
}
if (i_width > USHRT_MAX || i_height > USHRT_MAX)
{
(*error_handler)(
RIE_LIMIT, RIE_ERROR,
"dspy_z : image too large for zfile format" \
" (use shadowmap display driver).\n" );
return PkDspyErrorUndefined;
}
zFile* aZFile = new zFile( i_filename, np, nl, i_width, i_height );
if( !aZFile || !aZFile->Valid() )
{
(*error_handler)
( RIE_SYSTEM, RIE_ERROR, "dspy_z : cannot create file.\n" );
delete aZFile;
return PkDspyErrorNoResource;
}
*i_phImage = (void*) aZFile;
/* Ask display manager to provide data scanline by scanline
*/
flagstuff->flags |= PkDspyFlagsWantsScanLineOrder;
return PkDspyErrorNone;
}
/*
DspyImageQuery
*/
PtDspyError DspyImageQuery(
PtDspyImageHandle i_hImage,
PtDspyQueryType i_type,
size_t i_datalen,
void *i_data )
{
zFile *aZFile = (zFile*) i_hImage;
if( !i_data )
{
return PkDspyErrorBadParams;
}
switch( i_type )
{
case PkSizeQuery:
{
PtDspySizeInfo sizeQ;
if( aZFile )
{
sizeQ.width = aZFile->GetWidth();
sizeQ.height = aZFile->GetHeight();
sizeQ.aspectRatio = 1;
}
else
{
sizeQ.width = kDefaultZFileSize;
sizeQ.height = kDefaultZFileSize;
sizeQ.aspectRatio = 1;
}
memcpy(
i_data, &sizeQ,
i_datalen>sizeof(sizeQ) ? sizeof(sizeQ) : i_datalen );
break;
}
case PkOverwriteQuery:
{
PtDspyOverwriteInfo overwQ;
overwQ.overwrite = 1;
memcpy(
i_data, &overwQ,
i_datalen>sizeof(overwQ) ? sizeof(overwQ) : i_datalen );
break;
}
default:
return PkDspyErrorUnsupported;
}
return PkDspyErrorNone;
}
/*
DspyImageData
Data is expected in scanline order (as asked in DspyImageOpen()).
*/
PtDspyError DspyImageData(
PtDspyImageHandle i_hImage,
int i_xmin, int i_xmax_plusone,
int i_ymin, int i_ymax_plusone,
int i_entrySize,
const unsigned char* i_data )
{
zFile* aZFile = (zFile*) i_hImage;
const float* fdata = (const float*) i_data;
if( !aZFile || !fdata )
{
return PkDspyErrorBadParams;
}
/* Perform some sanity checks but everything should be fine really ...
:> */
if( i_ymax_plusone - i_ymin > 1 ||
i_xmin != 0 ||
i_xmax_plusone != aZFile->GetWidth() ||
i_entrySize != sizeof(float) )
{
return PkDspyErrorBadParams;
}
if( !aZFile->WriteScanline(i_ymin, i_xmax_plusone - i_xmin, fdata) )
{
return PkDspyErrorNoResource;
}
return PkDspyErrorNone;
}
/*
DspyImageClose
delete our object.
*/
PtDspyError DspyImageClose( PtDspyImageHandle i_hImage )
{
zFile* aZFile = (zFile*) i_hImage;
if( !aZFile )
{
return PkDspyErrorUndefined;
}
delete aZFile;
return PkDspyErrorNone;
}
9.2.4 Compilation Directives
Here is the compilation command line for the given example (`zfile.cpp'):
- Linux
g++ -shared -o zfile.so -I$DELIGHT/include zfile.cpp- IRIX
CC -shared -o zfile.so -I$DELIGHT/include zfile.cpp- MacOS X
g++ -dynamiclib -o zfile.so -I$DELIGHT/include -arch "ppc" zfile.cpp- Windows
cl -I"%DELIGHT%/include" -LD zfile.cpp
3Delight 8.5. Copyright 2000-2009 The 3Delight Team. All Rights Reserved.