Skip to content

Commit d68ded6

Browse files
committed
09: EGL is source-built now, so four of the five packages are
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.
1 parent c1b6051 commit d68ded6

3 files changed

Lines changed: 78 additions & 31 deletions

File tree

‎examples/09-graphics-stack/README.md‎

Lines changed: 39 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ mcpp run
99

1010
```
1111
== the graphics stack, resolved from the index ==
12-
GBM_BACKENDS_PATH = …/subos/default/usr/lib/gbm
12+
GBM_BACKENDS_PATH = …/subos/default/usr/lib/gbm
13+
__EGL_VENDOR_LIBRARY_DIRS = …/subos/default/share/glvnd/egl_vendor.d
1314
wl_display_create 0x3f798be0
1415
-- DRM node -> GBM device -> EGL display --
1516
/dev/dri/renderD128
@@ -123,10 +124,13 @@ the ecosystem already owns, where building it would mean forking that project.
123124
|---|---|---|
124125
| `compat.libdrm` | source | `drmModeGetResources`, `drmModeAddFB2`, `drmModeSetCrtc` — the KMS side |
125126
| `compat.libgbm` | binds `xim:mesa` | `gbm_create_device`, `gbm_bo_create` — buffers out of a DRM device |
126-
| `compat.egl` | binds `xim:libglvnd` | `eglGetPlatformDisplay(EGL_PLATFORM_GBM_KHR, …)` — rendering onto them |
127+
| `freedesktop.egl` | source | `eglGetPlatformDisplay(EGL_PLATFORM_GBM_KHR, …)` — rendering onto them, and `import egl;` |
127128
| `freedesktop.wayland` | source | `libwayland-client.so.0`, and `import wayland.client;` |
128129
| `freedesktop.wayland-server` | source | `libwayland-server.so.0`, and `import wayland.server;` |
129130

131+
**Four of the five are source builds.** GBM is the only binding, and the
132+
paragraphs below are about why the line falls where it does.
133+
130134
libdrm passes the test — an independent freedesktop project with its own
131135
releases — so it is compiled here, five translation units with no dependencies
132136
at all. GBM fails it: `src/gbm/meson.build` is `link_with: [libloader]`, and
@@ -152,9 +156,20 @@ through it. That only holds because the package builds a *shared* library with
152156
the canonical soname; merged into the consumer as objects there would be two
153157
copies of libdrm's internal state over one set of file descriptors.
154158

155-
`compat.egl` is a binding for a duller reason: libglvnd IS separable, but
156-
`libEGL.so` also needs its Python-generated dispatch stubs, `winsys_dispatch`
157-
and the whole of `libGLdispatch.so`, so it has not been done yet.
159+
EGL used to be the exception that proved the criterion was being applied
160+
loosely. It was a binding for a duller reason than GBM's — libglvnd IS
161+
separable, but `libEGL.so` also needs its generated dispatch stubs,
162+
`winsys_dispatch` and the whole of `libGLdispatch.so`, so it simply had not
163+
been done. "Not done yet" is not a shape, so it is now a source build out of
164+
[mcpplibs/libglvnd](https://github.com/mcpplibs/libglvnd), and the dispatch
165+
tables upstream generates with ~1000 lines of Python are checked into that fork
166+
and diffed by its CI rather than regenerated during your build.
167+
168+
That package also carries `libGLdispatch.so.0`, as a **sibling workspace member
169+
reached by a path dependency** rather than as a second index entry. GLVND
170+
exists to be the one dispatch point in a process; two index entries would let a
171+
project name both and resolve two instances, and — by the same soname-reuse
172+
rule as above — one would be mapped and the other silently discarded.
158173

159174
One honest gap, since "Mesa/Vulkan" usually get named together: Vulkan is not
160175
part of this example and is not in the same state. `compat.vulkan-runtime`
@@ -164,15 +179,25 @@ to bind to yet. The GBM/KMS/EGL/Wayland stack above has no such edge.
164179

165180
## Two things worth knowing
166181

167-
**`GBM_BACKENDS_PATH` is not set by any of these packages.** libgbm is a
168-
loader: `gbm_create_device()` dlopens `<path>/<driver>_gbm.so`, and the path
169-
Mesa compiles in is `/usr/lib/gbm` — correct on a distribution, wrong the
170-
moment the payload lives anywhere else. Setting that variable is Mesa's own
171-
mechanism and the *environment's* job, which is where every relocated stack
172-
puts it (Valve's pressure-vessel, Nix, Conda all do exactly this). Here
173-
`xim:mesa` declares it into the SubOS and mcpp carries SubOS declarations into
174-
the processes it launches, so it is simply already set — which is why the
175-
program prints it rather than computing it.
182+
**Neither `GBM_BACKENDS_PATH` nor `__EGL_VENDOR_LIBRARY_DIRS` is set by any of
183+
these packages**, and both are needed, because two of the five are *loaders*.
184+
`gbm_create_device()` dlopens `<path>/<driver>_gbm.so`; `eglInitialize()`
185+
dlopens whatever a JSON file in the vendor directory names. The paths upstream
186+
compiles in — `/usr/lib/gbm`, `<prefix>/share/glvnd/egl_vendor.d` — are correct
187+
on a distribution and wrong the moment the payload lives anywhere else.
188+
189+
Those two variables are Mesa's and GLVND's own mechanisms, and setting them is
190+
the *environment's* job, which is where every relocated stack puts it (Valve's
191+
pressure-vessel, Nix, Conda all do exactly this). Here `xim:mesa` declares both
192+
into the SubOS and mcpp carries SubOS declarations into the processes it
193+
launches, so they are simply already set — which is why the program prints them
194+
rather than computing them.
195+
196+
`freedesktop.egl` goes one step further and compiles in an **empty** default
197+
rather than upstream's. A wrong compiled-in path is worse than no path: it
198+
would make a missing declaration load the *host's* driver into a sandboxed
199+
process, silently and successfully. Empty makes the same situation say "no
200+
vendor found".
176201

177202
**The wayland client and server are separate packages**, and this example asks
178203
for both because it creates a `wl_display` on the server side. That is not a

‎examples/09-graphics-stack/mcpp.toml‎

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,36 @@ standard = "c++23"
88
# The versions are the upstream projects' own release numbers, and the shapes
99
# differ on one criterion: a library is BUILT FROM SOURCE when upstream ships it
1010
# as a separable unit, and BOUND to the ecosystem's payload when it is an
11-
# internal target of a project the ecosystem already owns. libdrm and wayland
12-
# pass that test; GBM does not — it is a build target inside Mesa, and a loader
13-
# whose backends are Mesa's own.
11+
# internal target of a project the ecosystem already owns.
12+
#
13+
# Exactly ONE of the four is a binding, and it is GBM: it is a build target
14+
# inside Mesa (`src/gbm/meson.build` is `link_with: [libloader]`, which wants
15+
# ~120 TUs of Mesa's internal util library for one function), and it is a
16+
# LOADER whose backends are Mesa's own `dri_gbm.so` — built apart from Mesa it
17+
# would have nothing to load. libdrm, EGL and wayland all pass the test and are
18+
# built from source.
1419
[target.'cfg(linux)'.dependencies.compat]
1520
libdrm = "2.4.134" # source-built; the KMS side: modes, CRTCs, framebuffers
1621
libgbm = "25.0.7" # Mesa's GBM: buffer allocation out of a DRM device
17-
egl = "1.7.0" # libglvnd's EGL dispatch: rendering onto those buffers
1822

19-
# wayland is source-built too, out of mcpplibs/wayland — the client and the
20-
# server are distinct SONAMEs that Mesa's libEGL_mesa needs BOTH of, so they are
21-
# two packages rather than one with an ldflags escape hatch. Each also ships a
22-
# C++23 module wrapper; this example uses the headers, and 09's sibling text
23-
# explains what `import wayland.client;` would change (nothing but the include).
23+
# These three are source builds out of forks that add mcpp support and patch no
24+
# upstream file.
25+
#
26+
# wayland is two packages rather than one with an ldflags escape hatch: the
27+
# client and the server are distinct SONAMEs that Mesa's libEGL_mesa needs BOTH
28+
# of, and mcpp links every library target against all of a package's sources.
29+
#
30+
# egl is libglvnd's vendor-neutral dispatch — the piece that makes GBM useful
31+
# for RENDERING rather than only for allocation. It also carries
32+
# `libGLdispatch.so.0`, as a sibling workspace member reached by a path
33+
# dependency rather than as a second index entry, because being the ONE dispatch
34+
# point in a process is what GLVND is for.
35+
#
36+
# All three ship a C++23 module wrapper too. This example uses the headers, and
37+
# for a reason worth knowing: `EGL_PLATFORM_GBM_KHR` and friends are MACROS, and
38+
# no module can export a macro — so `import egl;` replaces the declarations but
39+
# never the `#include <EGL/eglext.h>` that the constants come from.
2440
[target.'cfg(linux)'.dependencies.freedesktop]
2541
wayland = "1.26.0"
2642
wayland-server = "1.26.0"
43+
egl = "1.7.0"

