Custom RSL Code Generation

Custom RSL Code Generation

Introduction

RenderMan Studio 4 includes a new RSL code generator designed to support a more natural expression of the shader pipeline dependencies in root nodes of a shader graph. We've implemented a small number of root-level shading nodes that should serve as excellent examples for facilities that wish to develop their own custom top-level shading nodes. Here, we present the specifications for the new capabilities.


The Anatomy of a Shader Template Node

Meta-information associated with RSL code is required to convey high-level semantics to our code generator. The meta-information is stored in the same file as the RSL source code and can be found below $RMSTREE/lib/rfm/rsl. Templates are either root-level or upstream nodes. Root-level nodes typically reside in a .sl file; upstream nodes are typically found in .h files suitable for inclusion in an RSL source stream. Meta-information resides within a simple XML node within an RSL comment block. An older container format, the .rslt file, shares many characteristics with the more modern representation, but is harder to read, debug, and maintain.

The rslt Container

The syntax for the XML container for rslt meta-information is simple:

/* --- a c-style comment ---
<rman id="rslt">
 (rslt data here)
</rman>
 ------ */

The rslt Data

The rslt data is a subset of the .slim file format. The basic structure of the rslt block of meta information is:

slim 1 extensions pixar_db {
  extensions pixar {} {
     template void GPSurface {
       userdata {
           rfm_nodeid 1053370
           rfm_classification \
       shader/surface:rendernode/RenderMan/shader/surface:swatch/rmanSwatch
       }
       codegenhints {
       }
       (Parameter blocks)
       (RSLInclude blocks)
       (RSLSource block)
    }
  }
}

An rslt block is a nested collection of blocks, each block is analogous to an XML node and the fields within a block are either additional nodes or fields, which are analogous to XML attributes. The key node types are:

  • Slim version contents author body: is the outermost husk of the rslt block, used to characterize the contents. In our case the usual contents are extensions.
    • extensions vendor prefix body: is the outer husk for all extensions, defines scopes and namespace/prefix conventions. You can define multiple templates within the extensions block, but in RfM's extension set there is typically a single extension in each rslt block.
      • template type name body: is the key block to define an RSL template.
        • userdata body: contains blind data that may be used by other subsystems. RfM defines attributes within user data to characterize Maya node identity and classification.
        • codegenhints body: conveys hints to the code generator. This is the key of the new functionality and will be described in detail below.
        • parameter type name body': enumerates per-parameter hints.
        • RSLInclude filename: conveys optional #include requirements to the code generator.
        • RSLSource type source: delivers the template source code to the code generator.

codegenhints

The job of the code generator is to produce RSL to represent the calling sequence characterized by a network of shading nodes. To support a completely encapsulated functional form for a root-level node, RMS 3 required that the root template be expressed as an RSL function. In order to produce a full-fledged shader we needed to know how that root level function "lives" within standard shader definition framework. The solution was to allow template designers to inject code in three different places (globals, pregen, postgen) relative to the generated code associated with a network. Here are the codegenhints for the Lambert node (located in the $RMSTREE/lib/rfm/rsl/mayaNodes/lambert.h).

codegenhints {
    codegenclass Surface_rfm
    globals {RFM_SURFACE_SETUP();}
    pregen {RFM_SURFACE_BEGIN();}
    postgen {RFM_SURFACE_END();}
}

With PRMan 16's introduction of the Plausible Shading framework, the need to characterize code generation within the more expressive context of RSL's standard shading pipeline became crucial. RMS 4 introduces a new codegenhint, shaderobject, that encapsulates the per-method dependencies on the parameters of the node. Coupled with special notation embedded in the actual RSL source (described below), it is now possible to write RSL shader objects in a natural and direct form. The shaderobject block is comprised of a list of blocks named by the shader object method. The contents of each block is a list of shader parameters that the method needs access to. The inter-dependencies of standard pipeline methods is built-in to the code generator, but pipeline methods may also depend upon custom shader object methods. A special notation, f:methodname, can be used to express these dependencies.

