![耀世-耀世娱乐网络科技媒体工作室](/public/upload/system/2018/07/24/c987e52b734492ba42fbbb065c91ae10.png)
发布时间:2024-06-19 09:45:00 作者:佚名 点击量:
程序方面:
内存优化
渲染效率优化(包含综合帧率)
机型适配优化
CPU利用率优化
网络流量优化
资源大小优化
耗电量优化
I/O效率优化
美术方面:
配合渲染(面数,材质球,特效制作效率...)
配合适配 (UI的适配性,字体的适配....)
配合内存及CPU(贴图的UV排布,材质大小及通道复用)
策划方面:
不产生新开发需求的各个模块调优
游戏软件的优化和一般软件是有一些区别。
游戏通常是软实时(soft real-time),就是说运行上有时间限制,但没有硬实时般严格。
先谈固定硬件的游戏平台,如游戏机和街机。在这些平台上,通常会设置固定的帧率目标,例如30 FPS(即每帧33.3毫秒)。游戏开发者希望在这个时间限制下,尽量提升游戏的品质,例如更精细的角色和场境、加入更多效果、提升人工智能水平等。优化的目的除了令游戏顺畅,也是提升游戏品质的必要条件之一。
对于PC或手机平台,因为硬件的性能有很大差异,优化就没有一个具体的目标,而是希望尽可能在大部分平台上都能做得最好(虽然PC游戏有几百FPS的情况,但实质上几乎不能增加流畅性)。
从玩家角度,我认为游戏的性能指标大概有这几方面:
而在开发的角度来说,我认为优化方法可以分为无损和有损的。无损是指不影响品质,纯粹通过技术上的优化去提升整体性能。而有损是指通过简化、近似化去改善性能,例如简化着色器(shader)、要求美术降低某角色的三角形数目、要求关卡设计师减少一些NPC等。
优化前我们要先进行性能剖析(profiling),找出性能问题的核心,然后再看看有什么方法可以尝试。主要可分为算法上的和底层的优化方法。不详细说明,就举个例子吧。
例如,在二维弹幕射击游戏中,需把大量子弹与飞机做碰撞测试(相交测试)。如果有n颗子弹,m个可被击中的目标,蛮力法需要mn次测试。我们可以看情况,使用一些空间分割的算法,把子弹和目标分配到不同的空间范围里,只需对每个范围里的物体做测试。而在底层方面,我们可以考虑使用多线性、SIMD指令,并考虑到缓存一致性等方面去优化。
上述例子主要是在CPU上进行的游戏逻辑方面的优化,而许多游戏中也需要在CPU/GPU上对图形方面进行优化。在PC/手机平台上,因为瓶颈不固定,游戏开发者通常会尽力优化每一个部分。
----------
@孟德尔和
@Thinkraft提到了Quake的平方根倒数,我引用一篇以前写的文章,测试SSE指令和Quake的实现:
在1999年,id software公司发布了《雷神之锤III竞技场(Quake III Arena)》巨作,此第一身射击游戏有别于前作,以多人连綫游戏为主轴,得到空前的成功。
在2002、2003年间,网上出现一段关于该游戏中的源代码讨论,那段代码是这样的:float Q_rsqrt( float number ) { long i; float x2, y; const float threehalfs=1.5F; x2=number * 0.5F; y=number; i=*(long*)&y; // evil floating point bit level hacking i=0x5f3759df - (i >> 1); // what the fuck? y=*(float*) &i; y=y * (threehalfs - (x2 * y * y)); // 1st iteration // y=y * (threehalfs - (x2 * y * y)); // 2nd iteration, this can be removed return y; }
它是用于计算一个单精度浮点数的平方根倒数(reciprocal square root, 即1/sqrt(x))。平方根倒数在游戏中经常用到,例如把矢量归一化(normalize)时,就要计算n=v / sqrt(v ? v)。
此段代码使用了牛顿法(Newton’s method)去提升精确度,但令人渍渍称奇的是它计算初始估值的这一句:i = 0x5f3759df - ( i >> 1 );
它利用了IEEE754浮点数的二进制表示来计算第一个近似值。此方法是谁发明的,魔术数字(magic number) 0x5f3759df 从何而来,暂时也没有确切的证据。但现在已找到比这更优的魔术数字[1]。
然而,本文想带出的是,虽然此方法如此神奇,在现今的机器上通常不是最理想的。在PC上,自1999年Intel推出的Pentium III,就已经加入了SSE指令集,当中的rsqrtss指令就是能够计算一个单精度浮点数的平方根倒数。此外,rsqrtps则能同时计算四个单精度浮点数的平方根倒数。
测试
我们可以写一个程序简单测试一下:
(略……)
结果及分析
使用VS2008 (缺省release配置),在i7 920 2.67Ghz上的結果:dummy 363.8ms error=83.70051795% standard 1997.4ms error=0.00000871% quake 586.1ms error=0.17520049% quake2nd 970.1ms error=0.00046543% dummy_ss 109.4ms error=83.70051795% vsqrt_ss 1160.3ms error=0.00000871% rsqrt_ss 108.3ms error=0.03087627% rt2nd_ss 180.6ms error=0.00002188% dummy_ps 26.8ms error=83.70051795% vsqrt_ps 288.4ms error=0.00000871% rsqrt_ps 27.0ms error=0.03087627% rt2nd_ps 53.4ms error=0.00002188%
standard用了标准库的sqrt()函数,编译器使用传统FPU的运算计算开方和倒数。
quake和quake2nd的确比standard快,但quake的相對误差峰值约是千分之2,误差较大。quake2nd则用接近一倍的运算时间来改善精确度,相對误差峰值降至约百万分之5。
divsqrt_ss使用了SSE运算,准确程度与standard相同,而耗时仅比quake2nd慢一点点。实际上,如果在编译器开启/arch:SSE,standard也会使用SSE运算,产生的代码和divsqrt_ss相约,性能也差不多。
重点来了,rsqrt_ss的耗时只有quake的18%,而相對误差峰值也更好,约万分之3。仔细一看,发现它的耗时与dummy_ss相若。换句话说,因为使用了流水綫的潜伏时间,其数据吞吐量和至dummy_ss相若。
那么,再比较使用多一次牛顿迭代的版本。rsqrt2nd_ss的耗时也只有quake2nd的18%。而相對误差值也更好,去到千万分之2的水平。
最后,若真正运用了SIMD的并行运算能力,使用ps后缀的指令又会如何?在此测试中,可以看到性能比ss版本的提升了3至4倍。而rsqrt_ps也因流水綫达至dummy_ps的吞吐量。rsqrt_ps比quake版本快20倍以上,比standard版本快70倍以上。
总结
虽然quake里的平方根倒数算法是令人津津乐道的话题,但从应用来说,它并不一定是最好的选择。
……
参考
[1]Lomont, Chris. "Fast inverse square root." Technical Report, 2003.http://www.lomont.org/Math/Papers/2003/InvSqrt.pdf
只答前半部分「什么是优化」。
我个人的看法:广义上, 优化是「为了达成相同目标,寻求并采用消耗更少资源的办法」的过程,或其结果。
不知道题主小时候读没读过高斯那个很流行的传说,老师让计算1+2+3+...+99+100,比起全部一个个相加,他发现1+100=2+99=3+98=...=50+51=101,然后直接101*50得出了答案5050。利用等差数列的求和公式使计算更加简便快捷,这就是一种算法优化。
那么把上面斜体部分代入到游戏这个话题中,不难理解优化是怎么一回事了:
通过特别的软件编程技巧……
实现相同的画面表现效果、流畅度,对硬件机能的需求更低、更平民化
或者
在相同机能的平台上,实现更好的画面表现效果、流畅度
如今玩家们口头说的优化,一般是针对移植作品,也就是至少有两个平台对比。人们会将游戏在原平台的画面表现和平台机能作为基线,去衡量移植版,有时也会以同平台的不同游戏作对比进一步论证。总之,一个游戏在一个平台上的优化好坏,大体可以用「表现效果/环境需求」的比值来衡量,或者说白了就是画面好不好看、跑起来卡不卡。
@孟德尔提到的卡马克平方根倒数算法是一个细节优化的例子,就是采用了特殊处理而非标准库函数来提升运行速度,无数个这样的细节堆砌起来,造成的效果可能甚至不是不太卡和有点卡的区别,而是有的玩和没的玩的区别——那个时候你甚至想不到「优化」这个词。卡马克之所以神,就是因为他屡次化不可能为可能。他在PC机上先后实现了卷轴游戏、带贴图的伪3DFPS、真3DFPS、真实光源……这每一个进步背后都隐藏着优化,只是那个时代ID的光芒掩盖了一切,其领先业界的幅度之大让大家只能看到质变而已,可以说,在20世纪时,玩家们还没意识到优化一说。
——————————
下面不说怎么实现优化(可以参见
@Milo Yip的回答),而是谈谈大家是怎么意识到需要优化的,题主为何产生这样的问题——其实那些嘴上说着优化好优化渣的人未必比题主更懂。
回过头来,我们什么时候开始感到「优化」的概念呢?我想,应该是PS2时代。
PS和SS时代,主机阵营相当分明,即使跨平台游戏的表现有差异,多半也被归功于SS的扩展卡。同期主机游戏和PC游戏的互相移植相当有限,也就是一些大作,例如PC版FF8、PC版MGS、SS版仙剑之类。
进入PS2时代后,微软参战,加之卡社、科社为代表的公司进一步婊化,游戏跨平台化变得越来越流行。由于电视游戏主机的硬件相对固定,为了最大限度发挥机能,提高3D画面表现,开发过程中需要针对主机特性进行大量特殊处理,也就是所谓优化。而移植时限于成本,往往无法做到这么彻底,因此即使硬件性能相当,原平台版的表现通常还是会比移植版要强一些,这个差距可能体现在分辨率、抗锯齿、贴图精度、多边形数量、运行帧率等各个方面。
到了PS3/X360时代,PC移植兴旺后,「优化不足」的问题更加明显,抱怨也更多,其实主要是国内玩家对移植版抱有不应有的期待,原因大概有以下几点:
补一下,还是拿被骂优化烂最凶的GTA4说,这游戏大陆根本买不到合法正版。你上amazon、steam去买下载版,没有美国信用卡账单地址人家根本都不让你支付的。所以,吐槽归吐槽,但有几人真有资格抱怨优化问题?
说起游戏的优化,在游戏开发中经常分为这几步:
游戏开发中一定是先做工具,进行Profile,再进行优化,所以,说优化就不得不再扯一下Profile
常见的工具有一些是引擎和IDE自带的,比如Unity自带的Profiler,就包含了CPU,GPU,Memory等等各式各样的性能分析工具,其他的比如GPA,Xcode Instrument和Visual Studio,Intel自带的内存管理工具在必要的时候也使需要去学习和使用的。
另外一些工具,就需要根据游戏的需求去编写了,比如一键关闭所有特效,一键更改分辨率等等,一键设置场上NPC数量,简单的游戏如啪啪三国是做成快捷键开启Profile功能的,更为复杂的游戏如神秘海域则是通过游戏内控制台来进行更为细致的Profie。
接着,我们再来说说游戏优化中主要的四个考虑方向:
1、CPU
引发的问题:
常见的优化手段:
2、GPU
引发的问题:
常见的优化手段:
3、IO和网络
引发的问题:
常见的优化手段:
4、内存
引发的问题:
常见的优化手段
其实这四个方面的优化总是相互制衡的,你把一个方面的优化做好了,另一个方面的问题又会出现了,比如,我们如果使用动态加载和卸载资源,这就虽然减少了内存占用量,会在IO上造成加载时间延长的问题。
所以,我们在做游戏优化的时候,不能太追求完美,刚刚好就是真的好(Good Enough Is Fine)。最终使得以上这四个方面能达到均衡即可,切忌在某一方面优化过头,又引发其他方面的问题,此消彼长的情况下,有时反而不如不做优化。
更新完成。
@Milo Yipmilo老师说了整个优化的大框架,那我来补充一点实践方面的知识好了,聊一下天涯明月刀OL的优化,下文把这个游戏简称天刀。这是一个大型mmorpg,pc平台,客户端程序员数量在25人以上,其中引擎程序员大约在10人不到。
天刀是我从业以来优化最久,投入最大的项目,没有之一。以前的AAA游戏,我们通常会有一个两个senior的程序员,进行数个月的优化,而天刀的优化,断断续续做了接近3年,长期来看平均有两个以上的senior程序员投入,整个引擎和游戏方方面面都做了好几轮优化。
优化的流程始于profiling,我们用过各种各样的工具,cpu性能方面包括比较大路一点的vtune,自制profiling工具,已经数个小众一点的基于sampler的cpu监测工具。为了监测各种卡顿,windows performance toolkit是最常用的利器,虽然相对难学点,但这是最好的查找卡顿的工具了,是windows平台上性能优化必须要掌握的工具。gpu性能方面gpa,perfhud用的比较多,中后期gpuview也很常用。至于为什么要用这么多不同的工具,主要是每个工具各有擅长,往往能看见其他工具不容易发现的盲区。
来几个有趣的优化例子,看看天刀这里的优化幅度:
1,milo老师的tag math+visibility组件,理念在2015年已经不算先进,但令人惊讶的是组件的效率。参考的dice公司同样的技术,运行在ps3的spu,这样的计算密集型特性,经过milo的优化,运行在普通cpu上,毫无压力,在i3的入门级cpu上,依然只消耗低于2ms的cpu时间。这个技术甚至颠覆了传统场景管理技术。天刀这个大场景远视距的游戏,玩过的朋友可能知道,场景复杂度和可视距离,都是国内少见的,即使和国外游戏相比,也只有逊色于just cause,farcry等不多的游戏,但是天刀里面的场景管理,没有用4叉树或者8叉树,直接扔进底层visibility组件。。。当然visibility组件里面还是有一些简单的类似管理,把这一层管理放到底层之后,场景管理和移动物体变得无比方便,对引擎层完全透明了。visibilty组件如此高效,以致阴影、树木、反射,全部可以由它来裁剪,简洁优雅。
在天刀早期做完大地形后,粗粗用visibility裁剪一下,帧数直接翻倍。后期主城场景密密麻麻,用到了其中的遮挡裁剪,看不见的就不画,又没有gpu的oclussion culling的延迟。
2,植被系统的优化。严格来说这不算优化,是一个特性。天刀植被用了speedtree,相当出色的中间件,基本一个美术就能把整个游戏需要的树全部建模出来了。于是我们就在考虑是不是可以在场景里面种满树,因为这个中间件也有类似的demo。但是结果并不令人满意,原生的技术在效率和质量上都不 够好。
开发同事在speedtree的基础上做了很多尝试和优化,效果被制作人几次拍死,最后终于达到了满意的质量,也就有了大家看见的漫山遍野得植被。远处树木本质上使用speedtree的billboard,但是在normal生成、树木根部和地形的融合等多方面做了很多尝试,对于树木建模,是多一些polygon,好降低fillrate(因为pre z可以裁剪掉后面的像素),还是用大一点的面,可以减少polygon,也做了细致的美术微调,找到了平衡。前后整个特性应该做了超过半年,初始效果做出来以后,在场景序列化、裁剪等方面又要做深度整合,而不能简单充用原先speedtree的整合方式。在远处使用billboard的树木,同事很创新的想出密度倍增的技巧,一下子就出现了震撼的全景植被效果。
天刀的植被目前来说,效果和效率应该是是顶尖的。我们在2014年给unreal引擎创始人tim sweeney展示的时候,他也表示这是他见过的最好的植被表现,能得到偶像君的肯定,团队也非常受鼓舞。更多细节我们今年下半年会有一个gdcc session,同事应该会介绍更多。
这个工作有一点值得注意,真正好的优化,是程序、美术等多团队共同努力的结果。在效果和效率上很多团队说起优化,就是程序定个指标,扔给美术去做,程序就不管了。这样做下去,很容易出现美术用力缩贴图减模型,效果一塌糊涂,帧数倒是没提高多少。真正好的优化,必须是多方一起坐下来,共同分析问题,一起解决,即使是纯美术的改动,也需要程序帮助定位问题,这样才更有针对性,才能把优化带来的质量损失降到最低。
3,卡顿优化,shader cache收集系统
天刀一测二测得到了较好的性能方面反馈,一方面的确优化的还可以,另一方面由于我们花了非常多的精力在防卡顿上面,导致游戏里面的卡顿相对较少,有玩家反映帧数只有20左右,但也能顺利玩下来。由此可见,片面追求高帧数意义不大,去掉性能上的spike,会给玩家更好的体验。
卡顿原因多种多样,单独就可以是一个很大的话题。我分几个单独案例来说。
一个情况是shader cache,天刀使用了uber shader的机制,根据场景和人物材质不同,可以组合出多种多样的shader,开发早期会在构建版本的时候穷举所有参数组合把shader全部编译出来,存在版本里面。很快这个方式就没法用了,shader参数组合爆炸了,于是就用动态生成机制,根据参数组合去取shader,如果之前没有编译过就同步编译,然后把编译后的shader保存到cache文件,以后要用就可以直接load。之所以这里没有在后台用别的线程编译,主要原因还是不希望编译的过程中模型不显示造成显示上的瑕疵。但是这样做的话,如果遇到缺失shader,就会有几百ms的卡顿了。
开发过程中这个同步编译当然无妨,但是见玩家的时候可不能这样,事实上三测一开始就因为bug,cache没处理好导致卡顿乱七八糟,被玩家喷死了。我们需要做的就是给玩家的版本里面需要把所有的shader组合都编译出来。
07年做xbox360游戏的时候也遇到过一样的问题,当时的团队有足够的测试资源,我们的方案是把所有用到过的shader参数存在文本文件,每天测试人员会把所有的参数发给我,我运行一个脚本去掉重复,整合所有的shader参数,然后第二天构建版本的时候就可以用收集的参数来预先生成所有的shader了。
但这次天刀项目组规模比较大,用类似的方法收集比较累。
我写了个简单的小server,测试版本都会上传所有的shader参数组合到这个小server,这个server会定期合并参数,输出后就可以上传到版本里面,下一个版本就可以构建这些shader了。
通过这个方法,我们在整个团队不受到干扰的情况下,收集了所有的shader参数组合。同时如果一个shader太久没有被用掉,也会被server去掉,这样如果因为更新程序导致shader改变,那些废旧的shader会在一段时间以后被慢慢淘汰,避免了shader的无止境增加。
4,卡顿优化,跨进程卡顿
有一阵子上线以后玩家表示卡得很,但只要删除cross组件(腾讯内部的一个通用组件),就会流畅很多。听到这个迷信的说法,大家都不以为然,自己测试一下,发现也没有重现。
可是过了一阵子,发现坊间这个传说越传越广,进一步测试,发现在主程或者群战的时候,的确有可能会有很多没发现的卡顿,这些都是我们开发版里面不能重现的。
开发同学用Windows performance toolkit查了很久,结论非常令人崩溃。
起源是Cross组件使用了内部的另一个登录组件,这个组件也被很多其他腾讯产品使用。每一帧cross更新的时候,这个登录组件都会去读一个系统的锁。如果在游戏内存占用量非常高的时候,这个系统锁的变量有可能被page out,于是引起了一个page fault,所以系统就会卡顿一下。我们内部的电脑都是SSD,所以page in也一般不是很慢,理论上也不应该卡啊。继续追查,发现出问题的机器上往往装了微云,微云经常读写硬盘,在多台电脑件同步数据。如果正好page in发生在读写数据的时候,就会卡了。换句话说,我们内部观察到的现象,是腾讯的微云读写一个文件,正好和游戏中那个系统级锁的page in过程重叠了,所以会卡。外部玩家如果用了其他腾讯服务,或者硬盘比较慢,也可能引起一样的问题。
解决方案就比较简单,把这块东西的update全扔到另一个线程就好了。
就先讲四个案例吧。可以看见在这几个案例里面,查找问题、解决思路都各不相同,但共同特点都是在程序端做了非常多细致的工作,也有一些非常有创意的解决方法。在我看来,优化绝不仅仅是设一个budget,对美术资源的大小、polygon数量等等设好限制,然后push他们去减贴图简化模型。程序作为性能的主导者,有非常大的主动性,如果能有很多更有创意的实现方式,可以大大简化美术的工作,也把性能和效果推到更极限。
联系我们
contact us地址:广东省广州市天河区88号
电话:400-123-4567
点击图标在线留言,我们会及时回复