Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

探索影响drawImage的性能因素及优化策略 #5

Open
CoderMing opened this issue Apr 27, 2018 · 1 comment
Open

探索影响drawImage的性能因素及优化策略 #5

CoderMing opened this issue Apr 27, 2018 · 1 comment

Comments

@CoderMing
Copy link
Collaborator

在许多的关于canvas性能的文章中,说了很多可以给canvas进行分层,离屏渲染,然后再绘制到当前显示的canvas中。但实际上,离屏渲染是否有一些需要注意的地方?

问题的出现

在本小游戏写游戏逻辑的时候,有一个箱子由上到下掉落的动画。这个动画的实现是通过维护dataBus中的boxList,来draw相应的箱子。整个渲染过程是放在副屏(ctxAssociate)上的。但后期发现,当我加上这个动画之后,GPU的性能消耗提升了近一倍。

查资料踩坑

首先我以为是优化的问题,便去网上找了许多比较火的文章:
http://jsperf.com/ 性能测试网站
https://www.cnblogs.com/rhcad/archive/2012/11/17/2774794.html 这篇文章做了很多性能测试,从中得出了很多结论,比如一次stoke要尽量多的绘制等。
https://developer.mozilla.org/zh-CN/docs/Web/API/Canvas_API/Tutorial/Optimizing_canvas MDN的文章。很多tips
http://web.jobbole.com/82072/ 也是做了很多性能测试,得出了少用浮点运算,少用canvasAPI(和之前cnblogs的那篇文章有异曲同工之妙)
http://taobaofed.org/blog/2016/02/22/canvas-performance/ 淘宝FED的深度好文

产生疑问

看完这些文章,仿佛得到的结论就是:离屏渲染就是好的。但为什么我的代码却因此消耗了大量的性能?直觉告诉我这和drawImage这个canvas上用得最多的API的性能问题有关。为了找到答案,我决定动手尝试一番以得出真正的答案:
首先,我将我的canvas副屏去除,将所有的图像都通过主屏绘制,运行Chrome的performance得:
2018-04-27 2 52 44
可以看到,其中的GPU大概占了4ms左右的时间。
然后我仍将所有的东西通过主屏绘制,仅仅在每次render的时候将副屏绘制上去(注意,是绘制空的副屏),得到一下结果:
2018-04-27 2 55 48
发现了吗?我仅仅是将空的副屏绘制到主屏,也会消耗很大一部分性能!

更多的思考

从上面的一些东西,我们可以得出结论:使用drawImage将一个canvas绘制到另一个上面的时候,会消耗很多性能。
但这也引发了我的思考:这个小游戏的界面上,我绘制了大量的图片。性能消耗也没有这么大啊?
接下来,我尝试着探索了drawImage的性能和和绘制物的具体关系。

绘制对象是canvas和image时的性能差别

测试方法:同时绘制1w次图片或者canvas,绘制的大小均为1000x1000。关于此次测试的源码可以在这里查看。最后得出的结果:
image
这说明,使用drawImage将canvas的时候,消耗了很大一部分性能,而且消耗的性能比绘制同等大小的图片要高得多。

绘制对象同分辨率,绘制不同尺寸的性能差别

image
这说明:绘制对象同分辨率,不同尺寸的情况下,对drawImage的性能影响不大

绘制对象不同分辨率,绘制同尺寸的性能差别

image
这说明:绘制对象同分辨率,不同尺寸的情况下,对drawImage的性能有影响,大概是成线性的关系

总结

  • 使用drawImage绘制一个canvas的时候,会比绘制普通的图片性能消耗高很多倍,所以合成canvas是一个高开销的操作
  • 使用离屏canvas尽量不要使用,可以使用分层canvas(即仍然在屏幕上显示几个canvas,但是是叠起来的)。
  • drawImage与绘制图片本身的尺寸有关,与绘制到屏幕上的大小有关。这就说我们尽量不要用高清的小图标。
  • 许多文章上写的分层cnavas,主要是适用于有很多元素不需要每一帧都render,这时候可以将其放在副屏,以节约渲染时间。如果没有很多元素不需要每一帧都render,就不要使用了,因为将canvas合成也需要不小的开支。

基于上面的这些探索,最终我将小游戏中的副屏撤下了,仅使用主屏进行渲染。

(完)

@buuing
Copy link

buuing commented Aug 16, 2023

针对测试代码来看, 有个小疑惑, 这个所谓的差100倍, 有没有可能是代码差异造成的

fill函数有个特性, 他填充的对象都会指向同一个引用地址, 所以我提议, 测试代码要改成

imgList = Array(10000).fill(img)
canvasList = Array(10000).fill(canvas)

下面是我修正后的测试结果

image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants