We have been Beta Testing our soon to be released game The Dog Run and it’s been mostly OK but we had a number of issues with memory on smaller or older devices. We made some gains with modifying our audio files (See this post) but were still running into niggling crashes on start up and longer than normal load times.
We were getting feedback like:
“Hey, I installed the game and couldn’t run it. When I started it there was a black screen for about 15s and then it went back to the launcher. Then each time I went back to the game there was unity and game logo fading out and again the app crashed/hanged and I was sent back to the launcher.”
(Thanks slomoian and the_blanker for all your help testing)
Obviously feedback like this is a little disheartening and far from ideal. The game was running fine on every device and emulator I had access to but it’s only when you send something into the wild that you realise the full breadth of the spectrum that is the Android platform. I guess this is another lesson in the importance of proper Beta testing. One we hadn’t learned last time we released an app (see this old post on the perils and difficulty of finding Beta Testers).
We were using adb logcat to monitor our start up problems but not finding a “smoking gun” that solved every case. It seemed to be a memory problem and often with the graphics cache so again we went back to the Unity Editor build log to investigate our image files. The game uses multiple large files to ensure that our animated sprites were always in the right spot. The game is dependent on the titular Dog hitting the ground line accurately on every frame to achieve the look we wanted when he runs and the paw breaks the ground line and appears as a gap. We used a “flip-book” old fashioned style of animation where each frame sits exactly on top of the old frame and everything lines up on a transparency like in classic animated movies.
By using this schema we had to keep to a certain scale that fit within the constraints of a typical Android device format. This meant that when the images were imported the POT was not going to be something we could play with easily to get performance gains. (Image files that have a width and breadth that is a power of 2 are faster and easier for the compression functions to work with – so 2, 4, 8, 16, 32, 64, 128, etc). If I had the chance to do this again this is something I would probably start doing right from the beginning of development. When going through the Editor Logs we did find something interesting (get to the Editor Logs by right clicking on the arrow or tab near the Console and selecting it).
We found that some of our image files were 10 MB and a few were 2 MB. Which was a little weird as they were all exported as layers from the same Gimp file so I must have done something in the import settings or the editor to change them.
This is a comparison of two files of the same dimensions and basically the same content but with two very different file sizes:
10.6 mb 0.8% Assets/artwork/RunOnSpot6.png
2.0 mb 0.1% Assets/artwork/DogSitHeadWag.png
The difference that I found was MIP Maps. I’d selected to use MIP Maps fairly early on as it made the art work look smoother in the Editor. MIP Maps are generated in the engine to make smaller more compressed versions of your artwork that can be used at longer distances from the camera where the detail is less visible. My game is 2D and has everything running at pretty much the same distance from the screen so really MIP Maps should not be required. My art did look a bit better in the editor with them turned on but on a smaller device like a phone I couldn’t really tell the difference. See below the difference in a file with MIP Maps selected and a file without.
With MIP Maps turned on (see the file size at the bottom and that the type is RGBA 32 bit):
The same file with MIP Maps removed (down to 2 MB and using ECT2 compression):
This is the difference that generating those MIP Maps makes. Your file is converted from the Android default compression to a larger (harder to process) 32 bit compression format.
So by turning off MIP Maps across the three hundred plus image files in my game reduced my application start up time to under a few seconds and reduced the APK file size by over one thousand MB.
This is the Build Report from the Editor Logs that shows the larger texture sizes and final build size:
Uncompressed usage by category:
Textures 1292.3 mb 94.6%
Complete size 1365.9 mb 100.0%
Compare this later Build Report with MIP Maps turn off to the original one above:
Textures 284.5 mb 82.6%
Complete size 344.6 mb 100.0%
It’s a considerable difference with little or no quality loss on most devices. When I say most devices there were a few cases where the running dog did look a little tatty. On very small emulated devices (3.5″ screens and low memory) the images were being scaled down quite a lot and the results were a lot less enjoyable but still an acceptable compromise considering previously the game would not run on these devices at all.
The next thing I started playing with was the different texture compression variables available for Android. I tried all of the settings (see screenshot below) in a different build and tested them against at least ten different devices with various architectures and screen dimensions and Android versions.
In each of the cases but one there was at least one test device that failed to start the game. Once again exposing the issues of working with so many platform variables on Android. Even when I built the APK with the (default) ETC selected one device failed the start up test. So in the end the final build used the “Don’t override” setting which seemed to work on all devices.
Hopefully this is helpful to someone else out there and if it is try hitting the “Like” button below or sharing the link (the feedback keeps me going).
I found these references useful when troubleshooting my start up issues and learning more about compression on Android:
2 thoughts on “Image File Size in Unity and their Impact on Start Up Time on Android”
Hey, I’m the author of unity3dtips.com just thought I would clarify some things in this article.
The example you gave with DogSitHeadUp with and without mip map generation having such as huge change in filesize with mip mapped textures was because NPOT cannot be compressed (when it says RGBA 32 bit the texture has no compression applied to it).
“Your file is converted from the Android default compression to a larger (harder to process) 32 bit compression format.” this is actually quite the opposite; uncompressed textures will load much faster compared to compressed ones and as long as their resolution is unchanged there’s also no runtime rendering performance difference. Both would result in the same memory usage too as the texture is decompressed into memory when it’s first used in a scene (or startup if it’s the splash screen)
Again in the case of DogSitHeadUp a better option would be to instead have 2 images, one which is a long ground object and another which is just the dog. These would then be added to a UI texture atlas (either with Unit’s default UI atlas system or personally I use NGUI for my UI)
However one major thing you seem to mis-understand is that all textures are not decompressed on app launch. Texture are decompressed as and when they’re needed in the scene (You’ll see this recorded in the profiler as Loading.ReadObject(..)) so if you were seeing the app spend a long time on the splash screen then an option could be to add a “pre-load scene” where you load in and destroy prefabs 1 by 1 to preload their textures while a loading screen is displayed. (But note that having direct references to prefabs in a script would result in the textures being loaded as soon as the scene is entered anyway so something like assetbundles would need to be used)
However you mentioned the players spending a long time on a black screen before the splash screen even appears. There’s a few things which are loaded at the start of the app before the splash screen appears: The Unity engine, the splash screen texture, assets in the preloaded objects list of player settings > other settings, preloaded shaders defined in graphics settings, and anything in a folder named “Standard Assets”
Lowering the sizes of everything in this list will make the game spend less time on a black screen and show the splash screen faster.
Hope this wall of text helps lol, thanks for sharing my site too! Let me know if you have any questions 🙂
Indeed! I bow to your greater Unity Foo. Thanks so much for the comment and the advice.