‎examples/09-graphics-stack/src/main.cpp‎

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
#include <xf86drm.h> // compat.libdrm
1111
#include <xf86drmMode.h>
1212
#include <drm_fourcc.h>
13-
#include <EGL/egl.h> // compat.egl
13+
#include <EGL/egl.h> // freedesktop.egl
1414
#include <EGL/eglext.h>
1515
#include <wayland-client.h> // compat.wayland
1616
#include <wayland-server-core.h>
@@ -43,13 +43,18 @@ int main()
4343
{
4444
std::puts("== the graphics stack, resolved from the index ==");
4545

46-
// Where the GBM backends are found. Nothing in this program and nothing in
47-
// compat.libgbm sets this: `xim:mesa` declares it into the SubOS through
48-
// the graphics discovery layer, and mcpp carries SubOS declarations into
49-
// the processes it launches.
50-
const char *backends = std::getenv("GBM_BACKENDS_PATH");
51-
std::printf(" GBM_BACKENDS_PATH = %s\n",
52-
backends ? backends : "<unset — the ecosystem did not supply it>");
46+
// Where the two LOADERS in this stack find what they dlopen. Nothing in
47+
// this program and none of the packages sets either: `xim:mesa` declares
48+
// both into the SubOS through the graphics discovery layer, and mcpp
49+
// carries SubOS declarations into the processes it launches.
50+
//
51+
// GBM_BACKENDS_PATH -> gbm_create_device() dlopens <path>/<drv>_gbm.so
52+
// __EGL_VENDOR_LIBRARY_DIRS -> eglInitialize() dlopens what a JSON there names
53+
for (const char *name : {"GBM_BACKENDS_PATH", "__EGL_VENDOR_LIBRARY_DIRS"}) {
54+
const char *value = std::getenv(name);
55+
std::printf(" %-25s = %s\n", name,
56+
value ? value : "<unset — the ecosystem did not supply it>");
57+
}
5358

5459
// Wayland: build a server-side display. No socket is bound, so this needs
5560
// no session and no privileges — the cheapest proof the library is live.

0 commit comments

Comments
 (0)