Friday, 26 April 2013

Fullscreen 60 FPS - The ultimate mobile milestone

60 FPS is that magic number that makes gaming look and feel great, but achieving this on mobile devices has been a rather large challenge for a game that has fullscreen, graphically heavy gameplay.

There were a number of ways I tried to get it running at 60 FPS on iPad 2 / iPhone 4 and after the 4th attempt I've cracked it!

Attempt 1 & 2:
Firstly I started out thinking that all I have to do is create a bitmap the size of the screen and then let the built in actionscript "bitmap.scroll" method do the leg work. But no this was probably one of the worse results I received during the tests (around 21 - 25 FPS on iPad mini). Shortly after this I thought maybe making the bitmap size a power of two, so rather than 1024*768, 1024*1024, but nope, still no good, in fact worse.

I assumed that the GPU would automatically handle all the lengthly calculations - which is does in a sense but the blasted Flash DisplayList kept on rending it's dirty regions - filthy displaylist!

Attempt 3:
I then looked into using Stage3D (AKA Molehill), as using the stage3d features in AIR on mobile directly utilises the GPU. So the theory is that I generate a quad from two triangles and texture the quad with bitmapdata.

It worked a dream, but it was the "uploadTextureFromBitmap()" method that killed it this time. Even though there was no display list being used, parsing the bitmap data each frame to the texture on the quad was still too heavy but was a vast improvement - 31 - 40 FPS.

Attempt 4:
So scrolling bitmapdata is too heavy as the fullscreen displaylist has to be updated every frame, stage3D doesn't quite meet the mark - what's next? Fullscreen? does it need to be? Yes, but wait...

I went back to the original method but this time rather than scrolling the bitmapdata, why not scroll a bitmap object- or more accurately, why not scroll multiple bitmaps objects? Turns out creating more bitmaps was the key.

I've created a grid/array of bitmaps, each bitmap being 256*256, enough to fill more than the screen, each bitmap object is then cached as a bitmap (bare with me). This means that when the bush is over a section of this grid only a 256*256 section of the screen is being updated because of the display list, the rest is cached (because i set the object to be cached) and handled directly by the GPU. 60 FPS - BOOM!

All that was left to do is to reposition the bitmaps when they go offscreen to wrap around again so stop the need to create new ones every time a new row or column of bitmaps is required.

I've put together a short video to demonstrate how the new method is working (also offset the bitmaps so you can see that they are being removed and repositioned, and also coloured them so you can see the individual bitmaps themselves).

The video is recorded at 30 FPS, but a promise you it's running at 60 =).