Skip to content

Commit 1a3fa43

Browse files
committed
examples/10-graphics: the offscreen example runs on all three platforms
macOS and Windows built it and did not run it, which is the shape the cross-platform work exists to remove: the half of a lane written for a host is the half that host never exercises. A build asserts that the shader compiler this platform publishes works and that the Vulkan half links. It cannot assert that the loader hands the program a device, and on macOS that is the interesting half. The example now declares a device for those two platforms as well -- `xim:moltenvk` under `cfg(macos)`, `xim:mesa-lavapipe` under `cfg(windows)` -- rather than leaving CI to install one. The omission was invisible while the example was never run, because a program that does not run never asks the loader for a device, and declaring it here is what makes the example complete for anyone who checks it out rather than only for the runner that had an extra command. The two CI steps therefore install nothing. They locate the ICD the build already provisioned and set `VK_DRIVER_FILES`, so a manifest that failed to name the driver fails the step. An `xlings install` in the step would have made it pass either way. macOS and Windows assert different things, and the difference is the point. lavapipe is a software rasteriser producing the same pixels by construction, so the image cannot distinguish it and the device name is what does. MoltenVK is the host's own GPU through Metal, so its name differs by runner; what distinguishes reaching a device there is that a centre pixel was reported at all, which a portability driver the loader declined to show would not produce. This closes the dependency between the two halves of the portability change: the package without the enumeration finds no device, and the enumeration without the package has nothing to find.
1 parent 235449a commit 1a3fa43

4 files changed

Lines changed: 136 additions & 0 deletions

File tree

.github/workflows/ci-macos.yml

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,54 @@ jobs:
361361
done
362362
echo "ok: both shader stages compiled and the Vulkan half linked"
363363
364+
# AND NOW IT RUNS, WHICH IS A DIFFERENT CLAIM FROM THE ONE ABOVE.
365+
#
366+
# A build asserts that the shader compiler this platform publishes works
367+
# and that the Vulkan half links. It cannot assert that the loader hands
368+
# this program a device, and on macOS that is the interesting half:
369+
# MoltenVK is a PORTABILITY driver, which the loader does not give to
370+
# `vkEnumeratePhysicalDevices` unless the instance asked for portability
371+
# enumeration. A program written against native drivers therefore finds no
372+
# device here and reports it as "this machine has no GPU".
373+
#
374+
# This step is what turns that into a red build rather than a plausible
375+
# message. It depends on the portability enumeration in this PR: without
376+
# it the run reaches zero devices even though everything installed
377+
# correctly.
378+
#
379+
# `VK_DRIVER_FILES` rather than an ICD search directory: the package is in
380+
# the xlings store, not in `/usr/local/share/vulkan/icd.d`, and naming the
381+
# file is the one form that does not depend on where the loader looks.
382+
- name: "Graphics: the offscreen example RUNS on MoltenVK"
383+
shell: bash
384+
run: |
385+
set -e
386+
export MCPP_VENDORED_XLINGS="$XLINGS_BIN"
387+
# NOT installed from here. The example declares `xim:moltenvk` under
388+
# `cfg(macos)`, so the build above already provisioned it, and looking
389+
# for the ICD without installing anything is what asserts that the
390+
# declaration works. An `xlings install` here would make this step
391+
# pass whether the manifest named the driver or not.
392+
icd=$(find "${MCPP_HOME:-$HOME/.mcpp}/registry/data/xpkgs/xim-x-moltenvk" \
393+
"$HOME/.xlings/data/xpkgs/xim-x-moltenvk" \
394+
-name 'MoltenVK_icd.json' -print -quit 2>/dev/null || true)
395+
[ -n "$icd" ] || { echo "no MoltenVK ICD in either store"; exit 1; }
396+
echo "ICD: $icd"
397+
cd "$GITHUB_WORKSPACE/examples/10-graphics/offscreen"
398+
out=$(VK_DRIVER_FILES="$icd" "/tmp/mcpp-fresh" run 2>&1) || { echo "$out"; exit 1; }
399+
echo "$out"
400+
# The program asserts the corners and the centre itself and exits
401+
# non-zero on either. What CI adds is that the run reached a DEVICE:
402+
# a portability driver that the loader declined to show would leave
403+
# the program reporting no device, which is the failure this step
404+
# exists for. The name is not compared to a fixed string because it is
405+
# the host's GPU and differs by runner; that it is non-empty and the
406+
# centre pixel is opaque is what distinguishes reaching a device from
407+
# not.
408+
echo "$out" | grep -qE 'centre pixel: \([0-9]+, [0-9]+, [0-9]+, 255\)' \
409+
|| { echo "no centre pixel was reported: the run reached no device"; exit 1; }
410+
echo "ok: MoltenVK enumerated and the image was rendered on it"
411+
364412
# Integration: the mcpp built from THIS PR's source (the self-host binary,
365413
# $MCPP = /tmp/mcpp-fresh) builds & runs a real external C++ project —
366414
# xlings (openxlings/xlings ships its own mcpp.toml).

