现象
总体而言, iOS 14 渲染性能变差, 可以从以下几个测试看出.
测试 1:
简单 demo, 使用 egret 引擎显示 3000 个图(都是同一个 100*100 PNG 纹理), 逐帧做旋转.(博客园视频播放可能有问题, 视频地址:)
视频中, 黑色机器是 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 个.(博客园视频播放可能有问题, 视频地址: )
和上边测试 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.(博客园视频播放可能有问题, 视频地址:)
情况和 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 做监控.
来源: https://www.cnblogs.com/kenkofox/p/13893092.html