UWA

每天一点UWA:加载模块深度解析

Posted by 蔡华的博客 on August 27, 2017

专题

  • 因为UWA上会对一些技术进行系列的专题讲解,而且文章并不连续,如果按照每周的形式就会割裂这些内容,因此针对这样的情况统一写成专题形式。

概述

  • 载模块中最为耗时的性能开销可以归结为以下几类:资源加载、资源卸载、Object的实例化和代码的序列化等。
  • 资源加载是加载模块中最为耗时的部分,其CPU开销在Unity引擎中主要体现在Loading.UpdatePreloading和Loading.ReadObject两项中。
  • Loading.UpdatePreloading,这一项仅在调用类似LoadLevel(Async)的接口处出现,主要负责卸载当前场景的资源,并且加载下一场景中的相关资源和序列化信息等。下一场景中,自身所拥有的GameObject和资源越多,其加载开销越大。
  • Loading.ReadObject,这一项记录的则是资源加载时的真正资源读取性能开销,基本上引擎的主流资源(纹理资源、网格资源、动画片段等等)读取均是通过该项来进行体现。可以说,这一项很大程度上决定了项目场景的切换效率。

纹理篇

只给出结论

  • 同一的格式下(ETC1(Android)和PVRTC(iOS)、且关闭Mipmap功能),图片分辨率越大加载越慢,但是在Android上中(红米NOTE2)高(S6)端机在加载效率上差不多。
  • 同一分辨率(1024x1024)上 -不同格式(对于Android平台,使用ETC1、ETC2、RGBA16和RGBA32四种格式,对于iOS平台,使用PVRTC 4BPP、RGBA16和RGBA32三种格式)
    • 所占的内容首先是有差异的(三组纹理的内存占用分别为1MB、1MB、4MB 和 8MB(Android平台)/1MB、4MB 和 8MB(iOS平台))。
    • 加载效率上,ETC1、ETC2、RGBA16基本一致;PVRTC 4BPP、RGBA16基本一致。
  • 开启Mipmap功能会导致资源加载更为耗时,且设备性能越差,其加载效率影响越大。打破了Android上同一分辨率下ETC1、ETC2、RGBA16基本一致的情况,需要慎用。
  • PS:压缩后的文件大小,和解压缩的效率与纹理的内容相关,这是因为图片的压缩本质是用矩阵对图片数据进行变换,最终得到一个较小的数据。解压缩也是一样,数据的内容影响了反向计算的效率。

UWA给出的建议

  • 严格控制RGBA32和ARGB32纹理的使用,在保证视觉效果的前提下,尽可能采用“够用就好”的原则,降低纹理资源的分辨率,以及使用硬件支持的纹理格式。
  • 在硬件格式(ETC、PVRTC)无法满足视觉效果时,RGBA16格式是一种较为理想的折中选择,既可以增加视觉效果,又可以保持较低的加载耗时。
  • 严格检查纹理资源的Mipmap功能,特别注意UI纹理的Mipmap是否开启。在UWA测评过的项目中,有不少项目的UI纹理均开启了Mipmap功能,不仅造成了内存占用上的浪费,同时也增加了不小的加载时间。
  • ETC2对于支持OpenGL ES3.0的Android移动设备来说,是一个很好的处理半透明的纹理格式。但是,如果你的游戏需要在大量OpenGL ES2.0的设备上进行运行,那么我们不建议使用ETC2格式纹理。因为不仅会造成大量的内存占用(ETC2转成RGBA32),同时也增加一定的加载时间。下图为测试2中所用的测试纹理在三星S3和S4设备上加载性能表现。可以看出,在OpenGL ES2.0设备上,ETC2格式纹理的加载要明显高于ETC1格式,且略高于RGBA16格式纹理。因此,建议研发团队在项目中谨慎使用ETC2格式纹理。

网格篇

  • 总结一下:网格的加载效率的影响因素在于网格面数、顶点信息(tangent、color)数和read/write是否开启。
  • 结论很简单,面数多、信息多、开启会造成内存的变大和加载的变慢。

建议

  • 在保证视觉效果的前提下,尽可能采用“够用就好”的原则,即降低网格资源的顶点数量和面片数量;
  • 研发团队对于顶点属性的使用需谨慎处理。通过以上分析可以看出,顶点属性越多,则内存占用越高,加载时间越长;
  • 如果在项目运行过程中对网格资源数据不进行读写操作(比如Morphing动画等),那么建议将Read/Write功能关闭,既可以提升加载效率,又可以大幅度降低内存占用。

Shader篇

  • Shader资源的效率加载瓶颈并不在其自身大小的加载上,而是在Shader内容的解析上。
  • 总结一下:,Mobile-Bumped Diffuse、Mobile-Diffuse、Mobile-VertexLit和Mobile-Particles Additive加载耗时依次减少,对于的normal版本也是一样。

影响shader解析的因素

  • 一般情况下,Shader加载的CPU耗时与其Keyword数量有关,Keyword数量越多,则加载开销也越大。(具体数据看原文)
  • Shader的Keyword数量是会随着场景设置的不同而变化的。在Unity 5.x中,Unity默认会根据场景设置、Shader Pass等来调整Shader的Keyword,比如如果存在Lightmap的使用,则会默认将对应的Keyword打开,而对于没有使用Fog的项目,则会直接将相关Keyword关闭。

如何提高解析速度

  • 就是减少keyword数量。
  • 首先可以使用skip variant来减少 image
  • 其次去掉Fallback选项,因为文中认为目前不支持Mobile/Diffuse和Mobile/Bumped Diffuse的设备已经相当少,没有必要做这层保险了。

避免重复解析

  • 因为一个游戏内用到的shader的数量不会太多,如果shader打包到场景或者模型的AssetBundle中,那么在资源加载卸载的过程中会出现同一个shader多次加载解析的问题。因此建议shader单独打包。
  • 注意:对于Unity4.x版本,Shader的AssetBundle加载后只需LoadAll即可完成所有Shader的加载和解析,但对于Unity5.x版本,除执行LoadAllAssets操作外,还需要进行Shader.WarmupAllShaders操作,因为在Unity5.x版本中,Shader的解析和CreateGPUProgram操作是分离的。
  • 对于Unity 5.x版本且正在使用Resources.Load来加载资源的研发团队,可以尝试使用ShaderVariantCollection来对Shader进行Preload,同样也可以达到避免相同Shader重复加载的效果。

  • 注意:对于Unity5.x版本,如果可以通过AssetBundle来加载和解析Shader,则不建议通过ShaderVariantCollection来处理Shader的加载。

总结

  • 综上所述,shader的处理就是减少keyword、对于简单的渲染去掉Fallback、尽量对shader进行单独打包。