作者:英特尔:卢卷彬, Kiefer Kuah 巨人:余娜娜
下载PDF版本:使用英特尔GPA优化《兵王》游戏的性能
《兵王》是由巨人网络公司旗下,上海巨贤公司耗时两年自主研发的一款未来战争MMORGP。《兵王》使用了优秀的3D引擎技术,在声光特效方面有极其优秀的表现。为了使《兵王》能够流畅的运行在更多玩家的机器上,我们在英特尔 集成显卡上对游戏进行了优化。英特尔集成显卡的出货量非常大,超过了60%(Q3’2011, Jon Peddie Research统计),其性能和特性也日益增强,是游戏开发者应该重点关注的对象。为了帮助游戏开发者在英特尔集成显卡上优化游戏性能,英特尔推出了一款出色的免费的性能分析工具,英特尔图形性能分析器(Graphics Performance Analyzers, 简称GPA),不用修改游戏的代码,不用安装特殊的驱动版本就能够使用。游戏开发者可以利用这个工具清晰的了解自己的游戏在什么地方耗费了更多的时间,以进行有针对性的优化。GPA是我们在这次优化中使用的主要工具。
以下的分析和优化基于Intel HD Graphics 3000硬件平台,代号Sandybridge(简称SNB),SNB拥有出色的图形性能,完全可以满足绝大多数网络游戏对性能的需求。我们使用的操作系统为Windows 7, 64位版本,GPA版本为4.2。
关于优化场景的选择,《兵王》支持大规模的群体作战,开发者对这种场景下的游戏性能特别关注,所以我们选择的是以下的一个群体战斗场景,其中有~100个玩家,并且有大量的武器和技能特效,游戏的性能在这个时候遇到很大的挑战,只有10fps左右,如图1所示:
GPA HUD可以从平台的角度分析游戏的性能,它提供了超过80个关于CPU,D3D runtime和GPU的性能指标,以及Null Hardware,Disable all draw calls, 1x1 ScissorRect, 2x2 Texture,Simple Pixel Shader等覆盖模式,收集这些性能指标和覆盖模式得到的数据,可以帮助我们分析出游戏的瓶颈所在。使用GPA HUD分析游戏的这个场景后我们得到以下的结果:
游戏在该场景下,FPS只能维持在10左右,此时的Null Hardware 和Null Driver覆盖模式分别只能达到14fps和20fps左右,也就是说显卡无限快的时候,游戏也只能跑到14fps,因此游戏在CPU上的性能消耗非常大。游戏有7800多个draw calls,这个一个非常大的数字。大量的draw calls,会导致游戏程序,D3D runtime以及显卡驱动等这些CPU上的计算量增加,由此导致性能非常的低。所以,游戏在这个场景中的优化重点,应该是合并小的画图操作,减少draw calls和State Changes的数量,这样可以同时减少CPU和GPU上的负担。
GPA可以抓取游戏的一帧数据,然后使用帧分析器进行离线的详细分析。帧分析器打开一帧的过程,就相当于是重新发送指令让GPU渲染出这一帧。帧分析器界面主要包含上部分的柱状图(A),它们代表的是游戏中按照调用顺序排列的Clear, Draw Primitive,StretchRect等,我们统称为ergs,可以通过设置X轴和Y轴来快速的定位最耗时的调用,比如X轴设为GPU Duration,Y轴设为GPU Breakdown。界面左边中间(B)是所有ergs的列表,左边下面(C)是Render Target,右边下面(D)是一系列Tab,包括性能数据,ergs的纹理,shader,state,API log等等。我们在目标场景中抓取了一帧数据,下图2是使用帧分析器打开这帧数据的界面:
从图2所示的帧分析器界面我们可以看出,渲染这一帧GPU花费的时间为71ms。游戏的渲染步骤是先生成阴影贴图(0-1198),然后是正常场景的渲染,最后是后处理以及UI部分。
阴影贴图使用了1198个draw calls,通过GPA帧分析器的纹理tab可以查看这些draw calls所使用的纹理,我们发现,游戏对某些特效也计算了阴影:
如图3所示,选中的那个纹理,以及相邻的纹理,都是特效相关的,而游戏设计之初并没有要求计算它们的阴影,可以移除掉。
另外,游戏花费了5526个draw calls来渲染场景中的所有物体,占了整个一帧的72.6%的时间。这5526个draw calls中,有超过4000个是渲染特效部分,它们占用了整个一帧44.9%的时间,这是优化的重点目标,如图4所示,在界面的render target那里可以看到所有的特效:
通过查看和分析这些特效,我们发现每一个特效的draw call所渲染的东西都是非常小的,而且它们所使用纹理,多数都是小纹理,如下所示:
上图5中,选中的这10个相邻的draw calls使用的都是这个128*128的蓝色纹理,所渲染的东西也是相邻的,我们可以考虑合并这几个draw calls。同时,考虑到纹理比较小,我们还可以合并相邻的特效纹理,这样可以减少SETTEXTURE的次数,也减少了CPU端的消耗。
坐骑和人物部分的渲染,也划分得很细,头发,脸孔,衣服,武器等都是由单独的draw call完成。这些也可以进行适当的合并,从而减少draw calls和state changes的数量。
5.1 移除特效的阴影计算
如前面的图3所示,游戏中对特效也计算了实时阴影,一般来说,特效是不需要计算阴影的。移除掉这些draw calls后,生成阴影贴图 的draw calls从1198个减少到了860个, 这部分的GPU时间消耗从6.5ms减少到了4.86ms。
5.2 合并特效draw calls和纹理
由前面的分析我们知道,要优化游戏在该场景下的性能,最重要的就是减少CPU端的消耗,也就是要减少游戏的draw calls和State Changes的数量。场景中特效的draw calls数量是最多的,而且特效的draw calls都是非常小的,所以我们首先来合并特效的draw calls。
从以上图6和图7的对比我们看到,合并优化后,渲染相同的特效,我们减少了draw calls的数量,从37减少到1,纹理也从4个合并到1个。整个一帧,特效的draw calls数量从4000多次减少到了700多次,set texture的次数从3764减少到2029次。整个特效部分的GPU时间从45ms减少到了21ms,提升非常明显!下图8为优化后的结果:
5.3 合并人物和坐骑的渲染
游戏对人物的渲染区分很细,帽子,头发,衣服,裤子,鞋子,武器等都由独立的draw calls来完成,对坐骑也是由3个draw calls来完成的,虽然他们使用的是同样的纹理。以下以坐骑为例,将优化前需要3个draw calls完成的渲染,合并为一个draw call来完成。
如上图9和图10所示,优化前需要用3个draw calls来完成一个坐骑的渲染,我们合并了坐骑的IB和VB,优化后只需要一个draw call就完成了这个坐骑的渲染。整个一帧有~100个坐骑,合并优化后,节省了~200个draw calls。
我们针对人物也进行了类似的合并优化,也节省了200个左右的draw calls。
5.4 移除重复的SETTEXTURE
为了减少CPU端的消耗,我们还应该尽量减少state changes的数量,在帧分析器对另外一个场景的分析中,我们发现,每个draw call都会设置自己所需要的所有纹理,而不管是否和上一个draw call使用了相同的纹理。
如图11所示,柱状图中选中的临近的2个draw call,是在画地形部分,即使它们使用的是完全相同的纹理,它们也都重新调用了5次完整的的SETTEXTURE API。该场景中整个一帧总共有1837个SETTEXTURE API 调用。这不是有效率的做法,如果相邻的draw call使用的是同样的纹理,可以不用调用SETTEXTURE再次设置,这样可以减少state changes的数量,提高游戏性能。
以下是优化后的截图,移除掉了重复的不必要的SETTEXTURE API调用,如图12所示,第二个地形draw call本身已经没有调用set texture了。经过优化后,整个一帧所调用的SETTEXTURE API次数从1837次减少到了554次。
经过以上几种优化,我们把《兵王》在这个场景下的draw calls数量从7839次减少到了3812次,SETTEXTURE API调用次数从3764次减少到了2029次。游戏帧率从10fps提升到了20fps左右,基本能够流畅的运行,达到了游戏开发者的期望。
6 . 总结
本文介绍了使用GPA对《兵王》进行性能分析和优化的过程,从中我们可以看出,游戏客户端的性能优化并不难,我们所使用的优化方法也都很常见,但重要的是如何很快的找到这些游戏热点和瓶颈。在《兵王》的性能分析和优化过程中,GPA发挥了非常重要的作用,通过它我们很容易就发现了游戏的瓶颈,进行针对性的优化,游戏的性能得到了很大的提升。我们将继续使用GPA进一步优化游戏。
作者简介
卢卷彬 英特尔公司的应用工程师,他和国内几家大的游戏公司有着多年的合作,帮助他们在英特尔平台上优化游戏客户端的性能。
余娜娜 巨人前传技术部经理,参与开发过3DRPG《仙剑奇侠传三-问情篇》,3DMMORPG《巨人》、《兵王》。主要致力于3D引擎的研制开发,与Intel的几年合作中,极大提升了自主研发的3D引擎的特性,在效果提升的基础上,同屏人数也大大提升。
Kiefer Kuah 英特尔的软件工程师,他主要负责游戏在英特尔平台上的优化工作。