C++ API Reference

Base class for user defined GPU deformer override evaluators. More...

#include <MPxGPUDeformer.h>

Public Member Functions

 MPxGPUDeformer ()
 Constructs an MPxGPUDeformer.
 
virtual ~MPxGPUDeformer ()
 Destructor.
 
virtual DeformerStatus evaluate (MDataBlock &block, const MEvaluationNode &evaluationNode, const MPlug &outputPlug, unsigned int numElements, const MAutoCLMem, const MAutoCLEvent, MAutoCLMem, MAutoCLEvent &)
 This method is deprecated and should no longer be used. More...
 
virtual DeformerStatus evaluate (MDataBlock &block, const MEvaluationNode &evaluationNode, const MPlug &outputPlug, const MGPUDeformerData &inputData, MGPUDeformerData &outputData)
 This method is deprecated and should no longer be used. More...
 
virtual DeformerStatus evaluate (MDataBlock &block, const MEvaluationNode &evaluationNode, const MPlug &outputPlug, const MPlugArray &inputPlugs, const MGPUDeformerData &inputData, MGPUDeformerData &outputData)
 evaluate() is called each time the deformation chain is evaluated. More...
 
virtual void terminate ()
 This method is called when the GPU deformer is being destroyed. More...
 
MGPUDeformerBuffer createOutputBuffer (const MGPUDeformerBuffer &inputBuffer)
 This method creates an output buffer based on inputBuffer. More...
 
bool getIndexMapper (MIndexMapper &imap)
 This method will update the IndexMapper information if required. More...
 
MObject getFixedSetupData (const MString &name)
 This method can retrieve data from the related node that is implemented for the CPU. More...
 
bool isFalloffWeightsDirty () const
 This method tells the GPU deformer if it need to ask weight from the deformer again the plug weightFunction might not be dirty (function is constant) but the falloff function might depends on the current geometry. More...
 

Static Public Member Functions

static bool hasAttributeBeenModified (const MEvaluationNode &evaluationNode, const MObject &attribute)
 This method returns true if the data for attribute on the overridden deformation node has been modified since the last call to MPxGPUDeformer::evaluate(). More...
 
static bool hasPlugBeenModified (const MPlug &plug)
 This method returns true if the data for plug on the overridden deformation node has been modified since the last call to MPxGPUDeformer::evaluate(). More...
 
static MUniqueString sPositionsName ()
 This method returns the name of position buffers on the GPU. More...
 
static MUniqueString sGeometryMatrixName ()
 This method returns the name of geometry matrix buffers on the GPU. More...
 
static MUniqueString sInverseGeometryMatrixName ()
 This method returns the name of inverse geometry matrix buffers on the GPU. More...
 
static bool isFalloffWeightsDependantOnCurrentGeometry (const MEvaluationNode &evaluationNode, unsigned int multiIndex)
 This method tells the GPU deformer if the weight function requires the current geometry to compute. More...
 

Detailed Description

Base class for user defined GPU deformer override evaluators.

MPxGPUDeformer lets you create user-defined GPU deformer overrides. A GPU deformer override replaces the CPU implementation of a deformer node when the evaluation manager is enabled and the deformerEvaluator plug-in is enabled. Use MPxGPUDeformer to override the deformation for a Maya deformer or for a plug-in deformer implemented through MPxDeformerNode. MPxGPUDeformer must register which node type it overrides using MGPUDeformerRegistry.

MPxGPUDeformer defines a deformer node as a node which has input geometry and output geometry. MPxGPUDeformer assumes that the number of vertices in the input and output geometry of a deformer node are the same. This definition includes nodes which are traditionally thought of as deformer nodes, such as skinCluster or blendShape, but also includes nodes like groupParts, which may be part of deformation chains.

To ensure optimal performance when you implement MPxGPUDeformer, keep the following in mind:

  • Calls to the constructor must be fast. Do not do heavy work in the constructor because the deformer evaluator may allocate MPxGPUDeformer instances which are never used. Save heavy work for the evaluate() method.
  • Cache needed values on the graphics card during evaluate(). Use the MEvaluationNode interface to determine if input values are constant or change over time.

