Repository navigation
tmp(ci): probe Vulkan distribution (windows pack, macOS ICD discovery) -- do NOT merge #2
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
| # TEMPORARY PROBE -- do NOT merge. Answers two questions the Vulkan | |
| # cross-platform design left unmeasured, against the compat.vulkan 1.4.357.3 | |
| # branch (mcpp-index #395): | |
| # | |
| # windows Does `mcpp pack` carry the deployed vulkan-1.dll, and does the | |
| # packed program start from a clean directory on an image that has | |
| # NO system loader? | |
| # macos Does the statically linked loader search <exe dir>/vulkan/icd.d | |
| # (CFBundle resources of an unbundled executable), and does MoltenVK | |
| # enumerate a device on the runner at all? | |
| name: tmp-vulkan-dist-probe | |
| on: | |
| pull_request: | |
| paths: [".github/workflows/tmp-vulkan-dist-probe.yml"] | |
| permissions: | |
| contents: read | |
| env: | |
| MCPP_VERSION: "2026.9.11.2" | |
| MCPP_INDEX_MIRROR: GLOBAL | |
| PROBE_REF: feat/vulkan-windows-ships-loader | |
| jobs: | |
| windows-pack: | |
| runs-on: windows-2022 | |
| timeout-minutes: 60 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: { ref: "${{ env.PROBE_REF }}" } | |
| - name: The image must have no system Vulkan loader | |
| shell: pwsh | |
| run: | | |
| foreach ($p in @("$env:SystemRoot\System32\vulkan-1.dll", "$env:SystemRoot\SysWOW64\vulkan-1.dll")) { | |
| if (Test-Path $p) { throw "found $p -- this image cannot prove anything" } | |
| } | |
| "no system vulkan-1.dll: confirmed" | |
| - name: Download mcpp | |
| shell: bash | |
| run: | | |
| a="mcpp-${MCPP_VERSION}-windows-x86_64.zip" | |
| curl -L -fsS -o "$a" "https://github.com/mcpp-community/mcpp/releases/download/v${MCPP_VERSION}/$a" | |
| powershell -NoProfile -Command "Expand-Archive -Force -Path '$a' -DestinationPath '.'" | |
| root="$PWD/mcpp-${MCPP_VERSION}-windows-x86_64" | |
| mkdir -p "$HOME/.mcpp/registry" && cp -a "$root/registry/." "$HOME/.mcpp/registry/" | |
| echo "MCPP_HOME=$HOME/.mcpp" >> "$GITHUB_ENV" | |
| echo "MCPP=$(cygpath -m "$root/bin/mcpp.exe")" >> "$GITHUB_ENV" | |
| echo "MCPP_VENDORED_XLINGS=$(cygpath -m "$root/registry/bin/xlings.exe")" >> "$GITHUB_ENV" | |
| echo "INDEX=$(cygpath -m "$PWD")" >> "$GITHUB_ENV" | |
| - name: Write the probe program | |
| shell: bash | |
| run: | | |
| P="$RUNNER_TEMP/vkprobe"; mkdir -p "$P/src" | |
| echo "PROBE_DIR=$P" >> "$GITHUB_ENV" | |
| cat > "$P/mcpp.toml" <<EOF | |
| [package] | |
| name = "vkprobe" | |
| version = "0.1.0" | |
| [indices] | |
| compat = { path = "$INDEX" } | |
| [dependencies.compat] | |
| vulkan = "1.4.357.3" | |
| EOF | |
| cat > "$P/src/main.cpp" <<'EOF' | |
| #include <vulkan/vulkan.h> | |
| #include <cstdio> | |
| #include <cstring> | |
| #include <vector> | |
| #if defined(_WIN32) | |
| extern "C" __declspec(dllimport) void* GetModuleHandleA(const char*); | |
| extern "C" __declspec(dllimport) unsigned long GetModuleFileNameA(void*, char*, unsigned long); | |
| #endif | |
| int main() { | |
| uint32_t v = 0; | |
| if (vkEnumerateInstanceVersion(&v) != VK_SUCCESS) { std::puts("PROBE: vkEnumerateInstanceVersion failed"); return 1; } | |
| std::printf("PROBE: loader api %u.%u.%u\n", VK_VERSION_MAJOR(v), VK_VERSION_MINOR(v), VK_VERSION_PATCH(v)); | |
| #if defined(_WIN32) | |
| char dll[4096] = {0}, exe[4096] = {0}; | |
| GetModuleFileNameA(GetModuleHandleA("vulkan-1.dll"), dll, sizeof dll); | |
| GetModuleFileNameA(nullptr, exe, sizeof exe); | |
| std::printf("PROBE: vulkan-1.dll = %s\nPROBE: exe = %s\n", dll, exe); | |
| #endif | |
| uint32_t n = 0; | |
| vkEnumerateInstanceExtensionProperties(nullptr, &n, nullptr); | |
| std::vector<VkExtensionProperties> ex(n); | |
| vkEnumerateInstanceExtensionProperties(nullptr, &n, ex.data()); | |
| bool port = false; | |
| for (auto& x : ex) if (!std::strcmp(x.extensionName, "VK_KHR_portability_enumeration")) port = true; | |
| const char* names[] = { "VK_KHR_portability_enumeration" }; | |
| VkApplicationInfo app{}; app.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; app.apiVersion = VK_API_VERSION_1_1; | |
| VkInstanceCreateInfo ci{}; ci.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; ci.pApplicationInfo = &app; | |
| if (port) { ci.enabledExtensionCount = 1; ci.ppEnabledExtensionNames = names; ci.flags = VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR; } | |
| VkInstance inst = VK_NULL_HANDLE; | |
| VkResult r = vkCreateInstance(&ci, nullptr, &inst); | |
| std::printf("PROBE: portability_enumeration=%d vkCreateInstance=%d\n", port ? 1 : 0, (int)r); | |
| if (r != VK_SUCCESS) { std::puts("PROBE: devices=0 (no instance)"); return 0; } | |
| uint32_t dc = 0; vkEnumeratePhysicalDevices(inst, &dc, nullptr); | |
| std::vector<VkPhysicalDevice> ds(dc); vkEnumeratePhysicalDevices(inst, &dc, ds.data()); | |
| std::printf("PROBE: devices=%u\n", dc); | |
| for (auto d : ds) { VkPhysicalDeviceProperties pr; vkGetPhysicalDeviceProperties(d, &pr); std::printf("PROBE: device %s\n", pr.deviceName); } | |
| vkDestroyInstance(inst, nullptr); | |
| return 0; | |
| } | |
| EOF | |
| - name: Build and run in place | |
| shell: bash | |
| run: | | |
| cd "$PROBE_DIR" | |
| "$MCPP" build 2>&1 | tail -15 | |
| exe=$(find target -type f -name vkprobe.exe | head -1); echo "built: $exe" | |
| ls -la "$(dirname "$exe")" | |
| "$exe" | |
| - name: Pack, extract into a clean directory, run there | |
| shell: bash | |
| run: | | |
| cd "$PROBE_DIR" | |
| "$MCPP" pack 2>&1 | tail -15 | |
| zip=$(find target/dist -maxdepth 1 -name '*.zip' | head -1); echo "archive: $zip" | |
| [ -n "$zip" ] || { echo "PROBE-FAIL: no zip produced"; ls -la target/dist 2>/dev/null; exit 1; } | |
| unzip -l "$zip" | |
| unzip -l "$zip" | grep -qi 'vulkan-1.dll' && echo "PROBE: archive carries vulkan-1.dll" || { echo "PROBE-FAIL: archive has no vulkan-1.dll"; exit 1; } | |
| clean="$RUNNER_TEMP/clean-run"; rm -rf "$clean"; mkdir -p "$clean" | |
| powershell -NoProfile -Command "Expand-Archive -Force -Path '$(cygpath -w "$zip")' -DestinationPath '$(cygpath -w "$clean")'" | |
| find "$clean" -type f | head -20 | |
| exe=$(find "$clean" -type f -name vkprobe.exe | head -1) | |
| # A PATH with nothing of ours on it, so only the archive can supply the loader. | |
| env -i SYSTEMROOT="$SYSTEMROOT" PATH="/c/Windows/System32:/c/Windows" "$exe" | |
| macos-bundle: | |
| runs-on: macos-15 | |
| timeout-minutes: 60 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: { ref: "${{ env.PROBE_REF }}" } | |
| - name: Where CoreFoundation puts an unbundled executable's resources | |
| shell: bash | |
| run: | | |
| d="$RUNNER_TEMP/cfprobe/bin"; mkdir -p "$d" | |
| cat > "$RUNNER_TEMP/cfprobe/b.c" <<'EOF' | |
| #include <CoreFoundation/CoreFoundation.h> | |
| #include <stdio.h> | |
| static void show(const char *label, CFURLRef u) { | |
| char p[4096]; | |
| if (u && CFURLGetFileSystemRepresentation(u, true, (UInt8 *)p, sizeof p)) printf("PROBE: %s = %s\n", label, p); | |
| else printf("PROBE: %s = (none)\n", label); | |
| } | |
| int main(void) { | |
| CFBundleRef b = CFBundleGetMainBundle(); | |
| printf("PROBE: main bundle %s\n", b ? "present" : "absent"); | |
| if (!b) return 1; | |
| show("bundle url", CFBundleCopyBundleURL(b)); | |
| show("resources dir", CFBundleCopyResourcesDirectoryURL(b)); | |
| show("executable", CFBundleCopyExecutableURL(b)); | |
| return 0; | |
| } | |
| EOF | |
| clang "$RUNNER_TEMP/cfprobe/b.c" -framework CoreFoundation -o "$d/cfprobe" | |
| "$d/cfprobe" | |
| - name: Download mcpp | |
| shell: bash | |
| run: | | |
| a="mcpp-${MCPP_VERSION}-macosx-arm64.tar.gz" | |
| curl -L -fsS -o "$a" "https://github.com/mcpp-community/mcpp/releases/download/v${MCPP_VERSION}/$a" | |
| tar -xzf "$a" | |
| root="$PWD/mcpp-${MCPP_VERSION}-macosx-arm64" | |
| mkdir -p "$HOME/.mcpp/registry" && cp -a "$root/registry/." "$HOME/.mcpp/registry/" | |
| echo "MCPP_HOME=$HOME/.mcpp" >> "$GITHUB_ENV" | |
| echo "MCPP=$root/bin/mcpp" >> "$GITHUB_ENV" | |
| echo "MCPP_VENDORED_XLINGS=$root/registry/bin/xlings" >> "$GITHUB_ENV" | |
| echo "INDEX=$PWD" >> "$GITHUB_ENV" | |
| - name: Build the probe (statically linked loader) | |
| shell: bash | |
| run: | | |
| P="$RUNNER_TEMP/vkprobe"; mkdir -p "$P/src" | |
| echo "PROBE_DIR=$P" >> "$GITHUB_ENV" | |
| printf '[package]\nname = "vkprobe"\nversion = "0.1.0"\n\n[indices]\ncompat = { path = "%s" }\n\n[dependencies.compat]\nvulkan = "1.4.357.3"\n' "$INDEX" > "$P/mcpp.toml" | |
| cat > "$P/src/main.cpp" <<'EOF' | |
| #include <vulkan/vulkan.h> | |
| #include <cstdio> | |
| #include <cstring> | |
| #include <vector> | |
| int main() { | |
| uint32_t v = 0; | |
| if (vkEnumerateInstanceVersion(&v) != VK_SUCCESS) { std::puts("PROBE: vkEnumerateInstanceVersion failed"); return 1; } | |
| std::printf("PROBE: loader api %u.%u.%u\n", VK_VERSION_MAJOR(v), VK_VERSION_MINOR(v), VK_VERSION_PATCH(v)); | |
| uint32_t n = 0; | |
| vkEnumerateInstanceExtensionProperties(nullptr, &n, nullptr); | |
| std::vector<VkExtensionProperties> ex(n); | |
| vkEnumerateInstanceExtensionProperties(nullptr, &n, ex.data()); | |
| bool port = false; | |
| for (auto& x : ex) if (!std::strcmp(x.extensionName, "VK_KHR_portability_enumeration")) port = true; | |
| const char* names[] = { "VK_KHR_portability_enumeration" }; | |
| VkApplicationInfo app{}; app.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; app.apiVersion = VK_API_VERSION_1_1; | |
| VkInstanceCreateInfo ci{}; ci.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; ci.pApplicationInfo = &app; | |
| if (port) { ci.enabledExtensionCount = 1; ci.ppEnabledExtensionNames = names; ci.flags = VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR; } | |
| VkInstance inst = VK_NULL_HANDLE; | |
| VkResult r = vkCreateInstance(&ci, nullptr, &inst); | |
| std::printf("PROBE: portability_enumeration=%d vkCreateInstance=%d\n", port ? 1 : 0, (int)r); | |
| if (r != VK_SUCCESS) { std::puts("PROBE: devices=0 (no instance)"); return 0; } | |
| uint32_t dc = 0; vkEnumeratePhysicalDevices(inst, &dc, nullptr); | |
| std::vector<VkPhysicalDevice> ds(dc); vkEnumeratePhysicalDevices(inst, &dc, ds.data()); | |
| std::printf("PROBE: devices=%u\n", dc); | |
| for (auto d : ds) { VkPhysicalDeviceProperties pr; vkGetPhysicalDeviceProperties(d, &pr); std::printf("PROBE: device %s\n", pr.deviceName); } | |
| vkDestroyInstance(inst, nullptr); | |
| return 0; | |
| } | |
| EOF | |
| cd "$P" && "$MCPP" build 2>&1 | tail -15 | |
| exe=$(find target -type f -name vkprobe -perm -u+x | head -1); echo "EXE=$PWD/$exe" >> "$GITHUB_ENV"; echo "built: $exe" | |
| - name: "A: no MoltenVK anywhere" | |
| shell: bash | |
| run: | | |
| set +e | |
| VK_LOADER_DEBUG=error,warn,driver "$EXE" > "$RUNNER_TEMP/a.out" 2> "$RUNNER_TEMP/a.dbg"; echo "EXIT=$?" | |
| cat "$RUNNER_TEMP/a.out" | |
| grep -E "Found ICD|drivers named|Could not|Failed|failed|ERROR" "$RUNNER_TEMP/a.dbg" | sort | uniq -c | head -20 | |
| - name: "B: manifest beside the executable, RELATIVE library_path" | |
| shell: bash | |
| run: | | |
| set +e | |
| curl -L -fsS -o mvk.tgz https://github.com/xlings-res/moltenvk/releases/download/1.4.2/moltenvk-1.4.2-macosx-universal.tar.gz | |
| tar -xzf mvk.tgz | |
| bindir=$(dirname "$EXE") | |
| mkdir -p "$bindir/vulkan/icd.d" | |
| cp moltenvk-1.4.2/lib/libMoltenVK.dylib "$bindir/" | |
| sed 's#"library_path"[^,]*#"library_path" : "../../libMoltenVK.dylib"#' moltenvk-1.4.2/share/vulkan/icd.d/MoltenVK_icd.json > "$bindir/vulkan/icd.d/MoltenVK_icd.json" | |
| VK_LOADER_DEBUG=error,warn,driver "$EXE" > "$RUNNER_TEMP/b.out" 2> "$RUNNER_TEMP/b.dbg"; echo "EXIT=$?" | |
| cat "$RUNNER_TEMP/b.out" | |
| grep -E "Found ICD|drivers named|Could not|Failed|failed|ERROR|dlopen|Loading" "$RUNNER_TEMP/b.dbg" | sort | uniq -c | head -20 | |
| - name: "B2: manifest beside the executable, ABSOLUTE library_path" | |
| shell: bash | |
| run: | | |
| set +e | |
| bindir=$(dirname "$EXE") | |
| sed "s#\"library_path\"[^,]*#\"library_path\" : \"$bindir/libMoltenVK.dylib\"#" moltenvk-1.4.2/share/vulkan/icd.d/MoltenVK_icd.json > "$bindir/vulkan/icd.d/MoltenVK_icd.json" | |
| cat "$bindir/vulkan/icd.d/MoltenVK_icd.json" | |
| VK_LOADER_DEBUG=error,warn,driver "$EXE" > "$RUNNER_TEMP/b2.out" 2> "$RUNNER_TEMP/b2.dbg"; echo "EXIT=$?" | |
| cat "$RUNNER_TEMP/b2.out" | |
| grep -E "Found ICD|drivers named|Could not|Failed|failed|ERROR" "$RUNNER_TEMP/b2.dbg" | sort | uniq -c | head -20 | |
| - name: "B3: whole bin/ copied elsewhere (does the relative layout travel?)" | |
| shell: bash | |
| run: | | |
| set +e | |
| bindir=$(dirname "$EXE") | |
| sed 's#"library_path"[^,]*#"library_path" : "../../libMoltenVK.dylib"#' moltenvk-1.4.2/share/vulkan/icd.d/MoltenVK_icd.json > "$bindir/vulkan/icd.d/MoltenVK_icd.json" | |
| moved="$RUNNER_TEMP/moved-app"; rm -rf "$moved"; cp -R "$bindir" "$moved" | |
| VK_LOADER_DEBUG=error,warn,driver "$moved/vkprobe" > "$RUNNER_TEMP/b3.out" 2> "$RUNNER_TEMP/b3.dbg"; echo "EXIT=$?" | |
| cat "$RUNNER_TEMP/b3.out" | |
| grep -E "Found ICD|drivers named|Could not|Failed|failed|ERROR" "$RUNNER_TEMP/b3.dbg" | sort | uniq -c | head -20 | |
| - name: "C: MoltenVK through VK_DRIVER_FILES (xim:moltenvk's documented route)" | |
| shell: bash | |
| run: | | |
| set +e | |
| abs="$PWD/moltenvk-1.4.2/lib/libMoltenVK.dylib" | |
| sed "s#\"library_path\"[^,]*#\"library_path\" : \"$abs\"#" moltenvk-1.4.2/share/vulkan/icd.d/MoltenVK_icd.json > "$RUNNER_TEMP/mvk_icd.json" | |
| VK_LOADER_DEBUG=error,warn,driver VK_DRIVER_FILES="$RUNNER_TEMP/mvk_icd.json" "$EXE" > "$RUNNER_TEMP/c.out" 2> "$RUNNER_TEMP/c.dbg"; echo "EXIT=$?" | |
| cat "$RUNNER_TEMP/c.out" |