mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-11-15 22:13:24 +09:00
Two things land together: the plan and contract draft P6 needs before it starts, and the
working notes that until now existed on exactly one machine.
1. P6's plan and contract draft (neither started; not one line of code)
docs/Disaggregated/P6-SPAWN-PLAN.md the package plan
docs/Disaggregated/P6-CONTRACT-DRAFT.md the contract draft (moves to
MG_Remote/CONTRACT-P6.md when c6 lands)
The three findings that mattered, all written into the documents:
a. P6 is SMALLER than its roadmap cell suggests. The transport primitives are already in
the tree and already tested: SocketDoorbell including hangup detection and the two
lost-wakeup fences (Doorbell.cpp:211-360, four cases in FdPassingTest), SCM_RIGHTS fd
passing, ShmSegment::Adopt, and the SurfaceOp / WindowKind schema already defined in
protocol.fbs. inproc uses a real second shm mapping rather than an alias, and
SessionRings.h:22-28 says that was done so today is not the first time it runs. P6 is
assembly plus a control plane plus a process, not "write a socket transport".
b. The evidence for "P6 is only a transport swap" has EXPIRED. It was bought by P5c, whose
audit found 59 direct memory accesses in a tree that already looked clean. P5e changed
the tree again. So the first package, a6, is a read-only audit that writes no code, and
every contract row that depends on it is marked PENDING a6 rather than decided in
advance.
c. P6 does not get a game running on the phone. ANativeWindow* is meaningless in the child,
and protocol.fbs:186-196's own comment says Android transfers the window out of band -
that band is P12. P6 lands pbuffer / surfaceless / offscreen, which is what HeadlessGL,
the split-equivalent lane and trace replay need, and which is exactly what P6's own exit
gate measures. A real window reaching the server is a named refusal. This gets its own
section because "P6 is done" would otherwise read as "the game runs now".
The contract adds rule G: a role may not name a process-local handle. It extends P5c's
rule E (a role may not name the other role's memory) from memory to identity - EGLDisplay /
EGLSurface / EGLContext / ANativeWindow* / fd number / pid / mapped address either cross as
a token this contract defines or they do not cross, and a descriptor crosses only through
SCM_RIGHTS, where the kernel renumbers it.
What P5e left for P6 gets its own section. The latch semantics are already designed
(ARCHITECTURE.md:504) and are copied unchanged. What is new is that after run-ahead the
client no longer waits while the peer process can DIE: the client holds records that were
published and will never be applied, and Android's freezer suspends the child without
closing anything. So slow and dead must be distinguishable WITHOUT a timeout threshold.
That is P6's only new design; everything else is carriage. It shares a predicate with E1's
outstanding re-specification (ID-122), and the two should be designed together.
Two gates carried over from P5e's lessons: G2 requires integration-spawn's test-name set to
match integration-split's name for name, because a differing set means the lane is quietly
running less; and the arm-proof gate requires every entry to record the child pid and
transport=spawn in its OWN private log - ID-124 established that this kind of false green
really happens, and a spawn lane that actually ran monolith would pass every other gate.
S2's shape must be settled before it is written, and the document says why: E1 is the
cautionary case.
2. The working notes move into the repository (docs/Disaggregated/notes/, 415 .md files)
ROADMAP / CURRENT_STAGE_PROGRESS / MEASUREMENTS cite ruling numbers, audit line numbers and
the provenance of gate figures from ~/w7/notes/, a directory that exists on one machine and
is under no version control. A record cited hundreds of times that lives only locally is not
a record, so it moves in whole, with its directory structure intact.
Only .md moves. Scripts, run logs, census and gate result JSON, patches, APKs and nm dumps
stay where they are: they are evidence DATA rather than documents - large, regenerated per
run, and mostly carrying absolute paths and device serials that mean something only on the
machine that produced them. The mapping and this split are written in
docs/Disaggregated/notes/README.md.
Two exceptions are recorded there too: P6's BRIEF has been promoted to a first-class
document and keeps no copy under notes/, so the two cannot drift apart; and recovered/ holds
material salvaged from failed workflow runs, which may duplicate the like-named phase
directories and is kept because a few of those were the only complete version at the time.
CI's citation lint takes docs/Disaggregated/*.md only (non-recursive), and notes/ is
deliberately outside it: those notes' file:line citations were written against their own
code heads, and resolving them against today's HEAD would fail wholesale - which is not an
error, it is their correct state as a historical record. To check one, use the revision it
declares (--rev).
3. Inconsistencies fixed in passing
- README.md's status line still said P5e was in progress while the stage table already said
closed.
- CURRENT_STAGE_PROGRESS's next-step item 1 still listed the exit gate as pending, though
(a) and (b) were run in the previous commit; rewritten to closed plus the one item that
actually remains (E1).
- ROADMAP's P6 row still had the pre-phase plan in three of its cells; it now carries the
package order, the scope boundary and the two new gates.
Checked with check_doc_citations.py: 125 citations across docs/Disaggregated/*.md, 0 problems.
257 lines
19 KiB
Markdown
257 lines
19 KiB
Markdown
# P5e `fix1` — the by-handle attachment sync on the monolith arm (report v1)
|
||
|
||
Branch `p5e/fix1`, head **`66621767`**, base `44f91c74` (P5e wave-2 integration). Tree
|
||
`~/w7/p5e-fix1`. One commit. Three files: `MG_Backend/DirectGLES/Managers.cpp`,
|
||
`MG_IntegrationTest/CMakeLists.txt`, and a new `MG_IntegrationTest/Scenarios/MonolithAttachmentClearScenario.cpp`.
|
||
|
||
## 1 Root cause
|
||
|
||
The push build has TWO server arms and P5e only repaired one of them at this seam. Since P4a
|
||
(D-C2) the push build resolves framebuffer ATTACHMENTS from the applier record in **both**
|
||
arms — `BackendFramebufferObject::SyncToBackend(const SharedPtr<FramebufferObject>&, target)`
|
||
computes `pushedRecord` gated on `FramebufferSubsystemEnabled()` **alone**, with no transport
|
||
test — and under `Transport == Monolith` that object overload is what `SyncCurrentFBO` →
|
||
`SyncCurrentFBOByRecord` reaches (`DirectGLES.cpp:4304`, the arm below
|
||
`FramebufferRecordArmIsMandatory()`), which is exactly the frame the `hs_err` names. Package fb
|
||
then dropped `SyncAttachmentSurface`'s frontend attachment parameter and pointed its storage sync
|
||
at tx2's `SyncMipmapsToBackendByHandle`, whose contract is "the noted handle routes the body onto
|
||
the record arm". That contract is false on the monolith arm: the record arm inside
|
||
`SyncMipmapsToBackend` is itself selected by `MG_Config::Transport != Monolith` (the texture-VIEW
|
||
test at `Managers.cpp:7397-7412` and `RequireImageBindableStorage`'s re-dirty transition at
|
||
`:7458`, both tx2's ruling-1 arms), so with a null `SharedPtr` and monolith transport control fell
|
||
through to `stateTextureObject->IsTextureView()` and dereferenced null — the SIGSEGV at
|
||
`libMobileGL.so+0x80b57c` in `Lightmap.<init>` → `clearColorTexture` → `glClear`. fb's report §7
|
||
enumerates the push-monolith deltas it took deliberately (the two cross-checks, the N-6 refusal)
|
||
and this one — the storage sync itself — is not among them; it is an unnamed delta, and a fatal
|
||
one. The renderbuffer half of the same function has the same arm error with a milder symptom
|
||
(§3.2). **Everything in the task brief's diagnosis is confirmed**, with one correction: the
|
||
governing arm selection that is missing is not "the monolith arm must not use the record-shaped
|
||
attachment sync" (that is P4a's design and predates ID-81) but "the monolith arm must not use the
|
||
by-handle STORAGE sync", which is the thing ID-81 actually governs.
|
||
|
||
## 2 The fix, and why this one
|
||
|
||
Three edits, all in `Managers.cpp`.
|
||
|
||
**(a) `SyncAttachmentSurface` selects the storage sync by transport, and the monolith arm keeps
|
||
its frontend object** (`Managers.cpp:10068-10230`). The frontend attachment comes back as a
|
||
`const FramebufferAttachmentObject*` parameter — null on the handle arm, `&attachmentObject` from
|
||
the object-form walk — used for **nothing but** the storage sync:
|
||
`Transport != Monolith` → `SyncMipmapsToBackendByHandle(surface.Res)` / RBO
|
||
`SyncToBackendByHandle(surface.Res)` exactly as §5.4 prescribes; `Transport == Monolith` →
|
||
`SyncMipmapsToBackend(attachment->GetTexture())` / `SyncToBackend(attachment->GetRenderbuffer())`,
|
||
which is `f6cfcbd3`'s text token for token. Which arm is taken is decided by `MG_Config::Transport`
|
||
and never inferred from the pointer, so the two statements cannot drift.
|
||
|
||
This is candidate **(a) of the brief, narrowed to the seam ID-81 owns**, plus candidate **(c)** as
|
||
its backstop. I did not take (a) at the FBO sync site (gating `pushedRecord` itself on the
|
||
transport): that would revert P4a's record-driven attach shape in the push-monolith build,
|
||
silently moving draw-buffer decode, the four cross-object masks and the empty-point test back onto
|
||
the frontend for the arm the phone ships on — a far larger behaviour change than the defect, and
|
||
one that would reopen review N-6's hole (attach decided by the record, detach by the frontend).
|
||
I did not take **(b)** (resolve the frontend object from the registry inside
|
||
`SyncMipmapsToBackendByHandle`) because the registry *cannot reliably answer*: `Entry::stateRef`
|
||
is written only by the minting `GetOrCreate(StatePtr)`, and this path adopts its twin through
|
||
`AdoptTwinByHandle`, which sets no `stateRef` — a texture that reaches the driver first as an
|
||
attachment would get a null from `StateForHandle` and we would be back where we started, one
|
||
indirection later. The caller *has* the object; asking it is honest and total.
|
||
|
||
**(b) The null-frontend contract is named and enforced** (`Managers.cpp:6972-7017`).
|
||
`RefuseNullFrontendTextureOffTheHandleArm(entry, notedHandle)` is one shared check behind the new
|
||
`MGB_TEXTURE_NULL_FRONTEND_REFUSED` macro, spelled by all three sync bodies
|
||
(`SyncMipmapsToBackend`, `SyncTextureParamsToBackend`, `SyncBuiltinSamplerToBackend`) in place of
|
||
the bare `!obj && MGB_TEXTURE_HANDLE_ARM_OFF(*this)` test. Semantics: no handle noted → decline as
|
||
before; handle noted **and** (`Transport != Monolith` **and** `TextureResourceSubsystemEnabled()`)
|
||
→ proceed onto the record arm; handle noted on any other arm →
|
||
`Fatal{RoleViolation, "texture-handle-arm"}` and `std::abort()`. So the shape becomes **loudly
|
||
named wherever it is not impossible**, which is what the brief asked for and what makes the
|
||
red-once below a named abort rather than a SIGSEGV. G1: the `#else` branch defines the macro to
|
||
`(true)`, so the pull build's preprocessed text is `(!obj && true)` — identical to before, no call
|
||
added (the D-P rule `MGB_TEXTURE_HANDLE_ARM_OFF` was written for).
|
||
|
||
**(c) The renderbuffer arm is arm-selected too**, for a reason that is a behaviour delta rather
|
||
than a crash: `BackendRenderbufferObject::SyncToBackendByHandle` dereferences nothing, but it
|
||
deliberately drops the `MGB_CTX->RecordError(OutOfMemory, …)` the object form raises, "because on
|
||
this arm there is no application on this side of the wire to report it to". On the push-monolith
|
||
arm there is one, so the monolith arm keeps the object form.
|
||
|
||
## 3 The audit of the same class
|
||
|
||
The class: *a by-handle seam that passes a null/empty frontend object and relies on a record arm
|
||
being selected*. I walked every one in `MG_Backend/DirectGLES` and checked each call site's arm
|
||
selector by hand.
|
||
|
||
**3.1 Defective, fixed.** `BackendTextureObject::SyncMipmapsToBackendByHandle`, single call site
|
||
`SyncAttachmentSurface` (`Managers.cpp:10102`) — the crash.
|
||
|
||
**3.2 Same arm error, milder, fixed.** `BackendRenderbufferObject::SyncToBackendByHandle`, single
|
||
call site the same function (`Managers.cpp:10201`) — §2(c) above.
|
||
|
||
**3.3 Checked and correct (transport-gated at every call site).**
|
||
|
||
| seam | call sites | selector |
|
||
|---|---|---|
|
||
| `TextureImpl::SyncTextureToBackendByHandle` (passes `kNoFrontendTexture` to all three sync bodies) | `DirectGLES.cpp:2950, 2958` (fb's two attachment lists) | `FramebufferRecordArmIsMandatory()` at `:3333` |
|
||
| | `:3230, 3243` (the unit work list) | `UnitTexturesByHandle()` at `:2821` |
|
||
| | `:3669` (image bind) | reached only from the record overload, selected at `:3824` |
|
||
| | `:10242, 10249` (blit expressed as a copy) | the record overload, selected at `:10303` / `:10378` |
|
||
| | `:11453, 11570, 11801` (CopyTex ×2, GenerateMipmap) | each its own `Transport != Monolith` |
|
||
| | `Managers.cpp:7125` (a view's storage owner) | inside `SyncTextureViewToBackendByRecord`, itself behind `:7397` |
|
||
| `BackendFramebufferObject::SyncToBackendByHandle` / `SyncReadBufferToBackendByHandle` | `DirectGLES.cpp:4265, 4267, 5905` | `FramebufferRecordArmIsMandatory()`; bodies are record-only and hold no frontend object |
|
||
| `BackendProgramObjectImpl::SyncToBackendByHandle` | `DirectGLES.cpp:5692` via `SyncCurrentProgramByHandle` | `ProgramHandleArm()`; it is a SEPARATE body from the object head, not the object body fed a null |
|
||
| `RequireImageBindableStorageByHandle` | `Managers.cpp:7459` | `:7458`, record-only body |
|
||
| `SyncTextureViewToBackendByRecord` | `Managers.cpp:7450` | takes a possibly-null object and guards it: `if (stateTextureObject) SyncTextureViewToBackend(...)` |
|
||
|
||
**3.4 Checked and correct by a different construction — null-tolerant AND transport-aware bodies.**
|
||
vi's and sb's buffer seams are the model this fix follows and they got it right:
|
||
`IsBufferDrawCleanByHandle(res, resource, nullptr)` makes its one frontend read conditional on
|
||
`askTheObjectWhetherItIsMapped = (Transport == Monolith)` *and* on `frontend != nullptr`, and
|
||
`EnsureBufferResourceForHandle(nullptr, res)` returns `resource->hostBytes` under a transport and
|
||
`bufferObject->MappedData()` only when it has an object. The record-arm callers are
|
||
`VertexInputReadsRecords()`-gated and the push-monolith arm passes the real object
|
||
(`DirectGLES.cpp:1172` vs `:1386`); sb's whole walk is behind `ResolveBufferBindingSubsystemArm()`,
|
||
which returns false under monolith.
|
||
|
||
**3.5 One case named rather than changed.** `Managers.cpp:8650`'s `kNoFrontendBuffer` (the
|
||
buffer-texture backing on the `texBufferByRecord` arm) is keyed on `pushedStorage != nullptr &&
|
||
!MGB_TEXTURE_HANDLE_ARM_OFF(*this)` — on a NOTED HANDLE, not on the transport. It is unreachable
|
||
under monolith only because every writer of that note is transport-gated (§3.3), i.e. it is safe
|
||
by a chain rather than by a statement. It does not crash (the callee is null-tolerant); what it
|
||
would lose on the monolith arm is the live `MappedData()` base for a mapped buffer texture. I left
|
||
it alone rather than widen this fix's blast radius. **Ruling wanted**: whether the handle-note
|
||
should be re-stated as a transport test there, or whether "a handle is only ever noted under a
|
||
transport" is to be made an invariant with a check of its own.
|
||
|
||
**3.6 Not in this class, checked anyway.** `ScopedDetachedTextureFramebufferAttachments`' handle
|
||
arm and `ForEachLive`+`StateForHandle` (`DirectGLES.cpp:11078`) hold no null across a sync;
|
||
Magma's `VulkanRenderer.cpp` is out of scope (ID-90 keeps it lockstep) and untouched.
|
||
|
||
## 4 The coverage hole, and what closes it
|
||
|
||
**The hole is bigger than "no lane entry clears a texture-attached FBO" (ID-107), and it is
|
||
structural.** The gate runs `-L unit` and `-L integration-split`. **Every** `integration-split`
|
||
entry exports `MOBILEGL_TRANSPORT=inproc`, which selects `FramebufferRecordArmIsMandatory()` and
|
||
therefore `SyncToBackendByHandle` — the arm that never enters the object overload at all. And
|
||
every P5-era scenario that *does* clear a texture-attached framebuffer (`F1WireScenario` and its
|
||
family) calls `SplitRuntimeSkipReason()` in `SetUp` and **skips itself the moment the runtime is
|
||
not split**, so the ambient monolith registration of those files runs zero cases. The push build's
|
||
second runtime arm — the one the phone ships on — had no gated entry at all.
|
||
|
||
Measured on this tree, at HEAD's behaviour: `ctest -L integration-gpu` (the ungated monolith lane)
|
||
is **1157 passed / 137 failed of 1294, every failure a DirectGLES SEGFAULT**. The information was
|
||
there; nothing the gate runs looked at it.
|
||
|
||
**What I added.** `Scenarios/MonolithAttachmentClearScenario.cpp`, three cases in the shape the
|
||
existing scenarios use (fixture + attach + `glClear` + `glReadPixels` against pinned channel
|
||
values): a **mutable** 2D texture attachment (`glTexImage2D`, the Lightmap shape, the arm that
|
||
crashed), an **immutable** one (`glTexStorage2D`, so a regression confined to level definition
|
||
cannot account for a green), and a **renderbuffer** (the sibling arm of §2(c)). The assertion is
|
||
the pixel, not "it did not crash": a clear against a driver framebuffer whose attachment point was
|
||
never filled raises `GL_INVALID_FRAMEBUFFER_OPERATION` and writes nothing, which is the silent
|
||
form of the same defect and fails here too.
|
||
|
||
**How it reaches the gate.** A new `DirectGLES.PushMonolithArm.` block in
|
||
`MG_IntegrationTest/CMakeLists.txt`, registered inside `if (MOBILEGL_BUILD_DISAGGREGATED)`, with
|
||
`MOBILEGL_TRANSPORT=monolith` as a ctest ENVIRONMENT property (review N-6's reason, the
|
||
`PersistentMapArm` pair's precedent: a job-level `inproc` export must not be able to turn the
|
||
other-arm lane into a fourth copy of the split one) and `LABELS "integration-gpu;integration-split"`.
|
||
**The `integration-split` label on a monolith entry is a deliberate reading** and the one thing in
|
||
this package an integrator may want to overrule: that label does not mean "runs inproc", it means
|
||
"the set the disaggregation gate runs", which is why the block above it argues that a build without
|
||
`-DMOBILEGL_BUILD_DISAGGREGATED=ON` must match nothing under it — still true, since my block is
|
||
inside that `if`. ID-81 gives the push build two arms; if only one of them is in the gated set,
|
||
"the gate is green" keeps meaning "one of the two arms is green". The alternative, which I did not
|
||
take because it edits shared tooling outside this tree, is to add `-L integration-gpu` (or a third
|
||
label) to `~/w7/notes/tools/p5e_gate.sh`.
|
||
|
||
`integration-split` is now **116** entries (113 + 3). The scenario is also registered ambiently, so
|
||
`DirectGLES.MonolithAttachmentClearScenario.*` and `DirectVulkan.MonolithAttachmentClearScenario.*`
|
||
exist in `integration-gpu` (9 entries in total, all green).
|
||
|
||
### 4.1 Red-once, executed and quoted verbatim
|
||
|
||
**R1 — the arm selection reverted, the named refusal kept.** `SyncAttachmentSurface`'s
|
||
`if (MG_Config::Transport != Monolith)` replaced by `if (true)`; rebuilt.
|
||
`ctest -R 'PushMonolithArm.MonolithAttachmentClearScenario'` → **33% tests passed, 2 tests failed
|
||
out of 3**:
|
||
```
|
||
3457 - DirectGLES.PushMonolithArm.MonolithAttachmentClearScenario.AClearOfAMutableTextureAttachmentReachesItsPixels (Subprocess aborted) integration-gpu integration-split
|
||
3458 - DirectGLES.PushMonolithArm.MonolithAttachmentClearScenario.AClearOfAnImmutableTextureAttachmentReachesItsPixels (Subprocess aborted) integration-gpu integration-split
|
||
```
|
||
and the abort is named, from the entry's own log (`MOBILEGL_LOG_FILE_PATH`, because `MGLOG_F` does
|
||
not reach ctest's captured stdout):
|
||
```
|
||
[09:41:35] [Linux MobileGLIntegra/FATAL]: MGPipe: Fatal{RoleViolation, "texture-handle-arm"} - SyncMipmapsToBackend was handed a NULL frontend texture for handle {15, 0} on an arm that cannot answer without one. The by-handle entries pass null on the strength of a noted handle, and the record arm they rely on is selected by Transport != Monolith AND the texture-resource subsystem bit (CONTRACT-P5E §5.8, ID-81); neither holds here, so the body below would read a frontend object that does not exist
|
||
```
|
||
The renderbuffer case stays green, which is the control: that arm's by-handle form dereferences
|
||
nothing.
|
||
|
||
**R2 — both halves reverted (= HEAD's behaviour), the device's own crash.** The refusal macro
|
||
additionally reverted to `MGB_TEXTURE_HANDLE_ARM_OFF`; rebuilt. Running the case directly:
|
||
```
|
||
2258 Segmentation fault ./MobileGLIntegrationTest --gtest_filter=MonolithAttachmentClearScenario.AClearOfAMutableTextureAttachmentReachesItsPixels
|
||
direct-run exit=139 (139 = SIGSEGV, 134 = SIGABRT)
|
||
```
|
||
and through ctest:
|
||
```
|
||
3457 - DirectGLES.PushMonolithArm.MonolithAttachmentClearScenario.AClearOfAMutableTextureAttachmentReachesItsPixels (SEGFAULT) integration-gpu integration-split
|
||
3458 - DirectGLES.PushMonolithArm.MonolithAttachmentClearScenario.AClearOfAnImmutableTextureAttachmentReachesItsPixels (SEGFAULT) integration-gpu integration-split
|
||
```
|
||
|
||
**R3 — the whole monolith lane, before and after.** Taken at R2's state and again at the restored
|
||
head, same build flags, same machine:
|
||
|
||
| `ctest -L integration-gpu` | result |
|
||
|---|---|
|
||
| HEAD's behaviour (R2) | **1157 / 1294**, 137 failed — 137 SEGFAULT, 0 Failed, 0 aborted, 0 timeout, all `DirectGLES.` |
|
||
| with the fix | **1294 / 1294** |
|
||
|
||
The 137 span **38 distinct scenarios** (36 pre-existing plus my two new prefixes) —
|
||
`OrientationScenario`, `PrimitiveRestartScenario`, `DepthStencilReadbackMatrixScenario`,
|
||
`SnormAttachmentScenario`, `ThreeChannelAttachmentScenario`, `SampledSetStalenessScenario`,
|
||
`PixelStoreSweepScenario`, `FramebufferChurnScenario`, `P4aSeamAuditScenario`,
|
||
`CopyImageLayeredScenario`, `GuiBatchScenario`, … — every one of them a case that renders to or
|
||
clears a framebuffer holding a texture attachment. So the
|
||
regression was never confined to the Lightmap path; it broke the monolith arm wholesale, and no
|
||
gated lane entry touched it.
|
||
|
||
**R4 — restored, markers clean.** No `RED-ONCE` marker survives in the tree (`grep`, checked
|
||
before the commit); the committed diff is the three files in §0.
|
||
|
||
## 5 Gate
|
||
|
||
| step | result |
|
||
|---|---|
|
||
| `build` | rc=0 |
|
||
| `unit` | **2250 / 2250** |
|
||
| `isplit` (`integration-split`) | **116 / 116** (113 + the 3 new entries) |
|
||
| `gens` | all seven rc=0; the one doc-citation line (`CONTRACT-P5.md:524 Core.cpp:39 ambiguous`) is the pre-existing one every package has reported |
|
||
| `one` (the new entries) | **9 / 9** on `MonolithAttachmentClearScenario` (3 `DirectGLES.`, 3 `DirectVulkan.`, 3 `DirectGLES.PushMonolithArm.`) |
|
||
| `integration-gpu` (not a gate step; measured for §4.1 R3) | **1294 / 1294** |
|
||
| `strict` | 7 passed / 109 failed of 116. The 3 new entries pass (they are monolith, so no BARRIER-PULLED row is reachable); the 113 pre-existing entries are unchanged, and cannot be otherwise — every edit in this commit either selects the monolith arm or, under a transport, returns exactly what the pre-fix expression returned. No `Fatal{UnmigratedPipeInput, …}` marker moved. |
|
||
|
||
## 6 What I could not settle
|
||
|
||
1. **The `integration-split` label on a monolith lane** (§4). My reading is that the label names
|
||
the gate's set, not the transport, and that ID-81's second arm has nowhere else to live. The
|
||
clean alternative is a gate-script change (`-L integration-gpu`, or a new `integration-arms`
|
||
label) in `~/w7/notes/tools/p5e_gate.sh`, which is outside this worktree and shared with every
|
||
other package, so I did not touch it. **Integrator's call.**
|
||
2. **The 137 monolith SEGFAULTs were latent for the whole of wave 2** and no package's gate could
|
||
have seen them, because none of `build`/`unit`/`isplit`/`strict`/`gens` runs the monolith
|
||
integration lane. Whether the phase should adopt `integration-gpu` as a standing gate step —
|
||
it is ~10 minutes at `-j 4` — is a phase-level decision, not fix1's.
|
||
3. **`Managers.cpp:8650`'s handle-note-keyed arm** (§3.5): safe by a chain of transport-gated
|
||
writers rather than by its own statement. Left alone deliberately.
|
||
4. **No pull-build (G1) compile.** The gate configures the split flavour only, as fb also
|
||
reported. The argument that the pull build's bytes do not move is textual and I state it rather
|
||
than measure it: `SyncAttachmentSurface` lives inside `#if MOBILEGL_PIPE_PUSH` and is not
|
||
compiled there at all; the new refusal is inside the same `#if`, and its `#else` macro is
|
||
`(true)`, so the three guards' preprocessed text in the pull build is the pre-fix1 text token
|
||
for token. A `-DMOBILEGL_PIPE_PUSH=OFF` configure would settle it.
|
||
5. **No device run.** I have no adb and did not take one. **Request:** one push-monolith run of
|
||
Minecraft 26.3-rc-3 on the Redmi at `66621767` to confirm the startup crash is gone and the
|
||
frame is correct, and — if it is cheap — one inproc run to confirm the split arm is still the
|
||
140-145 fps / 852-draws-per-frame shape ID-107 recorded, since this commit touches a line that
|
||
both arms read (the arm selection itself).
|