- CONTRACT-P5E §2.3. EmitAndWaitTails now switches on the generated WaitClass column
when RunAheadArmed(): kWaitNone and kWaitPresent return at the publish, kWaitApplied
waits and then releases the fill pins, and a reply row is unchanged. It REPLACES the
BatchWaits skip rather than layering on it - the batch rule reads the call CLASS and
this one reads what an apply READS, and the server's MGPipeBarriered reads the second,
so two rules layered is two halves of the wire disagreeing about whether the client is
parked.
- §1: RunAheadArmed() = barrierArmed && Ipc.RunAhead && Caps().HasCap(kCapRunAheadApply),
latched at the first caps adoption after Start and demotable but never promotable - a
record already published without a wait cannot be un-published. A knob set against a
server that does not publish the bit logs once and runs lockstep.
- §2.4: MGPPresent::FrameSerial is minted client-side, 1-based, by AcquirePresentCredit,
which pays the credit BEFORE the encode (a client that encoded first would hold a
SEG_CMD reservation across the park). ServerVerbSink::OnPresent returns one credit per
swap through ServerSession::ReturnPresentCredit, which has had no production caller
since v1 wrote it; serial 0 on a run-ahead server is Fatal{ProtocolCorruption,
"Present.FrameSerial"}. The stats line and the wire ledger gain credit-waits.
- §2.5, the drains and the forced waits: every wait exit drains SEG_EVENT (the applied
wait, the present-ack wait, the cmd-space wait and - through a hook the session lends
the encoder - the stage bell); glFinish becomes WaitForApplyToCatchUp, since "the
commands issued so far have completed" is a promise a queue nobody waits on can break;
every Server* EGL forwarder waits first, because the control mailbox is pumped BETWEEN
drain batches and a make-current landing between two run-ahead records is not a wrong
pixel but a wrong everything. ServerSwapEGLBuffers is not on that list: present is the
swap.
- §2.5 / ruling 15 (ID-93): resource_subdata's BUFFER half stops waiting. The row keeps
kReplySlot, the server still posts, and the slot is never read - which is what makes
the 64 KB persistent-map push fire-and-forget instead of a hidden round trip per block.
MGPipeSubDataWantsItsReply is the one predicate, read by the route and by the emit
table, so the two cannot disagree about whether a wait is owed. The texture half keeps
its reply: DrainTextureSubData clears a level's dirty flag on the accepted answer.
- §3: gPipeInputs becomes server-role memory for unbarriered records. The tracker walk
and the emitters still run - they produce records - but the serial bump, the verb
stamp, the server-stamp withdrawal and the whole of step 4 do not, and
MGPipeNoteFrontendMutation / MGPipeLeaveVerb return instead of writing. `fillOwed` is
a separate name from `barriered` on purpose: the red-once is that one line.
- §3.3: MGPipeInputUnfreshRead's counter becomes an UNCONDITIONAL abort under an
unbarriered record, knob or no knob, because the value is torn or stale by
construction and there is nothing for a counter to count. The marker gains its reason
so the lane's allowlist can tell the two apart.
- §3.5: RefusePipeInputsTouchWhileApplierOwnsIt is re-derived - under run-ahead the rule
is "not at all unless you are the barriered fill", and the BatchWaits early return
goes with the argument that justified it.
- §2.3's ReleaseFillPins drops the four O-class SharedPtr rows once a barriered apply
returns: under lockstep the next fill overwrote them within a verb, under run-ahead a
pinned VAO can make the apply thread its last owner a frame later.
- §2.7 / ruling 13 (ID-91): the deferred-destroy queue stays live, and an enqueue from
an UNBARRIERED apply is now a named finding - loud once, Fatal under strict.
- NAMED DEVIATION from §3.1: the identity/liveness pair is still written on every verb.
It is not a field of the residual model - no ownership row owns it, no record carries
it, no server stamp can answer it - it is "does this process still have a GL context",
which only this thread knows and every null-context guard reads.
- Inert until the integration commit: RunAheadArmed() is false while
kMGPipeP5eRunAheadReady is, so every branch above answers as it did before.
fff9d639 - the DirectVulkan resident-buffer ordering and coherent trace-copy fix for Minecraft 26.3-rc-3 - into feat/disaggregated
MobileGL
A desktop OpenGL implementation
MobileGL is a free and open-source project that implements a desktop OpenGL API. The goal is to provide a complete desktop OpenGL implementation with a state management layer and multi-backend support.
Note
Status: In development. Parts of the codebase are incomplete. Current short-term target: OpenGL 4.2 (Core Profile).
Project positioning
MobileGL is an implementation of a desktop OpenGL library. It aims to provide:
- Full OpenGL state management.
- A front-end that exposes OpenGL functions.
- Multiple independent backend implementations, where each backend targets a specific graphics API and remains fully isolated from others.
This project is intended as an implementation/translation layer.
Key components
The repository is organized into following top-level modules:
- MG_State — state tracking and management logic for Graphics APIs.
- MG_Impl — front-end implementations of Graphics APIs that interact with
MG_StateandMG_Backend. - MG_Backend — per-backend translation layer that maps front-end Graphics APIs' semantics and state into concrete backend API calls (e.g. OpenGL ES, Vulkan).
- MG_Util and other utility modules.
Third-party components
MobileGL reuses several open-source projects:
- SPIRV-Cross by KhronosGroup - Apache License 2.0: github
- glslang by KhronosGroup - Various Licenses: github
- DiligentCore by Diligent Graphics - Apache License 2.0: github
- flat_hash_map by Malte Skarupke - Boost Software License 1.0: github
Refer to each component's repository for exact license texts. Any bundled third-party code in this repository is included under the upstream project's license.
Compatibility & target
- Short-term target:
OpenGL 4.2 (Core Profile). - Current development focus:
- Performance improvement
MG_StateandMG_ImplforOpenGL 4.2 (Core Profile)Direct (Vulkan)backendDirect (OpenGL ES)backend
Build Instructions
We currently provide no releases and no precompiled binaries.
If you want to try the project right now, you’ll need to build it yourself:
-
Clone the repository:
git clone https://github.com/MobileGL-Dev/MobileGL.git -
Initialize and update all submodules recursively:
git submodule update --init --recursive -
Follow glslang’s own documentation for its required initiation.
-
Configure and build the project with CMake:
cmake -B build cmake --build buildor do it in a modern way:
cmake -S . -B build -G Ninja -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ cmake --build buildAlternatively, you can use platform-specific build commands as needed.
Build For macOS
On macOS, MobileGL can be built as a dylib that exposes the normal OpenGL/CGL/NSOpenGL entry points and routes them to the DirectVulkan backend. This is useful for running applications such as Minecraft through their stock GLFW/LWJGL OpenGL path while MobileGL is injected before context creation.
Prerequisites:
- macOS with Clang and Ninja.
- Vulkan loader and MoltenVK installed. With Homebrew, the MoltenVK ICD is commonly located at
/opt/homebrew/etc/vulkan/icd.d/MoltenVK_icd.json.
Configure and build:
cmake -S . -B build-macos-magma \
-G Ninja \
-DCMAKE_BUILD_TYPE=Release \
-DMOBILEGL_BACKEND_TYPE=DirectVulkan \
-DMOBILEGL_BUILD_TEST=OFF \
-DMOBILEGL_BUILD_BENCHMARK=OFF
cmake --build build-macos-magma --target MobileGL -j8
The dylib will be generated at:
build-macos-magma/libMobileGL.dylib
To run Minecraft by MobileGL from a launcher like PrismLauncher, keep the stock LWJGL/GLFW natives and add a wrapper command to the instance settings:
env DYLD_INSERT_LIBRARIES=/absolute/path/to/MobileGL/build-macos-magma/libMobileGL.dylib MOBILEGL_BACKEND_TYPE=DirectVulkan VK_ICD_FILENAMES=/opt/homebrew/etc/vulkan/icd.d/MoltenVK_icd.json
Also make sure the JVM arguments include:
-XstartOnFirstThread
DYLD_INSERT_LIBRARIES must be active before GLFW creates its OpenGL context. After startup, the Minecraft F3 screen should report MobileGL and the Direct (Vulkan) backend if the injection worked.
Build Options
| Option | Description | Default |
|---|---|---|
MOBILEGL_BUILD_TEST |
Build MobileGL tests (requires Clang) | ON |
MOBILEGL_BUILD_BENCHMARK |
Build MobileGL benchmarks (requires Clang) | ON |
MOBILEGL_FORCE_RELEASE_OPT |
Enable O3 and LTO in Debug build | ON |
MOBILEGL_ENABLE_TRACY |
Enable Tracy profiler for performance analysis | OFF |
Notes:
- The project requires C++23.
MG_TestandMG_Benchmarkcan only be built with Clang, not GCC. To enforce Clang, add-DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++to your command.- On Android, tests and benchmarks are always disabled.
Environment Variables
MobileGL supports runtime configuration via environment variables.
Supported Keys
| Variable | Description | Allowed Values | Default |
|---|---|---|---|
MOBILEGL_BACKEND_TYPE |
Select active backend implementation at startup. | DirectGLES, DirectVulkan |
DirectGLES |
MOBILEGL_DISABLE_TIMERQUERY |
Disable GPU timer-query exposure and use. | 0, 1 |
0 |
MOBILEGL_ESPRYT_USE_ANGLE |
Load ANGLE EGL/GLES libraries. | 0, 1 |
0 |
MOBILEGL_MAGMA_DISABLE_SUBGROUP |
Disable Vulkan shader subgroup support. | 0, 1 |
0 |
MOBILEGL_ADVERTISE_FP64 |
Advertise GL_ARB_gpu_shader_fp64. GLSL double/dvec/dmat compile and run either way - they are narrowed to 32 bits - so this only changes whether an application is told it has 64-bit precision, which it does not. |
0, 1 |
0 |
MOBILEGL_MAGMA_R11G11B10F_FALLBACK |
Use Magma's R11G11B10F format fallback. | 0, 1 |
0 |
MOBILEGL_MAGMA_FRAMESINFLIGHT |
Set Magma frames in flight. | Integer 1–64 |
3 |
MOBILEGL_ESPRYT_AVOID_SAMPLER_MIPMAP_MIN_FILTER |
Avoid sampler mipmap minification filters. | 0, 1 |
0 |
MOBILEGL_COHERENT_AS_FLUSH |
Treat persistent GL_MAP_FLUSH_EXPLICIT_BIT maps as coherent (app-compat for engines like Flywheel that never flush them). |
0, 1 |
0 |
MOBILEGL_ESPRYT_FORCE_DS_READBACK_EMULATION |
Always emulate depth/stencil glReadPixels/glGetTexImage by shader sampling on Espryt, instead of using the driver's own depth/stencil readback where it has one. |
0, 1 |
0 |
VK_ICD_FILENAMES |
Select the Vulkan ICD used by the Vulkan loader. | Path to an ICD JSON file | Loader default |
License
This project is distributed under GNU LGPL v3.0. See the LICENSE file in the repository for detailed information.