From be345d711c89a46197a4fa01fc8405ffc7e430c1 Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Fri, 18 Sep 2026 21:30:15 -0400 Subject: [PATCH] [Docs] (Disaggregated): P5e progress, in full - the landed-content table, open items, ruling index and phase report - New P5E-RUNAHEAD.md: P5e's phase report, a sibling of P5D-INPROC-PERFORMANCE.md. It carries the twelve-package list, the three defect classes and the lesson each one cost (a runtime arm no gate covered / build flavours no gate covered, where the FIRST gate added for them was itself a false green / a guard that could never fire), the profile and both device tiers, the unfinished items, and the next opportunity run-ahead unlocked. - CURRENT_STAGE_PROGRESS.md: the stage table's P5e row becomes twelve packages + run-ahead armed + ID-80..136; new section 2.7, the P5e landed-content table, in the same shape as P5c's and P5b's; section 2.5 retitled as historical; two sections both numbered 4 fixed; five P5e entries added to the open items; next-step item 1 rewritten from c0e to the evidence the close still lacks; the ruling index extended from ID-79 to the load-bearing entries of ID-136. - ROADMAP.md: the P11 row gains P5e's correction - retiring the draw lockstep did NOT need gPipeInputs versioning, because that race was in the metadata scalars. The row still holds for the general case, and contract section 3.2's per-role stamp storage is still unlanded. - README.md: the status line gains P5e and run-ahead, and "next is P6" becomes "P6 comes after P5e". Doc citation check: 3 problems, all pre-existing entries in CONTRACT-P5.md, none related to this change. --- docs/Disaggregated/CURRENT_STAGE_PROGRESS.md | 46 ++++- docs/Disaggregated/P5E-RUNAHEAD.md | 198 +++++++++++++++++++ docs/Disaggregated/README.md | 2 +- docs/Disaggregated/ROADMAP.md | 2 +- 4 files changed, 242 insertions(+), 6 deletions(-) create mode 100644 docs/Disaggregated/P5E-RUNAHEAD.md diff --git a/docs/Disaggregated/CURRENT_STAGE_PROGRESS.md b/docs/Disaggregated/CURRENT_STAGE_PROGRESS.md index 92ce6736..da1f7404 100644 --- a/docs/Disaggregated/CURRENT_STAGE_PROGRESS.md +++ b/docs/Disaggregated/CURRENT_STAGE_PROGRESS.md @@ -11,7 +11,7 @@ | **P5b** inproc 下的 verb 迁移(Minecraft 优先) | **已收官(2026-09-16)** | `37fc4fdb..82683d4a`;`MEASUREMENTS.md` §7 | | **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 | **进行中** | 契约 `MobileGL/MG_Remote/CONTRACT-P5E.md`(c0e 落地);计划 `~/w7/notes/p5e/BRIEF-P5E.md`、裁定 `~/w7/notes/p5e/INTEGRATOR-DECISIONS-P5E.md`(ID-80..98);包序 c0e → id → {vi, sb, pg, tx2, fb} ∥ ra,集成 commit 翻 `kMGPipeP5eRunAheadReady`。**八包已全部落地合并**,当前头 `3cc4e1ec`,逐门数字与设备验证见 §2.5 | +| **P5e** 退役 Espryt draw path 的 lockstep | **进行中(run-ahead 已武装)** | 契约 `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 | P5e 之后 | 届时只是传输替换 | ## 2. 当前头实测 @@ -33,7 +33,7 @@ | Redmi 四臂复测 | **未做**(E-P5c #5 是记录项;本机无设备,需 Redmi `2f7cbe2e` 窗口) | | 79 trace 普查 | **未重跑**(全集语料不在本机;本机可用语料的实测见 `rsp` 与 audit 行)。P5b 收官数字(72/6/1)仍以其头为准 | -## 2.5 P5e 当前进度(头 `3cc4e1ec`) +## 2.5 P5e wave 2 与 fix1:设备发现的 monolith 回归(历史,头 `3cc4e1ec`) 八包全部落地并合并:wave 1 = **c0e**(契约 + 线上行)+ **id**(身份按 `{slot, gen}` 重键),wave 2 = **vi / sb / pg / tx2 / fb** ∥ **ra**,集成提交 `44f91c74` 解了三处两包相接的 seam。**`kMGPipeP5eRunAheadReady` 与 `kMGPipeP5eClientWaitRuleLanded` 仍为 false**:翻转它们的门是 BRIEF-P5E §3(strict 车道转硬绿,只余 §7 白名单)与 §4(设备出口),前者尚未达成。裁定见 `~/w7/notes/p5e/INTEGRATOR-DECISIONS-P5E.md`(ID-80..110)。 @@ -159,6 +159,24 @@ flaky 是竞态**。根因(ra2 实测,推翻了我 ID-132 的机制猜测) **尚未回答的**:VD12 下面板上限之上谁更快。本机定不住更高的频率,而未定频的一次高频窗口里 monolith p50 201 / run-ahead p50 181(约 1.10 倍)——那一次不可配对,只能作为方向性提示。要回答它需要关掉 vsync 或换一台上限更高的设备。 +## 2.7 P5e 落地内容 + +| 波 | 包 | 内容 | 效果 | +|---|---|---|---| +| 1 | c0e | 契约与线上行:`PipeCalls.def` 第五列 `WaitClass` + `MGPipeWaitClassFor`、`set_program_bindings`(opcode 80)、`kCapRunAheadApply`(位 10,只在 DirectGLES 臂)、`kDrawClientArrays`、子系统位 13、`MGPipeImageAccess` 一张表、两个旋钮、跨包 seam | 十一个包按同一契约并行施工,且全部 inert | +| 1 | id | registry 按 `{slot, gen}` 重键、三个 by-handle resolver、分配器守卫(含按对方身份索引自己表的第二道守卫,ID-101) | 身份不再是前端指针 | +| 2 | vi | 顶点输入 / VAO / 索引 | 普通 draw 路径的 bound-VAO 行退役 | +| 2 | sb | 缓冲绑定点;`MGPShaderBuffers::WritableMask` 加宽(ID-104);consumer 位 13(ID-106) | 索引绑定点过线 | +| 2 | pg | 逐 link 的反射归档进 SEG_STAGE;`set_program_bindings` 的三条 post-link 尾 | 后续 pa 的前提:数据已在线上 | +| 2 | tx2 | 纹理与 sampler、staged store 的按句柄读、纹理单元窗口 | | +| 2 | fb | framebuffer、附件、image、blit | | +| 2 | ra | 等待规则 `EmitAndWaitTails`、present credit、`gPipeInputs` 变 server 角色内存、事件环流控 | 等待规则本体 | +| 2.5 | fix1 | 设备发现的 monolith 臂回归(ID-107):`SyncAttachmentSurface` 的存储同步按传输选臂;`MonolithAttachmentClearScenario` + `DirectGLES.PushMonolithArm.` 车道 | `integration-gpu` 1157→1294 | +| 3 | pa | `PrepareForDraw` / `PrepareForCompute` 的 program 拉取离开前端,`SyncCurrentVertexAttributeValues` 改读 `record->Archive->Link` | `GetProgramForDraw@DrawArrays` 62→0、`GetProgramForDispatch@DispatchCompute` 7→0,**无线上改动** | +| 3 | mv | `MultiDraw.cpp` 的 `BoundIndexBuffer` 改按句柄(ID-113);五个 multi-draw 档位各自的门禁条目 | `GetBoundVertexArray@DrawArrays` 9→0;`integration-split` +60 条 | +| 3 | gl | 生成式采纳谓词、strict 旋钮的第三态、车道两侧棘轮、`ResourceCopyRegion` 等待类、Magma 车道切分;以及两处潜伏崩溃(ID-111 的盖章、ID-112 的编译期绊线) | 车道成为可被脚本判定的硬绿 | +| 3 | ra2 | 翻开开关后暴露的竞态(守卫把"关于未来的断言"当豁免依据);`BoundDrawIndirectBufferId` 退役并撤回升级 (iii)(ID-136) | 翻开开关后 `isplit` 112→179 | + ## 3. P5c 落地内容 | 包 | 内容 | 效果 | @@ -187,7 +205,7 @@ p50 181(约 1.10 倍)——那一次不可配对,只能作为方向性提 | r1 / r2 | P5 收官审查 13 项(coherent-map 服务器侧绕过、FBO 死亡在 client 线程、`PACK_SWAP_BYTES`、`RingOverrun` 等待、CI 对照九项) | — | | 收官审查修复 | 索引 span 上界(`Count × IndexSize` 必须装进声明的 run)、fence wait 预算、`Fatal{ReplyStatusInvalid, "ReadPixels"}` | P5b 审查 0 blocker | -## 4. 真实负载(inproc,独立 apply 线程) +## 4.1 真实负载(inproc,独立 apply 线程) - `DrawElements` 不再是阻塞:d1 普查时 77 个 Minecraft 后端用例中 28 个渲染通过(SSIM ≥ 0.99995)。 - blit + mip 之后:`improved-transparency-minecraft-26.3` DirectGLES SSIM 1.0 / DirectVulkan 0.999914;`minecraft-1.21.4-fabric-iris-bsl-in-world` DirectVulkan 0.997324、DirectGLES 0.997496;OpenRA 双后端 1.0。 @@ -208,11 +226,16 @@ p50 181(约 1.10 倍)——那一次不可配对,只能作为方向性提 | 15 个 class-C 槽(query / sync 尾 / `GetTexImage` / `SetSwapInterval` 等)无负载命中,仍具名拒绝 | P9 / P10 | | Redmi 定频行只在树外 `~/w7/notes/p2/devices/pin_device.sh`;钉频口径 2026-09-16 起 1100 MHz(原 1050) | `devices/pin-verification-2026-09-07.md` | | `test.yml` / `apk.yml` 的 `feat/disaggregated` 触发器是临时的,合入 dev 前必须移除 | — | +| **P5e:BRIEF §3 的三条阴性对照与逐包 red-once 重跑未执行**(ID-121 已把后者重排到车道变绿之后,现在可跑) | `~/w7/notes/p5e/FLIP-CHECKLIST.md` phase C | +| **P5e:`split_negative_controls.sh` 的 E1 对照自检失败**,且索要的 `Fatal{BarrierViolation}` 在 `BATCH_WAITS=1` 下不可能触发——一个永远打不响的对照比没有对照更坏 | ID-122 | +| **P5e:契约 §3.2 的 per-role stamp 存储未落地**,正是它让 run-ahead 的元数据竞态成立;§8 修正 8 同样 UNLANDED | ID-120 / ID-135;`CONTRACT-P5E.md` | +| **P5e:VD12 面板上限之上谁更快未答**(本机定不住更高频);VD32 那一轮回答了一般性问题 | `MEASUREMENTS.md` §11 / §11.1 | +| **新机会(非缺陷)**:VD32 下 apply 11-12 ms 对 client 15.7 ms,两侧不平衡;把工作从 client 挪到 apply 会直接降瓶颈——lockstep 下无意义,run-ahead 才解锁 | `P5E-RUNAHEAD.md` 末节 | ## 6. 下一步 0. P5d 的遗留(`P5D-INPROC-PERFORMANCE.md` "什么没完成"):R-1 序列化留给 P3b/P4b → P11(`gPipeInputs` 版本化已写进 P11 行);线程放置记录;小项随 P3b/P4b 顺手。 -1. **P5e 退役 draw path 的 lockstep**(进行中):c0e 已落地契约与线上行——`PipeCalls.def` 的第五列 `WaitClass` + 生成的 `MGPipeWaitClassFor(op)`、`set_program_bindings`(opcode 80,空路由)、`kCapRunAheadApply`(位 10,只在 DirectGLES 臂且只在 `kMGPipeP5eRunAheadReady` 为真时发布)、`kDrawClientArrays`、子系统位 13 与 push 默认 `0x3fff`、`MGPipeImageAccess` 一张表、`MOBILEGL_IPC_RUN_AHEAD` / `MOBILEGL_IPC_PRESENT_CREDIT`、以及跨包 seam(`MGPipeBarriered`、两个 applier 入口、六个 by-handle 后端签名)。全部 inert:caps 位未发布前 client 跑今天的 lockstep 路径。下一个是 **id**(registry 重键 + by-handle resolver + 分配器守卫),然后 {vi, sb, pg, tx2, fb} 并行、ra 从第一天起并行。 +1. **P5e 收官**(十二包已落地、run-ahead 已武装,报告 [`P5E-RUNAHEAD.md`](P5E-RUNAHEAD.md))。剩下的不是迁移而是证据:(a) BRIEF §3 item 4 的三条阴性对照;(b) item 5 的逐包 red-once 重跑——其证据形式当时**不可执行**(每个候选在 revert 之前就是红的),ID-121 已重排到车道变绿之后,现在可以跑了;(c) 修或删 `scripts/ci/split_negative_controls.sh` 的 E1 对照(ID-122:自检就失败,且索要的 Fatal 在 `BATCH_WAITS` 默认值下不可能触发);(d) G1 仍由 CI 断言(ID-123)。契约 §3.2(per-role stamp 存储)与 §8 修正 8 标为 UNLANDED,随后续阶段。 2. **P6 spawn transport**:`SocketTransport` + `ServerMain` + 握手 / 退出语义 + EGL forwarder 的控制面帧;P5c 之后这只是传输替换。注意 P5c 留下的:`s_synced` / `g_syncedRenderStateParameters` 按 context 世代重置;两个豁免 scope 里的探测在 spawn 下根本不存在对应内存,P6 第一天的红就是它们的清单。 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。 @@ -257,4 +280,19 @@ p50 181(约 1.10 倍)——那一次不可配对,只能作为方向性提 | 76 | P5b 出口 = 主机门 `348d22a4` complete + 79 trace 普查 + Redmi 正确性 8/8 + 四臂 A/B | | 77 | Redmi 钉频期望 1050 → 1100 MHz(2026-09-16);iris-bsl 8 组判为 fixture 123 帧上限,补充表不进 200 帧尾门表 | | 79 | **P5d 三轮规则(2026-09-18)**:批处理免等只允许 apply 不读残余填充任何字段的记录(`generate_mipmap` 因后端读 `GetActiveTextureUnit` 而必须等);性能诊断以符号化调用图为准、不再从 self% 猜归属;Redmi 上 fps > ~115 的 monolith 数字视为 120 Hz vsync 封顶,配对比较以逐线程 CPU ms/帧为主指标;`sched_setaffinity` / `taskset` 在该内核对 app 线程无效,线程放置只记录 | +| 81 | **P5e**:handle 臂只在 `Transport != Monolith`;push-monolith 构建保留其前端臂、字节不动 | +| 84 | rule F 只约束**未设障**记录;设障记录保留 P5C 语义 | +| 90 | Magma 整个 P5e 保持 lockstep;`kCapRunAheadApply` 从不在 DirectVulkan 上发布 | +| 107/109/110 | 一整条运行时臂没被门禁(137 SEGFAULT / 38 scenario);`gpu` 成为常设门禁步;**"记下了句柄"≠"记录臂被选中"**,要自述式传输测试 | +| 111 | 盖章必须与服务端**自身**能力合取,否则翻开开关会当场打死 Magma | +| 112 | `EmittedCallSuppliesTheWholeField` 上编译期绊线:把 Magma 的空指针崩溃换成构建断裂 | +| 116/125/128 | 采纳规则**生成**而非手抄,三个析取项(静态设障 / 退役相不在本阶段 / 按升级设障) | +| 117 | 被采纳的 pull **响而不致命**(`Admitted{` 标签),车道才可能真正变绿 | +| 119/115 | `rsp` 的钉子改写为可检查的形式;车道必须有阳性对照 | +| 121 | §3.5 的 red-once 证据形式在当时不可执行,重排到车道变绿之后 | +| 124 | 四个构建 flavour 里只有 split 编得过;**一条臂的证据必须自证它确实是那条臂**(看生效的编译宏,不看 flag、不看 cache) | +| 129 | 没有 stamp 行的 verb 什么都不采纳(采纳集合宁窄勿宽) | +| 131 | `ctest -L` 是**正则**:新标签不得以既有标签为前缀,且要数一遍证明 | +| 132/135 | **strict 硬绿是必要而结构性地不充分**(它跑在 lockstep 下);竞态在元数据标量而非字段值,而**打不响的守卫比没有守卫更坏** | +| 136 | 为让车道变绿而加的等待是真实路径上的真实代价:要问**哪条臂在买单**,不是哪条车道绿了 | | 78 | **P5c 插在 P6 之前**(2026-09-17):`inproc` 先做到两角色之间除 wire 零直接内存访问,P6 只换传输;值类 BARRIER-PULLED 行在 P5c 过线,对象类行留 twin 表阶段;codex 无额度期间只读审计改派 Kimi(K3) | diff --git a/docs/Disaggregated/P5E-RUNAHEAD.md b/docs/Disaggregated/P5E-RUNAHEAD.md new file mode 100644 index 00000000..43172748 --- /dev/null +++ b/docs/Disaggregated/P5E-RUNAHEAD.md @@ -0,0 +1,198 @@ +# P5e:退役 Espryt draw path 的 lockstep(run-ahead) + +头 `5c007b22`,`feat/disaggregated`。契约 `MobileGL/MG_Remote/CONTRACT-P5E.md`,裁定 +`~/w7/notes/p5e/INTEGRATOR-DECISIONS-P5E.md`(ID-80..136)。**run-ahead 已武装,阶段未收官**(见"什么没完成")。 + +## 目标 + +P5c 让 `inproc` 成为只经 wire 交换的诚实两角色,P5d 把它的 CPU 代价压下来,但 R-1 的 lockstep 还在: +客户端每条 `kCtxVerb` 都要等 `appliedSeq == emitSeq`。P5e 退役 draw path 上的这道等待——**客户端发了就走**, +只有"设障"的记录才等。 + +一条记录能否未设障,取决于**应用它时会不会读客户端内存**。所以这个阶段的绝大部分工作不是改等待逻辑, +而是把后端仍在读前端对象的那些点迁到记录上;等待规则本身是最后一步。 + +## 落地了什么 + +十二个包,三波: + +| 波 | 包 | 一句话 | +|---|---|---| +| 1 | c0e | 契约与线上行:`PipeCalls.def` 第五列 `WaitClass` + `MGPipeWaitClassFor`、`set_program_bindings`(opcode 80)、`kCapRunAheadApply`(位 10,只在 DirectGLES 臂)、`kDrawClientArrays`、子系统位 13、两个旋钮、跨包 seam | +| 1 | id | 身份:registry 按 `{slot, gen}` 重键、by-handle resolver、分配器守卫。ID-96 的答案是空集 | +| 2 | vi | 顶点输入 / VAO / 索引 | +| 2 | sb | 缓冲绑定点;`MGPShaderBuffers::WritableMask` 加宽(ID-104) | +| 2 | pg | program:逐 link 的反射归档进 SEG_STAGE、`set_program_bindings` 的三条尾 | +| 2 | tx2 | 纹理与 sampler、staged store 的按句柄读 | +| 2 | fb | framebuffer、附件、image、blit | +| 2 | ra | 传输机制:等待规则、present credit、`gPipeInputs` 变 server 角色内存 | +| 2.5 | fix1 | 设备发现的 monolith 臂回归(ID-107),见下 | +| 3 | pa | program 家族在 draw / dispatch 路径上离开前端 | +| 3 | mv | multi-draw 的前端 VAO 读点;并给五个 multi-draw 档位补上门禁条目 | +| 3 | gl | 门禁机制本身,加两处潜伏崩溃 | +| 3 | ra2 | 翻开开关后暴露的竞态,以及间接绑定那次 pull | + +**最重要的一条实现事实**:退役 `GetProgramForDraw@DrawArrays`(每帧每次 draw 都触发的那次拉取) +**没有改动任何线上结构**。记录早就带着逐 link 的 `ProgramArchive`;缺的只是仍去问前端对象的读者。 +本阶段多数迁移是这个形状——数据已经在线上,读者没跟上。 + +## 三类缺陷,以及各自教的东西 + +这一节是本阶段最可复用的部分。 + +### 1. 一整条运行时臂没有被门禁(ID-107 / 109 / 110) + +push 构建有**两条服务端臂**,运行时由 `MG_Config::Transport` 选;而 `integration-split` 的**每一条**条目 +都导出 `MOBILEGL_TRANSPORT=inproc`,split 专属场景又都在 `SetUp` 里自跳过——**手机实际发布的那条臂在 +门禁集合里零条目**。`44f91c74` 上 unit 2250/2250、`integration-split` 113/113 全绿,同一个头 +`ctest -L integration-gpu` 是 **1157/1294,137 个 SEGFAULT、横跨 38 个 scenario**,设备上表现为 monolith +启动即崩。`66621767` 修复,`gpu` 成为常设门禁步。 + +代码形态的教训:**"已经记下句柄"不等于"记录臂会被选中"**,这是两句不同的话;只因"所有写入点碰巧都被 +传输门住"而安全的座,是靠链条安全,要改成自述式的传输测试(ID-110 的 `MGB_TEXTURE_RECORD_ARM_SELECTED()`)。 + +### 2. 构建 flavour 也没有被门禁,而且第一次补的门禁是假绿(ID-124) + +门禁只配 split flavour。补上 `flavours` 步后实测:**四个 flavour 里只有 split 编得过**。 + +更值得记的是补门禁本身翻了一次车:第一版 `pull` 臂报 rc=0 / 982 TU,是假的—— +`CMakeLists.txt:496-500` 规定 `MOBILEGL_BUILD_DISAGGREGATED=ON` **强制** `MOBILEGL_PIPE_PUSH=ON`, +所以 `-DMOBILEGL_PIPE_PUSH=OFF` 被静默覆盖,那一臂其实是 split 构建的第二份拷贝;**而 CMakeCache 里 +那一行仍然写着 OFF**。改成从 `compile_commands.json` 断言**实际生效的宏**、不符即失败,才拿到真数字 +(743 TU,rc=1)。 + +**规则:一条臂的证据必须包含"它确实是那条臂"的证明。** 传的 flag 不是证据,cache 不是证据。 +同源:`ctest -L` 是正则,新标签不得以既有标签为前缀,且要数一遍(ID-131)。 + +### 3. 一个永远打不响的守卫(ID-132 / 135) + +翻开两个常量后 `unit` / `gens` / `flavours` 仍全绿,而 `isplit` 掉到 **112/181**、`gpu` 掉到 +**1290/1359**,**且 flaky**(-j 4 红 71、-j 1 红 68,单跑一条先过后崩)。全量普查:70 条红、 +只涉及**四个字段**、横跨**十一个 verb**。 + +**判据**:一个字段在一个 verb 上稳定失败 = 漏迁移;**四个字段散在十一个 verb 上且 flaky = 竞态**。 + +根因不是当初的猜测。`gPipeInputs` 的**字段值**已由 `SuppliedFieldMask` 分区、不竞态;竞态的是 +**元数据标量**(`CurrentVerbSerial`、`FilledGen[]`、`m_currentVerb`、`m_serverStampedVerb`)。 +而让它长期不可见的,是守卫本身:`RefusePipeInputsTouchWhileApplierOwnsIt` 的 run-ahead 臂对任何 +`isBarrieredFill` 一律豁免,豁免依据是一句**关于未来**的断言——"客户端马上就要 park"——而真实顺序是 +fill → emit → park。修法是把那句断言**变成事实**(写块之前强制等待),并让守卫去测事实。 + +同源的第二个缺陷:`MGP_VERB_OP_LIST` 把整个 draw 家族并到一行,于是十九个索引 draw verb 都答"设障"、 +都去填充、都不 park。 + +契约 §3.2 早就**规定了 per-role 的 stamp 存储,却从未落地**——那个缺口正是这次竞态成立的前提。 +一份描述"设计意图"而代码另做一套的设计文档,是这类缺陷得以存活的方式;`ARCHITECTURE.md` 的对应段落 +已按落地实现改写。 + +### 贯穿三者的方法论结论(ID-132) + +**strict 车道硬绿是必要条件,而且结构性地不是充分条件。** strict 跑在 lockstep 下,`ApplyOne` 把每条 +记录都盖成 barriered,所以"只在客户端不再等待时才发生"的事,它按构造看不见。它量的是**剩余欠债**, +不是 run-ahead 的**正确性**。阶段出口必须有一条独立的、**翻开开关之后**的车道。 + +## strict 车道成为可被脚本判定的硬绿 + +`GetProgramForDraw@DrawArrays` 62 → 0,`GetProgramForDispatch@DispatchCompute` 7 → 0, +`GetBoundVertexArray@DrawArrays` 9 → 0。采纳规则最终是**三个析取项**,没有一条是预先设计出来的, +每一条都是被一条真实条目逼出来的: + +1. 该 verb 的线上 op 静态设障(ID-116); +2. 该字段的退役相不指向本阶段(ID-125); +3. 记录是**按升级**而非按静态类设障的(ID-128)——XFB 活跃与客户端数组两种 draw,契约把两者都放在 + P5e 之外。 + +白名单由 `scripts/gen_pipe_field_ownership.py` **生成**而非手抄:ra 原本落在 CI 里的 13 行手抄表 +**在两个方向上都错**(漏了 `GetFramebufferBindingSlot@ReadPixels` 的 21 条,又多了不该采纳的 +`GetTextureObject@CopyImageSubData`)。被采纳的 pull **响而不致命**(ID-117):同样的标记语法,头部 +标签换成 `Admitted{`,按 `(field, verb)` 去重。车道是**两侧棘轮**——新出现的对要认领,不再出现的对 +要删除,否则会"烂绿"。 + +棘轮在合并当天就自己点了名:`GetProgramForDraw@DrawArrays` 不再出现(pa 退役了它), +`GetBufferBindingSlot@DrawArrays` 新出现(mv 的间接档位逼出了它,而 mv 自己在报告里**先命名了**这行 +潜伏项,然后才让它触发;它退役在 P8)。 + +## 门 + +| 门 | 阶段初 | 现在 | +|---|---|---| +| `unit` | 2250 | **2256 / 2256** | +| `integration-split` | 116 | **179 / 179** | +| `integration-gpu` | 1294 | **1357 / 1357** | +| 构建 flavour | 只有 split 编得过 | **pull / push / split 三个全绿** | +| 逐条 strict 普查 | 7 绿 / 109 红 | **179 绿 / 零 `Fatal{`**,八行 `Admitted{` | + +新增的 65 条车道条目,全部覆盖此前**一条门禁条目都没有**的臂或档位。两条预期红车道各自具名断言: +`integration-clientarrays-split`(`(Multi)DrawArrays+CLIENT_ARRAYS`)、`integration-magma-split` +(`GetFramebufferBindingSlot@Clear`)。 + +## 数字 + +设备:Redmi `2f7cbe2e`,FCL fordebug + Minecraft 26.3-rc-3 世界 `test`,DirectGLES。 + +### 先讲定频,因为第一轮矩阵作废了 + +第一轮**没有定 CPU 频**:**monolith 自己那一臂**的逐帧 CPU 在两次运行之间从 4.43 跳到 7.07 ms, +而慢的那次**温度更低**(53.5 vs 58.9 °C)——是 `walt` governor 把 policy0 从 2745 拉到 748 MHz, +不是热降频。未定频的逐线程 CPU ms/帧**不能跨臂比较**。 + +`tools/device_bench/pin_device.sh` 不认识本机 serial 并**明确拒绝猜**(正确,猜错会静默失败); +`~/w7/notes/p5e/pin_redmi.sh` 按本机节点写,写序 **min→硬件底 → max→目标 → min→目标**,并**回读 +`scaling_cur_freq` 验证**,每轮跑前跑后各 check 一次。 + +### 为什么值得做(侧写,`7c6f6886`,翻开关之前) + +symbolized `simpleperf cpu-cycles`,客户端 GL 线程:`SessionProducer::WaitForAppliedOrEventBacklog` +**31.58% 自身时间**,第二名只有 3.72%。计数器同期显示客户端每帧进入 doorbell 等待约 **916 次** +(对 ~849 次 draw),99.8% 靠自旋解决;`MOBILEGL_IPC_SPIN_US=0` 让每次都 park,帧率塌到 **20.9**。 +**代价是会合本身,不是自旋参数**——所以修法是"不再每次 draw 会合",不是"把每次会合做便宜"。 +等待之后,客户端线程是一条 1-3% 的长尾:**没有第二个杠杆**。 + +### 渲染距离 12:两条臂都撞面板上限 + +| 臂 | fps p50 | fps max | client ms/帧 | apply ms/帧 | +|---|---|---|---|---| +| monolith | 115.4 / 115.6 | 117.5 / 117.2 | 6.25 / 6.45 | — | +| inproc run-ahead | 118.0 / 117.6 | 118.6 / 118.5 | 6.87 / 6.92 | 4.35 / 4.43 | +| inproc lockstep(`RUN_AHEAD=0`) | 111.6 | 113.7 | 8.16 | 6.53 | + +monolith 与 run-ahead 都撞到 **120 Hz 面板**,只有 lockstep 撞不到。所以这一档回答的是"能不能撞到 +上限",不是"谁更快"。 + +### 渲染距离 32:可比的一轮 + +`renderDistance:32`,**~3550 draws/帧**(VD12 的 4.2 倍),**两条臂都远离任何上限**(游戏自身 +`enableVsync:false`、`maxFps:260`,实测 max 只有 37-71)。静置提到 75-150 s——VD32 的区块流式加载 +远比 VD12 长。每臂五次,交错。 + +| 臂 | n | fps p50 中位 | p50 范围 | client ms/帧 中位 | apply ms/帧 | +|---|---|---|---|---|---| +| monolith | 5 | 58.5 | 56.3 – 67.9(±10%) | 16.19 | — | +| **inproc run-ahead** | 5 | **59.9** | 56.8 – 61.1(±4%) | **15.72** | 11.1 – 12.1 | +| inproc lockstep | 2 | 35.5 | 34.5 – 36.5 | 26.17 | 19.7 – 20.5 | + +**结论:齐平。** run-ahead 在两个中位数上都略微领先,但**差值远在 monolith 自身离散度之内,不能读作 +"比 monolith 快"**。确实超出噪声的是稳定性:monolith 五次 ±10%,run-ahead ±4%。`mono1` 的 67.9 是 +全场唯一离群值(装包后第一次运行)。 + +**相对它取代的 lockstep:p50 +69%、client CPU −40%、apply CPU −43%。** 负载越重越值钱,符合预期—— +会合次数随 draw 数走,VD12 每帧约 900 次,VD32 约 3600 次。 + +两条臂**都是 client 线程打满**(约 94%),所以 client ms/帧 这一列是各自真正的瓶颈,可直接比。 + +## 什么没完成 + +- **BRIEF-P5E §3 的 item 4 与 item 5**:三条阴性对照、逐包 red-once 重跑。item 5 的证据形式在当时 + **不可执行**(每个候选在 revert 之前就已经是红的),ID-121 把它重排到车道变绿之后——之后尚未执行。 +- `scripts/ci/split_negative_controls.sh` 的 E1 对照**自检就失败**,且它索要的 `Fatal{BarrierViolation}` + 在 `MOBILEGL_IPC_BATCH_WAITS` 默认值 1 下根本不可能触发(ID-122)。一个永远打不响的对照比没有对照更坏。 +- **G1** 仍由 CI 断言;本阶段门禁只编 flavour,不做符号恒等(ID-123)。 +- 契约 §8 修正 8(`GetBufferBindingPointCount` 的 sticky forward)标为 **UNLANDED**(ID-120); + §3.2 的 per-role stamp 存储同样未落地,正是它让上面第 3 类缺陷成立。 +- **VD12 面板上限之上谁更快**没有回答:本机定不住更高频率。VD32 那一轮回答了一般性的问题。 + +## 下一个机会(不是缺陷) + +VD32 下 apply 线程只有 **11-12 ms**,client 有 **15.7 ms**——**两侧不平衡**。把工作从 client 挪到 +apply 会直接降低瓶颈。这在 lockstep 下毫无意义(两侧串行相加),**是 run-ahead 解锁出来的方向**, +留给后续阶段。 diff --git a/docs/Disaggregated/README.md b/docs/Disaggregated/README.md index 06dd9451..7c37781f 100644 --- a/docs/Disaggregated/README.md +++ b/docs/Disaggregated/README.md @@ -1,6 +1,6 @@ # MGPipe:MobileGL 前后端拆分 -> 状态:**P0–P5d 已收官**(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 spawn transport**——此时只是传输替换。当前头、逐门数字与开放项见 [`CURRENT_STAGE_PROGRESS.md`](CURRENT_STAGE_PROGRESS.md)。 +> 状态:**P0–P5d 已收官**,**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 交换的诚实两角色。P5e 之后才是 **P6 spawn transport**——此时只是传输替换。当前头、逐门数字与开放项见 [`CURRENT_STAGE_PROGRESS.md`](CURRENT_STAGE_PROGRESS.md)。 > > 性能纪律(2026-09-08 起):逐线程 CPU 与 tracker 绝对 ns **对着 pull 臂基线记录**,不作阻塞门;专门的优化阶段排在路线图推完之后。 diff --git a/docs/Disaggregated/ROADMAP.md b/docs/Disaggregated/ROADMAP.md index e3447a04..32313b67 100644 --- a/docs/Disaggregated/ROADMAP.md +++ b/docs/Disaggregated/ROADMAP.md @@ -35,7 +35,7 @@ | **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` 逐用例发布 | | **P9** 反向通道 | 待排 | `SEG_REPLY` 异步 slot 池;PBO 回读 fire-and-forget;`OnGpuWritten` 收窄;`OnBufferWriteback` 批处理 + epoch 排序;`OnXfbScatterReady`;`OnTextureWriteback`;`OnMipLevelsGenerated`;纹理拉取四条缓解 + 终止符;`OnGlError` 有序;`OnSurfaceChanged`;`OnLog` 分级;`SEG_EVENT` 溢出策略;class-C 的 readback 尾 | 回读 / XFB 场景在 split 下绿;`TextureRemintPullScenario`(含无解用例);拉取计数逐 trace 发布;两条故障注入 | | **P10** sync / query / present 节奏 | 待排 | client 铸造 query handle;轮询入口成门铃点 + `MOBILEGL_IPC_POLL_ESCALATE`;fence 完成度来自逐 fence 退休;非 present fence tick;`present` 1:1;credit 默认 1;roundtrip 计数器与输入延迟直方图;class-C 的 query / swap-interval 尾 | query / XFB / `AsyncCompile` 场景在 split 下绿;trace 上 draw / state / upload 路径 roundtrip 读零;零 timeout 轮询有界退出;配对 A/B 记录 | -| **P11** persistent map 与 ≥16 MiB 采纳 | 待排 | POST 探针档位选择(T0 主攻,Adreno 可选 T1,T2 回退);`SEG_ADOPT` 生命周期绑 `completedFrameSerial`;**`gPipeInputs` 版本化 / 双缓冲**(P5d 研究:draw barrier 延后一个 verb 的前提,须在 P3b/P4b 的句柄键控 twin 表之后) | `LargeArenaAdoptionScenario` 在所选档下绿;26.3 与两个 Create fixture SSIM ≥ 0.99;Adreno 830 上 p99 帧时与峰值 RSS 对采纳基线(163→21 ms / 40→115 fps / ~400 MB)回归不超过 10% | +| **P11** persistent map 与 ≥16 MiB 采纳 | 待排 | 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 存储仍未落地 | `LargeArenaAdoptionScenario` 在所选档下绿;26.3 与两个 Create fixture SSIM ≥ 0.99;Adreno 830 上 p99 帧时与峰值 RSS 对采纳基线(163→21 ms / 40→115 fps / ~400 MB)回归不超过 10% | | **P12** Android 生产窗口路径 | 待排 | `android:process=":mgl"` Service 收 Java `Surface`;server 生命周期绑 Activity;FCL env 与 plugin APK 开关表接线 | Minecraft 经 FCL 在 spawn 模式下于 Adreno 830 双后端入世界;杀 server 产生干净 device-lost latch | | **P13** 退役 pull 路径 | 待排 | 删 `SnapshotFromGLContext()` 非 verify 分支、`MGB_CTX`、`MOBILEGL_PIPE_PUSH`、`MOBILEGL_PIPE_LEGACY_MEMOS`;保留 `MOBILEGL_PIPE_VERIFY`;MGPipe recorder;删 `set_residual_value_state`;在计数器活着的情况下重调幸存缓存容量 | `static_assert(sizeof(ResidualValueBlock) == 0)`;三道纯度门在非 verify 构建上转绿;recorder 金标建立;monolith 逐线程 CPU 不差于 P0 基线 |