Here is an incomplete snippet that shows the new shaderobject hint in action:

codegenhints {
    shaderobject {
        displacement {
            displacementVector
            displacementAmount
        }
        prelighting {
           lightCategory
        }
        initSpec {
           f:prelighting
           roughness
           specularGain
           roughnessMap
        }
        initDiff {
           f:prelighting
           diffuseGain
       }
       diffuselighting {
           f:initDiff
           ior
           lightCategory
           albedo
           sssMix
        }

RSLSource

Within the context of the rslt block is the RSLSource keyword.

RSLSource ShaderPipeline _thisfile_

The a new RSLSource type, ShaderPipeline, activates the new code generator. The special keyword, _thisfile_, is a convenient means to refer to real RSL source when it is located in the same file as the rslt hint.

Closely coupled with the shaderobject codegenhint are special nuggets that you must embed directly in your RSL source. These nuggets indicate where the code generator should inject the results of its code generation.

  • RSLINJECT_preamble: indicates where to inject the required #include, #define and plugin requests associated with all nodes in the shader network context.
  • RSLINJECT_shaderdef: indicates where to inject the shader object declaration and parameter list. The parameter list may include parameters from any node in the shader network. Parameter list names are often "unique-ified" by the node-name from which the parameter originates.
  • RSLINJECT_members: indicates where to inject intermediate variable declarations that must exist across methods.
  • RSLINJECT_<methodname>: indicates where to inject code and variable declarations within the scope of the named method. Generally this hint should immediately follow the method definition and precede your code. Conceptually, the role of this injection is to make the variables described in your shaderobject codegenhint available in the correct form and computational state required for consumption by the code in your method. Variables will either be declared and computed within the context of your method or may have been computed earlier in the shading pipeline and hoisted to the status of member variable.

Here's a snippet of RSL with RSLINJECT hints present in typical locations:

 -- (snip) --
</rman>  (end of rslt)
 */
 RSLINJECT_preamble

 RSLINJECT_shaderdef
 {
    // your own member variables
    shader m_lights[];
    stdrsl_ShadingContext m_shadingCtx;
    stdrsl_Fresnel m_fres;
    stdrsl_Lambert m_diffuse;

  // member variables produced by code generator
  RSLINJECT_members

  // a standard pipeline method
  public void prelighting(output color Ci,Oi)
  {
      RSLINJECT_prelighting
      m_lights = getlights("category",lightCategory);
      LightSampleCount = max(1,floor(totalLightSamples/arraylength(m_lights)));
  }

  // a non-standard shader method: Shared initialization function for diffuse.
  public color initDiff()
  {
      RSLINJECT_initDiff
      color surfColor = surfaceColor * Cs;
      if(surfaceMap!= "")
      {
          color c = color texture(surfaceMap);
          surfColor *= c;
      }
      surfColor *= diffuseGain * Cs;
      m_diffuse->init(surfColor);
      m_Incandescence = incandescence;
      //Reduce the number of indirect samples based on diffuse ray depth
      if(m_docoshaders == 1)
      {
          stdrsl_SampleMgr mgr;
          mgr->computeDepthBasedSampleReduction(m_shadingCtx->m_DiffuseDepth,
                  indirectDiffuseSamples, 1,0,
                  m_indirectSamples);
      }
      return surfColor;
  }

    -- (snip:  more methods here) --

See Also

  • Real production templates are included here: $RMSTREE/lib/rfm/rsl/customNodes

  • The results of code generation can be found in the /shaders subdirectory of your current Maya project. By default, generated source is cleaned up. To defeat the cleanup, put the following in your RenderMan_for_Maya.ini (see the Initialization documentation for more information about .ini files and preferences):

    SetPref ShaderCompilerCleanup 0
    
  • The Slim File Format documentation.