.github/workflows/ci-windows.yml

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -456,6 +456,45 @@ jobs:
456456
done
457457
echo "ok: both shader stages compiled and the Vulkan half linked"
458458
459+
# AND NOW IT RUNS. `xim:mesa-lavapipe` publishes a Windows build, so this
460+
# host has a software device and the claim can be the same one Linux
461+
# makes: the program reached a device and rendered the image, not merely
462+
# that it compiled.
463+
#
464+
# The device name IS compared here, unlike on macOS. lavapipe is a
465+
# software rasteriser that produces the same pixels by construction, so
466+
# the image cannot distinguish it from any other implementation and the
467+
# name is what does.
468+
#
469+
# `VK_DRIVER_FILES` rather than an ICD search directory: the package is in
470+
# the xlings store rather than in the registry the Windows loader reads,
471+
# and naming the file is the one form that does not depend on that.
472+
- name: "Graphics: the offscreen example RUNS on lavapipe"
473+
shell: bash
474+
run: |
475+
set -e
476+
export MCPP_VENDORED_XLINGS="$XLINGS_BIN"
477+
# NOT installed from here, for the reason the macOS job records: the
478+
# example declares `xim:mesa-lavapipe` under `cfg(windows)`, so
479+
# finding the ICD without installing anything is what asserts the
480+
# declaration works.
481+
icd=$(find "${MCPP_HOME:-$HOME/.mcpp}/registry/data/xpkgs/xim-x-mesa-lavapipe" \
482+
"$HOME/.xlings/data/xpkgs/xim-x-mesa-lavapipe" \
483+
-name '*.json' -path '*icd.d*' -print -quit 2>/dev/null || true)
484+
[ -n "$icd" ] || { echo "no lavapipe ICD in either store"; exit 1; }
485+
echo "ICD: $icd"
486+
cd "$GITHUB_WORKSPACE/examples/10-graphics/offscreen"
487+
# `$MCPP_SELF`, which is what the build step above used. `/tmp/mcpp-fresh.exe`
488+
# is a copy a LATER step makes, so naming it here would look right and
489+
# fail with a missing file.
490+
out=$(VK_DRIVER_FILES="$icd" "$MCPP_SELF" run 2>&1) || { echo "$out"; exit 1; }
491+
echo "$out"
492+
echo "$out" | grep -q 'llvmpipe' \
493+
|| { echo "the run did not reach the lavapipe device"; exit 1; }
494+
echo "$out" | grep -qE 'centre pixel: \([0-9]+, [0-9]+, [0-9]+, 255\)' \
495+
|| { echo "no centre pixel was reported"; exit 1; }
496+
echo "ok: lavapipe enumerated and the image was rendered on it"
497+
459498
- name: "Toolchain: LLVM — build mcpp (self-host)"
460499
shell: bash
461500
run: |

