Skip to content

Commit 8635f53

Browse files
committed
examples/10-graphics: 依赖不能被 layer 条件化,反向腿改成比较像素
这个示例最初把 Vulkan loader、运行时适配器与软件设备三条都门控在 `cfg(accelerator = "vulkan")` 上。**这条做不到,而且它失败的方式是安静的一半**: `accelerator` 是从依赖图里解出来的,所以一个由它选出的依赖会决定它自己正在问的那个 答案 —— mcpp 忽略这个谓词并给出警告,而同一个谓词下的 `[build]` 源照常生效。于是包 被丢掉、包含它的源被留下: src/vulkan/render.cpp:20:10: fatal error: vulkan/vulkan.h: No such file or directory 谓词改成按平台写(只有 Linux 有的两项用 `cfg(linux)`),包无条件,accelerator 只选 `[build]` 源 —— 既有的 examples/09-heterogeneous/vulkan 一直是这个形状。示例 README 把这条写成正文,因为它是使用者会撞上的规则。 反向腿加强:实测两条腿的中心像素逐字节相同(`(124, 70, 62, 255)`),所以 CI 从 「CPU 腿跑起来了」改成「两条腿报出同一个像素、不同的设备名」—— 这才是这个示例声称 的那个契约。 本地实测:lavapipe 腿 `device: llvmpipe (LLVM 22.1.8, 256 bits)`,CPU 腿 `device: cpu rasteriser`,两者 `centre pixel: (124, 70, 62, 255)`。
1 parent c319c1b commit 8635f53

4 files changed

Lines changed: 61 additions & 15 deletions

File tree

.agents/docs/2026-09-07-heterogeneous-cross-platform-ecosystem.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,23 @@ install/config 形状,而那套形状是按 Linux 写的:
329329
Metal 的编译器只在 Xcode 内、不可再分发,所以它不是一个打包问题;ROCm 的运行时可
330330
再分发,但它需要的是一个 `rules-hip` 的 AMD 平台实现,而不是一个包。
331331

332+
### 10.4b 第四处「读出来的写法是错的」:依赖不能被 layer 条件化
333+
334+
图形示例最初把 Vulkan loader、运行时适配器与软件设备三条都门控在
335+
`cfg(accelerator = "vulkan")` 上,理由是「`--no-accel` 一个字节都不装」。**这条做不
336+
到,而且它失败的方式是安静的一半**:`accelerator`**从依赖图里解出来的**,所以一个
337+
由它选出的依赖会决定它自己正在问的那个答案 —— mcpp 因此忽略这个谓词并给出警告,而
338+
**同一个谓词下的 `[build]` 源照常生效**。结果是包被丢掉、包含它的源被留下:
339+
340+
src/vulkan/render.cpp:20:10: fatal error: vulkan/vulkan.h: No such file or directory
341+
342+
判据是构建本身。谓词现在按平台写(`cfg(linux)` 用于只有 Linux 有的两项),包是无条件
343+
的,accelerator 只选 `[build]` 源 —— 也就是既有的 `examples/09-heterogeneous/vulkan`
344+
一直在用的形状。示例 README 把这一条写成了正文,因为它是一条使用者会撞上的规则。
345+
346+
顺带一条读数:两条腿的中心像素**逐字节相同**(`(124, 70, 62, 255)`),所以 CI 的反向
347+
腿从「CPU 腿跑起来了」加强成「两条腿报出同一个像素、不同的设备名」。
348+
332349
### 10.5 一条留下的不一致,以及它什么时候消失
333350

334351
`mcpp:plugins` 0.2.5 里 `xim:shaderc` 在 macOS 与 Windows 上是**精确版本**,而

.github/workflows/ci-linux.yml

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -312,11 +312,23 @@ jobs:
312312
|| { echo "the run did not reach the lavapipe device"; exit 1; }
313313
echo "$out" | grep -qE 'centre pixel: \([0-9]+, [0-9]+, [0-9]+, 255\)' \
314314
|| { echo "no centre pixel was reported"; exit 1; }
315-
# The reverse leg: the same assertions hold with no accelerator, which
316-
# is what makes the pixel test a contract rather than a device quirk.
315+
# THE REVERSE LEG, AND IT COMPARES THE PIXELS RATHER THAN JUST
316+
# RUNNING. The claim this example makes is that the image is a
317+
# contract two independent rasterisers satisfy, not a property of one
318+
# device -- so the criterion is that the two legs report the SAME
319+
# centre pixel while reporting different devices. Measured: both give
320+
# (124, 70, 62, 255), byte for byte.
321+
gpu_px=$(echo "$out" | grep -m1 '^centre pixel:')
317322
"$MCPP" build --no-accel
318323
"$MCPP" run --no-accel | tee cpu.log
319324
grep -q 'cpu rasteriser' cpu.log || { echo "the CPU leg did not run"; exit 1; }
325+
cpu_px=$(grep -m1 '^centre pixel:' cpu.log)
326+
[ -n "$gpu_px" ] && [ "$gpu_px" = "$cpu_px" ] || {
327+
echo "the two legs disagree about the image:"
328+
echo " device: $gpu_px"
329+
echo " cpu: $cpu_px"
330+
exit 1; }
331+
echo "ok: both legs agree on $cpu_px"
320332
321333
- name: "Vulkan example: build the device half and run it on lavapipe"
322334
run: |

