Unity 性能优化

Unity性能优化

性能优化问题的本质 - 慢与快的问题

  • 前提
    1. 稳定性:不能因优化造成稳定性变差
    2. 兼容性:不能因优化导致兼容性变差
    3. 性价比:优化要有度,考虑成本与复杂度
  • 性能优化的流程
    1. 发现问题(什么平台、什么操作系统、什么情况下出现问题,一般问题还是特例问题等)
    2. 定位问题(什么地方造成的性能问题,我们要用什么工具、什么方法确定瓶颈)
    3. 研究问题(确定用什么方案处理这个问题,要考虑性能优化的前提)
    4. 解决问题(按问题研究的结论去实际处理,并验证处理结果与预期的一致性)
  • 影响性能的四大类问题
    • CPU、GPU、内存、带宽
  • 性能问题可能的情况(可能性按由高到低的顺序排列)
    • CPU利用率
    • 带宽利用率
    • CPU/GPU强制同步
    • 片元着色器指令
    • 几何图形到CPU到GPU的传输
    • 纹理CPU到GPU的传输
    • 顶点着色器指令
    • 几何图形复杂性

资源优化

  1. 模型:
    1. 降低模型面数、低模Mesh+高模法线、凹凸纹理
      1. 对于不会动态修改的网格关闭Read/Write,这样Unity只会将Mesh上传到GPU、不会在CPU内存中也保存一份。关闭会导致MeshCollider变得不可用
    2. 勾选Optimize Mesh、Mesh Compression
    3. 使用LOD,降低渲染压力,但会增加包体大小
  2. 纹理资源
    1. 降低分辨率,最好不要超过2048*2048
    2. 压缩算法,尽量将纹理大小设置为2的幂次方,来兼容更多压缩算法
    3. 取消勾选Read/Write Enabled
    4. 禁用多于 MipMap,可以减少1/3体积,针对距离不会改变的贴图
    5. 使用图集,相同纹理图集的静态网格可以进行静态和批,减少 DrawCall 数量
      • 图集划分的粒度
        • 共用的common不能太大。
        • 一个功能一个图集 登录,背包,技能,角色,商店等。
        • 一同出现的最好一个图集,比如主界面一个图集。
  3. 动画资源
    1. 2D帧动画转为spine动画
    2. 3D动画可以复用人体动画
    3. 降低帧率
  4. 脚本资源
    1. 减少第三方插件
    2. 考虑代码复用
  5. 音频
    1. 启动Force To Mono选项,强制单声道,减少音频文件大小
    2. 压缩, Vorbis 格式
    3. 减少采样率,通常22050Hz
  6. 字体
    1. 剔除字库中用不到的字
  7. 配置数据
    1. 将文本格式转换为二进制格式,如 protbuf
  8. AB包
    1. 压缩算法
    2. 远程部署、动态下载

场景优化

  1. 合理设计场景一级节点的同时,避免场景节点深度太深,一些代码生成的游戏对象如果不需要随父节点进行Transform的,一律放到根节点下。
  2. 尽量使用Prefab节点构建场景,而不是直接创建的GameObject节点。
  3. 避免DontDestroyOnLoad节点下太多生命周期太长或引用资源过多的复杂节点对象。Additive场景尤其要注意。
  4. 最好为一些需要经常访问的节点添加tag,可以极大加快查找速率
  5. 静态节点一定要添加Static标记,减少不必要的计算

