Mar 24, 2014

How to move from PNG to GPU Textures on Android (and keep your sanity)

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.

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:

32 bpp
RGBA - 8888
24 bpp
RGB - 888
16 bpp
RGBA - 4444
16 bpp
RGBA - 5551
16 bpp
RGB - 565

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.
This format is supported by all Android phones. But no alpha support.
Used in devices with Adreno GPU from Qualcomm (Nexus One...).
Supported by devices with PowerVR GPUs (Nexus S, Kindle fire...).
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.
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.


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:



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

  2. LifeVoxel.AI has developed a Interactive Streaming and AI Platform for medical imaging using GPU clusters cloud computing. It is a leap in cloud technology platform in medical imaging that encompasses use cases in visualization, AI, image management and workflow. It’s approach is unique that it has been granted 12 International patents. LifeVoxel.AI’s platform is certified for HIPAA compliancy. The platform was granted an FDA 510K approval for use in diagnostic interpretation of medical images.

    Interactive Streaming AI Platform RIS PACS

  3. LifeVoxel.AI platform helps imaging diagnostic centers and hospitals to save up to 50%+ over conventional RIS PACS with higher functionality. LifeVoxel.AI is the fastest RIS PACS available globally and have unimaginable capabilities of centralized PACS across all your network of Imaging Centers to single window HUB.

    RIS PACS software