[Docs] (Disaggregated): P5f - every piece of state on the wire, and P6 is blocked until it is

P6's premise is that it is "only a transport swap". That premise was bought by P5c and has
expired. Measured on this head, the two roles still share state directly, so a spawned server
would have no source for it. P5f is the phase that makes the premise true, and P6 does not
start before it.

WHAT THE MEASUREMENT FOUND

- FieldOwnership.def still carries 21 BARRIER_PULLED rows over 15 distinct fields. That class
  is defined as "the server answers by reading a value the client's residual fill left in THE
  SINGLE SHARED gPipeInputs while the verb barrier holds both threads apart". Eight of those
  fields were retired by P5e for Espryt's unbarriered path only - Magma has not moved at all.
  The strict lane measures 10 live <field>@<verb> pairs today.
- gPipeInputs (MG_Backend/MGPipe/PipeInputs.h:835) is a plain process global,
  `inline PipeInputs& gPipeInputs = *new PipeInputs();` - not a shm segment. The client fills
  it (MG_Impl/Pipe/PipeFill.cpp), the backend reads it. Under spawn the two processes have
  DIFFERENT objects, the 15 BARRIER_PULLED fields have no filler in the server's copy, and
  every read is Fatal{UnmigratedPipeInput}. MG_Impl is not in the server, so "let the server
  fill it" is not available either. The header says this itself at :279 and :902.
- The twelve Server* EGL forwarders do not use the wire at all: they post a function pointer
  plus a stack-local void* through a one-slot mailbox (ServerLoop.cpp:533-568, :645). The
  SurfaceOp / SurfaceReply / WindowKind schema is defined in protocol.fbs and unused.
- Process-level statics with per-context semantics (g_syncedRenderStateParameters,
  ScopedDefaultUnpackState::s_synced) - and the full list is unknown, because this defect class
  is structurally invisible to inproc.
- MGPipeFrontendKeyedRegistryScope is still live at ~12 sites in DirectGLES.cpp, carrying
  P3b/P4b's debt: the registry is still keyed by frontend address / lifetime id.

THE SCOPE RULE

P5f takes the cross-process-blocking subset of P3b/P4b/P7/P8/P9 and nothing else. One test:
does it still hold in another address space? No -> P5f. Yes but slow or ugly -> it stays with
the phase that owns it. So Magma's nine fields and its direct writes into client storage come
here; content-addressed CSO, internal shader baking and the fixed-width rewrite stay P7's. The
reverse channel's asynchrony and ordering stay P9's; only the parts that touch client memory
move. Class C is NOT P5f's - a named refusal behaves identically in two processes.

THE CENTRAL MECHANISM: A TWO-BLOCK REHEARSAL

P5c set the same rule (rule E) and checked it by AUDIT - a human reading 59 lines. P5f checks
it by MACHINE: give each role its own PipeInputs under inproc, and its own copy of every
per-context static, behind a knob. Anything that depends on the two being the same object
becomes Fatal{UnmigratedPipeInput, "<Field>@<Verb>"} with the field and verb named.

That rehearses the process split's SEMANTICS without a second process. It matters because
today "the server read the value the client filled" and "the server read the value a record
carried" are indistinguishable at runtime - which is exactly what P6 would discover on day
one. The package order puts the mechanism (f1) BEFORE the migrations deliberately: let the red
appear first and let it be the worklist, rather than having each package declare itself done
and then looking for a criterion.

Exit gate: the two-block lane green; strict-expected-markers.txt EMPTY with its two-sided
ratchet still armed; zero BARRIER_PULLED rows; rsp reading zero per frame; Magma judged by the
same criteria as Espryt. The negative control is that turning the two-block knob OFF must move
at least one named case from green to red - otherwise the mechanism proves nothing, which is
ID-122's lesson.

ALSO IN THIS COMMIT

- ROADMAP gains the P5f row and the IPC track becomes P5e -> P5f -> P6; P6's status cell
  becomes "blocked on P5f".
- README and CURRENT_STAGE_PROGRESS gain the same, plus the file-map entry.
- Both P6 documents gain a blocked banner naming what is not true yet.

