Implementing the compute() method for a dependency graph node

This topic provides some principles to keep in mind when implementing the virtual MPxNode::compute() method for a custom dependency node class.

Maya calls your node's MPxNode::compute() method when it determines that one of your node's output attributes, or plugs, is out-of-date and needs to be re-computed. It passes as arguments the plug that needs to be recomputed, and a data block that contains the current values of the input and output attributes for the current instance of your node. Your compute() method is expected to re-calculate the value of the requested plug based on the current values of the node's input attributes, and to store the computed output values back into the data block.

Performance matters

Assume that your node will be re-evaluated frequently. Strive to make its computations as efficient as possible.

For example, during rendering a node may need to be evaluated several times per pixel. If you do not take care to make its computations as fast as possible, the dependency graph can easily become a bottleneck.

Rely only on input attributes

The compute() method in every node must operate with only the data that is present in its parameters: i.e. the current values of its attributes.

If you need to use any other data in your computations, make that data into attributes. Even if it is only temporarily cached information to speed up computation, it could be useful as a hidden attribute to speed up the first refresh after file retrieval.

Change only output attributes

A node must not affect any values or objects apart from its output attributes. For instance, a node should not directly alter the DAG or any other dependency graph nodes. Doing so could result in an infinite loop: it could prompt a new evaluation of the dependency graph, which could cause your node to be re-evaluated, altering the DAG or dependency graph again and forcing a new dependency graph evaluation. If for some reason a node needs to alter something about its environment, you should encapsulate that knowledge as an attribute and connect the attribute to another component that can accept the computed data. Affecting any outside data will almost certainly violate one of the following:

Always check the requested plug

Always check which plug Maya is requesting you to recompute, and handle any unrecognized plugs by returning MS::kUnknownParameter.

For example:

{
    ...

    if ( (plug == myOutputPlug) || (plug == myOtherOutputPlug) )
    {
         ... // compute and set the requested output values here.
    }
    else
         return MS::kUnknownParameter;

    return MS::kSuccess;
}

This can avoid your node re-computing output attributes unnecessarily.

In addition, returning MS::kUnknownParameter will make Maya call the compute() method of the base class that you have derived your class from. If the attribute that needs to be recomputed is initialized in the base class and computed by its compute() method, the value will be automatically updated by the base class. Therefore, you do not need to handle any inherited attributes in your derived class. You may, however, override the way the base class handles any of its attributes in your implementation by updating its value and returning MS::kSuccess as you would for any attributes that are specific to your derived class.

Always set computed plugs as clean

When your node computes a new value for a requested plug and stores the value in the data block passed to the compute() method, always notify Maya that the plug's value has been newly computed by marking the plug as "clean" by calling:

data.setClean(plug);

and returning MS::kSuccess.

If you do not mark the plug as clean, Maya may request multiple re-evaluations of your node each time it refreshes the dependency graph.

Do not save references to data

Your node must not save a reference to any of the data that is passed in to the compute() method for a plug. If, for example, surface geometry comes in on a plug, do not save a pointer to this geometry. The data coming in on a plug is transitory and may cease to exist without you knowing it. You are only guaranteed that it will exist during the execution of your node’s compute() method.

Respond to the HasNoEffect node state, if necessary

If your node accepts a certain type of input attribute and produces the same type of data as an output, you may want to support the "HasNoEffect" or pass-through node state. Setting a node to this state typically results in the input attribute value being assigned directly to the output attribute, without any modifications. This allows a Maya user to bypass the node's calculations without needing to break any connections in the dependency graph.

For example, the following snippet from the splitUVCmd/splitUVNode.cpp sample retrieves the value of the state attribute, and tests to see if its value is 1, which corresponds to the "HasNoEffect" setting. If so, it simply returns the input attribute.

MStatus splitUVNode::compute( const MPlug& plug, MDataBlock& data )
{
    MStatus status = MS::kSuccess;
     
    MDataHandle stateData = data.outputValue( state, &status );
    MCheckStatus( status, "ERROR getting state" );

    if( stateData.asShort() == 1 )
    {
        MDataHandle inputData = data.inputValue( inMesh, &status );
        MCheckStatus(status,"ERROR getting inMesh");
        MDataHandle outputData = data.outputValue( outMesh, &status );
        MCheckStatus(status,"ERROR getting outMesh");
        // Simply redirect the inMesh to the outMesh for the PassThrough effect
        //
        outputData.set(inputData.asMesh());
    }
    ...
}

For details about node states and their values, see About the dependency graph.