mraak

What makes Pixi faster than Easel?

Recommended Posts

I have to admit I was impressed with the bunny mark example, and I was also a bit unimpressed by Easel performance issues on lower resource devices on the last project. Since we have quite a few games in the works, I would like to know what, if anything, would make Pixi faster than Easel, and if there are any head to head tests done.

 

Cheers!

Share this post


Link to post
Share on other sites

There was a very recent head to head test done on a Japanese blog. Pixi was clearly in the lead and it didn't even cover the latest version with SpriteBatches and other optimisations. Why is it faster? better code, better structure and better use of WebGL. However they're also quite different, so it's not a completely fair comparison - Easel offers a 'short hand' way of drawing onto a canvas in a way that Pixi doesn't. I'm not sure how useful that actually is given how easy the canvas API is to use, but there we have it. So it's not a direct apples to apples comparison really.

Share this post


Link to post
Share on other sites

With regards to the canvas renderer, I recently extracted that part from both libraries and found Pixi to be the smarter one for a few major reasons. The biggest gain was that Pixi never calls context.save() or context.restore(). Those are some very expensive functions, and Easel calls these for every single sprite. Big no-no.

 

The other reason is because Pixi has Sprites directly reference their parent matrix transforms and run with that. Easel instead runs with whatever transform came before it, because it uses save/restore. But I found this to be a poor design that might lead to some potential issues. Pixi's general architecture is much safer, though it is more expensive (two full loops, one for updateTransform and another for render, while Easel just has one), it makes up for this by never calling save/restore on the context.

 

Another reason is that Easel's event/input model is, quite frankly, bad. There's a ton of dispatches, ferrying around, and terrible flaws with hit detection, while Pixi's "interaction manager" handles input a little more intelligently, in a way that makes more sense for canvas. However, I haven't dug into Pixi's input stuff yet so I could be wrong! I've only brushed over it as I ripped through the lib recently.

 

I know nothing about their webgl implementations, but judging by what I've seen so far, I trust Pixi more ;)

 

The downside of Pixi is that a ridiculous amount of local variables are created in hot functions like updateTransform and getBounds. For smaller devices this is the single biggest reason you see hitches--it's thrashing the garbage collector because it has to run quite often in order to clean up that mess. It also causes a great deal of memory fragmentation since it's allocating/unallocating frequently, and I don't know what devices you run with, but this was a huge problem for us (several random crashes, often, which were fixed by never allocating anything after initialization--something ALL game devs should have been doing anyway!).

 

For the reason above, I had to strip out the canvas part of Pixi and wrapped it in my own library. I was seeing way too many slowdowns and some hitching, although it was waaay worse with Easel. And this both is on my gaming rig and Macbook D:

 

Anyway, this topic was rather timely :) Hope I explained things well enough, and that it actually made sense. I'm not exactly active here, so I apologize if you have questions that I'll never answer.

Share this post


Link to post
Share on other sites

The downside of Pixi is that a ridiculous amount of local variables are created in hot functions like updateTransform and getBounds. For smaller devices this is the single biggest reason you see hitches--it's thrashing the garbage collector because it has to run quite often in order to clean up that mess. It also causes a great deal of memory fragmentation since it's allocating/unallocating frequently, and I don't know what devices you run with, but this was a huge problem for us (several random crashes, often, which were fixed by never allocating anything after initialization--something ALL game devs should have been doing anyway!).

 

This is very interesting and something I've not put much thought into. We've run into random crashes on older mobiles, I wonder if this is the reason. Makes sense to initialise all your variables into a pool of sorts and reuse them - obviously this is done more easily in lower level languages as you can allocate memory manually, but do you have any recommendations on how to cleanly implement this in Javascript? Or is it just a case of always initialising all local variables at the start? What about when you need functional scope, is it ok to initialise new variables then?

Share this post


Link to post
Share on other sites

getBounds is only called when you need it (not every frame), so the impact of that isn't much of an issue - but I agree it could be optimised further.

 

updateTransform creates 14 local variables, 8 of which aren't needed and the rest could be stored in a local cache, per object, per frame. However it's trivial to replace this single function with a more optimised one. I doubt it's going to lead to any kind of dramatic gain, but it will help with gc spikes for certain.

 

Almost certainly won't stop crappy mobiles from crashing though.

Share this post


Link to post
Share on other sites

The downside of Pixi is that a ridiculous amount of local variables are created in hot functions like updateTransform and getBounds. For smaller devices this is the single biggest reason you see hitches--it's thrashing the garbage collector because it has to run quite often in order to clean up that mess. It also causes a great deal of memory fragmentation since it's allocating/unallocating frequently, and I don't know what devices you run with, but this was a huge problem for us (several random crashes, often, which were fixed by never allocating anything after initialization--something ALL game devs should have been doing anyway!).

 

For the reason above, I had to strip out the canvas part of Pixi and wrapped it in my own library. I was seeing way too many slowdowns and some hitching, although it was waaay worse with Easel. And this both is on my gaming rig and Macbook D:

 

Why not contribute back the optimizations you made to make pixi better for everyone?

Share this post


Link to post
Share on other sites

Why not contribute back the optimizations you made to make pixi better for everyone?

 

From the looks of things, those micro-optimisations didn't do much, as his 'optimised' pixi lib seems to be waaaay more stripped down than what he suggested - ie missing pretty much every feature besides drawing an image file to the screen... No filters, no masking, etc. Still curious to see how much of a difference the micro-optimisations would make, though.

Share this post


Link to post
Share on other sites

Lewis is correct, the amount of work I did is rather pithy and it'd be silly to do a pull request for it, especially when Mat would probably have a better idea for his framework to make it thrash less anyway. It's "optimized" because, yes, it does nothing fancy. Less work = faster.

 

This is very interesting and something I've not put much thought into. We've run into random crashes on older mobiles, I wonder if this is the reason. Makes sense to initialise all your variables into a pool of sorts and reuse them - obviously this is done more easily in lower level languages as you can allocate memory manually, but do you have any recommendations on how to cleanly implement this in Javascript? Or is it just a case of always initialising all local variables at the start? What about when you need functional scope, is it ok to initialise new variables then?

 

I use a couple linked lists (one for free objects, one for used), that's initialized by a certain amount on init, but will grow as needed at runtime (if needed at all). I use lists because they're way faster when dealing with huge pools where objects are added and removed quite often. I have an example you can look at here. In the constructor is this._pool, and it's that DualPool class that does the management. There are a ton of resources on object pooling, and you can apply any code you find from ActionScript3/Flash as well.

Share this post


Link to post
Share on other sites

Can you talk more about which devices are getting the GC issues due to local variables?

I was under the impression that most modern engines would be smart enough to optimize away local primitive types in "hot" functions (it does not lead to any visible allocations in Chrome). So I guess this is mainly an issue for much older devices.

Share this post


Link to post
Share on other sites

One thing I noticed was that, by default, EaselJS uses setTimeout for its animation loop instead of RAF unless you change the Ticker.timingMode. That has a SERIOUS impact on the performance of EaselJS on most systems. I found them to perform pretty much the same on the destop and all my mobile devices after I made that one simple change. Before I did that, I thought EaselJS was brain dead.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Recently Browsing   0 members

    No registered users viewing this page.