Page tree

Versions Compared

Key

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

...

RixShadingPlugin is a base class characterizing the requirements of a RenderMan renderer from shading plugins: RixBxdfFactoryRixDisplacementFactoryRixDisplayFilterRixIntegratorFactory, RixLightFactoryRixLightFilterRixPatternRixProjectionFactory, and RixSampleFilter. These are plugins that implement services for the renderer.

This class provides entry points that are executed with a mix of various frequencies:

...

The other plugin types using this representation are: RixBxdfRixDisplacementRixDisplayFilterRixLightFilter and  RixSampleFilter. RixBxdf and RixDisplacement additionally make use of closures.Note that RixShadingPlugin

Note
RixShadingPlugin::CreateInstanceData() may be called in multiple threads, and so its implementation should be re-entrant and thread-safe.

XXX* RixYYYPlugin()::CreateXXX()

...

The other plugin types using this representation are: RixIntegrator and RixLightFactory.

Closures

(previous section title:  AnchorlightweightinstancelightweightinstanceLightweight instancing)Generally, the RixShadingPlugin subclass (e.g. RixPattern) will expose only a few compute methods (e.g. RixPattern::ComputeOutputParams()) that is executed once per batch of points (RixShadingContext), per plugin instance. When a specific class is used to represent the plugin instance (e.g. RixProjection), it will usually expose the compute methods itself (e.g. RixProjection::Project()).

...

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.

...

Anchor

...

dynamicparameters

...

dynamicparameters

...

Dynamic Parameters

A plugin can create its parameter table dynamically based on the parameters provided to each instance of the plugin. This dynamically created table is created using the CreateInstanceData() method, and should be saved in the paramtable member of the InstanceData. If the associated memory need to be freed, if should be taken care of in the freefunc() routine. Generally, static interfaces should be preferred over dynamic interfaces due to their extra memory expense. If the paramtable member remains null, all instances will share the parameter table returned by GetParamTable(). In order to prevent the renderer from filtering out dynamic parameters as bad inputs, a plugin that is using a dynamically created table should have a k_RixSCAnyType entry in its plugin parameter table.

Initialization and Synchronization

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.

Note

The Init() method will only ever be called once per plugin.

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).

The renderer provides the unique evocation parameters that triggered the creation of the plugin instance through the the RixParameterList class.

RixParameterList allows for the evaluation of the plugin instance parameters via the EvalParam() method. To aid in this, it allows for the querying via RixParameterList::GetParamInfo() of whether the parameters have been unset (and are therefore at their default value), set as a uniform value, or are part of a network  connection, i.e. the parameter is computed by an upstream node in the shading graph. A network connection is understood to be a varying quantity, and its value cannot be evaluated at the time that CreateInstanceData is evoked; this is why EvalParam() will return k_RixSCInvalidDetail if the parameter is a network connection. Otherwise, EvalParam() can be used to get an understanding of the uniform, non-varying parameters that are passed to the shading instance, and these can be used to perform any precomputations as needed.

Note

The CreateInstanceData() method will only ever be called once per plugin instance (a unique set of parameters).

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 a checkpoint. The

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.

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).

The renderer provides the unique evocation parameters that triggered the creation of the plugin instance through the the RixParameterList class.

RixParameterList allows for the evaluation of the plugin instance parameters via the EvalParam() method. To aid in this, it allows for the querying via RixParameterList::GetParamInfo() of whether the parameters have been unset (and are therefore at their default value), set as a uniform value, or are part of a network  connection, i.e. the parameter is computed by an upstream node in the shading graph. A network connection is understood to be a varying quantity, and its value cannot be evaluated at the time that CreateInstanceData is evoked; this is why EvalParam() will return k_RixSCInvalidDetail if the parameter is a network connection. Otherwise, EvalParam() can be used to get an understanding of the uniform, non-varying parameters that are passed to the shading instance, and these can be used to perform any precomputations as needed.

...

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 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 constant integer "increment" indicating the increment value the renderer will 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 from which 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.

Closures synchronization

