最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • Cocos 小白的性能优化探索

    正文概述 掘金(腾讯IMWeb团队)   2021-06-18   609

    虽然 Cocos 属于游戏开发范畴,但与前端开发中遇到的性能问题还是有很多共通之处,无非是加载速度、CPU、内存这三个指标。接下来分别从这三个指标来阐述一些优化手段。

    1. 加载速度优化

    Cocos 的启动大致可以分为5个阶段:

    Cocos 小白的性能优化探索

    其中 Cocos 引擎加载和运行的耗时,业务侧是无法改动的,这部分黑屏时间无法优化。那么黑屏时间优化就只剩 Cocos 静态资源加载了。

    静态资源加载的手段有两个:

    Cocos 小白的性能优化探索

    资源压缩主要是针对图片资源的压缩,tinify支持 png 和 jpg 格式图片的在线压缩,一般可以压缩掉 75% 的大小,并且在视觉上不会有明显的差异,十分推荐。

    如果接受一定程度的失真,在 cocos creator 编辑器中也能够对 png 和 jpg 图片进行压缩。 Cocos 小白的性能优化探索

    如果是 png 格式图片就 png,jpg 格式则选 jpg,选择后可以调整图片质量,图片质量越低,大小越小,失真也会越多。 Cocos 小白的性能优化探索

    资源缓存分为硬盘缓存和内存缓存。

    对于原生端,资源本身是存在本地的。对于 Web 端,可以通过 http 的缓存,或者 PWA 来实现资源在硬盘的缓存。

    资源还可以缓存在内存中,一般来说,游戏中会有多个场景,例如游戏中会有很多关卡,每个关卡一个场景。如果一个场景不会重复进入,那么场景资源可以不用缓存。如果场景需要重复进入,那么缓存一下,可以加速第二次打开的速度。

    一般来说,硬盘的存储空间比较大,多做硬盘的存储问题不大。但是内存一般空间比较宝贵,不能啥资源都一股脑往里塞,容易造成内存占用率高,并且可能存在内存泄漏的风险,所以一般来说只缓存一些常驻的资源。

    2. CPU 优化

    由于游戏中需要大量的计算与绘制,本身是比较吃cpu的。所以在游戏过程中, CPU 的优化是非常重要的。如果 CPU 负载过高,会造成设备发热严重、帧率降低甚至是卡退。

    CPU 是负责解析执行指令的,那么cpu高负载的原因主要就是需要执行的指令过多,尤其是一些耗时的指令。在游戏中,主要是绘制指令的调用,也就是 drawcall。还有其他的一些计算量比较大的系统,例如物理系统、碰撞系统。另外就是结点的创建与销毁,以及业务代码中一些 update 逻辑。

    对于 drawcall 的优化,理想的情况是 drawcall 的次数越少越好。要了解优化 drawcall 的意义和方法,首先要知道在执行 drawcall 后, CPU 做了什么操作。

    CPU 对于图形处理不太擅长,所以一般都是将图形处理丢给 GPU (Graphics Processing Unit,图形处理器)去做,这就是为什么打大型游戏需要比较好的显卡的原因,其实就是需要性能更强大的gpu。

    CPU 要将数据交给 GPU 渲染,也不是啥都不用干的。CPU 需要把要渲染的数据,写入到数据缓冲区(显存),并设置渲染状态(纹理、着色器等),然后 GPU 才去取数据计算并渲染。

    由于 GPU 的图形处理能力强,所以每次给一点数据和一次性给一堆数据处理速度是差不多的。但是对于 CPU 来说,如果频繁调用 drawcall,每次一点点数据,那么 CPU 就会忙得焦头烂额。所以优化 drawcall 的最有效方式就是批处理了。

    批处理的方式就是合图了。所谓合图,就是将要渲染的纹理图合成一个大的图集,一次性送给 GPU 去渲染。例如有 3 个 sprite,3 个 sprite 有自己的纹理,如果不合图,那么就需要 3 次 drawcall。如果开启了合图,那么只需要 1 次 drawcall。 Cocos 小白的性能优化探索

    3 个星星图标的 sprite,显示 drawcall 是 4,为什么不是 3 呢,因为相机的背景本身需要一次 drawcall,所以星星总共需要 3 次 drawcall。 Cocos 小白的性能优化探索

    添加图集后,可以看到 drawcall 就变成 2 了,说明星星现在只需要 1 次 drawcall。 Cocos 小白的性能优化探索

    Cocos 小白的性能优化探索

    除了 sprite 可以合图,label 组件 (font) 也能支持合图。实际上,渲染字体也是将纹理送到gpu去渲染。

    字体分为两种实现方式,一种是位图字体 (Bitmap font),一种是 Free type 字体。

    所谓位图字体,就是将所有字符全部都打到一张图片中,这样做简单粗暴,效率也比较高,因为相当于字体都是预渲染好的。缺点是在字符集比较大时,例如所有汉字,那么字符的图片可能会比较大,内存占用率会比较高。并且不够灵活,因为图片的分辨率固定,在高分屏中,位图字体会出现一些锯齿。

    另外一种是 Free type 字体,例如ttf格式的字体。不同于位图字体使用像素来表示字体,Free type 字体只是定义了字体的渲染数据,需要在运行时实时计算然后渲染。这样的字体就不存在放缩问题,但需要一定的计算消耗,所以一般需要通过缓存来优化。

    对于只有数字和英文字母,并且文本结点比较多或者经常变化的情况,可以考虑使用位图字体进行优化,可以有效降低文字渲染造成的 drawcall 数。

    我们来看看这样一个简单例子。场景中有 3 个 label 结点,字体的格式为 ttf 格式。 Cocos 小白的性能优化探索

    预览一下,发现 drawcall 是 4,前面提到了相机默认会有一次 drawcall,说明 3 个文本结点带来了 3 次 drawcall,如果是大量文本结点或者文本结点经常变化,将会造成大量的 drawcall。 Cocos 小白的性能优化探索

    如果我们使用 BMFont,可以看到 drawcall 立即降为 2,也就是 3 个结点只绘制了 1 次,带来的 drawcall 优化非常可观。 Cocos 小白的性能优化探索

    对于系统自带字体,Cocos 也会为每个 label 组件创建字符纹理,并且默认不参加合图。

    Cocos 为 label 组件提供了类似 BMFont 的功能,我们可以使用 Cache Mode 来优化 CPU 。 Cocos 小白的性能优化探索

    Cache Mode 值为 NONE 的时候,Cocos 会为每个 label 组件的文本创建字符纹理,并且默认不参加合图。

    值为 BITMAP 的时候,Cocos 会为每个 label 组件的文本创建字符纹理,但是可以参加动态合图(后面会讲到),批量绘制。

    值为 CHAR 的时候,Cocos 会为字体生成一张单独的字符图集,并缓存起来。后续的新的文本,可以直接从字符图集缓存中获取,不需要重新渲染。(事实上 Cocos 官方文档对此的描述是”下次遇到相同字符不再重新绘制”,但就我的理解来说还是需要绘制的,否则为什么屏幕显示的文字会更新呢,所以应该只是复用了渲染的数据)。

    相较于自动图集这种静态合图方式, Cache Mode 为 BITMAP 使用的是动态合图。静态合图的方式是在构建时生成合图,而动态合图是运行时生成合图。静态合图会减少一些运行时的消耗,但是一些动态加载图片资源没办法应用静态合图,这时候可以通过动态合图进行优化。关于如何使用动态合图,Cocos 官方文档已经讲得很详细,这里不再赘述,可以直接查看:docs.cocos.com/creator/man…

    前面我们说到合图是降低 drawcall 是一种常见并且有效的手段,但是使用合图的方式会占用一定的内存,所以同时要关注内存指标。另外需要注意的是,合图之后并不意味着就能够批量渲染,参与合图的 sprite 或者 label 结点的需要是连续的。还是上面那个星星的例子,场景中有 3 颗星星,也就是 3 个 sprite,原本需要 3 次 drawcall,合图之后只需要 1 次 drawcall。我们在第一和第二个星星中间,加入一个 sprite 结点,批量渲染就会被打破:
    Cocos 小白的性能优化探索

    Cocos 小白的性能优化探索

    插入红色小方块后,drawcall 变成4。分别是相机背景 drawcall + 第一个星星 drawcall + 红色方块 drawcall + 第三和第四个星星的 drawcall。第一个星星本来可以和第三和第四个星星一起批量渲染的,被红色方块的渲染打断了。

    我们再将小方块的位置调整一下,调到第一个星星的前面。 Cocos 小白的性能优化探索

    Cocos 小白的性能优化探索

    可以看到,尽管显示上没有任何变化,但是 drawcall 变成了3次。

    所以,尽量让参与合图的结点连续,中间不插入其他的 sprite 类的结点,以免打破批次渲染。

    此外,mask 组件也可能是 drawcall 数量上升的元凶之一。mask 在 Cocos 中,主要是用来实现一些形状,例如圆角。

    为什么这么说呢,我们来看个例子:

    场景中有一个白色方块。 Cocos 小白的性能优化探索

    总的 drawcall 是 2,所以渲染方块需要 1 次 drawcall。 Cocos 小白的性能优化探索

    如果想要显示圆形,可以通过加 mask 组件来遮罩。 Cocos 小白的性能优化探索

    Cocos 小白的性能优化探索

    Cocos 小白的性能优化探索

    可以看到 drawcall 从 2 变成了 4,说明使用了 mask 之后,会产生 2 次 drawcall。很神奇哦,这是什么原理呢?

    cocos文档中的解释是这样的: Cocos 小白的性能优化探索

    结论就是使用 mask 组件的结点,绘制总共需要 3 次 drawcall,使用 mask 组件不能与相邻的结点合批渲染,即使它们使用的是相同的图集。所以,尽量少用 mask,如果要实现圆角等效果,结点的尺寸也比较固定,可以让设计同学直接给图。

    当然如果你和我一样想细扣里面的细节,什么是模板缓冲?为什么一定要 3 次 drawcall ?可以看接下的详细解释,需要一点 OpenGL 知识,如果不想深入细节可以直接跳过:

    1. 什么是模板测试?

      模板测试其实就是通过模板缓冲区中的设置,来决定某些区域要不要渲染。

    Cocos 小白的性能优化探索

    详细学习请见:[learnopengl-cn.readthedocs.io/zh/latest/0…learnopengl-cn.readthedocs.io/zh/latest/0… Advanced OpenGL/02 Stencil testing/)

    1. 使用 mask 组件的结点渲染三步骤

      可以通过spector.JS来查看渲染帧信息。这是圆形渲染相关的三个帧:

    Cocos 小白的性能优化探索

    第 1 帧渲染:

    渲染命令如下,意思是通过 6 个顶点画出 2 个三角形,实际上就是原本的小方块。 Cocos 小白的性能优化探索

    但是实际上这里并没有将小方块真正渲染出来。

    模板缓冲状态为 Cocos 小白的性能优化探索

    这里的意思是将小方块区域对应的模板缓冲区位置的值直接置为 0,也就是刷新该区域的模板缓冲区。

    第 2 帧渲染:

    渲染命令如下,意思是通过 186 个顶点,画出 n(很多)个三角形,其实就是画出圆形,因为在 OpenGL(Webgl)中,各种形状都是通过三角形去拼出来的。 Cocos 小白的性能优化探索

    模板缓冲状态为 Cocos 小白的性能优化探索

    直接将圆形遮罩对应的模板缓冲区位置的值设成 1。

    第 3 帧渲染:

    渲染命令如下,与第一帧一样,都是渲染出小方块,这次会将方块渲染出来。

    模板缓冲状态如下,意思是只有缓冲区对应位置的值为 1,才会渲染出来,所以方形被遮罩出了圆形。 Cocos 小白的性能优化探索

    除了 drawcall,一些逻辑计算也会影响cpu的使用率。例如 widget 组件的计算时机:

    Cocos 小白的性能优化探索

    如果选择了 ALWAYS,那么每一帧都会重新计算结点的位置、大小,所以比较耗计算。可以只选择 ON_WINDOW_RESIZE,只在窗口大小变化时,才会重新计算。如果还需要在其他时机计算 widget,可以按需手动调用 widget.updateAlignment

    另外,由于 update 这个生命钩子在每一帧都会调用,所以也需要注意在 update 中的逻辑是否执行过于频繁,例如不停地打 log,或者不停地计算,都会影响 CPU 的性能。

    结点的创建以及销毁也是比较耗费性能的,所以要避免频繁地进行结点的创建和销毁操作,并且应该尽量减少结点的数量。

    由于 Cocos 在 Web 中通过 canvas 进行绘制,没办法使用浏览器的开发者调试工具去查看结点,这里推荐一个 Cocos 插件 ccc-devtools,github 地址:github.com/potato47/cc… Cocos 小白的性能优化探索

    如果发现结点数量过多,并且结点频繁创建销毁,例如游戏中的小怪、子弹等数量比较多的重复物体,通常可以通过回收工厂进行优化。回收工厂就是结点用完之后,不销毁,而是缓存起来,下次获取结点可以直接复用缓存中的结点,而不需要重新创建。Cocos 本身提供了回收工厂的接口 NodePool,可以了解一下:docs.cocos.com/creator/man…

    游戏中的碰撞检测,也会比较耗性能。我们可以尽量使用box或者circle碰撞器,而少用多边形碰撞器

    3. 内存优化

    游戏中比较占用资源的主要是资源的缓存,例如图片资源缓存。而资源分为静态资源和动态资源。

    静态资源指的是,场景一开始进入时便立即加载的资源。动态资源是指在场景中异步加载的资源,例如一些网络图片、音频等通过 cc.loader.load 或者 cc.loader.loadRes 加载的资源。

    我们可以通过 cc.loader._cache 查看当前场景下面的资源列表 Cocos 小白的性能优化探索

    也可以通过前面提到的 ccc-devtool 可视化地查看资源列表,并且还能看到纹理资源的大小:

    Cocos 小白的性能优化探索

    Cocos 小白的性能优化探索

    注意到一张图片在内存中是比存在磁盘中要大很多的,因为在图片存在磁盘中时,是经过编码的,例如使用 png 和 jpg,数据量会小很多。但是存在内存中时,是解码成像素值的,所以需要占据的空间比较大。

    内存要降下来,也无非两种方式,一是减少不必要的资源、二是资源压缩。

    减少不必要的资源,例如:场景中的背景图,在移动端中是一套,在 PC 端是一套。那么应该是通过代码判断是什么平台,然后再动态加载对应资源的方式实现,而不是在场景中同时放置移动端和 PC 端的背景,然后控制显隐的方式实现。这样可以减少一套资源的内存占用。

    对于背景,一般来说由设计直接给图会比较大,如果是只是纯色或者通过简单的背景重复或者变换可以实现,可以由开发来实现,这样可以把大背景图优化掉。

    另外,合图的时候我们注意只将比较相关的图片进行合图,否则意味着可能加载一整张合图,只是用到其中的一个小图,会造成很多内存空间的浪费。

    资源压缩,主要是指对图片资源的压缩,也称纹理压缩

    单纯使用 tinify 等工具,对图片大小进行压缩,如果不改变图片尺寸,是不会减少图片资源在内存中的体积的,只能减小图片在磁盘中的存储体积。对于分辨率要求不高的资源,可以使用2倍图或者1倍图,可以减小资源在内存中的体积。

    纹理压缩算法,例如 Etc1, Etc2, PVRTC 等,可以优化图片在内存中的体积。jpg 和 png 格式虽然能够对图片数据进行压缩,但是并不能被gpu读取,所以是需要 CPU 解码之后再给到 GPU 渲染的。而经过纹理压缩算法压缩后的数据,是能够直接给gpu渲染的,所以纹理压缩不仅能够优化内存,还能优化 CPU。

    需要注意的是,纹理压缩一般都是有损压缩,可以选择压缩率。另外,纹理压缩的算法依赖于设备的 GPU 能否解码,所以针对不同的平台,需要使用不同的纹理压缩算法。

    关于纹理压缩算法的介绍,推荐看这篇文章:zhuanlan.zhihu.com/p/237940807…

    Etc1 绝大部分的安卓设备支持,PVRTC 所有的 iOS 设备支持。

    如果图片不需要支持 alpha 通道,安卓选择 Etc1 RGB、iOS 选择 PVRTC 4bits RGB 即可。如果需要支持 alpha 通道,安卓选择 Etc1 RGB Separate A,iOS 选择 PVRTC 4bits RGBA Separate ACocos 小白的性能优化探索

    Cocos 小白的性能优化探索

    对于不用的内存,我们也要及时释放,防止内存泄漏。分自动释放和手动释放两种。

    对于静态资源的释放,可以通过勾选场景自动释放选项来实现: Cocos 小白的性能优化探索

    这样在场景切换后,场景中的静态资源就会被自动释放了。

    如果不想等到切换场景才释放静态资源,也可以使用 cc.assetManager.releaseAsset 进行手动释放。

    有一个坑点是,动态加载的资源无法在场景切换时,跟随静态资源自动释放。需要通过 cc.setAutoReleaseRecursively 手动设置一下: Cocos 小白的性能优化探索

    这样资源在场景切换时,会自动释放这部分动态加载的资源。也可以通过 cc.loader.releaseRes 手动释放动态加载资源。


    起源地下载网 » Cocos 小白的性能优化探索

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元