Evaluating spritesheet animations on mobile
Recently, after haxeCon in May I got really exited to try some mobile development possibilities with Haxe and NME (many thanks for really inspring session and source code sharing to Tarwin from Touchmypixel!). The performance of displaying animations (based on spritesheets) was very low at first so I decided to take a closer look at it. I’ve implemented a small spritesheet rendering/animation framework as part of my personal library and decided to compare some results. This is how I got into writing those few tests.
I’ve run all tests on HTC Nexus One with Android 2.3 with AIR 18.104.22.1688 installed. Used haxe (2.07) and latest NME (svn revision 681) to compile and also generate action script 3.0 source that I then compiled with Flash Professional CS 5.5 with AIR 2.7 SDK (See: Overlaying AIR SDK for Flash Professional CS5.5). On each test the desired frame rate is set to 60.
Test 1 – 30×50 animated sprites
Sprite sheet looks like this: . It is a simple 64×64 spritesheet that I made from brand new haXe logo for a proof-of-concept game I recently developed (more on that soon…). Test scenario is as follow: place 30 rows x 50 columns of 16×16 animating sprites made of that spritesheet on stage, play the animation starting from frame 16 modulo spriteNumber. Observe frame rate-and that’s it. Here is a flash version to check how it should look like.
- Flash/AIR naive approach (using movieclips) – I’ve just create a movieclip with 16 frames (each contain a single 16×16 bitmap) and place them on stage (just few lines of code on timeline..) = very unstable at first, settles at 7-8 FPS CPU (GPU completly unstable…)
- Corona sprites – same test implemented with Corona sprite API = I expected something more… 3 FPS
- Flash/AIR with copyPixels on seperate (1500) bitmap instances added to display list = not bad, 12-13 FPS on CPU (starting to look ok, on however GPU it 1 fps and hangs…)
- NME similar as above (just not copyPixels because it is really slow on NME but beginBitmapFill instead and 1500 Shape instances) - honestly I hoped for some more.. 5 FPS
All above approaches have animated sprite as a separate display object (movieclip and bitmap for AIR, shape for NME and corona sprites). This means that we could hitTest, depth sort and use all those goodies that particular api provides. Now let’s focus on performance and use rendering technique called blitting =in short, everything on a single layer (bitmap for AIR and shape for NME). Using this technique you need to take care about depth sorting, hitTesting etc yourself but this is pretty common approach (e.g. http://blog.theflashblog.com/?p=2749). I don’t see any option of blit or something similar in Corona so I’ll just stick to Flash/AIR and NME.
- Flash/AIR - Blit – single bitmap on display list + 15000 copyPixels on it = 21-22 FPS on CPU / 16 on GPU – smooth With GPU render mode it looks a little better regardless the lower fps
Really impressive result-it works quite smooth. So you might think we have a winner cause Flash/AIR was far better then NME with previous approach
- NME blit – using the awesome nme.display.Graphics.drawTiles method by Hugh = 57-59 FPS!
It runs so smooth so you can basically say that is runs on maximal FPS. This is quite impressive in my opinion-1500 animated sprites on mobile device running on almost 60 FPS! So let’s see how far we can push it
Test 2 – 60×100 animated sprites (blit) + 35 kill-able critters flying on top of it
Previous test was very simple so I decided to add some interactivity. And reduce the size of background sprite so that 6000 will fit on stage(36×36 spritesheet). To spare you reading long description this should look like this. This time background tiles starts animation on a random frame. Flying monsters are clickable. Credits for the nice spritesheet of flying critter goes to my brother-thanks Kubasa!
- Flash/AIR – blitting + 35 sprites and bitmap inside of it = unstable and slow at start, after 1-2 seconds 8 FPS on CPU but still playable (on GPU very unstable 5-15… not really playable)
- NME – blitting background (drawTiles method) + 35 sprites and shapes inside of it = initialize very fast, 24-26 FPS, really smooth!!
Below are all android application .apk files so you can test it yourself. The sources are available here (hapi library ver 0.2 is required). Because I don’t have any iDevice, nor a Mac I can not currently test it on iOS but if someone would be willing to compile and run those tests on iPhone or something I am VERY curious about the results.
|Test||Frame rate||Memory usage||APK|
|Flash Naive 30×50 MovieClip||7-8||4.5 – 5.3 Mb|
|Corona 30×50 sprites||3||0.01 Mb||here|
|NME 30×50 shapes||5||1.29 Mb||here|
|Flash/AIR 30×50 bitmaps||12 – 13||3.93 Mb||here|
|Flash/AIR 30×50 blitting||21 – 22||3.7.0 – 4.0 Mb||here|
|NME 30×50 blitting||57 – 59||0.59 Mb||here|
|Flash/AIR 60×100+35||8||4.55 Mb||here|
|NME 60×100+35||24-26||2.2 Mb||here|
I think the table above is at least a pretty good reason to give haxe+NME a try. The great thing about haxe is that you can have AIR (generate-as3), Flash, Android NDK with NME and event HTML5/Phonegap (with js target) versions at the same time without big effort. Isn’t that awesome?
Here is a video-recording of running those tests on my phone