BinomialLLC / basis_universal

Basis Universal GPU Texture Codec
Apache License 2.0
2.7k stars 263 forks source link

-uastc_rdo_l doesn't seem to do anything #345

Closed TobiasWehrum closed 1 year ago

TobiasWehrum commented 1 year ago

The readme says:

To compress a image to a higher quality UASTC .basis file:

basisu -uastc -uastc_level 2 x.png

To compress a image to a higher quality UASTC .basis file with RDO post processing, so the .basis file is more compressible:

basisu -uastc -uastc_level 2 -uastc_rdo_l .75 x.png

-uastc_rdo_l X controls the rate distortion stage's quality setting. The lower this value, the higher the quality, but the larger the compressed file size. Good values to try are between .2-3.0. The default is 1.0. RDO post-processing is currently pretty slow, but we'll be optimizing it over time.

I tried both basisu -uastc -uastc_level 2 x.png and basisu -uastc -uastc_level 2 -uastc_rdo_l .75 x.png on Windows 10 using the current version on NPM (1.16.3), and both results have the exact same size.

thokra1 commented 1 year ago

Straight from the introduction:

The UASTC encoder has an optional Rate Distortion Optimization (RDO) encoding mode (implemented as a post-process over the encoded UASTC texture data), which conditions the output texture data in a way that results in better lossless compression when UASTC .basis files are compressed with Deflate/Zstd, etc. In UASTC mode, you must losslessly compress .basis files yourself. .KTX2 files have built-in lossless compression support using Zstandard, which is used by default on UASTC textures.

The key things here being

when UASTC .basis files are compressed with Deflate/Zstd, etc.

and

In UASTC mode, you must losslessly compress .basis files yourself.

because, AFAIR, BasisU doesn't provide built-in compression of the encoded data for UASTC, only for ETC1S. This means that you have to do an additional, manual compression pass when using .basis containers. As stated above, going with KTX2 containers gives you the opportunity to have the contained data ZStandard-compressed. With the BasisU CLI, all your need to do is provide the -ktx2 option:

-ktx2: Write .KTX2 ETC1S/UASTC files instead of .basis files. By default, UASTC files will be compressed using Zstandard unless -ktx2_no_zstandard is specified.\n

When doing it programmatically with libKTX, you'd do

// assume 'tex' is a ktxTexture2* and '19 is the ZStandard compression level
ktxTexture2_DeflateZstd(tex, 19);

after compression to UASTC and application of RDO to that data. The resulting size of the KTX2 container will be smaller with RDO applied, unless RDO cannot actually optimize anthing further, ofc.

So, do some manual compression af the result you got from the CLI and see for yourself. It's worth to add some compression on your binaries anyway, depending on what your use-case is. For instance, for stuff transmitted over the web, all the additional lossless compression (like ZStd) you can get is worth it IMHO.

TobiasWehrum commented 1 year ago

That makes sense, thanks!

Ttofuuu commented 1 year ago

Here is my thoughts after reading babisu_comp.cpp: when it comes to uastc, BabisU just pack the texture into many astc blocks, and save these blocks directly with the .basis file extension so that the GPU can decode them by the astc decompression format; but when it comes to .ktx2 file, GPU is unable to handle the decompression of Zstd-compression used in the ktx saving work. Did I miss something or up to now it works just like this?

thokra1 commented 1 year ago

ZStandard does not produce anything that GPUs can understand. It's used on top of the block compression to further reduce the size of the KTX payload and thus the file size. Since it's not directly consumable by a GPU, the payload has to first be decompressed and then u can use that clock compressed data with your API of choice.

Ttofuuu commented 1 year ago

Thank you, here comes my understanding after reading your comments: image Btw, in my eye the low-level operation of RDO mode in uastc is just replacing a lot of blocks' texel weight data with some few blocks' texel weight data when they are measured to be similar, so that the same weight datas can be seen as one symbol then zstd can compress them more efficiently, right?