技术分享连载(七十二)

技术分享连载(七十二)

本期我们聚集了这些话题:深度剖析Occlusion Culling、频繁开启界面对Mono堆内存的影响、如何获取详细的GPU信息...


我们将从日常技术交流中精选若干个开发相关的问题,建议阅读时间15分钟,认真读完必有收获。如果您有任何独到的见解或者发现也欢迎联系我们,一起探讨。

UWA QQ群:793972859
UWA 问答社区:answer.uwa4d.com


渲染

Q1:我们是手游项目,想对场景中的静态物件做一个遮挡剔除,不知道Unity自带的Occlusion Culling对性能优化的提升有多少呢?我们用Unity官方例子测试,启用Occlusion Culling后发现DrawCall、Triangle几乎没有变化。另外也想获知,Occlusion Culling的数据能否像NavMeshData那样可以动态加载呢?谢谢!

Occlusion Culling在使用后具体能提升多少,这个其实是没有明确数值的,甚至可能不升反降!这个只能题主在自己的项目中进行尝试。

一般来说,Occlusion Culling功能特别适合第一人称或第三人称跟随的平视角相机(比如传统意义上的MMO等类似游戏),且适用于在场景中存在大量的遮挡情况。因此,在城市街道漫游、峡谷漫游等特定场景中,比较推荐开启Occlusion Culling功能。

但是Occlusion Culling功能本身存在一定的性能开销,因为需要每帧均遍历烘焙的Cell来明确哪些物体需要或不需要渲染,所以场景中Cell越细,那么其查询开销也就越大。因此,如果场景中本身没有大量的遮挡关系,那么开启Occlusion Culling功能后,其节省下来的渲染耗时可能抵不上其Cell查询耗时来的大,这样就得不偿失了。

下图为我们优化过的一个案例,项目中开启了Occlusion Culling功能,由于场景过于复杂且Cell也烘焙较细,所以可以看到其Culling中的WaitingForJob耗时达到了33%。
请输入图片描述
因此,我们尝试将项目中的Occlusion Culling在运行时关闭,并在Profiler中直接查看关闭前后的对比结果(下图),可以看到,Rendering中的Draw Call、Triangles并没有变化,但其Culling开销却大幅下降。这说明,在该项目中,Occlusion Culling虽然开启但并没有起到效果,反而大幅增加了CPU端的Culling耗时。
请输入图片描述
综上所述,Occlusion Culling功能仅适合于特定项目、特定场景来进行使用,并且建议在使用前后,项目组应该多加对比,反复查看其加入后是否确实带来性能提升。

最后,Occlusion Culling的Data目前并不能动态加载,只能随场景来进行加载。因此,可以尝试创造一个拥有Occlusion Culling Data的“空场景”,然后通过LoadLevelAdditive方式来进行加载,从而来达到“动态”加载的效果。

此问答来自于UWA 问答社区:
https://answer.uwa4d.com/question/596c93da2832ee102d2ac18e
如您对该问题仍有疑问,可以转至社区进行进一步交流。


内存管理

Q2:通过Profiler.GetMonoHeapSize()获取到的堆内存,在操作UI的时候,打开某个节点比较多的UI界面,根据上面接口取到的堆内存会突然涨6~8MB,但是Profiler取不到这个数据,该怎么办呢?我不是固定打开某个界面,而是随机出现在某个界面上,这样的问题该如何入手呢?

Mono总体堆内存一次性涨6-8MB是比较正常的。Mono内存并不是随用随分配的,而是当其发现不够用时,一次性从系统中获取一段连续的内存。在游戏运行一段时间后,这个值一般是8MB。所以,题主遇到的情况很有可能是在连续操作UI界面时,Mono发现堆内存不够用了,于是就有了题主观察到的内存分配。

此问答来自于UWA 问答社区:
https://answer.uwa4d.com/question/596f3a85e7b53b286095384d
如您对该问题仍有疑问,可以转至社区进行进一步交流。


动画

Q3:我写了一个顶点动画的Shader,对物体顶点位置进行了修改,使其能够跟随相机移动,并总是出现在相机前面。当游戏运行后,开始时物体能够正常显示。相机在场景中移动一段距离后,物体便不再显示,但相机换一下朝向,物体又能显示。目前排除了面剔除的原因,在Shader中已经关闭了Cull(Cull Off),请问还可能是什么其他原因呢?

消失的原因应当是Unity的Frustum Culling引起。因为顶点动画是在shader中修改的顶点位置,而Frustum Culling是根据顶点修改之前的Mesh的bounds进行的,因此随着相机移动,Mesh实际已经出了Frustum的范围。最简单的解决方法是直接将Mesh的bounds设置足够大,让Unity始终不对其进行Culling:
mesh.bounds = new UnityEngine.Bounds(transform.position, new Vector3(float.MaxValue, float.MaxValue,
float.MaxValue));

此问答来自于UWA 问答社区:
https://answer.uwa4d.com/question/596c8cb52832ee102d2ac18c
如您对该问题仍有疑问,可以转至社区进行进一步交流。


GPU

Q4:我使用SystemInfo.graphicsDeviceName获取的GPU信息不太详细。比如ARM系列的Mali,T880 MP2与T880 MP12相差甚远,但是只能获取到T880,无法获取MP的信息。各位是否有办法?

对于获取GPU名称信息,在Android的Java层中可以利用API:GLES20.glGetString(GLES20.GLRENDERER)获取显示设备名。可以尝试在Unity中利用AndroidJavaClass(https://docs.unity3d.com/ScriptReference/AndroidJavaClass.html)来调用该API,尝试获取更详细的型号。

如果获取GPU名称的目的是判断GPU性能,可以看下SystemInfo中其他graphicsDevice相关的GPU性能信息是否够用。

此问答来自于UWA 问答社区:
https://answer.uwa4d.com/question/59688ccda5c647ad018763cf
如您对该问题仍有疑问,可以转至社区进行进一步交流。


物理模块

Q5:两个挂了Collider的物体,一个物体挂了Rigidbody,去碰撞另一物体,为什么每次OnCollisionEnter这个函数在实际还没有碰撞的时候就调用了呢?如下图,这时OnCollsionEnter已经被调用,但从图上可以看到两物体实际还没有接触,这个函数调用之后才会真正碰撞在一起。
请输入图片描述

物理系统在FixedUpdate中触发,包括OnCollisionEnter。而FixedUpdate早于Frame Rendering,因此碰撞实际已经发生,只是画面还没更新。其实在运行时这点时间差几乎应该可以忽略。

此问答来自于UWA 问答社区,感谢 Saber的回答。
https://answer.uwa4d.com/question/5970192f8cbe6c92774e0507
如您对该问题仍有疑问,可以转至社区进行进一步交流。


今天的分享就到这里。当然,生有涯而知无涯。在漫漫的开发周期中,您看到的这些问题也许都只是冰山一角,我们早已在UWA问答网站(answer.uwa4d.com)上准备了更多的技术话题等你一起来探索和分享。欢迎热爱进步的你加入,也许你的方法恰能解别人的燃眉之急;而他山之“石”,也能攻你之“玉”。
官网:www.uwa4d.com
官方技术博客:blog.uwa4d.com
官方问答社区:answer.uwa4d.com
官方技术QQ群:793972859(仅限技术交流)