Format Version: 2.0

Document Revision: draft8

Editor: Mark Callow (Edgewise Consulting)


KTX2 is a format for storing textures for OpenGL, OpenGL ES and Vulkan applications. It is distinguished by the simplicity of the loader required to instantiate texture objects from the file contents.

It extends the KTX format with support for easier loading of Vulkan textures, easier use by non-OpenGL and non-Vulkan applications, storage of multisample images, streaming and supercompression.

Status of this document

WIP. Under review; not ready for implementation.

1. File Structure

Basic Structure
Byte[12] identifier
UInt32 endianness
UInt32 glType
UInt32 glTypeSize
UInt32 glFormat
Uint32 glInternalFormat
Uint32 vkFormat
UInt32 pixelWidth
UInt32 pixelHeight
UInt32 pixelDepth
UInt32 numberOfArrayElements
UInt32 numberOfFaces
UInt32 numberOfMipmapLevels
UInt32 levelOrder
UInt32 supercompressionScheme

UInt32 bytesOfDataFormatDescriptor (1)
for each dfDescriptorBlock that fits in bytesOfDataFormatDescriptors
    dfDescriptorBlock descriptorBlock
    blockPadding[3 - ((descriptor_block_size + 3) % 4)]

UInt32 bytesOfKeyValueData
for each keyValuePair that fits in bytesOfKeyValueData
    UInt32   keyAndValueByteSize
    Byte     keyAndValue[keyAndValueByteSize]
    Byte     valuePadding[3 - ((keyAndValueByteSize + 3) % 4)]
Byte keyValuePadding[0|4]

UInt64 bytesOfSupercompressionGlobalData
Byte supercompressionGlobalData[bytesOfSupercompressionsGlobalData]
Byte sgdPadding[7 - ((bytesOfSupercompressionGlobalData + 7) % 8)]]

UInt64 bytesOfImages
UInt64 bytesOfUncompressedImages

for each mipmap level in numberOfMipmapLevels (2)
    UInt64 levelOffset

for each mipmap_level in numberOfMipmapLevels (2)
    Uint64 bytesOfLevelImages
    Uint64 bytesOfUncompressedLevelImages
    Byte levelImages[bytesOfLevelImages] (3)
    Byte mipPadding[0-7]
1 These 5 lines are a Data Format Descriptor. See Data Format Descriptor.
2 Replace with 1 if this field is 0 or if glInternalFormat is one of the GL_PALETTE* formats from GL_OES_compressed_paletted_texture [OESCPT].
3 See levelImages below.

After inflation from supercompression or when supercompressionScheme == 0, levelImages looks like this:

levelImages Structure
for each array_element in numberOfArrayElements (1)
   for each face in numberOfFaces (2)
       for each z_slice in pixelDepth (1)
           for each row or row_of_blocks in pixelHeight (1)
               for each pixel or block_of_pixels in pixelWidth
                   Byte data[format-specific-number-of-bytes] (3)
1 Replace with 1 if this field is 0.
2 Must be 1 if glInternalFormat is one of the GL_PALETTE* formats from GL_OES_compressed_paletted_texture [OESCPT].
3 Rows of uncompressed texture images must be tightly packed, equivalent to a GL_UNPACK_ALIGNMENT of 1.

2. Field Descriptions

2.1. identifier

The file identifier is a unique set of bytes that will differentiate the file from other types of files. It consists of 12 bytes, as follows:

