博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

为毛GPU Cache不能移动顶点?

Posted on 2018-01-15 13:48  SolHe  阅读(1389)  评论(0编辑  收藏  举报

      这篇文章属于典型的剥洋葱文,由表及里,逐步引入新的知识点,挖掘最本质的原因。这篇文的逻辑是先假设再证明,按照这个思路去阅读会比较轻松。

      Maya里的GPU Cache导入的几何体为什么不能编辑顶点?这可以算是一个高频问题了,这个问题可以转换为:GPU Cache导入的几何体为什么不能编辑Mesh(不仅不能编辑顶点,为它添加bevel一类的节点也不可以)?实际上这个问题在Maya的帮助文档中是有明确答案的。

      我们来看看官方是如何解释的:

GPU caches are Alembic-based files that are optimized for fast playback performance in Maya. These performance gains come from the way GPU cache files are evaluated. The GPU cache node routes cached data directly to the system graphics card for processing, bypassing Maya dependency graph evaluation. 

GPU cache是由Alembic文件派生出来的一种文件格式,为获取Maya中快速播放的性能专门做了优化。这些性能的提升来自于GPU cache文件求值的方式。GPU cache节点会避开Maya的dependency graph求值机制,把缓存数据直接发送到系统的图形卡接口进行处理。

 

      现今的图形卡都有着比cpu夸张很多的线程数量,在并行计算的应用上有着极大优势,而图形处理的计算几乎都是线性计算,与图形卡的多线程简直是天作之合,GPU cache利用了图形卡的优势,把GPU cache文件中的缓存数据直接发送给图形卡进行计算,效率自然与基于传统计算框架cpu<-->memory的DG(dependency graph)机制不可同日而语。

      但仔细一想,这段话有一个漏洞:虽然GPU cache导入的geometry无法编辑顶点,但整体移动还是可以的。这就与官方的解释相悖了,说好了会避开DG求值机制呢?

 


为了在这个问题上做更深层次的分析,我们插播一个DAG的小广告,快速的了解一下DAG的概念:

In Maya, a directed acyclic graph (DAG), defines elements such as the position, orientation, and scale of geometry. The DAG is composed of two types of DAG nodes, transforms and shapes. Maya's Directed Acyclic Graph (DAG) scene architecture, organized as a tree of transform nodes and shape nodes.

在Maya中,有向非循环图(Directed Acyclic Graph,DAG),定义了诸如几何体的位置、方向和大小等元素。有向非循环图由两种DAG节点构成,分别是Transform和Shape。Maya的有向非循环图(Directed Acyclic Graph,DAG)场景结构就是由transform和shape节点组成的一颗层级树。

A DAG node is simply an entity in the DAG. It may have and know about parents, siblings, and children, but it does not necessarily know about transformations or geometry. Transforms and Shapes are two types of nodes derived from a DAG node. A transform node is a type of DAG node which handles transformations (translate, rotate, and scale), while a shape node is a type of DAG node which handles geometry. A shape node does not maintain transformation information, and geometry cannot be hung below a transform node. This means that any piece of geometry requires two DAG nodes above it, a shape node immediately above it, and a transform node above the shape node.

DAG节点就是有向非循环图中的实体,该节点有且知道有哪些父节点、邻节点和子节点,但它并不必需要知道相关的transform信息和几何体信息。Transform和Shape是两种派生自DAG节点的节点,Transform节点是一种能处理transformation(译作变换,属于线性计算,包含translate、rotate、scale信息)信息的DAG节点,Shape节点是一种能处理geometry(几何体)信息的DAG节点。Shape节点不会维护transformation信息,geometry也不能放在Tranform节点下面。这意味着任意一片geometry都需要有两个DAG节点放在它的层级之上,Shape节点放置在geometry上面,transform放置在Shape节点上面,通过这种方式构造出一个层级关系树,供DAG使用。

Geometry层级示例:

Transform Node --> 处理transformation信息

        |

Shape Node       --> 处理下方Geometry信息

        |

Geometry Data   --> 可能存放在内存中,也可能存放在GPU显存中

 

      根据DAG的概念,我们可以推论:对于GPU cache导入进来的geometry来说,必然会为它创建两个DAG节点,一个是Transform Node,一个是Shape Node。其中Tramsform节点中的tramsformation信息是可以编辑的,可以接受外部的位移操作,而Shape节点持有的geometry信息是不可编辑的。

 

      但现在这个推论还是不够深入,为什么Alembic Cache既能编辑transformation又能编辑geometry信息,唯独GPU cache只能编辑transformation?

 


下面我们再引入DG(dependency graph)的概念,更进一步的,去Maya的底层找答案。

The dependency graph lies at the heart of Maya. It maintains the construction history of the scene: a record of what operations you have applied in what order. The graph is made up of a series of interconnected nodes, each of which encapsulates a single operation or a single set of calculations. Each node accepts a limited set of well-defined input data, performs its calculations based on that data, and produces one or more output values.

Dependency graph(依赖图)位于Maya核心,它维护着场景的构造历史,在这里,构造历史指的是你以某种顺序应用的操作记录。该图由一系列相互连接的节点组成,每一个节点封装一个操作或一组计算。每个节点接受有限的一组定义好的输入数据,再根据该数据执行计算,并生成一个或多个输出值。

Most objects in Maya are dependency graph nodes, or networks of nodes (several nodes connected together). For example, DAG nodes are dependency graph nodes, and shaders are networks of nodes.

Maya中的大多数对象都是依赖图节点或是由多个节点连接到一起构成的子网络。例如,DAG节点是依赖图节点,着色器是节点网络。

Certain events cause the dependency graph to re-evaluate itself, examples being screen refresh, and animation playback. During a refresh for example, the system will walk down the DAG and for each DAG node check to see whether it needs to be re-evaluated.

某些事件会触发依赖图重新对自己求值,例如屏幕刷新和动画回放。在屏幕刷新期间,Maya底层将遍历DAG层级结构,并检查每一个经过的DAG节点是否需要重新求值。

 

      由上可知,Transform和Shape节点实际上就是DG节点的一种,我们可以猜测,“The GPU cache node routes cached data directly to the system graphics card for processing, bypassing Maya dependency graph evaluation. 这句话指的是,在viewport显示geometry的时候,GPU cache构造的Shape node所持有的geometry数据会直接发送到系统的图形卡接口进行直接处理,避开DG的求值;而GPU cache构造的Transform node则依然会进入DG的求值机制,用户可以像使用Polygon一样,在Transform node上添加历史节点,比如Time node来key关键帧。

      实际上,在GPU cache的开发文档中还提到了这样一句:

The gpuCache node is a Shape node that holds baked animated geometry in memory.The node can stream the animated geometry to the viewport without triggering any DG evaluation of geometry attributes (mesh, nurbs or subd surface data).

gpuCache 节点是一个在内存中持有几何体数据的Shape节点,当然这个几何体数据是烘焙后的、有动画的几何体数据。gpuCahe节点可以把几何体数据传递给viewport(就是hardware renderer或者viewport2.0),而不会触发任何几何体属性的DG求值行为。

       

      这条说明就充分佐证了我们的猜测,这个解释无疑比文章最开始的答案要更深入。但我们依然还有一个疑惑,这种特性是如何实现的呢?为什么Alembic Cache导入abc文件可以编辑顶点,GPU Cache就不行?下面我们打破砂锅问到底,去GPU Cache的源码里寻找答案。

 


      GPU cache的源码是公开的,你可以通过下载对应Maya版本的devkit来获取,附个链接:Maya2017 devkit下载地址。现在我们阅读GPU cache的源码来验证这个猜测是否为真。

      首先,在doGpuCacheExportArgList.mel中有这样一段代码可以展示GPU cache创建DAG节点的过程:

string $xformNode = `createNode transform -name $nodeName`;
createNode gpuCache -name ($nodeName + "Shape") -parent $xformNode;
    setAttr ".cacheFileName" -type "string" $fileFullPath;

      其中第一行创建transform节点;第二行创建gpuCache节点,gpuCache节点就是shape节点,下面会讲到;第三行为gpuCache节点制定abc文件路径。这就印证了我们在DAG环节的推论,GPU cache导入abc数据时会分别创建一个transform节点,一个shape节点。而transform节点就是Maya内置的transform节点,所以当然可以提供位移、旋转、缩放等操作。

     

      我们再看下一段代码,在gpuCacheShapeNode.cpp中有如下定义:

///////////////////////////////////////////////////////////////////////////////
//
// ShapeNode
//
// Keeps track of animated shapes held in a memory cache.
//
////////////////////////////////////////////////////////////////////////////////
class ShapeNode : public MPxSurfaceShape
const char* ShapeNode::nodeTypeName = "gpuCache";

      这段代码这足以证明gpuCache是一个标准的Shape node。倒数第一行意为ShapeNode的类型名是“gpuCache”,倒数第二行意为ShapeNode继承自MPxSurfaceShape。这这里简单介绍一下MPxSurfaceShape,该类主要用于实现新的shape,并且有可选、可操作的组件,还有与Maya中默认shape相似的行为。也就是说,ShapeNode是可以提供与默认Shape相似功能的,比如,在viewport2.0中能显示并编辑点线面,能添加bevel一类的历史节点。

 

      但显然gpuCache并没有显示顶点,在被选中时,显示的边缘也是dashed line,不是正常的实线。在这里我们应该去查看MPxSurfaceShapeUI类,MPxSurfaceShape和MPxSurfaceShapeUI是需要一并实现的,MPxSurfaceShape主要用于操作几何体数据,MPxSurfaceShapeUI则用于显示几何体。在gpuCache中这两个类分别由ShapeNode、ShapeUI实现。

      在ShapeNode中没有实现setInternalValue()方法,而MPxSurfaceShape类中对顶点的编辑都是通过setInternalValue()来处理的,显然ShapeNode不具备编辑顶点的接口,自然gpuCache也就无法编辑顶点了

      在ShapeUI只有drawBoundingBox()、drawWireframe()、drawShaded()三个绘制函数,分别绘制boundingbox、模型线框及表面。在这个类里并没有找到绘制顶点的函数,这就是你在Viewport 2.0中无法看到gpuCache顶点的原因,无法看到顶点,自然也无法操作顶点。另外drawWireframe()函数中也指定了线框的绘制方式:kLineStippleShortDashed,强制Maya以虚线的方式绘制线框。

 

      可以断定,gpuCache的源码中一定重写相关方法。继续阅读源码能够看到在gpuCacheSubSceneOverrride.h文件中有如下声明:

class SubSceneOverride : public MHWRender::MPxSubSceneOverride

      MPxSubSceneOverride是Viewport 2.0的绘图基类,该类允许用户完整的定义渲染单元(MRenderItem),通过update()方法将计算后的geometry数据添加到MRenderItem,再通过绘图函数进行绘制。该类主要用于在使用Viewport 2.0时绘制sub-scene类型的DAG节点。SubSceneOverride子类在gpuCachePluginMain.cpp中通过MHWRender::MDrawRegistry::registerSubSceneOverrideCreator()方法注册到了Maya中,并与ShapeNode建立关系。这就意味着一旦创建了gpuCache的DAG Shape Node实例,就会有SubSceneOverride负责它在Viewport 2.0中的绘制工作。当然具体的点线面绘制工作由Viewport 2.0来执行,SubSceneOverride主要负责获取renderitem,并将处理过的renderitem推送至Viewport 2.0。

      而几何体数据就是在MPxSubSceneOverride中发送到GPU memory中的。这个过程会涉及到更多的开发细节,以后有时间再做分享吧。

      另外,为什么不能在gpuCache上添加诸如bevel之类的历史节点呢?答案很简单,因为gpuCache节点没有outmesh属性,bevel这类节点是无法连上去的。

     

以上,就是gpuCache不能编辑顶点的真相。