1)在渲染大面积草地时,如何降低消耗
2)使用LoadFromMemory导致内存翻倍问题
3)对Android x86平台的支持问题
4)OnGUI的堆内存分配问题
Rendering
Q:请问下大家,渲染大面积草地时,如何降低消耗呢?
A1:回答如下:
1.使用DrawMeshInstance;
2.上面这个API是不会进行视距剔除、视锥体剔除和遮挡剔除的。下面有两种方案:
a.将草地按区域分组,用每组的中心点计算视距,依据距离切换网格LOD或剔除;还能用向量点乘简单剔除在相机后方的草地(注意临界问题)。
b.借助CullingGroup。
CullingGroup.onStateChanged事件绑定,通过事件触发调整传入;DrawMeshInstanced的Matrix顺序和渲染数量(但是DrawMeshInstanced只能指定渲染前几个Matrix);
通过cullingGroup.SetBoundingSpheres实现视锥体剔除和遮挡剔除;
通过cullingGroup.SetBoundingDistances实现视距剔除和LOD。
这个方案最好也进行区域分组,不然CullingGroup的事件监听占用会比较高,在中端机上4000个监听会占约2ms的大小。以后如果有对比两种方案的性能,我再进行补充。
附:
AssetBundle
Q:UWA的几篇文章中都有说到尽量不要用AssetBundle.LoadFromMemroy接口,因为这个接口会使得内存的占用翻倍,但是我用Unity 2017.4.5f1进行测试后,发现AssetBundle.LoadFromMemroy和AssetBundle.LoadFromFile几乎是没有差别的,因为文章中也没有关于体积具体的版本信息。所以我想问是之前的Unity 4.x或者Unity 5.x版本才有这个问题?还是说测试用例是有要求的呢?
另外,我是在PC上进行测试的,打包出exe,直接用任务管理器查看内存占用情况,发现并没有区别。
A1:应该是在C++里面直接申请一块内存用来解压,Mono占用不会变但是PSS会增大,内存峰值也会变高,使用adb shell dumpsys meminfo packageName来查看PSS。
如果目标平台是PC,那就说明内存策略可能是分配后立即归还给操作系统了。如果PC不是目标平台,那么看内存就没有很大的意义。
感谢张迪@UWA问答社区提供了回答A2:LoadFromMemroy输入的byte数组用的是Mono堆内存,哪怕这个内存有释放,但Mono堆内存总量是只升不降的,在加载资源的过程中,一旦Mono触发GC后仍内存不够,很可能需要申请新的Mono内存,这会导致Mono内存持续升高。
而且用LoadFromMemroy没有必要性,用这个接口哪怕内存不是问题,也会在加载资源时明显比LoadFromFile慢不少,而且真的想要逆向AssetBundle资源还是很容易的。退一步说,非要加密建议使用LoadFromStream,然后自己去实现Stream解密。
感谢loy_liu@UWA问答社区提供了回答A3: 我之前的测试有问题,现在更正一下:LoadFromMemroy即使在PC平台也不如LoadFromFile接口。
经测试,PC上LoadFromMemroy接口内存的占用大概会高1/5左右,加载时间比LoadFromFile接口慢1/5左右,而且如loy_liu所说,LoadFromMemroy接口需要先读取byte数组,会产生Mono内存的分配问题,而LoadFromFile不会。在Android平台上,内存的对比非常夸张,我这边测试的数据翻了接近3倍。
所以尽量不要使用LoadFromMemroy接口,我没有去测试LoadFromStream接口的性能和内存,但据说和LoadFromFile差不多。
感谢题主fdy@UWA问答社区提供了回答A4: Lzma的资源包LoadFromMemory会导致资源本身翻倍。L4z资源包类似于管理器,本身占用内存很小,翻倍也没关系,而内存流本身也是用完就能扔。
Build
Q:目前项目发布后大于100MB,检查发现是libil2cpp.so较大,大概大于60MB,而且同时支持了ARMv7、ARM64、x86,相当于x3的大小。所以想问一问大家,目前x86架构的机型大概占比多少?需要继续支持吗?
Memory
Q:在PC上运行游戏,用Profile工具检测的时候,发现GUIUtility.BeginGUI内存泄漏,每次调用产生0.8KB的内存消耗,每帧调用4次,就是3.2KB。请问这是什么原因引起的呢?
A:OnGUI必然有GC Alloc,即使是空函数。你可以拿宏括起来,用来做Profiling的包不要编译进去。