MPxGPUDeformer Class Reference

#include <MPxGPUDeformer.h>

Class 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 ctor must be fast. Do not do heavy work in the ctor because the deformer evaluator may allocate GPU deformers 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. 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 then geometry connections upstream until a source plug, that meets any one of the following criteria, is reached:

  • On an unsupported node.
  • 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 the 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

Example 1:

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

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.

Example 3:

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

In this scenario, the deformer evaluator does 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.

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.

+ Examples:

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 &)=0
 This method 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...
 

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

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 
)
pure virtual

This method is called each time the deformation chain is evaluated.

This method is given an input position buffer, an input event, an output position buffer, and an output event. This method should enqueue kernels which:

  • wait for the input event.
  • read the input position information from the position buffer.
  • write the deformed position information info the output buffer.
  • save an event into the output event, which indicates it is safe to read the positions from the output buffer when 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 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.

inputPositionBuffer is a cl_mem object that stores the input mesh positions as float3 elements. inputPositionBuffer is readable but may or may not be writable. The vertices stored in inputPositionBuffer are stored in the same order that MItGeometry visits the vertices.

outputPositionBuffer is a cl_mem object that stores output mesh positions as float3 elements. outputPositionBuffer is writable but may or may not be readable. The vertices stored in outputPositionBuffer are stored in the same order that MItGeometry visits the vertices. outputPositionBuffer is not initialized, so you must write position data into outputPositionBuffer for each vertex.

inputPositionBuffer and outputPositionBuffer may or may not represent the same cl_mem object.

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.

If the GPU override does not need to modify the input buffer to produce the output buffer, then evaluate() returns kDeformerPassThrough. In this case, evaluate doesn't need to explicitly run a kernel to copy the values from inputPositionBuffer to outputPositionBuffer, Maya handles this automatically. Returning kDeformerPassThrough is faster than explicitly copying the data, we are often able to use the inputPositionBuffer for this GPU deformer in the next downstream GPU deformer. That way no additional heavy work is done at all.

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 copied to the outputPositionBuffer by Maya.
+ Examples:
void terminate ( )
virtual

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

Any data stored on this object should be released.

+ Examples:
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().

The documentation for this class was generated from the following files:
  • MPxGPUDeformer.h
  • MPxGPUDeformer.cpp