Skip to content

docs(examples): 09 —— 系统级图形栈,用推荐方式做完整条链 (#527) - #532

Closed
Sunrisepeak wants to merge 6 commits into
mainfrom
example/graphics-stack
Closed

docs(examples): 09 —— 系统级图形栈,用推荐方式做完整条链 (#527)#532
Sunrisepeak wants to merge 6 commits into
mainfrom
example/graphics-stack

Conversation

@Sunrisepeak

Copy link
Copy Markdown
Member

examples/09-graphics-stack —— 系统级图形栈按 mcpp 推荐方式做完整一条链。

为什么是示例而不是特性

#527 §1 的问法是「宿主库直接动态链接能不能原样用」,复现用的是:

[build]
cxxflags = ["-I/usr/include"]
ldflags  = ["-L/usr/lib", "-lgbm"]

这个方向是错的。mcpp 运行时不依赖 Host 是设计决策(#527 A3:保证可验证/可复现 +
跨发行版),产物拿到的是 mcpp 自己算出搜索路径的私有 PT_INTERP。所以
-L/usr/lib -lgbm 不是缺失的支持,而是错误用法,mcpp 在构建期就明说了。
toolchain = "system" / sysroot = "system" 这两个提案按 A1/A3 暂不做。

该回答的是功能需求本身能不能做到 —— §1 点名的合成器、Mesa 面向的扩展、GBM 显存
管理。,这个 PR 就是那个答案。

内容

[target.'cfg(linux)'.dependencies.compat]
libgbm  = "2026.08.29"
libdrm  = "2026.08.30"
egl     = "2026.08.30"
wayland = "2026.08.30"

这是全部配置。src/main.cpp 用上游原样的头和 API(<gbm.h><xf86drm.h>
<EGL/egl.h><wayland-client.h>),没有任何 mcpp 特有的东西 —— 别处照着这些
库写的代码搬过来就能编。

它做的是真事,不是「符号能链上」:打开 /dev/dri/renderD128,由该 fd 建出真的
gbm_device,交给 eglGetPlatformDisplay(EGL_PLATFORM_GBM_KHR, ...) 并初始化
EGL。

  GBM_BACKENDS_PATH = /home/speak/.mcpp/registry/subos/default/usr/lib/gbm
  wl_display_create        0xca11be0
-- DRM node -> GBM device -> EGL display --
  /dev/dri/renderD128
  drm driver               nvidia-drm
  eglInitialize            EGL 1.5, vendor Mesa Project
  /dev/dri/card0
  drm driver               simpledrm
  eglInitialize            EGL 1.5, vendor Mesa Project

对照 §1.2 的 gbm_create_device(-1) 返回 0(即 NULL)—— 那个用例即使在宿主上
跑通也没证明栈可用。

零 Host 是核过的

用产物自己的私有加载器解析完整闭包,11 条全部落在 registry 内,没有一条在
/usr/lib/lib64:

compat-x-{egl,libdrm,libgbm,wayland}/...   5 条
xim-x-{expat,gcc,glibc,libffi,libglvnd}/... 6 条

其中 libexpatlibffilibGLdispatch传递依赖,mcpp.toml 里谁都没写
—— 而 §1.4 断言的正是这层级联在私有加载器里解不开(「libgbm.so.1 依赖的
libdrm.so.2 ... 程序仍会启动失败」)。声明四个依赖把 11 条都解掉了。

如实记的一处缺口

README 里写明:Vulkan 不在本示例内,形态也不同 —— compat.vulkan-runtime 仍从
/usr/lib/*/lib64 收割宿主 ICD,因为 Vulkan 驱动属于 GPU 厂商,生态里还没有
可绑的 payload。GBM/KMS/EGL/Wayland 这条没有这个边。

依赖

四个包已在 mcpp-index(mcpplibs/mcpp-index@21d8dd0e),xim:mesa
GBM_BACKENDS_PATH 发现层已在 xim-pkgindex(#713, bb97b614)。本 PR 只有
examples/docs/,不动构建代码。

Refs #527

speak-agent and others added 5 commits August 30, 2026 03:52
#527 §1 的问法是「宿主库直接动态链接能不能原样用」,拿 `-L/usr/lib -lgbm`
做复现。这个方向是错的:mcpp 运行时不依赖 Host 是设计决策(#527 A3),产物
拿到的是 mcpp 自己算出搜索路径的私有 PT_INTERP,所以 `-L/usr/lib` 不是缺失
的支持,而是错误用法,mcpp 在构建期就会说出来。

该回答的是功能需求本身能不能做到。这个示例就是答案:合成器 / Mesa 面向的
扩展 / GBM 显存管理所需要的那条链,按推荐方式声明依赖跑通。

    [target.'cfg(linux)'.dependencies.compat]
    libgbm  = "2026.08.29"
    libdrm  = "2026.08.30"
    egl     = "2026.08.30"
    wayland = "2026.08.30"

这是全部配置。src/main.cpp 用的是上游原样的头和 API,没有任何 mcpp 特有的
东西 —— 别处照着这些库写的代码搬过来就能编。

它做的是真事而不是「符号能链上」:打开 /dev/dri/renderD128,由该 fd 建出真
的 gbm_device,交给 eglGetPlatformDisplay(EGL_PLATFORM_GBM_KHR, ...) 并初始
化 EGL。实测两个节点(nvidia-drm / simpledrm)都到 EGL 1.5, vendor Mesa
Project。对照 §1.2 里的 gbm_create_device(-1) 返回 NULL —— 那个用例即使在
宿主上跑通也没证明栈可用。

零 Host 是核过的,不是断言的:用产物自己的私有加载器解析完整闭包,11 条全部
落在 registry 内,没有一条在 /usr/lib 或 /lib64。其中 libexpat、libffi、
libGLdispatch 是传递依赖,mcpp.toml 里谁都没写 —— 而 §1.4 断言的正是这层
级联在私有加载器里解不开。声明四个依赖把 11 条都解掉了。

README 另外如实记了一处缺口:Vulkan 不在本示例内,形态也不同 ——
compat.vulkan-runtime 仍从 /usr/lib/*、/lib64 收割宿主 ICD,因为 Vulkan 驱动
属于 GPU 厂商,生态里还没有可绑的 payload。GBM/KMS/EGL/Wayland 这条没有这
个边。
mcpplibs/mcpp-index#289 replaced the date-stamped versions with the upstream
projects' own release numbers, and made compat.libdrm a source build. The
example follows:

    libdrm  2026.08.30 -> 2.4.134   (now built from source)
    libgbm  2026.08.29 -> 25.0.7    (Mesa's version)
    egl     2026.08.30 -> 1.7.0     (libglvnd's version)

The README's 'The packages' section said none of them vendors a source tree,
which is no longer true, and the closure listing is re-measured against the
published index. The interesting line is the first one: libdrm.so.2 resolves to
the project's OWN build output rather than the payload copy Mesa was linked
against — a soname already in the link map is reused, so the consumer's copy
wins and Mesa's GBM allocates through it. That is why a library the payload
also carries can still be built from source.
mcpplibs/mcpp-index#290 replaced compat.wayland with freedesktop.wayland and
freedesktop.wayland-server, built from source out of mcpplibs/wayland.

They are two packages because they are two SONAMEs and Mesa's libEGL_mesa has
DT_NEEDED on both, and because mcpp links every library target in a package
against all of that package's sources — one package cannot emit two libraries
with disjoint contents. So the -lwayland-server escape hatch this example used
to demonstrate is gone: a compositor asks for the server package by name.

The closure is re-measured. Four of the twelve entries are now this project's
own build output — libdrm, libffi and both wayland libraries — and the first of
those is the interesting one: Mesa's libgbm has an absolute RUNPATH into the
payload's libdrm and still binds to ours, because a soname already in the link
map is reused.
compat.egl bound xim:libglvnd, and its own comment recorded why that was wrong:
libglvnd IS a separable project, so by the criterion it should have been a
source build; it stayed a binding for effort alone. It is now freedesktop.egl,
out of mcpplibs/libglvnd, and GBM is the only binding left in this example --
which is the honest picture, since GBM is the one that genuinely fails the test.

That package also carries libGLdispatch.so.0, as a sibling workspace member
reached by a path dependency rather than a second index entry, because being the
one dispatch point in a process is what GLVND is for.

The program now prints __EGL_VENDOR_LIBRARY_DIRS beside GBM_BACKENDS_PATH. Both
loaders in this stack dlopen something the environment has to point them at, and
the EGL package compiles in an EMPTY default rather than upstream's: a wrong
compiled-in path is worse than none, because it would make a missing declaration
load the HOST's driver into a sandboxed process, silently and successfully.

Measured end to end, with every library attributed through the binary's own
loader -- libEGL.so.1, libGLdispatch.so.0, libdrm.so.2 and both libwayland
libraries come from this project's build output, libgbm.so.1 from compat-x-libgbm
(the binding), and nothing from a host path. eglInitialize reaches
"EGL 1.5, vendor Mesa Project" through the source-built dispatch.
import khronos.egl / import freedesktop.wayland.{client,server}. A module name
is global and permanent in a way a package name is not, so it names the
INTERFACE's owner: freedesktop owns the wayland protocol, Khronos owns EGL and
libglvnd merely implements it. That is why freedesktop.egl the PACKAGE exports
khronos.egl the MODULE, and the mismatch is deliberate.

The closure listing was re-measured rather than edited: libEGL.so.1 and
libGLdispatch.so.0 now come from this build too, where before they came from
compat-x-egl and the xim:libglvnd payload. Also records that the same program
was built and run from scratch inside `xlings subos use … --sandbox --gpu`,
where /usr is still the host's and its libEGL/libgbm/libdrm/libwayland are all
present and reachable -- and lost anyway. "Nothing from the host" is a claim
about what WINS, not about what exists, and only the second version of that
claim matches a user's machine.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants