Cocos Creator ScrollView 性能优化

本文基于 Cocos Creator v2.1.2

《Cocos Creator ScrollView 性能优化》讲师:Cocos 技术顾问组分享原因:

Cocos Creator 的 ScrollView 组件是游戏开发中常用的组件,在一些商城界面、排行榜界面、任务列表、背包系统等模块中经常会使用到它,但同时它的开销也非常大。当我们需要显示的条目比较多时,单纯简单地去使用的话,往往性能不大理想。

Cocos Creator 只实现了最基本的 ScrollView,相应的优化还需要我们根据项目的情况来进行针对性的优化。

常见问题:

当数据量比较大时,我们很容易碰到两个问题:

  • DrawCall 的数量比较高,渲染性能比较低

  • 整个 Scrollview 的节点数太多,导致隐藏或显示界面时的 onEnable 和 Disable 开销比较大

比如下面这个界面:

Demo 中的 ScrollView 1 场景

ScrollView 当中有 20 个 Cell,总共的 DrawCall 达到了 790,单个 Cell 大概有 50 个节点,总共就有差不多 1000 个节点,这时我们的开销会变得非常的大

常规优化方式推荐:

1、合并渲染批次,降低 DrawCall,提升渲染性能

有两个方法可以使用:

(1)使用自动图集或使用 TexturePacker 对碎图进行打包处理:

这样操作的话,可以让多个 Sprite 渲染的纹理都是同一张图集图片,合并这些 sprite 的渲染批次,就可以减少 DrawCall 以及 CPU 的运算开销。

这里我使用了 AutoAtlas 来实现,关于 AutoAtlas 的使用可以[参考文档],让我们来看下效果:

同样是 20 个 Cell,DrawCall 降低到了 556,相比较之前有了比较明显的降低。

优化到这个程度还不够,如果你是在 Google 的 Chrome 浏览器上进行调试的话,推荐你使用 spector.js 这个插件,通过这个插件你可以看到每个 DrawCall 对于纹理的处理情况。对于原生开发,则可以使用 XCode 中的 GPU analysis功能。

(2)开启 dynamicAtlas 功能

开启这个功能,在 main.js 中的 window.boot() 方法内加入下面的两行代码即可:

cc.macro.CLEANUP_IMAGE_CACHE = false;

开启之后,我们可以看到** DrawCall 大幅下降,部分 DrawCall 已经合并。**

2、对于 Label 的处理。

Label 的处理跟前面的优化方案选择有关系:

(1)如果使用了自动图集或 TexturePacker 对碎图进行合并的话,可以选择 Label 使用 bmfont 字体,而不是使用系统字体,同时将 bmfont 使用到的纹理资也一起合并到图集资源中。

可以看到,DrawCall 又进一步地降低了,目前运行的效果是 330 个 DrawCall。

(2)如果前面使用的是 dynamicAtlas 的功能,那么可以选择 Label 使用系统字体,同时将 Label 的 CacheMode 属性更改为 BITMAP。

这个模式下会将 Label 的纹理当作一个 Sprite 纹理,并且参与到 dynamicAtlas 中去,这样就可以跟 Sprite 的纹理进行合批处理。

从这两个方案可以看出来,优化策略都是想办法对 DrawCall 进行合并批处理,只是使用 dynamicAtlas 更加地智能,同时也可以更好地适应复杂的节点结构。

但需要注意的是,dynamicAtlas 会有额外的 CPU 计算以及动态纹理的绘制开销,因此需要根据项目的情况去选择使用。

3、通过 Cell 的位置进行计算,让显示范围外节点的 opacity 为0,即不显示,减少 DrawCall 。

在可视范围外的节点,本身我们就不可见,所以就不需要它再进行绘制,平白增加 Drawcall。我们可以将这些可视范围外节点的 opacity 属性设置为 0,从而避免绘制,可以有效的降低 DrawCall。

