From dba5c640b05b161fc4f1e765e22527424c3144f5 Mon Sep 17 00:00:00 2001 From: Arseny Kapoulkine Date: Sun, 25 Jan 2026 09:27:35 -0800 Subject: [PATCH] gltfpack: Implement initial support for XUASTC When compiling against latest Basis Universal (2.0+), we can now use XUASTC compression. It supports variable block sizes and a much larger gamut of quality settings from the perspective of the resulting file size, using DCT to compress the block data with arith/Zstd on top. This format is currently not supported by any glTF extensions; as such, this might need refinements if this happens. For now, the block size is fixed at 4x4; this could be controlled via quality settings or a separate future command line argument. The quality curve for now is somewhat approximate and based on the target size; the quality impact has not been analyzed yet. --- gltf/encodebasis.cpp | 53 +++++++++++++++++++++++++++++++------------- gltf/gltfpack.cpp | 25 ++++++++++++++++++--- gltf/gltfpack.h | 1 + 3 files changed, 60 insertions(+), 19 deletions(-) diff --git a/gltf/encodebasis.cpp b/gltf/encodebasis.cpp index 720262d6f..762df71d7 100644 --- a/gltf/encodebasis.cpp +++ b/gltf/encodebasis.cpp @@ -30,24 +30,39 @@ struct BasisSettings int etc1s_q; int uastc_l; float uastc_q; + int xuastc_e; + int xuastc_q; }; static const BasisSettings kBasisSettings[10] = { - {1, 1, 0, 4.f}, - {1, 32, 0, 3.f}, - {1, 64, 1, 2.f}, - {1, 96, 1, 1.5f}, - {1, 128, 1, 1.f}, // quality arguments aligned with basisu defaults - {1, 150, 1, 0.8f}, - {1, 170, 1, 0.6f}, - {1, 192, 1, 0.4f}, // gltfpack defaults - {1, 224, 2, 0.2f}, - {1, 255, 2, 0.f}, + {1, 1, 0, 4.f, 0, 10}, + {1, 32, 0, 3.f, 0, 20}, + {1, 64, 1, 2.f, 1, 30}, + {1, 96, 1, 1.5f, 1, 40}, + {1, 128, 1, 1.f, 1, 50}, + {1, 150, 1, 0.8f, 1, 60}, + {1, 170, 1, 0.6f, 1, 70}, + {1, 192, 1, 0.4f, 1, 80}, // gltfpack defaults + {1, 224, 2, 0.2f, 2, 85}, + {1, 255, 2, 0.f, 2, 90}, }; -static void fillParams(basisu::basis_compressor_params& params, const char* input, const char* output, bool uastc, int width, int height, const BasisSettings& bs, const ImageInfo& info, const Settings& settings) +static void fillParams(basisu::basis_compressor_params& params, const char* input, const char* output, TextureMode mode, int width, int height, const BasisSettings& bs, const ImageInfo& info, const Settings& settings) { - if (uastc) + if (mode == TextureMode_XUASTC) + { + params.m_uastc = true; + +#if BASISU_LIB_VERSION >= 200 + params.m_xuastc_or_astc_ldr_basis_tex_format = int(basist::basis_tex_format::cXUASTC_LDR_4x4); + params.m_quality_level = bs.xuastc_q; + params.m_xuastc_ldr_effort_level = bs.xuastc_e; + params.m_xuastc_ldr_use_dct = true; + params.m_xuastc_ldr_use_lossy_supercompression = true; + params.m_xuastc_ldr_syntax = int(basist::astc_ldr_t::xuastc_ldr_syntax::cHybridArithZStd); +#endif + } + else if (mode == TextureMode_UASTC) { static const uint32_t s_level_flags[basisu::TOTAL_PACK_UASTC_LEVELS] = {basisu::cPackUASTCLevelFastest, basisu::cPackUASTCLevelFaster, basisu::cPackUASTCLevelDefault, basisu::cPackUASTCLevelSlower, basisu::cPackUASTCLevelVerySlow}; @@ -110,7 +125,7 @@ static void fillParams(basisu::basis_compressor_params& params, const char* inpu params.m_ktx2_srgb_transfer_func = info.srgb; #endif - if (uastc) + if (mode == TextureMode_UASTC) { params.m_ktx2_uastc_supercompression = basist::KTX2_SS_ZSTANDARD; params.m_ktx2_zstd_supercompression_level = 9; @@ -134,6 +149,11 @@ static void fillParams(basisu::basis_compressor_params& params, const char* inpu static const char* prepareEncode(basisu::basis_compressor_params& params, const cgltf_image& image, const char* input_path, const ImageInfo& info, const Settings& settings, const std::string& temp_prefix, std::string& temp_input, std::string& temp_output) { +#if BASISU_LIB_VERSION < 200 + if (settings.texture_mode[info.kind] == TextureMode_XUASTC) + return "XUASTC mode requires BasisU library version 2.0 or higher"; +#endif + std::string img_data; std::string mime_type; @@ -156,11 +176,11 @@ static const char* prepareEncode(basisu::basis_compressor_params& params, const return "error writing temporary file"; int quality = settings.texture_quality[info.kind]; - bool uastc = settings.texture_mode[info.kind] == TextureMode_UASTC; + TextureMode mode = settings.texture_mode[info.kind]; const BasisSettings& bs = kBasisSettings[quality - 1]; - fillParams(params, temp_input.c_str(), temp_output.c_str(), uastc, width, height, bs, info, settings); + fillParams(params, temp_input.c_str(), temp_output.c_str(), mode, width, height, bs, info, settings); return NULL; } @@ -181,8 +201,9 @@ void encodeImagesBasis(std::string* encoded, const cgltf_data* data, const std:: { const cgltf_image& image = data->images[i]; ImageInfo info = images[i]; + TextureMode mode = settings.texture_mode[info.kind]; - if (settings.texture_mode[info.kind] == TextureMode_ETC1S || settings.texture_mode[info.kind] == TextureMode_UASTC) + if (mode == TextureMode_ETC1S || mode == TextureMode_UASTC || mode == TextureMode_XUASTC) if (const char* error = prepareEncode(params[i], image, input_path, info, settings, temp_prefix + "-" + std::to_string(i), temp_inputs[i], temp_outputs[i])) encoded[i] = error; } diff --git a/gltf/gltfpack.cpp b/gltf/gltfpack.cpp index 01dde94b3..4ba6eefb2 100644 --- a/gltf/gltfpack.cpp +++ b/gltf/gltfpack.cpp @@ -1453,6 +1453,16 @@ int main(int argc, char** argv) applySetting(settings.texture_mode, TextureMode_ETC1S, mask); } + else if (strcmp(arg, "-tx") == 0) + { + settings.texture_ktx2 = true; + + unsigned int mask = ~0u; + if (i + 1 < argc && isalpha(argv[i + 1][0])) + mask = textureMask(argv[++i]); + + applySetting(settings.texture_mode, TextureMode_XUASTC, mask); + } else if (strcmp(arg, "-tw") == 0) { settings.texture_webp = true; @@ -1634,8 +1644,9 @@ int main(int argc, char** argv) fprintf(stderr, "\t-o file: output file path, .gltf/.glb\n"); fprintf(stderr, "\t-c: produce compressed gltf/glb files (-cc/-cz for higher compression ratio)\n"); fprintf(stderr, "\nTextures:\n"); - fprintf(stderr, "\t-tc: convert all textures to KTX2 with BasisU supercompression\n"); - fprintf(stderr, "\t-tu: use UASTC when encoding textures (much higher quality and much larger size)\n"); + fprintf(stderr, "\t-tc: convert all textures to KTX2 with BasisU ETC1S supercompression\n"); + fprintf(stderr, "\t-tu: use UASTC when encoding textures (higher quality and larger size)\n"); + fprintf(stderr, "\t-tx: use XUASTC when encoding textures (DCT supercompression)\n"); fprintf(stderr, "\t-tw: convert all textures to WebP\n"); fprintf(stderr, "\t-tq N: set texture encoding quality (default: 8; N should be between 1 and 10)\n"); fprintf(stderr, "\t-ts R: scale texture dimensions by the ratio R (default: 1; R should be between 0 and 1)\n"); @@ -1647,6 +1658,7 @@ int main(int argc, char** argv) fprintf(stderr, "\tTexture classes:\n"); fprintf(stderr, "\t-tc C: use ETC1S when encoding textures of class C\n"); fprintf(stderr, "\t-tu C: use UASTC when encoding textures of class C\n"); + fprintf(stderr, "\t-tx C: use XUASTC when encoding textures of class C\n"); fprintf(stderr, "\t-tw C: use WebP when encoding textures of class C\n"); fprintf(stderr, "\t-tq C N: set texture encoding quality for class C\n"); fprintf(stderr, "\t-ts C R: scale texture dimensions for class C\n"); @@ -1710,7 +1722,7 @@ int main(int argc, char** argv) if (require_texc && !settings.texture_ktx2 && !settings.texture_webp) { - fprintf(stderr, "Texture processing is only supported when texture compression is enabled via -tc/-tu/-tw\n"); + fprintf(stderr, "Texture processing is only supported when texture compression is enabled via -tc/-tu/-tx/-tw\n"); return 1; } @@ -1735,6 +1747,13 @@ int main(int argc, char** argv) if (settings.keep_nodes && (settings.mesh_merge || settings.mesh_instancing)) fprintf(stderr, "Warning: option -kn disables mesh merge (-mm) and mesh instancing (-mi) optimizations\n"); + for (int i = 0; i < TextureKind__Count; ++i) + if (settings.texture_mode[i] == TextureMode_XUASTC) + { + fprintf(stderr, "Warning: -tx uses XUASTC format which is not part of the KHR_texture_basisu specification\n"); + break; + } + return gltfpack(input, output, report, settings); } #endif diff --git a/gltf/gltfpack.h b/gltf/gltfpack.h index e1cf03b3c..3125a7388 100644 --- a/gltf/gltfpack.h +++ b/gltf/gltfpack.h @@ -114,6 +114,7 @@ enum TextureMode TextureMode_Raw, TextureMode_ETC1S, TextureMode_UASTC, + TextureMode_XUASTC, TextureMode_WebP, };