Skip to content

UASTC implementation details

Rich Geldreich edited this page Apr 13, 2021 · 6 revisions

.basis/.KTX2 files support both UASTC and ETC1S texture data. In UASTC mode, there is no extra lossless compression applied apart from UASTC itself in .basis files, so the raw UASTC .basis file sizes are quite large (8-bpp). This is not true for .KTX2 files, which support additional lossless Zstandard supercompression layered on top of the lossy UASTC texture data.

The encoder supports an optional Rate-Distortion Optimization (RDO) mode, which reduces the entropy of the output lossy UASTC texture data by conditioning it for better lossless LZ compression. This trades off quality for fewer LZ compressed bits, in an intelligent way that tries to increase the probability/density of close (low distance) LZ matches. In our experiences, we've seen bitrates (using Deflate) ranging between 2-7bpp, with the typical usable bitrate being around 5-7bpp. With the current RDO encoder, the sweet spot is around 5-6 bpp.

A previously unused field in .basis files indicates the internal universal texture format (ETC1S or UASTC). See basis_file_header::m_tex_format. UASTC files reuse all the existing ETC1S file structures. There are no codebooks in UASTC mode, so a bunch of header fields (like m_total_endpoints, m_endpoint_cb_file_ofs, etc.) are zero. The helper method basisu_transcoder::get_tex_format() can be used to determine the texture format of a .basis file.

The transcoder API transparently supports both ETC1S and UASTC files. The UASTC code was added to the transcoder in transcoder/basisu_transcoder.cpp. It can optionally be disabled by setting the BASISD_SUPPORT_UASTC preprocessor macro to 0.

There are three ways of accessing the new UASTC functionality:

  1. Use the command line tool to encode UASTC .basis files using the -uastc, -uastc_rdo_l, and -uastc_level options. Then use basisu_transcoder::transcode_image_level() and transcode_slice() which transparently support both ETC1S and UASTC files. This is the way web developers use the system, by compiling the transcoder's .cpp file to WebAssembly.

  2. A lowlevel class, basisu_lowlevel_uastc_transcoder, was added with a single method that transcodes UASTC data to GPU texture data in any format: basisu_lowlevel_uastc_transcoder::transcode_slice(). Internally, this is what basisu_transcoder::transcode_image_level() uses on UASTC .basis files.

You could roll your own file format and just call this helper to transcode the UASTC data.

  1. For even lower level access to the UASTC functionality, developers can ignore .basis and our high/low level helpers completely and just use the very lowest level UASTC block functions directly. transcoder/basisu_transcoder_uastc.h declares the UASTC transcode functions, and basisu_uastc_enc.h declares the UASTC encode functions.

Note that before using any encoder functions you must call basisu_encoder_init(). Similarly, before calling any transcoder functions you must call basist::basisu_transcoder_init().

The two functions pack_uastc() and uastc_rdo() are used for encoding individual blocks (with no RDO), or groups of blocks with RDO. They are very simple to use. You call them with a pointer to 4x4 RGBA pixel blocks, and flags/options, and you get back packed 128-bit UASTC blocks.

The key lowest-level block transcoder functions defined in basisu_uastc_enc.h are:

transcode_uastc_to_bc1(), transcode_uastc_to_bc3(), transcode_uastc_to_bc4(), transcode_uastc_to_bc5(), transcode_uastc_to_bc7(), transcode_uastc_to_astc(), etc.

Note that we will be optimizing transcode_uastc_to_etc2_eac_r11() and transcode_uastc_to_etc2_eac_rg11() to be roughly 2-2.5x faster. We targeted too high a quality for EAC R11/RG11. We will also be further optimizing UASTC->ASTC and UASTC->BC7. I would expect ~2x faster without using any SIMD.