In the most recent BioBeasts Alpha test, several of the staff reported lag and slow performance. Given that BioBeasts is supposed to be a fast-paced action-arcade game, even a 5-10ms lag can cause a player to get smashed by the robots. This is BAD NEWS BEASTS and we won't let that happen!
Over the past few months we've encountered a number of things that can result in loading pops, reduced FPS, reduced battery life, and overall poor performance.
These bad practices include:
- Over instantiating objects
- Calling Resources.Load() at inopportune times
- Running complex calculations in the Update loop
- Using too many complex particles
While we've addressed many of these issues in an earlier post (Unity Memory Management), one area where we previously did a poor job was in the optimization of our textures. This week we took a new approach to texture compression that has reduced our texture memory footprint by over 50%.
What is Texture Compression?
Texture compression is a specialized form of image compression designed for storing texture maps in computer graphics rendering systems. Unlike conventional image compression algorithms, texture compression algorithms are optimized for random access. Since predicting the order that a renderer accesses texels would be difficult, any texture compression scheme must allow fast random access to decompressed texture data. This tends to rule out many better-known image compression schemes such as JPEG or Run-length encoding. (Wikipedia - Texture Compression)
Uhh, whaaaat?? OK, basically, we MUST use texture compression to optimize our graphics for mobile devices so the images look great while simultaneously taking up the least amount of space and memory.
Initially, we figured it made sense to organize our textures in logical groups. So for example, our Loading interface uses a solid metal door and flicking light with a bunch of alpha. We figured it made sense to place those on the same sheet and let Unity's Sprite Packer handle combining our sheets to optimize the space.
Turns out that was dumb... really dumb. Using Unity's runtime memory profiler, we found that the 2048x2048 sprite sheet above, even with compression applied, took up 4mb in Texture2D memory. Stack several of those up and pretty soon you're hosed. If you ever encounter an issue where several of your graphical assets fail to render and appear like black shapes, you're probably pushing beyond the texture memory limitations of the device and need to optimize your application.
After several experiments, we arrived at a much better approach. The following technique reduced our Texture2D memory usage in BioBeasts by over 50% with no noticeable loss of quality.
In this approach, we no longer organize our sprite sheets in logical groups, and instead organize them into three compression types: Solids, Fades, and Alpha Punchouts.
Solids are any rectangular assets with NO alpha. Here we can get a major savings by selecting a 4-bit compression that doesn't support alpha.
Alpha Punchouts are any other shapes that use binary alpha, meaning the area of the image is either 100% or 0% alpha. These assets generally have a strong simple edge. You can imagine Alpha Punchout graphics as something that could be cleanly cutout from a piece of paper and is NOT rectangular. Here we select a 5-bit compression (4-bits for the image + 1-bit for the binary alpha).
Fades are reserved for our most complex objects that contain variable degrees of alpha. We use these sparingly and compress them using a minimum of 8-bit RGBA to look best.
In the examples above, it might look like we're wasting a lot of space on our sprite sheets... we're not. Prior to a build, because we've tagged our sheets with Packing Tags, we pack sheets with the same compressions together. This dramatically reduces the total number of sheets, and results in better image quality post compression.
This technique was applied in Unity 5.1.3p3. We're aware several improvements are available in Unity 5.2, but so far we've encountered too many bugs in the editor to make the switch. Hopefully in an upcoming patch Unity will resolve these issues and we can utilize their latest improvements!
Have thoughts on this approach, let us know in the comments below!