If you are doing 3D graphics with the XNA Framework, you are probably already using compressed textures, even if you don’t realize it. It occurred to me it might be interesting to explain what these are and how they work.
Note that texture compression is not the same thing as the new .xnb compression feature in Game Studio 3.0. If you store an uncompressed texture inside a compressed .xnb file, this will reduce the size of your game on disk, but the texture will be decompressed into memory when you load it. DXT texture compression, on the other hand, is supported by the graphics card, so you can render directly from the compressed format. How cool is that?
Why should I compress my textures?
- It saves space, both on disk and in memory while your game is running.
- It speeds up drawing. Because the graphics card works directly with the compressed format, it does not have to fetch so many bits from memory. This can make a big difference if your performance is bandwidth limited.
How do I control texture compression?
Set the Texture Format content processor parameter. This defaults to Color when you add textures directly to Visual Studio (so sprites are not compressed), but defaults to DxtCompressed when you add 3D models (so any textures used by the model will be compressed).
You can change this parameter if you want to compress your sprite textures, or to disable compression of your 3D model textures.
How does texture compression work?
- Divide the image into 4×4 blocks
- For each block, find the two most important colors in it
- Store these two colors in 16 bit 5.6.5 format
- For each of the 16 texels in the block, store a 2 bit value indicating how far it lies between the two main colors
When I first read about this, I thought it was crazy. With only two unique colors per block, how can you possibly represent blocks that contain more than two different colors? Well, you can’t. But this simple scheme turns out to work amazingly well for many real world images.
There are five variants of DXT compression:
- DXT1 works like I described above, plus some extra magic for encoding alpha cutouts
- DXT3 also stores a 4 bit alpha value per texel
- DXT5 encodes an alpha channel using a similar scheme to the RGB data
- DXT2 and DXT4 were a not-very-well-thought-out attempt at standardizing premultiplied alpha – these have no purpose and you should pretend they don’t exist 🙂
DXT1 uses 64 bits per 4×4 block. Compared to a 32 bit uncompressed image, this is an 8x compression ratio. DXT2-5 use 128 bits per 4×4 block, which is a 4x ratio.
These ratios are pretty good for a format so simple it can be implemented directly in hardware on the graphics card, but not as good as you might expect from a sophisticated compression algorithm like Zip or Rar. For best results, you should combine DXT compression with the Game Studio 3.0 .xnb compression. Using both together will most often produce smaller .xnb files than either one alone.
When you set the Texture Format processor parameter to DxtCompressed, the Content Pipeline automatically chooses between DXT1 and DXT5, depending on whether your texture has an alpha channel. If it is entirely solid, or contains only cutout alpha, the pipeline will use DXT1 to get the best possible compression ratio, but if the texture includes fractional alpha values, it will select DXT5 instead.
How much quality do I lose?
Now for the bad news 🙂
DXT compression is lossy. Sometimes it can be very lossy indeed. It works ok for some images, but is not suitable for everything.
Here is my cat Rhys, first in uncompressed Color format, then compressed using DXT1:
The two images do not look exactly the same, but the compressed version is probably close enough for most purposes. Not bad considering this reduced the texture size from 64k to just 8k!
Things are not so rosy with this texture, however:
Zooming in on the compressed version, check out the bletcherous black blocks along the upper right of the X, and note the banding in the background gradient:
DXT compression usually works ok for noisy organic textures, but not so well for clean vector style images. The trick is to use it wherever possible, and turn it off for just the handful of textures where the compression artifacts are too objectionable to live with.