Dxt compression in games

In this article I want to share my experience in developing a mobile game, since I am a Windows Phone developer, I will talk about my experience with this system.

Memory and textures


If you have already been developing mobile games, then the main evil is not the lack of CPU / GPU resources, but the lack of memory. It is about memory that you need to think about in mobile development first. In Windows Phone 7, the limit was 100MB, in Window Phone 8 it became better, but not much:
Limit TypeType of applicationLow Memory Phones1 GB phones2GB phones
DefaultXNA or native150 MB150 MB150 MB
DefaultXAML / .NET excluding XNA150 MB300 MB300 MB
HigherherAll app types180 MB380 MB570 MB

And if you are developing a game in which a fairly large number of sprites (laid out, of course, in atlases), then sooner or later you will think about the number of these atlases and texture compression.
The standard atlas, with which more or less self-respecting devices work, is 2048x2048 pixels. That in uncompressed form (32 bits per pixel) will occupy as much as 2 * 2 * 4 = 16 MB of memory. Then texture compression formats come to the rescue, in our case it is DXT compression.
Compressed textures not only require significantly less video card memory, but generally display faster than uncompressed textures, due to lower bandwidth requirements. But some image quality may be lost due to compression. Nevertheless, the decrease in memory allows increasing the resolution of the textures to be used, which can really give a significant gain in quality.

Dxt compression is already implemented in the .XNA Frameworke, as well as in Monogame.
For clarity, we take 256 images of 128x128 pixels in size, one texture atlas of these textures of 2048 * 2048 in size, and compress this atlas.
Rumor has it that textures are loaded faster, the size of which is a multiple of the power of two, for the experiment we will change the original texture a bit, cut off one pixel, bringing it to size 2048 * 2047.
To show the effectiveness of the described methods, we make control measurements. We will conduct them on the Nokia Lumia 800 phone. For greater accuracy, we will make 10 measurements for each method.
We summarize the results in table 1 and summarize.

Table 1. Image download speed.
 256 textures of 128 * 1281 dxt 2048 * 20481 origin 2048 * 20481 origin 2048 * 2047
100: 00: 00.646000000: 00: 00.033000000: 00: 00.151000000: 00: 00.1200000
200: 00: 00.644000000: 00: 00.033000000: 00: 00.151000000: 00: 00.1180000
300: 00: 00.647000000: 00: 00.041000000: 00: 00.187000000: 00: 00.1570000
400: 00: 00.640000000: 00: 00.033000000: 00: 00.149000000: 00: 00.1190000
500: 00: 00.642000000: 00: 00.033000000: 00: 00.150000000: 00: 00.120000
600: 00: 00.634000000: 00: 00.047000000: 00: 00.132000000: 00: 00.161000
700: 00: 00.634000000: 00: 00.050000000: 00: 00.159000000: 00: 00.179000
800: 00: 00.630000000: 00: 00.050000000: 00: 00.158000000: 00: 00.179000
900: 00: 00.633000000: 00: 00.048000000: 00: 00.158000000: 00: 00.179000
1000: 00: 00.621000000: 00: 00.047000000: 00: 00.165000000: 00: 00.1820000
Average00: 00: 00.637100000: 00: 00.041200000: 00: 00.155800000: 00: 00.1514000

For clarity, a graph of the dependence of various loading methods on time (Fig. 1.)


Figure 1. A graph of the dependence of various loading methods on time.

Table 2. Image sizes
 MethodsSizes, Mb
1Size 256 texture 128 * 12816
2Texture size 2048 * 2048 without compression16
3Texture size 2048 * 2048 with compression4

As we can see from the presented Dxt experiments, compression is very effective. Let's consider it in more detail.
DXT compression (also sometimes known as S3 compression) is actually very simple. Here's how it works:
  • The image is divided into 4x4 blocks
  • For each block, there are two most important colors.
  • The resulting two colors are stored in 16 bits, in RGB 5.6.5 format
  • For each of the 16 pixels in the block, 2 bits of value are stored, indicating how far it is between the two primary colors

This simple circuit appears to work surprisingly well for many real world images.

There are five options for DXT compression:
  • DXT1 works as I described above, plus some additional magic to encode the alpha channel
  • DXT3 color is encoded in the same way as DXT1, and also stores 4 bits of the alpha channel value in a pixel
  • DXT5 color is encoded in the same way as DXT1, and a similar scheme is used to encode the alpha channel
  • DXT2 and DXT4 were not a very well-thought-out attempt to standardize and nothing good came of it. You must pretend that they do not exist

