unity性能优化方案

unity性能优化方案

1.Unity 性能调试工具及使用方法
2.垃圾回收(Garbage Collection)
3.CPU相关优化
4.GPU相关优化
5.使用UPR(Unity Performance Reporting)云服务进行性能测试

每一帧需要做到的事情

  • 渲染一帧所需的时长 > CPU计算所需时长 + GPU计算所需时长
  • (比如,如果CPU上的物理计算和脚本运行要花很多时间,那么即使Shader优化的再好,也不会提高帧率;反过来说,如果GPU上压力山大,那么优化物理系统和脚本也对提高帧率没有什么大的帮助。因此,我们要分析找到整个系统中的薄弱环节,也就是漏水的水桶上最低的那一块板。)

CPU 和 GPU 各自负责的工作

  1. CPU 相关:Skinning,Batching(Static Batching,Dynamic
  2. Batching等),物理相关,用户脚本,粒子效果等 GPU 相关:Shader,Drawcalls,Image
  3. Effects(后处理) 但是CPU和GPU不是各自孤立的:
  4. Draw Call太高 -> 使用CPU做Batching降低Draw Call -> 性能提升
  5. Draw Call不高 -> 使用CPU做Batching降低Draw Call -> 浪费CPU处理能力,导致culling效率下降 -> 性能下降

优化时使用的环境

对Project Settings进行优化设置

  • 通过编辑器顶部菜单Edit/Project Settings打开项目设置界面。找到Player下面的 Other Settings界面。
  • 确保Static Batching,Dynamic Batching,GPU Skinning,Graphics Jobs 都已经启用。
  • (如果是Oculus Quest)禁用ARMv7,打开ARM64。在Scripting Backend下拉框中选择IL2CPP,这样所有的C#代码会被转成C++代码,提高运行速度。
  • (如果是Rift和Rift S)在Scripting Backend下拉框中选择IL2CPP(但是Debug会变得不方便),所以在开发阶段可以使用Mono,正式出包时改成IL2CPP。
  • 打开XR Plugin Management设置界面,安装Oculus XR Plugin,确保Initialize on Startup为勾选状态。
  • 打开Oculus设置界面,确保Shared Depth Buffer和Dash Support都为勾选状态,然后把Stereo Rendering Mode设置为Single Pass Instanced。
  • 在Quality设置界面,确保Android平台下只勾选Medium选项,其他选项都要为未勾选状态;其他平台可以全部勾选。
  • 将Pixel Light Count设置为1或者0(如果通过Vertex着色器提供光照信息的话)。
  • 把Anti Aliasing 设置为 4x Multi Sampling,在Oculus Quest也可以用,因为对GPU压力不大,但是画面质量可以提升很多。
  • 把Shadows 设置为 Disable Shadows 或者Hard Shadows Only。
  • 将Blend Weights设置为2 Bones。
  • 打开Graphics设置界面,确保Android平台设置为Low或者Medium(Oculus Quest)。

代码相关

  • 可以使用 Invoke Repeating 或者 Coroutine 来替代 Update 方法

  • Debug.Log() 方法会消耗很多性能

  • 注意缓存经常需要用到的 Component(组件)

CPU 相关优化

  • 光照烘焙
  • 使用 Occlusion Culling
  • 通过 Mesh Baker Free 提升网格资源性能
  • GPU Instancing
  • Texture Atlasing
  • 从脚本中获取材质时要使用 Renderer.sharedMaterial 而不是 Renderer.Material,因为后者会产生一份新的copy,如果原先这份材质是被 Batch 的,那么 Batch 会失效

使用Occlusion Culling(遮挡剔除)

一般降低drawcall通用方法

  • 在 Hierarchy 窗口中找到 Main Camera,在Scene窗口用旋转工具对相机进行旋转,我们可以看到不管相机朝向哪个方向,在 Scene 窗口中从顶部往下看,场景中所有的 GameObject 都会被渲染。
  • 把 Main Camera 上的 Occlusion Culling 选项勾选。
  • 在 Occlusion 界面中点击右下角的 Bake 按钮,稍等片刻完成烘焙。

通过 Mesh Baker Free 插件提升网格资源性能

这个插件可以合并网格

网格合并可能的副作用是:因为合并完的网格过大,导致Occlusion Culling不起作用。

GPU Instancing

  • 可用较少的 Draw Call 渲染多个使用同一网格(Mesh)的Game Object,提高渲染性能。
  • 适用于渲染建筑物,树林,草地,或者任何在场景中可以重复使用的模型。
  • 即使这些 GameObject 拥有不同的颜色,尺寸等变化,只要它们使用的是同一个网格,就可以使用GPU Instancing。
  • 也可以在脚本中使用 GPU Instancing API 实现相关功能:
    Graphics.DrawMeshInstanced 和 Graphics.DrawMeshInstancedIndirect

Unity 的 Batching 优先级:

  • Static Batching > GPU Instancing > Dynamic Batching
    • 如果 Project Settings > Player 界面中的 Static Batching 勾选,GameObject也被设置为 Batching Static 并且材质的 GPU Instancing 打开:会在 Inspector 中显示一个报警,提示 GPU Instancing 会被禁用。

GPU相关优化

  • GPU优化的三个方面:
    • Fill Rate(填充率):
      • Pixel Fill Rate:GPU每秒可以在屏幕上画出的像素数量
      • Texture Fill Rate:GPU每秒可以查看的贴图数量
  • GPU Memory Bandwidth(显存带宽):
    • 每一秒GPU可以传输到显存(VRAM)的数据量(以字节为单位)
  • Vertex Processing(顶点处理):
    • 网格上的顶点数量
    • 针对每个顶点要做的操作数量

