[Unity优化] Unity CPU性能优化 (难度3 推荐4)

原文地址:

http://www.cnblogs.com/chwen/p/4396515.html

  前段时间本人转战unity手游,由于作者(Chwen)之前参与端游开发,有些端游的经验可以直接移植到手游,比如项目框架架构、代码设计、部分性能分析,而对于移动终端而言,CPU、内存、显卡甚至电池等硬件因素,以及网络等条件限制,对移动游戏开发的优化带来更大的挑战。

  这里就以unity4.5x版本为例,对Unity的优化方案做一个总结,有些是项目遇到的,也有些是看到别人写的不错拿来分享,算作一个整理,后期也会持续更新。本优化从CPU、GPU和内存三个方面着手总结,这一篇先从CPU说起,整理一些针对CPU相关的优化建议。

  对CPU的优化主要是从drawcall、物理组件、GC(垃圾回收)、脚本等几个方面开展。

  1. Drawcall 的优化
  • 什么是Drawcall?

  Drawcall是CPU向GPU发送绘制命令的接口调用。理论上每一个不同材质的物件需要渲染在屏幕上时,CPU都会调用图形API ( openGL or Diract3D ) 的Draw接口触发显卡进行绘制。

  • 为什么优化Drawcall?

  Drawcall对硬件和驱动而言,要求大量设置状态(使用哪些顶点、哪些shader等)和状态转换。而Drawcall最大的消耗在于:如果每次drawcall只提交少量的数据将导致CPU瓶颈,CPU无法将GPU填满。Drawcall对GPU的耗费在于硬件一直等待CPU提交数据,而无法得到有效利用。GPU大量的时间耗费在不断切换状态和正确性检测上。 GPU在Draw Call之间,为了防止前后Draw的依赖关系造成绘制错误或者资源竞用,一般会在Draw Call后Flush整个流水线,小粒度的Draw Call对GPU流水线来说是个很大的浪费。(这个问题在D3D老版本存在,在新版D3D11中得到改善。)实际上unity官方指出,Drawcall数量的降低并非重点,重点是减少批次的数量,Drawcall优化实际上是对批次数量的优化。 
  延伸阅读:
  · why are draw calls expensive? — Stack Overflow
  · Direct3D Draw函数 异步调用原理解析

  • 如何优化Drawcall?

  在Unity中对Drawcall的优化有以下几个策略:Drawcall batching,合并打包图集,减少光照和阴影以及遮挡剔除和视锥剔除等。以下分别谈一下各个策略的优缺点。

  1.1. Drawcall Batching

  Unity中对Drawcall的批次有两种:静态批次(static batching)和动态批次(dynamic batching)。但不论静态批次还是动态批次都要求对象的材质是共享的,即不同材质的对象是无法进行批次的。而且要注意的一点:如果在脚本中调用材质时,使用Renderer.material会造成材质的拷贝,而使用Renderer.sharedMaterial来调用则不会拷贝材质。

  1.1.1. 静态批次 Drawcall static batching

  场景中的多个物件如果是不移动的(包括位置、缩放、旋转等),并且共享同一材质,比如地形、建筑、花盆等,那么可以选择采用静态批次。静态批次只需要在Inspector勾选static选项即可。静态批次需要注意的是,unity会将进行批次的多个对象合并成一个大的对象,也会导致内存损耗,有时候要避免太多对象静态批次造成的内存过高。这也表明,优化并非绝对做好某一方面,而是平衡各个硬件的瓶颈和效率,选择相对适中的方案。

  1.1.2. 动态批次 Drawcall dynamic batching

  动态批次是运动的物件在unity中也可以进行批次渲染,动态批次不需要手动设置,是unity自动进行的,但是这里有诸多陷阱和约束,开发者需要遵守一定的限制条件才能享受动态批次的好处。

  根据unity官方文档描述:

  1) . 动态批次是逐顶点处理的,因此仅对少于900个顶点的mesh有效。如果shader使用了顶点位置,法线和UV那么仅支持低于300顶点的mesh,而如果shader使用了顶点位置,法线、UV0、UV1和切向量,则之多仅支持180顶点。

  2) . 缩放问题

  缩放对于批次是有影响的,这里涉及到一个统一缩放和非统一缩放的概念。统一缩放即为三轴同比例缩放,比如(1,1,1),(2,2,2)(5,5,5)... 非统一缩放即为三轴不同比例缩放,如(1,2,1)(2,1,1)(1,2,3)等等。

  Unity对统一缩放的对象是不进行动态批次的,而对非同一缩放的对象是可以进行动态批次的。这里有点诡异,查阅了一些资料,解释如下:

  对于非同一缩放的物件,unity将其mesh进行了复制,因此即便是从相同物件进行的非同一缩放的两个对象是两份mesh;对统一缩放的对象来说,unity不对mesh进行复制,而是使用同一mesh进行缩放,此时复制mesh来进行批次渲染是不值得的,但是对于非统一缩放的对象,既然已经复制了mesh(不是为了批次,而是其他原因决定复制mesh),那么进行批次是顺带实现的。

