Monday, April 18, 2011

App Size

Hey iDevBlogADay,

I'll only write a short post, as we're in the final crunch for our next game. I'm super excited about it, and I'll share more in the following weeks.

This week I'm just going to cover one aspect that is of particular importance to me and Limbic, and I think it's one of the thing that many devs ignore: Reasons for keeping your app size small, and how to achieve that. Obviously, this doesn't apply to every app (I couldn't imagine Rage being a 20 MiB app), but especially for us Indies, it has some important arguments we should consider.

In our upcoming game, our artists did an incredible job at optimizing the assets. We're right now at around an incredible 8 MiB, and it's visually stunning (you'll see :-).

Why keep the App Size small?

I'll just do a classical bullet list collection of arguments:
  • First of all, there is the over-the-air download limit of 20 MiB. That means any app larger than 20 MiB can not be downloaded via 3G, and must be downloaded on WiFi only. Obviously, this does affect sales to a degree. I've heard some numbers floating around of about 15% sales drop if you go over the 20 MiB limit, so that's something to very carefully consider.
  • There is a (somewhat complex) relationship between app size, loading time, and memory footprint of the app. And I have a fetish for fast loading times. Having a small memory footprint makes a lot of things easier for development, too (as you can basically ignore the memory warnings, that's what we're doing in TowerMadness and it works great).
  • The larger the app, the more likely I'm to delete it when I'm hitting the limit again (I only have a 16 GiB device, like most). As such, keeping the app size small means that people are more likely to keep it.
  • With a small app size you can ensure you're not going over the 20MiB limit even after many updates. TM went from having 4 maps to more than 60 maps, and it's still below the 20 MiB.

Obviously, there are also many drawbacks, such as potentially prolonged development time, limiting quality, preventing you to use certain features ,such as video. I remember one app that was very simple, and could probably fit in about 15 MiB. But they shipped with almost 200 MiB of video, which was shown once.

What can be done to optimize the app size?

For us, the classical items that quickly add a lot of size to an application bundle are:
  1. Videos
  2. Audio
  3. Code Bloat
  4. Textures/Images and other GPU data

I can't really say that much about 1), as we haven't included any videos in our apps yet. I really don't know a lot about 2), so I don't know how much you can gain by choosing different codecs, etc. We're mostly using .caf files, but that's probably not very optimal and all I know.

I can say a lot more about 3) and 4) though. Although it may sound a little strange, code size can very quickly blow up. I've done a little survey, and I've found binary sizes ranging between 1MiB and 10Mib, for apps of approximately equal complexity. This can mean that you can double the amount or quality of the assets, and still be in the 20MiB limit. One thing that can cause code bloat are third party libraries. I've seen anything from +10KiB executable size for a library, up to almost a megabyte, where you wouldn't expect it. And often, it doesn't even correlate to the complexity of the libraries functionality. So, an important hint here is to watch out for the executable size when you add libraries.

Another important factor is language-related code bloat. Especially if you use C++ with a lot of templates, this can happen pretty quickly. I haven't tried this on iOS, but I regularly had issues with Boost blowing up the binary size significantly. The problem with templates in C++ is that, if not written carefully, they can produce a lot of duplicate code, that essentially does the same thing, but for a different type. So, a second hint here would be to watch out these effects, and factor in exectuable size into consideration.

Multi-objective optimization

The fourth item on the list is a rather complex topic. On the one hand, you want your assets to be as high quality as possible. On the other hand, you want a low loading time, and small app size.

So you have three different objectives, that affect each other. For example, a common way to reduce file size is to use PNG or JPEG for the compression of texture data. While this has a huge effect on file size, it also negatively affects loading time. The PNG will be loaded to memory, then cpu-intensively decompressed into a larger buffer, and then uploaded to the GPU. It can also have grave implications for your memory footprint. While your PNGs are small, once they're decompressed, they're usually in memory as RGB or RGBA, using up to 32 bits per pixel. Just a single 512x512 texture already takes up 1MiB in memory (and on the GPU) then. To counteract that, you can do some real-time encoding to a different format (such as RGB565), but this takes up even more CPU time. So, here you have a difficult balance between file size, cpu time and memory footprint, which can quickly tip over to one side or another. Especially if you're not paying attention, and you're just adding the PNGs the artists hands you.

Our solution

So, here is what we do to tackle this problem at Limbic:

First of all, to minimze loading time, we're preprocessing everything to a GPU format. That means our meshes are in the exact format that the VBO data we upload to the GPU. For textures, we use the .pvr format. That means our textures themselves are not compressed (unless we use PVRTC), but they are only encoded with the GPU ready pixel format. This gives the artists a direct feedback on the final size of the texture and the strain of the GPU. We then put all the files into a custom package format, that's similar to Zip with a few performance enhancements. This approach gives us a really small load time, and the memory footprint is about equal to the uncompressed size on disk.

Another important element of the size optimization is a demanding producer, that constantly and from day one keeps a watch on the file sizes and the game performance, and lets the responsible people know if something is getting out of hand.

Conclusion

All the hassle finally pays off for us. For our latest game, that means gorgeous visuals, 8 MiB file size, 4s unoptimized initial loading time and no further delays, for a full 3d game with a completely 3d menu. It's a constant struggle to get there, but I'm looking forward to it for the next project already.

Back to crunch mode. :-)

Update:
Bob Koon sent me a link to his page: http://www.appsizematters.com/ Lots of great information there!

2 comments:

timzilla said...

Thanks for the post. I hadn't really thought about optimizing textures before. One thing you didn't discuss was universal apps and their affect on file size. Having to include textures for retina and older devices can really increase app size. Not to mention building for arm6 and arm7 (which I believe is required now). Any tips for dealing with that?

volcore said...

Hey tim,

good point. For our upcoming game, we don't even take into account the non-retina resolutions. We just ship with high res textures, and the mipmaps take care of choosing the right resolution for the job.

The game is not universal (we don't have an iPad version yet), but I don't think it'll add a lot of assets (which is one of the benefits of an all-3D in game menu).

I remember that we had a lot more problems in TowerMadness HD, which uses cocoa excessively. We even went over the threshold, and had to compact quite a few images. Often, resolution can be reduced quite a bit without noticing. Another thing we use a _lot_ in cocoa is indexed color PNGs. If done right, this can significantly reduce the file size and not be too noticable, but it can also backfire, so you have to trial&error a lot.