Add an end-user guide under userguide/.

This commit is contained in:
2026-10-09 13:36:15 -04:00
parent 9f4d9095a9
commit 9f61016776
8 changed files with 1281 additions and 0 deletions
+88
View File
@@ -0,0 +1,88 @@
# MobileGL user guide
MobileGL is a desktop OpenGL implementation, up to OpenGL 4.6 core profile. Applications call
OpenGL through EGL, GLX or WGL. MobileGL runs those calls on one of two backends:
| Backend | Internal name | Runs on top of |
|---|---|---|
| **Espryt** | `DirectGLES` (the default) | the platform's OpenGL ES 3.x driver (EGL + GLES) |
| **Magma** | `DirectVulkan` | the platform's Vulkan driver |
You pick the backend at run time with `MOBILEGL_BACKEND_TYPE` (see [configuration.md](configuration.md)).
This guide is for people who build, install, configure and run MobileGL. Developer notes live under
`docs/`; where they disagree with this guide, this guide follows the current code.
> **Status.** MobileGL is in development and ships no prebuilt binaries. Everything here is built
> from source. Commands were checked against the `feat/disaggregated` branch.
## Topologies: where the backend runs
The GL frontend (state tracking, shader translation) always runs in the application. The backend
can run in three places:
| Topology | `MOBILEGL_TRANSPORT` | What happens | Needs a split build |
|---|---|---|---|
| **Monolith** | `monolith` (default) | Frontend and backend run on the application's GL thread. | No |
| **In-process split** | `inproc` | The backend runs on a second thread of the same process, fed through a ring buffer. | Yes |
| **Out-of-process split** | `spawn` | The backend runs in a separate server process. The client either launches its own server or connects to a running one over a Unix socket or TCP. | Yes |
A *split build* is a library built with `MOBILEGL_BUILD_DISAGGREGATED=ON`. In a build without it,
`MOBILEGL_TRANSPORT` is silently ignored and everything runs monolith.
Out-of-process rendering needs somewhere to present frames. A server process started on its own has
no window. On Android, a client's window cannot be handed to another process, so a windowed Android
app can only render out of process into a window that the **server** owns. See
[android-plugin.md](android-plugin.md#out-of-process-rendering-with-a-window).
## Which setup do I need?
| You want to... | Setup | Guide |
|---|---|---|
| Play Minecraft on Android with the copy of MobileGL that is built into an FCL build | FCL embedded, monolith | [android-fcl.md](android-fcl.md) |
| Use MobileGL in FCL (or another launcher that reads FCL renderer plugins) without rebuilding the launcher | Renderer plugin APK, monolith or `inproc` | [android-plugin.md](android-plugin.md) |
| Test out-of-process rendering on Android, with frames shown on screen | Plugin APK: on-screen display server + `spawn` client | [android-plugin.md](android-plugin.md#out-of-process-rendering-with-a-window) |
| Render on a phone's GPU for a program on a Linux PC | Plugin (server) + Linux client over TCP | [android-plugin.md](android-plugin.md#tcp-server-for-remote-clients), [linux.md](linux.md#tcp-client) |
| Give a Linux container on Android (anland) GPU acceleration through the Android drivers | Linux client library as the system glvnd/GBM vendor, server on Android | [linux.md](linux.md#system-wide-split-client-glvnd-vendor-gbm-backend) |
| Run a Linux program on a Linux desktop through MobileGL | Linux library, monolith (experimental) | [linux.md](linux.md#monolith-on-a-linux-desktop) |
| Run a Windows OpenGL program (for example Minecraft) through MobileGL | WGL `opengl32.dll` drop-in, monolith | [windows.md](windows.md) |
| Build from source | | [building.md](building.md) |
| Look up a setting | | [configuration.md](configuration.md) |
| Fix something that does not work | | [troubleshooting.md](troubleshooting.md) |
When **not** to use the split topologies: if you only want the best frame rate in one app, use
monolith (FCL embedded) or `inproc` (plugin). `spawn` adds a process boundary and a socket for every
frame. It exists for sharing one GPU-owning server between processes, sandboxes or machines.
## Platform support at a glance
| Platform | Monolith | `inproc` | `spawn` (Unix socket) | `spawn` (TCP) | Server |
|---|---|---|---|---|---|
| Android (FCL embedded) | yes | no (built without the split layer) | no | no | no |
| Android (plugin APK) | yes | yes | yes | yes | yes, offscreen or on-screen |
| Linux (glibc) | experimental | yes | yes | yes | yes, offscreen only |
| Windows (WGL) | yes, but see the build note in [windows.md](windows.md) | not supported | not supported | not supported | no |
| macOS / iOS | the code exists (CGL and NSOpenGL frontends); not covered by CI and not verified for this guide. The project README describes the macOS build. | | | | |
## How to tell MobileGL is active
- `GL_RENDERER` starts with `Espryt (MobileGL Core)` or `Magma (MobileGL Core)`.
- `GL_VENDOR` contains `MobileGL-Dev`.
- `GL_VERSION` looks like `4.6 MobileGL 26.10.0-dev..., Direct (GLES) Backend, GIT@<hash>` (or
`Direct (Vulkan)`).
- In Minecraft, the F3 screen shows these strings.
- The log says `Config: Active backend type set to DirectGLES` (or `DirectVulkan`). Logs go to
logcat (tag `MobileGL`) on Android, and to the file named by `MOBILEGL_LOG_FILE_PATH` elsewhere.
See [troubleshooting.md](troubleshooting.md#logs).
## Files in this guide
| File | Contents |
|---|---|
| [android-fcl.md](android-fcl.md) | MobileGL built into FCL: monolith, choosing the backend, settings |
| [android-plugin.md](android-plugin.md) | The renderer plugin APK: install, `inproc`, the render server, windowed out-of-process rendering, TCP, same-device external programs, embedding the server in your own APK |
| [linux.md](linux.md) | Linux library: glvnd EGL/GLX vendor, GBM backend, `/etc/mobilegl/client.conf`, the server, TCP |
| [windows.md](windows.md) | WGL `opengl32.dll` drop-in |
| [building.md](building.md) | Toolchains, CMake options, Gradle properties, artifacts |
| [configuration.md](configuration.md) | User-facing `MOBILEGL_*` settings and where they can be set |
| [troubleshooting.md](troubleshooting.md) | Logs, common failures and their fixes |
+129
View File
@@ -0,0 +1,129 @@
# MobileGL embedded in FCL (Android, monolith)
Some FCL (Fold Craft Launcher) builds carry MobileGL as a Gradle module and ship `libMobileGL.so`
inside the launcher APK. This page covers that embedded copy. For the separately installed renderer
plugin, see [android-plugin.md](android-plugin.md).
## When to use it
- You play Minecraft on Android through an FCL build that already lists MobileGL renderers.
- You want the simplest setup with the least overhead: everything runs inside the game process.
When not to use it:
- You want `inproc`, `spawn` or TCP. The embedded copy is built **without** the split layer
(`MOBILEGL_BUILD_DISAGGREGATED=OFF`), so `MOBILEGL_TRANSPORT` is ignored and it always runs
monolith. Use the [plugin APK](android-plugin.md) instead.
- Your launcher does not embed MobileGL. Use the plugin.
## Requirements
- Android 8.0 (API 26) or newer, arm64-v8a. The embedded build packages arm64-v8a only.
- Espryt needs an OpenGL ES 3.x driver. Magma needs a Vulkan driver.
## How it is built and integrated
The FCL source tree has MobileGL as a git submodule and Gradle module:
| Where | What |
|---|---|
| `.gitmodules` | submodule `MobileGL` -> `https://github.com/MobileGL-Dev/MobileGL.git` |
| `settings.gradle.kts` | `include(":MobileGL")` |
| `FCLauncher/build.gradle.kts` | `implementation(project(":MobileGL"))` |
MobileGL's `build.gradle` detects that it is not the standalone plugin build (the root project is
not `MobileGLPlugin`) and then:
- builds arm64-v8a only;
- builds without the split layer (`MOBILEGL_BUILD_DISAGGREGATED=OFF`), so there is no
`libMobileGLServer.so`;
- builds with ThinLTO and `-O3` (`MOBILEGL_ENABLE_LTO=ON`) unless you pass
`-Pmobilegl.enableLto=OFF`;
- uses NDK `27.3.13750724` and the Android SDK's CMake `3.22.1`.
Before the first build, initialize MobileGL's submodules and glslang's external sources. Run these
inside the FCL checkout:
```sh
git submodule update --init --recursive
```
```sh
cd MobileGL/3rdparty/glslang && python3 update_glslang_sources.py
```
Then build FCL as its own documentation describes. The `mobilegl.*` Gradle properties from
[building.md](building.md#gradle-properties) apply when passed to FCL's Gradle invocation (for
example `-Pmobilegl.logLevel=MOBILEGL_LOG_LEVEL_DEBUG`).
## Choosing the renderer and backend
In FCL, open the game settings and pick the renderer under **OpenGL Implementation (Renderer)**. The
embedded copy appears as two entries. FCL sets the backend from the entry you pick:
| FCL entry | Sets |
|---|---|
| `MobileGL (Dev, Espryt)` | `MOBILEGL_BACKEND_TYPE=DirectGLES` |
| `MobileGL (Dev, Magma)` | `MOBILEGL_BACKEND_TYPE=DirectVulkan` |
The entry names above are from the FCL build that this guide was checked against; other FCL builds
may label them differently.
Which backend to pick: try Espryt first (the default). Use Magma if your device's Vulkan driver is
better than its GLES driver. Compare with the in-game frame rate; there is no universal answer.
## Other settings
FCL's **Environment Variables** editor (game settings, advanced section) takes one `KEY=VALUE` per
line. FCL applies it after the renderer's own variables, so it can also override
`MOBILEGL_BACKEND_TYPE`. Example:
```text
MOBILEGL_FRAMES_IN_FLIGHT=2
MOBILEGL_RELAXED_SEMANTICS=1
```
Useful keys are listed in [configuration.md](configuration.md). The FCL build checked for this guide
stores this editor's contents launcher-wide, not per instance.
Without access to the launcher's settings (for example while testing a launcher you cannot
rebuild), Android system properties work from `adb`, with no root needed:
| Property | Effect |
|---|---|
| `debug.mobilegl.backend` | Overrides `MOBILEGL_BACKEND_TYPE`, even when the launcher set it. |
| `debug.mobilegl.env` | `KEY=VALUE;KEY=VALUE` list of settings. It ranks **below** the process environment: it fills in only keys the launcher did not set. It holds at most 91 characters (Android's property length limit). |
| `debug.mobilegl.fps_log` | `1` logs the frame rate about every two seconds (same as `MOBILEGL_FPS_LOG=1`). |
```sh
adb shell setprop debug.mobilegl.backend DirectVulkan
```
```sh
adb shell setprop debug.mobilegl.env "MOBILEGL_FRAMES_IN_FLIGHT=2;MOBILEGL_RELAXED_SEMANTICS=1"
```
These properties apply to every app on the device that loads MobileGL, and they last until reboot.
Clear one by setting it to an empty string:
```sh
adb shell setprop debug.mobilegl.env ""
```
`debug.mobilegl.env` covers most settings in [configuration.md](configuration.md). The ones marked
*env only* there are read straight from the process environment and ignore it, notably
`MOBILEGL_LOG_FILE_PATH` and `MOBILEGL_IPC_TOKEN`. `MOBILEGL_FPS_LOG` and
`MOBILEGL_GPU_HANG_BUDGET_MS` have their own properties (`debug.mobilegl.fps_log`,
`debug.mobilegl.gpu_hang_budget_ms`).
## How to tell it works
- Minecraft's F3 screen shows `Espryt (MobileGL Core)` or `Magma (MobileGL Core)` as the renderer.
- logcat shows MobileGL's lines:
```sh
adb logcat -s MobileGL
```
Look for `Config: Active backend type set to DirectGLES` (or `DirectVulkan`).
- MobileGL also writes `/sdcard/MG/latest.log`, if the process may write there.
+236
View File
@@ -0,0 +1,236 @@
# MobileGL renderer plugin APK (Android)
The plugin is a standalone app (`top.mobilegl.plugin`, built from `android-plugin/`). It does three
jobs:
1. **Renderer plugin.** FCL and other launchers that read FCL renderer plugins find it and load its
`libMobileGL.so` into the game process.
2. **Render server.** It can run a MobileGL server, either offscreen (a foreground service) or
on-screen (a full-screen Activity that shows a client's frames).
3. **Driver self-test.** Its launcher screen (**POST** tab) tests the device's GLES and Vulkan
drivers, and can run a short benchmark.
## When to use it
- You want MobileGL in a launcher without rebuilding the launcher.
- You want `inproc`, out-of-process (`spawn`) or TCP rendering on Android. Only split builds can do
that, and the plugin is a split build by default.
- You want a phone to render for a client on another machine.
When not to use it: if your FCL build already embeds MobileGL and you only need monolith rendering,
the embedded copy is simpler ([android-fcl.md](android-fcl.md)).
## Build and install
Build instructions are in [building.md](building.md#android-plugin-apk). In short:
```sh
gradle -p android-plugin :app:assemblePluginRelease -Pmobilegl.buildDisaggregated=ON -Pmobilegl.buildDisaggregatedInproc=ON
```
The APK is
`android-plugin/app/build/outputs/apk/plugin/release/MobileGL-plugin-release-<git-hash>.apk`. A
release APK is signed only when the signing environment variables are set (see
[building.md](building.md#signing)); otherwise sign it yourself before installing.
```sh
adb install -r MobileGL-plugin-release-<git-hash>.apk
```
Android refuses to replace an installed plugin that was signed with a different key. Either
uninstall the old one first (the launcher then forgets its plugin settings) or build with
`-Pmobilegl.applicationIdSuffix=.dev` to install a second copy next to it.
Requirements: Android 8.0 (API 26) or newer. The default ABI is arm64-v8a
(`-Pmobilegl.abis=all` adds x86_64).
## Using the plugin in a launcher
The plugin advertises itself through manifest metadata. The launcher lists it as a renderer named
**MobileGL** and loads `libMobileGL.so` from the plugin's install directory.
| Metadata | Read by | Contents |
|---|---|---|
| `fclPlugin`, `renderer`, `boatEnv`, `pojavEnv` | launchers using the original FCL plugin format | Fixed environment: `LIBGL_ES=3`, `POJAV_RENDERER=opengles3`, `MOBILEGL_BACKEND_TYPE=DirectGLES`, plus `MOBILEGL_TRANSPORT=inproc` in an `inproc` build (see below) |
| `fclPlugin_V2` | launchers that read the v2 renderer-plugin format | The same environment plus user options: backend (`DirectGLES`/`DirectVulkan`), frames in flight, and toggles for `MOBILEGL_DISABLE_TIMERQUERY`, `MOBILEGL_MAGMA_DISABLE_SUBGROUP`, `MOBILEGL_MAGMA_R11G11B10F_FALLBACK`, `MOBILEGL_ESPRYT_AVOID_SAMPLER_MIPMAP_MIN_FILTER`, `MOBILEGL_COHERENT_AS_FLUSH`, `MOBILEGL_RELAXED_SEMANTICS`, `MOBILEGL_ESPRYT_USE_ANGLE` |
The FCL build this guide was checked against reads only the original format, so it shows no plugin
options and always starts the plugin with Espryt. To change anything there, use FCL's
**Environment Variables** editor (applied after the plugin's variables), for example:
```text
MOBILEGL_BACKEND_TYPE=DirectVulkan
```
The `debug.mobilegl.*` properties described in [android-fcl.md](android-fcl.md#other-settings) also
work for the plugin. One more exists for split builds: `debug.mobilegl.transport` overrides
`MOBILEGL_TRANSPORT`.
## In-process split (`inproc`)
With `MOBILEGL_TRANSPORT=inproc`, the GL frontend runs on the game's thread and the backend runs on
a second thread. On a CPU-bound game this can raise the frame rate; it costs up to one extra frame of
latency.
The plugin's launcher environment includes `MOBILEGL_TRANSPORT=inproc` **only if** the APK was built
with `-Pmobilegl.buildDisaggregated=ON` or `-Pmobilegl.buildDisaggregatedInproc=ON` passed explicitly
(the CI build of `feat/disaggregated` does this). A plain `gradle :app:assemblePluginRelease`
still builds the split layer into the library, but the launcher environment then omits the transport
and the plugin runs monolith. Either rebuild with the property, or add
`MOBILEGL_TRANSPORT=inproc` in the launcher's environment editor.
Check the log for:
```text
Config: MOBILEGL_TRANSPORT=inproc - the MGPipe record stream crosses a real ring to an apply thread
```
## The render server
Open the MobileGL app and switch to the **Render Server** tab. It starts one of two servers:
| Mode | Component | Process | What it is for |
|---|---|---|---|
| Offscreen (default) | `MobileGLServerService` (foreground service) running `libMobileGLServer.so <endpoint> --serve` | `:mglsrv` | Remote clients over TCP, offscreen (pbuffer) programs, same-device programs through the broker |
| On-screen (tick **On-screen window**) | `MobileGLDisplayActivity`, a full-screen Activity with an in-process server | `:mglwin` | Clients that set `MOBILEGL_IPC_SURFACE=server`: their window surface is drawn into this Activity |
Fields:
| Field | Default | Notes |
|---|---|---|
| Listen endpoint | `tcp://0.0.0.0:40613` | `tcp://host:port`, `@name` (abstract Unix socket) or a file path (relative paths resolve against the app's files directory) |
| Auth token | empty | Clients present it as `MOBILEGL_IPC_TOKEN`. A TCP listen on a non-loopback address (including `0.0.0.0`) **requires** a token of at least 16 bytes; without a token, use `tcp://127.0.0.1:<port>`. **Generate token** makes a random one. |
| Extra env | empty | `KEY=VALUE;KEY` list for the server process (`KEY` alone unsets). For example `MOBILEGL_BACKEND_TYPE=DirectVulkan` pins the server's backend. |
Only one server runs at a time: starting one stops the other. The plugin flavor does not export these
components, so other apps and `adb` cannot start them by component name; use the screen.
Backend selection: the client's `MOBILEGL_BACKEND_TYPE` decides the backend for its session. A
server started with `MOBILEGL_BACKEND_TYPE` in its extra env refuses clients that ask for the other
backend. The on-screen server keeps the first session's backend for its whole process lifetime; to
switch, stop the server and start it again.
Server logs go to logcat (tags `MobileGL`, `MobileGLServer`, `MobileGLDisplay`) and to
`mgl.server.log` (offscreen) or `mglwin.server.log` (on-screen) in the app's private files directory.
## Out-of-process rendering with a window
A spawned server process has no display, and an Android client's window cannot be passed to another
process. So a windowed app (a launcher's Minecraft, for example) can render out of process only into
a window the **server** owns: the plugin's on-screen display server.
Without that, a `spawn` client that creates a window surface fails. With the default
`MOBILEGL_IPC_SURFACE=offscreen`, the server rejects the window with
`Fatal{UnmigratedSurface, "AndroidNativeWindow@P12"}`. With `MOBILEGL_IPC_SURFACE=server` and an
offscreen server, the client logs `Refuse ServerOwned (NoServerDisplay)`.
Steps (client in FCL, server in the plugin, same device):
1. In the MobileGL app, **Render Server** tab: set **Listen endpoint** to
`tcp://127.0.0.1:40613`, leave the token empty, tick **On-screen window**, tap **Start server**.
A black full-screen window appears; this is the display server.
2. In FCL, select the **MobileGL** plugin renderer. The FCL-embedded MobileGL entries cannot do
this, because they are built without the split layer.
3. In FCL's **Environment Variables** editor, add:
```text
MOBILEGL_TRANSPORT=spawn
MOBILEGL_IPC_CONTROL=tcp://127.0.0.1:40613
MOBILEGL_IPC_SURFACE=server
```
If the server has a token, also add `MOBILEGL_IPC_TOKEN=<token>`.
4. Launch the game. Its frames are drawn into the display server's window, at the size the game
asks for, letterboxed to the screen.
Limits:
- The display window only shows frames. It has no input handling: touch and keys go to whichever
window has focus.
- The display window must be visible when the game creates its window surface. Otherwise the client
logs `Refuse ServerOwned (NoServerWindow)`. If the display window goes away while a game renders
into it, that session ends with a device loss. The server keeps listening, and the next session
renders when the window is back.
- Use this setup to test out-of-process rendering. For normal play, monolith or `inproc` is faster
and simpler.
- A known open issue: each run logs `MG_Remote server: shared image 1 could not be sent on the aux
socket (rc=3)`. Rendering continues.
How to tell it works: the client log has
`MG_Remote client: control=tcp data=stream server=127.0.0.1:40613 ... dial=connect`, and the
server's logcat has `listening on tcp://127.0.0.1:40613 (in-process display server, display installed)`.
## TCP server for remote clients
The phone renders; a client on another machine sends GL commands over TCP. The client must be a
split build of the **same commit** (see [linux.md](linux.md#tcp-client)). Only window surfaces the
client presents itself (Linux X11/Wayland windows) or offscreen surfaces work this way.
1. Find the phone's address:
```sh
adb shell ip -f inet addr show wlan0
```
2. In the **Render Server** tab: endpoint `tcp://0.0.0.0:40613`, tap **Generate token**, keep
**On-screen window** unticked, tap **Start server**.
3. On the client, set `MOBILEGL_TRANSPORT=spawn`, `MOBILEGL_IPC_CONTROL=tcp://<phone-ip>:40613` and
`MOBILEGL_IPC_TOKEN=<token>`.
If the network isolates devices, tunnel over adb instead: start the server on
`tcp://127.0.0.1:40613` (no token needed), then on the client machine run the command below and
point the client at `tcp://127.0.0.1:40613`:
```sh
adb forward tcp:40613 tcp:40613
```
## Same-device programs outside the app (Termux, `adb shell`)
Android does not let another app connect to the plugin's sockets directly. The plugin ships a broker
for this case: a helper class, run with `app_process` under the program's own user, asks the plugin
for a connected socket pair and then starts the program with it.
1. Start the **offscreen** server with a token of at least 16 bytes (**Generate token**).
2. Tap **Copy launch command**. It produces a line of this form:
```sh
MOBILEGL_IPC_TOKEN='<token>' CLASSPATH=<plugin APK path> app_process / top.mobilegl.plugin.ExternalClientHelper -- <program> [args...]
```
3. Run it with your program. The program must use a split build of MobileGL built for Android
(bionic). The helper starts it with `MOBILEGL_TRANSPORT=spawn`,
`MOBILEGL_IPC_CONTROL=fd:<control>,<aux>` and `MOBILEGL_IPC_DATA=shm` already set.
Helper options: `--server <package>` (default: the APK it was loaded from), `--timeout-ms <n>`
(default 10000), `--keep-affinity` (do not keep the program off the server's reserved CPU core). The
token can also come from a file named by `MOBILEGL_IPC_TOKEN_FILE`; never put it on the command line.
The program renders offscreen: the offscreen server has no display.
## Embedding the server in your own APK
An app can host the server itself instead of using the plugin. The C interface is
`MobileGL/MG_Remote/Server/EmbeddedServer.h` (plain C types, also reachable through `dlsym`); the
library must be a split build.
1. Before loading `libMobileGL.so`, set `MOBILEGL_IPC_ROLE=server` and `MOBILEGL_IPC_DIAL=no`, and
unset `MOBILEGL_TRANSPORT`, `MOBILEGL_IPC_SERVER_PATH`, `MOBILEGL_IPC_RING_MB` and
`MOBILEGL_IPC_STAGE_MB`. Running the server in its own process keeps a GPU fault away from your UI.
2. For a windowed server, call `mobilegl_server_display_install_android(callback, user)`, then
`mobilegl_server_display_attach_android(window, width, height)` with the `ANativeWindow` of your
`Surface`, and `mobilegl_server_display_detach(timeout_ms)` when the surface is destroyed.
3. Call `mobilegl_server_serve_inprocess("<endpoint>")` on a thread of its own (it blocks). Stop it
with `mobilegl_server_stop_inprocess()`, then `mobilegl_server_display_uninstall(timeout_ms)`.
Clients connect with `MOBILEGL_TRANSPORT=spawn`, `MOBILEGL_IPC_CONTROL=unix:@<name>` (or `tcp://...`)
and, for the windowed server, `MOBILEGL_IPC_SURFACE=server`. The anland Linux-container setup uses
exactly this interface ([linux.md](linux.md#the-anland-setup)). For more offscreen clients at the same
time, package `libMobileGLServer.so` and run it as
`libMobileGLServer.so @<name> --serve --max-sessions <1-64>` from your own service.
`MobileGLServerService.java` and `ServerEnvironment.java` in `android-plugin/` are working examples.
## The trace flavor
`assembleTraceRelease` builds `top.mobilegl.plugin.trace`, a development APK that replays recorded GL
traces and exports the server components so that `adb` can start them by name. It is not a renderer
plugin. See `android-plugin/TRACE_REPLAY.md`.
+200
View File
@@ -0,0 +1,200 @@
# Building MobileGL
MobileGL builds with CMake (all platforms) and with Gradle for the Android packages. It is C++23.
The commands below come from the project's CI workflows (`.github/workflows/test.yml` and
`apk.yml`), which build these exact shapes.
## Get the sources
```sh
git clone https://github.com/MobileGL-Dev/MobileGL.git
```
```sh
git -C MobileGL submodule update --init --recursive
```
glslang needs its external sources (SPIRV-Tools and SPIRV-Headers) fetched once:
```sh
cd MobileGL/3rdparty/glslang && python3 update_glslang_sources.py
```
Without this step, configuring fails with a message that SPIR-V tools were not found.
Large trace fixtures in the repository are stored with Git LFS. You do not need them to build. If
cloning or adding a worktree fails while fetching them, set `GIT_LFS_SKIP_SMUDGE=1`.
## Toolchains
| Target | Compiler | Other tools |
|---|---|---|
| Linux | Clang (CI: clang 20 with lld). The test and benchmark targets require Clang. | CMake 3.22.1 or newer (CI pins **3.31.10**), Ninja, Python 3, Vulkan development files, EGL and GLES development headers (Ubuntu: `libvulkan-dev libegl-dev libgles-dev`). For the GBM module: `gbm.h` and `gbm_backend_abi.h` from your distribution's GBM development package. |
| Android | NDK `27.3.13750724` (the version Gradle pins) | JDK 17, Gradle (CI: 8.10.2; the plugin uses Android Gradle Plugin 8.6.0), Android SDK platform 34, the SDK's CMake `3.22.1` |
| Windows | MSVC (Visual Studio 2022). See the build status note in [windows.md](windows.md). | CMake, Python 3, Vulkan SDK |
The minimum Android API level is **26**. Configuring for a lower level is an error; a higher level is
allowed, but the code must not use newer APIs. A separate API 29 build is planned and not available
yet.
## Linux
Shipping-style monolith library:
```sh
cmake -S . -B build -G Ninja -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_BUILD_TYPE=Release -DMOBILEGL_BUILD_TEST=OFF -DMOBILEGL_BUILD_BENCHMARK=OFF -DCMAKE_POLICY_VERSION_MINIMUM=3.5
```
```sh
cmake --build build --target MobileGL
```
Split build (`inproc`, `spawn`, TCP; adds the server executable and the GBM module):
```sh
cmake -S . -B build-split -G Ninja -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_BUILD_TYPE=Release -DMOBILEGL_BUILD_TEST=OFF -DMOBILEGL_BUILD_BENCHMARK=OFF -DMOBILEGL_BUILD_DISAGGREGATED=ON -DMOBILEGL_BUILD_DISAGGREGATED_INPROC=ON -DCMAKE_POLICY_VERSION_MINIMUM=3.5
```
```sh
cmake --build build-split --target MobileGL MobileGLServer mobilegl_gbm
```
Drop `mobilegl_gbm` from the target list if configure printed
`gbm.h / gbm_backend_abi.h not found, mobilegl_gbm is not built`.
`-DCMAKE_POLICY_VERSION_MINIMUM=3.5` is what CI passes. It is required only when configuring with
CMake 4.x, which refuses some submodules' old minimum versions.
For an aarch64 Linux target (for example a Linux container on an Android device), cross-compile with
your own CMake toolchain file and a sysroot of the target distribution (`--toolchain <file>`); there is
nothing MobileGL-specific about it. Build the client with `MOBILEGL_BUILD_DISAGGREGATED=ON`.
### Tests
Tests need Clang. Configure without the `MOBILEGL_BUILD_TEST=OFF` /
`MOBILEGL_BUILD_BENCHMARK=OFF` options (both default to `ON`; configure then downloads googletest and
Google Benchmark), build everything, and run the unit label. Use **CMake 3.31.x** for this: CI pins
3.31.10, because the test registrations carry escaped environment and label lists that newer CMake
releases handle differently. Under CMake 4.x, tests can start with the wrong environment.
```sh
cmake --build build
```
```sh
ctest --test-dir build --output-on-failure -L unit
```
GPU integration tests need `-DMOBILEGL_BUILD_INTEGRATION_TEST=ON` and a working GPU driver. Their
label is `integration-gpu`. Do not run two `ctest` suites at the same time.
## Android: direct CMake (NDK)
For embedding the library in your own app, or for the server of an app that hosts one:
```sh
cmake -S . -B build-android -G Ninja -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK/build/cmake/android.toolchain.cmake -DANDROID_ABI=arm64-v8a -DANDROID_PLATFORM=android-26 -DCMAKE_BUILD_TYPE=Release -DMOBILEGL_BUILD_DISAGGREGATED=ON -DMOBILEGL_BUILD_DISAGGREGATED_INPROC=ON
```
```sh
cmake --build build-android --target MobileGL MobileGLServer
```
Tests and benchmarks are always off on Android. Package `libMobileGL.so` (and, if you run the
offscreen server, `libMobileGLServer.so`) under the APK's `lib/arm64-v8a/`. `libMobileGLServer.so`
is an executable with a library name, because that is the only place in an APK from which Android
lets an app run executables. Extract native libraries at install time
(`useLegacyPackaging = true`) so it exists on disk.
## Android plugin APK
The Gradle project is `android-plugin/` (root project `MobileGLPlugin`, modules `:app` and
`:MobileGL`, which is the repository root). It has no Gradle wrapper; use a Gradle installation
(CI uses 8.10.2). The build needs network access to Google's Maven repository, Maven Central and
`jitpack.io` (for the renderer-plugin build DSL).
`android-plugin/local.properties` (or `ANDROID_HOME`) must name the SDK. If the NDK or the SDK's CMake
is missing:
```sh
sdkmanager "ndk;27.3.13750724" "cmake;3.22.1"
```
Release build, with `inproc` in the launcher environment (the same command CI runs):
```sh
gradle -p android-plugin :app:assemblePluginRelease -Pmobilegl.buildDisaggregated=ON -Pmobilegl.buildDisaggregatedInproc=ON
```
Output: `android-plugin/app/build/outputs/apk/plugin/release/MobileGL-plugin-release-<git-hash>.apk`.
A debug build, debug-signed and installable as is:
```sh
gradle -p android-plugin :app:assemblePluginDebug -Pmobilegl.buildDisaggregated=ON
```
Its file is also named `MobileGL-plugin-release-<git-hash>.apk`, but it is in `.../apk/plugin/debug/`.
### Signing
A release APK is signed only when `android-plugin/keystore.jks` exists **and** the environment
variables `SIGNING_STORE_PASSWORD`, `SIGNING_KEY_ALIAS` and `SIGNING_KEY_PASSWORD` are set (they belong
to the project's own key). Otherwise the release APK is unsigned; sign it with your own key:
```sh
apksigner sign --ks my-release.jks --out MobileGL-plugin-signed.apk MobileGL-plugin-release-<git-hash>.apk
```
### FCL-embedded library
When MobileGL is a module of an FCL build (see [android-fcl.md](android-fcl.md)), FCL's Gradle build
compiles it. The same `mobilegl.*` properties apply when passed to FCL's Gradle; the differences from
the plugin are in the table below.
## Gradle properties
Pass with `-P<name>=<value>`. Some can also come from an environment variable.
| Property | Env fallback | Default | Effect |
|---|---|---|---|
| `mobilegl.enableLto` | `MOBILEGL_ENABLE_LTO` | `ON` | ThinLTO and `-O3` for the native library. `OFF` builds faster. |
| `mobilegl.buildDisaggregated` | `MOBILEGL_BUILD_DISAGGREGATED` | plugin: `ON`; FCL-embedded: `OFF` | Build the split layer and the server. In the plugin, passing it explicitly as `ON` also adds `MOBILEGL_TRANSPORT=inproc` to the launcher environment. |
| `mobilegl.buildDisaggregatedInproc` | `MOBILEGL_BUILD_DISAGGREGATED_INPROC` | follows `mobilegl.buildDisaggregated` | Implies the above; the same launcher-environment effect. |
| `mobilegl.abis` | `MOBILEGL_ABIS` | `arm64-v8a` | Comma-separated ABIs, or `all` (= `arm64-v8a,x86_64`). Plugin only; the FCL-embedded build is always arm64-v8a. |
| `mobilegl.logLevel` | `MOBILEGL_LOG_ACTIVE_LEVEL` | `MOBILEGL_LOG_LEVEL_INFO` | Lowest log level compiled in: `MOBILEGL_LOG_LEVEL_DEBUG`, `_INFO`, `_WARN`, `_ERROR`, `_FATAL` |
| `mobilegl.cmakeCompilerLauncher` | `MOBILEGL_CMAKE_COMPILER_LAUNCHER` | none | Compiler launcher, for example `ccache` |
| `mobilegl.applicationIdSuffix` | | none | Suffix for the plugin's application ID, to install next to another copy |
| `mobilegl.apkSuffix` | `MOBILEGL_APK_SUFFIX` | git short hash | Suffix in the APK file name |
| `mobilegl.debuggableRelease` | | `false` | Make the release build debuggable |
Prefer the `-P` form for `mobilegl.buildDisaggregated`: the plugin's two Gradle scripts read the
environment fallback differently.
## CMake options
| Option | Default | Effect |
|---|---|---|
| `MOBILEGL_BUILD_DISAGGREGATED` | `OFF` | Build the split layer (`inproc`, `spawn`, TCP), the server executable `libMobileGLServer.so` (not on Windows) and, on Linux, the shared-image API and `mobilegl_gbm.so`. Needs the `3rdparty/flatbuffers` submodule; without it the option is turned off with a warning. |
| `MOBILEGL_BUILD_DISAGGREGATED_INPROC` | `OFF` | Implies `MOBILEGL_BUILD_DISAGGREGATED`. CI and Gradle set both. |
| `MOBILEGL_ENABLE_LTO` | `OFF` | ThinLTO (or the compiler's IPO) plus `-O3`, section garbage collection and identical-code folding. Gradle turns it on. |
| `MOBILEGL_FORCE_RELEASE_OPT` | `ON` | Apply the `MOBILEGL_ENABLE_LTO` flags in Debug builds too. Has no effect without `MOBILEGL_ENABLE_LTO=ON`. |
| `MOBILEGL_LOG_ACTIVE_LEVEL` | `MOBILEGL_LOG_LEVEL_INFO` | Lowest log level compiled in |
| `MOBILEGL_BUILD_TEST` | `ON` (forced `OFF` on Android) | Unit tests (Clang only) |
| `MOBILEGL_BUILD_BENCHMARK` | `ON` (forced `OFF` on Android) | Benchmarks (Clang only) |
| `MOBILEGL_BUILD_INTEGRATION_TEST` | `OFF` | GPU integration tests |
| `MOBILEGL_BUILD_TRACE_REPLAY` | `OFF` | apitrace replay runner (needs the `3rdparty/apitrace` submodule) |
| `MOBILEGL_ENABLE_TRACY` | `OFF` | Tracy profiler |
| `MOBILEGL_BUILD_STAMP` | empty (git commit) | Build identity used by the client/server handshake when `git rev-parse HEAD` does not work in the source tree |
## Artifacts
| Build | Files |
|---|---|
| Linux | `libMobileGL.so`, `libMobileGL_s.a`, symlinks `libEGL.so`, `libEGL.so.1`, `libGL.so`, `libGL.so.1`, `libGLX_mobilegl.so.0`; split: `libMobileGLServer.so`, `MobileGL/MG_Gbm/mobilegl_gbm.so` |
| Windows | `MobileGL.dll`, `opengl32.dll` (a copy), `MobileGL_s.lib` |
| Android (CMake) | `libMobileGL.so`; split: `libMobileGLServer.so` |
| Android (Gradle) | `MobileGL-plugin-release-<hash>.apk` (plugin flavor), `MobileGL-plugin-trace-release-<hash>.apk` (trace flavor, development only) |
Client and server talk only if they come from the same source revision (the wire layout must match;
for a different build of a compatible wire, the client logs a warning). Build both ends from one commit.
+130
View File
@@ -0,0 +1,130 @@
# Configuration
MobileGL is configured with `MOBILEGL_*` variables. This page lists the ones meant for users.
Variables that exist for development, CI and benchmarking (`MOBILEGL_PIPE_*`, most `MOBILEGL_IPC_*`
tuning knobs, quirk overrides) are left out on purpose. Some of them are negative controls that
break rendering by design.
## Where settings come from
Settings are read once, when the library initializes in a process. Changing them later has no effect
until the process restarts.
| Source | Platform | Precedence |
|---|---|---|
| `debug.mobilegl.backend` property | Android | Overrides `MOBILEGL_BACKEND_TYPE` from every other source |
| `debug.mobilegl.transport` property | Android, split builds | Overrides `MOBILEGL_TRANSPORT` from every other source |
| Process environment | all | Highest for everything else |
| `/etc/mobilegl/backend` (one line: the backend name) | desktop Linux | Below the environment; `MOBILEGL_BACKEND_TYPE` only |
| `/etc/mobilegl/client.conf` (`KEY=value` lines) | desktop Linux | Below the backend file |
| `debug.mobilegl.env` property (`KEY=VALUE;KEY=VALUE`, at most 91 characters) | Android | Below the environment |
| Built-in default | all | Lowest |
- Only keys starting with `MOBILEGL_` or `LIBGL_` are accepted from the environment and from
`client.conf`.
- `MOBILEGL_CONFIG_FILE` and `MOBILEGL_BACKEND_FILE` name other files instead of the two `/etc`
files; an empty value turns a file off.
- A server process (`MOBILEGL_IPC_ROLE=server`) reads neither file nor `debug.mobilegl.env`.
- Settings marked **env only** below are read straight from the process environment. They ignore the
files and `debug.mobilegl.env`.
- On Android, use `adb shell setprop <property> <value>` (no root needed). Properties apply to every
app on the device that loads MobileGL, and last until reboot.
Value rules:
- **Flag**: on when set to anything other than empty, `0` or `false` (case-insensitive).
- **Auto/on/off**: unset keeps the built-in choice; a flag-true value forces it on; anything else
forces it off.
- **Integer**: a value outside the range is ignored with a warning in the log, and the default is used.
Where a setting takes effect: each process reads its own settings. When you connect to a server that
was started separately (the plugin's render server, a Linux server), the backend settings
(`MOBILEGL_ESPRYT_*`, `MOBILEGL_MAGMA_*`, `MOBILEGL_FRAMES_IN_FLIGHT`) belong in the **server's**
environment too. The backend itself is the exception: the client asks for it (see
`MOBILEGL_BACKEND_TYPE`).
## Backend and general
| Variable | Values | Default | Meaning |
|---|---|---|---|
| `MOBILEGL_BACKEND_TYPE` | `DirectGLES`, `DirectVulkan` | `DirectGLES` | Espryt (GLES) or Magma (Vulkan). Case-sensitive. Any other value leaves MobileGL without a backend. In a split session the client's value is sent to the server, which uses it unless the server pinned a different one. |
| `MOBILEGL_FRAMES_IN_FLIGHT` | 1-64 | 3 | How many frames may be queued ahead of the GPU. Magma: its frame contexts. Espryt: after presenting frame N it waits for frame N+1-value to finish (1 = full CPU/GPU sync). Split topologies: the client may run `max(1, value - 2)` presents ahead of the server, so 4 gives one more frame of overlap (and of latency). The old name `MOBILEGL_MAGMA_FRAMESINFLIGHT` still works but is deprecated. |
| `MOBILEGL_RELAXED_SEMANTICS` | flag | off | Apply legacy leniencies (for example drawing with vertex array object 0, reusing texture names after delete) even to contexts that explicitly requested a core profile. Contexts that did not request core get them anyway. |
| `MOBILEGL_ADVERTISE_FP64` | flag | off | Advertise `GL_ARB_gpu_shader_fp64`. `double` types compile either way but run at 32-bit precision. |
| `MOBILEGL_DISABLE_TIMERQUERY` | flag | off | Hide and disable GPU timer queries |
| `MOBILEGL_COHERENT_AS_FLUSH` | flag | off | Treat persistent maps with `GL_MAP_FLUSH_EXPLICIT_BIT` as coherent, for engines that never flush them |
| `MOBILEGL_ASYNC_SHADER_COMPILE` | auto/on/off | on | Compile and link shaders on worker threads. Off runs them on the calling thread and withdraws `GL_KHR_parallel_shader_compile`. |
| `MOBILEGL_ASYNC_SHADER_COMPILE_THREADS` | 0-64 | 0 | Worker count; 0 = min(4, number of big cores) |
| `MOBILEGL_SHADER_CACHE` | auto/on/off | on | In-memory shader translation cache. Turn it off to rule it out when a shader renders wrongly. |
| `MOBILEGL_ES_CONTEXT_IDENTITY` | `1` | off | **env only.** Contexts created for the OpenGL ES API report themselves as OpenGL ES 3.2 instead of the desktop version |
## Espryt (DirectGLES)
| Variable | Values | Default | Meaning |
|---|---|---|---|
| `MOBILEGL_ESPRYT_USE_ANGLE` | flag | off | Load `libEGL_angle.so` / `libGLESv2_angle.so` instead of the system EGL/GLES. Not used on Windows, where Espryt always loads ANGLE's `libEGL.dll` / `libGLESv2.dll`. |
| `MOBILEGL_ESPRYT_AVOID_SAMPLER_MIPMAP_MIN_FILTER` | flag | off | Avoid mipmap minification filters in samplers (a workaround for certain driver bugs) |
| `MOBILEGL_YUV_DRIVER_CONVERSION` | `1` | off | **env only.** Convert YUV images with the driver's external-sampler conversion instead of MobileGL's own |
## Magma (DirectVulkan)
| Variable | Values | Default | Meaning |
|---|---|---|---|
| `MOBILEGL_MAGMA_DISABLE_SUBGROUP` | flag | off | Do not use Vulkan subgroup operations in shaders |
| `MOBILEGL_MAGMA_R11G11B10F_FALLBACK` | flag | off | Use a fallback format for `GL_R11F_G11F_B10F` |
| `MOBILEGL_MAGMA_MAX_DRAWS_PER_COMMAND_BUFFER` | 0-16777216 | 16384 | Submit a command buffer mid-frame after this many draws/dispatches (0 = never). Bounds driver memory in huge loading frames. |
| `MOBILEGL_MAGMA_DESCRIPTOR_TRIM_FRAMES` | 0-1048576 | 120 | Free grown descriptor pools after this many quiet frames (0 = never) |
| `VK_ICD_FILENAMES` | path to an ICD JSON | loader default | Standard Vulkan loader variable: picks the Vulkan driver |
## Logging and diagnostics
| Variable | Values | Default | Meaning |
|---|---|---|---|
| `MOBILEGL_LOG_FILE_PATH` | path | Android: `/sdcard/MG/latest.log`; elsewhere none | **env only.** Log file. Split builds write `<name>.client<ext>` and `<name>.server<ext>` instead (for example `latest.client.log`); in a client, `.server` receives the lines the server forwards. The file is truncated when opened. On Android, logs also go to logcat with tag `MobileGL`. |
| `MOBILEGL_FPS_LOG` | `1` | off | **env only** (Android: also `debug.mobilegl.fps_log`). One line about every two seconds with frame rate and frame time. |
| `MOBILEGL_GPU_HANG_BUDGET_MS` | 0-600000 | 2000 | **env only** (Android: also `debug.mobilegl.gpu_hang_budget_ms`). A GPU submission running longer than this ends its session as hung. 0 turns the watchdog off; do that on slow software rasterizers. |
## Transport (split builds only)
| Variable | Values | Default | Meaning |
|---|---|---|---|
| `MOBILEGL_TRANSPORT` | `monolith`, `inproc`, `spawn` | `monolith` | Where the backend runs ([README.md](README.md#topologies-where-the-backend-runs)). `unix:...` / `pipe:...` are rejected (logged, and the run stays monolith); use `MOBILEGL_IPC_CONTROL`. Ignored in builds without the split layer. |
| `MOBILEGL_IPC_CONTROL` | `fork`, `unix:<path>`, `unix:@<name>`, `tcp://<host>:<port>` | `fork` | With `spawn`: `fork` launches a private server; the others connect to a running one. (`fd:<a>,<b>` is set by the plugin's external-client helper.) |
| `MOBILEGL_IPC_DATA` | `auto`, `shm`, `stream` | `auto` | Data plane. `auto` picks shared memory for Unix sockets and `fork`, and a stream for TCP; TCP accepts only `stream`, the others only `shm`. |
| `MOBILEGL_IPC_SURFACE` | `offscreen`, `server` | `offscreen` | `server`: window surfaces are created on the server's own window, and the client's window handle is ignored. Needs a server with a display (the plugin's on-screen server, or an app embedding one). Only meaningful with `spawn`. |
| `MOBILEGL_IPC_TOKEN` | string | none | **env only.** Shared secret; client and server must agree. A server listening on a non-loopback TCP address requires one of at least 16 bytes. |
| `MOBILEGL_IPC_SERVER_PATH` | path | next to `libMobileGL.so` | Server executable for `fork` |
| `MOBILEGL_IPC_PROBE_TIMEOUT_MS` | 0-60000 | 250 | Before starting a session, probe the configured `unix:`/`tcp://` endpoint; if nothing answers, the EGL/GLX/GBM entry points decline so the system's next GL implementation is used. 0 disables the probe. |
| `MOBILEGL_IPC_RECOVER` | 0, 1 | 1 | After the server dies or the device is lost, let the next `eglCreateContext` / `eglCreate*Surface` / `eglInitialize` start a fresh session. 0 makes the loss final for the process. |
| `MOBILEGL_IPC_CONTROL_TIMEOUT_MS` | 100-600000 | 5000 | How long the client waits for an answer to a surface operation |
| `MOBILEGL_IPC_COLD_START_MS` | 100-600000 | 20000 | The same wait while the server brings its backend up (first surface and first make-current) |
| `MOBILEGL_IPC_PRESENT_CREDIT` | 1-8 | derived from `MOBILEGL_FRAMES_IN_FLIGHT` | Explicit override of how many presents the client may run ahead |
| `MOBILEGL_IPC_REQUIRE_SAME_BUILD` | `1` | off | **env only.** Refuse a server built from a different revision (by default that is a warning) |
## Server process
`libMobileGLServer.so` and embedding apps (see [android-plugin.md](android-plugin.md#embedding-the-server-in-your-own-apk)):
| Variable | Meaning |
|---|---|
| `MOBILEGL_IPC_DIAL=no` | **Required** in the server's environment. It stops the server from acting as a client itself. |
| `MOBILEGL_IPC_ROLE=server` | Marks the server role. `libMobileGLServer.so` sets it itself; an embedding app sets it before loading the library. |
| `MOBILEGL_IPC_ENDPOINT` | Endpoint, when none is given on the command line |
| `MOBILEGL_BACKEND_TYPE` | Optional: pin the server to one backend and refuse clients asking for the other |
| `MOBILEGL_IPC_TOKEN` | Token clients must present |
| `MOBILEGL_IPC_INPROC_MAX_SESSIONS` | Concurrent sessions of an embedded Unix-socket server (default 16, at most 256) |
The server refuses to start (exit code 65) if it inherited `MOBILEGL_TRANSPORT`,
`MOBILEGL_IPC_SERVER_PATH`, `MOBILEGL_IPC_RING_MB` or `MOBILEGL_IPC_STAGE_MB`.
## Linux desktop
| Variable | Meaning |
|---|---|
| `MOBILEGL_CONFIG_FILE`, `MOBILEGL_BACKEND_FILE` | **env only.** Other config files (default `/etc/mobilegl/client.conf`, `/etc/mobilegl/backend`); empty = none |
| `MOBILEGL_DEVICE_DRM_NODE` | Render node reported as MobileGL's EGL device (default `/dev/dri/renderD128`) |
| `MOBILEGL_WAYLAND_DMABUF` | **env only.** `0` presents Wayland windows through `wl_shm` instead of dma-bufs |
| `MOBILEGL_GLX_PRESENT` | **env only.** `readback`, `shm` or `putimage` forces a GLX readback path instead of DRI3/Present |
| `MOBILEGL_X11_DIAL` | **env only.** Override whether MobileGL may open an X11 connection |
| `MOBILEGL_GBM_LIBRARY` | **env only.** Where `mobilegl_gbm.so` loads `libMobileGL.so` from |
| `__EGL_VENDOR_LIBRARY_FILENAMES`, `__GLX_VENDOR_LIBRARY_NAME`, `GBM_BACKEND` | glvnd / libgbm variables that select MobileGL; see [linux.md](linux.md) |
+256
View File
@@ -0,0 +1,256 @@
# 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: `<endpoint>` (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=<pid> listening on <endpoint>` 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=<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` |
| `<libdir>/libGLX_mobilegl.so.0` | symlink to `/opt/mobilegl/lib/libMobileGL.so`; `<libdir>` is the system library directory (`/usr/lib` on Arch, `/usr/lib/<triplet>` on Debian/Ubuntu) |
| `/etc/mobilegl/client.conf` | client settings (below) |
| `/etc/mobilegl/backend` | optional: one line, `DirectGLES` or `DirectVulkan` |
| `<gbm backend dir>/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=<path>` and
`MOBILEGL_BACKEND_FILE=<path>` 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/Disaggregated/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.
+149
View File
@@ -0,0 +1,149 @@
# Troubleshooting
## Logs
The default log level of all builds is INFO: info, warning, error and fatal lines are compiled in.
Debug lines need a build with `MOBILEGL_LOG_ACTIVE_LEVEL=MOBILEGL_LOG_LEVEL_DEBUG`
(Gradle: `-Pmobilegl.logLevel=MOBILEGL_LOG_LEVEL_DEBUG`).
| Setup | Where |
|---|---|
| Android, any app | logcat, tag `MobileGL`: `adb logcat -s MobileGL` |
| FCL embedded (monolith build) | also `/sdcard/MG/latest.log`, if the process may write there |
| Plugin in a launcher (split build) | also `/sdcard/MG/latest.client.log` and `latest.server.log` (the `inproc` backend thread writes the `.server` file) |
| Plugin render server | logcat tags `MobileGL`, `MobileGLServer`, `MobileGLDisplay`; files `mgl.server.log` / `mglwin.server.log` in the app's private files directory |
| Linux, Windows | only the file named by `MOBILEGL_LOG_FILE_PATH`. Split builds write `<name>.client.<ext>` and `<name>.server.<ext>`; a remote server's lines are forwarded into the client's `.server` file. |
Native crashes on Android: `adb logcat -b crash -d`.
Lines worth searching for:
| Line | Meaning |
|---|---|
| `Config: Active backend type set to DirectGLES` / `DirectVulkan` | Settings were read; which backend is used |
| `Config: debug.mobilegl.env sets ...` | A setting came from the Android property |
| `Config: MOBILEGL_TRANSPORT=inproc - ...` / `=spawn - ...` | A split topology was selected |
| `MG_Remote client: spawn ARMED - the server role runs in pid N` | `spawn` with `fork` started its own server |
| `MG_Remote client: control=tcp data=stream server=...` / `control=unix data=shm ...` | Connected to a running server |
| `MG_Remote server: pid=N listening on <endpoint>` | A server is ready |
| `Loaded GL backend library: <name>` | Espryt found the system (or ANGLE) EGL/GLES |
## MobileGL is not used at all
The renderer string does not mention Espryt or Magma.
- **FCL:** check the renderer selection, and that no leftover `debug.mobilegl.*` property or line in
the Environment Variables editor points somewhere else.
- **Windows:** the program loaded the system `opengl32.dll`. Put the copy next to the executable that
creates the GL context (for Java, next to `java.exe`/`javaw.exe`).
- **Linux, system-wide setup:** the log says
`no MobileGL server answers at "<endpoint>" ... declining, so the system's next GL implementation serves this process`.
The server is not running or the endpoint in `client.conf` is wrong. `MOBILEGL_IPC_PROBE_TIMEOUT_MS`
raises the probe's patience (default 250 ms).
- **Linux, GLX:** `__GLX_VENDOR_LIBRARY_NAME=mobilegl` is not set in that process, or
`libGLX_mobilegl.so.0` is not in the system library directory.
## The transport setting has no effect
- No `Config: MOBILEGL_TRANSPORT=...` line: the library was built without the split layer. The
FCL-embedded copy is always built that way; use the plugin or rebuild with
`MOBILEGL_BUILD_DISAGGREGATED=ON`.
- `Config: MOBILEGL_TRANSPORT='unix:...' names a transport ... staying on monolith`: put the endpoint
in `MOBILEGL_IPC_CONTROL` and set `MOBILEGL_TRANSPORT=spawn`.
- The plugin runs monolith in the launcher although you expected `inproc`: the APK was built without
an explicit `-Pmobilegl.buildDisaggregated=ON`, so its launcher environment has no
`MOBILEGL_TRANSPORT`. Rebuild, or add `MOBILEGL_TRANSPORT=inproc` in the launcher's environment
editor.
## Out-of-process rendering on Android
| Message | Cause | Fix |
|---|---|---|
| `Fatal{UnmigratedSurface, "AndroidNativeWindow@P12"} ... until then the spawn surface path is pbuffer/surfaceless only` | A `spawn` client created a window surface with `MOBILEGL_IPC_SURFACE=offscreen`. An Android window cannot cross processes. (The text mentions a development phase; the limitation itself is permanent.) | Use the on-screen display server and `MOBILEGL_IPC_SURFACE=server` ([android-plugin.md](android-plugin.md#out-of-process-rendering-with-a-window)), or use `inproc` |
| `Refuse ServerOwned (NoServerDisplay)` | `MOBILEGL_IPC_SURFACE=server`, but the server is offscreen (the plugin's service, a spawned server, any Linux server) | Start the plugin's server with **On-screen window** ticked |
| `Refuse ServerOwned (NoServerWindow) ... is the display's surface visible?` | The display server's window was not visible when the client created its window surface | Bring the MobileGL display window to the front, then start the game |
| `SurfaceModeMismatch` | The session's first surface was a pbuffer, so the session is offscreen; a later server window surface is refused | The application must create its window surface first |
## Connection refusals
The client logs `MGPipe: peer Refuse{<code>} <detail> ...`:
| Code / detail | Fix |
|---|---|
| `Authentication` `token mismatch` | Set the same `MOBILEGL_IPC_TOKEN` on client and server |
| `WireFingerprint` `wire layout` / `ProtocolVersion` | Client and server come from different source revisions; build both from one commit |
| `BuildFingerprint` `build identity` | Different builds, and `MOBILEGL_IPC_REQUIRE_SAME_BUILD=1` (or `fork`, which always requires the same build). A stale `libMobileGLServer.so` next to the library, or a stale `MOBILEGL_IPC_SERVER_PATH`, causes this too. |
| `Backend` `Hello.backendType disagrees with pinned backend` | The server is pinned to the other backend: by its `MOBILEGL_BACKEND_TYPE`, or, for the on-screen display server, by its first session. Match the client's backend, or restart the server. |
| `Busy` | The server takes no more sessions (`--max-sessions`) |
A warning `compatible wire with different build` means the two ends are different builds of a
compatible protocol. It works, but build both ends from one commit when possible.
## The server does not start
`libMobileGLServer.so` exit codes:
| Code | Cause |
|---|---|
| 64 | `MOBILEGL_IPC_DIAL=no` missing from its environment |
| 65 | It inherited `MOBILEGL_TRANSPORT`, `MOBILEGL_IPC_SERVER_PATH`, `MOBILEGL_IPC_RING_MB` or `MOBILEGL_IPC_STAGE_MB`; unset them |
| 71 | Bad arguments: no endpoint, an unknown option, or `--max-sessions` outside 1-64 or used with TCP or without `--serve` |
| 72 | Could not listen. Also when `MOBILEGL_IPC_TOKEN` is shorter than 16 bytes, or a non-loopback TCP address has no token. The log says `Refuse{Authentication} ...`. |
| 73 | Accepting a connection failed or timed out |
In the plugin's **Render Server** screen:
- `Non-loopback listen requires a token of at least 16 bytes.`: `0.0.0.0` counts as non-loopback.
Tap **Generate token**, or listen on `tcp://127.0.0.1:<port>`.
- `A server is already running; stop it before reconfiguring.`: tap **Stop server** first.
- The on-screen server keeps its first configuration for the life of its process. To change it, stop
it (or force-stop the app) and start again.
## Device loss and hangs
- `MOBILEGL_GPU_HANG_BUDGET_MS` (default 2000) ends a session whose GPU submission runs longer than
that. Slow software rasterizers trip it; set it to `0` there.
- When a server dies or its window goes away, the client's session is lost. With the default
`MOBILEGL_IPC_RECOVER=1`, the application's next context or surface creation starts a fresh
session; contexts from before the loss stay lost.
- A GPU hang caused by one session stalls every GPU user on the device until the kernel resets the GPU
(about 2 s, longer in some cases). This is a hardware/driver property, not something MobileGL can
shorten.
## Rendering problems
- Try the other backend (`MOBILEGL_BACKEND_TYPE`). Backend-specific bugs are common.
- Older or non-conformant programs: `MOBILEGL_RELAXED_SEMANTICS=1`.
- Engines that write persistent buffer maps without flushing them: `MOBILEGL_COHERENT_AS_FLUSH=1`.
- Suspect shader translation: `MOBILEGL_SHADER_CACHE=0` rules out the translation cache.
- Magma on a driver with broken subgroup support: `MOBILEGL_MAGMA_DISABLE_SUBGROUP=1`.
## Platform-specific
**Windows**
- `Failed to open ANGLE libGLESv2.dll` (Espryt): put `libEGL.dll`, `libGLESv2.dll` and
`d3dcompiler_47.dll` from one x64 ANGLE build next to the program.
- Magma finds no device: install your GPU vendor's Vulkan driver; the Vulkan SDK alone has none.
- Crashes inside a third-party overlay's Vulkan layer (often at exit): disable implicit layers
for the program with the Vulkan loader's `VK_LOADER_LAYERS_DISABLE=~implicit~`.
- The build fails with `error C2610` in `MG_Util/Damage/Damage.h`: see the build status note in
[windows.md](windows.md).
**Linux**
- Windows show `presented through wl_shm` instead of `linux-dmabuf shared images`: zero-copy needs a
server that allocates shared images (the Android server) and a compositor with `linux-dmabuf`.
The `wl_shm` path works, but is slower.
- `eglCreateWindowSurface` fails on a GBM display: by design. GBM displays are offscreen in MobileGL.
- Replacing `libMobileGL.so` in place crashes running programs: install to a new file and rename it
over the old one.
**Building**
- `ENABLE_OPT set but SPIR-V tools not found` (or similar) at configure: run
`python3 update_glslang_sources.py` in `3rdparty/glslang`.
- `MOBILEGL_BUILD_DISAGGREGATED=ON but 3rdparty/flatbuffers/include is missing`: run
`git submodule update --init 3rdparty/flatbuffers`.
- Tests run with wrong environments under CMake 4.x: build and test with CMake 3.31.x.
- Gradle builds are slow: `-Pmobilegl.enableLto=OFF` skips ThinLTO (the library is then slower).
+93
View File
@@ -0,0 +1,93 @@
# MobileGL on Windows (WGL `opengl32.dll` drop-in)
On Windows, MobileGL builds as `MobileGL.dll` plus an identical copy named `opengl32.dll`. The copy
exports the WGL entry points (`wglCreateContext`, `wglGetProcAddress`, `wglSwapBuffers`, ...) and
the OpenGL functions. A program that loads `opengl32.dll` from its own directory gets MobileGL
instead of the system OpenGL driver.
## When to use it
- You want to run a Windows OpenGL program (for example Minecraft through GLFW/LWJGL) on MobileGL,
to test MobileGL or to compare its backends on desktop hardware.
When not to use it:
- Split rendering. On Windows only monolith is supported; the server executable is not built for
Windows.
- As a system-wide driver. Do not replace `C:\Windows\System32\opengl32.dll`. The drop-in works per
application directory.
> **Build status on the current branch:** building `feat/disaggregated` with MSVC 19.38
> (Visual Studio 2022 17.8) fails with `error C2610` in `MobileGL/MG_Util/Damage/Damage.h` (a
> defaulted `operator==`) and the same error in several Magma headers. Newer MSVC toolsets were not
> tested. The steps below are the ones the Windows tooling in this repository uses
> (`tools/cts/scripts/wgl_glcts_pipeline.py`); they apply once the tree builds with your compiler.
## Requirements
- Windows x64, Visual Studio 2022 with the Desktop C++ workload, CMake, Python 3, Git.
- The Vulkan SDK, so that CMake's `find_package(Vulkan)` finds it.
- For **Magma**: a Vulkan driver for your GPU (`vulkan-1.dll` and a vendor ICD). The SDK alone
provides no GPU device.
- For **Espryt**: Espryt on Windows always runs on ANGLE. Put matching x64 `libEGL.dll`,
`libGLESv2.dll` and `d3dcompiler_47.dll` from one ANGLE build next to the program.
## Build
Prepare the sources first (submodules and glslang's external sources, see
[building.md](building.md#get-the-sources)). Then, from the repository root:
```powershell
cmake -S . -B C:\mgl-build -G "Visual Studio 17 2022" -A x64 -DMOBILEGL_BUILD_TEST=OFF -DMOBILEGL_BUILD_BENCHMARK=OFF
```
```powershell
cmake --build C:\mgl-build --config Release --target MobileGL
```
Keep the build directory path short: a full build under a deep path can exceed Windows' path length
limit. The result is `C:\mgl-build\Release\MobileGL.dll` and `C:\mgl-build\Release\opengl32.dll`
(with the Ninja generator, both are directly in the build directory).
## Install
Copy `opengl32.dll` into the directory of the executable that creates the GL context. Windows
searches the application's directory before `System32`, so the copy wins.
| Program | Where `opengl32.dll` goes |
|---|---|
| A native `.exe` | next to the `.exe` |
| A Java program (Minecraft) | next to the `java.exe` / `javaw.exe` that runs it, in the runtime's `bin` directory. Use a private copy of the Java runtime so other programs are not affected. |
For Espryt, put the three ANGLE DLLs in the same directory.
## Configure and run
Settings are environment variables ([configuration.md](configuration.md)). Set them in the shell
that starts the program, or in your launcher's environment settings:
```powershell
$env:MOBILEGL_BACKEND_TYPE = "DirectVulkan"
```
```powershell
$env:MOBILEGL_LOG_FILE_PATH = "$PWD\mobilegl.log"
```
MobileGL does not log to the console on Windows, and writes no log file unless
`MOBILEGL_LOG_FILE_PATH` is set.
## How to tell it works
- The program reports `GL_RENDERER` = `Magma (MobileGL Core) (...)` or `Espryt (MobileGL Core) (...)`.
In Minecraft, look at the F3 screen.
- The log file exists and contains `Config: Active backend type set to DirectVulkan`.
- For Espryt, the log has `Loaded GL backend library: libGLESv2.dll` / `libEGL.dll`.
- `tools/wgl-smoke/` has two small test programs (a hand-written WGL bootstrap and a GLFW one) that
render a triangle and check it with `glReadPixels`. Exit code 0 means pass. Build instructions are
in `tools/wgl-smoke/README.md`.
- `tools/cts/scripts/wgl_glcts_pipeline.py` runs the Khronos OpenGL conformance suites against the
drop-in. Its preflight checks that the suite really loaded MobileGL.
If the program still reports your GPU vendor's renderer, it loaded the system `opengl32.dll`:
check that the copy sits next to the executable that actually creates the context.