目录
前言:
本人接触性能优化这一块的引子就是Mask,在之前的学习中了解到Mask会产生两个DrawCall??当时我也是第一次听到DrawCall这个词,至于什么是DrawCall,大家可以参阅下面这篇文章:DrawCall,Batches,SetPassCall_林万厦的博客-CSDN博客
本篇文章的内容是经过自己反复阅读源码和测试推敲出来的,如有解释不当的地方,望不吝赐教!
正文:
在过往的交流中身边总有人认为是Mask会产生两个DrawCall,Rect Mask 2D只占用一个,觉得用Rect Mask 2D一定能起到优化的作用,于是我看了源码,并亲自测试了一下。
下面是Mask为什么产生DrawCall的源码部分:
可以看到在Mask实现最初添加了一个特殊材质(Material)来作为模板材质(StencilMaterial)而这就不符合我们的合批规则,Mask第一个DrawCall设置模板缓存的时候产生的,第二个是所有Mask下的物体逻辑执行完之后要还原模板缓存。(这里说的两个包括它所依附的Image对象的DrawCall)实际上Mask本身只会产生一次DrawCall,而Rect Mask 2D本身并不产生DrawCall。
下图是一个Image添加了一个Mask组件,一共有3个Batch。相机,Image,Mask各占一个。
把Mask替换成Mask 2D后可以看到,减少了一个Batch
深度剖析:
接下来就是重头戏了,我们先来剖析一下Rect Mask2D的源码:
注意我加了注释的地方,在Mask2D组件获取到要裁剪的区域之后,遍历所要裁剪的子对象,调用了底层C++的SetClipRect方法,这里我猜测这个方法的作用是 告诉子对象哪些顶点不需要参与绘制,最终达到被裁减掉的部分像素和顶点不参与渲染的效果。我们可以根据猜想去测试一下:
首先创建一个空物体挂上Mask2D组件,在它的子节点下创建一个Image,注意顶点数(Verts)和面数(Tris)
然后我们把Image移除Mask2D的范围,让它整个被裁剪掉:
可以看到这个时候顶点数和面数的变化,也就证明了我们的猜想是正确的!
也就是说Mask2D与Mask最大的不同点就在于,Mask2D被裁剪掉的部分不会参与绘制,而使用Mask被裁剪掉的部分仍然参与绘制!这也就是为什么Mask会打断合批的原因。
Mask就一定比Rect Mask 2D性能要好吗?
首先,多个Mask之间是可以进行合批的,而Mask 2D就不行,这是因为他们的实现原理不同,上文中有讲到Mask实现最初添加了一个特殊材质(baseMaterial)来作为模板材质,所以多个Mask用的也都是这个相同的材质,可以达到合批规则。单是这一点,我们在开发过程当中一个界面需要多个遮罩时,Mask的性能就要高于Mask 2D,再然后,不同Mask的子对象之间也可以进行合批的,而Mask2D就不行。上述是我用FrameDebug测试得出的结果,我这里不会制作gif,就不插图了,大家可以自行测试一下哈。
总结:
使用Mask或是Mask2D要区分应用场景,首先Mask2D只能裁剪矩形,但是却能在单个界面只需要一个遮罩的时候起到优化作用,当单个界面需要多个遮罩时使用Mask更合理。