ctt
A Rust library with C bindings and CLI for GPU texture compression. ctt provides a unified interface over multiple encoder backends.
Encoders
ctt binds to established open-source compression libraries and exposes them through a common Encoder trait. Each backend is compiled as an optional feature and can be enabled independently.
| Backend | Prefix | Feature | Description |
|---|---|---|---|
| bc7enc-rdo | bc7e_ |
encoder-bc7enc |
Perceptual BC7 encoder with RDO support. |
| Intel ISPC Texture Compressor | intel_ |
encoder-intel |
SIMD-optimized BCn and ETC encoder. |
| etcpak | etcpak_ |
encoder-etcpak |
Fast ETC/EAC and BCn encoder. |
| AMD Compressonator | amd_ |
encoder-amd |
AMD's BCn encoder suite. |
| astcenc | astcenc_ |
encoder-astcenc |
ARM ASTC encoder. |
Encoders are listed in priority order — when multiple encoders support the same format (e.g. BC7), the first one in the table is used. To override this, prefix the format with an encoder name (e.g. intel_bc7).
Use ctt --list-encoders to see what's available in your build:
$ ctt --list-encoders
Encoder Priority Formats
------- -------- -------
bc7e 1 bc7
intel 2 bc1, bc3, bc4, bc5, bc6h, bc7, etc1
etcpak 3 etc1, etc2_rgba, eac_r, eac_rg, bc1, bc3, bc4, bc5
amd 4 bc1, bc2, bc3, bc4, bc4s, bc5, bc5s, bc6h, bc6hsf, bc7
astcenc 5 astc
Formats
| Format | CLI name | Description |
|---|---|---|
| BC1 | bc1 |
RGB with 1-bit alpha. Opaque textures and simple cutouts. |
| BC2 | bc2 |
RGBA with explicit 4-bit alpha. Sharp alpha transitions. |
| BC3 | bc3 |
RGBA with interpolated alpha. General-purpose transparency. |
| BC4 | bc4 |
Single channel. Grayscale, heightmaps, roughness. |
| BC4S | bc4s |
Single channel, signed. |
| BC5 | bc5 |
Two channels. Normal maps. |
| BC5S | bc5s |
Two channels, signed. |
| BC6H | bc6h |
HDR half-float RGB. Environment maps, HDR textures. |
| BC6H SF | bc6hsf |
HDR signed float RGB. |
| BC7 | bc7 |
High-quality RGBA. Best LDR quality, supports alpha. |
| ETC1 | etc1 |
Mobile-friendly RGB. |
| ETC2 RGBA | etc2_rgba |
Mobile-friendly RGBA. |
| EAC R | eac_r |
Single channel 11-bit. |
| EAC RG | eac_rg |
Two channel 11-bit. |
| ASTC | astc_WxH |
Adaptive scalable texture compression. Variable block sizes (4x4 to 12x12). |
Compression accepts a quality preset from ultra-fast to very-slow (default basic); each encoder maps presets onto its native effort levels.
Uncompressed formats are also supported using WebGPU names (e.g. rgba8unorm, rgb10a2unorm) or Vulkan names (e.g. r8g8b8a8_unorm, e5b9g9r9_ufloat_pack32).
Output containers
The container format is inferred from the output file extension (.ktx2 or .dds), or can be set explicitly with --container.
- KTX2 — Khronos cross-platform container. Supports all formats. Optional zstd or zlib supercompression.
- DDS — DirectX standard. Does not support ETC/EAC or ASTC formats.
Installation
# Install the CLI (includes all encoders)
# Or add the library to your project
By default the library enables all encoders. To select specific encoders:
Library usage
The library API mirrors the CLI. Build a Surface, wrap it in an Image, and call convert:
use ;
use ;
use Encoder;
See the API documentation for the full ConvertSettings options and the lower-level pipeline API.
Parallel compression
The Rust library is serial unless its default-off rayon feature is enabled. With that feature, each compressed surface is split into block-row jobs on the active Rayon pool. ctt does not configure Rayon's global pool or add a thread count to ConvertSettings; callers that need a fixed worker budget can install conversion into their own pool:
let pool = new.num_threads.build?;
let output = pool.install?;
Without an explicit install, Rayon uses its lazily initialized global pool.
C API
C bindings are available as a separate crate. See crates/ctt-c-api/README.md for build, link, and usage instructions; the full API reference lives in the generated header at crates/ctt-c-api/include/ctt.h.
CLI usage
ctt <INPUT>... --output <PATH> [--format <FORMAT>] [OPTIONS]
When --format is omitted the input format is preserved without compression.
Examples
Compress to BC7 (auto-selects bc7enc-rdo when available):
Force the Intel ISPC encoder for BC7:
Normal map to BC5 as DDS:
High quality:
Choose the compression worker count with --threads / -t. Zero (the default) uses Rayon's platform default, one is effectively serial, and positive values request that exact number of workers:
Cubemap from a cross layout. --cubemap-layout cross (the default) accepts
both the horizontal 4×3 and vertical 3×4 cross arrangements; --cubemap-layout strip accepts a horizontal 6×1 strip:
Cubemap from six separate faces (order: +X, -X, +Y, -Y, +Z, -Z):
Cubemap array from N×6 face images. Pass a multiple of six inputs with
--cubemap and every group of six becomes one cube, stacked in argv order
(you can also pass several already-assembled cubemaps):
2D array from multiple plain inputs. Passing more than one image without
--cubemap or --volume assembles a 2D array texture, one layer per input
(argv order). All inputs must share dimensions, format, and mip count:
3D (volume) texture. Each input is stacked as a Z slice in argv order.
3D textures are written as-is (passthrough only): compression, swizzle, and
mipmap generation are not supported for them, so omit --format. Both KTX2
and DDS output are supported:
Generate mipmaps:
With supercompression (KTX2 only). Use --zstd or --zlib; each optionally
takes a compression level (--zstd accepts negative through 22, default 0;
--zlib accepts 1 through 10, default 6):
Swizzle channels:
Converting a texture with straight alpha to a format without alpha preserves
the RGB values and emits a warning. Use --output-alpha premultiplied to bake
alpha into RGB, --output-alpha opaque to explicitly discard it, or
--allow-discarding-alpha to acknowledge the default discard and silence the
warning without changing pixels.
Per-encoder tuning
Each backend exposes its own low-level knobs through a
--<encoder>-opts "key=val;key=val" flag: --bc7e-opts, --intel-opts,
--etcpak-opts, --amd-opts, and --astcenc-opts. The opts apply only when
the selected format resolves to that encoder; opts for a different encoder are
ignored with a warning. Run ctt --help-encoder <name> (e.g. bc7e, intel,
etcpak, amd, astcenc) to list the available keys, their types, and
descriptions for the encoders compiled into your build:
Run ctt --help for a full list of options.
Minimum Supported Rust Version (MSRV)
The MSRV is 1.90 (edition 2024). MSRV bumps are considered breaking changes.
Prebuilt binaries
By default, ctt ships prebuilt ISPC static libraries for all supported platforms. A default build requires only a Rust toolchain and a C++ compiler.
Every prebuilt binary has a GitHub Artifact Attestation that cryptographically proves it was produced by this repository's CI. See docs/prebuilt-binaries.md for full details on the build process, attestation guarantees, and how to verify them.
To build from source instead (requires ispc.exe on PATH):
License
Licensed under any of:
- MIT License
- Apache License, Version 2.0
- Zlib License
at your option.