我正在编写JavaScript游戏。显然,该游戏需要不断渲染一个屏幕,该屏幕使用画布必须是Uint8Array(width * height * 4)
(由Canvas的ImageData
使用)。为了测试预期的FPS,我尝试用白色填充该阵列。令我惊讶的是,性能为mediocre。在150 fps的高端计算机上,我几乎无法用白色填充1000x1000位图。考虑到这是最佳性能,而无需运行任何游戏逻辑,最终结果可能会低得多。我的问题是:为什么性能这么低,我应该怎么做才能改善它?
最佳答案
确定使用putImageData可以填充1000x1000画布的次数不会给您任何真实的结果。原因是图形流水线化。在普通应用中,您只需要在一个帧中调用一次putImageData。如果在某个时刻多次调用它,您将填满管道并将其停顿。在真正的应用程序中,尽管您会在大部分帧中处理数据,并且只上传一次,但不会使管道停滞不前。
如果要查看可以执行的工作,请制作一个1000x1000的画布,在其上调用getImageData以获取一个ImageData,对该图像数据进行一定量的处理,然后调用putImageData,然后调用requestAnimationFrame并再次执行。慢慢增加操作量,直到开始运行速度低于60fps。这将告诉您可以实际完成多少工作。
这是一个小提琴尝试
http://jsfiddle.net/greggman/TVA34/
另外,使用putImageData也有一些问题。一种是Canvas需要预乘Alpha,而putImageData提供未预乘Alpha,这意味着某些过程必须将您的数据转换为预乘Alpha。然后,如今,大多数Canvas实施都是GPU加速的。这几乎适用于Canvas API的所有功能,但是它会吸收putImageData,因为该数据必须从CPU传输到GPU。对于getImageData来说更糟,因为将数据从GPU复制回CPU通常会使GPU停顿。在HD-DPI机器上的故事甚至更糟。 getImageData和putImageData必须从CSS像素转换为实际使用的分辨率。
如果使用WebGL,则至少可以跳过预乘alpha转换步骤。
这是WebGL版本
http://jsfiddle.net/greggman/XLgs6/
有趣的是,在我的2012 Macbook Pro Retina上,我发现画布版本在Chrome和Safari上速度更快。我很好奇为什么会这样,因为我不希望在他们身上工作过。
/*
Canvas WebGL
Chrome 32 : 710k 650k numbers are in 'operations per frame`
Firefox 26 : 80k 190k
Safari 7.0.1 : 150k 120k
*/
我的测试也可能无效。仅处理710k像素(1000k像素)似乎很慢。也许
Math.random
或Math.floor
之类的功能之一特别慢,特别是考虑到它们正在使用double的情况下。