WebGL Texture Compression Comparison
We’re adding support for compressed textures to Cesium 1.31 via the DXTn/BCn, ECT1, and PVRTC WebGL extensions. Textures compressed to these formats stay compressed in GPU memory and are decompressed in parallel in hardware on the fly when sampling the texture. The compression algorithms compress at a fixed rate that can be decompressed quickly on random access of the texture. For transmission over the web, these textures can be compressed further with a lossless compression algorithm, like gzip. The transmitted texture size is comparable to the size of an image compressed to the JPEG format.
GPU compressed texture formats are lossy, meaning that texture details are lost when compressed. There is a trade-off between texture compression time and texture quality; longer compression times improve the quality of the compressed textures. The crunch compressed textures have a smaller transmission size but have an increased compression time and a small increase to load time due to the transcoding to DXTn/BCn at runtime.
There is not one format that is universally supported by all devices. DXTn/BCn is supported by most desktops, ETC1 is supported on most Android devices and some desktops, and PVRTC is supported by most iOS devices. For an application to utilize texture compression, it must test for what formats are supported and request the appropriate texture. An application that needs to support as many devices as possible and use texture compression will need to compress each texture to each of the formats. If there is no texture compression support in the device, either the uncompressed texture or DXTn/BCn can be used. The DXTn/BCn texture can be transcoded to RGB565 on load.
The gltf-pipeline project has been updated to include texture compression options (see the various --texcomp.* options) using Cesium- and 3D Tiles-specific extras properties that reference ktx files. We’ll explore some of the compression formats and compare the compressed output given different quality values. The original image is a typical satellite image or 3D Tile texture. It is a 1024x1024 RGB image. Not all of the compression formats support an alpha channel. Luckily, most satellite imagery and 3D Tile textures will not require one. The transmitted size is 476KB (JPEG), but the in memory decompressed size is 3MB. JPEG compression is also a lossy compression format.
The image differences were generated with Resemble.js. Clicking an image will open the full 1024x1024 image in a new browser tab.
In the tables below, CRN is the format output by crunch. It is a highly compressed, lossy format that quickly transcodes to DXT1 or DXT5. KTX is a container format for storing textures. KTX + gzip is the size of the KTX file after compressing with gzip using the standard settings. PSNR is the peak signal-to-noise ratio. A higher PSNR means the image reconstructed from the compressed image is closer to the source image. The PSNR should only be used to compare an image compressed to the same format. Therefore, one should not compare the PSNR of the images compressed with ETC1 with the PSNR of the images compressed with PVRTC.
All compression times were recorded when running on a 3.20 GHz Intel Xeon E5-1650 CPU and Windows 10. Compressing multiple textures in parallel will, of course, improve compression times for an entire scene.
gltf-pipeline uses crunch to compress to CRN and DXTn/BCn, etc2comp to compress to ETC1, and PVRTexTool to compress to PVRTC. gltf-pipeline will compress textures with commands similar to below. The quality set for each compressed texture is the number above each image.
node ./bin/gltf-pipeline.js -i /path/to/gltf -o /path/to/output --texcomp.quality 9 --texcomp.format crunch-dxt1
node ./bin/gltf-pipeline.js -i /path/to/gltf -o /path/to/output --texcomp.quality 1 --texcomp.format etc1
CRN | 99KB | 117KB | 131KB | 143KB | 153KB |
KTX + gzip | 159KB | 193KB | 218KB | 240KB | 258KB |
PSNR | 26.140 | 27.272 | 27.924 | 28.487 | 28.989 |
Compression Time(s) | 3.127 | 3.506 | 3.413 | 4.076 | 4.581 |
CRN | 164KB | 174KB | 183KB | 192KB | 200KB |
KTX + gzip | 273KB | 286KB | 297KB | 305KB | 311KB |
PSNR | 29.463 | 29.887 | 30.315 | 30.708 | 31.085 |
Compression Time(s) | 5.482 | 6.335 | 7.473 | 9.286 | 11.528 |
DXT5
DXT5 does not compress as well as DXT1, but can also store an alpha channel. The DXT5 compressed texture size is 1025KB, about a third of the size of the uncompressed image. There is also an option to use DXT3. DXT3 uses the same compression for the RGB data of texture but uses less bits to store the alpha channel. DXT5 should be preferred for gradient alpha whereas DXT3 should be preferred for sharp alpha changes. DXT3 is also an unsupported crunch format.
CRN | 122KB | 139KB | 151KB | 162KB | 171KB |
KTX + gzip | 222KB | 261KB | 285KB | 302KB | 315KB |
PSNR | 26.951 | 27.856 | 28.449 | 28.948 | 29.343 |
Compression Time(s) | 4.858 | 5.415 | 5.451 | 6.427 | 7.355 |
CRN | 179KB | 186KB | 194KB | 200KB | 207KB |
KTX + gzip | 323KB | 330KB | 336KB | 341KB | 346KB |
PSNR | 29.745 | 30.091 | 30.420 | 30.737 | 31.018 |
Compression Time(s) | 7.565 | 8.933 | 9.338 | 10.619 | 13.153 |
KTX + gzip | 407KB | 408KB | 409KB | 409KB | 411KB |
PSNR | 38.7594 | 38.9216 | 38.9216 | 39.0379 | 39.1980 |
Compression Time(s) | 1.328 | 1.926 | 2.481 | 3.116 | 5.372 |
KTX + gzip | 413KB | 417KB | 419KB | 421KB | 425KB |
PSNR | 39.2808 | 39.3893 | 39.4450 | 39.4791 | 39.5111 |
Compression Time(s) | 8.221 | 16.419 | 29.390 | 43.279 | 60.042 |
KTX + gzip | 209KB | 210KB | 218KB | 219KB | 219KB |
PSNR | 40.222 | 40.294 | 41.119 | 41.174 | 41.188 |
Compression Time(s) | 0.908954 | 1.1497999 | 9.3760064 | 18.0452953 | 34.1600244 |
KTX + gzip | 357KB | 383KB | 418KB | 422KB | 423KB |
PSNR | 40.515 | 41.300 | 42.348 | 42.423 | 42.449 |
Compression Time(s) | 1.1524799 | 1.4298776 | 3.8276361 | 5.9442022 | 9.9473701 |
Recommendations
For desktops, you will need to use a DXTn/BCn format. If you are going to use crunch, I suggest DXT1 and setting the quality of textures at the root of the tileset to 1 and increase the quality to 10 as you reach the leaf nodes. As you can see in the table above, the quality of crunched textures quickly degrades. The benefit of crunch is the quick load times. The largest crunched texture was still less than half the size of the same texture in the JPEG format.
If you do not want to use crunch, I would suggest DXT1 with the quality set to 1 for all textures. The DXTn/BCn textures that were not crunched are not shown above. The image diffs are very similar to those in the ETC1 table. The lowest quality DXT1 compressed texture has better quality than the highest quality DXT1 crunched texture and is a little faster than the lowest quality crunched texture.
For Android, You will need to use ETC1. I suggest setting the quality to 1. The compression time drastically increases for very little quality gain.
For iOS, you will need to use PVRTC. I suggest using PVRTC 4bpp and setting the quality between 3 and 6. If your app is memory constrained and you can tolerate more error in the compressed texture, you can switch to using PVRTC 2bpp and setting the quality to be between 1 and 4.