前言
昨天去某公司面试,被面试官抓住不放,问了我一系列关于DC的问题。可惜我这两年一直在做3d小游戏。对于2d方面的的DC的关注点几乎没有。只能说是回答的勉勉强强。所以今天开始狠狠的补习了一下cocos的源码,并且萌生了绕开节点树的渲染提交顺序,通过自己指定每个节点的提交顺序来最大化合并DC的效果。经过一下午的尝试,终于有所收获。
引擎版本
基于 3.3.1
代码质量
瞎写的。
DC合并的原理
- 我们先来观察一下,上边的这张图片。这是一个游戏中常用的列表的。
- conten是列表的父亲节点
- green0,green1,green2是三个列表的单元格。
- 每个单元格由 green(绿色图片),yellow(黄色图片), label0(一个bmf文本构成)
- 绿色图片和黄色图片已经被打入了同一个贴图集内。
- 那么根据cocos传统的节点数的深度访问遍历来提交渲染批次。那么提交渲染的顺序就会是
Canvas->Camera->content->green0->yellow0->label0->green1->yellow1->label1->green2->yellow2->label2。
再根据DC和批的原理。如果渲染提交的时候,多个连续提交的节点使用了相同的图集,那么这多个节点的DC会合并一下。所以如下图,会有6个DC。本来green0,yellow0可以合并,然后label0打断了这个合并,接着green1,yellow1可以合并,label1又打断了这个合并。也就是说每个单元格会占据2个DC。如果有 单元格有N个的话。那么这个列表就会有2N个DC。
思路上的改进
- 此时,已经有聪明的小伙伴发现了。如果我提交渲染的顺序是
green0->green1->green2->yellow0->yellow1->yellow2(到这个时候用了一次DC)
->label0->label1->label2(此时使用了2个DC)
那岂不是美滋滋。升职加薪走上人生巅峰也不是梦想。 - 那我们需要一个功能,可以打破渲染提交的顺序。同时,如果我不用这个功能,就是按照传统的节点数去提交。最好的情况是,场景里的节点很多很杂。但是大部分都是按照原先的方式去提交批次。而我可以指定某一个节点,这个节点和它的子子节点按照我指定的顺序来渲染。
如上图的一个节点树。其中ABCDEFG都是节点团。在渲染的时候,走的顺序还是 A-B->D->C->E->F->G。但是D,G这2个节点团渲染的时候,是走自己指定的顺序渲染。这样子,渲染逻辑上可D和G之间是相互独立的。不会发生错乱。但是G节点团的渲染永远是后与D节点团的。
透明度的叠加
- 经过评论区的一位同学的提醒,这里还需要考虑透明度的叠加问题。
- 还是以上图为例子。传统的节点树上
D看起来的透明度 = A透明度 x B透明度 x D透明度
但是在打乱提交顺序之后,透明度如果还按照上述来计算的话。代码消耗会增大很多,所以我修改成了
任意子节点的透明度 = A节点透明度 x 任意子节点自身透明度
用法上的说明
- 我先选中content节点。
2.为了满足功能,我修改了UITransform.ts的源代码,为它添加了2个属性。
- SpanEffected 被勾选上之后表示这个节点和它的所有子节点的已经成为了一个节点团。在这个节点团的内部,所有的渲染提交顺序由用户来自己指定了。
- RenderIndex 则是渲染的优先级。在content这个节点团下,渲染优先级越小,则越先提交渲染。
- 接着我们将content节点团下的渲染提交优先级设置成如下图所示
此时我们提交的渲染顺序变成了content->green0->green1->green2->yellow0->yellow1->yellow2->label0->label1->label2
那么理论上就只有2个DC了。
接着我们用spectorJs来验证一下。
可以看到,确实如此,只有2个DC。到此,大功告成
代码修改
UITansform.ts文件修改如下
batcher-2d.ts代码修改行数在
610行-658行
在这里直接把修改后的源码打包了,开发者可以直接替换(源码里已经重新加入了计算透明度部分的代码)
修改后的源码.zip (14.7 KB)
后记
在这里笔者只是提供了一个改变渲染顺序的思路。代码写的并不规范。但是我认为这个思路是不错的。希望大家甚至引擎组都可以借鉴。直接把这个功能完美的集成到引擎中去。
测试
这里没有做大规模的测试。无法预估这个修改会不会引发未知的错误。如果要是用到实际中的项目,一定要多测试哦。项目崩了,老板跑路,工作丢了,笔者一概不负责。