I was so excited running all my tests and seeing how fast it all went that I forgot to test in Firefox. Big mistake, I needed to check that my weird findings held in FF too, like how to best clear the canvas, how to best draw, etc.
I ran all my tests again in both Chrome and FF, and this is what I found:
(a bit different from the previous results)
|Chrome 15||Firefox 8||Thoughts|
|First drawing test||295 FPS||311 FPS||Wow! Firefox is faster than Chrome! Nice|
|Not clearing canvas||312 FPS||314 FPS||Not much difference anymore. Maybe it’s just clearing the canvas that’s faster, not drawing|
|ClearRect vs FillRect||283 FPS||316 FPS||Wow, so clearRect is faster in FF, fillRect faster in Chrome. I probably won’t be clearing my canvas anyway, and even if I were to, this difference is probably not enough to branch the code|
|“Canvas width trick”||223 FPS||244 FPS||Consistently slower. Where is it that the width trick is a time saver?|
|Individual Tile canvas||316 FPS||17 FPS||Uh-Oh! This is a huge problem… I’ll need to experiment deep on this, because I was definitely going the individual canvas route. What the hell??|
|1000 canvases||226 FPS|
|20 canvases||311 FPS|
|Drawing outside||1043 FPS|
So… That’s no good. (And good thing I remembered to test FF now, rather than a week later when I would have no clue what the hell was slower)
I made a couple little changes to make sure I wasn’t missing anything stupid, and apparently I wasn’t. It’s also not the invidivual canvases or the “shorter overload” version of drawImage that’s the problem. Firefox is just immensely slower drawing from canvas to canvas, than drawing from image to canvas.
Googling around or asking didn’t really give me much to go on…
I still wasn’t sure what the exact problem was, so I decided to test whether the problem was that this was a canvas (instead of an image), or the problem was that I generated this image myself, somehow…
The only way I could think of testing this was first making my little canvas, and then using toDataURL() to convert it to an image, and then blit from that image.
Problem is… That didn’t work at all the way I was testing, because I just had an HTML file on disk that I’d open (file://), and toDataURL() pukes a huge security exception when you try to use it on a page that loaded over file://.
Fine! I’ll set up a new vhost and test…
And after a bit of work, it worked perfectly. I created a canvas the size of one of my tiles, blitted the tile from the sprite sheet to it, converted it to an image using toDataURL(), and then blitted thousands of times from that newly created image to the “screen” canvas.
That gave me 315 FPS, making it a little faster than blitting from the sprite sheet, just like I had been expecting given my Chrome results. Not as much performance gain as I had in Chrome, but this was still the preferred method.
Suspicion confirmed… Firefox sucks ass at blitting from canvas to canvas. I have no idea why, haven’t found any information about it, and I have no reasonable theory, it’s just 20 times slower.
Good thing I found a workaround! Not just because of the “individual tiles” case, but because I’ll need to manipulate some stuff sometimes, I won’t always be able to blit straight from the image I load from the server.
Firefox solved, let’s go back to Chrome and see if blitting from these “dataURL” images is as fast as from a canvas.
Old baseline benchmark: 316 FPS
Drawing from “dataURL” images: 120 FPS (shit!)
Drawing from my individual canvases: 316 FPS (phew)
Drawing from the original sprite sheet, as I was doing at the beginning: 119 FPS
I triple-checked… That code didn’t change at all. Drawing from the sprite sheet used to give me 295 FPS. Now it gives me 119. What the hell just happened here?
Took a while to figure that out, but… HTTP. That’s what happened.
As soon as I started loading from a webserver instead of file://, image-to-canvas drawing instantly started taking twice as long as before.
I’m guessing it’s slowed down by security checks to make sure you’re not doing any cross-domain shenanigans.
However, canvas-to-canvas performance remained the same. If I take my sprite sheet, blit it to a canvas just as big as it, and draw from that canvas, I get my original 295 FPS. So canvas-to-canvas is exactly the same for http:// or file://
I couldn’t find any justification for this by googling around. I’m guessing that when you draw to a canvas, Chrome security checks the originating image to see if you can draw there, and then when you’re blitting from canvas “it knows it’s fine”?
I’m surprised it doesn’t “cache” the fact that the image is fine and it stops checking… I’m also surprised that this would take so much time, compared to actual drawing. And I don’t even know what the hell is going on in there, it’s all just unfounded speculation…
Whatever the reason, my small little canvas kept working just as fast, so whatever, I’m just happy it works.
The sucky part is that there is no solution that’ll work fast in both Chrome and> Firefox.
So, the apparent conclusion is: When parsing my maps and loading sprites: I chop those into individual canvases, one per cell, and if I’m in Firefox, I convert those canvases to images using “toDataURL()”. The UA sniffing kind of sucks, however, wherever I was going to store the reference to the individual canvases, I now simply store the reference to these individual images instead, and the rest of the codebase is absolutely identical, so it’s not *too* bad, it’s a very localized change.
The other verdict is that it looks like I have no choice but make a better testing page. The plan was always to be able to test on cellphones, which I can’t with this test page as it is, but now that I found this gigantic difference between browsers, I definitely have to.