2

I'm reading about loading DDS textures. I read this article and saw this posting. (I also read the wiki about S3TC)

I understood most of the code, but there's two lines I didn't quite get.

blockSize = (format == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT) ? 8 : 16;

and:

size = ((width + 3) / 4) * ((height + 3) / 4) * blockSize;

and:

bufsize = mipMapCount > 1 ? linearSize * 2 : linearSize;
  1. What is blockSize? and why are we using 8 for DXT1 and 16 for the rest?

  2. What is happening exactly when we're calculating size? More specifically why are we adding 3, dividing by 4 then multiplying by blockSize?

  3. Why are we multiplying by 2 if mipMapCount > 1?
vexe
  • 5,433
  • 12
  • 52
  • 81

1 Answers1

3

DXT1-5 formats are also called BCn formats (it ends with numbers but not exactly the same ones) and BC stands for block compression. Pixels are not stored separately, it only stores a block of data for the equivalent of 4x4 pixels.

The 1st line checks if it's DXT1, because it has a size of 8 byte per block. DXT3 and DXT5 have use 16 bytes per block. (Note that newer formats exist and at least one of them is 8 bytes/block: BC4).

The 2nd rounds up the dimensions of the texture to a multiple of the dimensions of a block. This is required since these formats can only store blocks, not pixels. For example, if you have a texture of 15x6 pixels, and since BCn blocks are 4x4 pixels, you will need to store 4 blocks per column, and 2 blocks per row, even if the last column/row of blocks will only be partially filled.

One way of rounding up a positive integer (let's call it i) to a multiple of another positive integer (let's call it m), is:

(i + m - 1) / m * m

Here, we need get the number of blocks on each dimension and then multiply by the size of a block to get the total size of the texture. To do that we round up width and height to the next multiple of 4, divide by 4 to get the number of block and finally and multiply it by the size of the block:

size = (((width + 3) / 4 * 4) * ((height + 3) / 4 * 4)) / 4 * blockSize;
//                         ^                        ^     ^

If you look closely, there's a *4 followed by a /4 that can be simplified. If you do that, you'll get exactly the same code you had. The conclusion to all this could be comment any code that's not perfectly obvious :P

The 3rd line may be an approximation to calculate a buffer size big enough to store the whole mipmap chain easily. But I'm not sure what this linearSize is; it correspond to dwPitchOrLinearSize in the DDS header. In any case, you don't really need this value since you can calculate the size of each level easily with the code above.

Jerem
  • 1,725
  • 14
  • 24
  • Thanks for the reply. Could you elaborate more on the second line? Not sure what you mean by `N-1`? it seems that this 'size' is the size of a mipmap? I say that because then he's reading from the buffer by 'size' amount. Why can't we just do `width * height * blockSize`? The 'size' calculation has to be correct and accurate because we're incrementing our `offset` by it which we then add to our `buffer` to read the next mipmap. Wouldn't rounding yield inaccurate results in this case? (cause it's not the actual size of the mipmap) – vexe Nov 01 '15 at 18:30
  • I edited my post to add more explanations for the 2nd line (and a little bit for the others too). Tell me if something's left unclear. – Jerem Nov 01 '15 at 19:52
  • Not sure about the simplification part. Let `w` denote `width+3` and `h` denote `height+3` we get `(w / 4 * 4) * (h / 4 * 4) / 4 * blockSize` = `w * h / 4 * blockSize`? even if you meant `(w / (4 * 4))` ... it still won't equal the initial code cause there's only one 4 to simplify. am I missing something? – vexe Nov 01 '15 at 20:05
  • I added parenthesis and `^` below the fours that can be simplified. Note that `w / 4 * 4` in your comment cannot be simplified to `w` because it's an integer division (eg. 3 / 4 = 0 so 3 / 4 * 4 is still 0, not 3). Simplifying `w * 4 / 4`, however, is safe. – Jerem Nov 01 '15 at 20:42