This post also known as “trade-offs make for long sentences”
When I ported my game engine to Android (and thus GLES 2.0), I switched from using PNG images to using the Ericsson Texture Compression format, ETC1. The advantages were pretty clear: significant speed up, slightly reduced file size, acceptable loss of quality, faster loading speeds and simpler code. Of course, when do we ever get all that without a trade-off? ETC1 doesn’t support alpha channels and, more crucially, OpenGL can’t generate mipmaps for compressed textures. My solution to this was to write a python script which resizes a PNG multiple times and packs the resultant ETC1 data into a single file. You can find the script and code at the end of the article, but I’ll just explain quickly what it’s doing; it’s always best to have a deeper understanding than just “oh, this tool did it for me”.
Here’s how my PNG loading was, and chances are loading from another format or library is pretty similar:
And here’s my mipmapped ETC1 system:
Clearly because the diagram is more green it is much better! Maybe “runtime” is the wrong word, I mean “when the player runs the game on their device” but there’s not enough space for it! What we’re doing is performing the slow scaling before runtime (i.e. before we even create the APK), then the fast bit is done at runtime. Probably my favourite thing about using ETC1 on Android is that OpenGL just accepts raw compressed ETC1 data; we can literally just extract the data into a temporary buffer and upload it to OpenGL for super-fast loading!
Here’s the quick python script I wrote, just install Python and the Python Image Library and run it like so:
python makemipmaps.py image.png
Note that you’ll want it to have access to etc1tool (normally found in android-sdk/tools/). I run it by putting it in the same directory as etc1tool, adding that directory to my path environment variable, then running a batch script wrapper like below.
python “C:\Program Files (x86)\Android\android-sdk\tools\makemipmaps.py” %1
Obviously you’ll need to change the file name as appropriate, but it should let you run the python file from any directory, provided you put it in a directory that is in the path environment variable.
So now we have a file with the compressed and mipmapped ETC1 textures in, how do we load them? That’s actually pretty easy but you might need to make a few adjustments to my code. Also of note is that I’m using C++ with the Android NDK, but it should be pretty simple to port it to Java. The code is pretty well commented so it’s probably best if you just check it out! loadETC1.h
Let me know if any part of this didn’t work for you, or if I haven’t explained something very well!
As a follow up, here’s my game with and without mipmapping (click for a larger image)
Even with terrible JPG compression to get the image to ~100KB, the difference is massive! Since it’s doing less scaling at runtime, it’s also slightly faster. Whilst we do have a larger file size (we are storing more textures on disk), and we’re using more memory than not having mipmaps altogether, we should be using less memory than automatically generated mipmaps since our ETC1 mipmaps are compressed!