If you use this interface, you must implement the virtual method evaluate() for MPxGPUDeformer to function. There are several older deprecated signatures of evaluate(). See the evaluate() documentation for additional requirements on the evaluate() method. The terminate() method is optional.

About the deformer evaluator:

The deformer evaluator identifies chains of supported nodes terminated by a mesh. The deformer evaluator then replaces CPU evaluation of these nodes with GPGPU kernels. The final deformed geometry is directly shared with Viewport 2.0, which avoids any GPU read back. When you implement an MPxGPUDeformer for a given node type, you expand the list of deformer evaluator supported nodes, which then allows more deformer chains to execute on the GPU.

A deformer chain is created by identifying an animated display mesh and then following geometry connections upstream until source plugs, that meet any one of the following criteria, is reached:

  • On a node not supported by deformer evaluator.
  • On a node which does not depend on time.
  • Contains fan out connections.

When one of the preceding conditions is true for a source plug, that source plug is considered an input to the deformation chain, and the corresponding source plug node is not evaluated in the deformer evaluator.

Example Chains:

  • HistoryNode: any node (supported or unsupported) which does not depend on time.
  • origMesh: the original mesh shape: the ultimate source of the deformation chain.
  • SupportedNode: a node that the deformer evaluator explicitly supports that also depends on time.
  • UnsupportedNode: any node that the deformer evaluator does not support that also depends on time.
  • displayMesh: the direct connection to Viewport 2.

Example 1:

  • HistoryNode1 -> origMesh -> HistoryNode2 -> SupportedNode1 -> SupportedNode2 -> displayMesh

When the deformer evaluator initializes it identifies HistoryNode2.outputData as the source plug. The first time that the deformer evaluator runs on example 1, the HistoryNode2 geometry output is copied to the GPU. SupportedNode1 and SupportedNode2 then run kernels to perform the deformations, and the final deformed result is then shared with VP2. Subsequent evaluations re-use the copy of HistoryNode2 output geometry already on the GPU, which avoids expensive data transfer.

Example 2:

  • HistoryNode1 -> origMesh -> HistoryNode2 -> UnsupportedNode1 -> SupportedNode1 -> displayMesh

In this scenario, UnsupportedNode1 runs on the CPU and generates an intermediate result. This intermediate result is copied to the GPU. Once copied, SupportedNode1 runs its kernel and displayMesh shares data with VP2. The intermediate result is copied to the GPU every frame, resulting in slower performance.

Example 3:

  • HistoryNode1-> origMesh -> HistoryNode2 -> SupportedNode1 -> UnsupportedNode1 -> displayMesh

In this scenario, the deformer evaluator may create a chain with just displayMesh in it, or it may do nothing. If we performed SupportedNode1's deformation on the GPU, we would need to read back that data and use it as an input for UnsupportedNode1 on the CPU. Read back is not supported by the deformer evaluator. If the mesh geometry is large then a chain of only displayMesh may be used in order to compute mesh normals on the GPU.

Example 4:

  • origMesh1 -> SupportedNode1.outMesh[0] -> displayMesh1
  • origMesh2 -> SupportedNode1.outMesh[1] -> displayMesh2

In this scenario, the deformer evaluator creates two chains, one for displayMesh1 and a second for displayMesh2. These chains both run on the GPU. Note that if SupportedNode1 is derived from MPxDeformerNode, then there can be zero relationship between the mesh data used in displayMesh1 and displayMesh2. displayMesh1 could have 100 vertices, and displayMesh2 could have one million vertices.

Example 5:

  • origMesh1 -> SupportedNode1.outMesh[0] -> displayMesh1
  • origMesh2 -> SupportedNode1.outMesh[1] -> UnsupportedNode1 ->displayMesh2

In this scenario, the deformer evaluator does nothing. We do support the first chain for displayMesh1, but we do not support the chain for displayMesh2. The deformer evaluator does not support the partial override of a node. In this case, SupportedNode1 has only partial support because the deformer evaluator can override evaluation for outMesh[0] but not for outMesh[1]. This prevents deformer evaluator from doing any GPU work in this scenario.

