Skip to content

Web 端低端机 11 → 30 FPS 优化 ​

11fps 的页面没有「一个慢的地方」,只有一堆各自慢一点的地方。DevTools 逐帧拆解出耗时分布,按性价比从高到低挨个打,最后叠加出 11 → 30fps。

低端机帧率11 → 30fps
动画帧耗时4ms → 0.2ms
Draw Call-40%

背景 ​

B 站用户虚拟形象 2.0 用 Three.js 渲染(技术选型阶段对比过 laya、cocos、three.js、babylon,最终选了 three.js——生态成熟,且底层源码可改,后面修 mv pass 内存泄漏靠的正是这一点)。1.5 版本只是简单的小电视模型,2.0 是完整人形:捏脸系统、换装系统、妆容系统、骨骼动画系统全上,性能开销指数级上升。上线后低端机型帧率只有 11fps,远低于可接受的 30fps。约束是:不能放弃 3D 效果去换帧率。

问题 ​

用 Chrome DevTools Performance 逐帧录制,看峰值帧、拆每帧主要耗时,分布如下:

瓶颈占比优化方向
渲染绘制~50%减少 Draw Call、降低顶点数
动画更新~25%骨骼动画替代 BS 动画
内存/GC~15%修复内存泄漏
其他~10%—

除每帧耗时外还有两条副线:模型资源体积大导致加载慢,以及连续换装后内存持续增长(泄漏)。原则是从占比高、改动小的地方先动手,每一项优化完重新录制验证,再看下一个。

值不值得做这件事,1.5 版本时已经论证过:不优化,低端机型的用户覆盖率和对应的营收都会直接受损——性能在这里不是体验分,是准入门槛。

方案 ​

mermaid
flowchart TD
    A["低端机 11fps"] --> B["DevTools 逐帧拆解<br>按性价比排序"]
    B --> C["渲染:合并 Mesh + UV 图集<br>Draw Call -40%"]
    B --> D["动画:BS → 骨骼<br>4ms → 0.2ms"]
    B --> E["内存:修 Three.js<br>mv pass 纹理泄漏"]
    B --> F["加载:KTX2 纹理<br>+ GLB/Draco 网格压缩"]
    B --> G["机型分级:CPU 评分<br>匹配画质档"]
    C --> H["稳定 30fps"]
    D --> H
    E --> H
    F --> H
    G --> H

关键实现 ​

1. BS 动画 → 骨骼动画 ​

BS(BlendShape)动画每帧要 CPU 计算顶点变形,开销大。改为骨骼动画后,变形变成骨骼矩阵运算,在 GPU 端完成;同时清理了动画数据里大量无用的 Track。动画更新的帧耗时从 4ms 降到 0.2ms,另外减少了碰撞关节数。

2. 减少 Draw Call ​

  • 合并同材质的 Mesh。
  • 把同贴图的 UV 合并到一张 Atlas 上。
  • 收益:Draw Call 减少约 40%。

3. Vue + Three.js 集成优化 ​

页面是 Vue.js 框架套 Three.js 渲染循环,两者叠加产生了额外的每帧开销。针对这层集成问题优化后,每帧节省约 20ms——这是单项收益最大的一笔。

4. 内存泄漏修复 ​

换装部件切换时 WebGL 资源未正确释放,根因在 Three.js 的 Motion Vector(mv pass)相关类。修改 Three.js 底层源码,在 Mesh dispose 时级联释放纹理,修复后连续换装 100 次内存稳定。排查过程详见内存泄漏系统性排查。

5. 模型加载优化 ​

  • 贴图使用 KTX2 压缩纹理,GPU 可直接采样,加快用户端解压和加载速度。
  • 模型使用 GLB(GLTF)格式,网格数据用 Draco 压缩算法压缩。

6. 机型分级策略 ​

不指望一台低端机跑出高端机的效果,而是按机型给不同画质档:以 CPU 评分作为主要分级依据。压测用跨晚特效弹幕、拜年祭的流量回放,从稳定性、CPU 占用、内存占用、FPS、温度多个指标评价,据此调整分级策略——低端机保住 30fps 底线,高端机不牺牲效果。

指标 ​

指标优化前优化后
低端机帧率11fps稳定 30fps
动画帧耗时4ms0.2ms
Vue + Three.js 每帧开销—节省约 20ms
Draw Call—减少约 40%
内存(连续换装 100 次)持续增长至崩溃稳定
CPU 占用高(BS 顶点计算)中等(骨骼矩阵在 GPU)

「效果媲美 Unity 引擎」是当时产品给的评价——Web 端做到了 Unity 原生引擎的流畅度。

复盘 ​

  • 优化顺序按性价比来:先看 DevTools 拆解的占比,从「收益/改动量」最高的下手,每打完一项重新录制验证。
  • 最大的单项收益不在渲染层,而在框架集成层(每帧 20ms)——瓶颈在哪要测出来,不能想当然。
  • 内存泄漏和帧率要一起治:泄漏制造的 GC 压力会直接反映成帧率抖动,15% 的内存/GC 占比就是这么来的。
  • 机型分级是兜底不是偷懒:低端机保住流畅底线,高端机释放完整效果,比「一刀切降画质」对用户更诚实。

罗子初 · AI 座舱专家 · 8年端侧研发

Lv.1