diff --git a/pkgs/c/compat.vulkan.lua b/pkgs/c/compat.vulkan.lua index 5d16e344..d656a7da 100644 --- a/pkgs/c/compat.vulkan.lua +++ b/pkgs/c/compat.vulkan.lua @@ -33,12 +33,15 @@ -- this through `APPLE_STATIC_LOADER` + pthread_once; Linux through -- `__attribute__((constructor))`. Windows has neither. -- --- The supported Windows arrangement is the ordinary one every Vulkan --- application uses: link `vulkan-1.lib` and let the system `vulkan-1.dll`, --- installed by any GPU driver, do the ICD loading. The windows xpm entry is --- therefore a small artifact carrying that import library — symbol stubs, no --- code — generated from Khronos' own `loader/vulkan-1.def` (shipped in this --- very loader tarball) with a single reproducible command: +-- The supported Windows arrangement is the ordinary one: link `vulkan-1.lib` +-- and load `vulkan-1.dll` at process start. Through 1.4.357.2 that DLL was +-- assumed to come from the machine's GPU driver, which a driverless machine +-- does not have (0xC0000135 before `main`, measured on `windows-2022`). From +-- 1.4.357.3 the artifact also carries the DLL, built from the same tag, and +-- `runtime.library_dirs` has mcpp place it beside every consuming executable; +-- see the windows xpm entry. The import library in that artifact — symbol +-- stubs, no code — is generated from Khronos' own `loader/vulkan-1.def` +-- (shipped in this very loader tarball) with a single reproducible command: -- -- llvm-dlltool -d vulkan-1.def -l lib/vulkan-1.lib -m i386:x86-64 -- @@ -89,6 +92,16 @@ package = { -- version -- and where a consumer also names the farm directly, the -- two disagree and the build stops. Measured on a CI runner with a -- warm ~/.mcpp; see the note on compat.vulkan 1.4.357.1. + -- 1.4.357.3: the same loader source. A new version on every platform + -- because the WINDOWS artifact changed -- it now carries the loader DLL + -- (see the windows entry) -- and a version is one key across platforms. + ["1.4.357.3"] = { + url = { + GLOBAL = "https://github.com/KhronosGroup/Vulkan-Loader/archive/refs/tags/vulkan-sdk-1.4.357.0.tar.gz", + CN = "https://gitcode.com/mcpp-res/vulkan/releases/download/1.4.357.0/vulkan-1.4.357.0.tar.gz", + }, + sha256 = "54f2537df22313768da0317dda2abdaaab7711b4081c48c869a79db343d0ae70", + }, ["1.4.357.2"] = { url = { GLOBAL = "https://github.com/KhronosGroup/Vulkan-Loader/archive/refs/tags/vulkan-sdk-1.4.357.0.tar.gz", @@ -136,6 +149,16 @@ package = { -- version -- and where a consumer also names the farm directly, the -- two disagree and the build stops. Measured on a CI runner with a -- warm ~/.mcpp; see the note on compat.vulkan 1.4.357.1. + -- 1.4.357.3: the same loader source. A new version on every platform + -- because the WINDOWS artifact changed -- it now carries the loader DLL + -- (see the windows entry) -- and a version is one key across platforms. + ["1.4.357.3"] = { + url = { + GLOBAL = "https://github.com/KhronosGroup/Vulkan-Loader/archive/refs/tags/vulkan-sdk-1.4.357.0.tar.gz", + CN = "https://gitcode.com/mcpp-res/vulkan/releases/download/1.4.357.0/vulkan-1.4.357.0.tar.gz", + }, + sha256 = "54f2537df22313768da0317dda2abdaaab7711b4081c48c869a79db343d0ae70", + }, ["1.4.357.2"] = { url = { GLOBAL = "https://github.com/KhronosGroup/Vulkan-Loader/archive/refs/tags/vulkan-sdk-1.4.357.0.tar.gz", @@ -183,6 +206,37 @@ package = { -- version -- and where a consumer also names the farm directly, the -- two disagree and the build stops. Measured on a CI runner with a -- warm ~/.mcpp; see the note on compat.vulkan 1.4.357.1. + -- 1.4.357.3: THE LOADER DLL SHIPS WITH THE IMPORT LIBRARY. + -- + -- `vulkan-1.dll` is not a Windows component: it arrives with a GPU + -- driver, with LunarG's Vulkan Runtime redistributable, or beside an + -- application. So a machine without a driver has no loader, and a + -- program linking `vulkan-1.lib` dies before `main` with 0xC0000135 + -- (STATUS_DLL_NOT_FOUND). Measured on GitHub's `windows-2022` image + -- (mcpp-index #387 probe; `vulkan`, `eui-neo-vulkan` and + -- `vulkan-hpp-module` all failed that way). + -- + -- The artifact adds `bin/vulkan-1.dll`, built from + -- `vulkan-sdk-1.4.357.0` -- the same tag as the headers and the .def -- + -- by xlings-res/vulkan-loader's windows workflow, which loads the DLL + -- and resolves its entry points before publishing. `lib/vulkan-1.lib` + -- is byte-identical to 1.4.357.1's, and the DLL exports exactly the + -- 265 names in `vulkan-1.def`: every import that library can produce + -- resolves, so no consumer can hit "entry point not found". + -- + -- ON A MACHINE THAT ALREADY HAS A 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 GPU driver the + -- machine has is the ICD it uses. This is the ordinary arrangement for + -- an application that redistributes the loader. + ["1.4.357.3"] = { + url = { + GLOBAL = "https://github.com/xlings-res/vulkan-import/releases/download/1.4.357.3/vulkan-import-1.4.357.3.tar.gz", + CN = "https://gitcode.com/mcpp-res/vulkan-import/releases/download/1.4.357.3/vulkan-import-1.4.357.3.tar.gz", + }, + sha256 = "8118f1bd897e553baffabf484a14db980ce1f0a6cfdb5a6222c0a236ecdf12f5", + }, ["1.4.357.2"] = { url = { GLOBAL = "https://github.com/xlings-res/vulkan-import/releases/download/1.4.357.1/vulkan-import-1.4.357.1.tar.gz", @@ -359,15 +413,26 @@ package = { -- .def it came from. The anchor keeps a buildable target, the same -- shape `compat.opengl` uses for a headers-only package. -- - -- The artifact is packed FLAT — lib/ at the archive root, no wrap - -- directory — because `-L` is not glob-expanded the way + -- The artifact is packed FLAT — lib/ and bin/ at the archive root, no + -- wrap directory — because `-L` is not glob-expanded the way -- include_dirs and sources are. With a wrap layer the relative -- `-Llib` below misses and the link fails with -- "LNK1181: cannot open input file 'vulkan-1.lib'". sources = { "mcpp_generated/vulkan_import_anchor.c" }, ldflags = { "-Llib", "-lvulkan-1" }, runtime = { - -- vulkan-1.dll ships with the GPU driver, not with us. + -- THE LOADER TRAVELS WITH THE PROGRAM (1.4.357.3+). mcpp copies + -- every *.dll under a dependency's runtime library_dirs beside the + -- executable it builds -- for transitive dependencies too -- and + -- `mcpp pack` always searches the executable's own directory, so + -- the same file reaches a packed distribution. Versions before + -- 1.4.357.3 have no bin/ in their artifact; mcpp skips a declared + -- directory that does not exist, so for them this is inert. + -- + -- The ICD is deliberately NOT supplied: on a machine with a GPU + -- driver the driver's ICD is the right one, and a software + -- fallback would hide a missing driver behind a slow device. + library_dirs = { "bin" }, dlopen_libs = { "vulkan-1.dll" }, capabilities = { "vulkan.icd.driver" }, }, diff --git a/pkgs/e/compat.eui-neo.lua b/pkgs/e/compat.eui-neo.lua index 0fa4d0bb..25ffe7f5 100644 --- a/pkgs/e/compat.eui-neo.lua +++ b/pkgs/e/compat.eui-neo.lua @@ -148,6 +148,13 @@ package = { url = "https://github.com/sudoevolve/EUI-NEO/archive/refs/tags/v0.5.9.tar.gz", sha256 = "370d1da706d94bbbb144fa1634e1d9796a8a1ffd58b696fbb801296aef15703d", }, + -- 0.5.9.1: upstream 0.5.9 unchanged. Exists because the `vulkan` + -- feature now pins compat.vulkan 1.4.357.3, whose Windows artifact + -- carries the loader DLL; an installed 0.5.9 records the old pin. + ["0.5.9.1"] = { + url = "https://github.com/sudoevolve/EUI-NEO/archive/refs/tags/v0.5.9.tar.gz", + sha256 = "370d1da706d94bbbb144fa1634e1d9796a8a1ffd58b696fbb801296aef15703d", + }, }, macosx = { ["0.5.3"] = { @@ -183,6 +190,13 @@ package = { url = "https://github.com/sudoevolve/EUI-NEO/archive/refs/tags/v0.5.9.tar.gz", sha256 = "370d1da706d94bbbb144fa1634e1d9796a8a1ffd58b696fbb801296aef15703d", }, + -- 0.5.9.1: upstream 0.5.9 unchanged. Exists because the `vulkan` + -- feature now pins compat.vulkan 1.4.357.3, whose Windows artifact + -- carries the loader DLL; an installed 0.5.9 records the old pin. + ["0.5.9.1"] = { + url = "https://github.com/sudoevolve/EUI-NEO/archive/refs/tags/v0.5.9.tar.gz", + sha256 = "370d1da706d94bbbb144fa1634e1d9796a8a1ffd58b696fbb801296aef15703d", + }, }, windows = { ["0.5.3"] = { @@ -218,6 +232,13 @@ package = { url = "https://github.com/sudoevolve/EUI-NEO/archive/refs/tags/v0.5.9.tar.gz", sha256 = "370d1da706d94bbbb144fa1634e1d9796a8a1ffd58b696fbb801296aef15703d", }, + -- 0.5.9.1: upstream 0.5.9 unchanged. Exists because the `vulkan` + -- feature now pins compat.vulkan 1.4.357.3, whose Windows artifact + -- carries the loader DLL; an installed 0.5.9 records the old pin. + ["0.5.9.1"] = { + url = "https://github.com/sudoevolve/EUI-NEO/archive/refs/tags/v0.5.9.tar.gz", + sha256 = "370d1da706d94bbbb144fa1634e1d9796a8a1ffd58b696fbb801296aef15703d", + }, }, }, @@ -420,7 +441,7 @@ package = { "*/core/render/vulkan/vulkan_shadertoy.cpp", "*/core/render/vulkan/vulkan_text.cpp", }, - deps = { ["compat.vulkan"] = "1.4.357.0" }, + deps = { ["compat.vulkan"] = "1.4.357.3" }, }, -- ── Window backend ──────────────────────────────────────────── -- Exclusive in the same way and for the same reason as the render @@ -676,7 +697,16 @@ local function normalise_layout(layer) -- hook runs on every platform; the host guard is further down and it only -- covers the glib staging. With no directory listing available, the -- archive's shape is ASKED ABOUT by name rather than discovered. + -- + -- The name carries UPSTREAM's version, which is not always this package's. + -- A fourth component is this index re-releasing the same tag -- 0.5.9.1 is + -- upstream 0.5.9 with one dependency re-pinned -- and the archive still + -- unpacks to `EUI-NEO-0.5.9/`. Taking the version verbatim looked for + -- `EUI-NEO-0.5.9.1/`, found nothing, and failed with the error below; + -- measured on the first 0.5.9.1 install. Three-component versions are + -- unchanged by this. local v = pkginfo.version() + v = v:match("^(%d+%.%d+%.%d+)%.%d+$") or v for _, name in ipairs({ "EUI-NEO-" .. v, "eui-neo-" .. v, "EUI-NEO-v" .. v, "eui-neo-v" .. v }) do if os.isfile(path.join(name, "CMakeLists.txt")) then diff --git a/tests/examples/eui-neo-vulkan/mcpp.toml b/tests/examples/eui-neo-vulkan/mcpp.toml index 69576464..8528f1a5 100644 --- a/tests/examples/eui-neo-vulkan/mcpp.toml +++ b/tests/examples/eui-neo-vulkan/mcpp.toml @@ -6,7 +6,7 @@ version = "0.1.0" # package resolves the exclusive choice in its own preprocessor from the # MCPP_FEATURE_* flags. The window backend stays GLFW, unnamed. [dependencies.compat] -eui-neo = { version = "0.5.9", features = ["vulkan"] } +eui-neo = { version = "0.5.9.1", features = ["vulkan"] } [build] cxxflags = ["-DHAVE_EUI_VULKAN=1"] diff --git a/tests/examples/vulkan/mcpp.toml b/tests/examples/vulkan/mcpp.toml index 40056d82..46d0c66d 100644 --- a/tests/examples/vulkan/mcpp.toml +++ b/tests/examples/vulkan/mcpp.toml @@ -3,7 +3,7 @@ name = "vulkan-tests" version = "0.1.0" [dependencies.compat] -vulkan = "1.4.357.0" +vulkan = "1.4.357.3" # All three platforms now carry compat.vulkan — linux/macOS build the loader # from source, windows links the import library. HAVE_VULKAN_LOADER stays as the diff --git a/tests/examples/vulkan/tests/loader.cpp b/tests/examples/vulkan/tests/loader.cpp index 88ab1936..d801a4e5 100644 --- a/tests/examples/vulkan/tests/loader.cpp +++ b/tests/examples/vulkan/tests/loader.cpp @@ -8,14 +8,22 @@ // legitimately ABSENT on a driverless machine and asserting on it would just be // testing the runner's hardware. // -// HAVE_VULKAN_LOADER is set by THIS project's own [target.'cfg(...)'.build] -// cxxflags, because compat.vulkan has no windows entry. A consumer that keys -// its source off a dependency's presence has to declare that itself. +// HAVE_VULKAN_LOADER is set by THIS project's own [build] cxxflags. A consumer +// that keys its source off a dependency's presence has to declare that itself. #if defined(HAVE_VULKAN_LOADER) #include #endif import std; +#if defined(HAVE_VULKAN_LOADER) && defined(_WIN32) +// Declared rather than taken from : two functions are needed, and +// the header's macros and min/max collide with `import std`. Both live in +// kernel32, which every Windows program already links. +extern "C" __declspec(dllimport) void* GetModuleHandleW(const wchar_t* name); +extern "C" __declspec(dllimport) unsigned long GetModuleFileNameW(void* module, wchar_t* path, + unsigned long size); +#endif + #if !defined(HAVE_VULKAN_LOADER) int main() { std::println("compat.vulkan: skipped (no windows build — static loader unsupported upstream)"); @@ -36,6 +44,44 @@ int main() { return 2; } +#if defined(_WIN32) + // WHERE THE LOADER CAME FROM is the point of compat.vulkan 1.4.357.3 on + // Windows: the package now ships vulkan-1.dll and mcpp places it beside the + // executable, so a machine with no GPU driver can still start this program. + // The loader has just answered, so the DLL is mapped; its directory must be + // this executable's. That holds on a machine WITH a driver too (the + // application directory precedes System32), which is what makes this a real + // check: with an older compat.vulkan the loader came from System32 there, + // and on a driverless machine the process never reached main. + { + std::vector dll(32768), exe(32768); + void* module = GetModuleHandleW(L"vulkan-1.dll"); + if (module == nullptr) { + std::println("vulkan-1.dll answered but is not mapped in this process"); + return 7; + } + const auto dllLen = GetModuleFileNameW(module, dll.data(), 32768); + const auto exeLen = GetModuleFileNameW(nullptr, exe.data(), 32768); + auto dirOf = [](std::wstring_view p) { + std::wstring d(p.substr(0, p.find_last_of(L"\\/"))); + for (auto& c : d) c = static_cast(std::towlower(c)); + return d; + }; + auto narrow = [](std::wstring_view p) { + std::string out; + for (wchar_t c : p) out.push_back(c < 128 ? static_cast(c) : '?'); + return out; + }; + const std::wstring_view dllPath(dll.data(), dllLen), exePath(exe.data(), exeLen); + if (dirOf(dllPath) != dirOf(exePath)) { + std::println("vulkan-1.dll was loaded from {}, not beside {}", + narrow(dllPath), narrow(exePath)); + return 8; + } + std::println("compat.vulkan: loader deployed beside the executable ({})", narrow(dllPath)); + } +#endif + std::uint32_t extensionCount = 0; if (vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, nullptr) != VK_SUCCESS) { std::println("vkEnumerateInstanceExtensionProperties failed");