Release notes
2021.2.5f1c303(团结backport)
异常处理优化
游戏C#代码、Library、系统CoreLib、Il2Cpp Runtime,其中都会包含比较多的异常相关代码,即使没有真的抛出异常,仅仅try-catch的存在,也会造成一定的性能消耗。 但实际其中有些部分是多数情况不需要的,可以尝试关闭。
因此在PlayerSettings里新增“Enable Exceptions Only For User Codes”选项,细化了异常的范围:
- 当启用异常时(即“Enable Exceptions”不为“None”):
勾选“Enable Exceptions Only For User Codes”,则仅对用户的C#代码和Library开启异常支持,其他部分(包括Il2Cpp Runtime、系统CoreLib、Unity Builtin Library)关闭异常支持;
取消勾选“Enable Exceptions Only For User Codes”,则对所有代码均开启异常支持;
- 当全局关闭异常时(即“Enable Exceptions”为“None”):
“Enable Exceptions Only For User Codes”选项忽略,且UI不展示,对所有代码关闭异常支持。
绘制优化
- 减少不必要的glScissor,glClearColor等API调用
- 优化glInvalidateFramebuffer调用顺序,减少不必要的Load/Store操作
- 允许禁用DefaultFBO的DepthStencil Component
- 开启此项优化后,不再为DefaultFBO创建DepthStencil Component,减少显存占用和glClear指令
- 分辨率1920 x 1200的Framebuffer可节省约8.79MB
- 如果明确要使用DefaultFBO的DepthStencil Component,或场景中有可能由Depth导致的错误,可关闭此优化项
SIMD支持
- PlayerSettings里勾选SIMD时,将打开引擎的SIMD支持
- 如果同时开启了GPU Skinning(transform feedback),则SIMD将失效
案例
Autostreaming 首场景加载优化
- 优化SceneStreaming首场景的下载,并行下载场景和其依赖的共享AB
- 优化SceneStreaming首场景的加载,支持在Draw Splash期间执行场景下载并加载,避免Splash结束后黑屏
EndlessRunner项目开启Splash测试,首次进入游戏首画面显示时间提前约0.4s;重复进入游戏,首画面显示时间提前约0.1s。
问题修复
- 修复打包AB时,弹框提示"Moving file failed"的问题。 如果问题已经存在,需要先按照FAQ.15中的办法解决,或重新reimport整个工程。
2021.2.5f1c302(团结backport)
优化运行时内存
优化IL2CPP运行时内存
il2cpp的运行时,作为脚本代码运行的虚拟机,它本身也会较多的内存消耗。这些内存主要用来保存脚本代码运行时元数据来加速脚本运行。 脚本元数据大小和Unity工程的代码量是正相关的,一个直观反映就是global-metadata.dat文件的大小。 以一个14MB大的global-metadata.dat文件为例,在游戏运行时,il2cpp运行时的内存消耗就达到了64MB,优化后内存降低到33MB。
在之前的实现中,il2cpp在使用到某个类型时,会初始化这个类型完整的元数据,包括它的所有函数、接口、事件、属性、虚函数表等, 并且一旦加载后就不会再释放。 但分析发现,脚本代码实际运行时,通常只会用到很少的一部分元数据(反射、或者虚函数调用时访问)。 例如:一个数组类型有155个方法,25个虚函数,实现了6个接口,但实际运行时只会用到其中的很小一部分,存在冗余加载的情况。 因此延迟加载这些元数据,等到真正需要某个元数据项目时才去初始化这份数据可以显著降低内存占用。
提高DynamicVBO对象池复用
优化了DynamicVBO缓存池的复用策略,当上层模块使用了较多Size大小不一的VBO时(例如粒子系统使用较大的Mesh并且设置随机LifeTime),可以显著提升缓存复用率,降低GPU内存占用。
该功能可以在ProjectSettings->Graphics页面开启,默认关闭。
在测试工程上,粒子系统的显存占有从59MB降低到了38MB。
绘制优化
GPU Skinning(transform feedback)
Unity当前的gpu skinning都是基于compute shader实现的,但WebGL 1 和 2 都不支持compute shader。 目前wasm只有单线程,cpu性能也只有原生App的三分之一,并且不支持simd 这使得在webgl平台上使用cpu skinning,性能开销非常大。 用户为了使用gpu skinning, 需要自行修改vertex shader 或者使用一些第三方插件 增大了适配webgl平台的成本。 因此我们实现了基于transform feedback的gpu skinning 只需要选择webgl2.0,打开 compute skinning选项即可使用。
在测试场景中,对比gpu skinning之前的性能,每帧cpu耗时从67ms降低为41ms。
Shader优化
Unity WebG平台在设计之初针对的是PC浏览器,因此shader并没有针对微信小游戏进行优化。 在保证渲染质量的前提下,我们通过以下优化手段来提高渲染GPU性能:
优化Shader Compiler,将non-const global变量移到main函数中
max visible lights值,32可降为16*
修改Immediate Const Buffer转换过程,声明成const并直接附初始值
在测试项目上,以上优化均能达到fps翻倍的收益。 这些功能的开关可在ProjectSettings->Player->Other页面选择控制,默认关闭。
*影响URP及ShaderGraph中的预设值MAX_VISIBLE_LIGHTS。 勾选该选项时,建议在Quality Setting页面只勾选单一quality level以,并关闭additional light shadow,否则max visible lights可能没有效果。
异常处理优化
游戏C#代码、Library、系统CoreLib、Il2Cpp Runtime,其中都会包含比较多的异常相关代码,最终都会编译成Wasm,以Wasm的方式来进行处理,具体有两种方式:JS方式和Wasm原生方式。
JS方式下,try块中的所有函数调用,都会先跳转到JS,记录堆栈,再跳转回Wasm继续执行,无论是否实际有异常抛出都会这样,性能开销比较大。不过优点是浏览器支持比较好。
Unity之前使用的就是JS方式。另外Unity之前“Enable Exceptions”设置为“None”时,并没有移除全部异常处理指令,仍然会存在JS-Wasm跳转。
Wasm原生方式下,异常会在Wasm内部直接处理,不会有和JS的反复跳转,性能较好,缺点是一些旧版本的浏览器内核没有支持。
针对这方面问题,进行了如下优化:
- PlayerSettings里“Enable Exceptions”设置为“None”时,彻底移除Wasm代码中的异常处理相关指令
- PlayerSettings里“Enable Exceptions”中增加“Wasm Exception”选项,支持Wasm原生异常处理(需要关注浏览器的支持情况)
- 精简Il2Cpp生成代码中的异常相关部分
资产轻量化
unity_default_resources文件从3.5MB减少到400KB,因此微信小游戏首包数据文件也会减少3MB,对启动时间和内存都会有所帮助。 实现方式包括:
- 缩小splash-cube纹理尺寸(2048 -> 1024)
- 调整WebGL平台默认纹理的压缩格式(rgba -> astc)
- 移除WebGL 平台不支持的compute shader
对默认资源使用没有影响。
Memory Profiler增强
增加MemLabel统计
勾选Gather MemLabels选项后点Take Sample,结果中会增加MemLabel分类,按内存用量排序展示所有MemLabel
MemLabel总量对应Native Total Used。
Il2cpp运行时内存统计
在Project Settings - Memory Settings - Players 增加开关“Il2Cpp Memory Allocator”,允许将Il2Cpp Runtime Allocator接入引擎MemoryManager中,从而在unity Profiler - Memory 模块能够统计到il2cpp的内存使用量。 对应的MemLabel是StriptingNativeRuntime
由于对性能有影响,因此目前建议只在profile内存时勾选。
增加AssetBundle内存统计
由于WebGL平台没有真实的文件系统,加载AssetBundle时往往会带来AB文件大小2-3倍的开销。因此我们添加了AssetBundle内存统计,便于查看各个AB内存占用情况,用于分析AB加载逻辑是否合理。
Simple View
Object Stats下增加以下统计:
- AssetBundles: 内存中AssetBundle Object对象本身占用内存
- AssetBundle Memory Storages : AssetBundle文件缓存(解压后)在C++ native heap中占用的内存
- AssetBundle Disk Storage : Emscripten JS文件系统中AssetBundle文件占用内存
Detail View
可以在Storage 和 Not Saved -> AssetBundle 查看各个AB的存储位置,内存占用大小
ProfilerRecorder C#接口
新增ProfilerRecorderC#接口,可以从代码中获取以下6项AB内存使用情况:
- AssetBundle Count
- AssetBundle Bytes(仅development版本可用)
- AssetBundle Memory Storage Count
- AssetBundle Memory Storage Bytes
- AssetBundle Disk Storage Count
- AssetBundle Disk Storage Bytes
实用工具
以下功能需要在PackManager中安装com.unity.instantgame package 0.3.1版本及以上才可以使用。
AutoStreaming按需加载
一个游戏中,资源占据了游戏的大部分空间,并且在游戏启动时仅有少部分资源是必需的。因此可以将大部分资源从游戏首包中抽离,并在打包小游戏时加入streaming资源的配置文件。游戏启动后根据配置文件,在需要用到streaming的资源时自动触发下载和加载,从而达到减小启动包体的目的。
得益于AutoStreaming基于引擎底层实现对游戏资源按需加载能力,原生APP游戏工程可以在较少的代码修改的情况下完成在微信小游戏平台的运行,减少游戏转化的工作量,这一优势对于没有细致分AB包的游戏尤为明显。
在加载AssetBundle或者场景时,通常并不会使用到其中所有的资源。AutoStreaming自动按需加载的逻辑可以避免将运行过程中未使用到的资源加载到内存中, 因此对减少内存占用也会有所帮助。
另外通过AutoStreaming Editor工具能够发现游戏使用到的大部分资源,以及每个资源对应的内存占用大小,从而可以方便的查看资源设置是否需要调整优化,如是否开启压缩,分辨率是否过高等等。
模型默认材质替换工具
Unity导入模型时,默认材质会引用到Standard Shader, 而Standard Shader, 无法主动打包进AssetBundle中。 因此当各个模型文件(如fbx文件)分开打包进不同的AB时, 就会产生大量Standard Shader 重复。这些重复的Shader可以通过运行时连接Unity Memory Profiler抓取内存详细数据,或使用Asset Studio打开AB文件看到。
通过将默认材质指定为一个用户创建的材质,并将其一起打包到AB中,可以利用AB依赖机制避免打包FBX时Standard shader的重复。
在Windows -> AutoStreaming -> Utilities页面,设置Model Importing功能下的Defaut material属性,从Assets目录下选择创建的默认材质,如ModelImportDefault。 然后点击Reimport按钮重新导入所有模型资源。
该步骤仅需操作一次,后续添加模型资源自动生效。Reimport操作,模型资源过多时可能需要很长时间。
AB依赖分析工具
AssetBundle依赖分析工具可用于查看游戏AB内的对象,资源和依赖关系,用于帮助优化AB打包时的资源组织结构,从而减少游戏启动时,需要使用到的AB总量。 通过Windows -> AutoStreaming页面,在左上角下拉菜单中选择AssetBundle Tools可以打开以下页面。