From 7aa958fbc994f84ab5f60ea1c478eb2a51176295 Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Tue, 11 Aug 2026 21:43:56 -0400 Subject: [PATCH 1/2] [Docs] (tools/trace_replay): reproduce the Android DirectGLES lane on Linux, and pin improved-transparency below MobileGL --- tools/trace_replay/README.md | 73 ++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/tools/trace_replay/README.md b/tools/trace_replay/README.md index 1c4d8950..bf6727f2 100644 --- a/tools/trace_replay/README.md +++ b/tools/trace_replay/README.md @@ -251,3 +251,76 @@ process-local. For cases registered with `coherent_as_flush` (Flywheel-style unflushed persistent maps, e.g. the Create fixtures), pass `--ez coherent_as_flush true` so the replay runs with `MOBILEGL_COHERENT_AS_FLUSH=1`. + +## Reproducing the Android DirectGLES lane on Linux (ANGLE on lavapipe) + +The APK workflow's DirectGLES lane is not the same stack as the Linux one, which +is why a case can be green here and red there: + +| lane | stack | +| --- | --- | +| Linux `Test` retrace, DirectGLES | Espryt -> Mesa GLES -> llvmpipe | +| Android `APK` retrace, DirectGLES | Espryt -> **ANGLE** -> Mesa Vulkan (lavapipe) | +| Android `APK` retrace, DirectVulkan | Magma -> lavapipe (no ANGLE) | + +Only the Android DirectGLES lane puts ANGLE in the middle, so an ANGLE +translation difference shows up in exactly one of the six combinations. That +stack can be reproduced on Linux without an emulator, which is far faster to +iterate on than a CI round trip. The Android emulator SDK ships a glibc ANGLE: + +```sh +ANGLE=$ANDROID_SDK_ROOT/emulator/lib64/gles_angle +mkdir -p ~/angle-farm && cd ~/angle-farm +# MobileGL dlopens these two names; ANGLE's own libEGL then dlopens the +# unsuffixed libGLESv2.so from the same directory - without that symlink it +# loads a truncated entry-point table and dies on a missing EGL function. +ln -sf $ANGLE/libEGL.so libEGL_angle.so +ln -sf $ANGLE/libGLESv2.so libGLESv2_angle.so +ln -sf $ANGLE/libEGL.so libEGL.so +ln -sf $ANGLE/libGLESv2.so libGLESv2.so +ln -sf $ANGLE/libvulkan.so.1 libvulkan.so.1 # else eglInitialize fails + +MOBILEGL_USE_ANGLE=1 \ +LD_LIBRARY_PATH=~/angle-farm:/path/to/build/ \ +VK_ICD_FILENAMES=/usr/share/vulkan/icd.d/lvp_icd.json \ +ANGLE_DEFAULT_PLATFORM=vulkan \ + ./mobilegl_trace_replay --trace trace.trace --golden golden.png \ + --target-call N --width 854 --height 480 --backend DirectGLES \ + --output outdir --pbuffer-surface +``` + +`ANGLE_DEFAULT_PLATFORM=vulkan` is required: ANGLE otherwise picks its OpenGL +backend and you get `ANGLE (Mesa, llvmpipe ..., OpenGL 4.6 (Core Profile))` +instead of the CI-shaped `ANGLE (Mesa, Vulkan 1.x (llvmpipe ...))`. Check +`MOBILEGL_TRACE_GL_RENDERER` in `outdir/retrace.log` before trusting a result. +Run the binary directly rather than through `ctest`, whose `ENVIRONMENT` +property overrides these variables. Build with clang, not gcc: gcc rejects +`GLXImpl.cpp` under `-Wchanges-meaning`. + +### improved-transparency-minecraft-26.3 on the ANGLE lane + +This case has never passed on the Android DirectGLES lane. It renders correctly +everywhere else, including Android DirectVulkan on the same emulator. The +rendered frame loses the whole translucent layer - clouds and water are absent +while opaque geometry is pixel-exact - so the compositing chain never receives +the translucent content rather than blending it wrongly. + +It is not a MobileGL defect. Replaying the fixture through the recipe above and +through Mesa GLES, with a `MOBILEGL_LOG_LEVEL_DEBUG` build, gives +SSIM 1.000000 on Mesa and 0.970127 on ANGLE while MobileGL issues a +**byte-identical GL call stream** - 2698222 calls, same names, same order, same +arguments, the only difference being how often the app polls +`glClientWaitSync`. Both drivers are asked for exactly the same thing and +disagree about the pixels, so the divergence is below MobileGL, in ANGLE's +GLES-to-Vulkan translation (or in something we emit that is legal but +underspecified and the two implementations resolve differently). + +Do not "fix" this by editing the DirectGLES blend or draw-buffer paths. Two +candidates were tested and ruled out: per-attachment blend equations are both +recorded and applied correctly on ANGLE (`glBlendEquationSeparatei` with GL_MAX +on one attachment reads back as GL_MAX and rasterizes as GL_MAX), and the GLES +draw-buffer slot restriction is already handled by +`BackendFramebufferObject::RecomputeBackendColorSlots`. Narrowing this further +means bisecting inside the frame to find which GLES construct ANGLE mistranslates; +the accommodation, once known, belongs with the existing +`--avoid-angle-llvmpipe-*` flags rather than in shared backend code. From 22859b09581d73d34c931b89eed71c273df47721 Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Tue, 11 Aug 2026 21:55:40 -0400 Subject: [PATCH 2/2] [Docs] (tools/trace_replay): localise the improved-transparency ANGLE divergence to the OIT composite --- tools/trace_replay/README.md | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/tools/trace_replay/README.md b/tools/trace_replay/README.md index bf6727f2..c01f6725 100644 --- a/tools/trace_replay/README.md +++ b/tools/trace_replay/README.md @@ -324,3 +324,33 @@ draw-buffer slot restriction is already handled by means bisecting inside the frame to find which GLES construct ANGLE mistranslates; the accommodation, once known, belongs with the existing `--avoid-angle-llvmpipe-*` flags rather than in shared backend code. + +#### Where in the frame it goes wrong + +Snapshotting the same intra-frame call points on both stacks (`--target-call`, +ten replays in parallel, logs to `/dev/null`) localises it. SSIM against the +golden at each point: + +| target call | what runs there | Mesa | ANGLE | gap | +| --- | --- | --- | --- | --- | +| 2667445 | last opaque draw, into FBO 29 | 0.146477 | 0.000195 | - | +| 2667488 | **OIT composite**: fullscreen triangle, program 50, into FBO 3 | 0.976448 | 0.945409 | 0.031 | +| 2667543 | post draw, program 56 | 0.985380 | 0.954480 | 0.031 | +| 2667595 | post draw, program 64 | 0.985685 | 0.954788 | 0.031 | +| 2667619 | golden point | 1.000000 | 0.970127 | 0.030 | + +The gap opens at the composite and is then constant to four decimal places - +every pass after 2667488 contributes the same increment on both drivers. So +nothing downstream of the composite is implicated, and the GUI/post chain is +fine. The two stacks already disagree at 2667445, before the composite runs, +which points at the translucent accumulation targets the composite samples +rather than at the composite draw itself. + +That is as far as this went. What is still unknown is **which** GLES construct +in the accumulation chain ANGLE translates differently - naming it needs the +intermediate FBO attachments dumped either side of 2667488 on both stacks, +which the replay CLI cannot currently do (it snapshots one framebuffer, not all +attachments). Until it is named there is nothing to put behind an +`--avoid-angle-llvmpipe-*` flag and nothing precise enough to file upstream, so +the fixture stays red on the Android DirectGLES lane rather than being papered +over.