Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions .agents/docs/2026-11-14-add-basis-universal-mwrap-plan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Add mcpplibs.mwrap.basis_universal (basis-universal-m 2.50)

## Form

- Third-party upstream [BinomialLLC/basis_universal](https://github.com/BinomialLLC/basis_universal), release v2_50,
Apache-2.0 (the Zstandard decoder it ships beside the transcoder is BSD-licensed).
- Form A external mwrap module repository [mcpplibs/basis-universal-m](https://github.com/mcpplibs/basis-universal-m),
package `mcpplibs.mwrap.basis_universal`, version 2.50, tag `mwrap-2.50`; release asset
`basis-universal-m-2.50.tar.gz` (`eb114f20…8921`), CN mirror `gitcode.com/mcpp-res/mwrap-basis-universal` with
identical bytes.
- Implementation ownership: the index has no KTX2 or Basis Universal package, so the wrapper vendors the transcoder
(`transcoder/`: one source file with its headers and tables) and the Zstandard decoder upstream ships beside it
(`zstd/zstddeclib.c`, `zstd.h`, `zstd_errors.h`) — 33 files, checksums in `upstream/documents.json` — and builds
them as upstream's default build does (every format, KTX2, Zstandard), with one choice: floating-point contraction
off. The encoder, the command line tool and the bindings are not vendored. It deploys upstream's README, LICENSE
and NOTICE and Zstandard's LICENSE with its own LICENSE/NOTICE (`runtime.deploy`). No dependencies.

## Module

- `mcpplibs.mwrap.basis_universal`: every name `<transcoder/basisu_transcoder.h>` declares at namespace scope in
`basist`, `basisu` and the namespaces inside them — 148 types, 273 functions, 70 objects, 112 enumerators of
unscoped enumerations (603 names) — the upstream entities by `using`, in upstream's namespaces. The header brings
upstream's internal header with it, so the tables and helpers are there as they are for a program that includes it.
- Adaptation: a module cannot export a name of internal linkage. The header's 139 scalar constants that are `const`
and not `inline` are declared again, with their names, types and values, under
`mcpplibs::mwrap::basis_universal::<their namespace>`. 11 names cannot be given again (five `static` functions, a
constant table, five `const float`s that are no constant expressions) and are left to the header; a translation
unit may include it beside the import (`tests/header_mix_test.cpp`).
- The export list is derived by `tools/surface.py` from the header as clang reads it (its JSON AST) and audited in CI
(`tools/check_exports.py`; derived again on the Linux LLVM row).

## Verification

- Wrapper CI (Linux x86_64 GCC 16.1 and LLVM 23.1.3, macOS LLVM, Windows LLVM): upstream's own KTX2 test files —
what each is (size, levels, codec, alpha); an ETC1S photograph as RGBA pixels and as BC7, ETC2 and ASTC blocks, each
pinned by a hash that holds on every row; one XUASTC picture packed by Zstandard and by the arithmetic coder is one
picture; noise, a file cut at four places and a changed identifier refused; header and module in one translation
unit; the export audit; the default template from an installed copy with a strict build.
- This index: consumer `tests/mwrap/basis-universal-transcode` (the transcoder made ready, what it writes, block
sizes, refusals, the adapted constants; it carries no texture file); the installed-template check with `--deploy`
(`STATIC_NOTICES`).
5 changes: 5 additions & 0 deletions .github/workflows/validate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1738,6 +1738,11 @@ jobs:
shell: bash
run: python3 tests/mwrap/check_installed_template.py --mcpp "$MCPP" --package mcpplibs.mwrap.libjpeg_turbo@3.2.1 --deploy

- name: Installed Basis Universal wrapper archive, actual default KTX2 template and its relocated notices
if: contains(format(' {0} ', env.MEMBERS), ' basis-universal-transcode ') && matrix.toolchain == 'default'
shell: bash
run: python3 tests/mwrap/check_installed_template.py --mcpp "$MCPP" --package mcpplibs.mwrap.basis_universal@2.50 --deploy

- name: Installed libarchive wrapper archive, actual default archive template and its relocated notices
if: contains(format(' {0} ', env.MEMBERS), ' libarchive-bundle ') && matrix.toolchain == 'default'
shell: bash
Expand Down
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,16 @@

### Added

- **mcpplibs.mwrap.basis_universal 2.50(新包)。** basis-universal-m:Basis Universal 2.50 转码器(把 KTX2 与 `.basis` 纹理转成
像素或 GPU 自己的压缩格式)的 C++23 模块,`<transcoder/basisu_transcoder.h>` 在 `basist`、`basisu` 及其内部命名空间声明的
603 个名字(148 个类型、273 个函数、70 个对象、112 个无作用域枚举的枚举项)以原名导出;头文件里 139 个内部链接的标量常量
以原名位于 `mcpplibs::mwrap::basis_universal::<所在命名空间>`,11 个无法重新给出的内部链接名字(`static` 函数、常量表、
非常量表达式的 `const float`)留给头文件。封装仓库逐字节收录 v2_50 发布包的 `transcoder/` 与上游随附的 Zstandard 解码器
(`zstd/zstddeclib.c`),按上游默认方式构建(全部格式、KTX2、Zstandard),关闭浮点收缩;不含编码器。封装的测试读取上游
自己的 KTX2 测试文件:ETC1S 照片转成 RGBA 像素与 BC7、ETC2、ASTC 块的哈希在四个平台行一致,同一幅 XUASTC 图的两种打包
给出同一幅图。消费者测试 `tests/mwrap/basis-universal-transcode`,安装后模板检查带 `--deploy`。GLOBAL 为 GitHub 发布资产,
CN 为 GitCode `mcpp-res/mwrap-basis-universal` 镜像,sha256 相同。

- **mcpplibs.mwrap.libjpeg_turbo 3.2.1(新包)。** libjpeg-turbo-m:libjpeg-turbo 的 TurboJPEG API(JPEG 压缩、解压、无损变换)的
C++23 模块,`<turbojpeg.h>` 的 7 个枚举、68 个枚举值、4 个类型、80 个函数以原名导出;头文件里的 43 个常量宏、9 个
`static const` 常量与表、3 个带参数的宏(`tj3Init`、`TJSCALED`、`TJPAD`)以原名位于 `mcpplibs::mwrap::libjpeg_turbo`。
Expand Down
1 change: 1 addition & 0 deletions mcpp.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ members = [
"tests/mwrap/meshoptimizer-simplify",
"tests/mwrap/cgltf-inspect",
"tests/mwrap/manifold-boolean",
"tests/mwrap/basis-universal-transcode",
"tests/mwrap/libjpeg-turbo-decode",
"tests/mwrap/libarchive-bundle",
"tests/mwrap/libc-stdio",
Expand Down
52 changes: 52 additions & 0 deletions pkgs/mwrap/basis_universal.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
-- Immutable upstream-aligned mwrap source release (basis-universal-m mwrap-2.50);
-- GLOBAL and CN bytes are identical.
--
-- The wrapper is Apache-2.0; the library it vendors and links statically, Basis
-- Universal 2.50's transcoder, is Apache-2.0 (Binomial LLC), and the Zstandard
-- decoder upstream ships beside it is BSD-licensed (Facebook, Inc.): the package
-- deploys upstream's README, LICENSE and NOTICE and Zstandard's LICENSE beside
-- programs (runtime.deploy). It has no dependencies.
package = {
spec = "1",
namespace = "mcpplibs.mwrap",
name = "basis_universal",
description = "basis-universal-m: Basis Universal 2.50's transcoder (KTX2 and .basis textures to pixels or to a GPU's own compressed format) as a C++23 module",
licenses = { "Apache-2.0" },
repo = "https://github.com/mcpplibs/basis-universal-m",
type = "package",
extensions = {
autoupdate = {
tag = "mwrap-${version}",
url = "https://github.com/mcpplibs/basis-universal-m/releases/download/${tag}/basis-universal-m-${version}.tar.gz",
},
},
xpm = {
linux = {
["2.50"] = {
url = {
GLOBAL = "https://github.com/mcpplibs/basis-universal-m/releases/download/mwrap-2.50/basis-universal-m-2.50.tar.gz",
CN = "https://gitcode.com/mcpp-res/mwrap-basis-universal/releases/download/2.50/basis-universal-m-2.50.tar.gz",
},
sha256 = "eb114f2026bb408fd73cc77852fc7539441d2717bac36bdc10bf7f4d09228921",
},
},
macosx = {
["2.50"] = {
url = {
GLOBAL = "https://github.com/mcpplibs/basis-universal-m/releases/download/mwrap-2.50/basis-universal-m-2.50.tar.gz",
CN = "https://gitcode.com/mcpp-res/mwrap-basis-universal/releases/download/2.50/basis-universal-m-2.50.tar.gz",
},
sha256 = "eb114f2026bb408fd73cc77852fc7539441d2717bac36bdc10bf7f4d09228921",
},
},
windows = {
["2.50"] = {
url = {
GLOBAL = "https://github.com/mcpplibs/basis-universal-m/releases/download/mwrap-2.50/basis-universal-m-2.50.tar.gz",
CN = "https://gitcode.com/mcpp-res/mwrap-basis-universal/releases/download/2.50/basis-universal-m-2.50.tar.gz",
},
sha256 = "eb114f2026bb408fd73cc77852fc7539441d2717bac36bdc10bf7f4d09228921",
},
},
},
}
11 changes: 11 additions & 0 deletions tests/mwrap/basis-universal-transcode/mcpp.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[package]
name = "basis-universal-transcode"
version = "2.50"
description = "Consume the immutable Basis Universal wrapper: the transcoder made ready, what it can write and how large a block is, what is no KTX2 file refused, the adapted constants"

[indices]
# Use the complete candidate built-in index prepared by tests/run_members.sh.
# An explicit empty table overrides the legacy workspace redirect.

[dependencies]
mcpplibs.mwrap.basis_universal = "2.50"
35 changes: 35 additions & 0 deletions tests/mwrap/basis-universal-transcode/tests/behavior.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// The installed Basis Universal wrapper: the transcoder made ready, what this
// build of it can write, how large a block of each format is, what is no KTX2
// file refused at each step, and the header's constants of internal linkage
// under their adapted names. (Pictures themselves are read in the wrapper's
// own CI, from upstream's test files: this consumer carries no file.)
import std;
import mcpplibs.mwrap.basis_universal;

namespace adapted = mcpplibs::mwrap::basis_universal;

int main() {
static_assert(adapted::basist::KTX2_KHR_DF_TRANSFER_LINEAR == 1 && adapted::basist::KTX2_KHR_DF_TRANSFER_SRGB == 2);
basist::basisu_transcoder_init();
if (!basist::basisu_transcoder_supports_ktx2() || !basist::basisu_transcoder_supports_ktx2_zstd()) return 1;
using format = basist::transcoder_texture_format;
if (basist::basis_get_bytes_per_block_or_pixel(format::cTFRGBA32) != 4 || basist::basis_get_bytes_per_block_or_pixel(format::cTFBC7_RGBA) != 16 ||
basist::basis_get_bytes_per_block_or_pixel(format::cTFETC1_RGB) != 8)
return 2;
if (!basist::basis_transcoder_format_is_uncompressed(format::cTFRGBA32) || basist::basis_transcoder_format_is_uncompressed(format::cTFASTC_4x4_RGBA)) return 3;
if (std::string_view(basist::basis_get_format_name(format::cTFBC7_RGBA)) != "BC7_RGBA") return 4;
if (!basist::basis_is_format_supported(format::cTFBC7_RGBA, basist::basis_tex_format::cETC1S) || !basist::basis_is_format_supported(format::cTFRGBA32, basist::basis_tex_format::cUASTC_LDR_4x4))
return 5;

// Noise, and the twelve bytes of KTX2's identifier with nothing after them: refused, and nothing read past the end.
basist::ktx2_transcoder transcoder;
const std::vector<std::uint8_t> noise(4096, 0x5a);
if (transcoder.init(noise.data(), static_cast<std::uint32_t>(noise.size()))) return 6;
const std::array<std::uint8_t, 12> identifier{0xAB, 0x4B, 0x54, 0x58, 0x20, 0x32, 0x30, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A};
if (transcoder.init(identifier.data(), static_cast<std::uint32_t>(identifier.size()))) return 7;
if (transcoder.start_transcoding()) return 8;
std::array<std::uint8_t, 64> out{};
if (transcoder.transcode_image_level(0, 0, 0, out.data(), 16, format::cTFRGBA32)) return 9;
std::println("Basis Universal transcoder: KTX2 with Zstandard; {} is {} bytes a block", basist::basis_get_format_name(format::cTFBC7_RGBA), basist::basis_get_bytes_per_block_or_pixel(format::cTFBC7_RGBA));
return 0;
}
1 change: 1 addition & 0 deletions tests/mwrap/check_installed_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"mcpplibs.mwrap.cgltf": ("cgltf.cppm", "upstream/cgltf/LICENSE", b"Permission is hereby granted", b"Kuhlmann"),
"mcpplibs.mwrap.manifold": ("manifold.cppm", "upstream/manifold/LICENSE", b"Apache License", b"The Manifold Authors"),
"mcpplibs.mwrap.libjpeg_turbo": ("libjpeg_turbo.cppm", "upstream/libjpeg-turbo/README.ijg", b"Independent JPEG Group", b"libjpeg-turbo Project"),
"mcpplibs.mwrap.basis_universal": ("basis_universal.cppm", "upstream/basis_universal/NOTICE", b"Binomial LLC", b"Basis Universal"),
"mcpplibs.mwrap.libc": ("libc.cppm", None, b"", b"ISO/IEC 9899:2018"),
}

Expand Down
Loading