Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Canvas performance change in Chrome

I'm working on an animation library, and every once in a while I run a benchmark test to see how much of a gain or loss I get with certain features. Recently I've run into something that has me quite perplexed, perhaps someone with more knowledge can shine a light on this for me.

Performance Before:

  • Chrome: ~4460 sprites @ 30fps
  • Safari: ~2817 sprites @ 30fps
  • FireFox: ~1273 sprites @ 30fps
  • iPhone 4S: ~450 @ 30fps

Peformance Now:

  • Chrome: ~3000 sprites @ 30fps
  • Safari: ~2950 sprites @ 30fps
  • FireFox: ~1900 sprites @ 30fps (before Garbage Collection becomes too distracting)
  • iPhone 4S: ~635 @ 30fps

So you can see, Chrome took quite a hit in performance, while every other browser seems to have gotten a little better over this time frame. The biggest thing I notice, and what I'm figuring is the answer, is that the CPU usage seems to have been throttled back in Chrome (I swear before I could get up near 90%, now its maxing around 60%). The majority of the CPU is being used for the drawImage() call, and I'm not sure I can do anything to optimize that.

If its simply an issue where Chrome is now limiting my CPU usage, I'm fine with that.

Any insight would be greatly appreciated...

_s.Sprite.prototype.drawBasic = function() {
    var s = this.ctx;
    if(s.globalAlpha!=this._alpha) s.globalAlpha = this._alpha;

    var width = this.width;
    var height = this.height;
    var x = this._x;
    var y = this._y;

    if (_s.snapToPixel) {
        x = this._x + (this._x < 0 ? -1 : 0) | 0;
        y = this._y + (this._y < 0 ? -1 : 0) | 0;
        height = height + (height < 0 ? -1 : 0) | 0;
        height = height + (height < 0 ? -1 : 0) | 0;
    }


    var frame = this.sequence[this.frame] || 0;
    var sheetY = frame + (frame < 0 ? -1 : 0) | 0;
    var sheetX = (frame - sheetY) * this.spriteSheetX || 0;

    s.drawImage(this.bitmap.image, this.bitmap.frameRect.x2 * sheetX, this.bitmap.frameRect.y2 * sheetY, this.bitmap.frameRect.x2, this.bitmap.frameRect.y2, x - (width * this._scaleX) * this.anchorX, y - (height * this._scaleX) * this.anchorY, width * this._scaleX, height * this._scaleY);
    this.updateFrame();

};

UPDATE

So I downloaded an old version of Chrome (25.0.1364.5), and ran my benchmark test: Before

Then I reran in the most current version of Chrome: After

Clearly Chrome has changed. Was it on purpose? I don't know. You can see that in the old version of Chrome I've actually gained more performance over my original 4460 (+ ~400, my optimizations must have worked), but you can also see it lets me hover at 100% cpu usage. 2x cpu almost 2x object on the screen.

like image 239
ericjbasti Avatar asked Nov 11 '22 22:11

ericjbasti


1 Answers

Update

setInterval doesn't have the issue. Only happens with requestAnimationFrame. This finally makes so much sense. requestAnimationFrame already throttles things to 60fps, what I wasn't aware of, and can't seem to find any info on is that Chrome (others?) throttle it to 30 (60/2) and then 20 (60/3) and probably 15(60/4)... this keeps it in sync with 60hz, so you never end up with 40fps that looks strange because its out of sync with your screen refresh rate.

This explains a lot. I'm really enjoying the cpu savings this provides us.

Updated

An example without any of my code... http://www.goodboydigital.com/pixijs/canvas/bunnymark/ if you run this in Chrome... you will see the point when it jumps from ~60fps straight to 30fps. You can keep adding more bunnies, pixy can handle it... Chrome is throttling the fps. This is not how Chrome use to behave.


So I figured out whats going on here. It's not that performance has changed per say, I can still get 4800 objects on the screen at 30fps. What has changed seems to be the way Chrome tries to optimize the end users experience. It actually throttles things down from 60fps to ~30fps (29.9fps according to dev tools), which causes if(fps>=30) to return false:

    stage.onEnterFrame=function(fps){  // fps = the current system fps
        if(fps>=30){  // add astroids until we are less than 30fps
            stage.addChild(new Asteroid());
        }
    }

For some reason around 2800 objects, Chrome throttles down to 30fps instead of trying to go as fast as possible... So if I start the benchmark with 4800 objects, it stays at a wonderfully consistent 29.9fps.

fps meter

(you can see here that its either 60fps or 29.9fps no real in-between, the only thing that changes is how often it switches)

This is the code used for stage timing...

_s.Stage.prototype.updateFPS = function() {
    var then = this.ctx.then;
    var now = this.ctx.now = Date.now();
    var delta = now - then;
    this.ctx.then = now;
    this.ctx.frameRatio = 60 / (1000 / delta);
};

Hopefully this helps someone else down the road.

like image 81
ericjbasti Avatar answered Nov 14 '22 21:11

ericjbasti