Page tree

You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 2 Next »

Contents

Options, Render State and editing

RenderMan 23 introduces the possibility of editing displays and LPEs during an interactive rendering session, without needing to restart the entire rendering session (e.g. translate the entire scene again).

As a consequence, several quantities that RixShadingPlugin could previously query in the RixShadingPlugin::Init() and RixShadingPlugin::CreateInstanceData() may change during the rendering session, leading to shading plugins using out-of-date values.

For example, the following may now change between renders:

  • resolution, number of displays, and other display-related options
  • contents of the RixIntegratorEnvironment
  • quantities available through the RixLPEInfo and RixCustomLPE interfaces

In order to avoid counter-intuitive behavior (e.g. restarting a render suddenly yields a very different render), it's is highly recommended for shading plugin to stop accessing these inside the Init() and CreateInstanceData(). Instead, the Synchronize() and (newly introduced) SynchronizeInstanceData() methods should be used, allowing the user to update the data stored for the plugin and plugin instances before a new render.

Note that:

  • RixLPEInfo and RixCustomLPE interfaces are not available in Init() and CreateInstanceData() anymore.
  • RixRenderState and calls to GetOptions() are still available, but may return out-of-date values. It is strongly recommended to move the associated code to the synchronization methods.

Plugin Instance Initialization

The return type of RixShadingPlugin::CreateInstanceData() has been changed from int to void.

    virtual void CreateInstanceData(RixContext& rixCtx, RtUString const handle,
                                    RixParameterList const* parameterList,
                                    InstanceData* instanceData)

Previously, when a value of -1 was returned, the renderer would ignore the InstanceData::data member, and nullptr would be provided as 'instancedata' to the various shading plugin methods (e.g. ComputeOutputParams()).

In 23, the renderer directly inspects the value of InstanceData::data to detect if the plugin instance allocated private instance data. The (newly introduced) SynchronizeInstanceData() will then be given a chance to update the contents of this private instance data, before the pointer is provided to the usual shading plugin methods.

Plugin Instance Synchronization

RixShadingPlugin::SynchronizeInstanceData() is a new API that allows user to update the private instance data associated with a plugin instance.

    virtual void SynchronizeInstanceData(RixContext& rixCtx, RtUString const handle,
                                         RixParameterList const* instanceParams,
                                         uint32_t const editHints,
                                         InstanceData* instanceData)


Because this method will be executed before a new render starts (i.e. after edits have been processed), it allows for options and render state queries to return up-to-date values.

For performance reasons, this method will only be called if the shading plugin instance has explicitly subscribed to it. This is done by setting the (new) InstanceData::synchronizeHints member to a non-zero value during CreateInstanceData(). This member is a bit field that the plugin instance can use to specify on which kind of edit category should the SynchronizeInstanceData() be called. Currently, only two values are exposed, but additional categories may be added shortly (e.g. k_DisplayEdit, k_OptionEdit, k_LPEEdit, etc...).

    enum SynchronizeHints
    {
        k_None = 0x00000000,
        k_All =  0xFFFFFFFF
    };

Example

Let's consider a plugin instance with a non-trivial RixShadingPlugin::CreateInstanceData() method, using option queries and storing the returned values in its own private instance data class.

int MyBxdfFactory::CreateInstanceData(RixContext& ctx, RtUString const handle,
                                      RixParameterList const* plist, InstanceData* result)
{
    MyInstanceData* data = new MyInstanceData;

    // This method fills the members of `data` required for RixBxdfFactory::GetInstanceHints().
    computeBxdfInstanceHints(data);

    // This method uses the RixRenderState object to query options values, that are then stored in
    // the `data` structure.
    RixRenderState* rst = (RixRenderState*)ctx.GetRixInterface(k_RixRenderState);
    doSomething(rst, data);

    result->data = data;
    result->freefunc = &ReleaseMyInstanceData;

    return 0;
}

Because MyBxdf::CreateInstanceData() will only be called once during an interactive rendering session, the option values stored in `data` may become out-of-date, yielding incorrect and/or counter-intuitive results.

In RenderMan 23, this plugin should do the following:

void MyBxdf::CreateInstanceData(RixContext& ctx, RtUString const handle,
                                RixParameterList const* plist, InstanceData* result)
{
    MyInstanceData* data = new MyInstanceData;

    // This method fills the members of `data` required for RixBxdfFactory::GetInstanceHints().
    computeBxdfInstanceHints(data);

    // Note: some members of the `data` structures are still initialized to their default values.
    // They will be updated for the current render during the call to SynchronizeInstanceData().
    result->data = data;
    result->freefunc = &ReleaseMyInstanceData;

    // We want SynchronizeInstanceData() to be called in all cases.
    result->synchronizeHints = RixShadingPlugin::SynchronizeHints::k_All;
}

void PxrSurfaceFactory::SynchronizeInstanceData(RixContext& ctx, RtUString const handle,
                                                RixParameterList const* plist, uint32_t editHints,
                                                InstanceData* result)
{
    MyInstanceData* data = static_cast<MyInstanceData*>(result);

    // This method uses the RixRenderState object to query options values, that are then stored in
    // the `data` structure.
    RixRenderState* rst = (RixRenderState*)ctx.GetRixInterface(k_RixRenderState);
    doSomething(rst, data);
}

If MyBxdfFactory::GetInstanceHints() relies on some member of the private instance data, CreateInstanceData() still needs to initialize these, because RixBxdfFactory::GetInstanceHints() is currently only called once per rendering session, before the first call to SynchronizeInstanceData().

A similar issue happens with RixPattern::Bake2dOutput() and RixPattern::Bake3dOutput(): these methods are run before SynchronizeInstanceData(), so it is important to ensure that CreateInstanceData() is properly initializing the required members of the private instance data.

Alternatively, if no computations need to happen in RixShadingPlugin::CreateInstanceData(), it is possible to delegate the management of the private instance data entirely to SynchronizeInstanceData() by doing the following:

void MyBxdfFactory::CreateInstanceData(RixContext& ctx, RtUString const handle,
                                       RixParameterList const* plist, InstanceData* result)
{
    // We do not allocate any memory for the private instance data here. This is done for each
    // render in SynchronizeInstanceData().

    // We want SynchronizeInstanceData() to be called in all cases.
    result->synchronizeHints = RixShadingPlugin::SynchronizeHints::k_All;
}

void MyBxdfFactory::SynchronizeInstanceData(RixContext& ctx, RtUString const handle,
                                            RixParameterList const* plist, uint32_t editHints,
                                            InstanceData* result)
{
    // Because this method is called before each render, we need to make sure we properly delete
    // the private instance data that was created for the previous render.
    if (result->data && result->freefunc)
    {
        (result->freefunc)(result->data);
    }

    MyInstanceData* data = new MyInstanceData;

    // This method uses the RixRenderState object to query options values, that are then stored in
    // the `data` structure.
    RixRenderState* rst = (RixRenderState*)ctx.GetRixInterface(k_RixRenderState);
    doSomething(rst, data);

    result->data = data;
    result->freefunc = &ReleaseMyInstanceData;
}