For most Android game developers, PNGs are a simple, easy to use texture format that offers ‘good enough’ compression alongside alpha support, which is important since the dominant form of game in the mobile market is 2D based. But the truth is that while PNGs may help with distribution, they don’t help you when it comes to GPU residency; they are still full, fat textures eating up your available memory. But, it doesn’t have to be that way.
GPU residency is a scarce resource for Android devices. Big, uncompressed textures take up a lot of space, and make it difficult to support the federation of unique devices.
PNG to GPU
One of the worst parts about PNG files is that they are not friendly for GPU residency. Sure they help you with distribution, but they still take up uncompressed 32bpp or 24bpp space on the GPU, which is less than ideal.
Trust me, I understand the whole GPU texture fragmentation thing. PVRTC, DXT, ETC, ASTC; it gets crazy when you try to segment your APKs based on what textures you’re using.
But this doesn’t mean you have to settle for 32bpp by default. There’s an array of GPU compatible formats that support smaller bytes per pixel, and work on the large majority of GPUs. For example, take a look at the 16 bits per pixel formats below:
BITS PER PIXEL
|
CHANNELS
|
GL ENUM
|
32 bpp
|
RGBA - 8888
|
GL_UNSIGNED_BYTE
|
24 bpp
|
RGB - 888
|
GL_UNSIGNED_BYTE
|
16 bpp
|
RGBA - 4444
|
GL_UNSIGNED_SHORT_4_4_4_4
|
16 bpp
|
RGBA - 5551
|
GL_UNSIGNED_SHORT_5_5_5_1
|
16 bpp
|
RGB - 565
|
GL_UNSIGNED_SHORT_5_6_5
|
Making these formats is pretty easy from any of the plethora of PNG compressors; most will support this output option, heck, even texture packer will do it for you.
And as a disclaimer, it’s important to note that 16 bit pixel format is not supported by the PNG format group directly, nor the W3C, but this doesn’t mean that it’s unsupported in the file format itself. Really, even a fast conversion from 32bpp to 16bpp at load time would be enough of a win.

One of the side effects of using 16bit textures is the insertion of visual quality issues. In the image above, you can see the side-by-side difference between RGBA32, RGB565, RGBA5551 and RGBA4444. Easily, the 4bit per channel texture produces the most inserted quantization in smooth gradients, but it’s difficult to see the difference in the high-noise areas.
GPU Texture support on Android
THe truth is that if you want real savings for texture distribution, you need to embrace using one of the many GPU specific texture compression formats. These formats offer supiror compression sizes, alongside hardware sampling support (so speed is not an issue). Sadly, due to the state of the industry, there’s LOTS of options when it comes to hardware support for compressed textures. For example, here’s a few of the more popular ones.
FORMAT
|
GL ENUM
|
SUPPORT
|
ETC1
|
GL_OES_compressed_ETC1_RGB8_texture
|
This format is supported by all Android phones. But no alpha support.
|
ATITC
|
GL_AMD_compressed_ATC_texture
|
Used in devices with Adreno GPU from Qualcomm (Nexus One...).
|
PVRTC
|
GL_IMG_texture_compression_pvrtc
|
Supported by devices with PowerVR GPUs (Nexus S, Kindle fire...).
|
DXT1
|
GL_EXT_texture_compression_dxt1
|
This texture compression is used in the NVIDIA chipset integrated devices (Motorola Xoom...)
|
So here’ becomes a disconnect when trying to determine distribution, since some devices won’t have support for your target texture type. But it’s easy to determine what texture formats are supported once the app loads, just check for the above enumerations against the GL extension string on app load. In addition, when loading your texture data, you use glCompressedTexImage2D, instead of using glTexImage2D.
You can generally see that the quality difference in GPU compressed formats is high.
Note : ETC1 is supported by all Android devices.
|
Using PNG to avoid APK management?
Some devs have been claiming that they use PNG in order to reduce the burden of needing to send separate APKs to devices depending on what texture format type they support. To this, I say Hogwash, and here’s way:
Consider the sizes of gpu texture for this 256x256 cropping of the parrot head. You can see that if you add up the sizes of the DXT, PVR, and ETC versions of the texture together, it’s still LESS SIZE than the PNG file, both on disk, and resident in GPU memory.
Consider the sizes of gpu texture for this 256x256 cropping of the parrot head. You can see that if you add up the sizes of the DXT, PVR, and ETC versions of the texture together, it’s still LESS SIZE than the PNG file, both on disk, and resident in GPU memory.

The takeaway here is that if you add up the footprint for DXT, PVR and ETC, you end up with an overall texture footprint that’s lower than the size of the web-exported version of the PNG file. AND they have better GPU residency! Or, to say it in a much more “blog friendly” way :
Instead of using PNG, export DXT, PVR and ETC, versions of your texture. Bundle all 3 in your APK, you’ll get a smaller distribution footprint AND smaller GPU residency.
Bare minimum, use ETC1 on all opaque textures.
|
Conclusion
Sure, PNG is an easy format to use; However it’s compression savings ends at distribution, meaning that you lose all those savings when it comes to GPU residency. Using 16bpp PNG textures are a quick hack, and supported by OpenglES2 directly. And if you’re feeling daring, go all the way and just bundle all your GPU specific versions of the texture into your apk; You’re still going to be saving distribution size, and GPU residency.
You can find Colt McAnlis here:



Thanks, found this useful (looking at reducing PNG size in an Andriod app)
ReplyDelete