Page tree

Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

  • once per rendering session
  • once per render (in an interactive session, there can be multiple renders)
  • once per batch of points to be shaded (i.e. RixShadingContext)
  • multiple times per batch of points to be shaded

and various granularitiesgranularity:

  • once per plugin
  • once per instance of the shading plugin (as defined in the next section)
  • multiple times per instance of the shading plugin

...

The other plugin types using this representation are: RixLightFactory.

RixBxdfFactory, RixDisplacementFactory, RixDisplayFilter, RixIntegrator, RixLightFilter, RixLightFactory, RixPattern, RixProjection, and RixSampleFilter. These are plugins that implement services for the renderer.

Here, it is important to distinguish between two types of plugins: ones that need to create short-lived lightweight instances during the course of a render, and ones that do not. RixBxdf, RixDisplacement, and RixLight represent lightweight instances that may be created many times during the course of a single render, and therefore are not directly subclasses of RixShadingPlugin. Instead, instancees of those classes are returned by the appropriate Factory (e.g. RixBxdfFactory), with the Factory itself being the subclass of RixShadingPlugin.

RixIntegrator, RixDisplayFilter, RixLightFilter, RixPatternRixProjection, and RixSampleFilter do not create many lightweight instances. As such, these classes are directly subclasses of RixShadingPlugin.

All RixShadingPlugins share common methods related to initialization, synchronization with the renderer, and management of lightweight instances.

...

Anchor
initialization
initialization
Plugin initialization

In order to initialize the plugin, the renderer will call Init() once. Even if the plugin is evoked multiple times during the render with different arguments, Init() will still be called only once during the lifetime of the render. The RixContext parameter can be used by the plugin to request any RixInterfaces services provided by the renderer. Any expensive memory allocations or operations that can be reused during the lifetime of the plugin can be performed in this routine. Upon successful initialization, this routine should return a zero status.

Finalize() is the companion to Init(), called at the end of rendering with the expectation that any data allocated within the Init() implementation will be released.

Anchor
getparamtable
getparamtable
Parameter Table

All shading plugins are expected to return a description of their input and output parameters via the GetParamTable() method. This returns a pointer to an array of RixSCParamInfo, containing one entry for each input and output parameter, as well as an extra empty entry to mark the end of the table. This parameter table is used by the renderer to ensure proper type checking and validate the connections of upstream and downstream nodes. As such, each entry in the table should set a name, a type (RixSCType enumeration), detail (varying vs uniform, RixSCDetail enumeration), and access (input vs output, RixSCAccess enumeration). These declarations also need to be kept in sync with the associated .args file.

For an example of usage, consider a pattern plugin which returns a color. The resultC output parameter is a color, so it is defined in the parameter table as:

RixSCParamInfo("resultC", k_RixSCColor, k_RixSCOutput)

A float input parameter named density can be defined as:

RixSCParamInfo("density", k_RixSCFloat)

While a float[16] input parameter named placementMatrix can be defined as:

