Unity 的一些优化总结 (难度3 推荐4)
原文地址:
http://www.jianshu.com/p/3acee1101fe4
从别的地方看到一些资源使用方面的文章,从中抽取一些细节在这里总结一下:
Draw Call数量、Triangle数量 和 可见蒙皮网格数量
一般来说,Draw Call 数量、Triangle 数量 和 可见蒙皮网格数量 的推荐值需根据平台的不同而不同。对于 Mobile 低端移动设备来说,建议 Draw Call 数量的主要范围在 [0,200] 区间内,Triangle 数量保持在 10万 以下,可见蒙皮网格数量保持在 50 以下。
Contacts 数量、Rigidbody 数量 和 碰撞体数量
Contacts 数量为碰撞对(Contact Pair)数量。所谓“碰撞对”,是指当前空间中发生碰撞的物体之间的接触点,每个 “碰撞对” 均连接两个相互产生碰撞的物体。一般来说,Contacts 数量越大,则表明碰撞物体的数量越多,即物理系统的CPU开销越大。
Rigidibody数量 和 碰撞体数量, 其标准值需因运行平台的不同而不同。对于Mobile移动设备来说,建议 Rigidibody数量 控制在 50 以下,碰撞体数量(静态碰撞体和动态碰撞体) 控制在 100 以下。
贴图资源
在纹理资源的优化方面,建议尽可能降低其内存上的占用,其主要影响因素包括纹理分辨率、纹理格式、Mipmap等。对于纹理格式,则尽可能采用适合于发布平台的硬件支持纹理格式,比如对于Android平台,建议使用ETC1格式,对于iOS平台,建议使用PVRTC格式,对于PC,建议使用DXT格式等等。
注意:在Android平台上,ETC1格式不支持半透明纹理,所以半透明纹理可以直接转换成RGBA16格式进行渲染。当然,更为出色的办法是,将半透明纹理拆分成两张纹理,记录RGB通道信息的RGB24格式纹理和记录Alpha通道信息的Alpha8格式纹理,然后再分别转成ETC1格式进行保存。这种方法既可以最大限度地保证渲染质量,又可以极大降低纹理内存的占用。
纹理从RGBA32转换成RGBA16、ETC1或PVRTC格式时,均会带来一定程度的质量损失,甚至出现难以接受的 “色阶” 效果。究其原因,是因为纹理图像中的过渡色所致。因此,建议您在显示效果和性能效率方面进行权衡,尽可能降低过渡色的跨度范围“多余” 数据的危害还体现在网格拼合上。Unity 引擎的 Draw Call Batching 功能或您自己调用的 StaticBatchingUtility API 均可以将场景中的网格数据进行拼合,从而形成一个或几个较大的 VBO(Combined Mesh)。这种做法是渲染优化时的常见方法,主要用于降低渲染模块的Draw Call数量,从而提升渲染效率。这本身是一个非常棒的功能,但您需要注意的是,网格合并时一定要保证网格数据结构的统一性,否则会大大增加拼合网格(Combined Mesh)的数据量!举个例子,当拼合100个网格模型时,如果99个模型网格数据中仅含有Vertex、UV等数据,而仅有1个模型网格数据中含有Vertex、UV、Normal、Color和Tangent数据,则拼合后,拼合网格中的所有顶点均会添加上 Normal、Color 和 Tangent 数据,大量的 “多余” 数据也就由此产生了...
总内存和使用内存
Reserved Total 和 Used Total 为Unity引擎在内存方面的总体分配量和总体使用量。 一般来说,引擎在分配内存时并不是向操作系统 “即拿即用”,而是首先获取一定量的连续内存,然后供自己内部使用,待空余内存不够时,引擎才会向系统再次申请一定量的连续内存进行使用。Reserved Total 的内存占用量略大于 Used Total, 且两者走势基本一致。
注意:对于绝大多数平台而言,Reseved Total内存 = Reserved Unity内存 + GFX内存 + FMOD内存 + Mono内存
通过针对大量项目的深度分析,我们发现导致 Reserved Unity 内存分配较大的原因主要有以下几种:
- WebStream内存占用:WebStream为项目通过特定API(WWW、CreateFormMemory等)加载AssetBundle文件所开辟的较大块内存。主要用于存放AssetBundle的原始数据和解压后数据。
- 序列化信息内存占用:Unity引擎的序列化信息种类繁多,其中最为常见且内存占用较大的为SerializedFile。该序列化信息的内存分配主要为项目通过特定API(WWW.LoadFromCacheOrDownload、CreateFromFile等)加载AssetBundle文件所致。
- 资源内存占用:主要包括Mesh、AnimationClip、RenderTexture等资源。在默认情况下,当FBX模型导入时,其 “Read/Write Enable” 选项是默认开启的,即其Mesh网格数据会在 Reserved Unity 中保留一份,便于项目在运行时对Mesh数据进行实时的编辑和修改。同时,如果研发团队同样开启了纹理资源的 “Read/Write Enable” 选项(默认情况下为关闭),则纹理资源同样会在 Reserved Unity 中保留一份,进而造成其更大的内存占用。
GFX内存 为底层显卡驱动所反馈的内存分配量,该内存分配由底层显卡驱动所控制。一般来说,该部分内存占用主要由渲染相关的资源量所决定,包括纹理资源、Mesh资源、Shader资源以及解析这些资源的相关库所分配的内存等。
托管堆内存 表示项目运行时代码分配的托管堆内存分配量。对于使用Mono进行代码编译的项目,其托管堆内存主要由Mono分配和管理;对于使用IL2CPP进行代码编译的项目,其托管堆内存主要由Unity自身分配和管理。目前,除iOS平台外,其他平台的绝大多数项目还在使用Mono来进行代码编译。
对于Mono堆内存来说,由于Mono自身的限制,其堆内存分配是 “只升不降” 的,即内存一旦分配给Mono,不论以后该内存是否继续被使用,都不会再归还给系统。因此,建议您对于代码的堆内存分配进行严格的控制,避免不必要的Mono堆内存分配。
Instantiate实例化操作
一般情况下,我们不建议项目在运行过程中频繁地调用 Instantiate 和 Destroy 来创建和销毁一个GameObject。这种做法不仅可能会造成频繁且较高的CPU占用,更重要的是,它将造成大量的内存碎片,从而造成越来越多的堆内存分配,进而加速系统垃圾回收操作(Garbage Collection)的到来