The deformer evaluator allocates a unique MPxGPUDeformer object for each supported node in each supported chain. In Example 4, two MPxGPUDeformer objects are allocated for SupportedNode1, one for multi-element 0 and a second for multi-element 1.

The deformer evaluator's emphasis on avoiding geometry read back from the GPU means that unsupported nodes that follow a deformation chain exclude that chain from GPU evaluation.

Example 6:

  • origMesh1 -> SupportedNode1.outMesh -> BlendShape.outMesh[0] -> displayMesh1
  • origMesh2 -> SupportedNode2.outMesh -> BlendShape.inputTarget[0].inputTargetGroup[0].inputTargetItem[6000].inputGeomTarget

In this scenario the blend shape node has an animated inputGeom as well as an animated blend shape target. The deformer evaluator forms a single chain which includes SupportedNode1, SupportedNode2, BlendShape and displayMesh1. MGPUDeformerRegistrationInfo::inputMeshAttributes() for the blend shape node returns both inputGeomTarget and inputGeom as attributes to search upstream of for additional GPU deformers.

In this case SupportedNode1 and SupportedNode2 may run in any order, or in parallel with each other. BlendShape won't run until all predecessor deformers have finished evaluation.

Example 7:

  • origMesh1 -> SupportedNode1.outMesh -> BlendShape.outMesh[0] -> displayMesh1
  • origMesh2 -> UnsupportedNode1.outMesh -> BlendShape.inputTarget[0].inputTargetGroup[0].inputTargetItem[6000].inputGeomTarget

In this scenario we have a combination of Example 2 and Example 6. GPU Override will form a chain including SupportedNode1, BlendShape and displayMesh1. When MPxGPUDeformer::evaluate() is called for BlendShape, inputData will include GPU buffers for BlendShape.input[0].inputGeometry and BlendShape.inputTarget[0].inputTargetGroup[0].inputTargetItem[6000].inputGeomTarget. The buffers coming from UnsupportedNode1.outMesh will automatically be copied to the GPU every frame.

Examples:
basicMorphNode/basicMorphNode.cpp, identityNode/identityNode.cpp, and offsetNode/offsetNode.cpp.

Member Function Documentation

MPxGPUDeformer::DeformerStatus evaluate ( MDataBlock block,
const MEvaluationNode evaluationNode,
const MPlug outputPlug,
unsigned int  numElements,
const MAutoCLMem  inputPositionBuffer,
const MAutoCLEvent  inputEvent,
MAutoCLMem  outputPositionBuffer,
MAutoCLEvent outputEvent 
)
virtual

This method is deprecated and should no longer be used.

Instead use MPxGPUDeformer::evaluate(MdataBlock&, const MEvaluationNode&, const MPlug&, const MPlugArray&, const MGPUDeformerData&, MGPUDeformerData&). The provided implementation of this function returns kDeformerFailure. Derived classes should only implement one version of the evaluate function.

Parameters
[in]blockThe data block of the deformer node to be overridden.
[in]evaluationNodeThe evaluation node of the deformer to be overridden.
[in]outputPlugThe output plug of the deformer to be overridden.
[in]numElementsThe number of float3 elements in inputPositionBuffer and outputPositionBuffer.
[in]inputPositionBufferThe handle to the input positions on the GPU.
[in]inputEventThe event that must be completed before it is safe to read values in inputPositionBuffer.
[in]outputPositionBufferThe handle of the output positions on the GPU.
[out]outputEventThe event which must be completed before it is safe to read outputPositionBuffer.
Returns
  • MPxGPUDeformer::kDeformerSuccess if a kernel was successfully created and enqueued.
  • MPxGPUDeformer::kDeformerFailure if no kernel can be created.
  • MPxGPUDeformer::kDeformerRetryMainThread if evaluate() requires access to the d3d/gl context.
  • MPxGPUDeformer::kDeformerPassThrough if the inputPositionBuffer should be used as the inputPositionBuffer by the next deformer.
