# MobileGL on Linux On Linux (glibc), MobileGL builds as `libMobileGL.so`. One library provides: - **EGL** for the surfaceless, Wayland and GBM platforms. On GBM it is offscreen only: window surfaces there are refused. - **GLX** for X11. - **glvnd vendor** entry points for both, so the system's `libEGL.so.1` / `libGL.so.1` (libglvnd) can route applications to it. - In split builds, a GBM backend module (`mobilegl_gbm.so`) and the server executable (`libMobileGLServer.so`). There are two ways to use it: | Setup | When | |---|---| | [Split client](#system-wide-split-client-glvnd-vendor-gbm-backend) talking to a MobileGL server on Android | The main Linux use case: a Linux container on Android (anland) or a Linux PC rendering on a phone's GPU | | [Monolith on a Linux desktop](#monolith-on-a-linux-desktop) | Experimental: running on the desktop's own GLES/Vulkan driver | Build instructions: [building.md](building.md#linux). ## Build outputs | File (in the build directory) | Build | Purpose | |---|---|---| | `libMobileGL.so` | always | The library | | `libEGL.so`, `libEGL.so.1`, `libGL.so`, `libGL.so.1`, `libGLX_mobilegl.so.0` | always | Symlinks to `libMobileGL.so`, for direct loading and for glvnd's GLX vendor naming | | `libMobileGLServer.so` | split | The server executable (despite the name, an executable) | | `MobileGL/MG_Gbm/mobilegl_gbm.so` | split, and only if `gbm.h` and `gbm_backend_abi.h` are installed | GBM backend module | ## Presentation Window surfaces are drawn by the backend and then handed to the window system by the client: | Window system | Zero-copy path (split client with server-allocated shared images) | Fallback | |---|---|---| | Wayland (`wl_egl_window`) | `zwp_linux_dmabuf_v1` buffers | read back into `wl_shm` buffers | | X11 (GLX) | DRI3 + Present | read back + MIT-SHM `PutImage`, or plain `PutImage` | Shared images come from a server that can allocate them; the Android server can. The log names the path chosen: ```text Wayland: window surface 1280x720 presented through linux-dmabuf shared images (...) glx: window 0x... (...) presented through DRI3+Present shared images (...) ``` `MOBILEGL_WAYLAND_DMABUF=0` forces `wl_shm`; `MOBILEGL_GLX_PRESENT=readback|shm|putimage` forces a GLX readback path. ## Topologies on Linux | `MOBILEGL_TRANSPORT` | `MOBILEGL_IPC_CONTROL` | Meaning | |---|---|---| | unset / `monolith` | | Backend in the application's thread | | `inproc` | | Backend on a second thread (split build) | | `spawn` | unset / `fork` | The client starts its own `libMobileGLServer.so`, found next to `libMobileGL.so` or at `MOBILEGL_IPC_SERVER_PATH` | | `spawn` | `unix:/path` or `unix:@name` | Connect to a running server on a Unix socket (shared-memory data plane) | | `spawn` | `tcp://host:port` | Connect to a running server over TCP (stream data plane) | `MOBILEGL_TRANSPORT=unix:...` and `pipe:...` are **not** valid: they log an error and run monolith. Put the endpoint in `MOBILEGL_IPC_CONTROL` instead. Client and server must be built from the same source revision. Mismatched wire layouts are refused; otherwise the client logs a `compatible wire with different build` warning. ## Running a server on Linux A Linux server is offscreen only: it has no display of its own, so clients present their windows themselves (table above). `MOBILEGL_IPC_SURFACE=server` is refused by a Linux server. The server needs `MOBILEGL_IPC_DIAL=no` in its environment, and must **not** inherit `MOBILEGL_TRANSPORT`, `MOBILEGL_IPC_SERVER_PATH`, `MOBILEGL_IPC_RING_MB` or `MOBILEGL_IPC_STAGE_MB` (it exits with code 64 or 65 otherwise). Unix socket, several clients (each session gets its own forked process and backend): ```sh env -u MOBILEGL_TRANSPORT MOBILEGL_IPC_DIAL=no ./libMobileGLServer.so @mobilegl --serve --max-sessions 16 ``` TCP, reachable from other machines (a token of at least 16 bytes is required for any non-loopback address): ```sh env -u MOBILEGL_TRANSPORT MOBILEGL_IPC_DIAL=no MOBILEGL_IPC_TOKEN=<16+ byte secret> ./libMobileGLServer.so tcp://0.0.0.0:40613 --serve ``` Arguments: `` (or `MOBILEGL_IPC_ENDPOINT`), `--serve` (keep accepting sessions; without it the process serves one session and exits), `--max-sessions <1-64>` (Unix `--serve` only, default 1). The client picks the backend for its session; set `MOBILEGL_BACKEND_TYPE` on the server to pin one. The server prints `MG_Remote server: pid= listening on ` when it is ready. ## TCP client On a Linux machine, run any GL program against a remote server (for example a phone running the plugin's render server, see [android-plugin.md](android-plugin.md#tcp-server-for-remote-clients)): ```sh MOBILEGL_TRANSPORT=spawn MOBILEGL_IPC_CONTROL=tcp://192.168.1.20:40613 MOBILEGL_IPC_TOKEN= MOBILEGL_LOG_FILE_PATH=/tmp/mgl.log __EGL_VENDOR_LIBRARY_FILENAMES=/opt/mobilegl/share/glvnd/egl_vendor.d/50_mobilegl.json eglinfo -B ``` (`eglinfo` and `glxinfo` come with your distribution's GL utilities package; any EGL or GLX program works. The vendor file is described below.) The client writes `/tmp/mgl.client.log`; the server's log lines are forwarded to the client and written to `/tmp/mgl.server.log`. A successful connection logs `MG_Remote client: control=tcp data=stream server=192.168.1.20:40613 ... dial=connect`. Every GL object creation, query and readback waits for a network round trip. Expect loading-heavy programs to be slow over Wi-Fi. ## System-wide split client: glvnd vendor, GBM backend This makes MobileGL the GL implementation for every process on the machine, while falling back to the distribution's driver whenever no MobileGL server is reachable. It is how the anland container is set up. Use it only with a split client configuration: in monolith mode MobileGL never declines, so it would take over every EGL application. How the fallback works: before a client starts a session, it probes the configured endpoint with one non-blocking connect (bounded by `MOBILEGL_IPC_PROBE_TIMEOUT_MS`, default 250 ms; `0` disables the probe). If nothing answers, the EGL vendor returns `EGL_NO_DISPLAY` and reports no devices, the GLX vendor fails to load, and `mobilegl_gbm` fails `create_device`. libglvnd and libgbm then use the next implementation. ### Files to install Install the library under a directory of its own. Copy to a new file and rename it into place: never overwrite a `.so` that running processes have mapped. | Path | Contents | |---|---| | `/opt/mobilegl/lib/libMobileGL.so` | the library | | `/opt/mobilegl/lib/libEGL.so.1`, `libGL.so.1` (and the unversioned names) | optional symlinks to `libMobileGL.so`, for programs started with `LD_LIBRARY_PATH=/opt/mobilegl/lib` | | `/usr/share/glvnd/egl_vendor.d/10_mobilegl.json` | EGL vendor, sorted ahead of the distribution's `50_*.json` | | `/opt/mobilegl/share/glvnd/egl_vendor.d/50_mobilegl.json` | the same JSON, for forcing MobileGL per process with `__EGL_VENDOR_LIBRARY_FILENAMES` | | `/libGLX_mobilegl.so.0` | symlink to `/opt/mobilegl/lib/libMobileGL.so`; `` is the system library directory (`/usr/lib` on Arch, `/usr/lib/` on Debian/Ubuntu) | | `/etc/mobilegl/client.conf` | client settings (below) | | `/etc/mobilegl/backend` | optional: one line, `DirectGLES` or `DirectVulkan` | | `/mobilegl_gbm.so` | GBM backend; the directory libgbm searches (`/usr/lib/gbm` on Arch; `GBM_BACKENDS_PATH` overrides it) | | `/etc/environment.d/10-mobilegl.conf` and `/etc/profile.d/mobilegl.sh` | `__GLX_VENDOR_LIBRARY_NAME=mobilegl` and `GBM_BACKEND=mobilegl` for the systemd user session and login shells | The vendor JSON (both copies): ```json {"file_format_version":"1.0.0","ICD":{"library_path":"/opt/mobilegl/lib/libMobileGL.so"}} ``` `/etc/environment.d/10-mobilegl.conf`: ```text __GLX_VENDOR_LIBRARY_NAME=mobilegl GBM_BACKEND=mobilegl ``` `/etc/profile.d/mobilegl.sh`: ```sh export __GLX_VENDOR_LIBRARY_NAME=mobilegl export GBM_BACKEND=mobilegl ``` glvnd picks GLX vendors per X screen, so GLX needs `__GLX_VENDOR_LIBRARY_NAME`; EGL needs only the JSON file. ### `/etc/mobilegl/client.conf` Every process that loads `libMobileGL.so` reads this file. Format: `KEY=value` lines; blank lines and `#` comments are skipped; one pair of matching quotes around a value is removed; only `MOBILEGL_*` and `LIBGL_*` keys are taken. Example for a server on an abstract Unix socket: ```text MOBILEGL_TRANSPORT=spawn MOBILEGL_IPC_DATA=shm MOBILEGL_IPC_CONTROL=unix:@anland-mobilegl ``` Precedence, highest first: the process environment, then `/etc/mobilegl/backend` (backend only), then `client.conf`, then built-in defaults. `MOBILEGL_CONFIG_FILE=` and `MOBILEGL_BACKEND_FILE=` point at other files; set either to an empty value to disable that file. A server process (`MOBILEGL_IPC_ROLE=server`) reads neither file. ### Other knobs | Variable | Use | |---|---| | `MOBILEGL_DEVICE_DRM_NODE` | DRM render node MobileGL reports as its EGL device (default `/dev/dri/renderD128`). Compositors match it against the node they opened. | | `MOBILEGL_GBM_LIBRARY` | Path `mobilegl_gbm.so` loads `libMobileGL.so` from, if the process has not loaded it already | | `MOBILEGL_X11_DIAL` | Override for whether MobileGL may open an X11 connection (by default it does not dial a display that the process itself serves, such as Xwayland) | ### How to tell it works ```sh eglinfo -B ``` ```sh glxinfo -B ``` The renderer line names `Espryt (MobileGL Core)` or `Magma (MobileGL Core)` while the server is up, and the distribution's driver while it is down. To see the fallback without stopping the server, point one process at an endpoint nobody listens on: ```sh sed 's|^MOBILEGL_IPC_CONTROL=.*|MOBILEGL_IPC_CONTROL=unix:@nobody|' /etc/mobilegl/client.conf > /tmp/down.conf ``` ```sh MOBILEGL_CONFIG_FILE=/tmp/down.conf eglinfo -B ``` ## The anland setup anland runs a Linux desktop (for example KDE Plasma in an Arch Linux ARM container) on a rooted Android device. With MobileGL: - the anland Android app embeds the MobileGL **server** through the C interface described in [android-plugin.md](android-plugin.md#embedding-the-server-in-your-own-apk): a windowed server whose window is the compositor's Android surface, and offscreen servers for the other applications (`docs/Disaggregated/guide/embedded-server.md` describes both shapes); - inside the container, the **client** library (built for aarch64 glibc with `MOBILEGL_BUILD_DISAGGREGATED=ON`) is installed system-wide exactly as above, with `client.conf` pointing at `unix:@anland-mobilegl`. The container shares the device's network namespace, so the abstract socket is reachable; - Wayland clients present zero-copy through `linux-dmabuf`, and GBM users (the compositor's GBM device, Chromium's GPU process) get MobileGL server images from `mobilegl_gbm.so`. The app, its container scripts and the compositor patch belong to the anland project, not to this repository. Build the server library with the Android NDK and the client with an aarch64 glibc cross toolchain ([building.md](building.md)). The developer runbook for this exact setup is `docs/archive/notes/anland/runbook-plasma.md`. ## Monolith on a Linux desktop The library also runs monolith on a desktop's own driver: Espryt on the system EGL/GLES (it opens `libEGL.so.1`, trying the system library directories first), Magma on the system Vulkan loader. CI runs MobileGL's test binaries and trace replayer this way, on software drivers. Running real applications this way is not covered by CI; treat it as experimental. Per process, through glvnd (EGL applications): ```sh MOBILEGL_BACKEND_TYPE=DirectVulkan MOBILEGL_LOG_FILE_PATH=/tmp/mgl.log __EGL_VENDOR_LIBRARY_FILENAMES=/opt/mobilegl/share/glvnd/egl_vendor.d/50_mobilegl.json eglinfo -B ``` GLX applications (with `libGLX_mobilegl.so.0` installed as above): ```sh MOBILEGL_BACKEND_TYPE=DirectVulkan __GLX_VENDOR_LIBRARY_NAME=mobilegl glxinfo -B ``` Prefer Magma for EGL applications. Espryt loads the system `libEGL.so.1`, which on a glvnd system is the same dispatcher. When `__EGL_VENDOR_LIBRARY_FILENAMES` lists only MobileGL, that dispatcher can only route Espryt back to MobileGL. This combination has not been verified. Without glvnd, start the program with `LD_LIBRARY_PATH` pointing at the build directory, so its `libEGL.so.1`/`libGL.so.1` resolve to MobileGL's symlinks.