(参考 Dynamic Batching and Scale ——unity3d answers  )

  3) . 使用了不同的材质,即便实质上是相同的(比如两个一模一样的材质),也不会进行批次。

  4) . 拥有lightmap的物件含有额外的材质属性,比如lightmap偏移和缩放系数等,所以拥有lightmap的物件不能批次。

  5) . 多通道的shader会妨碍批处理操作,接受实时阴影的物件无法批次。

  

  注意: unity渲染是有顺序的,渲染排序有可能打断动态批次。

  例如:

  场景中有物件ABC,假设AB使用同一材质1,C使用材质2.那么drawcall有可能是2个,也有可能是3个。

  如果顺序为:

    1.渲染A,使用材质1

    2.渲染B,使用材质1

    3.渲染C,使用材质2

  那么drawcall是2个,AB进行了动态批次。

  如果顺序为:

    1.渲染A,使用材质1

    2.渲染C,使用材质2

    3.渲染B,使用材质1

  那么drawcall就是3个,AB的批次被C打断了。

渲染顺序跟什么有关呢?

首先根据物件到摄像机的距离,进行远处物件先渲染近处物件后渲染。相同材质的物件尽量在一层,不要让不同材质的物件进入这一层。如果无法保证这一点,那么还有一种方法:修改shader中渲染队列值。即打开shader 将subshader中的tag{}中queue 修改为小于2500的值。

渲染队列小于等于2500时,unity认为其是不透明的,对于不同材质但z值相同对象,unity不对其进行排序,这样能保证相同材质的多个对象能是一个批次,不同材质的对象如果进入两个相同材质的对象之间,不会打破批次;

渲染队列大于2500时,unity会对不同材质的对象进行排序,此时如果不同材质的对象进入到两个相同材质的对象之间的话,会使相同材质的对象批次被打破。

  批次先写到这,其实很多网上都有,不过有些没深入讲解,也有些没给出解决办法,我就使用每个方案时遇到的困难给出了自己的解决方案。其实批次还有不少研究的地方,之后想到了会继续更新。

  延伸阅读:   

  · Unity -Draw Call Batching

  · Unity Drawcall 优化手记

1.2. 合并图集

  其实合并图集也是利用了Unity的Drawcall batching。将多个纹理进行打包成图集是为了减少材质,这样多个对象共享一个材质,并进而使用同一个纹理和shader,触发unity的动态批次。图集打包工具有很多,Asset store中也可以搜到不少,比如Texture Packer Free 、 DrawCall Optimizer(收费) Mesh Baker Free 等等都可以将贴图打包合并。

  但是合并图集也有缺点,合并贴图时应该注意选择同时出现在屏幕的对象贴图进行合并。如果不能做到这一点,那么合并图集可能起到反作用,即渲染一个对象需要加载过多无用贴图,造成内存占用率升高。我的项目这个方案也是采用之后又弃用的,因为归类同时出现在屏幕的贴图并非易事!

  1.3. 光照和阴影

  实时光照和阴影可能增加Drawcall,带有光源计算的shader材质会因为光照产生多个Drawcall。使用灯光会打断Drawcall batching,尽量使用烘焙灯光贴图等技巧来实现灯光效果。

  延伸阅读:

    · Forward Rendering Path Details

    · Light Troubleshooting and Performance

  1.4. 遮挡剔除、视锥剔除

  这两个Unity提供的剔除方案,出视野之后应剔除对象渲染。

  2. 物理组件

  3. GC 

  GC是unity自动回收内存垃圾的回收器,这虽没有内存泄漏的风险,但是过多的垃圾回收会让CPU高负荷。这里就要避免不必要的内存申请和释放。可以在某个脚本定时清理垃圾,如void update(){if(Time.framecount %5 ==0)System.GC.collect();}

  有以下几点需要注意:

  3.1.  字符串的拼接会产生临时字符串内存,移除代码中的字符串拼接,改用string.format,或stringbuilder,这没测。

  3.2. 用for代替foreach,foreach每次迭代产生24字节垃圾内存。100次循环就是2.4kB.

  3.3. 对象标签tag比较采用comparetag,不要用tag=="mytag"这样。

  3.4. 使用对象池。对象克隆也是调用new,因此对于可以循环利用的对象要采用对象池,比如子弹、特效、宝石等等。

  对象池使用时要注意一点:如果对象上挂了脚本,那么数据需要每次进行初始化或对象回收的时候进行重置,否则下次再利用的对象脚本可能存留上次的数据,那极有可能出bug。

  3.5. 尽量使用struct而非class,因为struct是栈区,class是堆区。

  GC涉及到内存,详细内容会在内存篇展开。

