Saturday, May 30, 2026

Basis Universal v2.5 with in-loop deblocking: shipping soon

We're feature complete. We're now on the downslope to shipping support for in-loop deblocking using a simple standardized reconstruction operator. The operator uses 1 tap or 5 taps (1 center, 2 up/down, 2 left/right) near ASTC block edges, and is fully compatible with mipmapping and bilinear/trilinear filtering. It can be applied in a simple GPU pixel shader or during transcoding. 

It's so simple I'm stumped why the IHV's didn't put this obvious thing directly in the sampling hardware. It makes the largest ASTC block sizes (10x8, 10x10, 12x10 and 12x12) immensely more usable in practice.

The encoder's final SCD (Stochastic Coordinate Descent) stage computes the final output taking into account the deblocking filter. The encoder simulates the exact filter the decoder will use, then optimizes the compressed data knowing the artifacts will be smoothed. This dramatically improves quality at low bitrates. 

We're shipping two full ASTC LDR encoders (an optimized version of our original one from v2.0, and a new one called "astcf") that have been modified to output 10's to 100's of block candidates for SCD. 

We're also going to optionally support the use of a slightly modified/forked version of ARM's "astcenc" library that can output candidates for use in our SCD stage. The library supports merging the output from (up to) all 3 encoders. We're supporting this because each encoder has different artifact profiles, which boosts SCD candidate diversity. Our second ASTC encoder was purposely engineered to look very different vs. our first.

With the largest ASTC/XUASTC block sizes, 80-90% of our output blocks are now created stochastically via SCD. The mutation operator can modify endpoints, partition patterns, and DCT AC/DC coefficients.

The output looks incredible at 12x12 ~0.5 bpp.

Thursday, May 7, 2026

bug found in Intel's ASTC compressor (part of ispc_texcomp)

I've ported Intel's ASTC compressor (in ispc_texcomp) to pure C for benchmarking and discovered this gem during the process. The swap()utility function for float values, located near the top, has a silly typo bug that casts the first value to int (!). It's used in several places on floating point values, including block errors (scores).

https://github.com/GameTechDev/ISPCTextureCompressor/blob/master/ispc_texcomp/kernel_astc.ispc#L41

How could a bug like this survive for so long? What other unfound bugs are in there?

I might release the port on GitHub after more testing, as time permits. I'll be checking for undefined behavior using UBSan, and also doing a statistical analysis on its ASTC output blocks to check for any issues.

I've already enhanced it to support sRGB decode vs. linear, candidate generation (instead of just best block by SSE), and I've added optional per-channel weights. For a single subset encoder (that only supports 8x8 or smaller block sizes) it looks surprisingly good - once swap() is fixed.