Doc citation lint: 124 citations across docs/Disaggregated/*.md, 0 problems.
This commit is contained in:
2026-09-19 05:32:38 -04:00
parent 7f1d2134c3
commit 465656843b
6 changed files with 238 additions and 6 deletions
+4 -2
View File
@@ -12,7 +12,8 @@
| **P5c** `inproc` 共享内存读点归零 | **已收官(2026-09-17)** | `11ac3de6..b88e8487` + triage 修复;契约 `MobileGL/MG_Remote/CONTRACT-P5C.md`;审计 `~/w7/notes/p5c/p5c-audit-v1.md` |
| **P5d** `inproc` 性能专项 | **已收官(2026-09-18,三轮)** | `cb06538c`、`56a77348`、`1f8de61b`;报告 [`P5D-INPROC-PERFORMANCE.md`](P5D-INPROC-PERFORMANCE.md);`MEASUREMENTS.md` §9 |
| **P5e** 退役 Espryt draw path 的 lockstep | **已收官(2026-09-19)**,附一条具名未决(E1 对照,ID-122) | 契约 `MobileGL/MG_Remote/CONTRACT-P5E.md`;计划 `~/w7/notes/p5e/BRIEF-P5E.md`、裁定 `~/w7/notes/p5e/INTEGRATOR-DECISIONS-P5E.md`(**ID-80..136**);**十二个包已全部落地合并**(§2.7),`kMGPipeP5eRunAheadReady` 与 `kMGPipeP5eClientWaitRuleLanded` 均已翻。strict 车道硬绿 179/179、`integration-gpu` 1357/1357、三个构建 flavour 全绿;设备上 VD32 已与 monolith 齐平。报告 [`P5E-RUNAHEAD.md`](P5E-RUNAHEAD.md),未完成项见 §5 |
| **P6** spawn transport | **计划与契约草稿已起草(2026-09-19)**,未开工 | 计划 [`P6-SPAWN-PLAN.md`](P6-SPAWN-PLAN.md)(a6 只读审计 → c6 契约 → so / sm / cp / st / t6 并行);契约草稿 [`P6-CONTRACT-DRAFT.md`](P6-CONTRACT-DRAFT.md)。传输原语(`SocketDoorbell`、SCM_RIGHTS fd 传递、`ShmSegment::Adopt`、`SurfaceOp` schema)**已在树上且有测试**;缺的是进程、控制面,以及 P5e 留下的“不等待的客户端 + 会死的服务端” |
| **P5f** 一切状态上 wire | **计划已起草(2026-09-19)**,未开工 | 计划 [`P5F-WIRE-COMPLETENESS.md`](P5F-WIRE-COMPLETENESS.md)。插在 P6 之前:跨角色的直接共享尚未归零(`BARRIER_PULLED` 21 行 / 15 字段,其中八个 Magma 未动;EGL 控制面仍走函数指针邮箱;per-context 语义的进程级静态量) |
| **P6** spawn transport | **阻塞于 P5f**;计划与契约草稿已起草 | 计划 [`P6-SPAWN-PLAN.md`](P6-SPAWN-PLAN.md)(a6 只读审计 → c6 契约 → so / sm / cp / st / t6 并行);契约草稿 [`P6-CONTRACT-DRAFT.md`](P6-CONTRACT-DRAFT.md)。传输原语(`SocketDoorbell`、SCM_RIGHTS fd 传递、`ShmSegment::Adopt`、`SurfaceOp` schema)**已在树上且有测试**;缺的是进程、控制面,以及 P5e 留下的“不等待的客户端 + 会死的服务端” |
## 2. 当前头实测
@@ -236,7 +237,8 @@ p50 181(约 1.10 倍)——那一次不可配对,只能作为方向性提
0. P5d 的遗留(`P5D-INPROC-PERFORMANCE.md` "什么没完成"):R-1 序列化留给 P3b/P4b → P11(`gPipeInputs` 版本化已写进 P11 行);线程放置记录;小项随 P3b/P4b 顺手。
1. **P5e 已收官**(2026-09-19,报告 [`P5E-RUNAHEAD.md`](P5E-RUNAHEAD.md)):出口门已逐条跑过(逐项结果记在 `ROADMAP.md` 的 P5e 出口门格):unit 两臂 2256/2256、`integration-split` 179/179 零 `Fatal{`、三条阴性对照按预期、逐包 red-once 各得恰好一个具名对。**剩下的只有 E1 对照**(ID-122):修掉它两个遮蔽性缺陷后仍红,而它索要的 `Fatal{BarrierViolation}` 在整次运行里出现 0 次,需要重新定义它证伪什么而不是调阈值。契约 §3.2(per-role stamp 存储)与 §8 修正 8 仍标为 UNLANDED,随后续阶段;G1 仍由 CI 断言(ID-123)。
2. **P6 spawn transport**:计划与契约草稿已起草([`P6-SPAWN-PLAN.md`](P6-SPAWN-PLAN.md)、[`P6-CONTRACT-DRAFT.md`](P6-CONTRACT-DRAFT.md)),**下一步是跑 `a6` 那次只读审计,不写代码**:“P6 只是传输替换”这句话的证据是 P5c 时代的,P5e 之后已经过期。审计要点名去查:进程级静态里语义属于 context 的那一类(`s_synced` / `g_syncedRenderStateParameters` 是已知的两个,问题是还有几个——这类缺陷`inproc` 永远看不见);十二个 EGL forwarder 对 `SurfaceOpKind` 缺几个枚举;server 是否真能不链 `MG_Impl`。
2. **P5f 一切状态上 wire**([`P5F-WIRE-COMPLETENESS.md`](P5F-WIRE-COMPLETENESS.md)):插在 P6 之前。下一步是 `f0` 普查(只读)与 `f1` 双块机制——让红先出现,红就是清单。
3. **P6 spawn transport**(阻塞于 P5f):计划与契约草稿已起草([`P6-SPAWN-PLAN.md`](P6-SPAWN-PLAN.md)、[`P6-CONTRACT-DRAFT.md`](P6-CONTRACT-DRAFT.md)),**下一步是跑 `a6` 那次只读审计,不写代码**:“P6 只是传输替换”这句话的证据是 P5c 时代的,P5e 之后已经过期。审计要点名去查:进程级静态里语义属于 context 的那一类(`s_synced` / `g_syncedRenderStateParameters` 是已知的两个,问题是还有几个——这类缺陷`inproc` 永远看不见);十二个 EGL forwarder 对 `SurfaceOpKind` 缺几个枚举;server 是否真能不链 `MG_Impl`。
3. 剩余首阻塞一轮(Magma compute/image、rd12、RGB mip、`texture-remint-pull` 仿真槽)。
4. P5e 出口门(`~/w7/notes/p5e/BRIEF-P5E.md` §3 / §4):`integration-split-strict` 转硬绿车道、三条阴性对照、Redmi 四臂(monolith / lockstep / credit 1 / credit 2)。P6 出口门:P5b 的完整渲染路径在 `spawn` 下绿;OpenRA 在 Adreno 830 上 split SSIM ≥ 0.99。
5. Redmi 四臂复测(P5c 的记录项,需设备窗口);79 trace 普查重跑(需全集语料)。
+221
View File
@@ -0,0 +1,221 @@
# P5f — 一切状态上 wire:跨进程前的最后一次归零
> 基线 `feat/disaggregated @ 7f1d2134`。路径在 `MobileGL/` 下。**未开工。**
> **P6 在 P5f 之前不开工**:P6 的前提"只是传输替换"今天不成立,§2 是不成立的清单。
---
## 0 目标与出口判据
**目标**:两个角色之间,除 `SEG_CMD` / `SEG_STAGE` / `SEG_REPLY` / `SEG_EVENT` / caps 快照 / 控制帧
之外零交换。不得有任何一方读写另一方的内存、也不得依赖"两者其实是同一个对象"。
P5c 立过同样的规则(规则 E),但它的判据是**审计**——人读 59 行清单。P5f 的判据是**机器**:
> **出口判据**:`inproc` 下给两个角色**各自一份 `PipeInputs`**(§1),以及各自一份所有
> per-context 的进程级静态,整条 `integration-split` 仍然全绿,且
> `MOBILEGL_IPC_STRICT_ERRORS=1` 下 `Harness/strict-expected-markers.txt` **为空**。
这条判据的价值在于它**不需要第二个进程**就能证伪跨进程的假设。今天两个角色共享一份
`gPipeInputs`,所以"server 读到了 client 填的值"和"server 读到了记录送来的值"在运行时完全
无法区分——这正是 P6 第一天会踩的坑。把块拆成两份,前者立刻变成 poison Fatal。
---
## 1 `gPipeInputs` 是什么,拆进程后它会怎样
### 1.1 它是什么
`MG_Backend/MGPipe/PipeInputs.h:835`:
```cpp
inline PipeInputs& gPipeInputs = *new PipeInputs();
```
一个**普通的进程级全局对象**,不在任何共享内存段里。它是“后端被推送的状态块”——
backend 不再每 draw 去读 `MG_State::pGLContext`,改为读这个块。每个字段带一个**归属类**
(CONTRACT-P5 表 2 / R-7,手维护的一半在 `MG_Pipe/FieldOwnership.def`):
| 归属类 | 量 | 含义 |
|---|---|---|
| `RECORD_SUPPLIED` | **41 个字段**(P5c rv 之前 32);**由推导得出,不列在表里** | 推送的记录供给**整个**字段,server 永远不需要 client |
| `APPLIER_DERIVED` | 4 行 | applier 从它已经应用的记录里写出它 |
| `BARRIER_PULLED` | **21 行 / 15 个字段** | **P5 的债**:server 读的是 client 的残余填充留在“那唯一一份共享的 `gPipeInputs`”里的值 |
| `FATAL` | 4 行 | 没有载体,缩减路径也不读;读到即 abort |
写它的是 client(`MG_Impl/Pipe/PipeFill.cpp`),读它的是 backend。每个字段带一个**逐 verb 的
世代戳**,读到比当前 verb 序号旧的戳就是
`Fatal{UnmigratedPipeInput, "<Field>@<Verb>"}`(`MOBILEGL_PIPE_POISON`)。
### 1.2 为什么今天能工作
因为两个角色在**同一个进程、同一个 `gPipeInputs` 对象**上,而 verb barrier 保证同一时刻
{GL 线程, apply 线程} 至多一个可运行——所以这个块任何瞬间只有一个写者
(`PipeInputs.h:904-908` 的裁定)。client 填,server 读,中间不经过任何载体。
`BARRIER_PULLED` 这一类的定义就是:"server 在 barrier 把两个线程隔开时,读 client 的残余填充
留在 `gPipeInputs` 里的值。**合法,并且被计数**(`PipeStats::CallClass::ResidualPulls`)。"
### 1.3 拆进程后它会怎样
**client 进程和 server 进程各有一个 `gPipeInputs`,是两个不同的对象。**
- `RECORD_SUPPLIED` / `APPLIER_DERIVED` 的 **45 个字段**没问题:它们的值本来就由记录或 server
自身产生,server 侧的块会被正常填上。
- `BARRIER_PULLED` 的 15 个字段**没有填充者**。client 进程里那次 fill 写的是 client 自己的块;
server 进程的块里那些字段的世代戳永远是旧的 → 每一次读都是
`Fatal{UnmigratedPipeInput}`。
- `MG_Impl` 根本不在 server 进程里,所以连"让 server 自己去 fill"这条退路都没有。
头文件自己写着这件事(`PipeInputs.h:279`):当前 verb 边界的清理"对 inproc 足够——两个角色共享
一个进程、一个 `gPipeInputs`——**而对 P6 是误导的,那里 `MG_Impl` 根本不在 server 里**"。
以及 `:902`:`rsp` 的值"**就是 P6/P7/P8 债务的大小**"。
**所以 P5f 对 `gPipeInputs` 的要求**:`BARRIER_PULLED` 归零。每个字段要么升成
`RECORD_SUPPLIED`(值随记录过线),要么升成 `APPLIER_DERIVED`(server 自己推),要么降成
`FATAL`(缩减路径不读它)。**块本身不需要跨进程共享,它需要的是每个字段都有一个不依赖对方
地址空间的来源。**
---
## 2 清单(在基线头上实测)
### 2.1 `BARRIER_PULLED` 的 15 个字段
| 字段 | 退役阶段(`FieldOwnership.def` 原文) |
|---|---|
| `GetBoundVertexArray` | P5e (Espryt unbarriered), **P7 (Magma)** |
| `GetBufferBindingPoint` | P5e (Espryt unbarriered), **P7 (Magma)** |
| `GetFramebufferBindingSlot` | P5e (Espryt unbarriered), **P7 (Magma)** |
| `GetImageTextureBinding` | P5e (Espryt unbarriered), **P7 (Magma)** |
| `GetProgramForDraw` | P5e (Espryt unbarriered), **P7 (Magma)** |
| `GetProgramForDispatch` | P5e (Espryt unbarriered), **P7 (Magma)** |
| `GetTextureUnitObject` | P5e (Espryt unbarriered), **P7 (Magma)** |
| `GetTextureObject` | P7 |
| `GetBufferBindingPointCount` | P7/P13 |
| `HasOpenTransformFeedbackSpan` | P7/P9 |
| `GetTransformFeedbackProgram` | P3b/P4b (Espryt), **P7 (Magma)** |
| `GetProgramObject` | P9 |
| `ValidateProgramName` | P9 |
| `RecordError` | P9 |
| `GetBufferBindingSlot` | P8 (indirect), P9 (readback), P13 (transfer) |
**八个字段 P5e 只为 Espryt 的未设障路径退役了,Magma 一条都没动。** 这是清单里最大的一块。
strict 车道当前实测活着 **10 对** `<field>@<verb>`(`Harness/strict-expected-markers.txt`),
其中两对是 escalation-barriered 的 client vertex array 条目(P8)。
### 2.2 Magma(DirectVulkan)
P7 原本是"全量迁移 10 个子系统",里面混着与拆分无关的工作(内容寻址 CSO、内部 shader 烘焙、
定宽重写)。**P5f 只取其中会阻碍跨进程的部分**:§2.1 里带 "P7 (Magma)" 的那 9 个字段,以及
Magma 侧一切直接读前端对象 / 写 client 存储的站点。P5c 审计的 **T5** 是已知的一条:
Magma 生成 mip 直接写 client 的 level 存储(`DirectVulkan/Renderer/VulkanRenderer.cpp:1562-1590`)。
Magma 今天不发布 `kCapRunAheadApply`,所以它跑 lockstep——**lockstep 掩盖的正是这类缺陷**,
与 P5e 的 ID-132 是同一条教训。
### 2.3 EGL / surface 控制面(P5c 审计 G4)
十二个 `Server*` forwarder(`MG_Remote/Server/ServerLoop.cpp:802-1022`)经单槽邮箱传
**函数指针 + 栈上 `void*`**(`ServerLoop.cpp:533-568`、`:645`)。`protocol.fbs:166-205` 的
`SurfaceOp` / `SurfaceReply` / `WindowKind` schema 已定但**无人使用**。
跨进程后函数指针与栈地址都无意义。
### 2.4 进程级静态,语义却是 per-context
已知两个:`g_syncedRenderStateParameters`(`MG_Backend/DirectGLES/DirectGLES.cpp:4586`)与
`ScopedDefaultUnpackState::s_synced`(`CONTRACT-P5C.md:538-539` 记为 P6 的)。
**完整清单未知**——这类缺陷 `inproc` 结构性看不见,必须普查。
### 2.5 P5c 留下的具名豁免 scope
`MGPipeFrontendKeyedRegistryScope` 在 `DirectGLES.cpp` 还有约 **12 个活跃站点**
(`:2542`、`:4505`、`:5540`、`:5659`、`:5940`、`:6490`、`:6576`、`:7092`、`:7682`、`:8213`、`:11231` …),
背 P3b/P4b 的债:registry 仍按前端地址 / lifetime-id 键控。
### 2.6 反向通道(P9 的一部分)
XFB scatter 对 client shadow 读改写、`OnXfbScatterReady`、`OnTexturePullRequest` 与终止符、
`OnGlError` 有序化。其中**凡是直接触碰 client 内存的**归 P5f;纯粹的异步化 / 批处理 / 排序
(`SEG_REPLY` 异步槽池、PBO fire-and-forget、epoch 排序)仍归 P9。
### 2.7 class C
`MG_Remote/Client/EmitTables.cpp` 里 10 处具名拒绝。**不归 P5f**:拒绝是诚实的,
`Fatal{UnmigratedVerb}` 在两个进程下行为一致。它们限制的是 spawn 车道的覆盖面,不是它的正确性。
---
## 3 范围边界
| 取 | 不取 |
|---|---|
| §2.1 全部 15 个字段(含 Magma 的 9 个) | P7 的性能与架构项:内容寻址 CSO、内部 shader 烘焙、`DynamicBackendParameters` 定宽重写 |
| §2.2 Magma 侧一切跨角色读写 | Magma 的功能补齐(占位纹理原生化、具名 UBO host payload 等),除非它们本身是跨角色读写 |
| §2.3 控制面帧(原 P6 的) | 真窗口到达(P12) |
| §2.4 静态量按世代/角色分区 | — |
| §2.5 registry 按句柄重键 | P3b/P4b 的其余项(view 索引重映射、workaround 删除) |
| §2.6 反向通道里触碰 client 内存的部分 | P9 的异步化与排序 |
| — | class C(§2.7)、chunked readback(P9)、persistent map 分档(P11) |
**判断准则只有一条**:*它在另一个地址空间里还成立吗?* 不成立 → P5f。成立但慢/丑 → 留给原阶段。
---
## 4 中心机制:双块演练
P5f 的每一条都靠同一个机制证伪,而不是靠审计:
1. **`PipeInputs` 按角色分区**:`gPipeInputs` 变成"取当前角色的块"。client 的 fill 写 client 的块,
backend 的读取 server 的块。任何今天依赖"两者是同一个对象"的路径,立刻变成
`Fatal{UnmigratedPipeInput, "<Field>@<Verb>"}`,带字段名和 verb 名。
2. **per-context 静态按角色 + 世代分区**,同理。
3. 两者都由一个旋钮武装(建议 `MOBILEGL_IPC_ROLE_SPLIT_STATE=1`),默认关,CI 开一条车道。
这等于在**不拆进程**的前提下把进程拆分的语义先跑一遍。代价是一个旋钮和一次分区;
回报是 P6 那天不会有任何一条"第一次执行"的路径。
---
## 5 包(草案,待 §2.4 普查后定)
```
f0 普查(只读):§2.4 的完整静态清单 + §2.6 的逐站点清单 + 每个 BARRIER_PULLED 字段的载体判定
└─ f1 双块机制 + 旋钮 + 车道(§4),此时预期大面积红,红就是清单
├─ fm Magma 的 9 个字段 + T5 + Magma 侧跨角色读写
├─ fc 控制面帧(§2.3)
├─ fs 静态量分区(§2.4)
├─ fr registry 按句柄重键(§2.5)
└─ fv 反向通道触碰 client 内存的部分(§2.6)
└─ 收口:strict 允许表清空,双块车道转硬绿
```
`f1` 先落是故意的:**让红先出现,再逐包消红**,而不是各包自称做完之后再找一个判据。
---
## 6 出口门
1. 双块车道(§4)`integration-split` 全绿。
2. `MOBILEGL_IPC_STRICT_ERRORS=1` 下 `strict-expected-markers.txt` **为空**,且两侧棘轮仍在
(空表 + 出现任何一对即红)。
3. `FieldOwnership.def` 零 `BARRIER_PULLED` 行;`rsp` 计数器逐帧读零。
4. **Magma 与 Espryt 跑同一套判据**,且 Magma 发布 `kCapRunAheadApply`——或者明确说明它为什么
不发布,并给出不发布也不掩盖缺陷的理由(ID-132)。
5. G1 0/0/0/0;G2 名集合不变;G14 测试名只增不删。
6. 逐包 red-once。
7. 设备:Redmi `2f7cbe2e`,双后端,reboot-clean 配对 A/B。性能只记录。
**阴性对照**:关掉双块(回到共享一块)必须让至少一条具名用例**由绿转红**——否则这个机制
什么也没证明。这是 ID-122 的教训:对照要证伪的是机制本身。
---
## 7 与 P6 的关系
P5f 收官后,P6 的"只是传输替换"才第一次成立,而且是**被机器证明过的**,不是被引用的。
P6 的 `a6` 只读审计随之缩小为一次核验,而不是一次发现。
`P6-SPAWN-PLAN.md` 与 `P6-CONTRACT-DRAFT.md` 里凡写"`PENDING a6`"的行,
其中属于 §2 的部分改由 P5f 回答。
+5
View File
@@ -1,5 +1,10 @@
# CONTRACT-P6 (DRAFT) — the backend runs in a second process
> **⏸ Blocked on P5f** (2026-09-19): this draft assumes P6 is only a transport
> swap, which is not true today - 15 `BARRIER_PULLED` fields are still filled by the
> client into a shared `gPipeInputs`, and Magma has not moved at all. See
> [`P5F-WIRE-COMPLETENESS.md`](P5F-WIRE-COMPLETENESS.md).
>
> **Draft, written before its own audit.** Package `a6` ([`P6-SPAWN-PLAN.md`](P6-SPAWN-PLAN.md) §5)
> is a read-only audit whose output is this file's input. Rows that depend on it are marked
> **`PENDING a6`**. `c6` moves this file to `MobileGL/MG_Remote/CONTRACT-P6.md`, where it becomes
+2
View File
@@ -2,6 +2,8 @@
> 基线 `feat/disaggregated @ f23fbc1b`。路径在 `MobileGL/` 下,行号以该头为准。
> 契约草稿 [`P6-CONTRACT-DRAFT.md`](P6-CONTRACT-DRAFT.md),`c6` 落地时移为 `MobileGL/MG_Remote/CONTRACT-P6.md`。
> **⏸ 阻塞于 P5f**(2026-09-19):本计划假定“P6 只是传输替换”,而那句话今天不成立——`BARRIER_PULLED` 还有 15 个字段靠 client 填共享的 `gPipeInputs`,Magma 一条未动。先跑 [`P5F-WIRE-COMPLETENESS.md`](P5F-WIRE-COMPLETENESS.md)。
>
> **未开工。** 标 `待 a6` 的条目由审计确认,现在不下结论。
---
+2 -1
View File
@@ -1,6 +1,6 @@
# MGPipe:MobileGL 前后端拆分
> 状态:**P0–P5e 已收官**(P5e 2026-09-19 收官,附一条具名未决:E1 对照需按 ID-114 重新定义),**当前在 P6 spawn transport**(计划 [`P6-SPAWN-PLAN.md`](P6-SPAWN-PLAN.md)、契约草稿 [`P6-CONTRACT-DRAFT.md`](P6-CONTRACT-DRAFT.md),均尚未开工)。P5e:run-ahead 已武装(十二包落地,strict 车道硬绿 179/179、`integration-gpu` 1357/1357、pull/push/split 三个构建 flavour 全绿;设备上渲染距离 32 下 `inproc` 已与 `monolith` 齐平,相对它取代的 lockstep p50 +69%。报告 [`P5E-RUNAHEAD.md`](P5E-RUNAHEAD.md))(P5c 2026-09-17 `b88e8487`;P5d 三轮 2026-09-18,代码头 `1f8de61b`——`inproc` 性能专项,VD12 Minecraft 上 split 从 7-13 fps 拉到 103-106 fps、client 线程 CPU/帧从 15 ms 到 9.2 ms,报告 [`P5D-INPROC-PERFORMANCE.md`](P5D-INPROC-PERFORMANCE.md))。目标负载(四条 Minecraft A/B trace)已在 Redmi Adreno 830 上以 `inproc`(独立 apply 线程)双后端渲染,barrier tax 首次实测;P5c 把审计出的 59 处不经 wire 的直接内存访问全部归零(契约 `MobileGL/MG_Remote/CONTRACT-P5C.md`),`inproc` 成为只经 wire 交换的诚实两角色。**P6 只是传输替换**——但这句话的证据是 P5c 时代的,P6 的第一个包是重新证明它的只读审计。当前头、逐门数字与开放项见 [`CURRENT_STAGE_PROGRESS.md`](CURRENT_STAGE_PROGRESS.md)。
> 状态:**P0–P5e 已收官**(P5e 2026-09-19 收官,附一条具名未决:E1 对照需按 ID-114 重新定义),**当前在 P5f**(一切状态上 wire,计划 [`P5F-WIRE-COMPLETENESS.md`](P5F-WIRE-COMPLETENESS.md);P6 阻塞于它),P6 计划 [`P6-SPAWN-PLAN.md`](P6-SPAWN-PLAN.md)、契约草稿 [`P6-CONTRACT-DRAFT.md`](P6-CONTRACT-DRAFT.md),均尚未开工)。P5e:run-ahead 已武装(十二包落地,strict 车道硬绿 179/179、`integration-gpu` 1357/1357、pull/push/split 三个构建 flavour 全绿;设备上渲染距离 32 下 `inproc` 已与 `monolith` 齐平,相对它取代的 lockstep p50 +69%。报告 [`P5E-RUNAHEAD.md`](P5E-RUNAHEAD.md))(P5c 2026-09-17 `b88e8487`;P5d 三轮 2026-09-18,代码头 `1f8de61b`——`inproc` 性能专项,VD12 Minecraft 上 split 从 7-13 fps 拉到 103-106 fps、client 线程 CPU/帧从 15 ms 到 9.2 ms,报告 [`P5D-INPROC-PERFORMANCE.md`](P5D-INPROC-PERFORMANCE.md))。目标负载(四条 Minecraft A/B trace)已在 Redmi Adreno 830 上以 `inproc`(独立 apply 线程)双后端渲染,barrier tax 首次实测;P5c 把审计出的 59 处不经 wire 的直接内存访问全部归零(契约 `MobileGL/MG_Remote/CONTRACT-P5C.md`),`inproc` 成为只经 wire 交换的诚实两角色。**P6 只是传输替换**——但这句话的证据是 P5c 时代的,P6 的第一个包是重新证明它的只读审计。当前头、逐门数字与开放项见 [`CURRENT_STAGE_PROGRESS.md`](CURRENT_STAGE_PROGRESS.md)。
>
> 性能纪律(2026-09-08 起):逐线程 CPU 与 tracker 绝对 ns **对着 pull 臂基线记录**,不作阻塞门;专门的优化阶段排在路线图推完之后。
@@ -54,6 +54,7 @@ Android 三份 APK flavour:pull、push、split(Gradle 属性 `mobilegl.pipeP
| `CURRENT_STAGE_PROGRESS.md` | 当前头实测、P5b 落地内容、开放项、下一步、证据位置、仍在生效的裁定;随每次落地更新 |
| `ARCHITECTURE.md` | 已定稿的设计:句柄与世代、调用目录与生成器、记录约定、tracker、纹理路径、shader 制品、反向通道、后端改造、传输、persistent map 分档、进程 / EGL / 平台、构建与门、P5 / P5b 落地形状、开关表 |
| `ROADMAP.md` | 纪律、两条跑道、P0…P13 阶段表、里程碑、债务表、开放问题 |
| `P5F-WIRE-COMPLETENESS.md` | P5f 计划:`gPipeInputs` 是什么、拆进程后它怎样;阻碍跨进程的完整清单(`BARRIER_PULLED` 15 字段、Magma、EGL 控制面、静态量、豁免 scope、反向通道);双块演练机制与出口门 |
| `P6-SPAWN-PLAN.md` | P6 的包计划:树上已有什么(传输原语基本已就绪且有测试)、真正要造什么、P6 与 P12 的边界、七个包与依赖序、出口门与阴性对照 |
| `P6-CONTRACT-DRAFT.md` | P6 契约草稿(规则 G、令牌、控制面帧、死亡与 run-ahead、阶段修正);`c6` 落地时移为 `MobileGL/MG_Remote/CONTRACT-P6.md` |
| `MEASUREMENTS.md` | 逐阶段实测:P0 spike 与基线、P1 verify、P2 门与 DriverBench、P3a / P4a 门与设备 A/B、P5 全门 / E1–E6 / Redmi 四臂、P5b 主机门 / 普查 / 审查 / Redmi 出口 |
+4 -3
View File
@@ -1,6 +1,6 @@
# MGPipe 路线图
> 状态:**P0、P0.5、P1、P2、P3a、P4a、P5、P5b、P5c、P5d、P5e 已收官**(P5c:2026-09-17,代码头 `b88e8487`;P5d:三轮,2026-09-18,代码头 `1f8de61b`)。当前头、逐门数字、开放项与下一步见 **[`CURRENT_STAGE_PROGRESS.md`](CURRENT_STAGE_PROGRESS.md)**;设计见 `ARCHITECTURE.md`;逐阶段实测见 `MEASUREMENTS.md`;P5d 报告见 [`P5D-INPROC-PERFORMANCE.md`](P5D-INPROC-PERFORMANCE.md),P5e 报告见 [`P5E-RUNAHEAD.md`](P5E-RUNAHEAD.md)。**P5e 已收官**(退役 Espryt draw path 的 lockstep,契约 `MobileGL/MG_Remote/CONTRACT-P5E.md`;十二包已全部落地合并,**run-ahead 已武装**,头 `25fba0d5`;strict 车道硬绿 179/179、`integration-gpu` 1357/1357、三个构建 flavour 全绿;设备上 inproc 已与 monolith 齐平,见 `CURRENT_STAGE_PROGRESS.md` §2.6 与 `MEASUREMENTS.md` §11);出口门逐条已跑,唯一未过的是 E1 对照脚本本身,ID-122)。**当前在 P6 spawn transport**(此时只是传输替换;计划 [`P6-SPAWN-PLAN.md`](P6-SPAWN-PLAN.md)、契约草稿 [`P6-CONTRACT-DRAFT.md`](P6-CONTRACT-DRAFT.md),均尚未开工)。
> 状态:**P0、P0.5、P1、P2、P3a、P4a、P5、P5b、P5c、P5d、P5e 已收官**(P5c:2026-09-17,代码头 `b88e8487`;P5d:三轮,2026-09-18,代码头 `1f8de61b`)。当前头、逐门数字、开放项与下一步见 **[`CURRENT_STAGE_PROGRESS.md`](CURRENT_STAGE_PROGRESS.md)**;设计见 `ARCHITECTURE.md`;逐阶段实测见 `MEASUREMENTS.md`;P5d 报告见 [`P5D-INPROC-PERFORMANCE.md`](P5D-INPROC-PERFORMANCE.md),P5e 报告见 [`P5E-RUNAHEAD.md`](P5E-RUNAHEAD.md)。**P5e 已收官**(退役 Espryt draw path 的 lockstep,契约 `MobileGL/MG_Remote/CONTRACT-P5E.md`;十二包已全部落地合并,**run-ahead 已武装**,头 `25fba0d5`;strict 车道硬绿 179/179、`integration-gpu` 1357/1357、三个构建 flavour 全绿;设备上 inproc 已与 monolith 齐平,见 `CURRENT_STAGE_PROGRESS.md` §2.6 与 `MEASUREMENTS.md` §11);出口门逐条已跑,唯一未过的是 E1 对照脚本本身,ID-122)。**当前在 P5f**(一切状态上 wire,计划 [`P5F-WIRE-COMPLETENESS.md`](P5F-WIRE-COMPLETENESS.md))——它插在 P6 之前,因为“P6 只是传输替换”今天还不成立;P6 计划 [`P6-SPAWN-PLAN.md`](P6-SPAWN-PLAN.md)、契约草稿 [`P6-CONTRACT-DRAFT.md`](P6-CONTRACT-DRAFT.md),均尚未开工)。
## 通用纪律(每个 commit)
@@ -12,7 +12,7 @@
- **拆分不借机顺手修 `dev` 的 bug**:句柄化过程中发现的 `dev` 侧缺陷记成独立条目、独立 PR(开放问题 15、17)。
- 过程(用户 2026-09-16,ID-66):不过度验证;轮次间不做对抗性审查,每个 P 阶段收官由 Codex 审一次,发现进入下一阶段首轮;只读 / 非核心任务派给 Codex;实现方与审查方错开模型族。
两条跑道分开:**monolith 跑道** P0 → P0.5 → P1 → P2 → P3a → P4a → P3b/P4b → P7 → P8 → P13,每段可独立交付、可随时中止;**IPC 跑道** P5 → P5b → P5c → P5d → **P5e** → P6 → P9 → P10 → P11 → P12。目标顺序:先完整的 separate-thread rendering(P5b,已达成),再让 `inproc` 变成只经 wire 交换的诚实两角色(P5c),再把它的 CPU 代价压到可接受(P5d),然后退役 draw path 上的 lockstep(P5e:client 发了就走,server 只从记录带来的句柄解析对象),最后才是 separate-process transport(P6,此时只是传输替换)。
两条跑道分开:**monolith 跑道** P0 → P0.5 → P1 → P2 → P3a → P4a → P3b/P4b → P7 → P8 → P13,每段可独立交付、可随时中止;**IPC 跑道** P5 → P5b → P5c → P5d → P5e → **P5f** → P6 → P9 → P10 → P11 → P12。目标顺序:先完整的 separate-thread rendering(P5b,已达成),再让 `inproc` 变成只经 wire 交换的诚实两角色(P5c),再把它的 CPU 代价压到可接受(P5d),然后退役 draw path 上的 lockstep(P5e:client 发了就走,server 只从记录带来的句柄解析对象),最后才是 separate-process transport(P6,此时只是传输替换)。
## 阶段
@@ -29,7 +29,8 @@
| **P5c** `inproc` 共享内存读点归零 | ✅ `11ac3de6..b88e8487` | server 端纹理 staged shadow(`StagedTextureStore`,句柄为键、整段覆盖、defined-ness 追踪);`SEG_EVENT` 三个 producer + `kEventGlError`(反向通道零裸指针);sink / twin 按记录句柄解析(blit 具名臂经 verb 句柄工作区,Magma 走 G6 消费臂);`applier_reset` / `object_death` 控制记录(opcode 77/78,framebuffer 首个 wire delete);`set_context_values`(79)退役全部值类 BARRIER-PULLED 行,三 shutter 自答;双层 `Fatal{RoleViolation}` 守卫 + `InBarrierWait` 接线;两个具名豁免 scope(通告家族 P4b/P7、G6 registry 家族 P3b/P4b)承接未到期的债 | 角色守卫开启下 unit 2187/2187(strict 下同绿)、`integration-split` 111/111、每层守卫 red-once 按名变红;纹理 `0xDD` audit 在 bsl / complementary 两条 in-world trace 零 Fatal;rsp 按帧实测(bsl 948.5/帧,残留全为钉住的对象类 15 行);G1 三个认定 resize(SwapchainObject::Create / CopyTexSubImage2D / ScopedRestartIndexSubstitution,均已具名)+ `.text` −16 B、pull 构建零 MG_Remote 符号;G5 保护区字节一致;双生成器 --check/--self-test 绿 | P5b |
| **P5d** `inproc` 性能专项 | ✅ `cb06538c`、`56a77348`、`1f8de61b` | 一二轮:persistent-map 推送三级臂(mprotect 脏页追踪 + 哈希抑制 + 绑定过滤 `PushDrawConsumers`)、barrier 批处理 `MOBILEGL_IPC_BATCH_WAITS` + 延期销毁队列、自旋时钟 64 合 1、零页注册 + shadow 页对齐分配。三轮(按符号化 profile 逐项切,四包 + 一份 lockstep 可行性研究):apply 空转轮询零锁(控制邮箱原子影子位)、`Doorbell::Wait` 校准自旋稳态不读时钟、wait/park 计数进 stats;`OnApplyThread()` 内联为线程指针比较、三个 scope 深度计数器退掉 `thread_local`;shadow 分配尺寸补页、追踪范围向外对齐、边块哈希消失、脏页排空修复;image unit 高水位、残余填充 memo、server stamp 常量表;`generate_mipmap` 不再免等(apply 读残余输入)、death 测试围栏 | 三轮设备(Redmi,CPU 定频):inproc **64.3 → 103-106 fps**(p50),client 线程 CPU 15.0 → 9.2 ms/帧、apply 12.4 → 7.0-7.4;monolith 同场景 115(120 Hz vsync 封顶的运行)/ 206(一次未封顶,渲染线程 5.0 ms/帧 大核);配对比较以逐线程 CPU/帧为主,见报告"数字"节;unit 2203/2203、`integration-split` 111/111(两条曾 flaky 的用例复跑 6/6);双生成器、include 闭包、dirty-surface、doc 引用绿。未完成:R-1 序列化(研究结论:留给 P3b/P4b 后的 P11 `gPipeInputs` 版本化)、线程放置(内核忽略亲和)、`SetHashSuppressor` 哈希等小项。报告:[`P5D-INPROC-PERFORMANCE.md`](P5D-INPROC-PERFORMANCE.md) | P5b |
| **P5e** 退役 Espryt draw path 的 lockstep | ✅ **已收官(2026-09-19)**,附一条具名未决:E1 对照需按 ID-114 重新定义 | 契约 `MG_Remote/CONTRACT-P5E.md`(规则 F:**未设障**记录的 apply 不得读任何 client 内存;ID-84 限定它只约束未设障记录);裁定 **ID-80..136**(`~/w7/notes/p5e/INTEGRATOR-DECISIONS-P5E.md`)。**十二个包,三波**:wave 1 **c0e**(契约 + 线上行:`PipeCalls.def` 第五列 `WaitClass`、`set_program_bindings` opcode 80、`kCapRunAheadApply` 位 10 仅 DirectGLES 臂、`kDrawClientArrays`、子系统位 13、两个旋钮)→ **id**(registry 按 `{slot, gen}` 重键、by-handle resolver、分配器守卫);wave 2 **vi / sb / pg / tx2 / fb** 五个 per-draw 家族 ∥ **ra**(等待规则、present credit、`gPipeInputs` 变 server 角色内存),加 **fix1**(设备发现的 monolith 臂回归,ID-107);wave 3 **pa**(program 家族离开前端)、**mv**(multi-draw + 五个档位的门禁条目)、**gl**(门禁机制本身 + 两处潜伏崩溃)、**ra2**(翻开关后暴露的竞态)。**`kMGPipeP5eRunAheadReady` 与 `kMGPipeP5eClientWaitRuleLanded` 均已翻**。最大的那次迁移(逐 draw 的 `GetProgramForDraw`)**没有改动任何线上结构**:记录早就带着逐 link 的 `ProgramArchive`,缺的是仍去问前端对象的读者。报告 [`P5E-RUNAHEAD.md`](P5E-RUNAHEAD.md) | 出口门已逐条跑过(BRIEF-P5E §3,2026-09-19 在集成树上,run-ahead 已武装):**item 1** unit 在 strict 两臂均 **2256/2256**;**item 2/3** `integration-split` **179/179**、逐条普查零 `Fatal{`、`Admitted{` 恰为八行;**item 4(a)** `RUN_AHEAD=0` 作为配对对照 **179/179 绿**;**item 4(b)** `VERB_BARRIER=0` **变红**(车道 62/179);**item 4(c)** Magma 逐字记下 `run-ahead requested, server does not publish kCapRunAheadApply - running lockstep`,而 Espryt 对照记下 `run-ahead ARMED`;**item 5** 在集成树上重跑 red-once(ID-121 重排后的形式):倒掉 pa 的臂得 `GetProgramForDraw@DrawArrays`、倒掉 mv 的臂得 `GetBoundVertexArray@DrawArrays`,**恰好各一个具名对**,还原后各自转绿;**item 7** `MOBILEGL_IPC_AUDIT=1` **179/179**。**唯一未过的是 E1 对照脚本本身**(ID-122):已修掉它两个遮蔽性缺陷(选中了一个在本车道永远跑不起来的阳性对照用例;ctest 与日志助手对“谁被选中”意见不一致),但它索要的 `Fatal{BarrierViolation}` 在整次运行里 **出现 0 次**:`BATCH_WAITS` 默认值 1 下那个重叠不发生,而 run-ahead 武装后 `VERB_BARRIER=0` 还会顺手关掉 run-ahead(ID-114),它产生的那条臂**既不是 lockstep 也不是 run-ahead**。E1 需要重新定义它要证伪什么,不是再调阈值;在想清楚之前不动它的计数规则,否则只是换一种伪绿。G1 仍由 CI 断言(ID-123) | P5d |
| **P6** spawn transport | 📝 **计划与契约草稿已起草(2026-09-19)**,未开工 | `SocketTransport`(socketpair + fork/execve,envp 剔除 + 强制 monolith 双保险);`ServerMain`;`MOBILEGL_IPC_SERVER_PATH` + `dladdr` 兜底;有界重试握手;EOF 即时退出;device-lost latch;EGL forwarder 的控制面帧;P5c 留下的 `s_synced` / `g_syncedRenderStateParameters` 按 context 世代重置。包与依赖序见 [`P6-SPAWN-PLAN.md`](P6-SPAWN-PLAN.md):**a6**(只读审计,先跑)→ **c6**(契约 + 惰性线上行)→ **so / sm / cp / st / t6** 并行。契约草稿 [`P6-CONTRACT-DRAFT.md`](P6-CONTRACT-DRAFT.md)(规则 G:**角色不得指名进程局部句柄**)。**落地边界**:P6 只落 pbuffer / surfaceless / 离屏(够跑 `HeadlessGL`、等价 split 车道与 trace replay);`ANativeWindow*` 在子进程里无意义,**真窗口的到达是 P12**,到达 server 时具名拒绝。新设计只有一处:P5e 之后客户端不再等待,而对端进程会死——闩锁语义照抄 `ARCHITECTURE.md:504`,新的是“慢与死如何区分”(不得用超时阈值) | P5b 的完整渲染路径在 `spawn` 下绿;进程树只多一个子进程;`HeadlessGL` fork 预检无孤儿;OpenRA 在 Adreno 830 上 split SSIM ≥ 0.99;**G2**:`integration-spawn` 的用例名集合与 `integration-split` 逐名相同(名集合不同即意味着偷偷少跑);**臂证明门**:每条用例在自己的私有日志里留下子进程 pid 与 `transport=spawn`(ID-124 已证明这类假绿真会发生,而一条实际跑了 monolith 的 spawn 车道能通过其余所有门);性能只记录不阻塞,但 **socket 门铃相对 inproc condvar 的代价必须真采到**(P6 唯一预期的回归) |
| **P5f** 一切状态上 wire | 📝 **计划已起草(2026-09-19)**,未开工 | 计划 [`P5F-WIRE-COMPLETENESS.md`](P5F-WIRE-COMPLETENESS.md)。**P6 的前提“只是传输替换”今天不成立**,本阶段把它变成成立:`FieldOwnership.def` 的 **21 行 / 15 个 `BARRIER_PULLED` 字段**归零(其中八个 P5e 只为 Espryt 退役、**Magma 一条未动**);十二个 EGL forwarder 的控制面帧(P5c 审计 G4,原记 P6);per-context 语义的进程级静态量;`MGPipeFrontendKeyedRegistryScope` 的约 12 个站点;反向通道中直接触碰 client 内存的部分。中心机制是**双块演练**:`inproc` 下给两个角色各一份 `PipeInputs`,把拆进程的语义在不拆进程的前提下先跑一遍 | 双块车道 `integration-split` 全绿;`strict-expected-markers.txt` **为空**且两侧棘轮仍在;`FieldOwnership.def` 零 `BARRIER_PULLED` 行、`rsp` 逐帧读零;Magma 与 Espryt 跑同一套判据;关掉双块必须有具名用例由绿转红 |
| **P6** spawn transport | ⏸ **阻塞于 P5f**;计划与契约草稿已起草(2026-09-19) | `SocketTransport`(socketpair + fork/execve,envp 剔除 + 强制 monolith 双保险);`ServerMain`;`MOBILEGL_IPC_SERVER_PATH` + `dladdr` 兜底;有界重试握手;EOF 即时退出;device-lost latch;EGL forwarder 的控制面帧;P5c 留下的 `s_synced` / `g_syncedRenderStateParameters` 按 context 世代重置。包与依赖序见 [`P6-SPAWN-PLAN.md`](P6-SPAWN-PLAN.md):**a6**(只读审计,先跑)→ **c6**(契约 + 惰性线上行)→ **so / sm / cp / st / t6** 并行。契约草稿 [`P6-CONTRACT-DRAFT.md`](P6-CONTRACT-DRAFT.md)(规则 G:**角色不得指名进程局部句柄**)。**落地边界**:P6 只落 pbuffer / surfaceless / 离屏(够跑 `HeadlessGL`、等价 split 车道与 trace replay);`ANativeWindow*` 在子进程里无意义,**真窗口的到达是 P12**,到达 server 时具名拒绝。新设计只有一处:P5e 之后客户端不再等待,而对端进程会死——闩锁语义照抄 `ARCHITECTURE.md:504`,新的是“慢与死如何区分”(不得用超时阈值) | P5b 的完整渲染路径在 `spawn` 下绿;进程树只多一个子进程;`HeadlessGL` fork 预检无孤儿;OpenRA 在 Adreno 830 上 split SSIM ≥ 0.99;**G2**:`integration-spawn` 的用例名集合与 `integration-split` 逐名相同(名集合不同即意味着偷偷少跑);**臂证明门**:每条用例在自己的私有日志里留下子进程 pid 与 `transport=spawn`(ID-124 已证明这类假绿真会发生,而一条实际跑了 monolith 的 spawn 车道能通过其余所有门);性能只记录不阻塞,但 **socket 门铃相对 inproc condvar 的代价必须真采到**(P6 唯一预期的回归) |
| **P3b / P4b** 深化(Espryt) | 待排 | 按存储属主键控的发射游标与 view 索引重映射;`g_fboTextureSyncList`;`ResolvedTextureBindingMemo` / `SamplerPassMemo` / image sweep / program registry 重键;XFB scatter 搬到 client;删 fragColor 重推导 workaround 与 `g_broadcastMemo*`;raw-depth-fetch sampler 原生化;回读 / pack state;P4a 记下的 R-3 / R-4 / R-5 / R-7 / R-10 / R-11;`ProgramArtifacts.h` 的 NDK 尺寸钉 | 纹理 / program 场景;CTS `texture_*` / `shader_image_*` / `packed_pixels` 在 0.5 pp 内;每一个 Iris trace;`TextureUploadShapeScenario` 升级成门 |
| **P7** DirectVulkan(Magma)全量迁移 | 待排 | 其余 10 个子系统:`SetupDrawSnapshot` 探测字段塌成 dirty mask;占位纹理原生化;具名 UBO host payload(D-B8);内部 shader 烘焙;`VertexInputStateFactory` 内容寻址 CSO;D18 容器纪律保留 | 集成 + trace 在 Magma 的 push 与 split 下全绿;verify 零分歧;`nm -D libMobileGLServer.so \| grep glslang` 为空;CTS 0.5 pp 内。**再基线检查点:中点完成子系统 < 40% 立即重定基线** |
| **P8** emulation 下放 + 索引宿主镜像 + 协议广度 | 待排 | `MG_Impl/Pipe/HostResolve.cpp`(client 数组范围、最大索引扫描、`*IndirectCount` 解析,逐站点 reconcile);`Server/IndexHostMirror`;CopyImage 镜像搬到 client;viewport-array 回放验证;`generate_mipmap` 计划 + CPU 回退纹素;大 blob carrier(开放问题 11);无 present fence tick;`kCapDriverOrderedXfbCapture` | `'^DirectGLES\.Split\.'` 与 `'^DirectGLES\.'` 逐名相同;trace split 双后端 SSIM ≥ 0.99 含两个 `coherent_as_flush` fixture;`ClientArrayAfterComputeWriteScenario`;`create-indirect` 上 `roundtrips-per-frame` 读零;`index-mirror-bytes` 逐用例发布 |