mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-11-16 06:23:23 +09:00
256 lines
17 KiB
Markdown
256 lines
17 KiB
Markdown
# P15 handoff (living document)
|
||
|
||
The plan and its measurements are in [PLAN-P15.md](PLAN-P15.md); the audit is in
|
||
[PARITY-AUDIT.md](PARITY-AUDIT.md).
|
||
|
||
**Target** (PLAN §0.1, revised by the user 2026-10-08):
|
||
- CPU scenes: FCL presenting-thread CPU at or below dev monolith (Magma <= 1.79, Espryt <= 2.14
|
||
ms/frame), dev measured in the same session, ~5 % noise.
|
||
- GPU scene (Iris + BSL): fps within ~5 % of MobileGlues on both backends.
|
||
|
||
## Latest (2026-10-08, after S0)
|
||
|
||
Every number below carries its profile and passed the validity check (`state.txt`). `cpuhunt` =
|
||
CPU about 1.5/1.25 GHz, GPU 903 MHz (cleanly CPU-bound); interval 0, anland off, daemons stopped.
|
||
|
||
- **The S0 milestone's absolute numbers are superseded.** The perf stack's `msm_performance` caps
|
||
(787 / 1286 / 1075 / 1248 MHz) were clamping the CPU under the pin for some of that session. Only
|
||
same-session deltas from it stand. Measurement now uses `bench_session.sh` and the sampler (PLAN
|
||
§1), and the probe reports only clean fps windows taken before any tracing (`P15PROBE
|
||
trace-start` marker, 25 s settle).
|
||
- **FCL vanilla 1.21.5, cpuhunt, presenting-thread CPU ms/frame:**
|
||
|
||
| build | Espryt | Magma |
|
||
|---|---|---|
|
||
| dev | 1.76 | 1.60 |
|
||
| p15k (S0 + VAO shadow) | 2.10 | 2.03 |
|
||
| p15l (+ vertex-elements dedup) | 2.10 | 2.08 |
|
||
|
||
MobileGlues: 1.58 in an earlier cpuhunt session (needs a same-session re-run with clean windows).
|
||
Floor gap: Espryt +0.34, Magma +0.43 ms.
|
||
- **Driver-call redundancy:**
|
||
- Measured with a diagnostic build that counts every host GLES/EGL call and redundant re-sets
|
||
(`p15diag` worktree, never committed; HostCount.inl and VkCount.cpp).
|
||
- Espryt sent 3717 host calls/frame against MC's ~874: 2118 `glDisableVertexAttribArray`, 378
|
||
enable, 378 divisor, all re-sets.
|
||
- Two fixes, each independently removes the walk churn:
|
||
- b4e209e1: a shadow of each driver VAO (850 calls/frame);
|
||
- c8a5abe5: the client no longer re-creates an identical vertex-elements record on every
|
||
`glBindVertexBuffer`. With the shadow off it takes 3724 down to 1228.
|
||
- CPU gain is small (about 0.1 ms): the calls were cheap in the driver.
|
||
- Magma issues about 380 Vulkan calls/frame with essentially none redundant.
|
||
- **Where Espryt's gap to dev is now:** the driver time equals dev's (0.50 vs 0.48). The excess is
|
||
libMobileGL itself, 0.75 vs 0.32: client half 0.51, backend 0.24, applier 0.02. The client half
|
||
is spread thin (validate, tracker, emitters, residual copy): S1's draw-delta.
|
||
- **Inproc (cpuhunt):**
|
||
- Espryt reaches 306 fps vs MobileGlues about 319; GL thread 1.68 ms, apply thread 1.83 ms of a
|
||
3.27 ms frame.
|
||
- Neither thread is saturated. The apply thread waits in `queueBuffer` on a GPU fence
|
||
(present pacing), and the GL thread waits on the apply thread.
|
||
- `MOBILEGL_IPC_PRESENT_CREDIT=2` changes nothing (300 fps).
|
||
- Beating MobileGlues on inproc needs a present-path change (stage P territory, dropped by the
|
||
user), not CPU work.
|
||
- **S0b:** the fixture `minecraft-1.21.5-vanilla-fcl-in-world` is landed (4e27342e) and is in the
|
||
SSIM gate as `fcl` (32/32 on p15j).
|
||
- **Knobs on FCL:** `adb shell setprop debug.mobilegl.env "MOBILEGL_A=1;MOBILEGL_B=0"` (49c6394d);
|
||
`fclab.sh` takes a 6th field `K=V,K=V`.
|
||
- **MC's AFK frame limit:** `inactivityFpsLimit:"afk"` (MC 1.21.2+) made later probe windows and
|
||
profiles spin in `System.nanoTime`. `fcl_scene.sh` now sets `"minimized"` for bench scenes and
|
||
restores the original options files (`/data/local/tmp/p15-opts-121{5,4}.txt`). Profiles taken
|
||
before this are partly AFK-polluted.
|
||
- **BSL at `gpuhunt`** (CPU 2380/1689 MHz, GPU 578 MHz, all 100 % GPU busy):
|
||
- MobileGlues 53.4 fps;
|
||
- dev Magma 53.2;
|
||
- p15l Magma 51.8 (3 reps, -3.0 % vs MobileGlues: target met, -2.6 % vs dev to bisect);
|
||
- Espryt dev and p15l 48.3 (-9.6 %): the GPU gap, inherited from dev.
|
||
- **Same-session cpuhunt, apply thread:**
|
||
|
||
| build | fps | apply ms/frame |
|
||
|---|---|---|
|
||
| MobileGlues | 329 | CPU 1.55 |
|
||
| Magma inproc p15m | 300 | 1.6 |
|
||
| Espryt inproc | 296 | 1.65-1.8 |
|
||
|
||
- Inproc beats MobileGlues only when the apply thread's CPU is below MobileGlues' (both pay the
|
||
same queueBuffer wait). About 0.3 ms per frame beyond the apply CPU is still unexplained.
|
||
- 500b63ea: per-store reuse of renamed-away wire buffers (Magma apply -0.13 ms).
|
||
- ONE_TIME_SUBMIT: no effect (A/B'd, knob dropped).
|
||
- Magma's driver work is about 2x dev's: vkResetCommandBuffer 0.09 + vkEndCommandBuffer 0.11
|
||
ms/frame.
|
||
- **Inproc serialization found (2026-10-08, p15l cpuhunt profiles):** every blit reached the
|
||
client as `BlitNamedFramebuffer`, a verb with no `MGP_VERB_OP_LIST` row, so
|
||
`ClientVerbIsBarriered` answered barriered and the GL thread quiesced the applier once per frame
|
||
(1.65 ms/frame parked in `WaitForApplyToCatchUp`, plus 0.11 from the following `glBindTexture`).
|
||
The apply thread then idled 0.24 ms parked plus 0.2 ms spinning in `Doorbell::Wait`. e00db8e6
|
||
replaces the inverse and its barriered fallback with a verb -> record-op table
|
||
(`MG_Pipe/VerbRecordOps.h`, one row per verb by static_assert; no knob, user decision).
|
||
- Same-session blab (cpuhunt): Magma inproc 319.6 -> 334.9 fps (+4.8 %), Espryt inproc
|
||
324.4 -> 338.1 (+4.2 %).
|
||
- Wait census (`MOBILEGL_IPC_WAIT_STATS=1`, 7acf3ea8): FCL vanilla inproc waits only on the
|
||
present credit (1.00/frame). BSL waits 7 times per frame on three kWaitApplied rows plus 22
|
||
follow-on quiesces; see [WAIT-AUDIT.md](WAIT-AUDIT.md). User decision applied in 2fbcc224:
|
||
fire-and-forget rows async, inproc creates and PBO readbacks no longer wait, and the dual block
|
||
(`MOBILEGL_IPC_ROLE_SPLIT_STATE`, now default on) lets a barriered verb cost one wait. BSL
|
||
census after it still owed (gpuhunt only; FCL's 1.21.4-Fabric profile exited with signal 34 at
|
||
JVM start on both backends in the last attempt).
|
||
- db915f76: a client parked on the present credit is rung only by the credit (apply-thread
|
||
futex wakes 0.056 ms/frame before).
|
||
- Frames in flight (0b96a3ef): `MOBILEGL_FRAMES_IN_FLIGHT` (default 3) drives Magma's frame
|
||
contexts, Espryt's post-present fence wait and the split present credit max(1, N-2); the Magma
|
||
name is an alias. Default latency unchanged; the FIF A/B is the user's input for the default.
|
||
- Present credit 2 (cens, cpuhunt, all VALID): Magma 309.0 -> 319.9, Espryt 299.1 -> 319.1 fps
|
||
vs MobileGlues 321.9 in the same session; apply CPU 1.40-1.43 vs MobileGlues' GL thread 1.57.
|
||
Credit 2 costs a frame of latency: user decision. The remaining inproc gap is the queueBuffer
|
||
fence wait (GPU-side), not CPU.
|
||
- BSL at cpuhunt (GPU 903 MHz) reaches GPU 95 C and kgsl thermal_pwrlevel 1 (834 MHz cap): the
|
||
census run was INVALID twice and the job was stopped. Run BSL only at gpuhunt.
|
||
- Apply-thread frame (Magma inproc): on-CPU 1.75 (0.2 of it spin), queueBuffer fence wait 1.45,
|
||
parked 0.24 ms.
|
||
- Magma apply on-CPU by verb: draws 0.55 (SetupWireDraw 0.40, of it descriptor binding 0.13),
|
||
present 0.37 (End CB 0.10, queueBuffer CPU 0.08, submit 0.07, wire-store sweep 0.035),
|
||
clear 0.19 (the frame's recording begin 0.09 + pass begin 0.05), buffer writes 0.09, blit
|
||
0.06 (two image views and a VkFramebuffer created and retired per blit).
|
||
- **Device contention, root cause (2026-10-08):** the anland app was relaunched by `fclab.sh`'s own
|
||
end-of-run `monkey` (launchedFromUid=0); its activity takes the foreground back whenever paused,
|
||
so FCL launched over it never reaches the world (RenderDoc job: 0 of 4 worlds). c41dfdda:
|
||
`bench_session.sh start` force-stops it and `stop` relaunches it only if it ran before.
|
||
- **Open user decision:** Espryt's apply thread spends 0.06 ms/frame in `LatchIfGuiltyBeforeApply`
|
||
(glGetGraphicsResetStatus is a kgsl ioctl, queried on every wake). Rate-limiting it to 2 ms
|
||
breaks `ServerLoopEglLatchTest.AGuiltyResetEndsTheSessionBeforeItsNextRecord...`, the designed
|
||
promptness guarantee, so it was reverted. Trading that guarantee for 0.06 ms is the user's call.
|
||
- **CI:** 4a8ecf9a green. 49c6394d: one job failed on a hosted runner's `apt-get update` timeout
|
||
(45 min, infrastructure); the failed job is re-run.
|
||
- **Next:**
|
||
- RenderDoc (rdc GLES layer, remote server stopped): Espryt vs MobileGlues BSL pass structure;
|
||
- S1 (client validate 0.25 ms) and S3 (backend by-handle syncs, descriptor resolve) for the CPU
|
||
floor and the inproc apply thread.
|
||
|
||
## Status (2026-10-08)
|
||
|
||
**Branch:** `p15impl` (worktree `.claude/worktrees/p15`, short path `W:/p15`), pushed to
|
||
`feat/disaggregated`.
|
||
|
||
**S0.1 parity audit:** written.
|
||
|
||
**S0.2, Magma wire non-draw ports:**
|
||
- **M1a, clears become part of the draw pass** (`ClearWireFramebufferInPass`; `PrepareWireDrawPass`
|
||
split out of `SetupWireDraw`):
|
||
- A whole-area clear that begins the pass begins it with a LOAD_OP_CLEAR variant of the pass's
|
||
render pass (`RenderPassEntry::clearVariants`, compatible with the framebuffer).
|
||
- Any other clear (into the open pass, or scissored) is `vkCmdClearAttachments` in the pass. A
|
||
forced pass restart for depth clears (an LRZ hypothesis) was tried and reverted: it cost CPU
|
||
and fixed nothing.
|
||
- Kill switches: `MOBILEGL_MAGMA_INPASS_CLEAR=0`; `MOBILEGL_MAGMA_INPASS_CLEAR_DEFAULT=1` puts
|
||
default-framebuffer clears in the pass too (off by default, see the open item).
|
||
- **Open item: default-framebuffer clears stay standalone.** With them in the pass, the openra
|
||
retrace on Adreno 750 loses its palette-sampled sprites (SSIM 0.956378, deterministic). What was
|
||
measured:
|
||
- Both `vkCmdClearAttachments` and LOAD_OP_CLEAR fail; so does splitting the clear calls.
|
||
- Colour-only or depth-only in the pass, with the other part standalone, passes.
|
||
- Event traces: the first draw flushes the recording (the palette `glTexImage2D`'s upload flush,
|
||
`FlushPendingCommands`) right after the clear. Command order otherwise matches the standalone
|
||
path.
|
||
- Not explained yet. Lavapipe and the InPassClear scenarios on FBOs are clean.
|
||
- Ruled out by reading: a missing ordering edge. Default-framebuffer images get the same
|
||
pass-begin barrier as FBO images (a full ALL_COMMANDS memory barrier, or their transition),
|
||
with their layout tracked per swapchain index through the swapchain object.
|
||
- Still open: the in-pass path commits the freshly acquired swapchain image
|
||
(`DefaultFramebufferWriteIndex(true)` in `PrepareWireDrawPass`) at the clear; check whether
|
||
the standalone path's clear and the in-pass clear address the same index on the trace route.
|
||
- Cost of leaving it: small for FCL (MC renders into its own FBO and blits to the default one).
|
||
- **M2, draw-pass cache across frames:** one cache, entries pruned when their image epochs move,
|
||
destroyed after their last submit. Kill switch: `MOBILEGL_MAGMA_PASS_CACHE=0`.
|
||
- **M3, blit descriptor sets:** one pool per frame slot.
|
||
- **M9, converted vertex streams** (wrongly marked "have" in the first audit). The wire arm
|
||
re-converted the whole remaining stream on every draw: Sodium's multi-draws over one arena cost
|
||
49 ms/frame of CPU on BSL (19.5 fps against dev's 64.7).
|
||
- Now one conversion per (store content serial, layout), kept across frames in
|
||
`VkBufferManager` (`LookupConvertedStream` / `StoreConvertedStream`).
|
||
- Each entry owns a vertex buffer. Bounded at 192 MB (least recently used first); entries idle
|
||
for 256 frames go. A replaced buffer is released when its frame slot comes round.
|
||
- The content serial is process-wide monotonic and moves on every host write, respecify,
|
||
GPU-write mark, shader-writable bind and donation.
|
||
- Kill switch: `MOBILEGL_MAGMA_VERTEX_CONVERSION_CACHE=0`.
|
||
- **M4, write renaming.** A busy `glBufferSubData` used to take a staged copy, which ended the
|
||
render pass with two full barriers (FCL: ~5.6 per frame, all on one 288-byte store). Now:
|
||
- A store of 64 KB or less keeps a host shadow (64 MB budget), from respecify on.
|
||
- A busy write into a shadowed store gives it a fresh VkBuffer filled from the shadow and
|
||
releases the old one like a glBufferData orphan. No copy is recorded and the pass stays open.
|
||
- GPU-written marks, shader-writable binds, donation and import drop the shadow.
|
||
- `ReadWireBuffer` serves shadowed stores from the shadow, so no host wait.
|
||
- Kill switch: `MOBILEGL_MAGMA_WRITE_RENAME=0`.
|
||
- **Tests:**
|
||
- `InPassClearScenario`: 4 cases, monolith and split, both backends.
|
||
- `ConvertedVertexStreamScenario`: 3 cases (reuse, same-frame sub-data, cross-frame sub-data).
|
||
Red-once: a lookup that ignores the content serial turns both sub-data cases red.
|
||
- `WriteRenameScenario`: 4 cases (order and carry, one-byte writes, uniform and index buffers,
|
||
multi-frame). Red-once: a rename that skips the shadow fill turns all 4 red, which also
|
||
proves the rename engages.
|
||
- Red-once checks: the colour index and the scissor (2 cases red); the depth clear-load bit
|
||
(depth/stencil case red).
|
||
- Syncval gates green (cache 7/7, run-ahead 14/14), plus the InPassClear cases under syncval.
|
||
- **Gates:**
|
||
- SSIM 24/24 (openra, rd12, bsl × 4 arms × 2 backends) on `p15e`.
|
||
- Host unit and integration-gpu green, apart from the known environmental `ServerSpawn` /
|
||
`FuzzArm2` / `PeerLatch` / spawn ColdStart failures.
|
||
- `CompositorRecovery` flakes under -j16 and passes alone 3/3.
|
||
|
||
**S0 milestone (one device session, FCL, interval 0, 2 reps; `runs/ms-s0i`):**
|
||
|
||
| scene | build | Magma CPU ms/frame | Magma fps | Espryt CPU ms/frame | Espryt fps | GPU busy |
|
||
|---|---|---|---|---|---|---|
|
||
| vanilla 1.21.5 (CPU) | fin | 3.27 | 170 | 2.81 | 190 | 12-15 % |
|
||
| | p15h (S0 without M4) | 2.59 | 191 | - | - | 15 % |
|
||
| | p15i (S0) | 2.56 | 190 | 2.77 | 193 | 13-15 % |
|
||
| | dev | 1.79 (n=1) | 234 | 2.14 | 218 | 14 % |
|
||
| | MobileGlues | 1.59 | 256 | | | 15 % |
|
||
| BSL (GPU) | p15i | 7.26 | 77.2 | 7.61 | 70.6 | 99 % |
|
||
| | dev | 13.85 | 66.1 | 7.17 | 70.5 | 85-100 % |
|
||
| | MobileGlues | 6.25 | 78.2 | | | 98-100 % |
|
||
|
||
(BSL before the converted-stream cache: Magma 19.5 fps at 26 % GPU busy, 49 ms CPU/frame.)
|
||
|
||
- Espryt's `base` lib (p15plan, unchanged) read 2.84 in the same session: the earlier "p15e
|
||
Espryt regression" was session variance. S0 does not touch Espryt.
|
||
- **Target status.** Magma BSL is met (within 1.3 % of MobileGlues, GPU-bound). Espryt BSL is
|
||
GPU-bound at 99 % and 10 % below MobileGlues, same as dev: a GPU-side gap still to explain (the
|
||
load/store and clear audit of PLAN §0.1). CPU scene: Magma 2.56 vs MobileGlues 1.59, Espryt 2.77.
|
||
- **Where Magma's CPU goes now** (p15i profile, `runs/prof-hi`, ms/frame inclusive):
|
||
`VerbChannel::Port` 0.85, `OnDrawVbo` 0.62, `SetupWireDraw` 0.46, `Present` 0.32,
|
||
`MGPipeValidateForVerb` 0.27, in-pass clears 0.18. The draw path is S1-S3's; S0's non-draw ports
|
||
are done. Proof that M4 engages: `StagedWireRangeCopy` 0.067 is gone and `RenameBusyWireStore`
|
||
costs 0.048 (a VMA allocation per rename; a recycle pool could take most of it).
|
||
|
||
**Stage P:** dropped for the target.
|
||
|
||
## Harness
|
||
|
||
| purpose | where |
|
||
|---|---|
|
||
| FCL probes and reducers | `tools/device_bench/disagg/` (README "P15 FCL probes") |
|
||
| FCL milestone (CPU + BSL scenes, fin / new / dev / MobileGlues) | scratchpad `p15/fcl_milestone.sh`, `fcl_scene.sh`, `fclab.sh`, `fclsum.py` |
|
||
| SSIM gate (openra, rd12, bsl × 4 arms × 2 backends) | scratchpad `p15/ssim_gate.sh`; `matrix_p14.sh` now takes `PREP_WLS` and a `bsl` workload |
|
||
| host CI-shaped build (WSL archlinux) | `~/mgl-p15-ci31`, scratchpad `p15/wsl_ci.sh`, `w_suite.sh`, `w_syncval.sh`, `w_sv_one.sh`, `w_one.sh` |
|
||
| trace APK builds | scratchpad `build_trace.sh W:/p15 .<tag>` (rerun once on the `IncrementalSplitterRunnable` failure) |
|
||
|
||
**Host-build trap:** editing a header while ninja is compiling leaves objects newer than the header
|
||
but built from its old text. This happened once and gave a `bad_alloc` in every scenario. Never
|
||
edit sources during a build; `touch` the edited headers if in doubt.
|
||
|
||
**Device facts:**
|
||
- Clocks: CPU 2035.2 MHz on policies 2/5/7 and 1574.4 MHz on policy0, GPU 680 MHz (new default since
|
||
2026-10-08, pending the soak). Pinned per session by `bench_session.sh`, which every `devjob.sh`
|
||
job runs.
|
||
- Frequency daemons are stopped only inside a bench session: `thermal-engine`, `perf2-hal-1-0`,
|
||
`vendor.perfservice`, `performance` and `hyperschedule_hal_service`. Their prior state is in
|
||
`/data/local/tmp/p15-bench-session` while a session is open, and they are restarted and verified
|
||
on exit. If that file exists with no job running, a session was left open: run
|
||
`bench_session.sh stop`.
|
||
- The perf stack's game-mode caps in `/sys/kernel/msm_performance/parameters/cpu_max_freq` survive
|
||
its services and silently override the pin. The session releases them.
|
||
- FCL's original lib md5 is `00c09f0a…`.
|
||
- MC 1.21.5 options md5 is `93b3f708…`; it is backed up at `/data/local/tmp/p15-options.txt`.
|
||
- Iris properties are backed up at `/data/local/tmp/p15-iris.properties`.
|
||
- `fcl_scene.sh restore` restores Iris; `fcl_p14.sh restore` restores the lib and `config.json`.
|
||
- The anland desktop is stopped during runs and restarted afterwards.
|