Why does ASTC use ISE when almost nothing else does? | The ryg blog
Skip to content
Follow:<br>RSS<br>Twitter
The ryg blog
When I grow up I'll be an inventor.
Home
About
Coding
Compression
Computer Architecture
Demoscene
Graphics Pipeline
Maths
Multimedia
Networking
Papers
Stories
Thoughts
Uncategorized
Why does ASTC use ISE when almost nothing else does?
May 29, 2026
The ASTC texture compression format has its "integer sequence encoding" to send small integers with a uniform probability distribution within their range. When that value range is [0,2k-1] for some integer k, this is straightforward: just send the values with k bits each. But ISE ups the ante by supporting not just power-of-2 sizes ranges, but also allowing a single prime factor of either 3 or 5 in the size of the range. So we can, for example, have some value x in the range [0,95], 96 total, where the low 5 bits are sent regularly, and then we have the "high trits" ⌊x/32⌋\lfloor x/32 \rfloor that are always in the range [0,2]. When the extra prime factor is 3, these trits are arranged in groups of 5. The number of possible combinations of 5 values in the range [0,2] is 35 = 243 The actual answer is at least slightly more interesting: because of course in practice this method isn’t competing with just rounding up to the nearest integer bit multiple, we could also use a prefix code. With 3 values, which in the following I’ll write as "a", "b" and "c" to avoid confusion with actual bit strings, there is only one real choice of prefix code: one of the values gets sent with a 1-bit code and the other two values each get a 2-bit code. Something like
a -> 0<br>b -> 10<br>c -> 11
You can change which of the three codes gets the 1-bit code length, but it’s always one of them. Our assumption is that all three values are equally likely, so the expected cost of a value ends up being 1/3 * (1 + 2 + 2) = 5/3 bits, or about 1.67 bits per symbol. The ISE encoding that sends 8 bits for every group of 5 values averages 8/5 = 1.60 bits per symbol. (For reference, the theoretical minimum is log2(3) ≈ 1.585 bits per symbol.)
And suddenly the difference looks a lot less drastic. Compared to a straightforward prefix code, the expected savings shrink from 0.4 bits per symbol down to about 0.07 bits per symbol, so we expect to save a single bit slightly less often than one in every 14 values.
ASTC blocks are not large. They have 128 bits, total, and of the modes that use ISE at all, at least 17 of those bits are, effectively headers. With at most 111 bits left for payload, the largest number of trit values we can even theoretically send in a block, assuming we stick with the minimum possible [0,2] value range works out to… 69 (I swear I didn’t fudge this). If you ever managed to cram that many values into a block (not actually possible in the format for reasons that don’t matter here), ISE’s expected savings over the bog-standard prefix code work out to under 5 bits, and because we have to round to an integer size at block boundaries, that means the trits can only ever save a maximum of 4 bits over a really elementary prefix code, in the most extreme case. In practice, most blocks don’t actually have that many values, and many of them will in fact not even make it over the threshold where the expected saving crosses 1 bit!
Now, I hopefully have you convinced that the question in the title is at least somewhat meaningful. Now clearly, eventually, with enough data, the savings from something like ISE are definitely noticeable. It’s just that ASTC blocks are quite short and can’t send that many values to begin with; what do we care about the long run if we can never get there? Sure, we might save 1-3 bits over the block as a whole, but that’s in a range where we possibly could’ve used a slightly more compact encoding in the header fields, maybe gotten rid of some rarely-used options, and gotten those extra bits that way, instead of dealing with the extra complexity that ISE brings. (Which is, on the whole, not that big a deal, other parts of ASTC are also rather complex, but it’s still a question worth asking.)
And now that I’ve convinced you that this is not quite as trivial a question as it may seem, time to fess up: I’ve not been entirely honest with you in this post. I haven’t said anything outright wrong (at least, not intentionally), but I’ve been using the word "expected" multiple times, and that is doing a lot of heavy lifting here.
It’s true, for uniformly distributed random input data, we expect the a ↦ 0, b ↦ 10, c ↦ 11 prefix code (or one of its permutations) to average 1.67 bits per symbol. But it depends on the data! If all the trits we send happen to be a’s, we’ll actually only use 1-bit codes and send 1 bit per symbol for that exact input sequence. Conversely, if there’s not a single ‘a’ in the data we want to encode, we’ll actually end up using 2 bits per symbol. With ISE, the size doesn’t depend on the data. You need to send n trits,...