像Pixi.js这样的2D绘图框架如何使画布绘制更快?

No *_*ing 16 javascript html5 canvas

我在这里找到了Javascript画布的兔子标记.

当然,我知道他们的默认渲染器使用的是webGL,但我现在只对本机2D上下文性能感兴趣.我在firefox上禁用了webGL,在产生了16500个兔子之后,计数器显示了25的FPS.我决定编写自己的一个非常简单的渲染循环,看看Pixi添加了多少开销.令我惊讶的是,我的FPS只有20.

我大致相当于JSFiddle.

所以我决定在这里查看它们的源代码,它似乎并不是魔术在它们的渲染代码中:

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);
        }                      
    }
Run Code Online (Sandbox Code Playgroud)

奇怪的是,似乎他们正在使用链接列表进行渲染循环,两个应用程序上的配置文件显示,虽然我的版本为每帧分配相同数量的CPU时间,但它们的实现显示了尖峰中的CPU使用率.

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

小智 8

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

画布操作

使用变换与直接坐标不应产生影响,因为设置变换只是更新矩阵,在任何情况下矩阵都与其中的任何内容一起使用.

相对于值,位置值的类型可以影响性能,但是因为你的小提琴和PIXI似乎都使用浮点数,所以这不是关键.floatinteger

所以在这里我不认为画布是造成差异的原因.

变量和属性缓存

(我在这个答案的第一个版本中无意中过于专注于原型方面.我试图得到的本质主要是对象遍历,所以下面的文字有点重新措辞 - )

PIXI使用对象属性作为小提琴,但PIXI中的这些自定义对象的大小较小,因此与遍历较大对象(如画布或图像)所需的时间相比,遍历对象树所花费的时间更少(如同属性width也是如此)在这个对象的最后).

由于这个原因(遍历时间),这是一个众所周知的经典优化技巧来缓存变量.由于引擎变得更加智能,所以今天的影响更小,特别是Chrome中的V8似乎能够在内部更好地预测/缓存这一点,而在Firefox中似乎仍然有一些影响,不能在代码中缓存这些变量.

性能方面是否重要?对于短期操作很少,但是拉丝16500只兔子在画布上要求严格,获得从这样做(在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;
Run Code Online (Sandbox Code Playgroud)

下一个版本将这些变量缓存为对象(本身)的属性,以显示与直接使用大型全局对象相比,这也提高了性能 - 结果与上面相同: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;
}
Run Code Online (Sandbox Code Playgroud)

结论

我确信基于结果和以前的经验,PIXI可以更快地运行代码,因为使用自定义的小型对象而不是直接从大型对象(元素)(如画布和图像)获取属性.

对于树和树枝的物体穿越而言,FF引擎似乎还没有像V8引擎一样"智能",因此缓存变量确实会对FF产生影响,当需求很高时(例如绘制16,500个兔子时)每"框架").


kan*_*gax 5

我注意到你的版本和Pixi之间的一个区别是:

通过将x/y直接传递给drawImage函数,可以在特定坐标处渲染图像:

drawImage(img, x, y, ...);
Run Code Online (Sandbox Code Playgroud)

..其中Pixi翻译整个画布上下文,然后在0/0(已经移位的上下文)绘制图像:

setTransform(1, 0, 0, 1, x, y);
drawImage(img, 0, 0, ...);
Run Code Online (Sandbox Code Playgroud)

他们还传递更多的论点drawImage; 控制"目标矩形"的参数 - dx,dy,dw,dh.

我怀疑这是速度差异隐藏的地方.但是,将测试更改为使用相同的"技术" 并不会让事情变得更好.

但还有别的......

我把兔子打到了5000,禁用了WebGL,而且Pixi实际上比自定义小提琴版表现更差.

我在Pixi上获得了~27 FPS:

在此输入图像描述

和小提琴上的32-35 FPS:

在此输入图像描述

这一切都在Chrome 33.0.1712.4 dev,Mac OS X上.