白鹭引擎的性能优化技巧

同事的一篇文章,技术干货,特此转载!

引子

最近有不少开发者发了一个 Demo 给我,想了解为什么不同的 HTML5 游戏引擎之间的性能差异这么大,特别是白鹭引擎性能并不是很好。 对此我很惊讶,仔细研究了一下这个 Demo。

经过研究发现,制作这个Demo的开发者使用引擎的方法有些小纰漏,可能是对白鹭引擎不太了解的原因。 今天做这个示例,是为了给大家解惑答疑,并引导一下白鹭引擎的正确用法。


示例一: 帧动画

demo1_1-1.jpg

测试设备:iPhone6P + 微信

从这个示例中可以看出,白鹭引擎的帧动画性能似乎很差。但是经过我的优化,帧频从34帧提升至了56帧。

为什么有如此差距呢?原来是开发者提供的Demo 采用了错误的动画制作方式。

白鹭引擎官方提供了 MovieClip API 来进行帧动画创作,而评测的开发者错误的使用了Container 在每帧切换 Bitmap 的方式实现帧序列动画。错误代码截图如下:

demo1_2-1.jpg

在这种情况下,由于每帧都要进行 addChild / removeChild 操作,这样的话每帧都会进行显示列表修改,派发事件等操作,这样自然性能很低。

而这种问题的正确处理方式应该采用以下代码:

var texture = this.mArrayTextures[index];
bitmap.texture = texture;
index++;

采用这样的方式,每一帧只需要更改一下 texture 的引用,无需对显示列表进行修改,性能可以得到很大的提升。


示例二:显示对象旋转位移

demo2_1.jpg

测试设备:小米4 + 微信 ( X5 Blink内核 )

从这个示例中可以看出,白鹭引擎的显示对象旋转位移的性能和其他引擎的差不多。但我观察白鹭引擎的 Profiler 面板,感觉还可以有性能提升,所以我修改了一下测试用例,进一步提升了白鹭引擎Demo的性能,从14帧提升至了19帧。

这个性能提升的方法是关闭白鹭引擎的脏矩形特性。

熟悉白鹭引擎的开发者可能了解,白鹭引擎采用独立研发的脏矩形技术,只会渲染屏幕上发生改变的部分。而世面上大部分其他引擎都是无论画面内容是否改变,都会进行强制全部重绘。

为了能获取画面上的显示对象是否“脏”了,必然会付出额外的性能开销。但是通常在正常的游戏场景中,这种开销是值得的,因为一个游戏场景在多数情况下,都不是所有显示对象全部都在移动的,而是静止与动态的相结合。举个例子:

demo2_2.jpg

在没有脏矩形的情况下,由于所有显示对象都需要渲染,游戏的帧频只有16。 但是在开启脏矩形功能之后,游戏里只有14%的内容需要被重绘,性能就大幅提升到了 54 帧。 这表明,作为一个游戏引擎,白鹭引擎针对游戏常用的场景进行了更有针对性的优化。 当然,也会有开发者表示,我们的游戏就是所有显示对象都在不停的动,那这种情况下性能反而不好怎么办呢?其实白鹭引擎提供了一个 API 来去处理这种极端情况。

this.stage.dirtyRegionPolicy = "off";

关闭脏矩形之后,由于不需要再进行脏区域,所以帧频从14提升到了19。关于这一点的详细介绍,官方文档传送门:http://edn.egret.com/cn/docs/page/719 。 或者在白鹭引擎开发者社区 http://edn.egret.com/cn 中搜索“脏矩形”,搜索结果第一篇文章就是。


示例3:5000个显示对象缩放位移

demo3_1.jpg

在这种场景下,传统的Canvas渲染方式几乎失去了意义,分别只有5帧和10帧。当然,和上个Demo一样,关闭脏矩形之后,白鹭引擎的性能也得到了提升。

