Error::UnsupportedFormat is returned when a format is outside the matrices
below. Fail closed — never invent pixels.
Same IDs as the decode/encode completeness tests.
| Content | DXGI / D3D | Decode | Encode | Compressed upload |
|---|---|---|---|---|
| C-BC1 | BC1_* / DXT1 |
✅ | ✅ | ✅ |
| C-BC2 | BC2_* / DXT2–3 |
✅ | ✅ | ✅ |
| C-BC3 | BC3_* / DXT4–5 |
✅ | ✅ | ✅ |
| C-BC4U | BC4_UNorm |
✅ | ✅ | ✅ |
| C-BC4S | BC4_SNorm |
✅ | ✅ | ✅ |
| C-BC5U | BC5_UNorm |
✅ | ✅ | ✅ |
| C-BC5S | BC5_SNorm |
✅ | ✅ | ✅ |
| C-BC7 | BC7_* |
✅ | ✅ modes 1 / 4 / 5 / 6 | ✅ |
| C-RGBA | R8G8B8A8_* |
✅ | ✅ | ✅ |
| C-BGRA | B8G8R8A8_* |
✅ | ✅ | ✅ |
BC7 encode picks per block: mode 6 is the always-tried baseline, modes 5 and 4 decouple colour and alpha indices (they win on content whose alpha gradient disagrees with its colour gradient — UI, decals), and mode 1 adds two-subset partitioning over a harvest-chosen 8-shape shortlist.
| Content | DXGI | Decode | Encode | Compressed upload |
|---|---|---|---|---|
| C-BC6H-UF16 | BC6H_UF16 |
✅ decode_rgba_f32 |
✅ encode_bc6h_uf16 (mode 11) |
✅ |
| C-BC6H-SF16 | BC6H_SF16 |
✅ decode_rgba_f32 |
❌ deferred | ✅ |
| C-BC6H-Typeless | BC6H_Typeless |
✅ decode_rgba_f32 |
❌ | ✅ |
LDR and HDR fail closed on each other: decode_rgba8 refuses BC6H and
decode_rgba_f32 refuses the LDR set, rather than silently converting.
sRGB: _sRGB tags keep stored channel bytes (no linearization) on
decode; GPU names map to *Srgb variants in GpuFormat.
| Format family | Status |
|---|---|
| ASTC / ETC / PVRTC | out of scope (not DDS-primary desktop BCn) |
| Packed RGB10A2, R16G16, float RGBA, etc. | container may parse; no decode/encode |
| Legacy D3D uncompressed bitmasks beyond the DXGI map | UnsupportedFormat on decode/encode |
| Video / YUV DXGI formats | unsupported |
X-2D · X-MIP · X-ARRAY · X-CUBE · X-NPOT · X-VOL — see texture-pipeline.md.
Dds::read reads the payload to end-of-stream with no cap, which is right for
a file on disk and wrong for bytes off a network or out of a mod archive. For
those, Dds::read_limited(r, max_data_len) fails closed with
Error::SizeLimitExceeded without buffering the overrun.
Every size computation downstream of the header uses checked arithmetic: a
header whose declared geometry cannot exist yields UnsupportedFormat or
OutOfBounds, never a wrapped size used to slice the payload. This is gated by
tests/parser_robustness.rs and by the fuzz/ targets.
| Cargo feature | Default | Effect |
|---|---|---|
decode |
✅ | decode_rgba8, decode_rgba_f32, bcdec_rs |
encode |
✅ | encode_from_rgba8, encode_bc6h_uf16, EncodeLayout, Rdo |
simd |
✅ | AVX2 encode kernels; byte-identical output. Off ⇒ forbid(unsafe_code) |
tuning |
❌ | Development only. Re-opens the frozen encoder constants to RUSTY_DDS_* environment overrides. Never enable in a shipped build — encoder output would stop being a pure function of its inputs. |
| (none) | — | container + surfaces + UploadPlan + decode_content() classification |