检查方法

  1. 在Profiler中查看GPU在渲染上花费的时长
  2. 在Player Settings中降低分辨率
  3. 再次运行查看GPU的渲染时长
  4. 如果性能提升,就说明GPU来不及渲染有 Fill Rate不够的问题

具体优化方法:

  • 减少 shader 中的 Fragment Shader 的复杂度,因为 Fragment shader 负责告诉GPU要怎么往屏幕上画像素。
  • 使用更优化的 Shader,比如 Mobile Shader (Mobile Shader 也可以用在除移动平台之外的地方)
  • 如果使用的是 Standard Shader,可以减少使用贴图的数量,这样 Unity 在编译时会自动优化。
  • 需要分析所使用的 Image Effect 对整体 Fill Rate 的影响,如果实在影响很大,那就要禁用某些 Image Effect。

GPU Memory Bandwidth

  • 在Profiler中查看GPU在渲染上花费的时长
  • 在Quality Settings中降低Texture Quality设置
  • 再次运行查看GPU的渲染时长
  • 如果性能提升,就说明贴图太大,显存不够用

具体优化方法:

  1. Texture Compression
  2. Mipmap 和 Mipmap Streaming

Texture Compression

  • Texture Compression 优化:
    • 例如: Build 构建完成后,这会在Editor Log里面生成一份构建日志、打开 Window > Console 窗口,在 Console 窗口右上角三个小点图标那里点击 Open Editor Log 菜单。在打开的文本中搜索:Used Assets and files from the Resources folder, sorted by uncompressed size
      这搜索到的地方可以看到所有在项目中使用的,但是没有被压缩过的贴图。
    • 对图片的compression属性进行更改有几种对应的压缩方法

Mipmap 和 Mipmap Streaming

  • Unity中的Mipmap相当于Texture的Lod技术,为了防止物体离相机较远时因为贴图的采样率不足导致出现条纹。Unity中生成Lod的方法很简单,在Texture的面板中勾选Generate Mip Maps,然后点Apply即可。生成完毕后可在下方查看每一级level所对应的图片,原理应该是均值采样法。
  • mipmap是很典型的用存储空间占用换内存占用的方法(如果资源的 Z 方向上未发生变化,那么使用 Mipmap 是一种浪费:)
  • Texture Steaming技术是解决Shader渲染时整个Texture Mipmap金字塔全部加载的问题,在模型离相机较远时的优化效果很好,换句话说大世界游戏的优化效果很不错.当 相机看到图片会计算得到一个合理的mipmap level,然后将其他level的mipmap全部裁剪。不进行加载,同样在MaxLevelReduction进行设置。在游戏运行时会得到一个先模糊再清晰的过程。

Vertex Processing

  1. 尽量减少网格上的顶点数量(简化模型)
  2. 使用 Normal Map 表现模型细节
  3. 如果模型上没有使用 Normal Map,可以在 Model Import Setting 界面禁用此模型的 Vertex Tangents 选项。
  4. 使用 LOD
  5. 降低自定义 Shader 中 Vertex shader 的复杂度(Vertex shader 是 shader 中告诉GPU如何画出每个顶点的代码块)
  6. 如果使用的是标准着色器,那么尽量使用简单的着色器,比如 Mobile Shader。
  7. 如果模型上没有使用 Normal Map,可以在 Model Import Setting 界面中将 Normals 设置为 None
  8. 如果不是要动态生成网格,或者需要在运行时拷贝网格的数据,Read/Write Enabled这个选项不应该打开。
  9. 如果打开读写,那么网格信息会被同时上传到 GPU 和 CPU;否则只会上传到 GPU。所以通常是不打开可读写选项,可以节省内存。
  10. 可以通过使用纹理和法线贴图来降低模型顶点数,合理布局UV避免接缝并让纹理可以均匀地分布在模型表面,复用模型和使用 Instancing 与 Static Batching 来进行模型合并减少Draw Call数量。
  11. 玩家会拿起物体靠的很近地观察,所以需要VR布局很均匀,因为在VR中可以从任何角度进行观察。
  12. 也要注意贴图接缝这些细节,因为玩家会仔细观察。
  13. 限制所用材质的数量可以减少 Draw Call,减轻GPU的负担。
  14. 使用模块化方式设计制作的模型可以尽量复用。

资产大小缩放建议:

  1. 确保场景中的物体有一致的尺寸大小非常重要。如果场景中的物体大小不统一,比如一扇房门比人还矮,或者一辆汽车像山一样大,都会让人觉得不真实。
  2. 另外,在普通游戏中常用的动态改变FOV的方法在VR中并不适用。在VR中,建议保持FOV不变。
  3. 在缩放场景中模型的时候,为了确保物理效果统一,也要相应的缩放碰撞体的大小。否则物理模拟的效果就会不真实。
  4. 人们在体验VR的时候,通常会把头钻进场景中的模型中,想要看到他们不该看到的地方。我们可以设计成当侦测到玩家把头转进模型中时,用渐变的方式把整个屏幕变黑。要做到这一点,我们要为所有相关模型设置正确的碰撞体大小才行。

猜你喜欢

转载自blog.csdn.net/bellainvan/article/details/127002843