4. 脚本

  脚本中如果在update 函数中调用了Getcomponent等接口,最好将组件缓存。

  在update中的处理如果允许,尽量隔多帧处理一次。

  如:将update() {DoSth();} 修改为:update() { if(Time.framecount %5 == 0) DoSth();}

  其实脚本的优化主要就是针对update中复杂和耗费的逻辑进行优化,其次对于游戏进行中的卡顿,可以修改逻辑,使用预加载方式,将游戏进行中的对象在开始前一次性加载到内存。

 Unity官方给出的一些优化建议:

  1.PC平台的话保持场景中显示的顶点数少于200K~3M,移动设备的话少于10W,一切取决于你的目标GPU与CPU。

  2.如果你用U3D自带的SHADER,在表现不差的情况下选择Mobile或Unlit目录下的。它们更高效。

  3.尽可能共用材质。

  4.将不需要移动的物体设为Static,让引擎可以进行其批处理。

  5.尽可能不用灯光。

  6.动态灯光更加不要了。

  7.尝试用压缩贴图格式,或用16位代替32位。

  8.如果不需要别用雾效(fog)

  9.尝试用OcclusionCulling,在房间过道多遮挡物体多的场景非常有用。若不当反而会增加负担。

  10.用天空盒去“褪去”远处的物体。

  11.shader中用贴图混合的方式去代替多重通道计算。

  12.shader中注意float/half/fixed的使用。

  13.shader中不要用复杂的计算pow,sin,cos,tan,log等。

  14.shader中越少Fragment越好。

  15.注意是否有多余的动画脚本,模型自动导入到U3D会有动画脚本,大量的话会严重影响消耗CPU计算。

  16.注意碰撞体的碰撞层,不必要的碰撞检测请舍去。

时间: 2024-10-09 23:22:40

[Unity优化] Unity CPU性能优化 (难度3 推荐4)的相关文章

Unity技术支持团队性能优化经验分享

https://mp.weixin.qq.com/s?__biz=MzU5MjQ1NTEwOA==&mid=2247490321&idx=1&sn=f9f34407ee5c5d0d1edb478981299108&chksm=fe1e2fbac969a6acee4eddb865d161fe09400c9147794c777ea4087e59f1e5fc3ccca5d1d0dd&scene=21#wechat_redirect 在10月26日的Unity独立游戏日上,

Android App性能优化笔记之一:性能优化是什么及为什么?

By Long Luo 周星驰的电影<功夫>里面借火云邪神之口说出了一句至理名言:“天下武功,唯快不破”. 在移动互联网时代,同样如此,留给一个公司的窗口往往只有很短的时间,如何把握住这个时机,迅速开发出产品,成为至关重要的一环.相对传统互联网时代的PC产品,用户对移动端产品的容忍度更低.而一款移动应用在推出的时候可能只是接近完成的状态,这就需要通过快速的迭代开发来更新产品,不断完善产品来留住用户.同时,通过更新产品也能唤醒一些沉默用户,让一些原本下载了应用但使用次数非常少的用户给该应用多一次

性能优化——Web前端性能优化