Examples:
basicMorphNode/basicMorphNode.cpp, identityNode/identityNode.cpp, and offsetNode/offsetNode.cpp.
MPxGPUDeformer::DeformerStatus evaluate ( MDataBlock block,
const MEvaluationNode evaluationNode,
const MPlug outputPlug,
const MGPUDeformerData inputData,
MGPUDeformerData outputData 
)
virtual

This method is deprecated and should no longer be used.

Instead use MPxGPUDeformer::evaluate(MdataBlock&, const MEvaluationNode&, const MPlug&, const MPlugArray&, const MGPUDeformerData&, MGPUDeformerData&). Derived classes should only implement one version of the evaluate function.

Parameters
[in]blockThe data block of the deformer node to be overridden.
[in]evaluationNodeThe evaluation node of the deformer to be overridden.
[in]outputPlugThe output plug of the deformer to be overridden.
[in]inputDataThe input data for this deformer, including geometry.
[in]outputDataThe output data for this deformer, including geometry.
Returns
  • MPxGPUDeformer::kDeformerSuccess if a kernel was successfully created and enqueued.
  • MPxGPUDeformer::kDeformerFailure if no kernel can be created and CPU evaluation should be performed.
  • MPxGPUDeformer::kDeformerRetryMainThread if evaluate() requires access to the d3d / gl context.
  • MPxGPUDeformer::kDeformerPassThrough if the inputPositionBuffer should be used as the inputPositionBuffer by the next deformer.
MPxGPUDeformer::DeformerStatus evaluate ( MDataBlock block,
const MEvaluationNode evaluationNode,
const MPlug outputPlug,
const MPlugArray inputPlugs,
const MGPUDeformerData inputData,
MGPUDeformerData outputData 
)
virtual

evaluate() is called each time the deformation chain is evaluated.

evaluate() is given an input MGPUDeformerData and an output MGPUDeformerData. The input MGPUDeformerData holds one or more named MGPUDeformerBuffer objects which represent the input data for the deformer.

evaluate() should enqueue kernels to perform the deformation. Each kernel must:

  • wait for the bufferReadyEvents of the input MGPUDeformerBuffers used by the kernel.
  • read the input MGPUDeformerBuffer data.
  • write the output MGPUDeformerBuffer data.
  • save an event into the output MGPUDeformerBuffer bufferReadyEvent, which indicates that it is safe to read the output buffer when the event is complete.

It is safe to enqueue multiple kernels from evaluate(). It is safe to read values from the data block and evaluation node passed into evaluate(). It is not necessarily fast to read values from the data block. Reading the value of MPxGeometryFilter::inputGeom or MPxGeometryFilter::outputGeom may caused unexpected CPU evaluation to occur. This evaluation is safe but slow. It is safe to read values for the deformation node being overridden by creating MPlug objects to read values. It is not safe to access data on any other node or data structure during evaluate(). evaluate() may be invoked in parallel with other dependency graph evaluation or calls to evaluate() for other deformer nodes. Any output buffer written to by evaluate() must be added to outputData. Any input buffer not written to by evaluate() will automatically appear as an input to the next downstream deformer. Any buffer in outputData will automatically appear as an input to the next downstream deformer.

If an implementation of evaluate() must be executed with access to the D3D / GL context because of some GPGPU to D3D / GL interop, then evaluate() returns kDeformerRetryMainThread. evaluate() is called a second time for that frame; this call is guaranteed to have safe access to the GPU context.

MGPUDeformerData inputData contains several MGPUDeformerBuffer objects created by deformer evaluator:

  • "positions" Holds a GPU representation of the input vertex positions.
  • "geometryMatrix" Holds a GPU representation of the geometry matrix.
  • "inverseGeometryMatrix" Holds a GPU representation of the inverse geometry matrix

Input vertex positions is a cl_mem object that stores the input mesh positions as float3 elements. Input vertex positions is readable but may or may not be writable. The vertices stored in input vertex positions are stored in the same order that MItGeometry visits the vertices. It is not safe to read input vertex positions until the buffer ready event in the input MGPUDeformerBuffer is complete.

