iOS 14 egret 游戏卡顿问题分析和部分解决办法
现象
总体而言,iOS 14 渲染性能变差,可以从以下三个测试看出。
测试1:
简单demo,使用egret引擎显示3000个图(都是同一个100*100 png 纹理),逐帧做旋转。(博客园视频播放可能有问题,视频地址:https://github.com/kenkozheng/kenkozheng.github.com/blob/master/WebGL/ios14/video/1.mp4?raw=true)
视频中,黑色机器是iOS14.0,白色是iOS13.7,都是iphone 7plus。
虽然从视频中来看,iOS 14的fps还要高一些,但实际上14明显卡顿。原因是:Egret检测的fps是web层面通过requestAnimationFrame得到的,实际上和画面渲染没有严格对等关系。
改为通过perfDog,从native层面看帧频,看到iOS14只有13fps,而旧版本有40+,这也解释了为什么肉眼看起来14的渲染要更卡顿。
测试2:
复杂demo,使用egret引擎显示candy爆炸龙骨动画100个。(博客园视频播放可能有问题,视频地址:https://github.com/kenkozheng/kenkozheng.github.com/blob/master/WebGL/ios14/video/2.mp4?raw=true)
和上边测试1类似,egret左上角的fps显示并不准确,通过perfDog检测,实际帧频只有7fps左右。

测试3:
在复杂demo基础上(还是100个爆炸动画),修改egret代码,禁用颜色混合shader,所有元素渲染都统一使用普通shader。
由于龙骨设定为24fps,而实际fps有40,从视频中肉眼无法看出卡顿。所以这里视频省去。
也是类似的情况,iOS14比iOS13渲染fps低,iOS14只有8fps左右,而iOS13有40+fps。
测试4:
使用自研的简单webgl引擎(min2d),显示15000个100*100的png。(博客园视频播放可能有问题,视频地址:https://github.com/kenkozheng/kenkozheng.github.com/blob/master/WebGL/ios14/video/3.mp4?raw=true)
情况和egret引擎类似,还是iOS14明显卡顿,虽然界面显示fps较大,但实际帧率只有10fps左右。题外话:自研引擎性能略比egret好10%左右,但上边测试中能支持15000个图片,只是因为自研引擎没有做像素密度加倍尺寸渲染。
由此可见,iOS14 webgl性能确实比iOS13有明显下降。
分析
从egret的监控来看,js层面的耗时(包括顶点计算、调用webgl)都没有明显问题,iOS14比iOS13甚至还有一些优化。但实际渲染帧频,iOS14又明显比iOS13更低,问题应该出于safari内部对webgl接口的具体实现上有一些改变。
调试过程中,发现两个比较奇怪的现象:
1、本来满帧运行,但运行一段时间后,会下降到40-50fps。
2、50个爆炸动画播放时能稳定在50fps,但增加到60个爆炸动画之后,fps会断崖式下跌,到14fps左右。
从这个现象,推测内部实现在显存管理上,可能出了较大变化,可能有一些缓存,数据达到阈值后,可能有反复的数据交换。

首先,排查了几个方面:
1、drawCall推送vertex buffer的方式(webgl.bufferData vs webgl.bufferSubData)
egret每次drawCall使用webgl.bufferData推送顶点数据,这个方法每次推送覆盖整个bufferData;而webgl.bufferSubData可以指定offset,只覆盖部分数据。
但由于每次修改的数据量并不多,两者从理论和实际测试来看,都没有区别。
2、推送纹理、webgl初始化设定(抗锯齿等)、frameBuffer
上述方面,egret的设置都属于通用做法,并没有特殊,而且调整了参数后,性能并没有提升。
3、去除shader的alpha计算
也没有明显变化
4、去除blendMode处理
虽然有明显的性能提升,但在iOS14上的性能提升并不比iOS13上的提升更大,blendMode并不是iOS14变慢的主要因素。
而且BlendMode是游戏素材制作的必需选项,影响到透明叠加效果,无法简单去除。
上述几个方面都没有找到解决方式。
另外,另外的游戏引擎cocos creator,官方提出在cocos引擎中使用了多次drawCall共享vertex buffer和index buffer的优化技术(也是常规的优化手段),但在iOS14中反而变成了性能瓶颈,已针对做了处理(针对iOS14,每次drawCall使用不同的vertex buffer)。
参考:
https://forum.cocos.org/t/ios-14-web/97808
https://github.com/cocos-creator/engine/pull/7415/files#
分析egret的实现,设置了默认每次drawcall最多同时批处理2048个元素,对于一般sprite来说,一个元素就等于一个图,等于一个长方形,等于两个三角形,也等于六个顶点。

进一步,egret在初始化时,会创建一个12288个unsigned short组成的index buffer,然后bindBuffer到GPU。这个过程一般只执行一次,后续不会再绑定,也不会再创建新的buffer(网格拉伸情况除外,会换一个indexbuffer数据内容)。
那么,每次drawcall时,无论是多少个元素,哪怕只有1个元素(6个顶点)都会使用这个12288长度的index buffer。
从这个角度来看,确实可能存在优化的可能。
引擎改进
从现象和分析过程得出,iOS14确实有性能下降,我们可以从一些维度,尽可能挽回一些性能下降。
目前确认可以从引擎层面改进的是index buffer问题。
改进的策略是:判断是否iOS14,如果是,就在每个drawcall前,推送新的index buffer和vertex buffer数据,这些数据只包括本次渲染所需,没有多余数据。
具体改动:
WebGLRenderContext的$drawWebGL方法中,判断是否Mesh绘制,在非Mesh绘制情况下,切分vao中的indices array和vertices array,取出本次drawcall所需部分,上传给gpu。而且,在这个情况下,drawData要忽略offset,改为固定的0(offset是对应vertex buffer中包含多次drawcall数据时才使用,现在每次按需推送,所以就不需要offset了)。

同样渲染50个爆炸龙骨动画,修改后的版本性能有明显提升。 如下图,左侧1分钟是原有版本下绘制50个爆炸龙骨动画的fps情况,右侧是优化后版本的fps情况。原有版本平均10fps,而优化后平均20fps(这个帧率可能和前边测试demo略有差异,是由于iphone渲染一段时间后变慢有关)。

index buffer的使用调整,确实能解决上述爆炸龙骨动画在iOS14的性能问题。
另外,排查过程中,还发现一些值得探索的方向:
1、带filter和不带filter的图元,如何批处理。egret引擎原来没有做批处理。如果能实现这个优化,将极大减少龙骨的渲染drawCall。
2、调整像素密度绘制策略。egret引擎默认以屏幕像素密度作为倍数绘制webgl画布,但游戏素材并没有这么大,这个扩大渲染对性能有影响,但视觉效果没有提升。
第2点,尤其可以针对低端机型,例如系统版本在6.x或以下的android,这部分机型本来性能就较差,但还可能按2-3倍像素去绘制webgl,渲染帧率就更低。
例如,oppo r9 系统版本5.1,屏幕像素密度3,强制以密度1绘制,性能能够提升30%。
除上述提到的方向外,针对iOS14,可能还存在更多针对性优化的方向,但还需要针对具体的场景,逐个分析。
性能结论
iOS14对比iOS13和以前版本,在webgl渲染性能上有明显下降,尤其在drawcall次数较大、渲染面积较大或使用较多颜色混合滤镜情况下,下降尤其明显。
针对iOS14,虽然能在一些方面改善性能,但单纯从js角度,无法让webgl渲染性能恢复到iOS13的水平,只能寄望于苹果官方自行修复底层问题(已有不少反馈到苹果论坛)。
素材开发建议
除了从引擎底层解决iOS14卡顿问题,另外,针对游戏业务素材,还可以做一些改动,提高渲染性能:
1、减少龙骨动画层级,减少图元个数;
2、避免使用颜色混合和BlendMode(混合模式);
3、避免使用有大面积透明区域的图片,可以把图片切分为只有有效内容的多个小图。
另外,iOS14在js层面监控到的帧频不是真正的webgl渲染帧频,性能优化需要直接连接perfDog做监控。
iOS 14 egret 游戏卡顿问题分析和部分解决办法的更多相关文章
- H5:加载原理,慢加载和卡顿原因分析,
前端H5工作原理: 请求和显示原理 H5页面卡顿原因分析: 1.动画太多:渲染重绘占用GPU 2.页面操作导致重绘频繁 3.页面元素复杂:资源类标签太多(图像/视频/dom树太长) 4.内置webvi ...
- 基于webstorm卡顿问题的2种解决方法
基于webstorm卡顿问题的2种解决方法:https://www.jb51.net/article/128441.htm
- android studio 一直卡在Gradle:Build Running的解决办法
转:android studio 一直卡在Gradle:Build Running的解决办法 在使用AS开发安卓应用程序的时候经常会遇到Gradle build running一直在运行甚至卡死的 ...
- ios iphone ipad上iframe的宽度会扩大的解决办法
这个问题,我从网上查了下,好像是属于ios的bug,android,windows都没有问题. 解决办法,就是在iframe加载完成后,设置 iframe里面body的宽度为多少PX. $(" ...
- Android中app卡顿原因分析示例
在知乎回答了一个“为什么微博的app在iPhone比Android上流畅”的问题.后面部分是一个典型的动画卡顿的性能分析过程,因此帖在这里.有编程问题可以在这里交流.知乎链接. =========== ...
- ios添加-webkit-overflow-scrolling依然卡顿
项目由vue-cli2创建 在overflow: auto区域内滑动ios手机出现卡顿,搜索资料后添加-webkit-overflow-scrolling: touch ios bug: 1.滑动区域 ...
- 【bug清除】Surface Pro系列使用Drawboard PDF出现手写偏移、卡顿、延迟现象的解决方式
最近自己新买的New Surface Pro在使用Drawboard PDF时,出现了性能问题,即笔迹延迟偏移,卡顿的问题. 排查驱动问题之后,确认解决方案如下: 将Surface的电池调到性能模式, ...
- DEDE织梦 后台特别卡,有时响应超时的解决办法
跟大家一样,大致情况是: 1.打开后台首页第一次没问题,但是刷新或者点其他菜单就一直卡着了. 2.关掉浏览器重新进首页没问题,但是一旦进了首页再打开php页面就卡死了. 3.服务器返回Maximum ...
- ios 页面过长卡顿的情况
解决方案如下: -webkit-overflow-scrolling: touch;
随机推荐
- SSRF漏洞(原理、漏洞利用、修复建议)
介绍SSRF漏洞 SSRF (Server-Side Request Forgery,服务器端请求伪造)是一种由攻击者构造请求,由服务端发起请求的安全漏洞.一般情况下,SSRF攻击的目标是外网无法访问 ...
- 消息队列之事务消息,RocketMQ 和 Kafka 是如何做的?
每个时代,都不会亏待会学习的人. 大家好,我是 yes. 今天我们来谈一谈消息队列的事务消息,一说起事务相信大家都不陌生,脑海里蹦出来的就是 ACID. 通常我们理解的事务就是为了一些更新操作要么都成 ...
- @DependsOn注解的使用
如果Bean A 在创建前需要先创建BeanB此时就可以使用DependsOn注解 @Configuration public class MyConfig { @Bean @DependsOn(&q ...
- Tomcat学习小记(一)
1.Tomcat概述 Tomcat 服务器是一个开源的轻量级Web应用服务器,擅长处理动态资源,在中小型系统和并发量小的场合下被普遍使用,是开发和调试Servlet.JSP 程序的首选. Tomcat ...
- Centos-进程运行状态-ps
ps 显示系统进程在瞬间的运行状态 相关选项 -a 显示所有用户的进程,包含每个程序的完整路径 -x 显示所有系统程序,包括那些没有终端的程序 -u 显示使用者的名称和起始时间 -f 详细显示程序执 ...
- 中心极限定理(为什么y服从高斯分布)
因为每一条数据都服从IID原则: 根据中心极限定理,当数据增加的时候,样本均值的分布慢慢变成正态分布 不管分布式什么分布,累加起来都是高斯分布 As sum increases, sum of non ...
- 类名@6d5037a9(地址)
问题: 输出的结果不正确 应出现结果 解决问题方法 在@Autowired注入的@Value中缺少toString方法 @Override public String toString() { ret ...
- windows10开机后恢复关机前打开的chrome网页
开始-设置-账户-登录选项-隐私,下面一个"更新或重启后--"即第二个开关打开. 这时,无论是重启.更新重启或关机后开机,均可恢复原关机时的网页(和应用). 注:题主安装的wind ...
- 02 C语言最简单快速上手的IDE
不要让开发环境 成为学习路上的拦路虎,先培养学习兴趣和学习路上的成就感,这个对于激励自己持续学习很重要. 等真正从小白入门了,甚至成为大牛了,能解决诸多困难问题了,安装个开发环境还会再是个什么难事吗? ...
- 【题解】SP1811 LCS - Longest Common Substring
\(\color{purple}{Link}\) \(\text{Solution:}\) 题目要求找到两个串的最长公共子串.\(LCP\) 我们将两个串中间和末尾插入终止符,并弄到一棵后缀树上去. ...