UC在今年年初发布了支持硬件加速的实验室版,开始实验性地支持对2D Canvas进行硬件加速。在之前发布的9.3版本中开始分开两个版本 – 普通版和加速版,针对中高端手机的加速版开始正式支持硬件加速2D Canvas渲染。并且在即将发布的9.4加速版还会带来全新的Canvas渲染架构,进一步提升性能和减少对系统资源的占用。
这篇文章的主要目的是为移动Canvas游戏的开发者如何针对Android UC浏览器加速版进行渲染性能优化提供指导,不过文中的大部分内容也适用于Android平台其它支持硬件加速2D Canvas的浏览器,比如Chrome for Android(Chrome也已经正式成为Android 4.4的系统内置浏览器)。另外,这篇文章的内容主要是针对渲染性能优化,而不是JavaScript性能优化,不过因为Android UC浏览器使用的是V8引擎,所以您应该很容易找到很多如何针对V8优化JavaScript性能的文章。
Rule #0 为移动平台进行优化
为移动平台进行优化是十分重要的,因为移动平台的性能大概只有桌面平台的1/10左右(注1),它通常意味着:
注释:
Rule #1 为Android而不是iOS而优化
牢记这一点非常重要,Mobile Safari的Canvas渲染机制跟Android平台有很大的不同,特别地针对Mobile Safari进行优化的Canvas游戏在Android平台的渲染性能会十分的糟糕。
Mobile Safari使用了iOS/MacOS平台特有的IOSurface作为Canvas的Buffer,当通过Canvas API往IOSurface绘制内容的时候是没有GPU加速的,iOS仍然使用CPU进行绘制,但是将一个IOSurface绘制到另外一个IOSurface上的时候,iOS会使用GPU的2D位拷贝加速单元进行加速(注1)。这种机制其实也是iOS UI界面Layer Rendering渲染架构的基础。所以为iOS优化的Canvas游戏会倾向于使用大量的Off-Screen Canvas(注2),不管是静态的图片也好,还是需要动态产生的内容也好,统统都缓存到一个Off-Screen Canvas上,最终游戏场景的绘制就是一个把一堆Off-Screen Canvas绘制到一个On-Screen Canvas的过程,这样就可以充分利用iOS绘制IOSurface到IOSurface使用了GPU加速的特性来提升渲染性能。
但是这种大量使用Off-Screen Canvas的做法在Android平台的浏览器上会非常糟糕。Android平台并没有IOSurface的同等物(一块同时支持CPU读写和GPU读写的缓冲区),所以它只能使用GL API对Canvas进行加速,一个加速的Canvas,它的Buffer是一个GL Texture(被attach到一个FBO上),这意味着:
后续的内容会进一步说明如何针对使用GL加速的Canvas渲染架构进行优化。
注释:
Rule #2 优化网页的DOM树结构
Canvas只是网页的一部分,它最终要显示出来还需要浏览器对网页本身的绘制,如果网页的DOM树结构越复杂,浏览器花在网页绘制上的时间也就越长,网页绘制占用的CPU/GPU资源也就越多,而留给Canvas绘制的CPU/GPU资源也就越少,这意味着Canvas绘制本身需要的时间也越长。并且网页绘制的耗时越长,Canvas最终更新到屏幕上的延迟也就越长,总之,这是一个此消彼涨的过程。所以,优化网页的DOM树结构,让其尽可能简单,浏览器就可以把更多的系统资源花费在Canvas的绘制上,从而提升Canvas的渲染性能。
最理想的DOM结构就是只包含一个 body ,加上一个 div 作为容器和加上一个 canvas 本身。如果Canvas上面需要显示其它的网页内容,最好只是用于一些临时使用的对话框之类的东西,而不是一直固定显示。
Rule #3 优化网页元素的css背景设置
跟#2的道理一样,背景设置越简单或者根本不设置背景,浏览器花费在网页本身绘制的开销也就越小,一般来说元素本身都不应该设置css背景,它的背景应该通过Canvas API来绘制,避免浏览器在绘制元素时还要先绘制背景,然后再绘制Canvas的内容。另外和其它元素都应该首先考虑使用background-color而不是background-image,因为background-image的绘制耗时比一个纯色填充要大的多,而且背景图片本身还需要消耗额外的显存资源(生成Texture)。
Rule #4 使用合适大小的Canvas
考虑移动设备的性能限制,Canvas不适宜太大,否则需要消耗更多的GPU资源和内存带宽,480p或者600p是一个比较合适的选择(横屏游戏可以选择800p或者960p),一般不应该超过720p。并且游戏图片资源的分辨率应该跟Canvas的分辨率保持一致,也就是正常情况下图片绘制到Canvas上应该不需要缩放。
我们需要避免创建了一个较大的Canvas,但是仍然使用较低分辨率的图片资源,图片绘制到Canvas上还需要经过缩放的情况。这样做毫无意义,因为游戏本身的分辨率是由图片资源的分辨率来决定的,上述的情形既不能提升游戏的精美程度,也白白浪费了系统资源。
如果自己预先指定了Canvas的大小,又希望Canvas在网页中全屏显示,可以通过标签设置viewport的大小(注1,注2),直接告诉浏览器网页虚拟viewport的宽度应该是多少,并且让viewport的宽度等于Canvas的宽度,而浏览器会自动按照viewport宽度和屏幕宽屏的比值对网页进行整体放大。
注释:
Rule #5 避免使用多个On-Screen Canvas
如#1所述,多个Canvas同时更新会降低GL渲染的效率,并且如#2所述,多个On-Screen Canvas会导致网页本身的绘制时间增加,所以应该避免使用。
Rule #6 合理地使用Off-Screen Canvas
在GL加速的Canvas渲染架构下,合理地使用Off-Screen Canvas可以在某些特定的场景提升渲染性能,但是不合理的使用反而会导致性能下降。
总结一下,Off-Screen Canvas的使用应该尽量遵循以下原则:
注释:
Rule #7 避免频繁调用getImageData,putImageData和toDataURL
因为它们都会需要从显存拷贝内容到普通内存,或者相反,拷贝的速度很慢并且会造成GL渲染流水线的阻塞。所以不要在每个Game Loop都调用这几个API。
Rule #8 如果需要最大帧率,优先使用requestAnimationFrame而不是Timer