UI渲染合批实践-根据社区文章-上篇(下篇会支持原生)

UI渲染合批实践-根据社区文章-上篇(下篇会支持原生)

前言

  • 社区对UI渲染合批已经有一些方案,笔者最近参考某些方案进行了实践。
  • 本实践的优点是无需定制引擎,支持嵌套mask,并且同时支持H5和原生。

方案一

1. 原理

  • 渲染开始前,按层次重新组织子节点。渲染完成后,还原子节点的结构。如图,该列表包含两个item,渲染开始前,临时将列表节点构造成右边临时结构,渲染完成后,再将列表节点还原成左边的原始结构。

图1.1 列表节点结构对比

  • 渲染遍历的函数在3.8上面是Batch2d的walk函数,节点的遍历是按照深度优先的规则进行的。渲染遍历的时候,通过将children重新组织成新的结构,可以优化渲染顺序。如图,在渲染的时候,如能通过重新组织children节点结构,使得左边的渲染顺序。变成右边的渲染顺序,那drawcall将会大大减少。

图1.2 item-渲染顺序对比

  • 由上图可以看出,渲染顺序并非只是简单的从深度遍历改为广度遍历。但如果item每一层的渲染组件都来自同一个图集,那确实可以简单的改为广度遍历。或者人为增加一些空节点,按是否同一图集来分层组织,这样也可以使得广度遍历可行。

2. 参考方案

  • 2.1 《性能优化1-列表渲染优化|社区征文》,作者Bool Chen,使用版本是2.x。这个方案需要增加一个新的组件,缓存新的子节点结构,另外还需要修改引擎的源码。

  • 2.2 如何一行代码减少合批,作者小不溜,使用版本是3.x。同样需要修改引擎源码或对引擎函数进行覆盖重写。

  • 2.3 之前似乎看到过98k写过一篇文章,分享过原理,但现在好像找不到那篇文章了。记得原理是,分别捕捉渲染开始和渲染结束事件,渲染开始前,将节点children的结构重排,渲染完成后,再将children的结构还原。这种捕捉渲染事件的方式,使得这个方法不需要修改引擎源码,这是这个方法的优点。

3. 问题与讨论

  • 上述的2.3文章暂时找不到,暂讨不论。

  • 上述2.1和2.2不知道是否支持内嵌mask,或内嵌scrollview,笔者没有验证,只是看了一下思路。

  • 根据笔者的实践,上述2.1和2.2,应该是不支持原生Android和Ios。根据商店售卖的商品,2.3应该是支持原生的。

4. 实践

  • 4.1 每个item添加一个组件MergeItem,在onEnable的时候,缓存一份分层的节点数组。如图:

图2 MergeItem分层节点数组

  • 4.2 列表父节点添加一个组件MergeRoot,捕捉渲染开始和结束事件。

    
    director.on(DirectorEvent.BEFORE_DRAW, this.onBeforeDraw, this);
    
    director.on(DirectorEvent.AFTER_DRAW, this.onAfterDraw, this);
    
    
  • 4.3 渲染开始的时候,重新组织children的结构,渲染结束之后,再还原children的结构。这样做的好处是无需修改引擎代码。

  • 4.4 根据笔者方案,重新组织children结构的时候,如果没有把node的children设置为空数组,会导致有些控件会重复绘制。原因是并未修改任何引擎源码,Batch2d的walk函数仍按深度遍历进行。如图,Button下的Label会绘制两次。

图3 需要把node的children先重置为空数组

  • 4.5 笔者的实践支持item内嵌scrollview或mask。

  • 4.6 创建1000个item,发现Batch2d的walk函数,花费的时间还不少,有5-6ms。如果在渲染之前,也先剔除一些列表外的item,是不是能减少walk函数的运行时间?实践证明,确实可以。在没剔除之前,滚动的过程中,卡顿明显,而在做了剔除之后,滚动依然可以满帧运行。

5. todo

  • 不支持原生Android或Ios,下篇文章已经支持了

6. 商城链接

其他方案

  • 有待实践。

下篇地址

ps,笔者正在寻找base广州的机会,如果合适的话,可以私信我,谢谢。

3赞

手动点个赞

每帧都这么做不卡吗,我试了下243个节点展开之后消耗0.14秒,10帧左右的时间才能完全展开

不太清楚你怎么写的,但我写的这个,确实没有你说的这种情况。

:fearful:又一个轮子

我的节点层级比较深来着

啥意义都没有

这么改,通过父节点显示隐藏和透明控制所有子孙节点的逻辑,不是全出问题了

这种改变渲染层级达到合批条件, 弊大于利 而且如果为了满足合批有其他更合适更优的方案代替。

我这个方法还是有优点的,就是不用定制引擎,而且同时支持原生。
我自己的测试,也证明是有明显的性能提升的。
你说的方案,有链接可以参考一下吗?

我实话说,楼主想要靠这篇文章找到高薪的工作,有点难度

那能找到工作吗?我工作都没有,已经失业好久了 :rofl:

本来就不高薪 :sweat_smile:

此插件方案从目前针对方向来看, 只是为了针对UI场景下的合批(label和sprite), 而又是重新排列渲染顺序的方案, 这个方案会有几个致命的缺陷 1.会打乱重叠UI的层级显示, 2.影响脚本控制节点的业务逻辑, 如果用此方案 在开发中所要解决问题的时间和精力 远远超过整理合批图集方案的时间和精力, 而是否支持原生这个就关系不大

第1点确实存在,第2点不存在,因为渲染完成之后,会还原节点的结构。这个方案是每次渲染,都对列表进行这样的处理的。
整个方案只在提交渲染数据和渲染的阶段起作用。

半透明叠加会有问题

免费开源了,希望轻喷。
UI渲染合批实践-根据社区文章-终篇(免费开源)