diff --git a/userguide/README.md b/userguide/README.md new file mode 100644 index 00000000..e3529e39 --- /dev/null +++ b/userguide/README.md @@ -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@` (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 | diff --git a/userguide/android-fcl.md b/userguide/android-fcl.md new file mode 100644 index 00000000..ec9add3d --- /dev/null +++ b/userguide/android-fcl.md @@ -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. diff --git a/userguide/android-plugin.md b/userguide/android-plugin.md new file mode 100644 index 00000000..6669e092 --- /dev/null +++ b/userguide/android-plugin.md @@ -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-.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-.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 --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:`. **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=`. +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://:40613` and + `MOBILEGL_IPC_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='' CLASSPATH= app_process / top.mobilegl.plugin.ExternalClientHelper -- [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:,` and `MOBILEGL_IPC_DATA=shm` already set. + +Helper options: `--server ` (default: the APK it was loaded from), `--timeout-ms ` +(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("")` 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:@` (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 @ --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`. diff --git a/userguide/building.md b/userguide/building.md new file mode 100644 index 00000000..2d747d6a --- /dev/null +++ b/userguide/building.md @@ -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 `); 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-.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-.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-.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=`. 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-.apk` (plugin flavor), `MobileGL-plugin-trace-release-.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. diff --git a/userguide/configuration.md b/userguide/configuration.md new file mode 100644 index 00000000..c58fde62 --- /dev/null +++ b/userguide/configuration.md @@ -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 ` (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 `.client` and `.server` 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:`, `unix:@`, `tcp://:` | `fork` | With `spawn`: `fork` launches a private server; the others connect to a running one. (`fd:,` 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) | diff --git a/userguide/linux.md b/userguide/linux.md new file mode 100644 index 00000000..7a995a09 --- /dev/null +++ b/userguide/linux.md @@ -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: `` (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/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. diff --git a/userguide/troubleshooting.md b/userguide/troubleshooting.md new file mode 100644 index 00000000..93fbc50e --- /dev/null +++ b/userguide/troubleshooting.md @@ -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 `.client.` and `.server.`; 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 ` | A server is ready | +| `Loaded GL backend library: ` | 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 "" ... 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 | 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:`. +- `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). diff --git a/userguide/windows.md b/userguide/windows.md new file mode 100644 index 00000000..93c975a7 --- /dev/null +++ b/userguide/windows.md @@ -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.