Compressed Textures (BC / ASTC / ETC2)¶
SimVX loads GPU block-compressed textures from .ktx2 (KTX 2.0) and .dds
containers, transcoding the universal UASTC interchange format to whatever block
family the current GPU prefers. Block-compressed textures use a fraction of the
VRAM of RGBA8 and stay compressed on the GPU, so they are the right choice for
shipped game assets.
A runnable example is at
examples/features/3d/compressed_texture.py (uv run python examples/features/3d/compressed_texture.py).
Cooking for shipping¶
simvx cook pre-transcodes UASTC .ktx2 sources to an explicit block format
offline, baking the full mip chain into the cooked container, so shipped
builds do zero transcode work at load time and never need the native
transcoder extension:
simvx cook hero.ktx2 -o cooked/ # UASTC -> explicit BC7 + mips
simvx cook scene.gltf -o cooked/ # cook everything a scene references
simvx cook hero.ktx2 -o cooked/ --target astc4x4 # mobile cook
A .gltf input carries every externally referenced buffer and image across
(relative layout preserved, so the cooked scene loads as-is), cooking each
UASTC .ktx2 reference along the way. Raw PNG/JPG references and
explicit-format KTX2/DDS files copy unchanged: raw colour maps get their mip
chains from the runtime mip generator, and explicit-format containers are
already cooked. When the native transcoder is unavailable the cook degrades to
a pass-through copy and says so in its report (mirroring the loading fallback
chain below).
Cook per shipping target: web exports should keep the original UASTC .ktx2
(the browser transcodes per-device, see the target matrix below), so cook into
a separate output directory rather than replacing your sources.
Loading¶
There is no special API. .ktx2 / .dds are accepted transparently anywhere a
texture source is accepted; the texture manager dispatches on the file suffix
(or the magic bytes for in-memory sources):
mat = Material(albedo_map="hero.ktx2") # UASTC or explicit-BC KTX2
spr = Sprite2D(texture="tiles.dds") # explicit-BC DDS
Fallback chain¶
Loading never crashes; it degrades step by step:
Native transcode. A UASTC
.ktx2is transcoded to the device’s chosen block target via the nativebasis_universaltranscoder. The target is picked by a device probe in this order, each gated by BOTH the coarse compression-family feature AND a per-format SAMPLED check: BC7 -> ASTC-4x4 -> ETC2. Explicit-BC files skip the probe (they carry their own VkFormat).CPU decode. If the GPU exposes no usable block family (or the native transcoder is not built), mip 0 is decoded to RGBA8 on the CPU via the optional
texture2ddecoderpackage and uploaded asR8G8B8A8_UNORM.Skip. If even the CPU decoder/dependency is absent, a one-time WARNING is logged and the texture resolves to the
-1“couldn’t resolve” sentinel.
Mip sampling¶
When a .ktx2 / .dds carries a mip chain, the whole chain is uploaded and the
bound sampler’s maxLod is set to mip_count - 1, so minified surfaces sample
the smaller levels (no aliasing). Single-mip textures keep maxLod = 0
unchanged. Generate the chain at authoring time with --genmipmap.
Optional dependency + build¶
The native transcoder is an opt-in C++ extension built once after install:
simvx build-textures # requires a C++ compiler (g++ / clang / MSVC)
texture2ddecoder is the pip-installable CPU-decode fallback (uv pip install texture2ddecoder). Both are optional: with neither present, compressed textures
degrade gracefully (see the fallback chain). See Installation for the
dependency matrix.
Re-enabling the ASTC and ETC2 transcode targets grows the built .so and adds
the ASTC table-generation cost at compile time (acceptable for the full
platform matrix). The extension’s name embeds a short hash of the
BASISD_SUPPORT_* define-set (_simvx_basis_transcoder_<hash>), so changing the
supported-format set rebuilds automatically: the new define-set maps to a new
name, a stale .so is simply not found, and simvx build-textures compiles the
current set instead of reusing a cached/installed binary.
Per-platform target matrix¶
Platform |
Block family |
Notes |
|---|---|---|
Desktop (PC) |
BC7 |
Near-universal; the desktop probe picks it first. |
Mobile / tablet |
ASTC-4x4 |
Modern GPUs (Adreno, Mali, Apple). |
Older mobile / WebGL-class |
ETC2 |
Widely supported baseline. |
Web runtime |
UASTC (in-browser) |
The web export ships raw UASTC and the browser/WebGPU transcodes per-device via the |
The desktop probe order is BC7 -> ASTC-4x4 -> ETC2 -> CPU-decode. ASTC sizes other than 4x4 are out of scope (their larger texel footprint would break the 4-texel block-row math).