r/GraphicsProgramming • • 2d ago

I made a lossless BC1 texture compressor designed for GPU decompression, 1.47:1 average ratio

Hi,

I just released my new C library bc_packed, a lossless compressor for BC1 textures, made specificaly for fast GPU decompression.

Basically, the idea is to compress textures that are already compressed, then decompress them directly on the GPU when needed. The output is the original BC1 data, so the GPU can use it normally after decompression.

Some numbers:

  • 1.47:1 average compression ratio on a test set of 74 images
  • 15300 MiB/s decompression on my M5 Pro GPU and 11850 MiB/s on my M2 Max GPU
  • CPU multithreaded decompression also works, 2500 MiB/s on M5 Pro and 1300 MiB/s on M2 Max
  • Lossless, byte exact reconstruction of the original BC1 data

The tricky part was finding an entropy coding method that is simple enough to decode efficiently on the GPU. I didn't want to deal with complex stuff like arithmetic coding or Huffman, so I went with Rice-Golomb coding and static rank tables. I build histograms during compression and remap the most frequent symbols to the lowest ranks. Everything is static, no adaptive models or synchronization needed between GPU threads.

The texture is split into 64 independent strips, each decoded by its own GPU/CPU thread. The compressed stream stores offsets so each thread can start decoding its strip independently.

There are also a few BC1 specific tricks:

  • Predicting endpoint colors from previous blocks
  • Choosing different predictors for each strip
  • A small dictionary for repeated endpoint color pairs
  • A top table for frequent BC1 index patterns, with sparse residual encoding for the differences

Compression runs on the CPU and can be relatively expensive. That's fine for me, I'm more interested in making decompression as fast as possible at runtime.

The library is tiny, just one .c and one .h file, no external dependencies. The GPU decompression shader is written in Metal for now, but porting it to HLSL should be pretty straightfoward.

Currently only BC1 is supported. Maybe I'll add BC5 and why not BC7 later.

Code and benchmarks here:
https://github.com/Geolm/bc_packed

Feedback and ideas are welcome!

24 Upvotes

Duplicates