DXT1 uses 64 bits on a 4x4 block. Compared to 32-bit uncompressed texture, this is an 8x compression ratio. DXT2-5 uses 128 bits in a 4x4 block, which gives a 4x compression ratio.

And now the bad news: DXT compression is lossy compression. Sometimes they can be very large. In fact, it works very well for some images and is not at all suitable for others.
So, when you want to use Dxt compression, say in XNA, when you set the Texture property to Texture format, the parameter DxtCompressed, Content Pipeline automatically selects between DXT1 and DXT5, depending on whether your texture has an alpha channel. If it does not contain an alpha channel or contains a uniform alpha channel, DXT1 will be used to get the best compression ratio, but if the texture contains fractional alpha values, it will select DXT5 instead.

Let us consider in more detail each of the compression methods:
Dxt1

DXT1 format is designed for real-time decompression by hardware on the video card during rendering. DXT1 is a lossy compression format with a fixed compression ratio of 8: 1. DXT1 compression is one form of block coding by truncation (BTC), where the image is divided into disjoint blocks, and the pixels in each block are quantized to a limited number of values. The color values ​​of pixels in a 4x4 pixel block are approximated from equidistant points on a line passing through the RGB color space. This line is defined by two endpoints, and for each pixel in a 4x4 block, a 2-bit index is stored at one of the equidistant points on the line. The ends of the line passing through the color space are encoded in a 16-bit 5: 6: 5 RGB format and one or two intermediate points are generated by interpolation.

Let's look at Figure 2 below:
The left image is the original. The right one illustrates DXT1 compression.


Figure 2. Example of Dxt1 compression.

The visually compressed image does not differ from the original, which makes the compression results acceptable to most users. Compression, however, significantly reduces the size of the texture.
In this case, from 256 KB to 32 KB.

However, everything is not so rosy with this texture (Figure 3):


Figure 3. Example of Dxt1 compression.

The main problem is the appearance of noise inside the text, and also distinct bands are visible against the gradient background.


Figure 4. Noises inside the text.

Figure 5 shows how compression affects color. On the left you see 16 shades of red, from pure red to pure black. On the right you see the four colors that result from DXT compression, from these 16 shades.


Figure 5. The effect of compression on color.

Figure 6 shows what happens when different colors are not on the same line in the color space. In this case, all extremes of the RGB palette (red, green, and blue) were used. Obviously, the resulting interpolated colors do not match the originals. Usually in the 4x4 pixel area there is not such a wide selection of colors, but it shows that textures with different colors suffer more.


Figure 6. Effect of compression on color.

DXT1 compression usually works well for noisy textures and not so good for clean images as well as gradients. The trick is to use it wherever possible and not use it only on textures where compression artifacts are too undesirable.

Dxt5

The DXT5 format differs from the DXT3 format in that it stores alpha channel information, much like storing color information.
For information about the alpha channel, he uses a palette similar to the way digital information is stored. This palette contains the minimum and maximum alpha channel value. There are two options with 6 and 4 reference points.
6 other alpha values ​​are interpolated between this minimum and maximum. Thus, this allows more gradual changes in the alpha value.
The second option interpolates only 4 other alpha channel values ​​between the minimum and maximum values, but also adds alpha values ​​0 and 1 (for completely transparent and not transparent). For some textures, this may give better results.


Figure 7. Example of Dxt5 compression.

As you can see, the edges are not very well processed in some parts.


Figure 8. Torn edges when using Dxt5 compression.

The texture size was reduced from 256 KB to 64 KB.
Losses of quality in real images are not so significant and they can be neglected for most images.
Using Dxt compression allows you to:
  • Reduce installation package size
  • Reduce RAM usage
  • Increase the speed of "rendering" images

In my project, after creating the texture atlas, I get the output .jpg / .png / .bmp and the description of the atlas in .xml / .txt / .json. Since I use XNA / Monogame for compression in .xnb, I use the XNA 4.0 Content Compiler as a whole, this is a very clear and simple solution, only to use Dxt compression you need to add one more property to ContentBuilder when creating buildProject:
buildProject.SetProperty(“XnaCompressContent“, “True”);

Sources:
S3 Texture Compression
DXT compression explained
DXT Compression Techniques
Real-Time YCoCg-DXT Compression

Also popular now: