Caching Data

It is possible to allocate a per-shader-instance cache for storage of frequently-accessed, constant items, such as time-consuming computations that can be done in advance (building internal tables, mi_query's etc.). It can also be used to cache constant input parameters (make certain the parameter has “texturable = off” in the SPDL file, to ensure users cannot attach other shaders to these inputs.), but this is not necessary in general since mental ray already does caching.

This data can be precomputed and released using the two special functions called _init and _exit functions. The first time the _init function is called, it is for the global initialization of the shader (and the parameter paras, in the code snippet below, is NULL). To do instance-specific initialization, set *inst_init_req to miTRUE (if you don't want this initialization, use miFALSE). mental ray will then call the _init and _exit functions for every instance of the shader.

When the _init function is called for instance-specific initialization of the shader (the parameter “paras” will now be non-NULL), calling mi_query() with the miQ_FUNC_USERPTR query code will return a pointer to a location in memory where a pointer to instance-specific user data can be stored.

The shader, when later called to compute a value, can use mi_query() with the miQ_FUNC_USERPTR query code to retrieve this pointer. For example:

/*
* shader initialization with instance storage
*/

DLLEXPORT void myshader_init(
miState *state,      // renderer state
myshader_par *par,   // shader input parameters
miBoolean *inst_req) // instance init request
{
if(!par) {                           // global shader initialization
*inst_req = miTRUE;              // request per-instance init
} else {                             // per-instance init

/*
* get instance data pointer, and allocate storage to it
*/
myshader_inst **shd_inst = NULL;
mi_query(miQ_FUNC_USERPTR, state, miNULLTAG, (void ***)&(shd_inst));
*shd_inst = (myshader_inst *) mi_mem_allocate(sizeof(myshader_inst));
}
return;
}

/*
* shader
*/

DLLEXPORT miBoolean myshader(
miColor *result,           // output color
miState *state,            // renderer state
myshader_par *par)         // shader input parameters
{

/*
* get and dereference instance data pointer
*/
myshader_inst **shd_inst, *inst;
mi_query(miQ_FUNC_USERPTR, state, miNULLTAG, (void ***)&(shd_inst));
inst = *shd_inst;

/*
* Shader, do your thing...
*/

return miTRUE;
}

Any storage allocated in the shader's instance initialization must of course be freed in the corresponding instance exit with a call to mi_mem_release().

/*
* shader cleanup with instance storage
*/

DLLEXPORT void myshader_exit(
miState *state,      // renderer state
myshader_par *par,   // shader input parameters
miBoolean *inst_req) // instance init request
{
if(par) {                            // instance cleanup

/*
* get instance data pointer, and free stored items
*/
myshader_inst **shd_inst = NULL;
mi_query(miQ_FUNC_USERPTR, state, miNULLTAG, (void ***)&(shd_inst));
mi_mem_release(*shd_inst);

} else {                             // global cleanup

}
return;
}

Do not confuse the instance-specific user data location with state->user, which is meant for storing a pointer to an arbitrary data structure for subsequent use by child shaders. Children of the first shader will inherit the pointer and may then read and write to it. state->user is best used for passing information down the call graph, and is not appropriate for long-term storage (as is the instance-specific user data location, which persists form one invocation of a shader to the next).

Finally, the _exit function will be called for every instance of the shader, and once for the global exit. During the instance-specific exit, the pointer to the user data should be retrieved, and any memory allocated should be disposed.

The example below shows how to do instance-specific initialization and to store instance-specific data with instances of your shader.

// Instance data structure 

typedef struct {
   miInteger type;
} instance_data;

// Init shader fills in the instance data 

DLLEXPORT void my_light_init(
   miState *state,
   struct my_light *paras,
   miBoolean *inst_init_req)
{
   miTag realLight;

   if (!paras)
   {
       /* request instance initialization */
       *inst_init_req = miTRUE;
   }
   else
   {
       /* using this to get the instance user data pointer */
       void **userptr;
       instance_data *idata;

       mi_query(miQ_FUNC_USERPTR, state, 0, &userptr);
       *userptr = (void *)mi_mem_allocate(sizeof(instance_data));

       idata = (instance_data *)&userptr;

       mi_query( miQ_INST_ITEM, NULL, state->light_instance, &realLight );
       mi_query( miQ_LIGHT_TYPE, NULL, realLight, &idata->type );
   }
}

// Release instance data 

DLLEXPORT void my_light_exit(
   miState   *state,
   struct my_light *paras)
{
   if (!paras)
   {
       /* main shader exit */
       /* nothing to do right now */
   }
   else
   {
       /* release our instance data */
       void  **userptr;

       mi_query(miQ_FUNC_USERPTR, state, 0, &userptr);

       if ( *userptr )
          mi_mem_release(*userptr);

       *userptr = NULL;
   }
}

// The shader function retrieves the instance data and uses it... 

DLLEXPORT miBoolean my_light(
   register miColor   *result,
   register miState   *state,
   register struct my_light *paras)
{
   instance_data *idata;
   void       **userptr;

   /*---- Get the handle to our instance data*/
   mi_query(miQ_FUNC_USERPTR, state, 0, &userptr);
   if (*userptr == NULL)
       return(miTRUE);

   idata = (instance_data *)*userptr;

   if ( idata->type == 2 )
   {
       // Spot light ...
   }

   ...
}


SOFTIMAGE|XSI v6.01     

Return to Softimage XSI Index