ippa / jaws

Jaws - HTML5 canvas javascript 2D Game Framework
https://jawsjs.ippa.se
GNU Lesser General Public License v3.0
364 stars 73 forks source link

Suboptimal SpriteSheet code #15

Open download13 opened 13 years ago

download13 commented 13 years ago

I was looking through the Jaws code to see how it worked when I noticed that you store sprites as canvas elements.

Curious, I wrote a quick test to see what sort of performance each type of drawImage will get, and here are the results. As you can see, canvas is by far the worst performer. The BlobURL method seem to work very well in Chrome, but createObjectURL isn't compatible with all browsers.

If compatibility doesn't matter as much as speed, you might want to switch to using createObjectURL. Otherwise, data: URLs are still looking good.

After that, I thought it might be good to look into the performance difference of pre-cutting spritesheets as opposed to just leaving them one image and using drawImage(Object image, float sx, float sy, float sw, float sh, float dx, float dy, float dw, float dh) to pick out pieces of them at blit-time. Here's that test which seems to indicate that it doesn't matter which you use. Since both are the same speed, I would go with the dynamic lift as it doesn't take up extra memory for all the individual sprites, it only needs a reference to the existing spritesheet image asset.

ippa commented 13 years ago

Hey .. thanks for taking the time to give feedback, fix benchmarks and so on.

Regarding compability, I can't ignore IE9 as of now.

Regarding your first benchmark, canvas image wins for my current browser, chrome 15. But I can see it kind of sucks in chrome 16 and FF. Right now I convert all images to canvas as a designchoice cause chrome has always given me better performence like that... but it looks like that will change with chrome 16. I'll keep an eye on it to see if a change in jaws is warranted.

I guess it would be possible to browsersniff and use different methods depending on the browser to get ultimate performence. It would add complexity though.. and I wan't to wait with that until I release "1.0" with a settled API.

The spritesheet idea is intersting.. I think it would take some refactoring of various constructors, Sprite(), Animation(), SpriteSheet(). The functionallity of a standalone SpriteSheet()-constructor might become obsolete.

Except for eating less memory, there's another benefit. It would make Canvas and HTML animations more simular. Cause HTML animations are done with CSS background-offsets (currenty not supported by jaws) which is simular to how you suggest canvas-animations/spritesheets should work.

If this is something you would be interesting hacking on I would most likely accept a patch right away. I'm ippa @ freenode ircnetwork if you wanna chat about it.

download13 commented 13 years ago

I don't think it's something that I can do without making drastic changes to the API, so it really comes down to whether you like backwards compatibility. Some users might rely on getting a canvas back from functions that later return an image.

Plus, there's the need to completely refactor the way that animations are drawn. An update would just update the frame index while the draw function would need the current canvas or viewport passed as an argument so that it could draw directly from the CSS-style spritesheet to the canvas.

In my library the drawing system works by calling a static draw function on the main object and passing it an array of all sprites and animations to be drawn. That way there is very little getting between the spritesheet image and the drawImage that will be required to blit from it. It's easier there because it's a new library (I started it a few days ago, haven't even pushed yet) and no one is depending on it having a stable API.

PS: I've sent a pull request for something a little more immediately practical. Some simple modifications that make loops and float-to-int conversion faster. They're easy to implement, it's just a little mind-numbing to have to wade through all the code making the changes.

PPS: Something else to remember for performance purposes. Function calls in JS are very expensive and slow. Try to minimize the number of function calls you make during loops. That's why I use the static draw with an array of drawables passed to it. It's only one function call in total instead of one for each item to draw.

izb commented 11 years ago

I stumbled across this issue and the performance boost with createObjectURL seems a little hard to believe. It seems that this jsperf page has been looked at before in stackoverflow:

http://stackoverflow.com/questions/13328184/explain-massive-performance-difference-in-html-canvas-image-drawing

This seems to suggest that the performance boost is actually due to the test throwing errors in chrome rather than actually painting the image.