Files
MobileGL/docs/Disaggregated/ROADMAP.md
T

283 lines
100 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# MGPipe 路线图
> 状态:**P0、P0.5、P1、P2、P3a、P4a、P5、P5b、P5c、P5d、P5e、P5f 已收官**。P5f 于 2026-09-20 完成主机门、Redmi 六个 clean-boot 臂、跨模型族审查及修复验证;最终验收代码为 WSL `cfca93c7885fd8db1e881f91189ba59090ae9c52`(Windows `c42577a4` 等价)。证据见 [`notes/p5f/close-report.md`](notes/p5f/close-report.md)、[`close-review.md`](notes/p5f/close-review.md)、[`device-report.md`](notes/p5f/device-report.md)。**P6 已收官(2026-09-22)**:全部包(a6 / c6 / lk / so / sm / cp / hs / dl / st / t6)已落地于 `feat/disaggregated`(头 `77cbd176`,2026-09-21),出口门第 1–6 项此前已达成;**第 7 项**(Redmi `2f7cbe2e` 的 reboot-clean 同热窗口配对 A/B)与**第 8 项**(三个强制性能数)已于 2026-09-22 采齐——门 7 判读为 **tie**(inproc vs spawn 的 client CPU p50 差 −0.15% / +0.16%,均在臂内散布 0.2–4.2% 之内,符号随臂序翻转;monolith 基线约贵 30%),门 8 三数齐:门铃 A/B tie(`clipark` 略差)、`SEG_STAGE` 字节/帧、分块后记录/帧。证据 [`notes/p6/gate7-device-ab.md`](notes/p6/gate7-device-ab.md)、[`notes/p6/gate8-doorbell-device.md`](notes/p6/gate8-doorbell-device.md)、[`notes/p6/gate8-wire-counters.md`](notes/p6/gate8-wire-counters.md)。逐包状态、出口门逐项、证据见 [`CURRENT_STAGE_PROGRESS.md`](CURRENT_STAGE_PROGRESS.md);设计见 `ARCHITECTURE.md`。P5e 的历史性能与 E1 对照债保留在阶段行、[`P5E-RUNAHEAD.md`](P5E-RUNAHEAD.md) 和 `MEASUREMENTS.md`,不由 P5f 的新对照代为关闭。**CI / inproc 功能补齐已收官(2026-09-21)**:拆分主产物 + inproc 主验收、完整 retrace 双 transport、18 项 CI 门、双 ABI APK / AVD 全绿并合入 `feat/disaggregated`(`e1bf3677`),(原记 `CURRENT_STAGE_PROGRESS.md` §2.8;该文档已为 P6.5 重写,原文在 git 历史)。**2026-09-22 起进入 Ph / P3b-P4b / P7 统筹推进**:三行逐项审计、判定与波次见 [`notes/p7/PLAN-PH-P34B-P7.md`](notes/p7/PLAN-PH-P34B-P7.md),裁定见 [`notes/p7/INTEGRATOR-DECISIONS-P7.md`](notes/p7/INTEGRATOR-DECISIONS-P7.md)。
> **P5f 后续 Magma run-ahead 已完成**(`194382c9`,2026-09-20):主机真实排队、credit、负控及 Vulkan 同步验证通过,Redmi 同包 Magma 开/关与 GLES 三臂 FCL 世界实测通过。见 [报告](notes/p5f/magma-runahead.md)。P6 / P7 全阶段状态不因此改变。
> **终局(2026-09-22 改判)**:client 与 server 可以在**不同机器、不同 OS / 架构**上,经 **TCP** 连接;同机的 `spawn`(AF_UNIX + 共享段)保留为本机形态。传输栈拆成**两根独立可选的轴**——控制面(`ITransport`:`fork` 继承 fd / `unix:<path>` / `tcp://host:port`)与数据面(`ILink`:共享段 `ShmLink` / 字节流 `StreamLink`)——由握手协商、可以混搭(同机可以控制面走 TCP、数据面走共享段以降开销),上层只见 `ITransport` + `ILink`,**不得按链路种类分支**。此前 [`P6-ENDSTATE-REVIEW.md`](P6-ENDSTATE-REVIEW.md) 假设的 AVF pVM / `AF_VSOCK` 终局撤回(该文顶部有更正)。这一改判把 **P6.5 从条件项变成 IPC 跑道的关键路径**,把 wire 定宽 + 布局摘要从 P7 / hs 的"以后"变成 P6.5 的第一个包,把 Ph(含配对 / 认证)变成 P12 非 loopback 监听的前提;设计见 `ARCHITECTURE.md` §11.9。
## 通用纪律(每个 commit)
- 默认 ALL target 必须完整构建;禁止提交热路径插桩(CI grep 门)。
- **每个门必须能因它存在的理由变红**:每个门带阴性对照并真跑过一次红(R-16);公共 GL 看不见的改白盒断言。
- 正确性门是硬门;**性能对着 pull 臂只记录、不作阻塞门**(用户 2026-09-08:push 比 pull 多约 10% 逐线程 CPU 已被接受;专门的优化阶段排在路线图推完之后)。唯一不可谈判的是数字必须真的采到。
- 每阶段出口跑一次五部分门(`ARCHITECTURE.md` §13.2);性能判据是**逐线程 CPU 时间**;设备对比走 reboot-clean + 同热窗口配对 A/B,只用 Redmi `2f7cbe2e`(`devices/pin-verification-2026-09-07.md`);Windows 机器不是正确性门。
- G1:pull 构建的符号集与 `.text` 字节对基线恒等(每阶段 0/0/0/0);G2:pull 与 push 的 ctest 名集合相同;G14:测试名只增不删;G5:保护区字节一致。
- **拆分不借机顺手修 `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 → **P5f** → P6 → **P6.5 → Ph → P12** → P9 → P10 → P11(同机臂)。目标顺序:先完整的 separate-thread rendering(P5b,已达成),再让 `inproc` 变成只经 wire 交换的诚实两角色(P5c),再把它的 CPU 代价压到可接受(P5d),然后退役 draw path 上的 lockstep(P5e:client 发了就走,server 只从记录带来的句柄解析对象),然后是 separate-process transport(P6,此时只是传输替换)。P6 之后(2026-09-22 定):传输栈两轴化并能跨机(P6.5),server 能安全地接受不是它自己产生的连接(Ph),server 成为拥有显示的常驻应用(P12),异步反向通道与节奏(P9、P10)建立在 P6.5 的消息式 reply 之上,同机的 ≥16 MiB 采纳(P11)最后。
## 阶段
| 阶段 | 状态 | 落地什么 | 验收门 / 证据 |
|---|---|---|---|
| **P0** 卫生、度量、门、骨架 | ✅ | 边界计数器;`PipeCalls.def` 完整目录 + payload POD + 生成器 G1–G7 + CI `pipe-gates`;`gen_pipe_dirty_surface.py`;`check_doc_citations.py`;`MOBILEGL_PIPE_*` 开关;`MG_Remote/{Protocol,Transport}` 骨架 + `protocol.fbs` + `flatc-check` + `MG_Test/Wire`;三个严格 no-op 收益;spike A / B;retrace `--env` 透传 | 单元 / 集成 / trace 逐名不变;wire 层测试绿;两台设备字节 / 调用基线在案;spike 结论。`MEASUREMENTS.md` §1 |
| **P0.5** 值头与制品头抽取 | ✅ `5d99ee43` | `MG_Pipe/MGPipeValueTypes.h`;`ProgramArtifacts.h`;`Visit()` 归档 + `sizeof` 绊线;CI `-H` include 闭包断言(`scripts/check_include_closure.py`)+ `scripts/symbol_report.py` | 测试名零删除、`.text` 不变、符号 0 增 / 0 删 / 42 重命名 |
| **P1** `PipeInputs` 替换与 verify harness | ✅ | `MG_Backend/MGPipe/PipeInputs.h`(63 字段);277 处箭头 + 58 行非箭头逐条转换;逐 verb 类填充点;逐 verb 世代 poison;G4 影子比对器 + verify CI 模式;push-on-mutation 三字段 | pull `nm` 不变;单元 1485×3;`integration-gpu` 878;`integration-verify` 818 零 Fatal;79 retrace verify 79/79 armed 零分歧。§2 |
| **P2** 渲染状态 CSO + 第一片 Track H + 残余值块 | ✅ `738b289d` | Tracker(dirty 位、5 个聚合世代、抑制器);dirty-surface 成门;`MGPipeRenderStateSpans` chunk 表 + G7;`CsoCache`(64);`create/bind_render_state` + `set_dynamic_state`(`SyncRenderState` 一行不动);pixel pack / patch / attrib defaults;`ResidualValueBlock` 棘轮 1248 → 8;Espryt slot 表 + Magma vertex input 重键(11 条 memo 删除 + 2 条重键);`MOBILEGL_PIPE_LEGACY_MEMOS`;三个 capability 真存储 | G1 4 处认定 resize、`.text` +160 B;G2 名差 0;单元 1566×3;`integration-gpu` 916 三臂;retrace 79/79 push + verify;对照 44/44。**GO/NO-GO(2026-09-08):继续**。§3 |
| **P3a** handle wave 1(Espryt):buffer、VAO | ✅ `fde5fda3` | 九条 `resource_*` + 五条 vertex-input 调用接线到句柄形 `MGPipeResourceOps`;第七张 Espryt slot 表;`ResourceRespecify` 的 `kNeedsAck` 谓词;VAO twin 六条身份 memo 在句柄臂退役、四条重键;`mpr` 定义;位 7/8,push 默认 `0x1ff` | 五部分门全绿:G1 0/0/0/0 `.text +0`;G5 十一函数;单元 1619×3;`integration-gpu` 958 五臂;verify 842;retrace 79/79 ×2。实际 1 天。§4 |
| **P4a** handle wave 2(Espryt):FBO / 纹理 / sampler / program | ✅ `8c458cd5` | 十四条族调用接线(framebuffer state、sampler state / view、texture params、shader images、shader state、draw / dispatch program、global constants);纹理 / renderbuffer 复用 `resource_*`(目录不加行);六种 kind 按 `{slot, gen}` 重键;`CompositeResolver`;`Named = 3` framebuffer 记录;消费者门 + 客户端依赖表;位 9–12,push 默认 `0x1fff`;emulation 在 split 下具名 Fatal | 全门:G1 0/0/0/0;G5 17 区;G2 2902;单元 1785×3;`integration-gpu` 1117 七臂;verify 920;retrace 79/79;八族拒绝普查 0;`PipeApplyPeek` 白盒对照。实际 1 天。§5 |
| **P5** 传输 + inproc applier + 发射表 | ✅ `ff2994d9..37fc4fdb` | 同一 codec 上的 `InProcessTransport`;四种 build flavour;发射表三类 / caps mirror / reply mailbox;apply-thread context 终身持有;lockstep verb barrier;tight ReadPixels;persistent-map 块推送;G8 字段归属生成器;split 测试 / CI / APK 车道;八包(c0 契约、w1 codec、s1 session、p1 归属、b1 persistent map、t1 车道、c1 路由、v1 server)+ j0 / x2 / v1-r3 收尾 | joint:OpenRA inproc 2/2 SSIM 1.0;`integration-split` 21/21;E1 14/14 红;G1 0/0/0/0。收尾头全门在 E3(a) 停止(设计性 skip 被判红),Part 2/4 未到达;收官审查 1 blocker / 10 major / 2 minor → P5b r1/r2。§6 |
| **P5b** `inproc` verb migration | ✅ `37fc4fdb..82683d4a` | class-C 普查决定顺序;d1 19 / i1 7 / t2 6 / f1 11 槽迁移;sync 五槽;具名 blit;GLES mip descriptor;r1 / r2;收官审查三项修复。发射表 A=2 / B=54 / C=15 | 主机全门 `348d22a4` complete;`integration-split` 107/107;合并普查零回退、79 trace 72/6/1;**Redmi 正确性 8/8 + 四臂 A/B、barrier tax 首测**。§7 |
| **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 |
| **P5f** 一切状态上 wire | ✅ **已收官(2026-09-20)**;验收代码 Windows `c42577a4` / WSL `cfca93c7885f` | f0 普查、f1 双块、fc 控制帧、fe Espryt 字段、fm Magma record consumers、fs 静态世代/角色及 liveness、fr registry、fv 反向回调均已合并。字段归属 **41 RECORD_SUPPLIED / 6 APPLIER_DERIVED / 0 BARRIER_PULLED / 16 FATAL**;strict / dualblock 预期 marker 集合均空。控制帧与静态量工作已完成,不再列为 P6 待实现项 | unit **2310 = 2300 PASS + 10 skip**;GPU **1377** 零失败;ordinary / strict split **180 = 176 PASS + 4 skip**;双块 **212 = 206 PASS + 6 精确具名 skip**;双后端 RSP 门 **2 PASS / 0 skip**、逐帧 rsp=0。G1 `.text` 10806051、symbols 27815 恒等且 0/0/0/0;G2 3016 同名;G14 3646→3744 无删名。逐包与收官修复均有 red-once;Claude 异族终审 P1/P2 已修;Redmi 六 clean-boot 臂 **60 PASS / 6 skip / 0 failed**、36 个窗口 rsp=0。详见 [`close-report`](notes/p5f/close-report.md) |
| **P6** spawn transport | ✅ **已收官(2026-09-22)**;全部包已落地于 `feat/disaggregated`(头 `77cbd176`,2026-09-21),出口门第 1–6 项此前已达成,第 7/8 项于 2026-09-22 采齐。收官前核出的三项:server 进程侧 `PipeStats` 从未初始化(`srv=0 srvpark=0` 是结构性零,门 8 无 server 半边)**已在收尾中修好**;184 符号棘轮 CI 门(契约 §12.1)与契约 §2 分给 hs 的 `LinkTerms` / `Refuse` / 指纹拆分**未落地**,移交 P6.5(负控 S6 随之) | 十包全落地:**a6** 进程装配/链接边界审计 → **c6** 规范契约 + 数据面缝惰声明 → **lk** `RingControl` 按写者重组 → **so** `SocketTransport`(AF_UNIX SOCK_STREAM + SCM_RIGHTS,12 条契约用例)→ **sm** 两个独立启动的进程(非 fork,name 汇合、共享匿名段,四守卫接线)→ **cp** EGL 控制帧跨进程并应答(`InitCapabilities` wire kind 11)→ **hs** build stamp 显式 present 入指纹 + `abiMajor/Minor` 补检 + 真实 pid → **dl** device-lost 闩(取自描述符挂断绝不取自超时)+ `Session::Fail` 漏斗(`FatalFamilies.def` 词表 + 90 站点归一 + `SessionFault` 帧命名家族)+ Fatal 普查门 → **st** fb-slot memo 具名 Fatal → **t6** `integration-spawn` 车道(102/102)+ 名集合结构性一致门 + per-role 日志(`<base>.client.log` / `<base>.server.log`)。Android CI 新增 `spawn-acceptance` 臂,App 从 `nativeLibraryDir` 解析 server 路径。真机 Redmi `2f7cbe2e` 上 `OpenRA.DirectGLES` spawn ssim=1.0 连跑三次 + 6 case 绿;`DirectVulkan` 真机分离路径不确定分歧(inproc 与 spawn 同表现→ Magma × 真实 GPU,非 P6,桌面 lavapipe 为 1.0)。边界仍为 pbuffer / surfaceless / 离屏;真窗口归 P12。契约 [`MG_Remote/CONTRACT-P6.md`](../../MobileGL/MG_Remote/CONTRACT-P6.md);a6 产出 [`a6-audit-v1.md`](notes/p6/a6-audit-v1.md)、[`a6-link-experiment.md`](notes/p6/a6-link-experiment.md)(**server 不能不链 `MG_Impl`**:184 符号,P7 101 / P3b-P4b 14 / 共有 60 / P6 自己 6);终局重审 [`P6-ENDSTATE-REVIEW.md`](P6-ENDSTATE-REVIEW.md)。**收尾四项(2026-09-22,工作树未提交)**:wire 计数器——`PipeStats` 新增 `ByteClass::StageSegmentBytes`(`PipeWireCodec.cpp:878` `StageAllocate` 唯一收口)与 `CallClass::WireRecords`(`:1188` `EncodeRecord` 提交路径)+ staged-blob 直方图,汇总行新增 `seg=` / `wrec=`;`ServerMain.cpp` 补 `PipeStats::Init()` / `Shutdown()`(spawn server 此前从不武装计数器——修前 0 条汇总行、修后 29 条,SSIM 1.0 不变);`MOBILEGL_PIPE_STATS_FILE` 按角色派生 `<base>.client.json` / `<base>.server.json`(走 `RoleLogPath` 同一导出规则,基名不指向文件);工具入库 `tools/device_bench/p6/{ab_session.py,render_ab.py,README.md}` 与 `tools/trace_replay/run_android_retrace_local.py`(+19 行 `MOBILEGL_TRACE_PACKAGE`),`pin_device.sh` 新增 `2f7cbe2e` 条目(本板 GPU 钳 1050 MHz:`thermal_pwrlevel` 写 0 读回 1、`max_gpuclk` 只读 1 050 000 000;绝对值与 1100 MHz 时代的 `35d0befa` 行不可比)。 | 出口门(契约 §9)1–6 ✅:G1 pull 0/0/0/0、`.text` 恒 `0xa52203`;`integration-spawn` 与 split 名集合一致(可比 102==102)且同数绿;进程树运行时恰多一子进程、结束为零;每条 spawn 条目记子进程 pid 与 `transport=spawn`(`--require-spawn` 门);每包 red-once。**7/8 已采(2026-09-22)**:**门 7** 真机 reboot-clean 同热窗口配对 A/B(Redmi `2f7cbe2e` / M332BF / SM8750 / Adreno 830v2,CPU 定频 little 1555200 / big 1958400,GPU 钳 1050 MHz(本板 `thermal_pwrlevel` 写 0 读回 1、`max_gpuclk` 只读 1 050 000 000;绝对值与 1100 MHz 时代的 `35d0befa` 行不可比,只有同会话配对可比),`minecraft-1.21.4-rd12-odinlite-in-world` + DirectGLES,~1471 draws/帧;六臂交错 × best-of-3:18/18 runner 退出 0、36/36 pin check `PINNED`、0 `Fatal`;client CPU p50 ms/帧 inproc 7.880/7.889 vs spawn 7.876/7.869,均值 **−0.15%**,符号随臂序翻转,臂内散布 0.2–4.2%;第二会话 inproc/spawn 交错 × best-of-2:4/4 OK、8/8 `PINNED`、0 `Fatal`,7.987/7.989 vs 7.992/8.009 = **+0.16%**)——判读 **tie**(差值在臂内噪声内),如实记录、不设门;monolith 基线约贵 30%(−29.8% / −29.9%)。**门 8①** socket 门铃 vs inproc condvar:client 121.1 waits/帧、5.6–6.1 parks/帧,两传输逐字相同(run total 30 401–30 407);server inproc 10 866–11 963 waits/帧、5.0–6.2 parks/帧,spawn 10 415–10 416 waits/帧、4.66–4.90 parks/帧——socket 阻塞读**既不更便宜也不更贵**(契约 §9「阻塞读可能更便宜」未兑现),spawn server waits 少 8.9–12.9% 归因于 handoff 纪律而非门铃本身,唯一 socket 略差的列是 `clipark/f`(6.07 vs 5.60/5.75)。**门 8②③**(inproc,真实 trace retrace):`SEG_STAGE` 字节/帧 OpenRA 130.8 KB median / 8.01 MB steady mean(含启动负载 8.07 MB),rd12 816.2 KB median / 1.53 MB steady mean(含负载 10.79 MB,rd12 首帧 981 654 draws vs 稳态 ~1471,run-wide mean 约 7× 稳态,两个读法都报);分块后记录/帧 OpenRA 211 median / 206 mean,rd12 7 497 median / 7 504 mean;`maxrec=1808` 与分块前相同(默认 8 MiB chunk 预算在这些负载上从未被打到)。证据 [`notes/p6/gate7-device-ab.md`](notes/p6/gate7-device-ab.md) / [`notes/p6/gate8-doorbell-device.md`](notes/p6/gate8-doorbell-device.md) / [`notes/p6/gate8-wire-counters.md`](notes/p6/gate8-wire-counters.md) |
| **P6.5** 传输栈两轴化 + 跨机 | **第一波已落地**(`feat/disaggregated@fe28bdb5`,2026-09-22):全 TCP 控制 + StreamLink 数据,WSL client ↔ Redmi server。四切片代码审查无硬缺陷 + 两修复(`AcceptPair` 重试 `EINTR`/`ECONNABORTED`、supervisor 自设 `MOBILEGL_IPC_ROLE=server`,均 `MG_Remote` 门内 G1 不动,见 [`code-review-findings.md`](notes/p65/code-review-findings.md));固定制品(两端 stamp `p65-acceptfix-20260922`)再验收:unit 2399、integration-tcp loopback 102、**device-102 102 全 ARMED**、代表性 retrace OpenRA 1.0 全 ARMED。**残余(CI/后续)**:完整 39 例 device golden 矩阵 + 必测数。设计见本表下方「立即的下一项」节 | 包:**wf** wire 定宽与布局摘要——`wireFingerprint` = `PipeFields.def` 派生的逐成员 name / `offsetof` / `sizeof` 摘要 + 有序目录摘要 + 记录头布局 + 两个 blob codec 版本 + `kOpCount` + 字节序 / 指针宽度,**保留** `sizeof(MGPCaps)` / `sizeof(DynamicBackendParameters)`(wire 事实)、删 `sizeof(GLFunctionsTable)`;`buildFingerprint` 只在 `Dial == Fork` 下比较;`DynamicBackendParameters` / `MGPCaps` / `RenderStateParameters` 整块 blob 的定宽化(两端是不同编译器产的不同二进制,`long` / padding / 位域差异都是静默读错状态);`Refuse{code, detail, 对端值}` 控制消息,握手路径零 abort → **ct** 控制面端点种类(`fork` 继承 fd / `unix:<path>` / `tcp://host:port`;`DialMode{No, Fork, Connect}` 与 `MGPipeShouldDial()` 按契约 §3 补齐;TCP keepalive 失败**作为传输层报告的挂断**进 dl 的闩,不是 apply 超时)→ **nd** 数据面协商(`LinkTerms{dataPlane, delivery, wireForm, maxReplyBytes, cmdWindowBytes, stageWindowBytes}` 入 Hello / Welcome,四个尺寸由 **server** 陈述;`dataPlane ∈ {SharedSegments, Stream}`;`auto` 的判据是**段实际交付成功**而不是地址判断,回落必须具名入日志与 stats;链路工厂在会话建立时选定一对,之后不变——缝是 setup-time 的,契约 §8.2)→ **sd** 共享段交付与控制面解耦(`ShmLink` 自带 AF_UNIX aux 汇合名、在 `Welcome` 里公布,`SCM_RIGHTS` 只走它;于是**控制面 TCP + 数据面共享段**在同机是合法配对;`ITransport` 收窄为纯控制帧,描述符传递归数据面)→ **sl** `StreamLink`(分块封帧;watermark 变消息且**规则逐字保留**——late-never-early、`>=` 不是 `==`、倒退是 Fatal;`appliedSeq` 修正案 + flush-on-idle;reply 变自带 seq 的消息,本链路上删 slot 池 / 取模 / stamp 自检;`SEG_STAGE` 变发送窗口,三个 cursor 要么完整复活要么不用、不留中间态;program archive 与 `draw_vbo` range 尾分片;`GetCaps` 两个 blobref 变消息;每方向每进程一条专用读线程)→ **t65** 车道。上层纯度:`SessionProducer` / `SessionConsumer` / `ServerLoop` / `PipeWireEncoder`、`ClientSession.cpp` 的 `kSegEvent` 挂载与 `ResourceTracker.h` 的 writeback 解析都只经 `ILink`。设计 `ARCHITECTURE.md` §11.9 | 门:inproc / spawn 车道结果数与逐线程 CPU 逐字不变(`ILink::ReserveRecord` 虚调用代价**实测**,`RingProducer::Reserve` 失去内联是具体风险);纯度 grep——`ShmLink` / `StreamLink` / `RingControl` / `ReplySlotPool` / `EventRingConsumer` / `m_shm.` 只出现在 `Transport/` 与其测试,上层零链路种类分支;**2×2 矩阵车道**(控制面 {unix, tcp} × 数据面 {shm, stream},loopback)名集合与 spawn 一致且同数绿,每条目记 `control=… data=…` 的 arm 证明(ID-124 的形状);跨机臂一条真 trace 逐像素;wf 三条 red-once(临时树里同宽成员互换 → 摘要变而 stamp 不变;`wireMajor = 99` → 两端具名拒绝、server exit 0、client 不回落 monolith;仅 stamp 不同的对端在 `Dial != Fork` 下连接成功——正控制);**必测数**:逐帧 `kWaitReply` 次数(`ResourceCreate` / `SetTextureParams` / 纹理半边 `ResourceSubData` 都在此列,TCP 上每次即一个 RTT)、`SEG_STAGE` 字节/帧对链路带宽、`PRESENT_CREDIT` 1..3 配对、混搭(tcp + shm)对(unix + shm)的逐线程 CPU 差 |
| **Ph** 不可信对端加固 + 配对 | **已审计(2026-09-22,`notes/p7/PLAN-PH-P34B-P7.md` §1.1)**:P6.5 已在 LAN 上跑 `tcp://0.0.0.0`,「P12 之前」的顺序陈述落后于树。必需小件(supervisor 命名子进程死亡、两处漏斗旁路、D11 五处上限、常量时间令牌 + 数据面绑定、`SEG_EVENT` 弃投闩)随 P7 波次推进;fuzz 臂 2 在 P7 之后;OQ-21 已裁定令牌足够(ID-P7-3)。**进展(2026-09-22 深夜)**:supervisor 命名子进程死亡 + 两处头层漏斗旁路已随 wave 0 落地(`origin@801f8eda`,`abort_sites` 79 成下行棘轮);其余小件是簇 **F** 包:**片 1–3 已到集成树(2026-09-22 夜,ID-P7-37)**——令牌合一 / ≥16 字节具名拒绝 / 无令牌只 loopback / smoke 进 CI;`Welcome.dataNonce` 绑定,TCP 上 250 ms 配对窗口消失,`wireFingerprint` 变更;PH-8 钳制证明——片 4–6 与 PH-7 (5) 需要对端字节的 fuzz 驱动,另派 F2(原顺序:常量时间令牌 / `Refuse{Authentication}` / ≥16 字节 + `tcp_supervisor_smoke.py` 接 CI → `Welcome.dataNonce` 数据面绑定 → PH-8 单测 → D11 五处 + PH-2 → PH-6 drop-with-latch → PH-1 (3)(4));逐日状态见 [`CURRENT_STAGE_STATUS.md`](CURRENT_STAGE_STATUS.md) | `Session::Fail` 的策略翻成每会话闩(**按 ID-P7-1 重定界**:98 处 `SessionFail` + 6 处 `WireLogFatal` 的字面翻转拒做——≥9 处返回引用没有诚实返回值;只对对端字节可达站点 latch-and-decline;P6 dl 收口到的是**两个漏斗 + 两处头层旁路**(`Server/StagedShadow.h`、`Server/StagedTextureStore.h`),不是一处;P6.5 fork-per-session 已给出「下一连接照常服务」);handle slot 预算(`BackendSlotTable` 的两条拒绝从 `MOBILEGL_ASSERT` 变 release 下的具名拒绝,杜绝静默 null twin);readback 尺寸门挪 server 侧且在 resize **之前**设绝对上限;`StagedTextureStore` 按该 level 宣告范围设界;`ArchiveVector` 按 `count * sizeof(element)` 设上限;`SEG_EVENT` 溢出不再能让一个不排空(或仅涓流排空)的对端在 30 秒内、乃至瞬间用 `Fatal{EventRingOverflow}` 打掉承载它的 server 进程(drop-with-latch,不等 PH-1,ID-P7-2;原文「60 秒」是两轮 30 s 的算术上界);**配对 / 认证**(`Hello` 带令牌与无令牌只 loopback **已由 P6.5 ct 落地**;欠常量时间比较、`Refuse{Authentication}` 入词表、≥16 字节、**数据面按 `Welcome.dataNonce` 绑定到已认证的控制连接**(今天 `AcceptPair` 按到达顺序配对、从不查 peer)、fork 前认证;OQ-21 裁定令牌足够、不做 TLS);段 / 窗口尺寸由 server 钳制(P6.5 nd 已陈述,欠一条钳制单测) | 非 loopback 监听在无令牌时具名拒绝(机制已在;名字入词表 + `tcp_supervisor_smoke.py` 接 CI 后算达成);畸形帧 / 超界计数 / 不排空对端的模糊臂下 server **不 abort、会话闩住、下一连接照常服务**(臂 1 机制已在、欠端到端脚本;臂 2 = PH-1 (3)(4) + D11;臂 3 = PH-6);`Session::Fail` 站点普查门扩到 server 镜像且 `abort_sites` 变棘轮(今天只扫 `MG_Remote`、站点数记了不比,wave 0);契约 §12.2 五处无界分配各一条 red-once(**五处全开**,不是两处) |
| **P3b / P4b** 深化(Espryt) | **已审计(2026-09-22,`notes/p7/PLAN-PH-P34B-P7.md` §1.2)**;Espryt 项与 P7 文件零重叠,作为 P7 的并行流(wave 2-D)推进。**D1 / D2 / D3 已落地(`origin@22761a69`,2026-09-22)**:D1 = XFB 两洞(孤儿目标保留 varyings、回写事件切片)+ 回调 10→9 + 四个 resolver 具名启动期 stop + readback 家族三臂(+39/臂,readback 半边其实早已关,ID-P7-24);D2 + 返工 = `TextureViewAliasScenario`、`TextureUploadShape` 成门(散点内缩后零补丁 red-once)、`espryt_memo_purity.py`、`SubsystemDeps.def`(R-5)、12 场景 split 普查(+76/臂)(ID-P7-26/29);D3 = view 在 owner 上传后陈旧的产品缺陷修复(clean gate 读 storage record,三臂 3 红→14/14)+ D-K2 六处读者读同一张表(ID-P7-28/30)。**余**:R-3 另一半(`MGPSamplerView::Target` 谓词)、`KHR-GL46.texture_view.coherency` 的 split 读数、CTS AFTER(wave 4);~~Iris trace 普查结果(wave 3)~~ 已有头上结果(`3c80cd62`,[`notes/p7/iris-census-1135c664.md`](notes/p7/iris-census-1135c664.md));逐日状态见 [`CURRENT_STAGE_STATUS.md`](CURRENT_STAGE_STATUS.md) | view/属主发射游标**别名场景**(代码半已由 P4a `TextureEmit.h` 属主键控游标 + `TextureObjectView::ToOwner*` + P5e tx2 落地,欠验证);`g_fboTextureSyncList`(分离安全——记录臂先返回、退路是具名 Fatal;四个 resolver 改启动期具名 stop,文本删除受 P5e 裁定 1 约束归 P13);memo 重键**已落地**(P4a/P5f),补纯度 grep 门;~~XFB scatter 搬到 client~~(P5c/P5f 以 server staged shadow + `OnBufferWriteback` 落地;剩两处真洞:孤儿捕获目标被静默丢弃、回写事件无界——>128 KiB 即 `Fatal{EventRingOverflow}`);删 fragColor 重推导 workaround 与 `g_broadcastMemo*`(动 pull `.text`,归 P13,只改注释);raw-depth-fetch sampler 的 monolith 清理(可选);**回读收口**(pack 半已关:`set_pixel_pack_state` APPLIER_DERIVED + server 中性读 + client 侧 PACK-PBO 散射;readback 半开:`get-tex-image-shadow` / `copy-image-shadow-mirror` 非 monolith 下仍 abort、split 零覆盖);P4a 的 R-5(D-K2 规则合一处)/ R-11 必须,R-3 / R-4 / R-7 / R-10 可选;~~`ProgramArtifacts.h` 的 NDK 尺寸钉~~(P6.5 portable archive v2 + 布局指纹取代);链接实验分给它的 **14 个符号**(D12,棘轮落地后 Tier 1) | 纹理 / program 场景(split 三臂零覆盖,先普查首阻塞再注册);CTS `texture_*` / `shader_image_*` / `packed_pixels` + P4a 欠的 `direct_state_access.framebuffers*` 在 0.5 pp 内(caselist 入库 + monolith 基线 + delta 工具,今天三者皆无);每一个 Iris trace(CI 管道 P6 已建;**`3c80cd62` 主机结果**:39 例 × 双后端 × {monolith, inproc, spawn} 231 次全活,split 与 monolith 逐字节相同 70 / 77 行,7 行分歧均过阈值、无 Fatal;`texture-remint-pull` 未到达,**P9 例外表为空**——[`notes/p7/iris-census-1135c664.md`](notes/p7/iris-census-1135c664.md) §3–§4);`TextureUploadShapeScenario` 升级成门(Mali 增量无设备,按 Adreno 记录) |
| **P7** DirectVulkan(Magma)全量迁移 | **进行中;wave 0–2 已落地,40% 检查点 6/9 已过(2026-09-22,`origin@b089e0de` + 集成树上的 B3)**:审计 + 五波计划见 `notes/p7/PLAN-PH-P34B-P7.md` §1.3 / §3,规范 `MG_Remote/CONTRACT-P7.md`,裁定 `notes/p7/INTEGRATOR-DECISIONS-P7.md`(ID-P7-1..33)。**wave 0** 仪器(Magma 两进程车道三臂、`link_ratchet.py` 186 基线、普查门 79 站点下行棘轮 + `MGPipeSessionFailHook`、设备跑器 `--matrix/--repeat/--archive-dir`、CTS caselists)`origin@801f8eda`;**wave 1** 设备窗口 #1(门 3 分母 36、E0–E7 归因、CTS `$BASE` 五块);**wave 2** X1(arm64 persistent-map tracker 从不认领 fault)、F1(split 起会提前)、C(`StateObjectDeathOps`、OQ-8、OQ-10、vertex-layout 拆分)、A(对齐 / native-range)、B(mip 半退役、深度 mip 烘焙 (A)(D)、copy-image、default-color-blit、回读 fence)、B2(multisample shape/aspect、AllocatorDebtScope ×2 删、(B′)、棘轮 **186→173**)、B3(**OpenRA 真机分歧的真因**:wire 臂 completed-frame-serial floor 在中帧 pooled-fence 提交退休时无证据上升 → 帧首次 `glBufferSubData` 走无序 host memcpy;计数 26→0,spawn 臂 red-once;ID-P7-33)——**树上 `@P7` 拒绝站点归零、§3.2 六退役全成、Magma 三臂 95/74/76**。**真机**:p7w4 门 3 AFTER 34/36 与 monolith 差 ≤ 0.0005;OpenRA p7w5 **27/27 golden**(ID-P7-34,项关闭);bsl-esc-menu = server 死 VkBuffer 无界累积(一帧 1.3 M 调用,ID-P7-32,**M2** 在修)。**已到集成树(2026-09-22 夜,ID-P7-37)**:B2 返工 r1 + r2、B3 返工 + 修复轮、V1、F 片 1–3 已推送 `origin@87584d0b`(2026-09-22 夜,ID-P7-39;pipe 门全绿:magma 97 / 76 / 71、full 535、verify 构建 1140 / 1074、OpenRA split 回放 0 条 unsound;四份 fable 审查 land with fixes,修复轮已落地,ID-P7-44);**M2 + M2 r2 已推送**(主机 bsl spawn server 825 → 546 MiB;memo destroy-epoch 关闭 wire store 句柄复用的 ABA;第二个泄漏 = descriptor set → M3;ID-P7-46);V1 修复 r2 已推送(read 侧负控数到 2、FATAL=0 摘要在 Close 前写出,ID-P7-47);**在跑**:F2 待派;窗口 1b 矩阵在跑(配对窗口在直连 LAN 上同样触发,ID-P7-41)。提交尾注已按用户令改写(origin `3c80cd62`)。**2026-09-23 更新(ID-P7-49–54)**:M3 已在 7ea7ce89 推送;B4 已集成于 pipe b19297d5,源码审查 land、完整集成门全绿(ID-P7-51)。p7w6 DirectGLES × TCP 38 例对照与 Photon / D24 同 APK monolith 对照已结案(ID-P7-52/53);两项错图亦在 monolith 复现。F2 PH-4/5/PH-2 已在包分支 commits `2c6a6e25` / `51480f8c` 落地并通过 Luna 审查(ID-P7-54);该矩阵不替代门 3。**CI 与门 4(ID-P7-55/56)**:CI 自 P6.5 起的七个红因已修;棘轮 171,`p7-magma` 余 86 标 `# P13`,门 4 关闭。**余**:F2 PH-6 / PH-1 (3)(4) / PH-7 (5)、fuzz 臂 2 与完整包门;wave 3 棘轮 88;wave 4 CTS AFTER 和门 3;wave 5 fuzz 三臂与整体审查。逐日状态见 [`CURRENT_STAGE_STATUS.md`](CURRENT_STAGE_STATUS.md) | `38919d45` 已补实际游戏的 server buffer consumers 和 Android 旋转 blit([报告](notes/p5f/magma-inproc-fix.md));`194382c9` 又完成 Magma run-ahead、GPU 延迟退休与 VAO 身份碰撞修复([报告](notes/p5f/magma-runahead.md));`ed507b1a..d56c0e83` 29 个 DirectVulkan 提交的 shape 波(随 `e1bf3677` 合入,本行此前未记)已退役 packed-float mip 原生 blit(`5310ff62`)、resolve(`ec4ca06d`)、占位图像(`WirePlaceholderImages.inc`)、copy-image 端点、XFB 捕获 span、纹理回读;`70fb6689` 链关闭 inproc RD32 性能回归(86.85→116.73 fps)。其余工作(2026-09-22 审计改判):~~`SetupDrawSnapshot` 探测字段塌成 dirty mask~~ 与 ~~`VertexInputStateFactory` 内容寻址 CSO~~ **本阶段拒做**(`SetupDraw` 在非 monolith 传输下分支到 `SetupWireDraw`,monolith 快照机制在 split 下不跑;无性能门;ID-P7-5);占位纹理原生化**已落地**(缺 split 车道条目);具名 UBO host payload(D-B8)走了直绑常驻 `VkBuffer` range 的备选,欠一次测量 + 裁定关 OQ-6;内部 shader 烘焙**半已落地**(颜色 blit `WireColorBlitSpirv.h`,仅 split 臂),欠深度 mip 烘焙 + `MOBILEGL_BAKED_INTERNAL_SHADERS` 新鲜度门(0 处);D18 按 ID-P7-6 读法一句闭。**新增必须项**:Magma `StateObjectDeathOps`(六处特判)、OQ-8 归档 `storageBlocks`(`MagmaProgramSource::EnsureStorageBlocks` 每 draw 重跑 SPIRV-Reflect)、OQ-10 `kCapResidentSubData` 从未发布(split 下双后端退回就地 memcpy)、两处 `MagmaP7AllocatorDebtScope`。**P6 真机发现(2026-09-22 归此)**:`DirectVulkan` 分离路径在 Redmi 上两次观测 0.988998 / 0.976494(inproc 与 spawn 同表现,pbuffer monolith 1.0;case 归属、重复次数未记录,原始证据不在树内;取自 run-ahead ARMED 下,**lockstep 对照从未在 golden 口径下跑过**——wave 1 的 E1)——Magma × 真实 GPU;树上 `@P7` 具名拒绝**实为 15 处 / 4 文件**(`multisample-blit-shape` / `-aspect`、`depth-stencil-mipmap`(已窄化到 D\|S 合并)、`vertex-layout`、`vertex-format-conversion`、`texel-buffer-native-format`、`unaligned-{texel,storage,atomic}-buffer-range`、`storage-buffer-native-range`、`uniform-block-native-range`、`uniform-buffer-byte-tail` / `-dynamic-offset`、`copy-image-in-place`、`default-color-blit-shape`;外加 Magma 无 `StateObjectDeathOps` 与 `buffer-legacy-arm`),**全部是绕过 `Session::Fail` 与普查门的裸 abort**;必须退役 `.6/.8/.9/.11/.12/.13`,其余改 decline 即可;链接实验分给 P7 的 **101 个符号**(D12)。`DynamicBackendParameters` 定宽重写**不再是 P7 的**——它是跨平台 wire 的前提,归 P6.5 wf | 集成 + trace 在 Magma 的 push 与 split 下全绿(inproc 半已是硬门;**spawn / TCP 臂今天 DirectVulkan 条目为 0**——四条 split 臂硬编码 `MOBILEGL_BACKEND_TYPE=DirectGLES`,wave 0 补后端维度);verify 零分歧(verify 今天只注册 monolith,verify+split 撞旧 `PipeRespecifyScope` 断言,先清);真机 DirectVulkan split ssim 回到 1.0(分母排除 rd12 in-world 与 create-indirect,ID-P7-4;必须带 `RUN_AHEAD=0` 对照臂);184 符号棘轮(契约 §12.1)下降 P7 的 101 个——原 "`nm -D libMobileGLServer.so \| grep glslang` 为空" 不可达,D12 裁定 server 镜像就是整个 `libMobileGL.so`;仪器 `scripts/link_ratchet.py`(存符号列表、只对新增红)归 wave 0;CTS 五块(`texture_*` / `shader_image_*` / `packed_pixels` / `direct_state_access.*` / `uniform_buffer_object*`)先立 monolith×DirectVulkan 基线再比 inproc,≤0.5 pp 且新 crash = 0 单独硬红。**再基线检查点:中点完成子系统 < 40% 立即重定基线**(分母 = `notes/p7/PLAN-PH-P34B-P7.md` §4 的 9 项) |
| **P8** emulation 下放 + 索引宿主镜像 + 协议广度 | 待排 | `MG_Impl/Pipe/HostResolve.cpp`(~~client 数组范围~~ 已由 `beba0256` 落地为 owned buffer;最大索引扫描、`*IndirectCount` 解析,逐站点 reconcile);`Server/IndexHostMirror` **待重裁**(树里 0 处;a6 §6 实测宿主索引 span 无 producer、client 索引已是 owned buffer——按 `kCapNeedsHostIndexBytes` 的真实消费者重新裁定,不照抄 `ARCHITECTURE.md` §10.3);multi-draw client indices(`MultiDrawElements+CLIENT_INDICES` 仍具名拒绝);CopyImage 镜像搬到 client;viewport-array 回放验证;`generate_mipmap` 计划 + CPU 回退纹素;`texture-remint-pull` 仿真下放(15 处具名拒绝);大 blob carrier 只剩 **program archive 与 `draw_vbo` range 尾**(a6 §6;与 P6.5 sl 共用同一分片);`kCapDriverOrderedXfbCapture`(0 处);~~无 present fence tick~~ 只记在 P10 | `'^DirectGLES\.Split\.'` 与 `'^DirectGLES\.'` 逐名相同;trace split 双后端 SSIM ≥ 0.99 含两个 `coherent_as_flush` fixture;`ClientArrayAfterComputeWriteScenario`;`create-indirect` 上 `roundtrips-per-frame` 读零;`index-mirror-bytes` 逐用例发布 |
| **P9** 反向通道 | 待排,**P6.5 之后**(2026-09-22 重定范围) | **异步 reply 语义**,机制建立在 P6.5 的消息式 reply 之上(不再造 `SEG_REPLY` slot 池——池今天就有,stream 链路会删它);2 MiB reply cap 与 chunked readback(契约 §10.2.3 从 P6 改判至此,ID-47);PACK-PBO 回读 fire-and-forget + client 侧 `MarkGpuWritten`(ID-57,P6 未做);十个回调里未武装的四个:`OnTextureWriteback`、`OnTexturePullRequest`、`OnMipLevelsGenerated`、`OnLog` 分级(`OnXfbScatterReady` 是零生产者、零消费者、无 EventKind 的死声明——随 P3b/P4b 删除,`kMGPipeCallbackCount` 10→9;`ClientSession.cpp:429`;`OnBufferWriteback` / `OnGpuWritten` / `OnSurfaceChanged` / `OnGlError` / `OnCapsInvalidated` 已武装);`OnGpuWritten` 收窄;`OnBufferWriteback` 批处理 + epoch 排序;`OnGlError` 有序(CONTRACT-P5C §4.2);纹理拉取四条缓解 + 终止符;XFB 命名空间(`DeleteTransformFeedback` 的 server 侧泄漏——按裁定无行、最后两个 class-C 之一)。~~`SEG_EVENT` 溢出策略~~ 机制已由 P5e 落地(`ARCHITECTURE.md` §11.7),剩余的"不排空对端杀宿主"归 Ph | 回读 / XFB 场景在 split 下绿;`TextureRemintPullScenario`(含无解用例);拉取计数逐 trace 发布;两条故障注入 |
| **P10** sync / query / present 节奏 | 待排 | ~~client 铸造 query handle~~(已是:`QueryCreate` 为 `kWaitNone`);轮询入口成门铃点 + `MOBILEGL_IPC_POLL_ESCALATE`(0 处;TCP 上是首要杠杆之一);DirectGLES 的 fence 完成度来自逐 fence 退休(`g_completedFrameSerial` 仍只在 `Present` 前进;Magma 那半已由 run-ahead 落地);非 present fence tick(唯一记载处);`present` 1:1(已是);~~credit 默认 1~~(已是默认,`ConfigLoader.cpp:414`);**`PRESENT_CREDIT > 1` 从未量过**(重审 §8.7)——带延迟链路上的首要杠杆,inproc 臂上即可取;roundtrip 计数器与输入延迟直方图(今天只有 `MapPersistentRoundtrips`);`SetSwapInterval`(最后一个手写 class-C,`EmitTables.cpp:1665`) | query / XFB / `AsyncCompile` 场景在 split 下绿;trace 上 draw / state / upload 路径 roundtrip 读零;零 timeout 轮询有界退出;credit 1..3 在 TCP loopback 与跨机臂上的配对记录;配对 A/B 记录 |
| **P11** persistent map 与 ≥16 MiB 采纳 | 待排,**同机臂专属、已让位**(2026-09-22) | T0 / T1 只对共享映射成立:`dataPlane = Stream` 上点名 T0 / T1 是具名拒绝并强制 T2(重审 §6 第 5 项),今天 `MOBILEGL_IPC_ADOPT_TIER` 0/1 已是 Fatal-at-use、`kSegAdopt` 只有枚举;前置开放问题 3。POST 探针档位选择(T0 主攻,Adreno 可选 T1,T2 回退);`SEG_ADOPT` 生命周期绑 `completedFrameSerial`;**`gPipeInputs` 版本化 / 双缓冲**(P5d 研究:draw barrier 延后一个 verb 的前提,须在 P3b/P4b 的句柄键控 twin 表之后)。**P5e 的更正**:P5e 退役 draw lockstep **没有**需要它——ra2 实测证明字段**值**已由 `SuppliedFieldMask` 分区,当时那次竞态在**元数据标量**上,修法是让守卫测事实而非断言未来(ID-135)。本行保留采纳存储的一般版本/时序问题;契约 §3.2 的 per-role stamp 已由 P5f 落地,不再是待办 | `LargeArenaAdoptionScenario` 在所选档下绿;26.3 与两个 Create fixture SSIM ≥ 0.99;Adreno 830 上 p99 帧时与峰值 RSS 对采纳基线(163→21 ms / 40→115 fps / ~400 MB)回归不超过 10% |
| **P12** Android 生产窗口路径 | 待排,**Ph 之后** | **按重审 §6 改小**:~~Service 收 Java `Surface`~~ 删掉 Surface 传递那一半——server app 拥有自己的 SurfaceView,client 根本没有窗口,于是 minSdk 26 没有扁平化 `ANativeWindow` 的 NDK API 这条约束不再成立;`WindowKind::ServerOwned`;两个后端各一条 `CreateEGLWindowSurface` 臂;`m_windowHandle` 四处写点 + `sameHandle` 去重键(a6 §6);DirectGLES `g_Display` / `g_Surface` / `g_Context` 去全局;`kEventSurfaceChanged` 的 Width / Height 缺口(Espryt 只发布格式,server 拥有显示时 client 默认 FBO 会停在 512×512 占位值);client 侧不再无条件发 `SetWindowHandle`(`BackendObject_Remote.cpp:215`——今天 FCL 里开 spawn,第一次 `eglCreateWindowSurface` 就在 server 进程 abort);server app 监听 `unix:` 与 `tcp://`(后者 Ph 之后);server 生命周期绑 Activity;cached-app freezer(SPAWN-PLAN §8.2);多 context(契约 §11);FCL env 与 plugin APK 开关表接线 | 两个门:(a) Minecraft 经 FCL **同机 spawn** 在 Adreno 830 双后端入世界,杀 server 产生干净 device-lost latch;(b) **另一台机器上的 client 经 TCP** 在 Redmi 上进世界(数据面 stream),并记录 P6.5 的必测数 |
| **P13** 退役 pull 路径 | 待排 | 删 `SnapshotFromGLContext()` 非 verify 分支、`MGB_CTX`、`MOBILEGL_PIPE_PUSH`、`MOBILEGL_PIPE_LEGACY_MEMOS`;保留 `MOBILEGL_PIPE_VERIFY`;MGPipe recorder;删 `set_residual_value_state`;在计数器活着的情况下重调幸存缓存容量;建立模块 target 与边界(D12:`SOURCE_FILES` 是一张平表喂两个库 target,无模块可链)| `static_assert(sizeof(ResidualValueBlock) == 0)`;三道纯度门在非 verify 构建上转绿;recorder 金标建立;monolith 逐线程 CPU 不差于 P0 基线 |
CTS 周转单独计价(`gl44to46` 约 56,271 例):逐阶段只跑该阶段可能影响的具名块;完整 caselist 只在架构边界与合并 `dev` 之前跑,放 CI 不放关键路径。
## 立即的下一项:P6.5 第一波 —— 全 TCP 传输(client 在 WSL,server 在 adb 设备)
> 状态:**已实现并落地推送 `feat/disaggregated@fe28bdb5`(2026-09-22),代码审查完成(无硬缺陷 + 两修复),固定制品代表性再验收通过。** 主机 unit 2399、integration-tcp loopback 102、Redmi **device-102 102 全 ARMED**、代表性 retrace OpenRA 1.0 全 ARMED;G1 与真实断线门通过。完整 39 例 device golden 矩阵与余下链路必测数按「本地代表性、CI 兜底」留 CI/后续,**尚未宣布阶段收官**;逐项见 [`CURRENT_STAGE_PROGRESS.md`](CURRENT_STAGE_PROGRESS.md) 与 [`notes/p65/`](notes/p65/)。目标形态:x86_64 的 WSL 进程作为 client,Redmi `2f7cbe2e`(aarch64,Adreno)上的 server 进程作为 server,两者在同一局域网内经 **TCP** 连接;`integration` 全部用例与 CI retrace 的 split 子集在这条链路上跑通并对上 golden。本节是 P6.5 行的第一波,只做「控制面 TCP + 数据面 stream」这一对;同机的「TCP 控制 + 共享段数据」混搭(nd `auto` / sd)是第二波,本节不做。
### 1 设计时的树上基线(实现前快照)
以下表格保留本波开工前的缺口定位;当前实现与契约更正见 `MG_Remote/CONTRACT-P65.md` 和阶段进度,不以历史行号描述今天的树。
| 已有 | 出处 | 对本波的意义 |
|---|---|---|
| 控制面 `SocketTransport`:`Listen` / `AcceptPair` / `ConnectTo`,**汇合点是一个名字**,两条连接按序接受(第一条控制、第二条 aux);`auxFd = -1` 已是「这条链不传 fd」的诚实答案 | `SocketTransport.h:70-91` | TCP 只是第三种名字(`tcp://host:port`);两条连接的规矩原样用:第一条控制,**第二条从 aux 变成数据流** |
| 帧:`[u32 'MGLF'][u32 len][payload]`,`FrameReader` 是真正的字节流重组器 | `Framing.h` | 数据连接复用同一重组器,只换 magic |
| server 主程序:endpoint 来自 `argv[1]` / `MOBILEGL_IPC_ENDPOINT`;`Listen` → `AcceptPair` → 建 backend → `Accept`(Welcome + 七个描述符)→ 控制泵,**对端 EOF 即退出**;`MOBILEGL_IPC_DIAL=no` 反递归 | `ServerMain.cpp`(门 8 的收尾修改已落入该文件,本表不引行号) | 单会话进程形状保留为 **child**;设备侧要加一个 **supervisor** 循环 |
| client:`StartOverSocket(transport)` 已把「socket 怎么来的」与握手分开;spawn 路径 = 造 `@mgl-<pid>-<this>` 名 → `LaunchServer` → `ConnectTo` | `ClientSession.cpp:693-724` | `Dial = Connect` 只是**跳过 `LaunchServer`**、把名字换成 `tcp://` |
| 握手后 client 收 **7 个 fd**(4 段 + 3 个门铃)再 `AdoptFromDescriptors` | `ClientSession.cpp:832-897` | stream 上这一段整体不存在:段是本地私有的,门铃是本地条件变量 |
| **数据面没有缝**:`ILink` 只有声明,树里用到它的只有 `LinkProgress`(`Ring.h:97`);`ShmLink` 不存在;两端 session 直接持有 `SessionSegments`(`SessionRings.h:130`);`ServerSession.h:93` 自己写着「`lk` 把门铃挪到 `ILink` 后面并删掉这些访问器」——**没做** | `Ring.h:108`(`RingControl` 重组已做)、`ILink.h`、`StreamLink.h` | 重审 §5.1 那「约一周」的 lk 主体仍然欠着,是本波**第一个包**,没有它 `StreamLink` 无处可插 |
| wire 寻址:`MGPBlobRef{Offset, Size, Seg}` 只允许 `kSegStage`,解析集中在 `ResolveOrFatal` | `PipeWireCodec.cpp:1421`(拒绝其它段:`:496`、`:1401`) | **两端各建一个同尺寸的本地 stage 窗口、偏移一致,codec 一行不改** |
| reply:`ReplySlotHeader{Seq, Status, Size}` 16 字节、`seq % slotCount`(8 槽) | `ReplySlot.h` | 头已经是消息形状;stream 上删掉取模与 stamp 自检 |
| event:四种 kind、定宽头 | `EventRing.h` | 已是 wire 形状,原样装帧 |
| 指纹:`CapsAbiFingerprint()` = 三个 `sizeof` + 协议版本 + git stamp | `protocol.fbs` `table Hello` 的注释 | x86_64 ↔ aarch64 两份二进制:**必须换成布局摘要**,否则要么永远拒绝(stamp 不同)要么静默读错 |
| 日志按角色分文件;server 角色由启动方设的 `MOBILEGL_IPC_ROLE=server` 决定;`LogLine` 已在 `CtrlMsg` 里、**零生产者** | `ServerSpawn.cpp:116`、`ClientSession.cpp:1948` | 34 个读 `<base>.server.log` 的消费方在跨机时读不到 → 日志前送(§3.5) |
| 车道:`mgl_itest_register_split_arms` 一个宏两条臂 {Split, Spawn},环境差只有 `MOBILEGL_TRANSPORT=spawn` + `MOBILEGL_IPC_SERVER_PATH` | `MG_IntegrationTest/CMakeLists.txt:1985-2010` | 加第三条臂 `Tcp`;名集合门(`scripts/ci/spawn_lane_parity.py`)与 arm 证明(`scripts/ci/junit_tally.py:27`)各加一臂 |
| retrace:桌面经 `MOBILEGL_TRANSPORT` / `MOBILEGL_IPC_SERVER_PATH` 环境(`run_trace_case.cmake:47-52`);设备经 `--env K=V;…` intent extra,server 路径从 `nativeLibraryDir` 解析(`TraceReplayActivity.java:374-386`);**桌面与设备共用同一套 golden**(`trace_cases.json`),驱动差异由 SSIM 阈值吸收 | — | 跨机 retrace 的 golden 就是现有的,不另造 |
| 设备已能从 app 进程 exec `libMobileGLServer.so`(spike A、`spawn-acceptance` 臂) | `apk.yml:477-487` | supervisor 就是这个可执行文件加一个 `--serve` |
| manifest **没有 `INTERNET` 权限** | `android-plugin/app/src/main/AndroidManifest.xml` | server 要监听 TCP,trace flavour 必须加 |
### 2 拓扑:谁监听、谁拨号、为什么
server 在手机上**监听**,client 从 WSL **拨出**。理由不是偏好:WSL2 默认 NAT,出站到局域网直通、入站要 portproxy;手机侧无防火墙。`Role × Dial` 两轴(契约 §3)因此在本波是 `Role=Client, Dial=Connect` / `Role=Server, Dial=No`。备用形态:`adb forward tcp:40613 tcp:40613` 把 server 绑在手机 loopback、client 连 WSL 内 adb 的 loopback——**要求 adb 跑在 WSL 里**(adb-over-Wi-Fi:`adb connect <phone>:5555`),因为 Windows 侧 adb 绑的 127.0.0.1 WSL2 够不着;路由器开了 AP 隔离时用它。
### 3 机制
**3.1 端点与握手(包 ct)**。`SocketTransport::Listen/ConnectTo` 识别 `tcp://host:port`(其余仍是 AF_UNIX 路径);两条 TCP 连接按序:控制、数据。socket 选项:`TCP_NODELAY`(批处理由我们的 publish 点决定,不交给 Nagle)、`SO_KEEPALIVE` + `TCP_KEEPIDLE=2s / KEEPINTVL=1s / KEEPCNT=3` + `TCP_USER_TIMEOUT=5000ms`——Wi-Fi 掉线不产生 FIN,这是唯一能把「半开」变成挂断的机制;它以 `POLLHUP`/错误进入 `Doorbell::Dead()`,走 dl 的闩,**是传输层报告的挂断,不是 apply 超时**(开放问题 23)。`Hello` 追加 `linkTerms{dataPlane=Stream, wireForm, maxReplyBytes, cmdWindowBytes, stageWindowBytes}` 与 `token`;`Welcome` 回填 server 陈述的四个尺寸;新增 `Refuse{code, detail, 对端值}`(`CtrlMsg` 只追加)。`buildFingerprint`(git stamp)比较策略:`Dial=Fork` 不匹配 → Refuse(抓陈旧 `MOBILEGL_IPC_SERVER_PATH`,不变);`Dial=Connect` 不匹配 → **警告**,`MOBILEGL_IPC_REQUIRE_SAME_BUILD=1` 时 → Refuse(车道设它:APK 与 WSL 构建必须同一 commit)。令牌:`MOBILEGL_IPC_TOKEN` 两端一致;server 无令牌时**只允许绑 loopback**,错令牌 → Refuse 并记日志。
**3.2 wire 定宽与布局摘要(包 wf)**。`wireFingerprint` = `gen_pipe.py` 从 `PipeFields.def` 生成的逐成员 `{name, offsetof, sizeof}` 表的摘要 + 有序目录摘要(opcode / payload 名 / flags / WaitClass)+ 记录头布局 + `MGPipeRenderStateSpans` 的 offsetof 表 + 两个 blob codec 版本 + `kOpCount` + `MOBILEGL_ABI_VERSION` + 字节序 + 指针宽度 + `sizeof(MGPCaps)` / `sizeof(DynamicBackendParameters)`;**删** `sizeof(GLFunctionsTable)`。x86_64 ↔ aarch64 都是 LP64、小端、Itanium ABI,布局大概率一致,但**由摘要断言、不由这句话断言**;已知的真实差异是 **`char` 的符号性**(aarch64 默认无符号)——wf 普查 payload 与 blob 里裸 `char` 当数用的站点。`DynamicBackendParameters` 定宽重写(从 P7 挪来)与 `RenderStateParameters` 1168 字节整块的成员表在此包。三条 red-once 见 P6.5 行。
**3.3 缝的主体(包 lk2,零行为变化)**。`SessionSegments` + `RingProducer/Consumer` + `ReplySlotPool` + `EventRingProducer/Consumer` + 两个门铃搬到 `ShmLink : ILink` 后面;`SessionProducer` / `SessionConsumer` / `ServerLoop` / `PipeWireEncoder` / `ClientSession.cpp` 的 `kSegEvent` 挂载 / `ResourceTracker.h` 的 writeback 解析都改收 `ILink*`;`ServerSession.h:93` 说要删的访问器删掉。门:inproc / spawn 车道结果数与逐线程 CPU **逐字不变**(`ILink::ReserveRecord` 的虚调用在 ~850–3550 次/帧上实测;若不在噪声里,答案是把 reserve/commit 做成 link 自有 arena 上的非虚函数);纯度 grep 门上线。
**3.4 `StreamLink`(包 sl)——被消息复制的镜像段**。设计原则一句话:**两端各自持有一份同尺寸的私有段,链路只负责把字节搬到对面同一偏移;codec、watermark 规则、apply 线程、编码器全部不变。**
- 段:握手协商后两端各 `malloc` 四块:cmd 环(`cmdWindowBytes`)、stage 窗口(`stageWindowBytes`)、event 环、reply 邮箱。尺寸相同 ⇒ 偏移相同 ⇒ `MGPBlobRef` 与 `ResolveOrFatal` 原样工作。
- 数据连接帧:`[u32 'MGLD'][u32 len][u8 kind][u8×3][u64 a][u64 b][payload]`。C→S:`Cmd{ringOffset, bytes}`(记录字节逐字、写到对面环的同一偏移)、`Stage{offset, bytes}`、`ClientProgress{cmdHead, submittedSeq, eventTail}`。S→C:`Progress{appliedSeq, retiredSeq, completedFrameSerial, presentAckSerial, cmdAppliedTail, eventRingFull}`、`Reply{seq, status, bytes}`(头即 `ReplySlotHeader`)、`Event{ringOffset, bytes}`。
- client 侧:编码器照旧写本地 cmd 环与 stage 窗口;`Flush()` 把 `[上次已送, cmdHead)` 作为一帧 `Cmd` 发出(环回绕用两段 iov 一次 `sendmsg`),**之前先发本次新 stage 的 `Stage` 帧**——顺序由编码器「先 stage 后 reserve」保证;`LinkCapabilities.PublishIsDelivery=false`,所以每个等待点先 `Flush`(这个位就是为此设的)。C→S 背压:`WaitForCmdSpace` 不变(读 `Progress` 里的 `cmdAppliedTail`),`send()` 阻塞是第二层,没有无界缓冲。
- server 侧:`mgl-srv-io` 读线程把 `Cmd`/`Stage` 拷到镜像段同偏移,再推进本地 `cmdHead`/`submittedSeq` 并敲 apply 的本地门铃;**apply 线程一行不改**。
- `Progress` S→C 的发出时机(对 `Ring.h` 「`appliedSeq` 不得懒发布」的**修正案**,规则改成「最多晚一次 flush,绝不早」):(a) apply 线程**即将 park 前**(flush-on-idle,解决「帧内最后一条 `kWaitReply` 记录两端互等」);(b) 每次 `PostReply` 之后;(c) present ack / `completedFrameSerial` 变化时;(d) 每 64 条已 apply 记录或 1 ms 取先到者。接收侧对每个 watermark 做 `AdvanceMonotonic`,**倒退是 `Fatal{ProtocolCorruption}`**。
- reply:`kWaitReply` 下同一时刻最多一条未决,client 侧是「单槽 + 条件变量 + seq 核对」;本链路上 slot 池、取模、stamp 自检**删除**(`ShmLink` 保留)。
- event:server 的 `EventRingProducer` 写本地环,io 线程把新增字节装 `Event` 帧;client 读线程写进 client 本地环同偏移,`EventRingConsumer` 不变;`Drained()` 发 `ClientProgress{eventTail}`;server producer 在 `head − 对端 tail > capacity` 时**阻塞**(P5e 的规则原样,`eventRingFull` 经 `Progress` 回传)。
- 门铃:`ConsumerBell()` / `ProducerBell()` 是本地 `CondVarDoorbell`,由各自的读线程敲;读线程见 EOF / 错误置 `Dead()`。每进程数据连接**一条**专用读线程(写在调用线程上),控制连接的读法不变。
- 帧上限:`Cmd`/`Stage` 单帧 ≤ `kMaxFramePayloadSize`(64 MiB);一条记录本来 ≤ 4 MiB。未分片的 program archive / `draw_vbo` range 尾在 stream 上和今天一样受 `stageWindowBytes` 约束,超出仍是 `Fatal{RingOverrun}`——**不是本波阻塞项**,记在开放问题 11。
- 不做的:`kSegShadow` / `kSegAdopt`(P8 / P11 的段)在 stream 上具名拒绝;`GetCaps` 的 blobref 若仍走 stage 之外 → 具名拒绝,本波不发明第三条载体。
**3.5 日志前送(包 lf)**。server 进程的日志 sink 在 `MOBILEGL_IPC_ROLE=server` 且有控制传输且 `MOBILEGL_IPC_LOG_FORWARD=1`(`Dial=Connect` 默认开)时,每行**同时**写本地 `<base>.server.log` 并作为一帧 `LogLine`(已在 `CtrlMsg` 里)经控制连接发出;client 收到后写入**自己的** `<base>.server.log`。于是 34 个消费方(`PipeStatsWindow` 的计数器窗口、四个 arming 场景、拒绝普查)**一字不改**。顺序:读者在 `MarkLaneLog` / `ReadLaneLogSince` 之前调一个导出的 `MGPipeSyncPeerLog()`(一次控制往返:`LogFlush` → server 冲刷并 ack),远端不存在时它是 no-op;导出符号一个,`-fvisibility=hidden` 下显式 `default`。若 lf 滑期,退路是这 34 处在 tcp 臂上**按名 SKIP**(清单文件,像 `dualblock-expected-fatals.txt`),不许静默通过。stats 行一帧一条,前送代价可忽略。
**3.6 设备侧 supervisor 与启动(包 sv)**。`mobilegl_server_main tcp://0.0.0.0:40613 --serve [--max-sessions 1]`:listen → 循环 { `AcceptPair`(控制 + 数据)→ `fork()` → child 关掉 listen fd、用 `SocketTransport(streamFd, dataFd)` 走今天的单会话路径到 `_exit`;parent 关掉已接受的 fd、非阻塞 `waitpid`、继续 accept }。每会话一进程 ⇒ `eglTerminate` 的进程级拆机被隔离(D9 原样);同时只服务 1 个会话,第二个连接 → `Refuse{Busy}`。启动:trace flavour 新增前台 `MobileGLServerService`(`android:process=":mglsrv"`),`am start-foreground-service` 带 `--es listen`、`--es token`、`--es env "K=V;…"`(复用 `--env` 的 `K=V;` 语法),它从 `nativeLibraryDir` exec `libMobileGLServer.so`(spike A 的路径)、给 child 设 `MOBILEGL_IPC_ROLE=server`、持 partial wake lock;`am force-stop` 整包停掉。后端由 client 的 `Hello.backendType` 请求、supervisor 的 env 可钉死,不一致 → Refuse。EGL 在 child 里是 pbuffer / surfaceless(P6 边界;真窗口归 P12)。manifest 加 `INTERNET`。
**3.7 谓词与测试的拓扑感知**。client 进程:`Transport=Spawn`(拓扑:两进程)、`Dial=Connect`、不 fork;`MGPipeBackendIsLocal()=false`。`CountOwnChildren()` 一类断言改为按 Dial 分支:Connect 下断言**本地零子进程** + `Welcome::serverPid` 非零 + 日志 `control=tcp`。S2(kill server)在跨机臂上经 `MGITEST_PEER_KILL_CMD` 模板(`adb -s <serial> shell run-as top.mobilegl.plugin.trace kill -9 {pid}`)真杀——**不**允许用协作式「请你退出」代替,那测的不是挂断。
### 4 包与顺序
`lk2`(缝主体,零行为)→ `wf`(定宽 + 摘要 + Refuse)→ `ct`(tcp 端点 + keepalive + 令牌 + Connect 拨号)→ `sl`(`StreamLink`,先在 **WSL loopback** 上对着 spawn 车道打平)→ `lf`(日志前送)→ `sv`(supervisor + Service + adb 脚本)→ `t65`(tcp 臂、跨机臂、门、数)。每包 red-once(R-16)。第二波(不在本节):`nd` 的 `auto` 与 `sd` 的 aux 交付(同机 TCP 控制 + 共享段数据)、P11 的 stream 具名拒绝、Windows client。
### 5 出口门(本波)
1. G1 pull 0/0/0/0、`.text` 不变(全部新代码在 `MOBILEGL_BUILD_DISAGGREGATED` 下)。
2. lk2 后 inproc / spawn 车道结果数与逐线程 CPU 逐字不变(实测,不论证)。
3. **`integration-tcp`(WSL loopback,server 以 `--serve tcp://127.0.0.1:40613` 本地起)**:名集合 == spawn(parity 脚本加臂)、同数绿;每条目 arm 证明 `control=tcp data=stream server=<host:port> pid=N`(`junit_tally --require-tcp-ran`)。
4. **`integration-tcp-device`(server 在 Redmi)**:同一名集合;首轮**记录**、不设阻塞门,每一条与 loopback 臂的差异必须具名(GPU 能力 / P7 的 Magma 分歧 / 时限);第二轮起转硬门。
5. **retrace**:CI split 子集(`trace_cases.json` 里未 `"split": false` 的)从 WSL 对着手机跑,DirectGLES 的 SSIM ≥ 现有阈值、OpenRA 为 1.0(E2 的锚);DirectVulkan 记录不设门(真机分歧是 P7 的)。
6. red-once:wf 三条(P6.5 行);ct——错令牌 → Refuse、`wireMajor` 不匹配 → 两端具名拒绝、`adb shell svc wifi disable` → 10 s 内闩住(而不是 120 s `BarrierTimeout`);sl——`Progress` 倒退 → `Fatal{ProtocolCorruption}`、越窗的 `Stage` 帧 → 拒绝、去掉 flush-on-idle 后「帧内最后一条 `kWaitReply`」用例必须挂住并被超时抓到;lf——关掉前送后四个 arming 场景必须**按名红**;sv——第二个并发连接 → `Refuse{Busy}`。
7. **必测数(记录)**:逐帧 `kWaitReply` 次数与 RTT 分布(OpenRA、MC in-world 各一);`SEG_STAGE` 字节/帧对链路实测吞吐(车道自己量一次 64 MiB `Stage` 突发);`PRESENT_CREDIT` 1 / 2 / 3 的帧率;loopback tcp+stream 对 spawn(unix+shm)的逐线程 CPU 差。
### 6 两边怎么配(第一波操作手册)
**手机(server)**
```bash
# 1. 同一 commit 构建 trace APK(分离构建)并安装
./gradlew :app:assembleTraceDebug -Pmobilegl.buildDisaggregated=ON && adb install -r app/build/outputs/apk/trace/debug/*.apk
# 2. 手机 IP
adb shell ip -f inet addr show wlan0
# 3. 起 server(前台 Service → supervisor 监听)
adb shell am start-foreground-service -n top.mobilegl.plugin.trace/top.mobilegl.plugin.MobileGLServerService \
--es listen tcp://0.0.0.0:40613 --es token devtoken-0123456789abcdef \
--es env "MOBILEGL_BACKEND_TYPE=DirectGLES;MOBILEGL_PIPE_STATS=1;MOBILEGL_PIPE_STATS_PERIOD=1;MOBILEGL_LOG_FILE_PATH=/data/data/top.mobilegl.plugin.trace/files/mgl.log"
adb logcat -s MobileGL | grep -m1 'listening on tcp://'
# 停:adb shell am force-stop top.mobilegl.plugin.trace
```
**WSL(client)**,在分离构建目录(今天是 `~/lk-split`):
```bash
export MOBILEGL_TRANSPORT=spawn # 拓扑:两进程;不 fork,因为下面给了远端
export MOBILEGL_IPC_CONTROL=tcp://<phone-ip>:40613
export MOBILEGL_IPC_DATA=stream
export MOBILEGL_IPC_TOKEN=devtoken-0123456789abcdef # ≥16 字节(PH-7 (3)):更短的令牌让 server 以 72 退出而不是监听
export MOBILEGL_IPC_REQUIRE_SAME_BUILD=1 # APK 与本地构建必须同 commit
ctest -L integration-tcp-device -j 1 --output-on-failure # -j 1:设备同时只服务一个会话
# retrace(同一环境):
(cd build-retrace/tools/trace_replay && ctest -V -R '^MobileGLTraceReplay\.OpenRA\.DirectGLES$')
```
日志:client 侧 `<base>.client.log` 与前送来的 `<base>.server.log` 都在 WSL;设备本地副本用 `adb shell run-as top.mobilegl.plugin.trace cat files/mgl.server.log`。
**备用(AP 隔离 / 没有局域网直通)**:adb 跑在 WSL 里——Windows 上一次 `adb tcpip 5555`,WSL 里 `adb connect <phone-ip>:5555 && adb forward tcp:40613 tcp:40613`;手机侧 `--es listen tcp://127.0.0.1:40613`(loopback,不需令牌),WSL 侧 `MOBILEGL_IPC_CONTROL=tcp://127.0.0.1:40613`。
### 7 风险与未决(本波)
1. **`kWaitReply` × RTT**:`ResourceCreate` / `SetTextureParams` / 纹理 `ResourceSubData` 每次一个 RTT(Wi-Fi 2–5 ms);MC 加载区块的创建突发会可感知地慢。本波**只量**(门 7),减少这一列是 P9 / P10 的活。
2. **带宽**:`SEG_STAGE` 字节/帧对 Wi-Fi;单次 128 MiB 上传 = 秒级停顿(开放问题 19、20)。
3. **Wi-Fi AP 隔离**:用 §6 的 adb forward 备用。
4. **DirectVulkan 真机分歧**(P7)会在门 5 出现:记录,不当本波的红。
5. **`MOBILEGL_IPC_ROLE`**:supervisor 必须替 child 设它,否则 child 的日志会当 client 写(`Log.cpp` 从这个 env 解析角色)。
6. **ctest 并行 vs 一设备一会话**:`-j 1` 或 supervisor `--max-sessions N`(每会话一进程,N 个 EGL 上下文同时存在,Adreno 上未验证)。
7. **同 commit**:两端二进制不同是常态,指纹只保证 wire 布局;`REQUIRE_SAME_BUILD` 是车道纪律,不是协议要求。
## 历史:P5c 计划与审计(`inproc` 共享内存读点;审计头 `a79a0af6`,2026-09-17)
**为什么插在 P6 之前**:P5 / P5b 的 `inproc` 只把 verb 记录、`SEG_STAGE` blob、reply 与 caps 快照做成了 wire。对 `a79a0af6` 的只读静态审计(`~/w7/notes/p5c/p5c-audit-v1.md`,59 行清单,逐条在该头上重新解析)证明两个角色之间仍有一批**不经任何载体的直接内存读写**,靠 verb barrier 与同一地址空间才正确。它们不是传输问题:`spawn` 换掉的只是 ring 的传递方式,而这些读写在另一个进程里根本没有对应内存。P5c 的目标是让 `inproc` 诚实——两线程之间除 `SEG_CMD` / `SEG_STAGE` / `SEG_REPLY` / `SEG_EVENT` / caps 快照 / 控制帧之外零直接访问——从而 P6 只是传输替换。**本节保留 P5c 启动时的计划与审计语境;P5c 已收官,后续对象类、控制帧、静态量与反向通道的收口结果以 P5f 报告为准。**
### 审计清单(按家族;`file:line` 以 `a79a0af6` 为准,完整 59 行见报告)
| # | 方向 | 直接访问的是什么 | 站点 | 今天为何能工作 | 现有守卫 | P5c 载体 |
|---|---|---|---|---|---|---|
| T1 | S→C 读 | **纹理纹素**:`resource_subdata` 已把 level 字节放进 `SEG_STAGE`,但 `ApplyTextureUpload` 只做门与累积、把指针丢掉,Espryt 同步时从 client 的 `MipmapStorage` 重读纹素;纹理对象的形状与脏区也直接读前端对象 | 丢指针 `MobileGL/MG_Pipe/PipeApply.cpp:989-1008`;重读 `DirectGLES/Managers.cpp:7315`(`MapMipmapData`)、`:6940`、`:7129`、`:7198`;形状 `:7314`、`:7332`、`:6878-6880`、`DirectGLES/DirectGLES.cpp:8502-8507`、`:8528-8529`;脏区 `DirectGLES/Managers.cpp:7360`、`:7371` | barrier + 同地址空间 | **无**:整个纹理家族不在 `FieldOwnership.def`,`rsp` / `MOBILEGL_IPC_STRICT_ERRORS` / `MOBILEGL_IPC_AUDIT` 都看不见 | server 端纹理 staged shadow(buffer `StagedShadowStore` 的纹理半边);`SyncMipmapsToBackend` 改读它,形状与脏区读描述符 |
| T2 | S→C 读写 | client 的 slot 分配器 `MGPipeSlots()`:mip / CopyTex / twin 创建时按前端 `GetLifetimeId()` 在 client 分配器里查找甚至铸造 | `DirectGLES/DirectGLES.cpp:8087` → `DirectGLES/SlotTables.h:392-400`(`HandleOf` → `FindByLifetimeId`);铸造 `DirectGLES/SlotTables.h:232`(`GetOrCreate`)、`:418`、`:428`;`DirectGLES/Managers.cpp:2708`、`:4293`、`:5627`;`DirectGLES/DirectGLES.cpp:6706`、`:8185`、`:8564`、`:8659` | barrier;mip 只 Find 不 mint | 无(`MOBILEGL_ASSERT` 在 INFO 构建失效) | 记录已带句柄(`MGPMipPlan::Res`、`MGPCopyFromFramebuffer::Dst`、`MGPBlit::ReadFbo/DrawFbo`)→ `GetOrCreate(MGPipeHandle)`(`DirectGLES/SlotTables.h:278`) |
| T3 | S→C 读 | 具名 blit:client 临时改绑自己的 read / draw 绑定槽,server 经 `MGB_CTX` 读绑定槽 | client `MobileGL/MG_Remote/Client/EmitTables.cpp:741-786`;server `DirectGLES/DirectGLES.cpp:7782-7785` | barrier + RAII 恢复 | `rsp`(`GetFramebufferBindingSlot`) | 记录里的 `ReadFbo` / `DrawFbo`,sink 侧按句柄解析(`MobileGL/MG_Remote/Server/PipeApplier.cpp:228-244`) |
| T4 | S→C 读 | `CopyTexImage2D` / `CopyTexSubImage2D` 经纹理单元绑定槽取目的纹理 | `DirectGLES/DirectGLES.cpp:8561-8563`、`:8656-8658` | barrier | `rsp`(`GetTextureUnitObject`) | 记录里的 `Dst` 句柄 |
| T5 | S→C 写 | Magma 生成 mip 直接写 client 的 level 存储(Espryt 的重推导臂没有 Magma 对应) | `DirectVulkan/Renderer/VulkanRenderer.cpp:1562-1590`(调用 `:11317`) | barrier | 无 | 与 GLES 同形:只按描述符验证 |
| B1 | S→C 读 | `BufferObject::HasDefinedContent()`,每个 ensure 的 draw 都读 | `DirectGLES/Managers.cpp:3147-3148`(`EnsureBufferResourceForHandle`;`:3141-3146` 自述为债) | barrier | 无 | 描述符旁的 live-content 位,client 发布 |
| B2 | S→C 读 | `HandleOfBuffer` 每次 ensure 读前端 `GetLifetimeId()` | `DirectGLES/Managers.cpp:2705-2716` | barrier + memo | 无 | 记录里的句柄 |
| B3 | S→C 读 | 旧 buffer 臂(`MappedData()` / `IsMapped()` / `GetChangeSerial()`)在 split 构建里位 7 清零时可达,arm resolver 只打一行 `MGLOG_D` | `DirectGLES/Managers.cpp:2586-2620`;臂体 `:1012-1022`、`:1119-1121`、`:2944-2949`、`:3281-3327` | 同进程 | 无 | 有传输时具名拒绝(同 `Fatal{PipeLegacyMemosDisabled}` 的形状) |
| B4 | S→C 读写 | XFB:`SharedPtr<BufferObject>` 跨整个 span 持有,结束时 `WritebackFromBackend`,scatter 对 client shadow 读改写 | `DirectGLES/DirectGLES.cpp:950`、`:1060`、`:1077`、`:1081`、`:1172`、`:1185` | barrier | 无 | `OnBufferWriteback` / `OnXfbScatterReady` 事件——P9 的题材,P5c 只记账 |
| R1 | S→C 写 | 反向通道是 apply 线程直接调进 client 的 `MG_State`:`OnBufferWriteback` 把**裸指针**塞进 `MGPBlobRef.Offset`,client 侧再转回指针;consumer 会拒绝任何真实段 | 生产 `DirectGLES/Managers.cpp:2339-2343`(`Ops_H_Readback`);消费 `MobileGL/MG_Impl/Pipe/ResourceTracker.h:553-572`(拒绝真实段 `:560-564`) | 同地址空间 + barrier | 无 | `SEG_EVENT` `kEventBufferWriteback`:consumer 已在(`MobileGL/MG_Remote/Client/ClientSession.cpp:176-267`),**全仓零 producer**(`MobileGL/MG_Remote/Server/ServerSession.cpp:484-492` 无调用者) |
| R2 | S→C 写 | `OnGpuWritten` 直接调进 client tracker;Magma 更是绕过回调直接 `MarkGpuWritten()` | Espryt `DirectGLES/Managers.cpp:2729-2752`,喂入 `DirectGLES/DirectGLES.cpp:568`、`:616`、`:2601`;Magma `DirectVulkan/Renderer/UniformManager.cpp:1075`、`:1231`、`DirectVulkan/Renderer/VulkanRenderer.cpp:11618` | 同地址空间 | `MGLOG_E_ONCE` | `SEG_EVENT` `kEventGpuWritten`(consumer `MobileGL/MG_Remote/Client/ClientSession.cpp:227-236`) |
| R3 | S→C 写 | 默认 framebuffer 附件(`pDefaultFramebufferInfo`)由 server 在 surface 创建时写 | `DirectGLES/DirectGLES.cpp:11519-11620`;Magma `DirectVulkan/Renderer/SwapchainObject.cpp:276-335` | barrier;罕见 | 无 | `SEG_EVENT` `kEventSurfaceChanged`(consumer stub `MobileGL/MG_Remote/Client/ClientSession.cpp:242-253`) |
| R4 | S→C 写 | sticky forward:`RecordError` 写 client 错误队列;`InvalidateCompileEnv` 写 client 编译环境(在 stamped verb 之外静默 no-op) | `MobileGL/MG_Impl/Pipe/PipeFill.cpp:1753-1756`、`:1764-1772` | barrier | `rsp`;strict 下 Fatal | `RecordError` → `OnGlError` 事件(顺序保证是 P9 的);`InvalidateCompileEnv` 的活已由 R-12 caps 重发布完成(`MobileGL/MG_Remote/Client/CapsMirror.cpp:78-80`),删 forward |
| R5 | — | `MGPipeCallbacks` 十个回调只装了两个,且都是 apply 线程内联调用;`SEG_EVENT` 段已创建、映射、握手公告、每个 reply verb 后排空,但没有任何生产者 | 安装 `MobileGL/MG_Impl/Pipe/ResourceTracker.h:606-613`;段 `MobileGL/MG_Remote/Server/ServerSession.cpp:285-301`;排空 `MobileGL/MG_Remote/Client/ClientSession.cpp:836` | — | 溢出 latch 与 `eventDropped` 已建未用 | 本身就是载体 |
| C1 | S→C 读 | Magma 四处直接读 client 镜像 `pActiveBackendObject->GetDynamicParameters()`(Espryt 已改走 server 自己的 backend);Espryt 的格式表回落臂在 server backend 为空时读 client 镜像 | `DirectVulkan/DirectVulkan.cpp:713-715`;`DirectVulkan/Renderer/VulkanRenderer.cpp:667-676`;`DirectVulkan/Renderer/VertexInputStateFactory.cpp:249-250`、`:502-503`;`DirectGLES/Utils.cpp:50` | 镜像值等于快照 | 无 | server 自己的 `Backend()->GetDynamicParameters()`;回落臂拒绝 |
| G1 | shared | `gPipeInputs`:client 残余填充写、applier 写、server 的 36 行 BARRIER-PULLED 读 | `MobileGL/MG_Backend/MGPipe/PipeInputs.h:806`;填充 `MobileGL/MG_Impl/Pipe/PipeFill.cpp:2798`;applier `MobileGL/MG_Pipe/PipeApply.cpp:1415-1560`;stamp `MobileGL/MG_Backend/MGPipe/PipeInputs.cpp:103-132`;`rsp` `MobileGL/MG_Backend/MGPipe/PipeInputs.cpp:72` | **只靠 verb barrier** 保证单写者 | `rsp` + strict Fatal + poison | 值类行(`GetActiveTextureUnit`、`GetMaxTouchedTextureUnit`、`GetTouchedBufferBindingPointCount`、`GetCurrentVertexAttribute`、XFB 六项、三个世代)→ 每 verb 一条残余值记录或按 `Coverage.def` 由 server 自答;**对象类行**(`GetBoundVertexArray`、`GetProgramForDraw/Dispatch`、`GetTextureUnitObject`、`GetImageTextureBinding`、`GetFramebufferBindingSlot`、`GetBufferBindingSlot/Point`、sticky `GetTextureObject` / `GetProgramObject`)返回前端对象指针、无法序列化,留 P3b/P4b/P7/P8 的 twin 表 |
| G2 | C→S 写 | GL 线程在 `FreshlyPrimed` 时整体 `MGPipeApplierReset()` server 的 `g_applier` | `MobileGL/MG_Impl/Pipe/PipeFill.cpp:2654-2656` → `MobileGL/MG_Pipe/PipeApply.cpp:1202-1299` | barrier 的后置条件(validate 时无在飞记录) | 无 | make-current 边上的 `applier_reset` 控制记录 |
| G3 | C→S 写 | 六种前端对象死亡:栈上结构体指针经 mailbox 送到 apply 线程(framebuffer 没有 wire delete opcode) | `DirectGLES/Managers.cpp:206-228` | 阻塞 + 同地址空间 | 无 | `object_death` wire 记录 |
| G4 | C→S 控制 | 十二个 `Server*` EGL forwarder 经 one-slot mailbox 传**函数指针 + `void*` 栈局部** | `MobileGL/MG_Remote/Client/BackendObject_Remote.cpp:125-264`;邮箱本体在 fc 前的 HEAD 上是 `ServerLoop.cpp:533-568`(PumpControlRequest)+ `:645-707`(RunOnApplyThread)——本行原引 `:543-598`,f0-egl F9 修正 | 阻塞 | `Fatal{ApplyThreadNotRunning}` | **已由 P5f 包 fc 帧化**(原记 P6):`SurfaceControlFrame` 纯值帧走同一阻塞通道(`Server/SurfaceControlFrame.h`、分发 `ApplySurfaceControlFrame`),wire 编解码与具名拒绝在 `Protocol/SurfaceOpCodec.cpp`;报告 `notes/p5f/fc-report.md` |
| G5 | C→S 读 | `ServerLoop::OnApplyThread()` 每条 routed 记录读;bring-up 直接读 `loop.Backend()` / `MGPipeGetResourceOps()` | `MobileGL/MG_Remote/Client/WireTables.cpp:71`;`MobileGL/MG_Backend/Init.cpp:100`、`:145`、`:164-170`、`:247` | 同进程 | — | client 本地角色标志;caps 快照的 cap 位(P6 顺手) |
| G6 | shared | 前端地址 / lifetime-id 键控的 Espryt twin registry;持前端裸指针的 memo(`ResolvedDrawBuffers::Entry::frontend`、三张 texture sync list);server 读 `pDefaultFramebufferInfo`;`g_rawDepthFetchSamplerState`;`ScopedDefaultUnpackState::s_synced` 从不按 context 世代重置 | `DirectGLES/Managers.h:309-377`、`:1185`;`DirectGLES/DirectGLES.cpp:61-62`、`:1958-1987`、`:2846`;`DirectGLES/Managers.cpp:5806-5812` | barrier / server 私有 | — | 历史移交给 P3b/P4b / P6;transport registry 与 `s_synced` 世代问题已由 P5f fr / fs 收口 |
| A1 | — | barrier 的 client 半边断言从未接线:`InBarrierWait()` 零调用点,只有 `ApplyThreadIsInsideApplier` 在 emit 时检查 | `MobileGL/MG_Remote/Client/ClientSession.cpp:880`、`:748-753` | — | — | 接上,或删掉契约里的这句声明 |
已确认 wire-clean、不进 P5c:buffer 全家族(`Ops_H_*` 只拿句柄 + 记录 + `SEG_STAGE` 字节,`StagedShadowStore::Adopt` 复制,`Fatal{StageSnapshotTooNarrow}` 六处);`resource_respecify` 初始字节双向拒绝;persistent map 仿真档按块过 `SEG_STAGE`,`Fatal{RoleViolation}` 挡住 apply 线程;`CreateShaderState` 归档是一个 blob;caps 的格式表与 renderer 字符串走 `CapsSnapshot` 控制帧(`GetCaps` 记录本身已死,只是卫生项);tight ReadPixels;`DrawVbo` 用户索引;fence 家族;`gMGPipeSegmentResolver` 与 `gBackendFunctionsTable` 的角色划分。
### 包与顺序
| 包 | 内容 | 关闭的行 |
|---|---|---|
| **c0c** 契约 | `MG_Remote/CONTRACT-P5C.md`:纹理 staged shadow 的所有权与覆盖规则;`SEG_EVENT` 记录与 blobref 约定(`Seg = kSegEvent`);sink / twin 按句柄解析的规则;`applier_reset` / `object_death` 记录;角色守卫的语义(有传输时 apply 线程触前端对象表面、GL 线程触 server 状态表面都是 `Fatal{RoleViolation}`);残余值记录的字段表;把纹理家族补进 `FieldOwnership.def` | — |
| **tx** 纹理 | `StagedTextureStore`:`ApplyTextureUpload` 采纳 `SEG_STAGE` 字节到 server 侧 level shadow;`SyncMipmapsToBackend`、形状读、脏区改读描述符与 shadow;Magma mip 改按描述符;`MOBILEGL_IPC_AUDIT` 的 `0xDD` 覆盖纹理 | T1、T5(T2 的 mip 半边随之) |
| **ev** 反向通道 | `ServerSession::PublishEvents` 接三个 producer(writeback / gpu-written / surface-changed),consumer 的段臂改为解析 `kSegEvent`;Magma `MarkGpuWritten` 改走回调;`RecordError` 进事件(顺序仍 P9);删 `InvalidateCompileEnv` forward;溢出策略沿用 `ARCHITECTURE.md` §11.7 | R1–R5 |
| **hd** 句柄解析 | blit / mip / CopyTex / `HandleOfBuffer` 的 sink 与 twin 按记录里的句柄解析(`GetOrCreate(MGPipeHandle)`),server 不再触 `MGPipeSlots()`;`HasDefinedContent` 改读描述符的 live-content 位;Magma 四处 caps 读改 server backend,`Utils.cpp` 回落臂拒绝 | T2、T3、T4、B1、B2、C1 |
| **ct** 控制记录 | `applier_reset` 与 `object_death` 两条 wire 记录(framebuffer 首次有 delete opcode);mailbox 只剩 EGL forwarder 给 P6 | G2、G3 |
| **rv** 残余值 | 值类 BARRIER-PULLED 行改为每 verb 的残余值记录(或 server 自答),`gen_pipe_field_ownership.py` 把它们改成 RECORD-SUPPLIED;对象类行保持 BARRIER-PULLED 并逐行标注归属阶段 | G1 的值类半边 |
| **gt** 门 | 角色守卫(两层 `Fatal{RoleViolation}`,仅 split 构建、有传输时生效);`InBarrierWait` 接线;旧 buffer 臂传输拒绝;`rsp` 按帧进 stats 行并在四条 A/B trace 上测得;CI 加一条 `MOBILEGL_IPC_STRICT_ERRORS=1` + 角色守卫的 `integration-split` 车道 | A1、B3,以及所有行的红一次证据 |
顺序:c0c → tx / ev / hd 并行 → ct / rv → gt 收口。每包自带 red-once(R-16),阶段末一次 Codex / Kimi 审查(ID-66)。
### 出口门(E-P5c)
1. **角色守卫下全绿**:守卫开启时 `integration-split`(107)、broad inproc 车道对 `348d22a4` 普查零回退、79 trace 无新增首阻塞;关掉任一层守卫必须能在一条具名用例上变红。
2. **纹理字节不再回读 client**:`MOBILEGL_IPC_AUDIT=1` 对纹理 staged 字节的 `0xDD` 填充在四条 A/B trace 上无失败;把采纳改回丢指针必须变红。
3. **`SEG_EVENT` 有生产者**:三条事件的往返单元与集成用例;排空点上 `eventDropped == 0`;把 producer 改回裸指针必须变红。
4. **`rsp` 分类完成**:四条 A/B trace 每帧 `rsp` 已测;剩余读全部是对象类行,名单由 `FieldOwnershipTest` 钉住,值类行为 0。
5. **Redmi 四臂复测**(记录,不设门):writeback / gpu-written 改走事件后 barrier tax 重新记录。
6. G1 0/0/0/0、G2 / G14、G5 照旧。
### 历史移交项(P5c 当时不碰)
P5c 当时移交的对象类 BARRIER-PULLED、transport registry 前端键、EGL 控制面、`s_synced` / `g_syncedRenderStateParameters` 世代与反向通道跨角色访问,已在 P5f 收口。P9 的异步化/排序、P8/P9 的协议广度与仿真功能仍按各阶段推进;不要把这份历史清单重新当成 P6 的帧化或静态量施工单。
## 里程碑
- **P2 出口(2026-09-08):GO/NO-GO 判定继续**——五部分门全绿,逐线程 CPU 代价约 +10% 被接受;tracker 绝对 ns 上限降为记录项。
- **P3a / P4a 出口(2026-09-08)**:各 1 天,远低于 27 / 39 天的再基线绊线,未触发重定基线。
- **P5 出口(2026-09-16)**:缩减路径首个 IPC 帧;最终全门在 E3(a) 停止;红米四臂的 split 均停在索引 draw,barrier tax 未测(归 P5b)。
- **P5b 出口(2026-09-16,已达成)**:主机全门 `348d22a4` complete;79 trace 72/6/1;设备源头 `82683d4a`(APK `p5bcodex2`)Redmi 正确性 8/8——四条 A/B trace 双后端首次在设备上 inproc 渲染;barrier tax 首测 split−push 逐线程 CPU p50 +5.9% – +18.2%。
- **P5f 出口(2026-09-20)**:双块和 strict 空棘轮、逐帧 rsp=0、零 BARRIER_PULLED、主机全门、异模型族审查修复和 Redmi 六 clean-boot 臂全部完成;P6 前提解除,实施未开始。
- **CI / inproc 收尾出口(2026-09-21)**:`feat/disaggregated` 主产物拆分编译 + inproc 主验收全绿——unit 2328 零失败、`integration-gpu` 双 transport 各 1455 零失败、retrace 77 例 monolith 77/77 且 inproc 的唯一间歇项(`bsl-esc-menu-854` DirectVulkan,lavapipe 固有 texture-handle 注册停顿越过旧 30 s barrier 预算)在预算修正为 120 s 后隔离 10/10 绿、18 项 CI 门全绿、双 ABI APK 与 AVD 生命周期验证通过;合入头 `e1bf3677`,最终矩阵以远端 CI 为准。
- **P6 收官(2026-09-22)**:a6/c6/lk/so/sm/cp/hs/dl/st/t6 十包于 2026-09-21 合入 `feat/disaggregated`(头 `77cbd176`);三种传输(monolith/inproc/spawn)桌面与真机两进程 retrace 逐像素对上 golden;device-lost 闩把杀 server 从 123 s 错误 `BarrierTimeout` 变 5.7 s 正确 `DEVICE LOST`;`Session::Fail` 漏斗把裸 abort 93→4。出口门 1–6 ✅;**门 7 / 8 于 2026-09-22 采齐**:真机 reboot-clean 同热窗口配对 A/B 六臂 18/18 runner 退出 0、36/36 `PINNED`、0 `Fatal`,inproc vs spawn 的 client CPU p50 差 −0.15%(第二会话 +0.16%)——**判读 tie**(臂内散布 0.2–4.2%),monolith 基线约贵 30%;门 8 三数齐(门铃 A/B tie、`SEG_STAGE` 字节/帧、分块后记录/帧);G1 符号 0/0/0/0 且 `.text` 逐字节相同。**P6 就此收官,未设性能门**(只记录)。逐项见 `CURRENT_STAGE_PROGRESS.md`。
- **终局改判(2026-09-22)**:AVF pVM / `AF_VSOCK` 终局撤回,改为 TCP 跨机跨平台 + 控制面 × 数据面两轴传输栈;P6.5 上关键路径,Ph 前置于 P12 的非 loopback 监听;P6 核出三项漏项(server 进程 `PipeStats` 结构性零——收尾中已修、184 棘轮门、hs 的三行契约未落地)与 `CURRENT_STAGE_PROGRESS.md` 里一条错误的 S6 陈述(已改);class-C 已归零到两个(`SetSwapInterval`、`DeleteTransformFeedback`)。
- **P7 wave 2 落地(2026-09-22)**:X1 / F1 / C / A / B / D1 / D2 / D3 / B2 / B3 十包合入 `feat/disaggregated`(`origin@b089e0de`,B3 在集成树上待推),每包八车道全绿、G1 恒等、每包一条两进程臂 red-once、每包一次 fable 聚焦审查;子系统分母 **6/9** 于 B2 落地时越过 40% 中点,不触发重定基线。真机 p7w4:门 3 34/36 与 monolith 同;OpenRA 的两个历史点值(0.976494 / 0.988998)被证明是同一帧一次 draw 的顶点前缀撕裂——wire 臂的 frame-serial floor 不健全,B3 关闭;bsl-esc-menu 是 server 死 VkBuffer 无界累积,M2 + r2 已落地(主机 spawn server 825 → 546 MiB;真机验收待 p7w6)。
- 仍是方向、不据此伪造新日历:全功能 split(P8 之后)、纯度门在非 verify 构建上转绿(P13)。
再基线检查点仅剩一条:**P7 中点完成子系统 < 40% 立即重定基线**(P3a 的检查点发现不了 Magma 特有的超期)。
## P5 / P5b 出口记录的债务
| 债务 | 去向 / 当前口径 |
|---|---|
| P5 的 27 个普通 inproc wrong-answer(历史计数) | **已结(2026-09-22,`3c80cd62` 同名重跑,`MEASUREMENTS.md` §7.2;inproc 与 spawn 逐名相同)**:原「22 → P4b/P7 texture readback」→ **19 绿**(14 layered、3 packed depth/stencil、2 framebuffer recycle),余 **3 条 framebuffer recycle 由错答变为具名停止**——三条都是 `integration-monolith-control` 里 `MOBILEGL_PIPE_PUSH=0` 的对照臂(DG `Legacy` → `Fatal{PipeLegacyMemosDisabled}`;DV `Legacy` / `AbaControl` → `Fatal{UnmigratedVerb, "Magma:clear-framebuffer-record"}`),旋钮在 split 下没有臂,不是 readback 欠账;原「3 → P7 query」→ **3 绿**;1 inspection(`TextureParamsWithoutASamplerView`)→ **绿,「待核」关**;1 FBO/RBO delete → 绿。同一重跑的 1267 名合并 lane 普查 811/203/62/191 → 1051/172/37/7,44 条非绿全是钉了非默认 `MOBILEGL_PIPE_PUSH` 的对照臂 |
| `rsp` residual inputs | **P5f 已关闭残余读取**:字段归属 41 record / 6 derived / 0 barrier / 16 fatal;strict 与双块 marker 表均为空,双后端逐帧 RSP 门 2 PASS / 0 skip,Redmi 36 个统计窗口 rsp=0。P5e 的“rsp 只计被采纳设障 pull”是历史计数规则,八对 admitted 的历史观察不再是当前允许集合 |
| `SEG_REPLY` 2 MiB 单槽 payload cap | → **P9**(契约 §10.2.3 改判,ID-47);stream 链路上 reply 本就是消息,cap 变成 P6.5 nd 的 `maxReplyBytes` 窗口 |
| `GetCaps` 的两个 blobref | 目前不骑 record;一旦运输,必须有 server→client carrier rule,不能套 `SEG_STAGE` |
| PACK-PBO readback | P5 曾具名拒绝;`70df57bb`(2026-09-20)起 **client 侧已实现**(bounce 紧凑 reply + 按 PACK 步长逐行 `UploadSubData`,`EmitTables.cpp`;`glGetTexImage` 同形且按 `MaxReplyBytes` 分块);剩余只是 fire-and-forget(`MarkReadPixelsPackBuffer` 全仓无生产调用者)→ **P9**(ID-57) |
| ABI fingerprint | **已由 P6.5 wf / nd 落地(`fe28bdb5`)**:`wireFingerprint` 布局摘要 + `buildFingerprint` 拆分(只在 `Dial==Fork` 或 `MOBILEGL_IPC_REQUIRE_SAME_BUILD=1` 下比较)、`LinkTerms` 四个尺寸由 server 陈述、`DynamicBackendParameters` 定宽;`CONTRACT-P6.md:666` 仍把定宽记在 P7 名下,待改 |
| 默认 32 MiB staging | 目标负载单次 128 MiB 上传装不进默认 stage;**内容侧分块已落地**(buffer 范围走查 `1e7c372e`、纹理整宽 slab `9469d48e`,预算 `MGPipeStageChunkBytes()` = 默认 segment/4 = 8 MiB),故普查 / Redmi 的显式 256 MiB profile 不再是这两条路径的必需;未接入分片的 record 类型与专用 carrier 仍归 P8(开放问题 11) |
| P5b 的 inproc 依赖 | 历史的 scoped client binding、mip registry 查询与 FBO death mailbox 已经 P5c/P5f 改为记录、句柄和控制/事件路径;P6 核验并装配已有载体,不再重做这些迁移 |
| 未迁移与仿真路径 | **class-C 已归零到两个**(2026-09-22 核):`EmitTables.cpp` 的 `MGR_UNMIGRATED_*` 列表全空,只剩手写的 `SetSwapInterval`(P10)与按裁定无行的 `DeleteTransformFeedback`(P9);契约 §11 / SPAWN-PLAN §8 的"511 class-C entries"是 P5 joint 树上的旧普查(`notes/p6/census-classC.md`),不是当前数;~~client vertex arrays~~(已实现:`beba0256` 起 wire 复制到 owned buffer 并保留 baseVertex/baseInstance/drawID,本轮补齐 monolith 臂 `62bfe461..e258a822`)、multi-draw client indices、RGB 三通道 CPU mip、renderbuffer copy endpoint、未绑定 named clear、`texture-remint-pull` 仿真保留具名拒绝 → P8 / P9;Magma split compute / image 的“82 个错答”是 P5b 历史普查计数,不是当前失败清单;P5f 后续已补应用 buffer consumers 与 Android 旋转 blit,Magma inproc 已能运行所测 MC 世界;后续 run-ahead 已完成([报告](notes/p5f/magma-runahead.md));XFB、部分不对齐范围、placeholder/native-format 及进一步性能工作仍归 P7([报告](notes/p5f/magma-inproc-fix.md)) | **P5e 补 → CI 收尾更正**:client vertex arrays 曾是 run-ahead 下的具名拒绝(ID-82)与预期红车道 `integration-clientarrays-split`(ID-134);`beba0256` 起已实现,`347bb90c` 已把该车道翻转为**硬绿门**并纳入主 GPU/split 标签,本轮补齐 monolith 臂(`62bfe461..e258a822`);staging 仍归 P8
| E2 wire 内容控制 | draw-drop 是 OpenRA 的有效控制(758 draws,SSIM 0.000036);clear-drop 被全屏 overdraw 掩盖,不能作控制 |
| max record bytes | 实测 reduced / OpenRA `maxrec=784 B`,默认 cap 4 MiB;只代表所测负载,后续索引 / indirect 尾仍须记录 |
| 树外脚本 | 普查跑器、`wsl_p5_gate.sh`、Redmi 定频行都在 `~/w7/notes/`,git merge 不会传播 |
| 临时 CI trigger | 合入 dev 前删除 `test.yml` / `apk.yml` 的 `feat/disaggregated` TEMPORARY trigger |
## 开放问题
P0 已回答的不再列出(spike A 的域、spike B 的分档、`posix_spawn` 不可用、OOM 探测惯用法、`GetInteger64i_v`/`GetProgramiv` 退役、D21 与 `RenderbufferObject` lifetime id、动态 accessor 基线)。
1. **client 侧 dirty 走查的真实每 draw CPU 代价。** 已答(P2–P4a):推送没有在拉取基线之下净减少;Release 下 P2 边界 +6–12%,P3a 在 VAO 切换密的 rd12 上再加 +17–19 pt,P4a 在 Espryt 上 +3.4–5.7 pt(`MEASUREMENTS.md` §3–§5)。用户 2026-09-08 接受,性能自此只记录。
2. **真实语料上纹理重铸拉取的发生率。** 已答(P4a):可忽略,保留 LRU 维持默认 0——79 例 × 两后端 780 个统计窗口里共 2 次(`iris-photon`、`iris-derivative` 各 1,只在 DirectGLES)。但 P5b 下这条路径是具名 Fatal,正是这两条 trace 加 `create-indirect` GLES 的首阻塞(P9)。
3. **spike B 的 `untrusted_app` 域复核。** 两台设备的分档在 `shell` 域测得;从应用进程再跑一次 `extmem_probe`(spike A 的 exec 钩子已可用)。P11 前做。**2026-09-22 降级**:只对同机臂的 T0 / T1 有意义,stream 数据面不需要。
4. **渲染状态的 wire 粒度。** 已答(P2):16 个边界 / 15 个 chunk,7 pipeline 396 B + 8 dynamic 772 B;CSO LRU 64 暂定,P13 重调。
5. **无存储的 capability。** 已答(P2):`FramebufferSrgb`、`DepthClamp`、`TextureCubeMapSeamless` 三个都补了真存储,`sizeof(RenderStateParameters)` 仍 1168。
6. **具名 UBO host payload 的形状(D-B8)。** Magma 在 26.3 世界每帧重打包 331 KB 具名 UBO 字节,Espryt 为 0。要么冻结第二变长尾形状,要么走备选(Magma 直接描述符绑定常驻 `VkBuffer` range,独立 `dev` PR)。P7。
7. **`MG_Util` 的切割缝。** server 需要 SPIRV-Cross / ESSL 转译缓存 / 格式处理器 / POST 探针,client 需要 glslang 与反射层;`MG_Util` 内部是否有干净的 Transpile-vs-Reflect 缝未审计。P7。
8. **一份反射归档能否服务三个消费者。** Espryt 那一半已答(P4a):能且不需要复制。Magma 的第一个消费者已由 P5f fm 答:`MagmaProgramSource` 全部从 wire `ProgramArchive` 取,无前端对象、无复制。第二个(storage block 表)今天是**否**且变差了:split 下 `MagmaProgramSource::EnsureStorageBlocks` **每 draw** 重跑 SPIRV-Reflect(monolith 臂 `DirectVulkan.cpp:177-200` 至少有持久缓存);答案是往 `LinkArtifacts` 加有序 `storageBlocks`(数据已在 `blockReflection` 里),两个消费者都改读它。**已答(P7 wave 2-C,`dce9953d..cf7ca59f`)**:`storageBlocks` 入归档、两个消费者改读、每 draw SPIRV-Reflect 4→0;`g_programResourceCaches` 的 monolith 半边归 P13(G1)。
9. **viewport-array 回放能否塞进一次 `draw_vbo`**:各遍之间观察到的状态是否与今天一致未验证。P8。
10. **`ResidentSubData` 的不对称。** 已答一半(2026-09-22 核):**Magma 的实现已补**(`VkBufferManager.cpp:123` monolith 表、`:148` wire 表,`fff9d639f`)。**仍未答:`kCapResidentSubData` 从未被 server 发布**——`MG_Backend/Init.cpp:192-219` 是全树唯一的 `SetCapabilityBits` 生产调用点,只 OR 了 XFB、三个 query 位与 run-ahead 位;于是 `PipeFill.cpp:848` 在 split 下恒 false,**两个后端在拆分模式下都退回 `BufferObject.cpp:711-716` 的就地 memcpy**,Magma 的实现在 split 里是死代码。**已答(P7 wave 2-C,ID-P7-20)**:`InitServerRoleCommon` 按 server 自己的 wire 资源表发布 `kCapResidentSubData`,镜像收到即验收;白盒「发出的是 opcode 49」在 P11 采纳档落地前不可达(§5.4 债)。
11. **`SEG_STAGE` 的上限。** 内容侧已答(2026-09-20):**分块已实现**——buffer 范围走查(`1e7c372e`)与纹理整宽 slab(`9469d48e`)按 `MGPipeStageChunkBytes()`(默认 32 MiB 的 1/4 = 8 MiB)切成多条 `resource_subdata`,所以单次 128 MiB 上传不再需要显式 `MOBILEGL_IPC_STAGE_MB=256`;单片仍超 arena 或该 record 类型未接入分片时仍是 `Fatal{RingOverrun, "SEG_STAGE"}` 兜底。**仍未答(2026-09-22 收窄)**:a6 §6 实测没走查的只剩 **program archive**(唯一无界 blob)与 **`draw_vbo` 的 `NumDraws*12` range 尾**;宿主索引 span 无 producer(划掉),`set_global_constants` 有走查。二者的分片是 P6.5 sl 与 P8 共用的同一件事,stream 发送窗口对着它们定尺。MC in-world / Create 的占用分布仍未量(门 8 已给出两条负载的字节/帧与记录/帧,逐 blob 分布只在 integration 车道取到)。
12. **P13 之后 split-only 渲染 bug 的 server 侧第二意见。** verify 构建 + recorder 只覆盖推送内容,不覆盖后端对它的解释。
13. **烘焙后的内部 shader 能否在没有活 `ProgramObject` 的情况下表达 uniform location 与 UBO 布局。** 颜色 blit 已答(P5f fv:`WireColorBlit.{vert,frag}` 用 push constant,`WireColorBlitSpirv.h` 签进树);窄化到深度 mip 程序(今天在 split 下被编译掉、其 split 路径是具名 Fatal)与直通 TCS(`ProgramFactory.cpp:4060-4262`,第三个未盘点的内部 shader 生成器)。**已答(P7 wave 2-B / B2)**:深度 mip 烘焙 (A)(D)(`WireDepthMipmap.*`,push constant,`MOBILEGL_BAKED_INTERNAL_SHADERS` 新鲜度门表驱动)与 (B′)(disaggregated 构建的 monolith 臂改用烘焙模块)落地;multisample resolve 第三套烘焙 shader 随 B2;直通 TCS 仍未盘点。
14. **推送模型改变哪些按拉取模式调过的缓存命中率。** 幸存者容量在 P13 重调。一条线索:设备上 26.3 Espryt 的 `sve` ≈ draw 数(每 draw 重发一次 sampler-view 集合),桌面只有 ~0.07/draw;列入 P3b/P4b 优化清单。
15. **monolith 的 `*IndirectCount` 不调 `SyncGpuWrites()` 是不是潜在缺口。** 独立 `dev` 问题,拆分不得借机顺手修。
16. **索引宿主镜像的实际内存占用。** MC / Sodium / Iris 语料里 element-array buffer 总量未测;若显著超 64 MiB,退化路径的频率与代价必须实测。P8。
17. **`create-indirect` fixture 在 Adreno 830 上的失败**是 `dev@81b17c0b` 就有的(基线 APK 复现),不是本分支造成;`rd12` + Magma 在两台 Adreno 830 上的 `scudo::reportMapError` 崩溃同样是 `dev` 侧。两者排除在设备 A/B 之外、留在桌面语料里;P8 要在 `create-indirect` 上断言 `roundtrips-per-frame == 0`,所以 `dev` 的修复在别人的关键路径上。
18. **split 两侧的负载平衡。** P5e 解锁出来的新问题:VD32(~3550 draws/帧)下 apply 线程 **11-12 ms**、client **15.7 ms**,**client 是瓶颈且打满 ~94%**。把工作从 client 挪到 apply 会直接降瓶颈——这在 lockstep 下毫无意义(两侧串行相加),**run-ahead 才使它成为一个杠杆**。供选:编码侧的 emitter、tracker 走查、持久映射的脏页追踪(`change_protection` 在侧写里占 1.3%)。需先重新侧写:等待消失后尾部各项的分母都变了。排在 P5e 之后;无主,2026-09-22 建议挂在路线图推完后的优化阶段,P6.5 的 2×2 矩阵车道顺带重新侧写
19. **链路带宽预算与压缩。** TCP 终局下 `SEG_STAGE` 字节/帧对的是 Wi-Fi / USB(量级 30–100 MB/s),不是内存;`ARCHITECTURE.md` §14 的 ~1 MB/帧预算在 60 fps 是 60 MB/s,已贴着 Wi-Fi 上限。是否对 stage 载荷做 LZ4 类压缩、纹理上传是否按内容哈希在 server 侧去重,要等门 8 的分布出来再定。P6.5 契约写下第一行之前必答。
20. **大单次上传的停顿。** 128 MiB 一次上传在 50 MB/s 上是 2.5 s;内容侧分块解决的是"装不装得下",不是"要等多久"。跨机臂上要么接受并记录,要么给上传做异步化(与 P9 的语义一起想)。不假装解决。
21. **配对 / 认证的形式。** **已裁定(ID-P7-3,2026-09-22):令牌足够,不做 TLS。** 威胁模型是「任何人都能连的远程代码路径」而不是窃听:令牌常量时间比较、≥16 字节、无令牌只 loopback、数据面以 `Welcome.dataNonce` 绑定到已认证的控制连接、fork 前认证。TLS 作为 P12 的开放项保留。
22. **client 平台范围。** 默认先 Linux + Android(现有 CI 能验),Windows client 第二批(`ShmSegmentWin32` 在;`pipe:` 具名拒绝作废、TCP 替代),macOS 仍不拆分。这决定 `ARCHITECTURE.md` §15.3 "Windows 机器不是正确性门"要不要改。
23. **半开连接的检测。** Wi-Fi 掉线不产生挂断事件;dl 的闩取自描述符挂断,TCP 上需要 keepalive / 心跳超时并**由传输层报告为挂断**——这与"绝不取自 apply 超时"不矛盾,写清楚免得重吵。P6.5 ct。
24. **E1 对照的重定义(ID-122)无主。** 建议并入 P6.5 的门设计:S8 的好路径断言(`SessionFaultCount() == 0`)已是它想表达的形状,缺的是一个能被证伪的负控。