CHANGELOG.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,32 @@ libmalloc 里 abort(#202)。CI 报的正是这条路的第一步:链接停在 `_
8686
`cfg(accelerator = ...)` 下的 `[build]` 源生效而依赖被忽略,于是包被丢掉、包含它的源
8787
被留下。
8888

89+
### 可移植性驱动默认是看不见的,于是三个平台都从「构建」抬到「运行」
90+
91+
macOS 上没有原生 Vulkan,MoltenVK 是 Metal 之上的实现,规范把这种实现叫**可移植性驱动**
92+
loader 默认不把它交给 `vkEnumeratePhysicalDevices`:实例要同时启用
93+
`VK_KHR_portability_enumeration` 并置位 `VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR`,
94+
随后凡是声明 `VK_KHR_portability_subset` 的设备必须在 `vkCreateDevice` 时启用它。一个照着
95+
原生驱动写的程序因此在那台机器上**一个设备都找不到**,并把它报成「这台机器没有 GPU」——
96+
诊断是错的,而它看起来完全合理。
97+
98+
按能力问,不按 `#ifdef __APPLE__`:性质是「我面前这个 loader 在展示可移植性驱动」,而
99+
Linux 上跑翻译层的机器也有它,macOS 上对着原生驱动构建的程序并不需要它。Linux/lavapipe
100+
上读数不变,中心像素仍是 `(124, 70, 62, 255)`,设备名仍是 `llvmpipe`
101+
102+
**示例本身补齐了另外两个平台的设备声明** —— `cfg(macos)``xim:moltenvk`,`cfg(windows)`
103+
`xim:mesa-lavapipe`。这个缺口在示例只构建不运行时是看不见的:不运行的程序从不向
104+
loader 要设备。声明写在示例里而不是 CI 步骤里,是为了让**任何人**检出它都能跑,而不只是
105+
那台多跑了一条命令的 runner。
106+
107+
于是两个新 CI 步骤**什么都不装**:它们找构建已经供给的 ICD 并设 `VK_DRIVER_FILES`,所以
108+
一份没能声明驱动的 manifest 会让步骤变红。步骤里放 `xlings install` 会让它两种情况都通过。
109+
110+
两个平台断言的东西不同,而这个差别正是重点。lavapipe 是软件光栅化器,像素由构造保证相同,
111+
所以图像分不出它,设备名才分得出。MoltenVK 是宿主自己的 GPU 经 Metal,名字随 runner 变,
112+
所以那边区分「够到了设备」的是**报出了中心像素**这件事本身 —— 一个被 loader 拒绝展示的
113+
可移植性驱动不会产生它。
114+
89115
### 文档
90116

91117
`docs/20` 新增「每条 lane 到得了哪些平台」:三件事同时为真才叫一条 lane 在某个平台上

examples/10-graphics/offscreen/mcpp.toml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,29 @@ vulkan-runtime = "2026.09.07"
4444
[target.'cfg(linux)'.xlings.workspace]
4545
"xim:mesa-lavapipe" = "26.2.1"
4646

47+
# THE OTHER TWO PLATFORMS GET A DEVICE TOO, AND FOR THE SAME REASON.
48+
#
49+
# These were absent while the example was built but not run on macOS and
50+
# Windows, which made the omission invisible: a program that never runs never
51+
# asks the loader for a device. Declaring the driver here rather than installing
52+
# it from a CI step is what makes the EXAMPLE complete -- anyone who checks it
53+
# out gets a machine that can run it, not only the runner that had an extra
54+
# command.
55+
#
56+
# macOS has no native Vulkan. MoltenVK is an implementation on top of Metal, and
57+
# the specification calls that a portability driver: the loader does not hand it
58+
# to `vkEnumeratePhysicalDevices` unless the instance asked for portability
59+
# enumeration, which `src/vulkan/render.cpp` now does. The two halves are one
60+
# change -- the package without the enumeration finds no device, and the
61+
# enumeration without the package has nothing to find.
62+
[target.'cfg(macos)'.xlings.workspace]
63+
"xim:moltenvk" = "1.4.2"
64+
65+
# Windows takes the same software rasteriser Linux does; upstream publishes a
66+
# separate build of it, one minor behind the Linux one.
67+
[target.'cfg(windows)'.xlings.workspace]
68+
"xim:mesa-lavapipe" = "26.2.0"
69+
4770
[build]
4871
accel = "vulkan1.2"
4972
sources = [

0 commit comments

Comments
 (0)