...
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. ThereforeHere, 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 instantiation 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 unique evocation of the plugin. By unique evocation, we generally mean the unique set of parameters given to the material definitionshading plugin instance that can be shared and reused amongst all the C++ objects that share the same shading plugin instance.
The shading plugin can create instance create private instance data using the CreateInstanceData()
method. Instance data is usually a function of the instance parameters (would typically be computed from the unique evocation parameters, supplied to CreateInstanceData
via the RixParameterList
), and using these parameters class.
This occurs when the shading plugin instance is created for the first time from those parameters, which is at the time the material definition is created (i.e. very early on in a render). Using these parameters, plugins may bake a cached understanding of their behavior/, requirements, or even precomputed results into a private representation that the renderer will automatically track with the instance and supply back to the plugin methods. This allows the plugin to cache and therefore avoid repeated computations with each new lightweight instantiation.
If the shading plugin does create instance data, it should be stored in the data field of the InstanceData
struct. If the data requires non-trivial deletion, the freefunc
field of the InstanceData
struct should be set to a function that the renderer will invoke when the plugin instance will no longer be needed. A trivial implementation of CreateInstanceData()
produces no instance data and returns a non-zero value.
Any instance data that is created will be automatically returned to the shading plugin methods by the renderer when the lightweight instance is created - for example, when RixBxdfFactory::BeginScatter()
is invoked to create a RixBxdf
. The implementation of BeginScatter()
is now free to use this instance data to reduce the cost of creating the associated RixBxdf
.
The RixParameterList
class 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.
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 RixBxdf
interface in order to get an understanding of the requirements for presence and opacity. In the following code, PxrDiffuse
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 | ||
---|---|---|
| ||
plist->GetParamInfo(k_presence, &typ, &cnx1);
if(cnx1 == k_RixSCNetworkValue)
{
if (cachePresence == 0)
{
req |= k_ComputesPresence;
}
else
{
req |= k_ComputesPresence | k_PresenceCanBeCached;
}
} |
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 ComputeOutputParams()
, knowing that its Args file prohibits those parameters from being set to network connections.
Code Block | ||
---|---|---|
| ||
Data *data = static_cast<Data*>(instanceData->data);
data->numSamples = 4;
data->distribution = k_distributionCosine;
data->cosineSpread = 1.0f;
data->falloff = 0.0f;
data->maxDistance = 0.0f;
data->direction = k_directionOutside;
data->raySpread = 1.0f;
params->EvalParam(k_numSamples, 0, &data->numSamples);
data->numSamples = RixMax(1, data->numSamples);
params->EvalParam(k_distribution, 0, &data->distribution);
params->EvalParam(k_cosineSpread, 0, &data->cosineSpread);
params->EvalParam(k_falloff, 0, &data->falloff);
params->EvalParam(k_maxDistance, 0, &data->maxDistance);
params->EvalParam(k_direction, 0, &data->direction); |
Note |
---|
|
Anchor | ||||
---|---|---|---|---|
|
...