|
| 1 | +# Vulkan 跨平台闭环 —— mcpp / xlings 生态 |
| 2 | + |
| 3 | +日期:2026-09-11 · 状态:**Windows / xlings / CI 已实施并实测;macOS 已实测,落地需要 mcpp 一项能力** |
| 4 | +涉及:`mcpp-index`、`xim-pkgindex`、`xlings-res`、`mcpp`(仅 macOS 部署) |
| 5 | + |
| 6 | +本文最初是一份待 review 的设计。实施过程中有几处原方案被实测否定或被更简单的既有机制取代, |
| 7 | +所以现在它记录的是**做了什么、测到了什么、和原设计哪里不同、为什么**。原设计中仍成立的部分保留。 |
| 8 | + |
| 9 | +--- |
| 10 | + |
| 11 | +## 0. 结论 |
| 12 | + |
| 13 | +| | 开发者侧不报错 | `mcpp pack` 分发能跑 | xlings 分发能跑 | |
| 14 | +|---|---|---|---| |
| 15 | +| **Windows,有显卡驱动** | ✅ | ✅ | ✅ | |
| 16 | +| **Windows,无显卡驱动**(用户重点) | ✅ 实测 | ✅ 实测(干净目录) | ✅(分发 pack 产物即可;xim 包亦有 1.4.357) | |
| 17 | +| **Linux** | ✅ 实测 | ✅(RPATH,既有) | ✅(既有) | |
| 18 | +| **macOS,无 MoltenVK** | ✅ 实测:不崩溃,设备数 0 | ⚠️ `mcpp pack` 目前**拒绝打包 Mach-O 程序**(mcpp 自身限制,与 Vulkan 无关);分发构建目录可运行 | ✅ 不崩溃 | |
| 19 | +| **macOS,要真实设备** | ⚠️ 需 mcpp#615 的部署能力;当下 `VK_DRIVER_FILES` 实测可用 | ⚠️ 同上,且依赖 pack 支持 Mach-O | ⚠️ 机制具备(xim:moltenvk + shim `envs` 设 `VK_DRIVER_FILES`),`VK_DRIVER_FILES` 路径实测可枚举设备,未经 xlings 端到端 | |
| 20 | + |
| 21 | +"无驱动 Windows 上 `0xC0000135` 启动即崩"这一核心问题已消除,并在没有任何系统 loader 的 |
| 22 | +`windows-2022` 上,对 `mcpp build`、`mcpp test`、`mcpp pack` 后在干净目录运行三种情形逐一实测。 |
| 23 | + |
| 24 | +--- |
| 25 | + |
| 26 | +## 1. 目标(不变) |
| 27 | + |
| 28 | +1. **开发者侧不报错。** 三平台开发 Vulkan 库/应用,`mcpp build`/`mcpp test` 不应构建或启动失败。 |
| 29 | +2. **分发能跑。** `mcpp pack` 产物、经 xlings 分发的程序,在目标机器上能启动。 |
| 30 | + |
| 31 | +--- |
| 32 | + |
| 33 | +## 2. 前提:三层,以及两种不同的故障 |
| 34 | + |
| 35 | +| 层 | 缺失后果 | 可否随程序分发 | |
| 36 | +|---|---|---| |
| 37 | +| **Loader**(`vulkan-1.dll` / 静态 loader) | 进程在 `main` 之前死(Windows `0xC0000135`) | 可以(Apache-2.0) | |
| 38 | +| **ICD**(驱动) | 正常启动,`vkCreateInstance` 返回 `VK_ERROR_INCOMPATIBLE_DRIVER`,设备数 0 | 厂商驱动不可;MoltenVK / lavapipe 可以 | |
| 39 | + |
| 40 | +**设计目标:让所有故障都落在第二种。** 这一条贯穿实施,且已在 Windows 与 macOS 上实测成立。 |
| 41 | + |
| 42 | +--- |
| 43 | + |
| 44 | +## 3. 实施了什么 |
| 45 | + |
| 46 | +| PR | 内容 | 状态 | |
| 47 | +|---|---|---| |
| 48 | +| mcpp-index **#395** | `compat.vulkan` **1.4.357.3**:Windows 产物带 `bin/vulkan-1.dll` + `LICENSE.txt`;`mcpp.windows.runtime.library_dirs = {"bin"}`;`compat.eui-neo` **0.5.9.1**(vulkan feature 改 pin);成员 pin;`tests/examples/vulkan` 在 Windows 上断言 loader 就在 exe 同目录;修 eui-neo install hook | 见 PR | |
| 49 | +| mcpp-index **#394** | PR 不再自动升级为全量 CI(仅 cron / 手动) | 已合 | |
| 50 | +| mcpp-index **#392** | mysql-connector-cpp:stdlib 限制写明 + llvm 腿跳过 | 已合 | |
| 51 | +| xim-pkgindex **#821** | `vulkan-loader` windows **1.4.357**、`latest` 指向它;install 目录由版本推导;声明平台版本分叉 | 已合 | |
| 52 | +| xlings-res/vulkan-loader | release **1.4.357**:Windows loader,由 runner 构建并 LoadLibrary 自检 | 已发布 | |
| 53 | +| xlings-res/vulkan-import | release **1.4.357.3**:扁平产物(见 §5.1) | 已发布 + CN 镜像 | |
| 54 | +| mcpp-index #391 | CI 往 System32 放 DLL | **已关闭**:会掩盖真实修复 | |
| 55 | +| mcpp-index #396 | 临时探针(Windows pack、macOS ICD 发现),runs 34610618549 / 34611138464 | 已关闭,结论见 §5 | |
| 56 | +| mcpp-community/mcpp **#615** | macOS:让依赖把 `.dylib` 与子目录里的 ICD 清单部署到可执行文件旁(附实测) | 已提 | |
| 57 | + |
| 58 | +后续(依赖 #395 合入并发布索引):`khronos.vulkan-hpp` **1.4.357.1** 改 pin + `vulkan-hpp-module` 成员(§4.4)。 |
| 59 | + |
| 60 | +--- |
| 61 | + |
| 62 | +## 4. 与原设计的偏差,以及原因 |
| 63 | + |
| 64 | +### 4.1 没有新建 `compat.vulkan-loader` 包 —— 用 openblas 的既有形状 |
| 65 | + |
| 66 | +原设计打算新建一个 `compat.vulkan-loader`,经 `xim:vulkan-loader` 依赖取 payload。实施时发现 |
| 67 | +**同一件事已经有被 CI 验证过的先例**:`compat.openblas`(mcpp #185 / v0.0.73,mcpp-index #55)—— |
| 68 | +compat 描述符自己的 `xpm.windows` 直接指向一个扁平预编译包,`mcpp.windows.runtime.library_dirs = |
| 69 | +{"bin"}` 让 mcpp 把 DLL 拷到可执行文件旁。 |
| 70 | + |
| 71 | +于是 DLL 直接放进 `compat.vulkan` 自己的 Windows 产物。**每个已经依赖 compat.vulkan 的消费者自动获得它, |
| 72 | +不需要任何人新增依赖边。** |
| 73 | + |
| 74 | +### 4.2 放弃 `xpm.<platform>.deps` 路线(原设计 §3.4 的判断被坐实) |
| 75 | + |
| 76 | +#391 首版在 `compat.vulkan` 的 windows 块声明 `deps = { "xim:vulkan-loader@>=1.4.313" }`,三次运行零效果、 |
| 77 | +零诊断(冷存储 / 热存储 / 删光缓存后冷跑)。该路径在本索引中:**linux 20 个包使用,windows 2 个, |
| 78 | +且那 2 个没有任何成员在 Windows 上测**。§4.1 的做法完全不经过它。 |
| 79 | + |
| 80 | +### 4.3 必须升版本号,不能原地改 pin |
| 81 | + |
| 82 | +已安装的副本记录着它解析时的 pin。原地改 pin 到不了热存储——#391 第二次运行就是这样:热缓存保留 |
| 83 | +`compat.vulkan@1.4.357.0`,闭包从不重新求值,loader 根本没装。与 compat.vulkan 1.4.357.1 的规则一致。 |
| 84 | + |
| 85 | +`mcpp` 表是版本无关的,所以新增 `library_dirs = {"bin"}` 对旧版本也可见;旧版本产物没有 `bin/`, |
| 86 | +mcpp 对不存在的运行期目录直接跳过(`plan.cppm`:`if (!is_directory) continue`),对它们无效果。 |
| 87 | + |
| 88 | +### 4.4 `khronos.vulkan-hpp` 拆到后续 PR |
| 89 | + |
| 90 | +`vulkan-hpp-module` 只把 `khronos` 命名空间重定向到本 checkout,`compat` 来自**已发布**索引, |
| 91 | +而 1.4.357.3 在 #395 合入前并不存在: |
| 92 | + |
| 93 | +``` |
| 94 | +xlings install_packages failed (exit 1) for 'compat.vulkan@1.4.357.3' |
| 95 | +with 1 index repo configured [mcpplibs -> https://github.com/mcpplibs/mcpp-index.git] |
| 96 | +``` |
| 97 | + |
| 98 | +尝试同时重定向 `compat`,被拒绝: |
| 99 | + |
| 100 | +``` |
| 101 | +≥2 project-level index repos is a known xlings resolution gap |
| 102 | +(mcpp #238; root cause openxlings/xlings#374) |
| 103 | +``` |
| 104 | + |
| 105 | +**注意:mcpp#238 与 xlings#374 均已关闭(2026-07-18/19),但 mcpp 2026.9.11.2 自带的 xlings 上仍能复现。** |
| 106 | +要么修复未进入 vendored xlings,要么有回归。这是一条独立的待查项(§6)。 |
| 107 | + |
| 108 | +### 4.5 能力名(`vulkan.loader`)暂未引入 |
| 109 | + |
| 110 | +原设计 §5.2 提议新增 `vulkan.loader` 能力。实施中确认 mcpp 对未满足的能力**不会**构建期报错 |
| 111 | +(`vulkan.icd.driver` 在 macOS/Windows 上一直未被满足,构建照常成功),所以新增一个"需求" |
| 112 | +既不能带来原设计 L3 想要的诊断,反而有误导性。loader 的供给改为由产物本身保证(§4.1)。 |
| 113 | + |
| 114 | +### 4.6 CI 往 System32 放 DLL 的方案被放弃 |
| 115 | + |
| 116 | +#391 第二版在 CI 里供给 loader。它能让 windows 腿变绿,**但无论包是否正确都会变绿**。 |
| 117 | +`windows-2022` 恰好没有系统 loader,是验证"无 vulkan dll 的 Windows"的理想环境,不应被掩盖。 |
| 118 | + |
| 119 | +--- |
| 120 | + |
| 121 | +## 5. 实测 |
| 122 | + |
| 123 | +### 5.1 产物与兼容性 |
| 124 | + |
| 125 | +`xlings-res/vulkan-import` **1.4.357.3**,sha256 `8118f1bd897e553baffabf484a14db980ce1f0a6cfdb5a6222c0a236ecdf12f5`: |
| 126 | + |
| 127 | +| 文件 | 来源 | |
| 128 | +|---|---| |
| 129 | +| `bin/vulkan-1.dll` | tag `vulkan-sdk-1.4.357.0`,xlings-res/vulkan-loader windows workflow(run 34608619850),构建时 LoadLibrary 并解析 `vkEnumerateInstanceVersion` / `vkCreateInstance` / `vkGetInstanceProcAddr` | |
| 130 | +| `lib/vulkan-1.lib` | 与 1.4.357.1 产物**字节一致** | |
| 131 | +| `vulkan-1.def` | 上游原样 | |
| 132 | +| `LICENSE.txt` | Vulkan-Loader Apache-2.0 —— 分发二进制须附许可证 | |
| 133 | + |
| 134 | +- 确定性打包(排序、固定 mtime、数值 owner、`gzip -n`),两次 sha 一致;GitHub 回读一致;gitcode 镜像字节一致 |
| 135 | +- **导出表:DLL 导出与 `vulkan-1.def` 的 265 个名字完全一致,差集 0**(`llvm-readobj --coff-exports`)。 |
| 136 | + 早先担心的"新导入库配旧 DLL → 找不到入口点"不存在;1.4.313 的 DLL 同样是这 265 个 |
| 137 | +- PE:`IMAGE_FILE_MACHINE_AMD64`,自报版本 1.4.357 |
| 138 | + |
| 139 | +### 5.2 mcpp 部署机制(本地,mcpp 2026.9.11.2) |
| 140 | + |
| 141 | +| 验证 | 结果 | |
| 142 | +|---|---| |
| 143 | +| 传递依赖 `app → mid → dep(bin/vulkan-1.dll)` | DLL 部署到 `app` 旁 ✅ | |
| 144 | +| `mcpp test` 的测试二进制 | DLL 部署到测试可执行文件旁 ✅ | |
| 145 | +| `pack.cppm` | PE 闭包"始终搜索产物自身目录";`vulkan-1.dll` 不在 `kPeSystem` 白名单(`opengl32`/`d3d12`/`dxgi` 在)| |
| 146 | + |
| 147 | +### 5.3 Windows(探针 #396,`windows-2022`,**已确认无系统 loader**) |
| 148 | + |
| 149 | +``` |
| 150 | +no system vulkan-1.dll: confirmed |
| 151 | +-- mcpp build, 原地运行 -- |
| 152 | +PROBE: loader api 1.4.357 |
| 153 | +PROBE: vulkan-1.dll = D:\a\_temp\vkprobe\target\x86_64-windows-msvc\...\bin\vulkan-1.dll |
| 154 | +PROBE: portability_enumeration=1 vkCreateInstance=-9 |
| 155 | +PROBE: devices=0 (no instance) |
| 156 | +-- mcpp pack -- |
| 157 | +archive: target/dist/vkprobe-0.1.0-x86_64-pc-windows-msvc.zip |
| 158 | + 741376 vkprobe-0.1.0-x86_64-pc-windows-msvc/vulkan-1.dll |
| 159 | +-- 解压到干净目录,PATH 只有 System32 -- |
| 160 | +PROBE: loader api 1.4.357 |
| 161 | +PROBE: vulkan-1.dll = D:\a\_temp\clean-run\vkprobe-0.1.0-x86_64-pc-windows-msvc\vulkan-1.dll |
| 162 | +PROBE: devices=0 (no instance) |
| 163 | +``` |
| 164 | + |
| 165 | +`-9` = `VK_ERROR_INCOMPATIBLE_DRIVER`:无 ICD 的机器的正确状态,不是故障。 |
| 166 | + |
| 167 | +#395 自己的 CI(`windows-2022`,同一台无 loader 的镜像)上,两个被判定的成员: |
| 168 | + |
| 169 | +``` |
| 170 | +Downloading compat.vulkan v1.4.357.3 |
| 171 | +compat.vulkan: loader deployed beside the executable (...\tests\examples\vulkan\target\x86_64-windows-msvc\...\bin\vulkan-1.dll) |
| 172 | +compat.vulkan: ok (loader api 1.4.357, 4 loader extension(s), WSI trampolines linked) |
| 173 | +
|
| 174 | +Downloading compat.eui-neo v0.5.9.1 |
| 175 | +Downloading compat.vulkan v1.4.357.3 |
| 176 | +compat.eui-neo[vulkan]: ok (backend=vulkan, loader api 1.4.357) |
| 177 | +``` |
| 178 | + |
| 179 | +第一条来自 `tests/examples/vulkan` 新增的断言:加载的 `vulkan-1.dll` 必须与 exe 同目录。第二条是经 |
| 180 | +eui-neo 的 feature 传递依赖到 compat.vulkan 的路径。同一腿上 `vulkan-hpp-module` 仍失败,原因如 §4.4 |
| 181 | +所预测:它从已发布索引解析到 compat.vulkan 1.4.357.0。 |
| 182 | + |
| 183 | +### 5.4 Linux(本地) |
| 184 | + |
| 185 | +`vulkan`(compat.vulkan 1.4.357.3,loader api 1.4.357)、`eui-neo-vulkan`(eui-neo 0.5.9.1 → compat.vulkan |
| 186 | +1.4.357.3,`backend=vulkan, loader api 1.4.357`)均通过。 |
| 187 | + |
| 188 | +### 5.5 macOS(探针 #396,`macos-15` arm64) |
| 189 | + |
| 190 | +CoreFoundation 对未打包可执行文件: |
| 191 | + |
| 192 | +``` |
| 193 | +PROBE: bundle url = /Users/runner/work/_temp/cfprobe/bin |
| 194 | +PROBE: resources dir = /Users/runner/work/_temp/cfprobe/bin ← 就是 exe 所在目录 |
| 195 | +``` |
| 196 | + |
| 197 | +静态 loader(`APPLE_STATIC_LOADER`)在 `loader.c` 中把 `<resources dir>/vulkan/icd.d` 放在搜索路径最前。 |
| 198 | + |
| 199 | +| 场景 | 退出 | 结果 | |
| 200 | +|---|---|---| |
| 201 | +| A 无 MoltenVK | 0 | `vkCreateInstance=-9`,"Found no drivers!",设备 0 —— **不崩溃** | |
| 202 | +| B `bin/libMoltenVK.dylib` + `bin/vulkan/icd.d/MoltenVK_icd.json`(`library_path: "../../libMoltenVK.dylib"`) | 0 | `vkCreateInstance=0`,**devices=1,Apple Paravirtual device** | |
| 203 | +| B2 同上,绝对路径 | 0 | devices=1 | |
| 204 | +| B3 **把整个 `bin/` 拷到别处**再运行 | 0 | 在新位置找到清单,**devices=1 —— 布局可随程序迁移** | |
| 205 | +| C `VK_DRIVER_FILES`(xim:moltenvk 文档的方式) | 0 | devices=1 | |
| 206 | + |
| 207 | +所有设备枚举都要求实例启用 `VK_KHR_portability_enumeration` 并设置 |
| 208 | +`VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR`——这是消费方 API 层的事,包无法代劳。 |
| 209 | + |
| 210 | +--- |
| 211 | + |
| 212 | +## 6. 仍未闭环的部分 |
| 213 | + |
| 214 | +| 项 | 性质 | 去向 | |
| 215 | +|---|---|---| |
| 216 | +| **macOS 部署 MoltenVK** | mcpp 目前只把 `*.dll` **平铺**拷进 `bin/`;macOS 需要 `.dylib` 与 `vulkan/icd.d/` **子目录**。§5.5 B3 证明该布局可行且可迁移,缺的只是部署能力 | **mcpp#615** | |
| 217 | +| **`mcpp pack` 拒绝 Mach-O 程序** | "cannot package the Mach-O program … yet":闭包靠 `LD_TRACE_LOADED_OBJECTS` 解析,dyld 不认。与 Vulkan 无关,但决定了 macOS 上 pack 这条分发路径目前不存在 | mcpp 既有限制 | |
| 218 | +| 双项目 indices 仍失败 | mcpp#238 / xlings#374 已关闭,2026.9.11.2 上仍复现 | 待查,可能是 vendored xlings 回归 | |
| 219 | +| `APPLE_STATIC_LOADER` 为上游不支持的配置 | CMake 原话:"not supported or tested as part of the loader. Use it at your own risk" / "only exists at the request of Google for Chromium. No other project should use this!" | 风险记录;§5.5 实测未见问题 | |
| 220 | +| exe 旁的 loader 优先于系统 loader | 用户驱动若带更新版本,用的仍是我们的 1.4.357;ICD 仍是用户的驱动,导出表稳定,但更新的 loader 级扩展不可用 | 已知代价,所有 bundling 应用共担 | |
| 221 | +| 同进程双 loader | 应用带一份、注入的插件加载系统那份 | 业界已知问题,无通用解 | |
| 222 | +| gitcode `xlings-res/vulkan-loader@1.4.357` 有一个误名资产 `loader357.zip` | 首次上传用错了文件名;gitcode 资产不能经 API 删除 | 需网页删除;描述符指向正确命名的资产 | |
| 223 | + |
| 224 | +--- |
| 225 | + |
| 226 | +## 7. 实施中发现并修复的 bug |
| 227 | + |
| 228 | +| 位置 | 问题 | 修复 | |
| 229 | +|---|---|---| |
| 230 | +| `compat.eui-neo` install hook | 用包版本拼上游目录名:`0.5.9.1` 找 `EUI-NEO-0.5.9.1/`,而归档是 `EUI-NEO-0.5.9/`,报 "neither wrapped nor flat" | 查找前剥掉第四段(实测 `0.5.9.1→0.5.9`、`1.2.3.45→1.2.3`、`0.5.10`/`0.5.9-rc1` 不变) | |
| 231 | +| `xim:vulkan-loader` install | 写死目录 `vulkan-loader-1.4.313`;加第二个版本就会移动空目录 | 由版本推导,缺失即报错 | |
| 232 | +| 描述符注释 | `compat.vulkan` 头部与 windows `runtime` 仍写着"DLL 随显卡驱动来,不随我们" | 按 1.4.357.3 修正 | |
| 233 | +| `tests/examples/vulkan` 注释 | "compat.vulkan has no windows entry" 早已不实 | 删除 | |
| 234 | + |
| 235 | +两处 hook bug 同属一类:**hook 从版本号推导路径,只在第二个版本共用同一个归档之前成立。** |
| 236 | + |
| 237 | +--- |
| 238 | + |
| 239 | +## 8. 附:参考 |
| 240 | + |
| 241 | +| | | |
| 242 | +|---|---| |
| 243 | +| mcpp `src/build/plan.cppm` | `runtimeDeployFiles`:依赖 `runtime.library_dirs` 下的 `*.dll` → `bin/<filename>` | |
| 244 | +| mcpp `src/pack/pack.cppm` | PE 闭包搜索产物自身目录;`kPeSystem` 白名单 | |
| 245 | +| mcpp `.agents/docs/2026-06-29-windows-runtime-dll-deployment-and-openblas.md` | 部署机制的原始设计 | |
| 246 | +| Vulkan-Loader `loader/loader.c` | macOS 下 bundle Resources + `vulkan/icd.d` 搜索 | |
| 247 | +| mcpp-community/mcpp#609 / microsoft/STL#6294 | windows-2022 钉子的起因 | |
| 248 | +| mcpp-community/mcpp#614 | `XLINGS_PROJECT_DIR` 的 Windows/POSIX 不对称 | |
0 commit comments