update (dt) {

这里我将判断逻辑放在了 Update 函数中,你也可以将这段方法放到 ScrollView 的滑动回调中去,这样的话不用每帧都计算,只在需要的时候才会去进行计算,节约一些 CPU 的开销。

最终效果可以看到,在启用 dynamicAtlas 的方案上,DrawCall 可以降低到 68,相比较最原始的 790 个 DrawCall,效果显著。

4、资源处理,减少 Mask 组件数量

通过对资源的处理,减少 Cell 中使用 Mask 组件的数量,尽量不使用 Mask 组件。

由于 Mask 组件需要在 stencil 和 content 前后都添加修改 gl 状态的 render command,因此使用 Mask 会打断我们的 DrawCall 批处理。

对于一些特殊的显示,例如圆角的 icon 等,如果条件允许,尽量不要使用 Mask 组件来进行处理,而是通过对资源进行处理达到同样的效果。

目前 Mask 组件、Spine 组件、DragonBone 组件都会打断批处理,在节点结构上我们要避免被打断的情况发生。

在这个 demo 当中,每一个 icon 都有一个使用了 Mask 组件的子节点,我们去除它,效果如下:

最终就只有 18 个 DrawCall,基本上是极限地 DrawCall 优化了。毕竟 ScrollView 本身就有一个 Mask 组件,这个组件我们无法避免,必须要使用。

5、复用 Cell 节点,减少节点数

对 Cell节点进行复用,减少节点数,这一块改动比较大。前面都是对 DrawCall 的优化,但实际的节点数量还是很多。当显示或隐藏这个界面时,大量的节点会带来大量的 enable 和 disable 的开销。

因此我们通过复用节点,根据滑动情况实时更新 Cell 位置以及显示内容的方式,减少节点的数量。具体操作可以参考[Cocos Creator 官方示例]中的 ListView 示例。

原理如下:(见F盘/资源/ScrollView 性能优化)

具体代码可以参考 demo 工程中的 ScrollView 3 场景的实现,最终效果如下:

20 个 Cell 的 ScrollView,实际上只是 7 个 Cell 节点在不停的复用显示,从而表现出来的。与之前的效果以及 DrawCall 相同,但实际使用的节点数大大降低,大约有 300 个节点,相比较之前的 1000 个节点大幅降低。

6、分帧加载节点与节点快照

由于背包模块的需求,往往首次需要同步加载并显示多个 Cell 节点,所以往往会造成加载卡顿、加载崩溃等问题,针对这个问题,我们提出了分帧加载节点与 renderTexture 节点快照组合而成的优化方案。

分帧加载方案,主要利用的是 ES6 特性中的异步加载方案:Generator(协程)。下面是节选自本次分享会 PPT 中的对协程执行流程的讲解:

协程能够有效地暂停当前语句并将转移它的执行权,等待执行权返回。这个过程可以一直重复,直到语句执行完毕。分帧加载方案就是利用协程的这一点特性,在每次语句循环执行时,将加载代码的执行权转移给下一帧去执行,从而达到分帧加载的效果。

节点快照方案的原理是:首先放置一个 Cell 参考节点,放置在可渲染但是不可见的区域(可通过多摄像机实现)。之后每当需要新增一个 Cell 节点,都让 Cell 参考节点去做相应变换,变换结束后利用 Camera 和 RenderTexture 将渲染结果[截图]下来,再交由 Content 中新增的 简易 Cell 节点渲染出来即可。

未优化前,500 个 Cell 节点 DrawCall 达到 2514 个,总节点数达到节点总数 3048 个。

优化后的效果如下:

优化完之后,加载完成时 DrawCall 稳定为 18 个,节点总数 548 个,效果显著。

以上就是一些常用的 ScrollView 性能优化手段,欢迎大家在 Cocos 论坛上一起讨论更有价值的游戏优化方案。

参考文档

查看截图方案

https://docs.cocos.com/creator/manual/zh/render/camera.html#%E6%88%AA%E5%9B%BE

Auto-atlas Asset:

https://docs.cocos2d-x.org/creator/2.1/manual/en/asset-workflow/auto-atlas.html

UI 渲染批次合并指南:

https://docs.cocos.com/creator/manual/zh/advanced-topics/ui-auto-batch.html

背包模块优化方案地址:

https://github.com/Jno1995/ScrollViewTest

你可能感兴趣的:(Cocos Creator ScrollView 性能优化)