Byte[12] FileIdentifier = {
  0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A

This can also be expressed using C-style character definitions as:

Byte[12] FileIdentifier = {
  '«', 'K', 'T', 'X', ' ', '2', '2', '»', '\r', '\n', '\x1A', '\n'

The rationale behind the choice of values in the identifier is based on the rationale for the identifier in the PNG specification. This identifier both identifies the file as a KTX file and provides for immediate detection of common file-transfer problems.

  • Byte [0] is chosen as a non-ASCII value to reduce the probability that a text file may be misrecognized as a KTX file.

  • Byte [0] also catches bad file transfers that clear bit 7.

  • Bytes [1..6] identify the format, and are the ascii values for the string "KTX 22".

  • Byte [7] is for aesthetic balance with byte 1 (they are a matching pair of double-angle quotation marks).

  • Bytes [8..9] form a CR-LF sequence which catches bad file transfers that alter newline sequences.

  • Byte [10] is a control-Z character, which stops file display under MS-DOS, and further reduces the chance that a text file will be falsely recognised.

  • Byte [11] is a final line feed, which checks for the inverse of the CR-LF translation problem.

2.2. endianness

endianness contains the number 0x04030201 written as a 32 bit integer. If the file is little endian then this is represented as the bytes 0x01 0x02 0x03 0x04. If the file is big endian then this is represented as the bytes 0x04 0x03 0x02 0x01. When reading endianness as a 32 bit integer produces the value 0x04030201 then the endianness of the file matches the the endianness of the program that is reading the file and no conversion is necessary. When reading endianness as a 32 bit integer produces the value 0x01020304 then the endianness of the file is opposite the endianness of the program that is reading the file. In that case the program reading the file must endian convert all header UInt32s and UInt64s and, when glTypeSize != 1, all data to the endianness of the program (i.e. a little endian program must convert from big endian, and a big endian program must convert from little endian).

2.3. glType

For block compressed textures, glType must equal 0. For uncompressed textures, glType specifies the type parameter passed to glTex{,Sub}Image*D, usually one of the values from table 8.2 of the OpenGL 4.6 specification [OPENGL46] (UNSIGNED_BYTE, UNSIGNED_SHORT_5_6_5, etc.)

2.4. glTypeSize

glTypeSize specifies the data type size that should be used when endianness conversion is required for the texture data stored in the file. If glType is not 0, this should be the size in bytes corresponding to glType. For texture data which does not depend on platform endianness, including block compressed texture data, glTypeSize must equal 1.

2.5. glFormat

For block compressed textures, glFormat must equal 0. For uncompressed textures, glFormat specifies the format parameter passed to glTex{,Sub}Image*D, usually one of the values from table 8.3 of the OpenGL 4.6 specification [OPENGL46] (RGB, RGBA, BGRA, etc.)

2.6. glInternalFormat

For block compressed textures, glInternalFormat must equal the compressed internal format, usually one of the values from table 8.14 of the OpenGL 4.6 specification [OPENGL46]. For uncompressed textures, glInternalFormat specifies the internalformat parameter passed to glTexStorage*D or glTexImage*D, usually one of the sized internal formats from tables 8.12 & 8.13 of the OpenGL 4.6 specification [OPENGL46]. The sized format should be chosen to match the bit depth of the data provided. glInternalFormat is used when loading both compressed and uncompressed textures, except when loading into a context that does not support sized formats, such as an unextended OpenGL ES 2.0 context where the internalformat parameter is required to have the same value as the format parameter.

glInternalFormat can take the value GL_FORMAT_UNDEFINED if the format of the data is not a recognized OpenGL format such as one that appears only in Vulkan.

There is currently no such token. A value will be requested from the OpenGL registry. Whether to include this token in the GL namespace and gl.h will have to be discussed by the working groups. Use GL_INVALID_VALUE (0x0501) for now.

2.7. vkFormat

vkFormat specifies the Vulkan image format, usually one of the values from the VkFormat enum in section 30.3.1 Format Definition of the Vulkan 1.1 specification [VULKAN11]. vkFormat takes the value VK_FORMAT_UNDEFINED (0) if the format of the data is a not a recognized Vulkan format.

2.8. pixelWidth, pixelHeight, pixelDepth

The size of the texture image for level 0, in pixels. No rounding to block sizes should be applied for block compressed textures.

For 1D textures pixelHeight and pixelDepth must be 0. For 2D and cube textures pixelDepth must be 0.

2.9. numberOfArrayElements

numberOfArrayElements specifies the number of array elements. If the texture is not an array texture, numberOfArrayElements must equal 0.

2.10. numberOfFaces

numberOfFaces specifies the number of cubemap faces. For cubemaps and cubemap arrays this should be 6. For non cubemaps this should be 1. Cube map faces are stored in the order: +X, -X, +Y, -Y, +Z, -Z.

Due to GL_OES_compressed_paletted_texture [OESCPT] not defining the interaction between cubemaps and its GL_PALETTE* formats, if glInternalFormat is a paletted format numberOfFaces must be 1

2.11. numberOfMipmapLevels

numberOfMipmapLevels must equal 1 for non-mipmapped textures. For mipmapped textures, it equals the number of mipmaps. Mipmaps are ordered according to the value of the levelOrder field. A KTX file does not need to contain a complete mipmap pyramid. If numberOfMipmapLevels equals 0, it indicates that a full mipmap pyramid should be generated from level 0 at load time (this is usually not allowed for compressed formats).

When glInternalFormat is one of the GL_PALETTE* formats from GL_OES_compressed_paletted_texture [OESCPT] this equals the number of mipmaps and is passed as the levels, parameter when uploading to OpenGL {,ES}. However all levels are packed into a single block of data along with the palette so numberOfMipmapLevels is considered to be 1 in the for loop over the data. Individual mipmaps are not identifiable.

2.12. levelOrder

levelOrder indicates the ordering of the mipmap levels. If 0, it indicates the levels are ordered from base level (the largest) to max level (the smallest). If 1, it indicates the levels ordered from the max level to base level. If numberOfMipmapLevels == 0, levelOrder must equal 0.

levelOrder is ignored when glInternalFormat is one of the GL_PALETTE* formats from GL_OES_compressed_paletted_texture [OESCPT] as from the perspective of the KTX2 file there is only a single level.


When streaming a KTX file, sending smaller mip levels first can be used together with, e.g., the GL_TEXTURE_MAX_LEVEL and GL_TEXTURE_BASE_LEVEL texture parameters, to display a low resolution image quickly without waiting for the entire texture data.

2.13. supercompressionScheme

supercompressionScheme indicates if an optional supercompression scheme has been applied to the data in levelImages. It must be one of the values from Supercompression Schemes. A value of 0 indicates no supercompression.

Table 1. Supercompression Schemes
Scheme Id Scheme Name Level Data Format Global Data Format






Crunch CRN













  1. A registry will be established to issue values in the reserved range for vendor compression schemes thus avoiding conflicts.

The supercompression scheme is applied independently to each mip level to permit streaming and random access to the levels. The format of the data in levelImages for a scheme is specified in the reference given in the Level Data Format column of Supercompression Schemes.

Schemes that require data global to all levels can store it in supercompressionGlobalData. Currently only Crunch CRN uses global data. Thje format of the global data for a scheme is specified in the reference given in the Global Data Format column of Supercompression Schemes.

When a supercompression scheme is used, the image data must be inflated from the scheme prior to GPU sampling.

LZW-style lossless supercompression, e.g, schemes 2 and 3, is generally ineffective on the block-compressed data of GPU texture formats. It is best reserved for use with uncompressed texture formats or with block-compressed data that has been specially optimized for LZW-style supercompression, such as by Crunch’s Rate Distortion Optimization mode [RDO].

Crunch CRN is specially designed for supercompression of some block-compressed texture formats.

2.13.1. Scheme Notes (Normative)

Crunch CRN
  • A file that specifies Crunch CRN with base formats other than ETC, ETC2 and BC[1-3] (S3TC_DXT[1-5]) must be considered invalid.

  • Only Zstandard frames are required. Inflators may skip Skippable frames.

  • Checksums are optional. If a checksum is present, inflators should verify it.

2.14. Data Format Descriptor

The next 3 items combined form a Data Format Descriptor (dfDescriptor) describing the layout of the texel blocks in data. The full specification for this can be found in the Khronos Data Format 1.2 Specification [KDF12].

The dfDescriptor is partially expanded here in order to provide sufficient information for a KTX2 file to be parsed without having to refer to [KDF12]. If consists of one or more Descriptor Blocks (dfDescriptorBlock).

The Data Format Descriptor describes the texel blocks as they are when supercompressionScheme == 0 or after reflation when supercompressionScheme != 0.


dfFormatDescriptor is useful in the following cases:

  • precise color management using the descriptor’s color space information,

  • storing multi-sample images. Neither OpenGL nor Vulkan define formats or an API for loading these. Applications can use the descriptor and a custom shader to load these.

  • easier use of the images by non-OpenGL and non-Vulkan applications. There will be no need for large tables to interpret format enums.

  • easier calculation of the offsets of each level, face and layer within the data. Again there will be no need for large tables.

2.14.1. bytesOfDataFormatDescriptor

Called total_size in [KFD12], bytesOfDataFormatDescriptor indicates the total number of bytes in the dfDescriptor including all dfDescriptorBlocks and all blockPadding fields.

2.14.2. descriptorBlock

A Descriptor Block as defined in [KDF12], the high-order 16 bits of its first UInt32 give the descriptor type and the high-order 16 bits of the second UInt32 give the descriptor_block_size.

2.14.3. blockPadding

blockPadding contains between 0 and 3 bytes to ensure that the byte following the last byte in blockPadding is at a file offset that is a multiple of 4. This ensures that every descriptorBlock field and the following bytesOfKeyValueData field are 4-byte aligned. This padding is included in bytesOfDataFormatDescriptor but not in the individual descriptor_block_sizes.

The Khronos Basic Data Format Descriptor Block which will be the type used in the vast majority of cases has a length guaranteed to be a multiple of 4 so typically there will be 0 bytes of padding.

2.15. bytesOfKeyValueData

An arbitrary number of key/value pairs may follow the header. This can be used to encode any arbitrary data. The bytesOfKeyValueData field indicates the total number of bytes of key/value data including all keyAndValueByteSize fields, all keyAndValue fields and all valuePadding fields but not the keyValuePadding field. The file offset of the bytesOfImages field is located at the file offset of the bytesOfKeyValueData field plus 4 plus the value of the bytesOfKeyValueData field rounded to the next 8-byte boundary.

2.16. keyAndValueByteSize

keyAndValueByteSize is the number of bytes of combined key and value data in one key/value pair following the header. This includes the size of the key, the NUL byte terminating the key, and all the bytes of data in the value. If the value is a UTF-8 string it should be NUL terminated and the keyAndValueByteSize should include tlhe NUL character (but code that reads KTX files must not assume that value fields are NUL terminated). keyAndValueByteSize does not include the bytes in valuePadding.

2.17. keyAndValue

keyAndValue contains 2 separate sections. First it contains a key encoded in UTF-8. The key must be terminated by a NUL character (a single 0x00 byte). Keys that begin with the 3 ascii characters 'KTX' or 'ktx' are reserved and must not be used except as described by this spec (this version of the KTX spec defines two keys). Immediately following the NUL character that terminates the key is the Value data.

The Value data may consist of any arbitrary data bytes. Any byte value is allowed. It is encouraged that the value be a NUL terminated UTF-8 string, but this is not required. If the Value data is binary, it is a sequence of bytes rather than of words. It is up to the vendor defining the key to specify how those bytes are to be interpreted (including the endianness of any encoded numbers). If the Value data is a string of bytes then the NUL termination should be included in the keyAndValueByteSize byte count (but programs that read KTX files must not rely on this).

2.18. valuePadding

valuePadding contains between 0 and 3 bytes to ensure that the byte following the last byte in valuePadding is at a file offset that is a multiple of 4. This ensures that every keyAndValueByteSize field is 4-byte aligned. This padding is included in the bytesOfKeyValueData field but not the individual keyAndValueByteSize fields.

2.19. keyValuePadding

keyValuePadding contains either 0 or 4 bytes to ensure that the following bytesOfSupercompressionGlobalData field is at a file offset that is a multiple of 8.

2.20. bytesOfSupercompressionGlobalData

bytesOfSupercompressionGlobalData indicates the number of bytes of supercompressionGlobalData. It does not include sgdPadding. For most schemes the value is 0.

2.21. supercompressionGlobalData

supercompressionGlobalData is an array of data used by certain supercompression schemes that must be available before any mip level can be expanded.

2.22. sgdPadding

sgdPadding contains bwteeen 0 and 7 bytes to ensure ensure that bytesOfImages is at a file offset that is a multiple of 8.

2.23. bytesOfImages

The total size of the image data. That is the sum of the bytesOfLevelImages within the Mipmap level array.

2.24. bytesOfUncompressedImages

The size of the image data after expansion from supercompression. When supercompressionScheme == 0, bytesOfImages must have the same value as this.

2.25. Level Index

This array provides the offset within the Mipmap Level Array for each mip level. Levels are ordered as indicated by the value of levelOrder. This index provides random access to supercompressed data. It is not necessary for non-supercompressed data, as the sizes and offsets can be calculated, but for consistency and reducing the possibilities for error it must always be included in a KTX file.

2.25.1. levelOffset

levelOffset gives the offset of a mipmap level from the start of the Mipmap Level Array.

2.26. Mipmap Level Array

2.26.1. bytesOfLevelImages

The total size of the data for a supercompressed mipmap level.

bytesOfLevelImages is the number of bytes of pixel data in the current LOD level. This includes all z slices, all faces, all rows (or rows of blocks) and all pixels (or blocks) in each row for the mipmap level.

If the sum of bytesOfLevelImages within the array is not equal to bytesOfImages, loaders should consider the file invalid and not load it.

2.26.2. bytesOfUncompressedLevelImages

The size of the data in a level after reflation from supercompression. When supercompressionScheme == 0, [bytesOfLevelData] must have the same value as this. bytesOfUncompressedLevelData does not include any mipPadding for the level.

bytesOfUncompressedLevelImages is the number of bytes of pixel data in the current LOD level after reflation from supercompression. This includes all z slices, all faces, all rows (or rows of blocks) and all pixels (or blocks) in each row for the mipmap level. It does not include any bytes in mipPadding.

If the sum of bytesOfUncompressedLevelImages within the array is not equal to bytesOfUncompressedImages, loaders should consider the file invalid and not load it.

In versions of OpenGL < 4.5 and in OpenGL ES, faces of non-array cubemap textures (any texture where numberOfFaces is 6 and numberOfArrayElements is 0) must be uploaded individually. Loaders wishing to minimize the size of their intermediate buffers may want to read the faces individually rather then as a block of size bytesOfUncompressedLevelImages.

2.26.3. levelImages

levelImages is an array of Bytes holding all the image data for a level.

When supercompressionScheme != 0 these bytes are formatted as specified in the scheme documentation.

2.27. mipPadding

mipPadding is between 0 and 7 bytes to make sure that all bytesOfLevelImages fields are at a file offset that is a multiple of 8.

3. General comments

Rows of uncompressed pixel data are tightly packed. Each row in memory immediately follows the end of the preceding row. I.e the data must be packed according to the rules described in section Unpacking of the OpenGL 4.6 specification [OPENGL46] with GL_UNPACK_ROW_LENGTH = 0 and GL_UNPACK_ALIGNMENT = 1.

Values listed in tables and sections referred to in the OpenGL 4.6 [OPENGL46] and Vulkan 1.1 [VULKAN11] specifications may be supplemented by extensions. The references are given as examples and do not imply that all of those texture types can be loaded in any particular version of OpenGL {,ES} or Vulkan.

4. Predefined Key-Value Pairs

4.1. Image Orientation

Texture data in a KTX file are arranged so that the first pixel in the data stream for each face and/or array element is closest to the origin of the texture coordinate system. In OpenGL that origin is conventionally described as being at the lower left, but this convention is not shared by all image file formats and content creation tools, so there is abundant room for confusion.

The desired texture axis orientation is often predetermined by, e.g. a content creation tool’s or existing application’s use of the image. Therefore it is strongly recommended that tools for generating KTX files clearly describe their behaviour, and provide an option to specify the texture axis origin and orientation relative to the logical orientation of the source image. At minimum they should provide a choice between top-left and bottom-left as origin for 2D source images, with the positive S axis pointing right. Where possible, the preferred default is to use the logical upper-left corner of the image as the texture origin. Note that this is contrary to the standard interpretation of GL texture coordinates. However, the majority of texture compression tools use this convention.

As an aid to writing image manipulation tools and viewers, the logical orientation of the data in a KTX file may be indicated in the file’s key/value metadata. Note that this metadata affects only the logical interpretation of the data, has no effect on the mapping from pixels in the file byte stream to texture coordinates. The recommended key to use is:

  • KTXorientation

It is recommended that viewing and editing tools support at least the following values:

  • S=r,T=d

  • S=r,T=u

  • S=r,T=d,R=i

  • S=r,T=u,R=o


  • S indicates the direction of increasing S values

  • T indicates the direction of increasing T values

  • R indicates the direction of increasing R values

  • r indicates increasing to the right

  • l indicates increasing to the left

  • d indicates increasing downwards

  • u indicates increasing upwards

  • o indicates increasing out from the screen (moving towards viewer)

  • i indicates increasing in towards the screen (moving away from viewer)

Although other orientations can be represented, it is recommended that tools that create KTX files use only the values listed above as other values may not be widely supported by other tools.

4.2. Swizzle

The recommended key for indicating desired component mapping for a texture is;

  • KTXswizzle

The format of the value is

  • R=<swizzle>,G=<swizzle>,B=<swizzle>,A=<swizzle>

where <swizzle> is one character from the set [01rgba]. For example

  • R=b,G=r,B=g,A=1

If not set, you will get the identity, i.e. no, swizzle.

4.3. Writer Id

KTX file writers must identify themselves by including a value with the following key:

  • KTXwriter

The value can be any UTF-8 string that will uniquely identify the tool writing the file, for example:

  • AcmeCo TexTool v1.0

Only the most recent writer should be identified. Editing tools should overwrite this value when rewriting a file originally written by a different tool.

5. An example KTX file:


6. IANA Mime-Type Registration Information


7. Issues

  1. How to refer to the DF descriptor block?

    Discussion: There is no such data type as dfDesriptorBlock but using primitive types would effectively mean repeating the definition of a descriptor block here which we do not want to do.

    Resolved: Show that dfDescriptorBlock is used as a shorthand for [KDF12]'s Descriptor block.

  2. How to handle endianness of the DF descriptor block?

    Discussion: The DF spec says data structures are assumed to be little-endian for purposes of data transfer. This is incompatible with the net which is big-endian and incompatible with endianness. What should we do?


  3. Can we guarantee the DF descriptor blocks are always a multiple of 4 bytes?

    Discussion The Khronos Basic Data Format Descriptor Block is a multiple of 4 bytes (24 + 16 x number of samples). Is there anything to require that extensions' block sizes be a multiple of 4 bytes? Need to maintain alignment.

    Resolved: The Data Format Specification will be updated to recommend but not require padding. This spec. will require padding.

  4. Should KTX2 support level sizes > 4GB?

    Discussion: Users have reported having base levels > 4GB for 3D textures. For this the imageSize field needs to be 64-bits. Loaders on 32-bit systems will have to ensure correct handling of this and check that imageSize <= 4GB, before loading.

    Resolved: Be future proof and make all image-size related fields 64 bits.

  5. Should KTX2 provide a way to distinguish between rectangle and regular 2D textures?

    Discussion: The difference is that unnormalized texel coordinates are used for sampling via a special sampler type in GLSL and, in the case of OpenGL {,ES}, the special TEXTURE_RECTANGLE target is used. If needed this could be supported by a metadata item instructing to use unnormalized texel coordinates.


  6. Should KTX2 provide a way to distinguish between 1D textures and buffer textures?

    Discussion: The difference is how you use the data in OpenGL. With buffer textures the image data is stored in a buffer object. Note that a TextureView can be used to give a different view of the data so supporting buffer textures probably requires metadata to indicate a preferred view as well as metadata to indicate the data should be loaded in a buffer.


  7. Should KTX2 support contexts that do not support sized internal fomats?

    Discussion: OpenGL ES 1.x and 2.0 do not support sized internal formats. The glBaseInternalFormat field was included in the header for easy support of these older versions. Now seems a good time to drop this field.

    Resolved: Drop glBaseInternalFormat. When loading to older version contexts the value of glFormat can be used instead.

  8. Use alphanumeric characters or binary values for component swizzles?

    Discussion: Values in the swizzle metadata could be either a character from the set [01rgba] or numeric values corresponding to the VkComponentSwizzle enum values from 0 to 6. In the latter case values could be expressed in binary or as numeric characters. The GL token values have been eliminated from this choice because they are not user friendly.

    Resolved: Use alphanumeric characters from the set [01rgba].

  9. Is anything needed to support sparse textures?

    Discussion: Sparse textures are provided by the GL_ARB_sparse_textures extension and are a standard feature of Vulkan. Are any additional KTX features needed to support them?


  10. Should KTX2 support metadata for effective use of Vulkan SCALED formats?

    Discussion: Vulkan SCALED formats convert int (or uint) values to unnormalized floating point values, equivalent to specifying a value of GL_FALSE for the normalized parameter to glVertexAttribFormat. Generally when using such data, associated scale and bias values are folded into the transformation matrix. Should KTX2 specify standard metadata for these?

    Resolved: Not at this time. These formats are primarily for vertex data and several Vulkan vendors have said they can’t support them as texture formats. Metadata can be easily added in future.

  11. Should the supercompression scheme be applied per-mip-level?

    Discussion: Should each mip level be supercompressed independently or should the scheme, zlib, zstd, etc., be applied to all levels as a unit? The latter may result in slightly smaller size though that is unclear. However it would also mean levels could not be streamed or randomly accessed.

    Resolved: Yes. The benefits of streaming and random access outweigh what is expected to be a small increase in size.

  12. Should we remove row padding from uncompressed image data?

    Discussion: Row padding was added to KTX so that data would have the default GL_UNPACK_ALIGNMENT of 4, which was chosen to help speed up DMA of rows by the GPU. Modern architectures are apparently not sensitive to this as evidenced by Vulkan deliberately omitting any equivalent of GL_UNPACK_ALIGNMENT. Thus an annoying chunk of code is required to upload row-padded images to Vulkan.

    Resolved: Remove this and cube padding. Formats that would need padding have texel sizes that are less than 4 bytes so no benefit is obtained by starting cube faces or rows of such images at 4-byte multiples.

  13. Should we require content checksums anywhere?

    Discussion: Modern transmission mechanisms, e.g, HTTP2, provide good robustness so checksums are less important than they used to be. Some supercompressions schemes have checksum which may be optional.

    Resolved: No. We can rely on modern transmission mechanisms. However if the supercompression scheme includes a checksum readers should verify it.

8. References

Normative References

References to the OpenGL and Vulkan specifications do not imply that KTX header field values are limited solely to those in the referenced sections or tables. These values may be supplemented by OpenGL {,ES} extensions, Vulkan extensions or new versions. They also do not imply that all of the texture types can be loaded in any particular version of OpenGL {,ES} or Vulkan.

Non-Normative References

Appendix A: Changes compared to KTX

  • vkFormat added.

  • levelOrder added.

  • Data format descriptor added.

  • Supercompression added.

  • glBaseInternalFormat removed.

  • Swizzle and writer id metadata added.

  • Row and cube padding removed.

Revision History

Document Revision Date Remark



First incarnation.



Update issue discussions and change OpenGL references to 4.6.



Clarify relation to Data Format Descriptor spec. Add global compression. Update issues.



Remove glBaseInternalFormat. Add zstd global compression option and issue 11. Add copyright & license.



Add acknowledgements.



Change all size & offset fields to 64-bit. Change global compression to supercompression. Add supercompressionGlobalData, level index and writer id. Define interactions with paletted textures. Remove cubePadding.



Remove rowPadding. Use registered trademarks. Improve supercompression section & add references. Add internal xrefs. Update issues.



Answer questions re. supercompression posed in draft 6 & finish section. Fix scheme numbers after ANS removal. Alphabetize references. Improve wording and formatting. Change status.



Change status back to not ready for implementation in view of issue #8.


Thanks to Manmohan Bishnoi for designing the KTX file and application icons.