我正在编写一个JavaScript游戏。这个游戏显然需要不断渲染一个屏幕,使用画布,它必须是Uint8Array(width * height * 4)
(由Canvas的ImageData
使用)。为了测试预期的FPS,我尝试用白色填充该阵列。令我惊讶的是,表现为mediocre。我几乎不能在150 fps的高端计算机上填充1000x1000位图和白色。考虑到这是最佳性能,没有任何游戏逻辑运行,最终结果可能会低得多。我的问题是:为什么表现如此之低,我该怎么做才能改善它?
答案 0 :(得分:2)
确定使用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
这样的功能之一特别慢,特别是考虑到他们使用的是双打。
答案 1 :(得分:1)
参见这两项测试
仅执行循环并分配到同一地址
http://jsperf.com/variable-assign
良好的旧数组扩展(以及另一种流行的for循环技巧,假设更快)
http://jsperf.com/fill-an-type-array-expand/3
首次测试显示地址查找大约需要3/4的时间。
第二次测试显示for循环占用的时间超过30%。
我认为什么类型的数组真正缺乏的是一些本地代码执行块复制,它实际上将用于游戏开发(而不是在测试中逐像素设置)。 WebGL可能值得考虑。