diff --git a/docs/Disaggregated/CURRENT_STAGE_PROGRESS.md b/docs/Disaggregated/CURRENT_STAGE_PROGRESS.md index 12479a61..fe885aaa 100644 --- a/docs/Disaggregated/CURRENT_STAGE_PROGRESS.md +++ b/docs/Disaggregated/CURRENT_STAGE_PROGRESS.md @@ -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 普查重跑(需全集语料)。 diff --git a/docs/Disaggregated/P5F-WIRE-COMPLETENESS.md b/docs/Disaggregated/P5F-WIRE-COMPLETENESS.md new file mode 100644 index 00000000..4f1859f4 --- /dev/null +++ b/docs/Disaggregated/P5F-WIRE-COMPLETENESS.md @@ -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, "@"}`(`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 对** `@`(`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, "@"}`,带字段名和 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 回答。 diff --git a/docs/Disaggregated/P6-CONTRACT-DRAFT.md b/docs/Disaggregated/P6-CONTRACT-DRAFT.md index 8a5537e9..967cd035 100644 --- a/docs/Disaggregated/P6-CONTRACT-DRAFT.md +++ b/docs/Disaggregated/P6-CONTRACT-DRAFT.md @@ -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 diff --git a/docs/Disaggregated/P6-SPAWN-PLAN.md b/docs/Disaggregated/P6-SPAWN-PLAN.md index c02b1d55..45d7f561 100644 --- a/docs/Disaggregated/P6-SPAWN-PLAN.md +++ b/docs/Disaggregated/P6-SPAWN-PLAN.md @@ -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` 的条目由审计确认,现在不下结论。 --- diff --git a/docs/Disaggregated/README.md b/docs/Disaggregated/README.md index c5aedff3..d55002be 100644 --- a/docs/Disaggregated/README.md +++ b/docs/Disaggregated/README.md @@ -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 出口 | diff --git a/docs/Disaggregated/ROADMAP.md b/docs/Disaggregated/ROADMAP.md index 9ed502fb..bf2d2c12 100644 --- a/docs/Disaggregated/ROADMAP.md +++ b/docs/Disaggregated/ROADMAP.md @@ -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` 逐用例发布 |