这个世界上的很多问题,其本质上都是数学问题,性能优化就是如此。
在上个月刚刚结束的UWA DAY 2019中,我们提出了UWA Benchmark。这是一个通过大量测试数据,逐步迭代分析出性能指标与性能耗时影响关系的基准测试模型。将高中低端不同机型上的大量随机性实验测试,作为样本基础,加以数据处理与分析,得到各个模块中重要指标的定量和定性关系。
在Benchmark的第一阶段,我们研究的是Unity引擎中单一模块单一变量的影响程度分析。UWA在做游戏项目的技术优化时,常会听到以下疑问:
- DrawCall和Triangle哪个对渲染耗时的影响更大,如何权衡?
- 我们游戏的目标机型是xxx,想同屏跑50个角色,角色骨骼数面数定在多少比较合适?
- 如何确定高端机/低端机的画面分级标准?
这些问题其实都是无法给出标准答案的复杂问题。由于设备配置的变化、游戏渲染效果的复杂度,以及在各种引擎设置的区别之下,是不可能有一个绝对正确的量化答案的。
之前,业内大部分项目的做法是通过真机测试的耗时帧率,反推性能指标的范围。如果耗时高了,就降一降DrawCall、减一减粒子、后处理。这种方式多用于研发中期,在主要角色、场景资源、渲染模型确定之后进行,但这种方式相对被动。而在游戏前期,对技术进行选型及重要指标进行制定时大多靠技术人员的“经验值”。
UWA在进行了上百场深度优化及数千个项目的性能测评之后,发现这些性能指标之间其实是有规律可循的。因此,我们希望从数据的角度总结归纳出这些规律,把“经验值”变成“计算结果”。在这个过程中,大部分结论是符合我们过往的认知和经验的,但也有少部分数据和分析结果是超出我们的固有印象,对性能体系做到了一个很好的补充和解释。
本篇文章中,将对目前UWA Benchmark中关于渲染模块的研究结果进行讲解。首先在测试机型的选取方面,我们选择了3款安卓机,相关配置如下:
选择的Unity版本为2017.4,这也是近半年在UWA的Unity版本使用情况统计中,使用率最高的一个Unity版本。
在渲染模块,我们关心的两个重要性能开销是不透明渲染耗时和半透明渲染耗时。也就是Render.OpaqueGeometry和Render.TransparentGeometry。而在性能指标变量方面主要修改的是三个重要变量:DrawCall数、TotalTriangle数、参与渲染的Material数。并且在测试用例中控制三个变量的大致范围如下:
我们通过渲染简单Shader的简单Mesh面片这种最简单的渲染例子,尽量排除了其他因素的干扰。同时为了使渲染开销尽可能准确,在测试中关闭了多线程渲染的选项。
一、不透明渲染
在不透明渲染的部分,经过对大量的实验数据进行特征分析、数学建模与训练之后,我们的模型得到了一个相对不错的拟合结果。可以先来看一下红米Note2上模型预测值的残差图:
从残差图中可以看到所有残差的绝对值都不大(小于2.5ms),并且残差都在0左右波动,变化幅度在一条带内。同时,表示真实值和预测值差值绝对值均值的MAE为0.71,表示模型对预测目标解释度的R2为0.94 (越接近1越好),这些都说明模型能够根据可控变量的输入来对目标函数的耗时进行良好的预测。
到这里我们就得到了在实验环境下不透明渲染耗时与三个变量之间的数学关系模型。在合适的范围内,可以通过给定的三个变量值,得到一个合理的预测值。通过模型还可以获取三个变量对不透明渲染耗时的相对影响程度:
上图展现了在红米Note2上三个输入变量对不透明渲染模块的相对重要性。以最重要的变量为基准(即图中的DrawCall),看做重要性系数为1,可以看到其余变量的重要性系数与DrawCall系数的相对比例。这里对变量的赋值量级做了调整,DrawCall数和Material数的量级都是1,而TotalTriangle数的量级为1000。也就是说,图中所呈现的结果可以解读为:当DrawCall数量增加1的时候,对不透明渲染耗时造成的影响与Material数量增加 1/0.51 = 1.96时差不多。而TotalTriangle的重要性则低到可以忽略不计,即在测试的范围内,TotalTriangle的变化几乎不会对不透明渲染耗时产生影响。
同样的,在小米5X和小米5s这两个设备上建模可以得到同样的模型数据,同样先看一下两个机型上的残差图:
(1)小米5X的不透明渲染残差图
(2)小米5s的不透明渲染残差图
同样也看到在这两个机型上的模型残差图表现也是比较好的。同时它们的MAE及R2的值也是非常不错,整理一下三个机型的这些模型指标如下:
可以看到小米5s上的数据最好,残差均值为0.435ms,解释度达到了95%以上。从这个评价结果来说,在测试的变量范围内模型的预测值准确度已经非常高了。
接下来再来看一下另两个机型的变量重要性系数图:
(1)小米5X的变量重要性系数图
(2)小米5s的变量重要性系数图
和红米Note2不同的是,在这两个设备上,在设定的量级下,Material数成为了影响不透明渲染耗时最明显的变量。而TotalTriangle在性能更好的机型上重要程度更低。最终我们可以得到在三个机型上三个变量对不透明渲染造成等值影响时的相对变化值:
根据以上模型的数据,我们可以得到初步的推测:
(1)DrawCall数和Material数对不透明渲染耗时的影响相近;
(2)TotalTriangle数对不透明渲染耗时的影响较小,在一定范围内的部分机型上甚至可以忽略不计;
二、半透明渲染
在半透明渲染部分,同样也是在红米Note2、小米5X及小米5s三个机型上进行测试和数据建模。三个机型下模型的评估指标如下:
相比于半透明渲染来说,这里的模型解释度有所下降,但也达到了90%。
接下来以红米Note2为例,看一下我们最关心的变量的重要系数图:
上图展现了各变量对半透明渲染模块在红米Note2上的相对重要性。其中以DrawCall的重要系数为基准,看做1,其余变量的重要系数是其与DrawCall重要系数的相对比例。这里同样对变量的赋值量级做了调整,DrawCall和Material数的量级都是1,而TotalTriangle数的量级为1000。
图中所呈现的结果可以解读为,当DrawCall数目增加1的时候,对半透明渲染的CPU耗时造成的影响与Material数量增加 1 / 0.37 = 2.7,或TotalTriangle数量增加 (1 / 0.01) * 1000 = 100000 差不多。
这里需要特别解释一下图中新的变量MatCountGT1,它的重要系数超过了1。在我们的模型中,这个变量是根据Material数额外生成的解释变量,其取值为布尔值,当Material数>1时为True,否则为False。
因为在实验中我们发现,当Material数 = 1(即渲染场景时不需要切换材质)时渲染模块的CPU耗时很小,但只要Material数 > 1,CPU耗时就有一个显著的提升。原因是半透明渲染的情况下,DrawCall会严格地从后往前提交,只要Material数 > 1,就会导致大量的材质穿插问题,使得DrawCall数接近测试用例中GameObject的数量(不透明的情况下,引擎会进行适当的DrawCall顺序调整,所以DrawCall数更接近Material数一些),所以导致Material数的数值对target的影响相对比较小。
同理得到小米5X和小米5s上的重要系数图如下:
(1)小米5X变量重要性系数图
(2)小米5s变量重要性系数图
同样可以得到在三个机型上三个变量对半透明渲染造成等值影响的自变化值(因场景材质数通常大于1,故省略MaterialCountGT1):
从以上模型的数据及分析我们可以得到初步的推测:
(1)在三个机型上DrawCall对半透明渲染的影响是最大的;
(2)相比于不透明渲染,半透明渲染中Material数量的影响程度较小;
本文主要利用由大量实验数据训练所得的Benchmark模型,对Unity引擎中不透明渲染和半透明渲染的CPU耗时进行了深度定量分析。可以看到,利用Benchmark模型,我们不仅可以在给定输入变量值时对相应模块的CPU耗时进行准确预测,还能进一步提取出不同控制变量对模块耗时的相对影响程度。关于引擎其他模块(粒子模块、物理模块、动画模块和UI模块等)的重要参数分析,您可以直接查看UWA DAY 2019的相关课程《如何根据UWA制定技术选型》。
尝试使用更为科学、普适的方式来研究问题,是我们UWA不断探索问题本质的方式。性能问题是我们想要不断突破的一个方面,而UWA Benchmark则是我们现阶段的一种尝试。以上内容只是我们尝试的一小步,在经过了大量的测试和分析之后,我们发现不同的引擎、引擎不同的版本、不同的硬件系统、设备厂商、系统版本等等都会对性能产生各种各样的影响。未来,我们会不断完善我们的分析框架、细化我们的测试条件,并结合真实项目的大量性能数据,力求挖掘出更多有意义的性能参数、打磨出更精准的数学模型,从而让整个行业不断获益!