RixSCParamInfo("placementMatrix", k_RixSCFloat, k_RixSCInput, 16

In order to initialize the plugin, the renderer will call Init() once. Even if the plugin is evoked multiple times during the render with different arguments, Init() will still be called only once during the lifetime of the render. The RixContext parameter can be used by the plugin to request any RixInterfaces services provided by the renderer. Any expensive memory allocations or operations that can be reused during the lifetime of the plugin can be performed in this routine. Upon successful initialization, this routine should return a zero status.

Finalize() is the companion to Init(), called at the end of rendering with the expectation that any data allocated within the Init() implementation will be released.

...

All shading plugins are expected to return a description of their input and output parameters via the GetParamTable() method. This returns a pointer to an array of RixSCParamInfo, containing one entry for each input and output parameter, as well as an extra empty entry to mark the end of the table. This parameter table is used by the renderer to ensure proper type checking and validate the connections of upstream and downstream nodes. As such, each entry in the table should set a name, a type (RixSCType enumeration), detail (varying vs uniform, RixSCDetail enumeration), and access (input vs output, RixSCAccess enumeration). These declarations also need to be kept in sync with the associated .args file.

For an example of usage, consider a pattern plugin which returns a color. The resultC output parameter is a color, so it is defined in the parameter table as:

RixSCParamInfo("resultC", k_RixSCColor, k_RixSCOutput)

A float input parameter named density can be defined as:

RixSCParamInfo("density", k_RixSCFloat)

While a float[16] input parameter named placementMatrix can be defined as:

RixSCParamInfo("placementMatrix", k_RixSCFloat, k_RixSCInput, 16)

The full implementation of GetParamTable() for this plugin would look something like this:

...

Note

In order to facilitate the reuse of the same parameter enumeration for pattern output computation, it is highly recommended that all outputs be placed at the beginning of the parameter table.

...

Plugin instance initialization

Depending on the paradigm used by the plugin type, CreateInstanceData() or CreateXXX() will be called once per plugin instance:

  • CreateInstanceData() allows the plugin instance to create private data (stored in InstanceData::data), that will then be provided back to the various plugin methods the renderer will call during rendering.
  • CreateXXX() returns a C++ object that should store its own representation of the plugin instance. The renderer will call methods on this object during rendering (instead of calling plugin methods).

Anchor
synchronization
synchronization
Plugin Synchronization

The Synchronize() routine allows the plugin to respond to synchronization signals delivered by the renderer. This call may happen multiple times during a render session and/or during a given render.

The renderer may provide additional information to the plugin via the input parameter RixParameterList. These signals include:

  • k_RixSCRenderBegin: The renderer is being initialized.
  • k_RixSCRenderEnd: The renderer is about to end.
  • k_RixSCInstanceEdit: Currently unused.
  • k_RixSCCancel: Currently unused.
  • k_RixSCCheckpointRecover: A signal that the renderer is about to restart rendering from

The Synchronize() routine allows the plugin to respond to synchronization signals delivered by the renderer. The renderer may provide additional information to the plugin via the input parameter RixParameterList. These signals include:

  • k_RixSCRenderBegin: The renderer is being initialized.
  • k_RixSCRenderEnd: The renderer is about to end.
  • k_RixSCInstanceEdit: Currently unused.
  • k_RixSCCancel: Currently unused.
  • k_RixSCCheckpointRecover: A signal that the renderer is about to restart rendering from a checkpoint. The parameter list will contain a single constant integer "increment" which contains the increment value from which the renderer will restart.
  • k_RixSCCheckpointWrite: A signal that the renderer is about to write a checkpoint. The parameter list will contain two values: a single constant integer "increment" indicating which contains the increment value from which the renderer will restart.
  • k_RixSCCheckpointWrite: A signal that the renderer is about to write a checkpoint. The parameter list will contain two values: a constant integer "increment" indicating the increment value the renderer will write, and a constant write, and a constant string "reason" which contains one of three values: "checkpoint", "exiting", or "finished", indicating why the renderer is writing the checkpoint.
  • k_RixSCIncrementBarrier: A signal that the rendering of an new increment is about to begin. This signal will only be received if the integrator has set wantsIncrementBarrier to true in the RixIntegratorEnvironment. The parameter list will contain a single constant integer "increment" which contains the increment value the renderer is about to render.

Plugin instance synchronization

Similarly to Synchronize(), the SynchronizeInstanceData() method allows the plugin instance to update its state when a render starts.

There are some subtle differences with Synchronize() though:

  • there is no synchronization message, it is always assumed to be k_RixSCRenderBegin
  • the plugin instance must subscribe to this mechanism, by appropriately setting InstanceData::synchronizeHints duringCreateInstanceData()

If the plugin type doesn't use CreateInstanceData() but CreateXXX(), the created objects will sometimes expose an Edit() or Synchronize() method.

Interactive rendering sessions

Because Init() and CreateInstanceData() are only called once during a rendering session, they are unable to capture edits that may have happened after the previous render.

It is therefore strongly recommended for these two methods to only rely on data that was explicitly provided (e.g. the plugin instance parameter list). In particular, special care should be taken not to query options or anything related to the render state (displays, integrator environment, lpe-related quantities, etc...) for the following reasons:

  • options may be edited after these methods have been called
  • displays and render state will most likely be undefined when these methods are called, and may be subsequently edited

In order to support general edits, and to be future-proof, the Synchronize() and SynchronizeInstanceData() should be used instead to query options and render state.

Subtleties

  • RixProjection (the type of objects created by RixProjectionFactory::CreateProjection() to represent projection plugin instances) doesn't expose a Edit() or Synchronize() method. Instead, RixProjection::RenderBegin() will be called, allowing for the plugin to set the RixIntegratorEnvironment::deepMetric. There is currently no way RixProjection plugins to opt-in / opt-out of the RixProjection::RenderBegin() call.
  • RixLight (the type of objects created by RixLightFactory::CreateLight() currently uses a mix of CreateInstanceData() and CreateLight() paradigms. It is strongly recommended to keep CreateInstanceData() empty and put the majority of the implementation in CreateLight().

Known limitations

  • RixBxdf::GetInstanceHints() is called after CreateInstanceData() but before SynchronizeInstanceData(). As a consequence, if a bxdf instance's hint depends on a global option or on the render state, edits will have no effect. This will lead to counter-intuitive behaviors, and it is strongly recommended for bxdf instance hints to only depend on the shader parameters provided to CreateInstanceData()
  • RixPattern::Bake2dOutput() and RixPattern::Bake3dOutput() are called after CreateInstanceData() but before SynchronizeInstanceData(). The same limitations as the one described in the item above apply.


Anchor
lightweightinstance
lightweightinstance
Lightweight instancing

For shading plugin types which support the creation of multiple lightweight instance classes not derived from RixShadingPlugin (i.e. RixBxdf, RixDisplacement, and RixLight), the renderer can potentially create many, many instances over the course of the render. Here, the term instance is unfortunately overloaded, and it is important to differentiate between: an instance of a shading plugin as defined by the unique set of parameters given to the material definition, and a C++ instance which involves an actual memory allocation, construction, and destruction of an object. Over the lifetime of a render there can be a one to many relationship between the former and the latter, and for performance reasons, it is important to keep the cost of the creation of the latter C++ objects low. In order to reduce the cost of these instantiations, the renderer offers the ability to track custom instance data with every shading plugin instance that can be shared and reused amongst all the C++ objects that share the same shading plugin instance.

...