examples/10-graphics/README.md

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,15 @@ shaders to the build program rather than to the C++ compiler; and the generated
7070
headers land on the include path, so `src/vulkan/render.cpp` writes
7171
`#include "triangle_vert.h"`.
7272

73-
`mcpp build --no-accel` installs nothing at all — the Vulkan loader and the
74-
software device are both gated on the accelerator.
73+
**A dependency cannot be conditioned on the accelerator, and this project is
74+
where that shows.** `accelerator` is resolved from the dependency graph, so a
75+
dependency chosen by it would decide the answer it is asking for. mcpp says so
76+
and ignores the predicate. An earlier revision of this manifest gated the Vulkan
77+
loader on `cfg(accelerator = "vulkan")` and the build failed on
78+
`vulkan/vulkan.h: No such file or directory` — the header's package had been
79+
dropped while the source that includes it, selected by the same predicate, was
80+
kept. Packages are therefore unconditional or conditioned on the platform;
81+
`[build]` sources are what the accelerator selects.
7582

7683
**One shader per stem.** The generated name is the shader's stem and its stage,
7784
so `ui/text.vert` and `world/text.vert` would both produce `text_vert.h`

examples/10-graphics/offscreen/mcpp.toml

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,23 +15,33 @@ import_std = true
1515
[build-dependencies.mcpp]
1616
plugins = { version = "0.2.5", features = ["rules-spirv"], host-module = true }
1717

18-
# The Khronos loader, and on Linux the adapter that makes the host's own ICDs
19-
# reachable from a binary running under mcpp's private loader. The adapter is a
20-
# Linux concern by construction: macOS resolves through dyld and Windows through
21-
# the PE loader, and neither has a private loader to work around.
22-
# GATED ON THE ACCELERATOR, so `mcpp build --no-accel` installs nothing at all.
23-
# The CPU leg includes no Vulkan header and links no loader, and a build that
24-
# pays for one anyway is the shape `cfg(accelerator = ...)` exists to remove.
25-
[target.'cfg(accelerator = "vulkan")'.dependencies.compat]
18+
# The Khronos loader. Unconditional, and the reason is a rule of the engine
19+
# rather than a preference: A DEPENDENCY CANNOT BE CONDITIONED ON A LAYER.
20+
# `accelerator` is resolved FROM the dependency graph, so a dependency chosen by
21+
# it would decide the answer it is asking. mcpp says so and ignores the
22+
# predicate -- measured here, where an earlier revision of this file gated these
23+
# three entries on `cfg(accelerator = "vulkan")` and the build failed on
24+
# `vulkan/vulkan.h: No such file or directory`, because the header's package had
25+
# been silently dropped while the source that includes it was kept.
26+
#
27+
# `[build]` sources under the same predicate DO apply, which is what makes the
28+
# seam work: the CPU leg below is selected by the accelerator, and only the
29+
# packages are unconditional.
30+
[dependencies.compat]
2631
vulkan = "1.4.357.0"
2732

28-
[target.'cfg(all(accelerator = "vulkan", linux))'.dependencies.compat]
33+
# The adapter that makes the host's own ICDs reachable from a binary running
34+
# under mcpp's private loader. An OS predicate, which IS allowed, and a Linux
35+
# concern by construction: macOS resolves through dyld and Windows through the
36+
# PE loader, and neither has a private loader to work around.
37+
[target.'cfg(linux)'.dependencies.compat]
2938
vulkan-runtime = "2026.09.07"
3039

3140
# A Vulkan device that needs no GPU, so this example runs on a machine that has
3241
# none. It is a DEVICE and therefore a payload; the drivers a real GPU needs are
33-
# the host's, and `compat.vulkan` models those as a capability.
34-
[target.'cfg(all(accelerator = "vulkan", linux))'.xlings.workspace]
42+
# the host's, and `compat.vulkan` models those as a capability. Published for
43+
# Linux alone, so the predicate names the platform rather than the accelerator.
44+
[target.'cfg(linux)'.xlings.workspace]
3545
"xim:mesa-lavapipe" = "26.2.1"
3646

3747
[build]

0 commit comments

Comments
 (0)