UGUI优化

  • 减少DrawCall角度(尽可能合批):

    • 使用图集:防止纹理切换打断合批,减少DrawCall数量。
    • 避免使用Mask:会生成额外的DrawCall ,可以用RectMask替代
    • 避免ABA的问题:图集B的图片夹在两个来自图集A的图片之间,会增加DrawCall
  • 减少UI加载带来CPU消耗的角度

    • CPU主要两点消耗:1.动态和批、2.加载UI、3.GraphicRaycaster与所有UI元素求交

    • 使用对象池:本质是内存换CPU时间,频繁创建和销毁UI元素会增加CPU开销,可以使用对象池进行缓存。

    • UI预加载:开始游戏预先加载UI资源但不实例化,使用UIManager缓存起来,或者实例化后隐藏起来,打开后显示。

      • 拆分大的UI:将UI中默认隐藏的界面拆分出来,减少网格重建的复杂度。
    • 动静分离:将动和静的UI元素放到不同Canvas下,减少合并网格带来的CPU开销。

  • 异步加载:协程+回调

  • 合批过程(rebatch)(非主线程运行)

    1. 根据UI元素深度关系进行排序
    2. 检查UI元素的覆盖关系
    3. 检查UI元素材质并进行合批
  • 渲染细节

    1. UGUI中渲染是在Transparent半透明渲染队列中完成的,半透明队列的绘制顺序是从后往前画,由于UI元素做Alpha Blend,我们在做UI时很难保障每一个像素不被重画,UI的Overdraw太高,这会造成片元着色器利用率过高,造成GPU负担。
    2. UI SpriteAtlas图集利用率不高的情况下,大量完全透明的像素被采样也会导致像素被重绘,再成片元着色器利用率过高;同时纹理采样器浪费了大量采样在无效的像素上,导致需要采样的图集像素不能尽快的被采样,造成纹理采样器的填充率过低同样也会带来性能问题。
  • Re-Build 网格重建过程(在ReBatch过程中完成)

    • CanvasUpdateRegistry中维护了两个队列m_LayoutRebuildQueuem_GraphicRebuildQueue,每个继承Graphic的对象在顶点、材质、布局等信息发送改变后会调用setDirty方法向这某个队列中添加自身。Canvs中的willRenderCanvases事件回调函数会执行CanvasUpdateRegistryPerformUpdatef方法,方法中会调用队列中没有被销毁的ICanvasElementRebuild方法,方法内部会重新计算网格、材质、纹理数据,并调用canvasRenderer.SetMesh、canvasRenderer.SetMaterial、canvasRenderer.SetTexture方法
  • Canvas使用准则(优化)

    1. 将所有可能打断合批的层移到最下边的图层,尽量避免UI元素出现重叠区域
    2. 可以拆分使用多个同级或嵌套的Canvas来减少Canvas的Rebatch复杂度
    3. 拆分动态和静态对象放到不同Canvas下。需要取舍,动静分离会打断合批,增加DrawCall,而在UI元素少的情况下DrawCall消耗可能会大于合批。所以在UI元素过多的情况下考虑动静分离。
    4. 将文字和图片放在不同的Canvas下,以防打断合批(或者考虑将文字直接放在图片中)
    5. 不使用Layout组件
  • 射线(Raycaster)优化

    • 必要的需要交互UI组件才开启“Raycast Target”
    • 开启“Raycast Targets”的UI组件越少,层级越浅,性能越好
    • 对于复杂的控件,尽量在根节点开启“Raycast Target”
    • 对于嵌套的Canvas,OverrideSorting属性会打断射线,可以降低层级遍历的成本

Batching

  • 资源Batcing
    • Mesh
      • Mesh.CombineMesh,合并静态网格对象
    • SetTexture
      • AtlasTexture
      • TextureArray
    • Shader变量与材质属性
    • Material Property Block(Build In管线)
    • Const buffer(SRP管线)
  • Draw Call Batching
    • Static Batching
      • 合并静态网格
      • 存在内存开销
      • 64000个顶点限制,超过会创建一个新的合批
      • 影响Culling剔除
    • Dynamic Batching
      • CPU开销大
      • 900个顶点属性,(位置、法线、切线、uv都算顶点属性)
      • 材质实例不同也不能合批
      • 有多个shader pass的游戏对象不能合批
      • 延迟渲染不支持动态和批
  • GPU Instancing
    • 用于一次渲染一份网格的多个实例
    • 不与SRP Batching兼容
    • 有图形API版本要求
    • 渲染顶点数较少的网格效率会比较差
  • Set Pas Call Batching
    • SRP Batching
      • 适用使用相同Shader的物体
      • 有图形API版本要求,必须支持const buffer
      • 必须是SRP渲染管线
  • 合批失败原因汇总
    • 此物体受到多个前向灯光的影响
    • 此物体有不同的材质
    • 此物体使用了多pass着色器
    • 此物体Trasform的Scale使用了负数
    • 此物体接收阴影的设置不同,或者物体有不同的的阴影距离设置