Closures are transient objects with a very short lifetime, and are re-created with such a frequency that there is no need for synchronization mechanism.

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() to represent light plugin instances) 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().
  • RixIntegrator ((the type of objects created by RixIntegratorFactory::CreateIntegrator() to represent integrator plugin instances) exposes Synchronize(). This method uses a slightly different signature, including a synchronization message, and it is currently not possible to opt-in/opt-out of the synchronization calls.

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.
  • RixLight::Edit() and RixLight::SynchronizeInstanceData() are currently not called.

Summary

The following table summarizes the structure of the various RixShadingPlugin, in relation to initialization and synchronization. All plugins use the RixShadingPlugin::Init() and RixShadingPlugin::Finalize() calls.

...

RixShadingPlugin

subclass

...

Generates a closure

(per RixShadingContext)

...

InstanceData::data

...

  • 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 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.


Note

The Synchronize() method may be called multiples times during a rendering session, and in some cases during a render (for the 'increment barrier' message).

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 explicitly 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.

Note

The SynchronizeInstancedata() method will always be called before a new render starts.

Closures synchronization

Closures are transient objects with a very short lifetime, and are re-created with such a frequency that there is no need for synchronization mechanism.

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 plugin instance has been initialized. In some cases, this includes edits that may have happened before the render starts, yielding counter-intuitive behaviors.

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() to represent light plugin instances) 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().
  • RixIntegrator ((the type of objects created by RixIntegratorFactory::CreateIntegrator() to represent integrator plugin instances) exposes Synchronize(). This method uses a slightly different signature, including a synchronization message, and it is currently not possible to opt-in/opt-out of the synchronization calls.

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.
  • RixLight::Edit() and RixLight::SynchronizeInstanceData() are currently not called.

Misc

Overview

The following table summarizes the structure of the various RixShadingPlugin, in relation to initialization and synchronization. All plugins use the RixShadingPlugin::Init() and RixShadingPlugin::Finalize() calls.

Plugin

RixShadingPlugin

subclass

Instance
representation

Generates a closure

(per RixShadingContext)

bxdfRixBxdfFactoryInstanceData::dataYes, RixBxdf
displacementRixDisplacementFactoryInstanceData::dataYes, RixDisplacement
display filterRixDisplayFilterInstanceData::dataNo
integratorRixIntegratorFactoryRixIntegratorNo
lightRixLightFactoryRixLightNo
light filterRixLightFilterInstanceData::dataNo
patternRixPattern

InstanceData::data

No
projectionRixProjectionFactoryRixProjectionNo
sample filterRixSampleFilterInstanceData::dataNo

Examples

PxrDiffuse

First, note that while the bxdf plugin is published and exposed as 'PxrDiffuse', it is comprised of two classes:

  • PxrDiffuseFactory, that inherits from RixBxdfFactory, a subclass of RixShadingPlugin. This is the class that manages instance data and associated closures.
  • PxrDiffuse, that inherits from RixBxdf. This is the type used by the bxdf closures.

As an example of usage of instance data, consider the PxrDiffuse bxdf. Although it is a fairly trivial bxdf, it does handle presence and opacity, and the renderer passes instance data to the RixBxdfFactory::GetInstanceHints

Examples

PxrDiffuse

First, note that while the bxdf plugin is published and exposed as 'PxrDiffuse', it is comprised of two classes:

  • PxrDiffuseFactory, that inherits from RixBxdfFactory, a subclass of RixShadingPlugin. This is the class that manages instance data and associated closures.
  • PxrDiffuse, that inherits from RixBxdf. This is the type used by the bxdf closures.

As an example of usage of instance data, consider the PxrDiffuse bxdf. Although it is a fairly trivial bxdf, it does handle presence and opacity, and the renderer passes instance data to the RixBxdfFactory::GetInstanceHints() interface in order to get an understanding of the requirements for presence and opacity. In the following code, PxrDiffuseFactory checks its own presence parameter to see if it is a connection, knowing that its Args file only allows presence to be set to a default value (and therefore is trivially fully opaque) or is connected (and therefore requires the renderer to perform presence calculations). If it is connected, then it sets the instance data to be the same InstanceHints bitfield that is requested by the renderer from RixBxdf::GetInstanceHints.

Code Block
languagecpp
    plist->GetParamInfo(k_presence, &typ, &cnx1);
    if(cnx1 == k_RixSCNetworkValue)
    {
        if (cachePresence == 0)
        {
            req |= k_ComputesPresence;
        }
        else
        {
            req |= k_ComputesPresence | k_PresenceCanBeCached;
        }
    }

PxrDirt

For a more complicated example of instance data usage, consider the PxrDirt pattern. Its instance data routine caches the values of many uniform parameters and reuses them in PxrDirt::ComputeOutputParams(), knowing that its .args file prohibits those parameters from being set to network connections.

Code Block
languagecpp
    Data *data = static_cast<Data*>(instanceData->data);

    data->numSamples = 4;
    data->distribution = k_distributionCosine;
   Data *data->cosineSpread = 1.0f;
    data->falloff = 0.0f;static_cast<Data*>(instanceData->data);

    data->maxDistance>numSamples = 0.0f4;
    data->direction>distribution = k_directionOutsidedistributionCosine;
    data->raySpread>cosineSpread = 1.0f;

    params->EvalParam(k_numSamples, 0, &data->numSamples)data->falloff = 0.0f;
    data->numSamples>maxDistance = RixMax(1, data->numSamples)0.0f;
    params->EvalParam(k_distribution, 0, &data->distribution);data->direction = k_directionOutside;
    data->raySpread = 1.0f;

    params->EvalParam(k_cosineSpreadnumSamples, 0, &data->cosineSpread>numSamples);
    params->EvalParam(k_falloff, 0, &data->falloffdata->numSamples = RixMax(1, data->numSamples);
    params->EvalParam(k_maxDistancedistribution, 0, &data->maxDistance>distribution);
    params->EvalParam(k_directioncosineSpread, 0, &data->direction);

...

>cosineSpread);
    params->EvalParam(k_falloff, 0, &data->falloff);
    params->EvalParam(k_maxDistance, 0, &data->maxDistance);
    params->EvalParam(k_direction, 0, &data->direction);

...


Anchor
installation
installation
Installation

...