Geometry matrix stores the geometry matrix as 16 float values in row major order.

Inverse geometry matrix stores the inverse geometry matrix as 16 float values in row major order.

This is the same order convention as other matrices in Maya, represented as MMatrix.

The MGPUDeformerBuffers in inputData may be read-only or read-write. evaluate() must create a new MGPUDeformerBuffer object to be used as the output. This MGPUDeformerBuffer can be safely created on the stack, it will be copied when it is added to outputData.

The output MGPUDeformerBuffer must have the same name as the input buffer and be backed by GPU storage large enough to hold the deformer output. createOutputBuffer() will automatically create an output buffer which is appropriate to use in most cases. It is not safe to hold any reference to a cl_mem object returned by createOutputBuffer() after the end of evaluate(). createOutputBuffer() never creates an MGPUDeformerBuffer which is read only.

Each output buffer must be added to outputData for that data to be available to downstream deformers. When a buffer is added to outputData its values are copied and stored in a separate structure, any further modification to the members of the buffer will not be reflected in the buffer in outputData.

If the evaluate() function returns kDeformerSuccess then outputData must contain at least a position buffer. Additional output buffers are optional.

If the evaluate() function returns kDeformerPassThrough then outputData must not contain a position buffers. Additional output buffers are optional.

The provided implementation of MPxGPUDeformer::evaluate(MdataBlock&, const MEvaluationNode&, const MPlug&, const MGPUDeformerData&, MGPUDeformerData&) calls the deprecated version of this function evaluate(MDataBlock&, const MEvaluationNode&, const MPlug&, unsigned int, const MAutoCLMem, const MAutoCLEvent, MAutoCLMem, MAutoCLEvent&) for backwards compatibility.

Derived classes should only implement one version of the evaluate function.

Parameters
[in]blockThe data block of the deformer node to be overridden.
[in]evaluationNodeThe evaluation node of the deformer to be overridden.
[in]outputPlugThe output plug of the deformer to be overridden.
[in]inputPlugsThe list of input plugs which have data in inputData.
[in]inputDataThe input data for this deformer, including geometry.
[in]outputDataThe output data for this deformer, including geometry.
Returns
  • MPxGPUDeformer::kDeformerSuccess if a kernel was successfully created and enqueued.
  • MPxGPUDeformer::kDeformerFailure if no kernel can be created and CPU evaluation should be performed.
  • MPxGPUDeformer::kDeformerRetryMainThread if evaluate() requires access to the d3d / gl context.
  • MPxGPUDeformer::kDeformerPassThrough if the inputPositionBuffer should be used as the inputPositionBuffer by the next deformer.
void terminate ( )
virtual

This method is called when the GPU deformer is being destroyed.

Any data stored on this object should be released.

Examples:
basicMorphNode/basicMorphNode.cpp, identityNode/identityNode.cpp, and offsetNode/offsetNode.cpp.
MGPUDeformerBuffer createOutputBuffer ( const MGPUDeformerBuffer inputBuffer)

This method creates an output buffer based on inputBuffer.

If inputBuffer is read only then the returned MGPUDeformerBuffer will be backed by a different cl_mem object large enough to hold the data stored in inputBuffer. If inputBuffer is writable then the returned MGPUDeformerBuffer is backed by the same cl_mem as inputBuffer. The MGPUDeformerBuffer returned by createOutputBuffer() is never read only.

The plug associated with outputBuffer will be the output plug corresponding to the MPxGPUDeformer, the same plug which would be passed as a parameter into a call to evaluate().

It is not safe to hold the MGPUDeformerBuffer returned by createOutputBuffer after the end of the evaluate() call. It is not safe to hold a reference to the cl_mem underlying an MGPUDeformerBuffer after the end of the evaluate() call.

createOutputBuffer uses a pool of cl_mem objects to fulfill requests for output buffers. This avoids allocating a new writable cl_mem object each time the deformation chain is evaluated.

Parameters
[in]inputBufferThe input version of the buffer we would like to create an output buffer for.
Returns
An MGPUDeformerBuffer to be used as an output buffer.
bool hasAttributeBeenModified ( const MEvaluationNode evaluationNode,
const MObject attribute 
)
static

