feat(compat.vulkan): 1.4.357.3 ships the Vulkan loader on Windows - #395
Merged
Conversation
A Windows program that links `vulkan-1.lib` needs `vulkan-1.dll` at process start, and that DLL is not part of Windows. It arrives with a GPU driver, with LunarG's Vulkan Runtime redistributable, or beside an application. A machine without a driver has none, and the program dies before `main` with 0xC0000135 (STATUS_DLL_NOT_FOUND). Measured on GitHub's `windows-2022` image (#387 probe, run 34569282837): `vulkan`, `eui-neo-vulkan` and `vulkan-hpp-module` all fail that way, while images that happen to carry a driver pass. WHAT CHANGES compat.vulkan 1.4.357.3 windows artifact adds bin/vulkan-1.dll and LICENSE.txt; mcpp.windows.runtime gains library_dirs = { "bin" } khronos.vulkan-hpp 1.4.357.1 same headers, pin -> compat.vulkan 1.4.357.3 compat.eui-neo 0.5.9.1 upstream 0.5.9 unchanged, `vulkan` feature pin -> compat.vulkan 1.4.357.3 members vulkan 1.4.357.3, vulkan-hpp-module 1.4.357.1, eui-neo-vulkan 0.5.9.1 tests/examples/vulkan on Windows, asserts the mapped vulkan-1.dll lives in the executable's own directory THE MECHANISM ALREADY EXISTS AND IS NOT NEW HERE. mcpp copies every *.dll under a dependency's `runtime.library_dirs` beside the executable it builds (mcpp #185, v0.0.73); `compat.openblas` has shipped `bin/libopenblas.dll` this way on Windows CI since mcpp-index #55. Two properties this change leans on were measured locally with mcpp 2026.9.11.2 rather than assumed: * transitive -- app -> mid -> dep(bin/vulkan-1.dll): the DLL lands beside `app`. `vulkan-hpp-module` and `eui-neo-vulkan` reach compat.vulkan only transitively. * test binaries -- `mcpp test` places it beside the test executable too. `mcpp pack` needs nothing further: its PE closure always searches the executable's own directory ("whatever the build staged beside it ... is by definition part of what it runs with"), and `vulkan-1.dll` is not in its system-DLL allow-list, so the deployed copy is what a packed program carries. THE ARTIFACT (xlings-res/vulkan-import 1.4.357.3, sha256 8118f1bd...12f5) lib/vulkan-1.lib byte-identical to 1.4.357.1's vulkan-1.def upstream `loader/vulkan-1.def`, tag vulkan-sdk-1.4.357.0 bin/vulkan-1.dll built from vulkan-sdk-1.4.357.0 by xlings-res/vulkan-loader's windows workflow, which loads the DLL and resolves vkEnumerateInstanceVersion, vkCreateInstance and vkGetInstanceProcAddr before publishing (run 34608619850) LICENSE.txt Vulkan-Loader's Apache-2.0 -- a redistributed binary carries its license README.md how each file was produced Packed deterministically (sorted, fixed mtime, numeric owner, gzip -n); the sha was computed twice, read back from GitHub, and the gitcode mirror (mcpp-res/vulkan-import 1.4.357.3) is byte-identical. COMPATIBILITY, MEASURED. The DLL exports exactly the 265 names in the .def -- no additions, no omissions -- so every import `vulkan-1.lib` can produce resolves, and no consumer can hit "entry point not found". The loader version the earlier xim payload carried (1.4.313) exports the same 265; the build is at 1.4.357 anyway so the loader matches the headers it is consumed with. On a machine that ALREADY has a GPU driver nothing is lost: the copy beside the executable is found first (the application directory precedes System32), and the loader still reads HKLM\SOFTWARE\Khronos\Vulkan\Drivers, so the driver the machine has is the ICD it uses. The ICD is deliberately not supplied -- a software fallback would hide a missing driver behind a slow device. WHY NEW VERSIONS INSTEAD OF MOVING PINS. An installed copy records the pins it resolved with. Moving a pin inside a published version does not reach a warm store, which is not hypothetical here: with #391's first approach, a warm CI store kept `compat.vulkan@1.4.357.0`, never re-evaluated its closure, and the loader never arrived. That is the same rule as compat.vulkan 1.4.357.1. Versions before 1.4.357.3 have no bin/; mcpp skips a declared runtime directory that does not exist, so `library_dirs` is inert for them. WHY THE TEST ASSERTION IS A REAL CHECK. On Windows the test now requires the mapped vulkan-1.dll to sit in the executable's directory. With an older compat.vulkan that fails on a machine with a driver (the loader comes from System32) and never runs on one without (the process dies first). The `windows-2022` leg of this PR has no system loader at all, so it can only pass if the deployment works. This is also why #391 -- which put the DLL in System32 on the runner -- is superseded rather than merged: it would make that leg pass whether or not the package works. Co-authored-by: sunrisepeak <x.d2learn.org@gmail.com>
…vulkan-hpp moves to a follow-up
TWO THINGS LOCAL VERIFICATION FOUND in the first push of this branch.
1. compat.eui-neo 0.5.9.1 could not install. The install hook looks for the
unpacked archive by name, `EUI-NEO-<version>`, and took the version
verbatim -- so it looked for `EUI-NEO-0.5.9.1/` in an archive that unpacks
to `EUI-NEO-0.5.9/`:
eui-neo: no CMakeLists.txt under .../compat-x-eui-neo/0.5.9.1/eui-neo-0.5.9.1
after unpacking; the archive layout is neither wrapped nor flat
A fourth version component is this index re-releasing the same upstream tag,
so the lookup now drops it. Three-component versions are unchanged; checked
directly: 0.5.9 -> 0.5.9, 0.5.9.1 -> 0.5.9, 0.5.10 -> 0.5.10,
1.2.3.45 -> 1.2.3, 0.5.9-rc1 -> 0.5.9-rc1. The `layer` directory keeps the
package's own version; the sources are `*/` globs, so its name never
mattered.
After the fix, locally with mcpp 2026.9.11.2:
compat.eui-neo[vulkan]: ok (backend=vulkan, loader api 1.4.357)
i.e. eui-neo 0.5.9.1 resolved compat.vulkan 1.4.357.3 through the feature.
This is the same class of bug as openxlings/xim-pkgindex#821 fixed in
vulkan-loader: a hook deriving a path from the version works exactly until a
second version shares an archive.
2. khronos.vulkan-hpp 1.4.357.1 cannot be verified in the same change that
introduces compat.vulkan 1.4.357.3. `vulkan-hpp-module` redirects only the
`khronos` namespace to this checkout, so `compat` comes from the PUBLISHED
index, which does not have 1.4.357.3 until this merges:
xlings install_packages failed (exit 1) for 'compat.vulkan@1.4.357.3'
with 1 index repo configured [mcpplibs -> https://github.com/mcpplibs/mcpp-index.git]
Redirecting `compat` as well was tried and is still refused -- mcpp reports
"≥2 project-level index repos is a known xlings resolution gap (mcpp #238;
root cause openxlings/xlings#374)" even though #238 is closed. So the
khronos.vulkan-hpp bump and its member pin are reverted here and follow
once this is merged and the index republished -- the same order
openxlings/xim-pkgindex#818 and mcpp-index#391 needed.
Consequence for this PR's CI, stated in advance: `vulkan-hpp-module` is
still selected (its manifest names vulkan) and still resolves
compat.vulkan 1.4.357.0, so on the windows leg it fails exactly as it does
on main today. `vulkan` and `eui-neo-vulkan` are the members this change is
judged by.
Co-authored-by: sunrisepeak <x.d2learn.org@gmail.com>
This was referenced Sep 11, 2026
Sunrisepeak
added a commit
that referenced
this pull request
Sep 11, 2026
…the macOS note says how to get a device (#398) KHRONOS.VULKAN-HPP 1.4.357.1 -- the same Vulkan-Headers tarball, pinned to compat.vulkan 1.4.357.3, whose Windows artifact carries the loader DLL. A new version rather than a moved pin, because an installed copy records the pin it resolved with. `vulkan-hpp-module` moves to it. This is the half of #395 that could not be verified in #395 itself: the member redirects only the `khronos` namespace to this checkout, so `compat` came from the published index, which did not have 1.4.357.3 until #395 merged. Redirecting `compat` as well is still refused on mcpp 2026.9.11.2 ("≥2 project-level index repos is a known xlings resolution gap"), so the order had to be: merge #395, republish, then this. On #395's own `windows-2022` leg the member failed exactly as predicted (it resolved compat.vulkan 1.4.357.0 and died with 0xC0000135); this change is judged by that leg passing. COMPAT.VULKAN, macOS -- a comment, no behaviour change. The block said only that the ICD is MoltenVK. It now records what was measured on macos-15 (#396): nothing crashes without MoltenVK; a device enumerates when libMoltenVK.dylib and a relative-path manifest sit in <executable dir>/vulkan/icd.d, which the static loader searches first, and that layout survives being moved; or through VK_DRIVER_FILES; and in both cases only with portability enumeration enabled. What mcpp would need to place those files is mcpp-community/mcpp#615. Co-authored-by: sunrisepeak <x.d2learn.org@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
A Windows program linking
vulkan-1.libneedsvulkan-1.dllat process start. That DLL is not part of Windows — it comes with a GPU driver, LunarG's Vulkan Runtime, or beside an application. A driverless machine has none, and the program dies beforemainwith0xC0000135(STATUS_DLL_NOT_FOUND).Measured on GitHub's
windows-2022(#387 probe):vulkan,eui-neo-vulkan,vulkan-hpp-moduleall fail this way.What changes
compat.vulkan1.4.357.3bin/vulkan-1.dll+LICENSE.txt;mcpp.windows.runtime.library_dirs = { "bin" }khronos.vulkan-hpp1.4.357.1compat.eui-neo0.5.9.1vulkanfeature pin → 1.4.357.3vulkan1.4.357.3 ·vulkan-hpp-module1.4.357.1 ·eui-neo-vulkan0.5.9.1tests/examples/vulkanvulkan-1.dllis in the executable's own directoryThe mechanism is not new
mcpp copies every
*.dllunder a dependency'sruntime.library_dirsbeside the built executable (mcpp #185, v0.0.73).compat.openblasshipsbin/libopenblas.dllthis way on Windows CI since #55. Measured locally with mcpp 2026.9.11.2 rather than assumed:app → mid → dep(bin/vulkan-1.dll): DLL lands besideapp(vulkan-hpp-module and eui-neo-vulkan reach compat.vulkan only transitively)mcpp testplaces it beside the test executable toomcpp pack's PE closure always searches the executable's own directory, andvulkan-1.dllis not in its system-DLL allow-list — so a packed program carries the deployed copy.Compatibility, measured
vulkan-1.def→ no "entry point not found" possiblelib/vulkan-1.libbyte-identical to 1.4.357.1'sHKLM\SOFTWARE\Khronos\Vulkan\Drivers→ the machine's driver is the ICD used. The ICD is deliberately not supplied.bin/; mcpp skips a missing runtime dir → inert for them.Why new versions, not moved pins
An installed copy records the pins it resolved with. With #391's first approach a warm CI store kept
compat.vulkan@1.4.357.0and the loader never arrived — measured. Same rule as compat.vulkan 1.4.357.1.Artifact
xlings-res/vulkan-import1.4.357.3 · sha2568118f1bd897e553baffabf484a14db980ce1f0a6cfdb5a6222c0a236ecdf12f5bin/vulkan-1.dllbuilt fromvulkan-sdk-1.4.357.0by xlings-res/vulkan-loader's windows workflow (run 34608619850), which LoadLibrary-tests the DLL before publishingmcpp-res/vulkan-importmirror both byte-identicalHow this PR proves itself
The
windows-2022leg has no system loader at all. The vulkan members can only pass if deployment works, and assertion 8 inloader.cpprequires the DLL beside the exe. This is why #391 is superseded, not merged: putting the DLL in System32 on the runner would make this leg pass whether or not the package works.