mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-11-16 06:23:23 +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.
89 lines
7.5 KiB
Markdown
89 lines
7.5 KiB
Markdown
# BRIEF-P5B — verb migration under inproc, Minecraft-first
|
|
|
|
**Goal, one sentence:** fully separate-thread rendering of REAL workloads as fast as possible —
|
|
every Minecraft trace, and the four A/B trace cases, rendered by the apply thread under
|
|
`MOBILEGL_TRANSPORT=inproc` — by migrating the class-C slots the census measured, in the order
|
|
it measured them, with the inproc lane's abort count and the traces' first-blocker list as the
|
|
only gates (ID-66, ID-68).
|
|
|
|
Read `PACKAGE-PREAMBLE.md` first, then `MobileGL/MG_Remote/CONTRACT-P5B.md` (the wire contract,
|
|
every row of it), then this file.
|
|
|
|
## 1. What c0b landed (head `p5b/c0b`, see `p5b-results/c0b-v1.md`)
|
|
|
|
Every package compiles and links on day one against real headers:
|
|
|
|
- the wire rows: `draw_vbo` grew `kDrawIsIndirect` + the `MGPDrawIndirect` second tail; five
|
|
rows appended at opcodes 72..76 (`bind_shader_image`, `patch_parameter`, `bind_stream_output`,
|
|
`set_storage_block_binding`, `copy_framebuffer_to_texture`); `MGPCopyRegion` grew its two GL
|
|
names; the clear discriminants have one spelling in `MGPipeTypes.h`;
|
|
- the codec: encode/decode arms for every row, tail cross-checks, the name blob;
|
|
- `WireVerbSink` has one method per row (13 new + the widened `OnDrawVbo`), and
|
|
`ServerVerbSink` overrides every one with a `Fatal{UnmigratedVerb, "<GL slot>"}` stub;
|
|
- the client emit table's class-C list is PARTITIONED per package
|
|
(`MGR_UNMIGRATED_{D1,I1,T2,F1,TAIL}_SLOTS`, `kEmittedSlots{D1,I1,T2,F1}`, ownership
|
|
`static_assert`s 19 / 7 / 7 / 11 / 20) so four packages edit four disjoint regions;
|
|
- the stamp rows (`FieldOwnership.def`), the generated tables, the pinned counts, and a
|
|
round-trip unit case per row in `PipeWireCodecTest.cpp`.
|
|
|
|
## 2. The four packages
|
|
|
|
Each package owns its slots, its list and count in `EmitTables.cpp`, its stub bodies in
|
|
`PipeApplier.cpp`, and its cases in `PipeWireCodecTest.cpp` + the integration lane. CONTRACT-P5B
|
|
§2 gives every slot's record fields, sink dispatch and refusal names; §8 the two pre-granted
|
|
edits to c0b's files.
|
|
|
|
| package | measured slots (census entries) | companions (same rows, owned too) | rows | model |
|
|
|---|---|---|---|---|
|
|
| **d1** — indexed / instanced / multi-draw / indirect | `DrawElements` 56 (+ every Minecraft trace), `MultiDrawElementsBaseVertex` 18, `DrawArraysInstancedBaseInstance` 4, `DrawElementsBaseVertex` 4, `MultiDrawArrays` 2, `MultiDrawElements` 2, `DrawArraysInstanced` 2, `MultiDrawElementsIndirect` 2, `MultiDrawArraysIndirectCount` 2, `DrawElementsInstancedBaseVertex` (trace) | `DrawElementsInstanced`, `DrawElementsInstancedBaseInstance`, `DrawElementsInstancedBaseVertexBaseInstance`, `DrawRangeElements`, `DrawRangeElementsBaseVertex`, `MultiDrawArraysIndirect`, `MultiDrawElementsIndirectCount`, `DrawArraysIndirect`, `DrawElementsIndirect` | `draw_vbo` (59) | Claude, premium |
|
|
| **i1** — image / compute / barrier / copy-image / storage block | `BindImageTexture` 138, `DispatchCompute` 49, `CopyImageSubData` 30, `MemoryBarrier` 18, `ShaderStorageBlockBinding` 4 | `DispatchComputeIndirect`, `MemoryBarrierByRegion` | `bind_shader_image` (72), `launch_grid` (60), `memory_barrier` (61), `resource_copy_region` (53), `set_storage_block_binding` (75) | Claude |
|
|
| **t2** — XFB spans, XFB object bind, patch parameter | `BeginTransformFeedback` 95, `PatchParameteri` 43, `BindTransformFeedback` 2 | `EndTransformFeedback`, `PauseTransformFeedback`, `ResumeTransformFeedback`, (`DeleteTransformFeedback`: no row, see contract) | `begin/end/pause/resume_stream_output` (62..65), `bind_stream_output` (74), `patch_parameter` (73) | Claude |
|
|
| **f1** — clears, framebuffer-sourced copies, mips | `ClearBufferiv` 12, `ClearBufferuiv` 8, `ClearBufferfv` 6, `ClearBufferfi` 1, `ClearNamedFramebufferfv` 2, `CopyTexImage2D` 3, `GenerateMipmap` 2 | `ClearNamedFramebufferfi/iv/uiv`, `CopyTexSubImage2D` | `clear` (57), `copy_framebuffer_to_texture` (76), `generate_mipmap` (54) | codex astra — additive and mechanical |
|
|
|
|
Per slot the work is the same three edits: the emitter (class C → B in `EmitTables.cpp`, the
|
|
pre-verb hook order kept), the `ServerVerbSink` body (the backend call the contract names), and
|
|
a lane case that proves the record crossed. Nothing in the backends, unless the contract names
|
|
the site (i1's mirror skip, f1's optional mip-storage re-derivation) - and then only behind
|
|
`#if MOBILEGL_BUILD_DISAGGREGATED`.
|
|
|
|
## 3. The per-package gate
|
|
|
|
- **the inproc lane's abort count for your slots → 0**, and each affected scenario reaches its
|
|
NEXT first blocker, reported by name (`Fatal{UnmigratedVerb, "…"}`, `Fatal{UnmigratedEmulation,
|
|
"…"}`, or a wrong-answer failure - whichever comes first). Run
|
|
`~/w7/p5b-c0b-census.sh <your tree> <outdir>` and diff `results.json` against c0b's baseline
|
|
(`~/w7/p5b-c0b-census-logs/results.json`: 432 passed / 185 skipped / 505 aborted / 27 failed,
|
|
all 505 aborts `UnmigratedVerb`, the per-slot table identical to `census-classC.md`'s).
|
|
- **d1 additionally: every Minecraft trace reaches its next first blocker under inproc**,
|
|
reported by name per fixture (the census's trace table), via the same per-entry private-log
|
|
method as `p6_census_traces.{sh,py}`; a trace that RENDERS is reported with its SSIM.
|
|
- the integrator's quick gate: unit lanes green (c0b's baseline + yours), `integration-split`
|
|
inproc 22/22, push `integration-gpu` 1128/1128 name-for-name, G1 0/0/0/0 `.text +0`.
|
|
- no reviews between rounds; one codex review at P5b's close.
|
|
|
|
## 4. File ownership
|
|
|
|
| file | owner in P5b | others |
|
|
|---|---|---|
|
|
| `MG_Pipe/MGPipeTypes.h`, `PipeCalls.def`, `PipeFields.def`, `FieldOwnership.def`, `Coverage.def`, `FillPoints.def`, `scripts/gen_pipe*.py`, `MG_Remote/CONTRACT-P5B.md` | c0b / integrator | t2: `kCapBackendOwnsXfbCapture` (contract §6.5); i1: the `GetProgramForDispatch` row (§6.9). Anything else: ask. |
|
|
| `MG_Remote/Wire/PipeWireCodec.{h,cpp}` | w1's file; c0b landed the arms | a package edits the layout arm of ITS row only, with the integrator |
|
|
| `MG_Remote/Server/PipeApplier.{h,cpp}` | v1's file | each package replaces ITS stub bodies (the `// ---- d1/i1/t2/f1 ----` blocks) and may add ITS tallies |
|
|
| `MG_Remote/Client/EmitTables.{h,cpp}` | c1's file | each package edits ITS `MGR_UNMIGRATED_*_SLOTS`, ITS `kEmittedSlots*`, ITS emitters |
|
|
| `MG_Remote/Client/SlotCaps.h` | c1's file | t2: the `EndTransformFeedback` probe → `kCapBackendOwnsXfbCapture` |
|
|
| `MG_Test/Wire/PipeWireCodecTest.cpp`, `MG_Test/Wire/RemoteClientTest.cpp` | shared | append your cases below c0b's; the partition pins (`UnmigratedSlotCount()` etc.) move with your counts |
|
|
| `MG_IntegrationTest/**` | t1's file | each package adds ITS lane cases / scenario arms |
|
|
| `MG_Backend/DirectGLES/**`, `MG_Backend/DirectVulkan/**` | untouched by c0b | only the sites the contract names, only behind `#if MOBILEGL_BUILD_DISAGGREGATED` |
|
|
| `~/w7/notes/p5b/p5b-results/<slug>-v1.md` | you | |
|
|
|
|
## 5. The loop that follows
|
|
|
|
1. The four packages land, each on its own gate. The integrator merges in landing order,
|
|
regenerates (`gen_pipe.py`, `gen_pipe_field_ownership.py` - never hand-merge `generated/`),
|
|
runs the quick gate.
|
|
2. **Re-census** on the merged head: the lane (`p5b-c0b-census.sh`) and the traces. The new
|
|
first-blocker table is the next round's package list, same shape, same gates.
|
|
3. Migrate the new first blockers (wave 3's tail is P9/P10-shaped: readbacks, queries, syncs)
|
|
and the residual-input debts `rsp` points at where a verb's pull is what blocks it.
|
|
4. Repeat until the four A/B trace cases render under inproc on the Redmi. That is P5b's exit;
|
|
P6's spawn transport follows (ID-68).
|