The RtxPlugin API
The RtxPlugin API
November, 2010
Introduction
Release 16 of Pixar's PhotoRealistic RenderMan introduced a new texture plugin feature that allows users to write C++ code that responds to texture requests from the renderer by filling cache tiles on demand. This provides many benefits: the renderer can reuse the tiles once they are cached, the tiles are requested at a resolution appropriate for the given grid being shaded, and antialiasing is automatically performed by the texture filters built into the renderer.
The header file below provides the interface description for the C++ plugins.
#ifndef RTXPLUGIN_H #define RTXPLUGIN_H #include "ri.h" #include <stdarg.h> #include <assert.h> #include <string> #include <map> // A RixContext is provided to an RtxPlugin at // creation time, so that the plugin may use // services provided by the renderer. class RixContext; // An RtxPlugin class class RtxPlugin { public: RtxPlugin() {} virtual ~RtxPlugin() {} //! A struct to define an X,Y coordinate struct Int2D { int X; int Y; }; //! A struct to define a 2D region struct Tile2D { Int2D offset; Int2D size; }; /** TextureCtx provides initial defining properties for textures generated by this plugin. Each plugin should fill this structure during an Open() method call. Open() is called for each unique instance of a given texture() call in the shader. A unique instance is defined by the string passed to the file parameter of the texture() SL call. NOTE: The Open() method may be called multiple times for a texture as will Close(). On a Close() all resources should be released, and on an Open() they are safe to reaquire. **/ struct TextureCtx { /// This is the total number of channels present int numChannels; /// The maximum # of layers this texture can access. unsigned int numLayers; /// Plugin fills in the names and channel-count of the layers /// associated with this texture. Fill requests are characterized /// by the combination of layername, channelOffset and nchans. /// /// Memory for the array is owned and managed by the plugin struct layerSpec { const char *name; unsigned numChannels; } **layers; /// The min,max resolution requested for this texture. Int2D minRes, maxRes; /// The wrap mode applied at the edges of max resolution enum WrapMode { k_Black = 0, k_Clamp, k_Periodic } sWrap, tWrap; /// The type of the data provided by the texture enum DataType { k_Byte = 0, k_Float } dataType; /// What type of pyramid should be used? enum PyramidType { k_Single = 0, k_MIP, k_RIP } pyramidType; /// Should the tile be locked while filling? (not thread safe?) bool isLocked; /// Read-only texture args, they come in pairs, memory /// is owned by the renderer. unsigned int argc; const char **argv; /// The plugin can use this field to stash its own instance /// data. The plugin manages ownership. void *userData; }; /// The Open() method is called the first time a texture() encounters /// the plugin and provides a unique arg string. virtual int Open (TextureCtx& tCtx) = 0; struct FillRequest { /// This is the resolution of the image at a given MIP level Int2D imgRes; /// This is the tile index and tile size Tile2D tile; /// This is a channel selection string. This string can be /// NULL if channel selection is not made by string. const char *channelRefExpr; /// This is the offset from zero for a given channel selection /// It is always zero if channelRefExpr is not NULL. int channelOffset; /// This is the number of channels we want to fill int numChannels; /// The data (interleaved) that the plugin should write /// the tile results into RtPointer tileData; }; /// The plugin should use the fillReq inputs to write to the /// tileData in the FillRequest. virtual int Fill (TextureCtx& tCtx, FillRequest& fillReq) = 0; /// The plugin should release all assets at Close() virtual int Close (TextureCtx& tCtx) = 0; }; /// This is the single C-entry point for creation of the C++ /// object that represents the texture plugin #define RTXPLUGINCREATE\ extern "C" const PRMANEXPORT int RtxPluginVersion=1;\ extern "C" PRMANEXPORT RtxPlugin *RtxPluginNew(RixContext *rixCtx, \ const char *pluginName) #endif
Here is an example plugin that creates a zonePlate texture at a given frequency, defined by the URI string.
// A simple plugin to create a zone plate texture #include "RtxPlugin.h" #include <cstdlib> #include <cmath> #include <cstdio> class ZonePlate : public RtxPlugin { public: ZonePlate( ); virtual ~ZonePlate(); virtual int Open (TextureCtx& tCtx); virtual int Fill (TextureCtx& tCtx, FillRequest& fillReq); virtual int Close (TextureCtx& tCtx); RtFloat evalZonePlate(RtFloat x, RtFloat y, RtFloat k); }; ZonePlate::ZonePlate() {} ZonePlate::~ZonePlate() {} int ZonePlate::Open (TextureCtx &tCtx) { tCtx.numChannels = 1; tCtx.minRes.X = 1; tCtx.minRes.Y = 1; tCtx.maxRes.X = 32768; tCtx.maxRes.Y = 32768; tCtx.sWrap = TextureCtx::k_Black; tCtx.tWrap = TextureCtx::k_Black; tCtx.dataType = TextureCtx::k_Float; tCtx.pyramidType = TextureCtx::k_MIP; tCtx.isLocked = false; RtFloat *myVal = (RtFloat *)malloc(sizeof(RtFloat)); myVal[0] = 0.0f; if (tCtx.argc == 2) { myVal[0] = atof(tCtx.argv[1]); } // Provide a default frequency if (myVal[0] == 0.0f) { myVal[0] = 820.0f; } tCtx.userData = (RtPointer) myVal; return 0; } RtFloat ZonePlate::evalZonePlate (RtFloat x, RtFloat y, RtFloat k) { RtFloat r = sqrt(x*x + y*y); RtFloat result = (1.0f + cos(k * r*r)) / 2.0f; return result; } int ZonePlate::Fill (TextureCtx& tCtx, FillRequest& fillReq) { // This assumes the plugin creates a 1 chan, RfFloat format texture RtFloat *myVal = (RtFloat *) tCtx.userData; RtFloat dX = 1.0f / fillReq.imgRes.X; RtFloat dY = 1.0f / fillReq.imgRes.Y; RtFloat startX = (RtFloat)fillReq.tile.offset.X * fillReq.tile.size.X * dX; RtFloat fY = (RtFloat)fillReq.tile.offset.Y * fillReq.tile.size.Y * dY; RtFloat *fdata = (RtFloat *)fillReq.tileData; for (int y = 0; y < fillReq.tile.size.Y; y++, fY+=dY) { float fX = startX; for (int x = 0; x < fillReq.tile.size.X; x++, fX+=dX) { RtFloat zoneVal = evalZonePlate (fX-.5, fY-.5, myVal[0]); *(fdata++) = zoneVal; } } return 0; } int ZonePlate::Close (TextureCtx& tCtx) { if (tCtx.userData) { free (tCtx.userData); } return 0; } RTXPLUGINCREATE { return new ZonePlate(); }