客户端性能优化

一些优化点的不完全总结

Posted by John Young on June 24, 2021

1. Profiling

最最总要的一部分。判断清除主次再进行优化

1.1 性能报告

  • 根据第三方性能的自动化测试报告确定性能瓶颈

1.2 引擎自带工具:

  • Unity Profiler 关键函数都有打桩(stub), 在时间上连续,可以排查性能峰值相关问题
  • Unreal Profiler
  • Magic Sniffer

1.3 IDE:(CPU、 网络,一般是快照)

  • Visual Studio性能探查器
  • Xcode Instuments
  • Android Profiler
  • Android Systrace(需要打桩)

1.4 渲染分析工具:

  • Xcode
  • RenderDoc
  • Visual Studio Graphics Diagnostics
  • Nvidia NSight (其中metal和Dx11可以pixel debugging)

2. 资源优化

2.1 纹理

2.1.1 纹理尺寸

  • 美术资源使用合理的纹理大小
  • RT、shadowmap使用合理的分辨率(trade off)
  • 了解设备支持的最大尺寸 (大部分支持4096x4096)
  • NPOT vs POT
    • ios PVRTC 只支持NPOT,而且是方图
    • 某些硬件只支持NPOT(纹理寻址、MIPMAP, 移位操作比(除法/乘法)快)
  • 资源检查管线

2.1.2 纹理通道

  • 灰度图、高度图、掩码图,16位图、8位图
  • 纹理压缩也是减少纹理通道的手段(GPU直接支持,可以减少包体、内存、显存)
  • 纹理压缩格式:
    • Window(DirectX) -> DDS,
    • Android -> ETC1(Alpha通道需要分开), ETC2
    • IOS -> PVRTC 压缩一般需要白名单,某些资源需要不压缩(压缩带来透明通道精度损失)

2.1.3 纹理复用

  • 共享图库(尽量使用通用控件,达到复用)
  • 九宫切图
  • 复合纹理(翻转、镜像、顶点偏移)

2.2 UI

2.2.1 图集

  • 图集应该关闭Mipmap
    • 减少30%纹理大小
  • 图集应该合理规划
    • 一个界面尽量只引用自己的图集和共享图集

2.2.2 UI层次

  • 合批条件:
    • 绘制顺序连续
    • 材质相同
  • 应该避免:
    • 引用很多别的图集
    • 自身图集和公共图集相互穿插
    • 文本穿插在sprite之间(极限优化的话,文字可以考虑做到sprite上,但是影响本地化)
    • 使用Mask(一个mask至少增加两个DC)
    • 频繁增删元素导致canvas更新,可能引起重新提交顶点

2.3 字体

2.3.1 尽量使用sdf字体

sdf字体优势: - 高质量缩放,增大字号无需额外纹理 - 描边、glow、文字阴影在shader里就能实现,且质量很高

2.4 模型(包括动画)

  • 模型减面(不可见面片剔除、低模)
  • 顶点属性剔除、浮点精度降低
  • LOD
  • static batch, dynamic batch
  • GPU instancing(大量同种模型)
  • GPU skinning

2.5 场景

  • static batch
  • light map烘培

2.6 粒子

性能杀手(CPU + GPU):

  • 每帧计算量大。涉及发射器/效果器/曲线插值等,耗费CPU性能。
  • 频繁操作内存。粒子在生命周期里,实例持续不断地创建/删除,即便有缓存机制下,依然避免不了内存的频繁读取和碎片化。
  • 每帧更新数据到GPU。从Lock顶点Buffer到写入数据到GPU,会引发线程等待,也加重CPU到GPU带宽的负担。
  • 增加大量Draw Calls。粒子特效通常五花八样,使用很多材质,导致引擎无法合批优化,大量增加绘制次数。
  • 导致Overdraw(过绘制)。粒子一般会开启Alpha Blend,在同屏粒子多的情况下,会造成严重的Overdraw。

优化方法:

  • 优化粒子属性:关闭阴影、光照。不透明的粒子关闭alpha blend
  • 禁用高级粒子特性:粒子碰撞器、模型粒子
  • 制定美术规范:限定粒子个数、贴图尺寸、材质个数等
  • 使用bookflip(序列帧动画)
  • 使用GPU 粒子(Unity: Vfx Graph, UE:Niagara)

3. CPU优化

  • 缓存计算结果
  • 预处理(空间换时间)
  • 分帧处理卡顿
  • 多线程
    • 文件IO
    • 网络IO
    • 骨骼动画
    • 渲染指令提交
    • 音视频解码
    • 加密解密
  • 性能敏感逻辑考虑写到native层
  • 使用GPU(compute shader)

4. 渲染优化

渲染性能消耗点:

  • DrawCall
  • OverDraw
  • 带宽负载(CPU到GPU的传输能力)
  • 显存
  • GPU计算量(片元数目、shader复杂度)

优化方法:

  • 合批,减少DrawCall
  • 减少Overdraw:禁用Alpha Test,少用Alpha Blend
  • 多线程渲染:硬件级:Vulcan/Metal,软件级:依赖引擎架构
  • 减少片元数目:软件阶段剔除(视锥剔除、遮挡剔除)、硬件阶段剔除(视口裁剪、背面剔除、early-Z优化)、慎用MSAA
  • 画质分级
  • 优化Shader
    • 降低浮点精度
    • 避免discard fragment
    • 能在Vertex中计算的不要放到Fragment;能在CPU算到uniform中的不要放到vertex中算
    • 像素光照改为顶点光照
    • 矢量计算与标量计算尽量分开进行
    • 考虑预生成LUT(Look Up Table)
    • shader分级(LOD)
    • 避免循环和分支(考虑手动loop unrolling)

5. 内存优化

  • 内存池
  • 对象池
  • GC控制

6. 网络优化

  • 合理使用字段
  • 更高效的压缩
  • 多线程处理网络IO