Keywords: main

Next: 3.23 Automatic Source Generation Up: 3. Using and Writing Previous: 3.21 Functions for Shaders

# 3.22 Initialization and Cleanup

mental ray provides a way to define initialization and cleanup functions for each user defined function. Many shaders need to perform operations such as initializing color tables or allocating arrays before they are called for the first time. They may also need to do cleanup operations after rendering has finished, for operations like releasing storage to prevent memory leaks.

Before a shader is called for the first time, ray checks if a function of the same name with `_init` appended exists. If so, it assumes that this is an initialization function (also called init shader ) and calls it once before the first call of the shader. The state passed to the initialization function is the same as passed to the first call of the actual shader to be initialized. Note that the order of shader calls is unpredictable because the order of pixel samples is unpredictable, so the initialization function should not rely on sample-specific state variables such as state- >point.

The initialization function has the option of requesting shader instance initializations by setting the boolean variable its third argument points to to `miTRUE`. A shader instance is a unique pair of shader and shader parameters. For example, if the shader soft_material is used in two different materials it is said to have two different instances (even if the parameter values happen to be equal). When an init shader is called as part of the shader instance initialization, it receives the same parameters that the main shader will receive. As always, shaders may not write back into their shader parameters because that confuses other threads or hosts that access the shader simultaneously, because it changes the scene for the next frame, and because a shader or interface assignment may get mangled.

When rendering has finished, mental ray checks for each user provided shader that was called whether a function of the same name with `_exit` appended exists. If yes, it assumes that this is a cleanup function and calls it once. For example, if a shader myshader exists, the functions myshader_init and myshader_exit are called for initialization and cleanup if they exist.

The functions are assumed to have the following type:

         void myshader_init(miState   *state,
                            void      *paras,
                            miBoolean *inst_init_req);

         void myshader_exit(miState   *state,
                            void      *paras);

The `-i` option of the mkmishader utility automatically creates init and exit shader prototypes. Here is an example for init and exit shaders for a shader named myshader. When myshader is about to be used for the first time in a frame, the calling order is:

1.
  myshader_init with a null paras argument
2.
  myshader_init with non-null paras argument
3.
  myshader itself with the same non-null paras argument
4.
  more calls to myshader with the same paras argument, and calls to other instances of myshader_init and myshader with different paras arguments,
5.
  one mi_shader_exit with a non-null paras argument for each corresponding myshader_init
6.
  finally one myshader_exit with a null paras argument.

Steps 2 and 5 would have been omitted if myshader_init in step 1 had not set its third argument inst_req to `miTRUE`. Two different instances of the same shader always have different paras argument pointers. However, a shadow or photon shader and a material shader in the same material may share parameters as described above; in this case both shaders are called with the same paras argument. Most scenes are built this way, it is recommended to write material shaders so they can also be used as shadow and photon shaders to simplify scene construction.

    DLLEXPORT void myshader_init( /* must end with "_init" */
        miState         *state,
        struct myshader *paras,     /* valid for inst inits */
        miBoolean       *inst_req)  /* for inst init request */
    {
        if (!paras) {               /* main shader init */
            *inst_req = miTRUE;     /* want inst inits too */
            ...
        } else {                    /* shader instance init */
                                    /* just an example: */
            state->shader->user.p = mi_mem_allocate(...);
            ...
        }
    }

    DLLEXPORT void myshader_exit( /* must end with "_exit" */
        miState         *state,
        struct myshader *paras)     /* valid for inst inits */
    {
        if (!paras) {               /* main shader exit */
            ...                     /* no further inst exits
                                     * will occur */
        } else {                    /* shader instance exit */
                                    /* just an example: */
            mi_mem_release(state->shader->user.p);
            ...
        }
    }

Note that there will generally be many instance init/exits (if enabled), but only one shader init/exit. If an init/exit shader is not available, it is not called; this is not an error. Initialization and cleanup are done on every host where the function was used, but only once on shared memory parallel machines. They are done for each frame separately. Init and exit shaders will never run concurrently with each other or the actual shader on shared-memory multiprocessor machines; mental ray takes care of the appropriate locking using the shader lock. For this reason, init and exit shaders should never lock the shader lock (obtainable with the `miQ_FUNC_LOCK` mode of the mi_query function) because that would deadlock mental ray.

Next: 3.23 Automatic Source Generation Up: 3. Using and Writing Previous: 3.21 Functions for Shaders