mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-07 19:58:32 +09:00
[Docs] (tools/trace_replay): name the two generated-ESSL defects that empty the improved-transparency lane on ANGLE
This commit is contained in:
+106
-26
@@ -163,6 +163,33 @@ build-test/tools/trace_replay/mobilegl_trace_replay \
|
||||
--ssim-threshold 0.99
|
||||
```
|
||||
|
||||
## Dumping framebuffer attachments mid-frame
|
||||
|
||||
`--target-call` snapshots one framebuffer. To see *inside* a frame - which
|
||||
intermediate render target a pass actually produced - pass
|
||||
`--dump-fbo-attachments CALL:DIR[:FBO,FBO,...]`, repeatably:
|
||||
|
||||
```sh
|
||||
build-test/tools/trace_replay/mobilegl_trace_replay \
|
||||
--trace trace.trace --golden golden.png --output out --target-call 2667619 \
|
||||
--dump-fbo-attachments 2666231:out/fbos-before \
|
||||
--dump-fbo-attachments 2666232:out/fbos-after
|
||||
```
|
||||
|
||||
At each call boundary it walks every live framebuffer object (or only the named
|
||||
ones), reads back every colour attachment and the depth attachment, and writes
|
||||
`fbo<N>-att<M>.png` / `fbo<N>-depth.png` plus a `manifest.txt` line per
|
||||
attachment recording the attached object, size, internal format, component type
|
||||
and per-channel min/max/mean and a content hash. Attachments are read as floats
|
||||
whatever their storage, so HDR accumulation buffers stay legible in the
|
||||
statistics even though the PNG has to clamp.
|
||||
|
||||
The manifest is the useful part when comparing two drivers: dump the same call
|
||||
on both stacks and `diff`/`paste` the two manifests, and the first attachment
|
||||
whose hash differs names the pass that diverged. Read-side and pixel-pack state
|
||||
is saved and restored, so the replay continues unperturbed; without the flag
|
||||
nothing is installed and the replay is byte-for-byte what it was.
|
||||
|
||||
Run the macOS native-window DirectVulkan retrace matrix and render the same
|
||||
HTML overview shape as CI:
|
||||
|
||||
@@ -305,25 +332,50 @@ 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).
|
||||
**It is two defects in the ESSL we generate, and both are ours.** 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 the *frontend* call stream is byte-identical - 2698222 calls, same
|
||||
names, same order, same arguments, the only difference being how often the app
|
||||
polls `glClientWaitSync`. That comparison is what misled the earlier
|
||||
investigation: it is the app-to-MobileGL direction. It says nothing about the
|
||||
GLES and ESSL MobileGL emits *downwards*, which is where the two lanes part.
|
||||
|
||||
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.
|
||||
Two shaders that MobileGL generates fail to compile on ANGLE and link no
|
||||
program at all, so every draw that uses them is a silent no-op:
|
||||
|
||||
| # | ANGLE compile error | what it is | what disappears |
|
||||
| --- | --- | --- | --- |
|
||||
| A | `0:2: 'GL_EXT_texture_buffer' : extension is not supported` then `'isamplerBuffer' : Illegal use of reserved word` | the cloud vertex shader emits `#extension GL_EXT_texture_buffer : require` and `uniform highp isamplerBuffer CloudFaces` unconditionally | clouds |
|
||||
| B | `'[' : array indexes for fragment outputs must be constant integral expressions` | the OIT coefficient fragment shader declares `layout(location = 0) out highp vec4 coeff[2];` and writes `coeff[attachmentIndex][i]` from a loop | the whole translucent accumulation |
|
||||
|
||||
Neither is an ANGLE mistranslation:
|
||||
|
||||
- **A is a capability gap we do not guard.** Minecraft 26.3 builds clouds
|
||||
entirely from `gl_VertexID` plus `texelFetch` on a buffer texture
|
||||
(`glTexBuffer(GL_TEXTURE_BUFFER, GL_R8I, ...)`). Mesa GLES advertises
|
||||
`GL_EXT_texture_buffer` and `GL_OES_texture_buffer`; this ANGLE advertises
|
||||
neither (142 extensions against Mesa's 162), and MobileGL's reported
|
||||
`GL_MAX_TEXTURE_BUFFER_SIZE` drops to the 65536 default because it cannot
|
||||
query one. We emit the `require` line anyway.
|
||||
- **B is a GLSL ES rule we violate.** Fragment output arrays must be indexed
|
||||
with constant integral expressions; SPIRV-Cross hands us a loop-variable
|
||||
index and Mesa's compiler accepts it, ANGLE does not. Any strict ES driver
|
||||
rejects this shader, so it is not ANGLE-specific in principle - it is
|
||||
Mesa's leniency that hides it on the Linux lane.
|
||||
|
||||
There is therefore **no honest harness accommodation**: no
|
||||
`--avoid-angle-llvmpipe-*` flag can conjure a missing extension or make an
|
||||
illegal shader legal, so the fixture stays red on the Android DirectGLES lane.
|
||||
Fixing it means fixing the emitters - emulating buffer textures where the ES
|
||||
driver has none, and rewriting non-constant fragment-output indexing into a
|
||||
switch over constant indices - which is shared backend work, not harness work.
|
||||
|
||||
Two earlier candidates remain correctly ruled out and should not be re-walked:
|
||||
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`.
|
||||
|
||||
#### Where in the frame it goes wrong
|
||||
|
||||
@@ -346,11 +398,39 @@ 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.
|
||||
#### Which attachment, and which draw
|
||||
|
||||
`--dump-fbo-attachments` on both stacks pins it to a single draw. Comparing the
|
||||
manifests at three call boundaries, over all 30 live framebuffers:
|
||||
|
||||
| call | what has just run | verdict |
|
||||
| --- | --- | --- |
|
||||
| 2665649 | depth blit, before the translucent chain | every OIT target identical on both stacks |
|
||||
| 2666231 | all entity/particle translucent draws done | still identical: FBO 21 att0 (texture 1554, RGBA32F) reads `c1 max=178.98 mean=1.81046` on Mesa against `179 / 1.8105` on ANGLE |
|
||||
| 2666232 | the cloud draw - `glDrawElementsInstancedBaseVertex(count=221706)` with `GL_TEXTURE_BUFFER` bound | Mesa moves to `c1 max=1727.87 mean=70.2899`; **ANGLE does not move at all** |
|
||||
|
||||
At the golden point the same holds for the rest of the chain: texture 1555 (the
|
||||
translucent colour accumulation) has mean 0.105617 on Mesa against 0.00403189 on
|
||||
ANGLE, and texture 1556 (the alpha/coefficient target, FBO 26 attachment 1) is
|
||||
alpha `max=1.34863 mean=0.261387` on Mesa and **identically zero** on ANGLE -
|
||||
never written, because defect B linked no program for that pass. Everything
|
||||
outside the translucent chain matches: the opaque terrain, the block atlas and
|
||||
its mips, and the GUI targets differ only in the last float ULP.
|
||||
|
||||
So the earlier reading of the SSIM bisect was right about the location and wrong
|
||||
about the cause. The composite at 2667488 is innocent; it faithfully composites
|
||||
accumulation buffers that two failed shader compiles left empty.
|
||||
|
||||
Reproduce with:
|
||||
|
||||
```sh
|
||||
for stack in mesa angle; do ... --target-call 2666232 \
|
||||
--dump-fbo-attachments 2666232:out-$stack/fbos ; done
|
||||
paste out-mesa/fbos/manifest.txt out-angle/fbos/manifest.txt
|
||||
```
|
||||
|
||||
and read the compile errors straight out of `mobilegl.log`:
|
||||
|
||||
```sh
|
||||
grep -aE 'Shader compilation failed|linking failed' out-angle/mobilegl.log
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user