This method returns true if the data for attribute on the overridden deformation node has been modified since the last call to MPxGPUDeformer::evaluate().

hasAttributeBeenModified returning true may indicate that the attribute is animated or that the attribute has been interactively changed.

Note
hasAttributeBeenModified stores the top-most array/parent of the plug tree in which given attribute exists in exactly the same way MEvaluationNode::dirtyPlugExists() does.
Parameters
[in]evaluationNodeThe evaluation node of the deformer which is overridden.
[in]attributeThe attribute to test.
Returns
true if the attribute has changed since the last call to evaluate().
Examples:
basicMorphNode/basicMorphNode.cpp, and offsetNode/offsetNode.cpp.
bool hasPlugBeenModified ( const MPlug plug)
static

This method returns true if the data for plug on the overridden deformation node has been modified since the last call to MPxGPUDeformer::evaluate().

hasPlugBeenModified returning true indicates that the plug has been interactively changed.

Note
hasPlugBeenModified checks if the exact plug has been modified, as opposed to hasAttributeBeenModified which checks the top-most array/parent of the plug tree in which given attribute exists in exactly the same way MEvaluationNode::dirtyPlugExists() does. Also note that hasPlugBeenModified does not use Evaluation Manager information to determine if the plug is animated, it only checks if it was interactively modified. Therefore, when hasPlugBeenModified returns true, it's because the plug has been iteractively changed, so hasAttributeBeenModified should also return true for any attribute in the plug's attribute hierarchy.
Parameters
[in]plugThe plug to test.
Returns
true if the plug has changed since the last call to evaluate().
MUniqueString sPositionsName ( )
static

This method returns the name of position buffers on the GPU.

The name may be provided to MGPUDeformerData::getBuffer() in order to access a position buffer in that data, such as from an upstream deformer.

Returns
The name of the position buffer.
Examples:
basicMorphNode/basicMorphNode.cpp, identityNode/identityNode.cpp, and offsetNode/offsetNode.cpp.
MUniqueString sGeometryMatrixName ( )
static

This method returns the name of geometry matrix buffers on the GPU.

The name may be provided to MGPUDeformerData::getBuffer() in order to access a matrix buffer in that data, such as from an upstream deformer.

Returns
The name of the geometry matrix buffer.
MUniqueString sInverseGeometryMatrixName ( )
static

This method returns the name of inverse geometry matrix buffers on the GPU.

The name may be provided to MGPUDeformerData::getBuffer() in order to access an inverse matrix buffer in that data, such as from an upstream deformer.

Returns
The name of the inverse geometry matrix buffer.
bool getIndexMapper ( MIndexMapper imap)

This method will update the IndexMapper information if required.

To be used when implementing GPU deformation. It uses information on the deformer and computes the indices that are meant to be deformed. This is meant to be called every evaluation and refresh the GPU buffers only if it returns true.

Parameters
[in]imapThe IndexMapper to be filled only if out of date
Returns
true if imap has been updated, otherwise false if imap already is up to date.
MObject getFixedSetupData ( const MString name)

This method can retrieve data from the related node that is implemented for the CPU.

A name identifier is given to specify what information is requested. It is up to the specific CPU and GPU implementations to agree upon the name and the type of data.

Parameters
[in]nameThe string identifying what information is requested
Returns
The object containg the data, or the Null object if it could not found.
bool isFalloffWeightsDirty ( ) const

This method tells the GPU deformer if it need to ask weight from the deformer again the plug weightFunction might not be dirty (function is constant) but the falloff function might depends on the current geometry.

if it depends on current geometry than we need to evaluate the weight everytime.

Returns
whether falloff weight should be refreshed
bool isFalloffWeightsDependantOnCurrentGeometry ( const MEvaluationNode evaluationNode,
unsigned int  multiIndex 
)
static

This method tells the GPU deformer if the weight function requires the current geometry to compute.

Returns
whether falloff function requires current geometry

The documentation for this class was generated from the following files: