-
Notifications
You must be signed in to change notification settings - Fork 320
KTX2 File Format Support Technical Details for basisu v2.1
- Useful External References
- Intro
- Basis Universal Formats
- DFD - Linear vs. sRGB Determination
- DFD - REC 2020/BT2100 Color Gamut Determination
- DFD - Alpha Channel Determination
- Key Value Fields
- Using the basisu command line tool to dump basic KTX2 header info
- Khronos Data Format Specification v1.4.0
- Khronos KTX2 File Format Specification
- Vulkan Buffer and Image Formats
- ASTC Texture Format Specification v1.1 or ASTC Texture Format Specification v1.4
Note as of 2/4/2026: How the HDR and XUASTC LDR formats exactly utilize KTX2 container files is currently in flux, as Khronos is still busy updating their software to support these formats. The below info is up to date as of the Basis Universal v2.0 release. The v2.1 release will be modifying how HDR and XUASTC LDR formats are stored in KTX2, but we're keeping full backwards compatibility with files generated by all previous versions of our library and command line tool.
The Basis Universal transcoder only supports reading compressed texture data from .KTX2 container files in the below specific formats. Our transcoder is not a general purpose .KTX2 reader. However, for ASTC LDR 4x4-12x12 (all 14 standard ASTC block sizes, UNORM or sRGB variants), and ASTC HDR 6x6 the transcoder supports entirely standard ASTC LDR/HDR blocks (i.e. blocks created by our encoder or external encoders).
This document assumes familiarity with the KTX2 container format, Khronos Data Format Descriptors (DFDs), and GPU block-compressed texture formats.
To see exactly how the compressor creates the output KTX2 file's DFD, see encoder/basisu_comp.cpp, method basis_compressor::get_dfd().
In the KTX2 header, the vk_format field must be KTX2_UNDEFINED (0), or one of these values:
// vk_format's basisu v2.0 supports:
// ASTC HDR 4x4 (also see the DFD, or Data Format Descriptor, as our usage is constrained for fast transcoding to BC6H) - this is used for UASTC HDR 4x4
uint32_t KTX2_FORMAT_ASTC_4x4_SFLOAT_BLOCK = 1000066000;
// ASTC HDR 6x6
uint32_t KTX2_FORMAT_ASTC_6x6_SFLOAT_BLOCK = 1000066004;
// ASTC LDR 4x4-12x12 (UNORM and sRGB variants)
uint32_t KTX2_FORMAT_ASTC_4x4_UNORM_BLOCK = 157, KTX2_FORMAT_ASTC_4x4_SRGB_BLOCK = 158;
uint32_t KTX2_FORMAT_ASTC_5x4_UNORM_BLOCK = 159, KTX2_FORMAT_ASTC_5x4_SRGB_BLOCK = 160;
uint32_t KTX2_FORMAT_ASTC_5x5_UNORM_BLOCK = 161, KTX2_FORMAT_ASTC_5x5_SRGB_BLOCK = 162;
uint32_t KTX2_FORMAT_ASTC_6x5_UNORM_BLOCK = 163, KTX2_FORMAT_ASTC_6x5_SRGB_BLOCK = 164;
uint32_t KTX2_FORMAT_ASTC_6x6_UNORM_BLOCK = 165, KTX2_FORMAT_ASTC_6x6_SRGB_BLOCK = 166;
uint32_t KTX2_FORMAT_ASTC_8x5_UNORM_BLOCK = 167, KTX2_FORMAT_ASTC_8x5_SRGB_BLOCK = 168;
uint32_t KTX2_FORMAT_ASTC_8x6_UNORM_BLOCK = 169, KTX2_FORMAT_ASTC_8x6_SRGB_BLOCK = 170;
uint32_t KTX2_FORMAT_ASTC_10x5_UNORM_BLOCK = 173, KTX2_FORMAT_ASTC_10x5_SRGB_BLOCK = 174;
uint32_t KTX2_FORMAT_ASTC_10x6_UNORM_BLOCK = 175, KTX2_FORMAT_ASTC_10x6_SRGB_BLOCK = 176;
uint32_t KTX2_FORMAT_ASTC_8x8_UNORM_BLOCK = 171, KTX2_FORMAT_ASTC_8x8_SRGB_BLOCK = 172; // note the ASTC block size order is off in the vkFormat definitions
uint32_t KTX2_FORMAT_ASTC_10x8_UNORM_BLOCK = 177, KTX2_FORMAT_ASTC_10x8_SRGB_BLOCK = 178;
uint32_t KTX2_FORMAT_ASTC_10x10_UNORM_BLOCK = 179, KTX2_FORMAT_ASTC_10x10_SRGB_BLOCK = 180;
uint32_t KTX2_FORMAT_ASTC_12x10_UNORM_BLOCK = 181, KTX2_FORMAT_ASTC_12x10_SRGB_BLOCK = 182;
uint32_t KTX2_FORMAT_ASTC_12x12_UNORM_BLOCK = 183, KTX2_FORMAT_ASTC_12x12_SRGB_BLOCK = 184;
If vk_format is not KTX2_UNDEFINED, the transcoder expects the block data to be bit-compatible with the corresponding Vulkan/Khronos texture format, and the DFD must not contradict it. (Also, as a side note: in practice we've seen at least one major KTX2 reader ignore the DFD entirely, and only pay attention to the vk_format.)
The supercompression field must be:
uint32_t KTX2_SS_NONE = 0; // data is raw
uint32_t KTX2_SS_BASISLZ = 1; // file must have global supercompressed data (sgd_byte_offset/sgd_byte_length must be valid)
uint32_t KTX2_SS_ZSTANDARD = 2; // data is Zstd compressed
The transcoder supports 2D or cubemap textures, with or without mipmaps. Array textures of any type (2D/cubemap, with or without mipmaps) are supported. 3D textures are not supported (i.e. the header's pixel_depth must be 0). Cubemaps must have square dimensions.
The vk_format field must be KTX2_UNDEFINED, and the DFD color model must be:
uint32_t KTX2_KDF_DF_MODEL_ETC1S = 163; // 0xA3
ETC1S textures may have an alpha channel (see below).
The supercompression type must be KTX2_SS_BASISLZ.
The supercompression global data contains compressed codebooks. See the ETC1S spec for details on the format.
The vk_format field must be KTX2_UNDEFINED, and the DFD color model must be:
uint32_t KTX2_KDF_DF_MODEL_UASTC_LDR_4X4 = 166; // 0xA6
UASTC LDR 4x4 textures may have an alpha channel. Here's the UASTC LDR 4x4 spec. Internally UASTC LDR 4x4 uses a conservative union of both the ASTC LDR 4x4 and BC7 latents, but the block packing is simplified to free up space for hint values to accelerate transcoding to other formats.
UASTC LDR 4x4 data may be supercompressed with Zstd.
The vk_format field must be KTX2_FORMAT_ASTC_4x4_SFLOAT_BLOCK, and the DFD color model must be:
uint32_t KTX2_KDF_DF_MODEL_UASTC_HDR_4X4 = 167; // 0xA7
UASTC HDR 4x4 data is 100% standard ASTC HDR 4x4 data, but constrained for fast transcoding to BC6H (unsigned variant only - ASTC HDR is unsigned). The UASTC HDR 4x4 spec describes the exact configurations supported. The texture data is standard ASTC HDR, but it cannot utilize configurations that are not supported by the UASTC HDR 4x4 spec, otherwise fast latent to latent transcoding from ASTC HDR to BC6H wouldn't be practical.
UASTC HDR 4x4 textures do not support alpha channels. ASTC HDR 4x4 does, but the constrained subset of ASTC HDR we support to enable fast, simple transcoding to BC6H does not. (BC6H doesn't support alpha.)
UASTC HDR 4x4 data may be supercompressed with Zstd.
The vk_format field must be KTX2_FORMAT_ASTC_6x6_SFLOAT_BLOCK, and the DFD color model must be:
uint32_t KTX2_KDF_DF_MODEL_ASTC = 162; // 0xA2
The transcoder uses a fully compliant ASTC HDR decoder whenever it has to decode this format to plain texels. If the ASTC HDR 6x6 block data has alpha, it will be decoded correctly but the alpha data may be ignored depending on the output transcode format. (BC6H doesn't support alpha.)
ASTC HDR 6x6 data may be supercompressed with Zstd.
The vk_format field must be KTX2_UNDEFINED, and the DFD color model must be:
uint32_t KTX2_KDF_DF_MODEL_UASTC_HDR_6X6_INTERMEDIATE = 168; // 0xA8
The supercompression type must be KTX2_SS_BASISLZ.
The supercompressed global data contains a seek table: an array of ktx2_slice_offset_len_desc struct's, one per KTX2 "image" (the image count = max(header.layer_count, 1) * header.face_count * header.m_level_count):
struct ktx2_slice_offset_len_desc
{
uint32_t m_slice_byte_offset;
uint32_t m_slice_byte_length;
};
UASTC HDR 6x6i doesn't support alpha channels. Here's the UASTC HDR 6x6i spec.
Note: UASTC HDR 6x6i is the "Codec". Each mipmap level has a small header or marker which defines the exact "version" (or variant, or semantic contract) used by that mipmap level. Each mipmap level may in theory use a different version. In practice, they'll very likely all use the same version. The version field allows Binomial to signal to the transcoder exactly which semantics should be used during transcoding. Binomial will always keep transcoder compatibility with all versions whenever possible. Currently there are 2 stream versions (those written by v1.6, and later by v2.0).
ASTC LDR data may be supercompressed with Zstd.
The vk_format field in the header must be between [KTX2_FORMAT_ASTC_4x4_UNORM_BLOCK, KTX2_FORMAT_ASTC_12x12_SRGB_BLOCK].
The DFD's model color field must be:
uint32_t KTX2_KDF_DF_MODEL_ASTC = 162; // 0xA2
The block size fields in the DFD must be set correctly and match the vk_format field.
The UNORM vk_format's will be emitted if the encoder was in linear mode, otherwise sRGB. This field slightly modifies how ASTC LDR blocks will be decoded by the transcoder (see the ASTC LDR specification).
The transcoder uses a fully compliant ASTC LDR decoder whenever it has to decode these formats.
ASTC LDR 4x4-12x12 may have an alpha channel.
ASTC LDR 4x4-12x12 data may be supercompressed with Zstd.
The vk_format field must be KTX2_UNDEFINED, and the DFD color model must be:
uint32_t KTX2_KDF_DF_MODEL_XUASTC_LDR_INTERMEDIATE = 169; // 0xA9
The supercompression type must be KTX2_SS_BASISLZ.
The ASTC block dimensions are written into the DFD.
The supercompression global data contains seek tables (see UASTC HDR 6x6i above).
XUASTC LDR 4x4-12x12 may have an alpha channel. See the XUASTC LDR spec.
Note: XUASTC LDR is the "Codec". Each mipmap level has a small header which defines the exact XUASTC LDR entropy "profile" (arithmetic, hybrid, or full Zstd), and the "version" (or variant, or semantic contract) used by that mipmap level. Each mipmap level may use a different profile, and (in theory) a different version. (In practice, they'll very likely all use the same profile version.) The version field allows Binomial to signal to the transcoder exactly which semantics should be used during transcoding. Binomial will always keep transcoder compatibility with all versions whenever possible. Currently there is only a single version of XUASTC LDR for all profiles.
For LDR formats, the DFD's transfer function field will be set to either LINEAR or sRGB, which indicates which ASTC LDR decoding profile was used during encoding. The HDR, it'll always be LINEAR (i.e. no transfer function).
uint32_t KTX2_KHR_DF_TRANSFER_LINEAR = 1;
uint32_t KTX2_KHR_DF_TRANSFER_SRGB = 2;
The C++ compressor API has numerous low-level settings (in struct basis_compressor_params) controlling compression and KTX2 file format generation. If the m_astc_hdr_6x6_options.m_rec2020_bt2100_color_gamut bool flag was set to true, the DFD's color primary field will be set to BC2020 (4). This is done even if the output format isn't ASTC HDR 6x6 or UASTC HDR 6x6i. (We'll be improving how this is handled in the future.)
enum ktx2_df_color_primaries
{
KTX2_DF_PRIMARIES_UNSPECIFIED = 0,
KTX2_DF_PRIMARIES_BT709 = 1,
KTX2_DF_PRIMARIES_SRGB = 1,
KTX2_DF_PRIMARIES_BT601_EBU = 2,
KTX2_DF_PRIMARIES_BT601_SMPTE = 3,
KTX2_DF_PRIMARIES_BT2020 = 4,
KTX2_DF_PRIMARIES_CIEXYZ = 5,
KTX2_DF_PRIMARIES_ACES = 6,
KTX2_DF_PRIMARIES_ACESCC = 7,
KTX2_DF_PRIMARIES_NTSC1953 = 8,
KTX2_DF_PRIMARIES_PAL525 = 9,
KTX2_DF_PRIMARIES_DISPLAYP3 = 10,
KTX2_DF_PRIMARIES_ADOBERGB = 11
};
For ETC1S: The texture is assumed to have alpha if the DFD's length is 60 bytes (2 samples), otherwise it's assumed to be RGB only.
For UASTC LDR 4x4, ASTC LDR 4x4-12x12, or XUASTC LDR 4x4-12x12, the DFD's channelType field (at uint32_t offset 7 relative to the beginning of the DFD or the initial DFD totalSize field, bit offset 24, 4-bits length) is interpreted to determine if the texture has alpha:
uint32_t KTX2_DF_CHANNEL_UASTC_RGBA = 3U;
uint32_t KTX2_DF_CHANNEL_UASTC_RRRG = 5U;
If channelType is either of these values, it's assumed the texture has an alpha channel. See the Khronos Data Format Specification v1.4.0, "Section 5.6.14 KHR_DF_MODEL_UASTC" for the table of UASTC channel names.
Our other formats don't support alpha channels.
The key KTXwriter will be set to the string Basis Universal 2.00.
The ldrHDRUpconversionNitMultiplier key indicates the HDR texture was automatically upconverted to HDR from an SDR/LDR asset (i.e. by converting it to linear light by undoing the sRGB transfer function and then multiplying the normalized linear [0.0, 1.0] intensity value by typically 80-100 nits). The value will be a decimal number, typically 100 nits unless changed by the user during compression time.
An additional key value field, indicating the HDR scale value to be applied to the decoded half-float texels, is planned to be added.
Our command line tool can help examine our generated .KTX2 files. Example on bik158.ktx2:
C:\dev\bin>wasmtime --wasm threads=yes --wasi threads=yes --dir=. basisu_mt.wasm -info bik158.ktx2
Basis Universal LDR/HDR GPU Texture Supercompression System v2.00.0 (WASI Threaded)
Copyright (C) 2019-2026 Binomial LLC, All rights reserved
No SSE, Multithreading: 1, Zstandard support: 1, OpenCL: 0
Input file "bik158.ktx2", KTX2: 1
Resolution: 960x960
Block size: 6x6
Mipmap Levels: 1
Texture Array Size (layers): 0
Total Faces: 1 (2D)
Is Texture Video: 0
Supercompression Format: XUASTC_LDR_6x6
Supercompression Scheme: BASISLZ
Has Alpha: 0
KTX2 header vk_format: 0x0 (decimal 0)
Data Format Descriptor (DFD):
DFD length in bytes: 44
DFD color model: 169
DFD color primaries: 1 (BT709)
DFD transfer func: 2 (SRGB)
DFD flags: 0
DFD samples: 1
DFD chan0: RGB
DFD hex values:
0x2C,0x0,0x0,0x0,
0x0,0x0,0x0,0x0,
0x2,0x0,0x28,0x0,
0xA9,0x1,0x2,0x0,
0x5,0x5,0x0,0x0,
0x0,0x0,0x0,0x0,
0x0,0x0,0x0,0x0,
0x0,0x0,0x7F,0x0,
0x0,0x0,0x0,0x0,
0x0,0x0,0x0,0x0,
0xFF,0xFF,0xFF,0xFF
Total key values: 1
0. Key: "KTXwriter", Value length in bytes: 22 Value Bytes: 0x42,0x61,0x73,0x69,0x73,0x20,0x55,0x6E,0x69,0x76,0x65,0x72,0x73,0x61,0x6C,0x20,0x32,0x2E,0x30,0x30,0x0,0x0
Levels:
0. Offset: 192, Length: 149083, Uncompressed Length: 0
Image level info:
--- Level Index: 0, Layer Index: 0, Face Index: 0
Orig width/height: 960x960
Width/height: 960x960
Block width/height: 6x6
Num blocks: 160x160, Total blocks: 25600
Alpha flag: false, I-frame flag: false
Success