2Di绘图框架(如Pixi.js)如何使画布绘制更快?

时间:2013-11-30 07:04:08

标签: javascript html5 canvas

我为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使用率。

不幸的是,我的知识在这里结束,我很好奇是否有人可以了解最新情况。

3 个答案:

答案 0 :(得分:8)

我认为,在我看来,它归结为代码的“可编译”(可缓存)。我们知道Chrome和Firefox使用两种不同的JavaScript“编译器”/引擎,它们以不同的方式优化和缓存代码。

画布操作

使用变换与直接坐标不应产生影响,因为设置变换只会更新矩阵,无论如何都会使用矩阵。

位置值的类型会影响性能,floatinteger值,但由于你的小提琴和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:

enter image description here

和~32-35 FPS on Fiddle:

enter image description here

这一切都在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)?

我不认为它是一个对象属性访问时间/可编辑性的东西:这一点是有效的,但我认为它不能解释那么大的差异。