我为Javascript画布here找到了一个兔子标记。
当然,我知道他们的默认渲染器使用的是webGL,但我现在只对本机2D上下文性能感兴趣。我在firefox上禁用了webGL,在产生16500个兔子之后,计数器显示了25的FPS。我决定编写自己的一个非常简单的渲染循环,看看Pixi添加了多少开销。令我惊讶的是,我只有20的FPS。
我大致相当于JSFiddle。
所以我决定查看他们的来源here,看起来并不是魔术在他们的渲染代码中:
do
{
transform = displayObject.worldTransform;
...
if(displayObject instanceof PIXI.Sprite)
{
var frame = displayObject.texture.frame;
if(frame)
{
context.globalAlpha = displayObject.worldAlpha;
context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]);
context.drawImage(displayObject.texture.baseTexture.source,
frame.x,
frame.y,
frame.width,
frame.height,
(displayObject.anchor.x) * -frame.width,
(displayObject.anchor.y) * -frame.height,
frame.width,
frame.height);
}
}
奇怪的是,他们似乎正在使用链接列表进行渲染循环,两个应用程序上的配置文件显示,虽然我的版本为每帧分配相同数量的CPU时间,但它们的实现显示了尖峰中的CPU使用率。
不幸的是,我的知识在这里结束,我很好奇是否有人可以了解最新情况。
答案 0 :(得分:8)
我认为,在我看来,它归结为代码的“可编译”(可缓存)。我们知道Chrome和Firefox使用两种不同的JavaScript“编译器”/引擎,它们以不同的方式优化和缓存代码。
使用变换与直接坐标不应产生影响,因为设置变换只会更新矩阵,无论如何都会使用矩阵。
位置值的类型会影响性能,float
与integer
值,但由于你的小提琴和PIXI似乎只使用浮点数这不是关键这里。
所以在这里我不认为画布是造成差异的原因。
(我在这个答案的第一个版本中无意中过于关注原型方面。我试图得到的本质主要是对象遍历,所以这里的文字是重写的有点 - )
PIXI使用对象属性作为小提琴,但PIXI中的这些自定义对象的大小较小,因此与遍历较大对象(如画布或图像)所需的时间相比,遍历对象树需要的时间更短(如width
也将在此对象的末尾。)
由于这个原因(遍历时间),这是一个众所周知的经典优化技巧来缓存变量。由于引擎变得越来越智能,所以今天的影响更小,特别是Chrome中的V8似乎能够在内部更好地预测/缓存这一点,而在Firefox中似乎仍然有一些影响,不能在代码中缓存这些变量。
性能方面是否重要?对于简短的操作很少,但是在画布上绘制16,500个兔子是非常苛刻的,并且做从这样做中获益(在FF中)所以任何微优化实际上都会计入这样的情况。
我将“渲染器”原型化为更接近PIXI以及缓存对象属性。这给Firefox带来了性能突破:
http://jsfiddle.net/AbdiasSoftware/2Dbys/8/
我使用了一台慢速计算机(以扩大影响力),以大约5 FPS的速度运行你的小提琴。缓存后,它以6-7 fps的速度运行,这台计算机的增加超过20%,显示它确实有效。在具有较大CPU指令缓存等的计算机上,效果可能会更少,但它存在,因为这与FF引擎本身有关(免责声明:我并不是说这是一个科学测试,但只有一个指针: - ))。
/// cache object properties
var lastTime = 0,
w = canvas.width,
h = canvas.height,
iw = image.width,
ih = image.height;
下一个版本将这些变量缓存为对象(本身)的属性,以表明与直接使用大型全局对象相比,这也提高了性能 - 结果与上述相同:
http://jsfiddle.net/AbdiasSoftware/2Dbys/9/
var RENDER = function () {
this.width = canvas.width;
this.height = canvas.height;
this.imageWidth = image.width;
this.imageHeight = image.height;
}
我确信基于结果和以前的经验,PIXI可以更快地运行代码,因为使用自定义的小型对象而不是直接从大型对象(元素)(如画布和图像)获取属性。
对于树和分支的对象遍历,FF引擎似乎还没有像V8引擎一样“智能”,因此缓存变量确实会对FF产生影响,当需求很高时(例如当每个“框架”绘制16,500个兔子。
答案 1 :(得分:5)
我注意到你的版本与Pixi之间的一个区别是:
通过将x / y直接传递到drawImage
函数:
drawImage(img, x, y, ...);
..而Pixi翻译整个画布上下文,然后在0/0(已经移位的上下文)中绘制图像:
setTransform(1, 0, 0, 1, x, y);
drawImage(img, 0, 0, ...);
他们还将更多参数传递给drawImage
; arguments that control "destination rectangle" - dx , dy , dw , dh 。
我怀疑这是速度差异隐藏的地方。但是,更改测试以使用相同的“技术”doesn't really make things better。
但还有别的......
我将兔子打到5000,禁用了WebGL,而Pixi实际上比自定义小提琴版本更差。
我在Pixi上获得了~27 FPS:
和~32-35 FPS on Fiddle:
这一切都在Chrome 33.0.1712.4 dev,Mac OS X上。
答案 2 :(得分:1)
我怀疑这是一些画布合成问题。默认情况下,Canvas是透明的,因此页面背景需要与画布内容结合...
我在他们的来源中找到了这个......
// update the background color
if (this.view.style.backgroundColor != stage.backgroundColorString &&
!this.transparent) {
this.view.style.backgroundColor = stage.backgroundColorString;
}
也许他们设置画布对于这个演示是不透明的(小提琴并不适合我,似乎大多数的bunnys大部分时间跳出一个非常大的dt)?
我不认为它是一个对象属性访问时间/可编辑性的东西:这一点是有效的,但我认为它不能解释那么大的差异。