Javascript 像Pixi.js这样的2D绘图框架是如何使画布绘图更快的?
我为Javascript画布找到了一个bunnymark 当然,我知道他们的默认渲染器使用的是webGL,但我现在只对本地2D上下文性能感兴趣。我在firefox上禁用了webGL,在生成16500只兔子后,计数器显示FPS为25。我决定编写自己的非常简单的渲染循环,看看Pixi增加了多少开销。令我惊讶的是,我的FPS只有20 我的想法大致相当 因此,我决定研究一下它们的源代码,它们的渲染代码似乎并没有什么神奇之处:Javascript 像Pixi.js这样的2D绘图框架是如何使画布绘图更快的?,javascript,html,canvas,Javascript,Html,Canvas,我为Javascript画布找到了一个bunnymark 当然,我知道他们的默认渲染器使用的是webGL,但我现在只对本地2D上下文性能感兴趣。我在firefox上禁用了webGL,在生成16500只兔子后,计数器显示FPS为25。我决定编写自己的非常简单的渲染循环,看看Pixi增加了多少开销。令我惊讶的是,我的FPS只有20 我的想法大致相当 因此,我决定研究一下它们的源代码,它们的渲染代码似乎并没有什么神奇之处: do { transform = displayObject.wo
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使用情况
不幸的是,我的知识到此为止,我很好奇是否有人能解释一下发生了什么。我注意到你的版本和Pixi的版本之间的一个区别是: 通过将x/y直接传递到
drawImage
函数,可以在特定坐标处渲染图像:
drawImage(img, x, y, ...);
..而Pixi转换整个画布上下文,然后在0/0(已移位的上下文)处绘制图像:
它们还将更多参数传递给drawImage
;-dx,dy,dw,dh
我怀疑这就是速度差隐藏的地方。但是,将测试更改为使用相同的“技术”
但还有别的事
我记录了5000只兔子,禁用了WebGL,Pixi的性能实际上比定制的小提琴版本更差
我在Pixi上获得约27 FPS:
小提琴上每秒32-35帧:
这都是在Chrome 33.0.1712.4开发版Mac OS X上完成的。我认为,这可以归结为代码的“可编译”(可缓存)程度。Chrome和Firefox使用两种不同的JavaScript“编译器”/引擎,我们知道这两种引擎对代码进行不同的优化和缓存 画布操作 使用变换与直接坐标不应产生影响,因为设置变换只会更新矩阵,而矩阵在任何情况下都与矩阵中的任何内容一起使用 虽然位置值的类型可能会影响性能,
float
与integer
值相比,但由于您的小提琴和PIXI似乎都只使用浮动,这并不是关键
所以这里我不认为画布是造成差异的原因
变量和属性缓存
(在这个答案的第一个版本中,我无意中过于关注原型方面。我试图了解的本质主要是对象遍历,因此下面的文本有点重新措辞-)
PIXI使用对象属性作为提琴,但PIXI中的这些自定义对象的大小较小,因此与遍历较大对象(如画布或图像)相比,遍历对象树所需的时间更少(如宽度属性也将位于此对象的末尾)
由于这个原因(遍历时间),缓存变量是众所周知的经典优化技巧。如今,随着引擎变得更加智能,这种影响变得越来越小,尤其是Chrome中的V8引擎,它似乎能够更好地在内部预测/缓存这些变量,而在Firefox中,不在代码中缓存这些变量似乎仍然有一定的影响
这对性能有影响吗?对于短期操作来说很少,但在画布上绘制16500只兔子是一项艰巨的任务,并且确实可以从中获益(在FF中),因此在这种情况下,任何微观优化都是非常重要的
演示
我将“渲染器”原型化,以便更接近PIXI,并缓存对象属性。这在Firefox中带来了性能爆发:
我用了一台速度慢的电脑(来衡量影响),它以每秒5帧的速度运行你的小提琴。在缓存值后,它以6-7 fps的速度运行,这在这台计算机上增加了20%以上,表明它确实有效果。在具有更大CPU指令缓存等的计算机上,效果可能会更小,但它确实存在,因为这与FF引擎本身有关(免责声明:我并不声称这是一项科学测试,但是,只是一个指针:-))
下一个版本将这些变量缓存为对象(自身)的属性,以表明与直接使用大型全局对象相比,这也提高了性能-结果与上面的结果大致相同:
总之
根据结果和以前的经验,我确信PIXI可以更快地运行代码,因为它使用定制的小尺寸对象,而不是直接从画布和图像等大对象(元素)获取属性
在对象遍历树和分支方面,FF引擎似乎还没有V8引擎那么“聪明”,因此缓存变量确实会对FF产生影响,当需求较高时(例如每“帧”绘制16500只兔子时),FF就会显示出来。我怀疑这是一些画布合成问题。默认情况下,画布是透明的,因此页面背景需要与画布内容相结合
我在他们的资料里找到了这个
// update the background color
if (this.view.style.backgroundColor != stage.backgroundColorString &&
!this.transparent) {
this.view.style.backgroundColor = stage.backgroundColorString;
}
也许他们在这个演示中将画布设置为不透明的(小提琴对我来说不太管用,似乎大多数的兔子在大多数情况下都会跳出一个非常大的dt)
我不认为这是一个对象属性访问时间/可编译性的问题:这一点是正确的,但我不认为它能解释这么大的差异。这很有趣,在Chrome 31上,我的小提琴版本有16500只兔子,每秒15帧,Pixi版本有每秒10帧。然而,对于Firefox,Pixi更快。而且它似乎并不是说drawImage(img,0,0)
比drawImage(img,x,y)
快。我不太明白为什么人们会对这个答案投反对票。即使它不能100%回答这个问题,这也是一个有洞察力的分析-/这很有道理。你对这件事有什么意见吗
/// cache object properties
var lastTime = 0,
w = canvas.width,
h = canvas.height,
iw = image.width,
ih = image.height;
var RENDER = function () {
this.width = canvas.width;
this.height = canvas.height;
this.imageWidth = image.width;
this.imageHeight = image.height;
}
// update the background color
if (this.view.style.backgroundColor != stage.backgroundColorString &&
!this.transparent) {
this.view.style.backgroundColor = stage.backgroundColorString;
}