Monday, 5 November 2012

Pigment & the Droid, Part 1

When Pigment became available as a free download on Amazon's FAotD feature it became clear very quickly that there was a problem: it didn't work on some Motorola handsets - the Droid 4, Droid Razr and Razr Maxx were all reported specifically by unimpressed customers.  So: why did it crash on those phones, and how could it be fixed?


While the Amazon App Store beats Google Play hands-down at promoting your app, it is lacking in Play's technical features. Notably, if your app crashes on a Play user's phone, then they have the option to send you a report which tells you pretty specifically what went wrong.  Amazon do not (yet) have this facility.  As such, there was no way to know what was going wrong with Pigment, aside from guessing.

Educated guessing.  For one thing, customers were reporting it was crashing on the splash screen: the splash screen is displayed while the game loads its assets into memory.  For another, these type of crashes happened often during development: they occurred when the device ran out of memory.

"Out of memory?", you say, "But my phone has 16GB of internal storage!"  The memory we're talking about is the phone's temporary storage, not the place it permanently saves things.  That 16GB is the phone's equivalent of hard disk space - what we're running out of is RAM.
"But my Razr has 1GB of RAM!  How can you be using all of that up?"  Ah, a good question, the answer to which is found in how Android works:

Every app running on an Android device has it's own little area of the phone to play in.  In effect, every app is running on its own virtual device.  Every one of these virtual devices is allocated a set amount of memory to run in: this is called the heap.  It's this heap which we're talking about.  The amount of heap allocated varies on a device-by-device basis, determined arbitrarily by the manufacturer; a typical amount would be around 30MB.

This is one of the most challenging areas of Android development.  iPhone developers have it extremely easy in comparison: they only have to deal with a handful of different devices, three or four different screen resolutions, and (until recently!) only one aspect ratio.  Android devices number in the thousands, with vastly differing screen sizes, aspect ratios, input facilities, storage spaces and memory allocations.

There is no set rule to determine how much heap your app will be allocated, but it will typically depend a lot on screen size: all the sprites and bitmaps the app uses will be loaded into heap before being displayed, and the screen the user sees will be held in heap before being rendered onto the physical display.  Therefor, the higher the resolution of the device's display, the more heap you will generally have.

Pigment was developed against an HTC Desire.  Firstly, because this was a tier 1 phone on release, but two years on was now a fairly modest device, and secondly, because it's the only phone One Lives Left had access to!  The Desire has a landscape screen resolution of 800x480.  This is the resolution which was used when creating all the art assets for the game: the logos, numbers, icons, messages and the canvas board the game would be played on.  When Pigment is run, the splash screen is displayed and all these assets are loaded into heap, ready to be used.  At various points, as features were added to the game, it would crash here: it was trying to load too many assets into the heap, and it would bomb out.  Simple to fix: load less stuff.  Thus, the game developed able to run on the Desire - in effect, the Desire bracketed how much graphical and sound data the game uses.

Then the game was tested on a friend's HTC Wildfire, and: it crashed on the splash screen.  The Wildfire's screen resolution is only 320x240 - a screen on it takes up only 20% of the memory that a screen on the Desire uses, so of course it has a much smaller heap.  However, Pigment didn't know this, and was trying to load in way more graphical data than the Wildfire should ever need to display, and so was crashing.

The fix was to have Pigment make a decision when loading its assets:
  • If the device has the same resolution as the Desire then it loads the assets normally.  As the game runs it will run at 800x480, then draw that board to the display.
  • If the device has a lower resolution than the Desire, then it will work out the size difference and scale the assets down by that amount as they load.  It will work out its board size to match this scaled down resolution.
  • If the device has a higher resolution than the Desire, then it will load the assets normally.  That is: it won't scale them up.  It will run as if the screen were 800x480, and only as it is about to draw on the screen will it scale that one bitmap up to the correct size to fill the display.
This worked.  On the Wildfire the assets all loaded in fine, scaling down to their much smaller versions, which took up less space and so didn't fill the heap.  For devices with a higher resolution than the Desire: they had a bigger screen, so they should have more heap, and since it was loading in only what would be loaded into the Desire it seemed like there wouldn't be any problems there either. 

From the descriptions written by users in their reviews, it certainly sounded a lot like Pigment was running out of heap.  With access to one of these handsets it would be easy to make sure that was the issue, but such access was not availble.  Luckily, one of the amazon reviewers had emailed about the issue, and they were happy to help.  To do so they downloaded the Pigment Demo from Google Play and, when it inevitably bombed out, use the crash report feature.  This confirmed it was due to the device running out of memory. (As an aside: Thanks, Tammy!)

The Droid 4, Droid Razr and Razr Maxx all sport screen resolutions of 960x540: a good bit larger than the Desire.  It was presumed that any device with a higher resolution would also have an increased heap size to match, though Pigment wasn't coded to need more: on devices with bigger screens it would load the assets exactly as it would on the Desire, taking up the same amount of heap.  There should have been enough memory to load the assets, and yet Pigment was crashing when it tried to.

At the time, however, one thing was clear: it needed to be fixed. So, going on the assumption that the Motorola devices had less heap than the Desire, it was time to try to trim down the memory use.  We'll see how that went in Part 2.

No comments:

Post a Comment