Starling的性能优化(译文)Part 2

今天来翻译第二部分。
针对Starling的专门建议
把状态变更缩减至最小
如你所知,Starling使用Stage3D做所有的可见对象渲染。这意味着所有的绘制都是由GPU完成的。
目前,Starling每次提交一个四边形数据,一个接一个地向GPU提交,一个接一个地绘制。实际上,这也是最初版本的Starling的工作办法,所以可以通过一次向GPU提交大批量的数据进行绘制的办法,来获得最佳性能。
这也是为什么新的Starling版本在每次向GPU提交之前尽量积存较多的四边形数据。不过,它只能批量提交具有相似特性的四边形。每当遇到一个状态发生变化的四边形时,一个“状态变更”就发生了,之前的批量数据们就被提交绘制了。

  • 注意,这里四边形数据(Quad)和图片(Image)在本文中是同义的。而在Starling类体系里,Image只是Quad的一个添加了材质(texture)的子类。
    这里有几个关键的点会触发一个状态变更:

  • 加载素材/材质时(texture)(同一舆图文件[atlas]中的不同材质还好。)

  • 显示对象的blendMode发生变化时

  • 修改图片的smoothing(平滑)属性时

  • 设置素材的repeat模式时

  • Quad的tinted属性发生变化时。
    所以,如果你有办法让你构建场景的过程尽可能少地触发状态变更,你的渲染性能就会获得非常地提升。

    Tinted Quads 有些移动设备(比如说第一代iPad)对于对素材做“着色(tinting)”处理是相当吃力的,表现在:
  • 当把它们绘制成透明状态时(alpha值不是1)

  • 当把它们绘制成其他颜色时(将image.color属性设置为非白色的其他值时)
    因此,Starling对“非着色”图像做了相应的代码优化。产生的问题是:当把图像在着色(tinted)状态和非着色状态(untinted)切换时会触发状态变更。所以当你想改变一个图片的alpha值或color值时,请三思。

    如果你正在创建的游戏所运行的硬件不care这个着色行为,这种状态变更会造成一些不必要的性能下降。
  • 有一个鸡贼的办法可以避开这种状态变更:只要将根显示对象的alpha值设成“0.999”或者一个类似的值这样这个alpha值将会在渲染时传递给它所有的子对象,Starling将会把 所有的 对象视为着色状态,这样这种状态变更就永远也不会被触发了。

    性能监控器 Starling的性能监控器可以通过设置starling.showStats来开启,它显示了每帧执行的绘制请求数量(第三行:DRW),你触发的状态变更越多,这个数就越大。你的目标使,让这个值尽可能地小。下面的文章中将会提到这些技巧。
    [![](http://tiger-a-s-s.tobybai.cn/stats_drawcalls.png)](http://tiger-a-s-s.tobybai.cn/stats_drawcalls.png)
    考虑到正在使用的性能监控器会对实际绘制数量造成影响,所以很明显地,Starling对这个值做了递减修正。 画家算法(The Painter's Algorithm) 想要达到最小的状态变更这个目标,你需要了解Starling处理你提交的对象的顺序。 与Flash相同,Starling也采用了画家算法来处理显示列表。这意味着,你就像画家画画一样显示你的对象:先绘制底层(通常是背景)然后一点点前移,每次都将新的对象绘制在之前对象的上层。
    [![](http://tiger-a-s-s.tobybai.cn/01f53c8d40d60bcc822b46eb4b09defc.media.600x152%20(1).png)](http://tiger-a-s-s.tobybai.cn/01f53c8d40d60bcc822b46eb4b09defc.media.600x152%20(1).png)
    如果你想在Starling里创建如上图的画面,你需要三个Sprite:一个包含远景的山,一个包含草地,最后一个包含植被。包含山的层次应该在底层(下标为0),植被在顶层(下标为2),每个Sprite都包含对应的若干图片,如下图描述的。
    [![](http://tiger-a-s-s.tobybai.cn/landscape.png)](http://tiger-a-s-s.tobybai.cn/landscape.png)
    实际渲染时,Starling会从左边的Mountain1开始,依次向右进行,直到抵达Tree2.现在,如果这些图片们都处于不同的状态,这个过程将会被拆分成6次绘制请求。如果你每次都从不同的位图数据中获取不同对象的数据的话,这种情况就一定会发生。
  • 这里要介绍另外一个有用的办法,就是DisplayObjectContainer::sortChildren(),这个函数可以用于排序子对象的层次。以Sprite为例,排序的标准可以时x,y,alpha这种属性,这个函数还支持自定义的比较函数,这意味着你可以按照你自定义的方案对这些层次进行排序了。

    Texture Atlas(素材舆图) 以上说到的问题,正是凸显Texture Atlas重要性的部分。如果你从同一个atlas里加载所有这些资源,Starling就能将所有的资源一次性地绘制出来!(如果它的上层显示列表木有发生状态变更的话。)
    [![](http://tiger-a-s-s.tobybai.cn/landscape-2.png)](http://tiger-a-s-s.tobybai.cn/landscape-2.png)
    在这里,每一个图片使用相同的atlas(所有的节点都以相同的颜色描绘),看起来,你的项目只需要一个atlas就ok了。 可是实际上,不是你所有的素材都能被刚好放在同一个atlas里面。atlas的尺寸不能大于2048*2048像素(在某些移动设备上这是被允许的最大素材尺寸),所以你迟早会发现装不下了的。但如果你聪明伶俐地安排好你的资源们的话,这都不叫事儿~
    [![](http://tiger-a-s-s.tobybai.cn/landscape-3.png)](http://tiger-a-s-s.tobybai.cn/landscape-3.png)
    上图显示的两种情况都使用了两个atlas中的资源(每种颜色表示一个atlas资源),但是左边的显示列表将会在每次添加的时候都触发一次状态变更,而右边的将会在两次内完成所有图形的绘制。 扩展资源: [画家算法](http://baike.baidu.com/link?url=LJnP4MSZFlcSJ9SftCQOQ0VbplaDAnThbrNeSJTZZKEKyCSxPZnRelBI9RmIsSNMRJnJx1pXAPxxHsYnYIKZIa) [Texture Atlas](http://en.wikipedia.org/wiki/Texture_atlas)