核心知识点: 1.排查网站性能瓶颈的手法:分析各个环节的日志,找出异常部分 2.Web前端:网站业务逻辑之前的部分(浏览器.图片服务.CDN) 3.优化手段 1)浏览器优化 (1)减少http请求 a.http请求的开销:建立通信链路.进行数据传输.,同时要启动独立的线程去处理. b.减少http的手段:合并CSS.合并JavaScript,合并图片,这样浏览器就只有一次请求. (2)使用浏览器缓存 a.将静态资源缓存到客户浏览器上, b.更新时是生成新的JavaScript文件,并更改html

【Unity游戏开发】性能优化之在真机上开启DeepProfile与踩坑

一.引子 最近马三入职了新公司,平时除了负责编辑器开发之外还要做一些游戏性能优化方面的工作.在这里首先给大家安利一下Unity官方的性能测试分析工具URP ,这个工具目前是免费,测试的过程中也不需要接入任何SDK,测试完成以后还可以生成一份性能测试报告.当然,相比侑虎科技的UWA GOT工具来讲,目前UPR功能还是比较少的,不过在项目初期完全可以先使用UPR做一些性能优化工作.好了言归正传,我们今天说说如何在真机上开启DeepProfile模式以及一些坑. 二.在真机上开启DeepProfile

Linux性能优化从入门到实战:07 CPU篇:CPU性能优化方法

性能优化方法论 ??动手优化性能之前,需要明确以下三个问题: ??(1)如何评估性能优化的效果? 确定性能的量化指标.测试优化前的性能指标.测试优化后的性能指标. ??量化指标的选择.至少要从应用程序和系统资源这两个维度,分别选择不同的指标:1)应用程序的维度,我们可以用吞吐量和请求延迟来评估应用程序的性能.2)系统资源的维度,我们可以用 CPU 使用率来评估系统的 CPU 使用情况. ??行性能测试注意点:1)避免性能测试工具干扰应用程序的性能:2)避免外部环境的变化影响性能指标的评估. ??

[Unity3D]图形渲染优化、渲染管线优化、图形性能优化

原地址:http://blog.sina.com.cn/s/blog_5b6cb9500101dmh0.html 转载请留下本文原始链接,谢谢.本文会不定期更新维护,最近更新于2013.11.09 主要内容也可以参考: http://docs.unity3d.com/Documentation/Manual/OptimizingGraphicsPerformance.html 最简单的优化建议: 1.PC平台的话保持场景中显示的顶点数少于200K~3M,移动设备的话少于10W,一切取决于你的目标

性能优化指南:性能优化的一般性原则与方法

作为一个程序员,性能优化是常有的事情,不管是桌面应用还是web应用,不管是前端还是后端,不管是单点应用还是分布式系统.本文从以下几个方面来思考这个问题:性能优化的一般性原则,性能优化的层次,性能优化的通用方法.本文不限于任何语言.框架,不过可能会用Python语言来举例. 不过囿于个人经验,可能更多的是从Linux服务端的角度来思考这些问题. 本文地址:http://www.cnblogs.com/xybaby/p/9055734.html 一般性原则 依据数据而不是凭空猜测 这是性能优化的第一

Nginx优化之服务性能优化

优化Nginx服务的worker进程个数 修改nginx主配置文件 worker_processes 1; #指定了Nginx要开启的进程数,结尾数字就是进程个数 Nginx有Master进程和worker进程之分,Master为管理进程,真正接待"顾客"的是worker进程. 优化Nginx进程个数的策略 (1)worker_processes参数大小的设置最好和网站的用户数量相关联, (2)新搭建服务器时,worker进程数最开始的设置可以等于CPU的核数,高流量高并发场合也可以考

前端性能优化:jquery性能优化

jQuery是前端最常用的一个js框架,其实有部分操作也是可以改进的.大部分情况下,封装的后性能是会降低的,如果发现很影响,就可以改为原生的. 另外使用jQuery需要注意一下几点来提高性能: 1不使用each, jQuery 的each循环比原生的for循环性能相差几十倍. 前端性能优化:循环优化 2尽量使用ID,类型,类选择器,避免使用属性选择器 尽量使用ID,类,类型选择器,避免属性选择器.ID,类,类型都有原生的方法,属性选择器需要遍历整个DOM,还需要读取每个节点的属性进行判断,性能很