但是很多开发者都了解,在这种情况下 WebGL 模式可以获得更好的性能。白鹭引擎在 2.5 版本因为引入 了脏矩形这样的重大架构调整,几乎所有代码都被重写,(相信去年9月份升级版本的开发者会对此也有所吐槽),所以在新版本中,我们暂时关闭了 WebGL 渲染模式,但是在我们的开发版本中,WebGL的性能也得到了很大的提升。我们会尽可能努力让 WebGL 这个特性重新回到白鹭大家庭中。


示例四:90个骨骼动画

在测试场景中,开发者评测的白鹭引擎的Demo存在两个问题: 首先是骨骼动画创建非常缓慢,这其实是评测代码中的一个疏忽带来的逻辑错误:

demo4_1.jpg

在创建骨骼动画的时候,正确的做法应该是第一次进行创建,之后的创建采用之前创建的缓存就可以了,就像上图中的代码一样的思路。

但是上图中的代码有一个小疏忽,就是用了 this.i 就行了判断,而 this.i在整个创建过程中,都是0,所以每次创建骨骼动画,都没有使用之前的缓存,自然创建速度非常缓慢。 正确的代码应该是 if ( i == 0 )而不是 if ( this.i == 0 ),开发者(特别是之前做过 Flash 的开发者,比如我)一定要注意在 JavaScript 中,this.i 和 i 是完全不一样的。这种错误通常埋得非常深,查起来也是非常苦逼。

其次,白鹭引擎的这个Demo 帧频很低,只有15左右,而其他引擎达到了38帧。经过我们的评测,这是由于两方面导致的:

一是白鹭引擎和骨骼动画 DragonBones 的适配层存在优化空间,我们修改了这个适配层( 在 GitHub DragonBonesPerformance 分支上 )。

二是作为一个强大的骨骼动画库,DragonBones 包含了很多强大的功能,但是针对简单的骨骼动画,这些功能反而带来了一些副作用。我们为了解决这个问题,在 Egret 2.0 版本开始引入 FastArmature 模式进行优化,但是优化的仍然不够彻底,接下来我们会继续努力完善这一部分。

在经过初步的优化之后,白鹭引擎的骨骼动画 Demo 性能已经提升至了 32 帧,敬请期待白鹭引擎的下个版本。


结论

开发者放出的 4个 Demo 采用白鹭引擎的最佳实践进行优化后,性能均得到了大幅提升。 强调一下,本次所有测试(除了 WebGL)都采用白鹭引擎的 3.0.5 版本的标准公开 API 进行测试,未进行任何的特殊处理。


我们的问题

白鹭引擎也不是完美的。在仔细对比性能测试中,我们也发现了白鹭引擎的一些问题,如: 引擎内部存在一个小疏漏,重复赋值了 imageSmoothing 属性。修改后在特定情况下性能提升了10%。 DragonBones 和白鹭引擎的适配层代码逻辑可以优化,优化后帧频从 20提升至了32。

WebGL 模式需要加快速度重新提供给开发者 白鹭引擎缺乏一份完善的性能优化指导性建议。我们会尽快提供一份官方的性能优化最佳实践,以帮助开发者以正确的“姿势”做出更精彩的游戏。

以上问题会在白鹭引擎双周更新的版本中逐步提供给开发者。


尾声

现在目前主流市场的几个引擎,无论从功能,性能,工具和易用性上,大家都有了飞速发展。 欢迎大家PK技术和良性沟通,一起努力提高。 白鹭引擎在2016年的上半年主要主要的工作方向是进一步完善和优化2D渲染,资源管理,网络优化等相关能力,除此之外进一步加强3D方面的能力。 最近有不少用白鹭引擎的新技术制作大型游戏的线上案例, 例如多人在线ARPG《暗黑之王》

samples_2.jpg

实时在线回合RPG《主宰之王》等等

samples_1.jpg

在最后的最后,感谢各位开发者对白鹭一如既往的支持 ^ ^