From a48bc46ec161c3ffe9be778468e33abf15edc858 Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Wed, 16 Sep 2026 10:32:58 -0400 Subject: [PATCH] [Docs] (Disaggregated): document the landed P5 inproc milestone --- docs/Disaggregated/ARCHITECTURE.md | 42 ++++++++++-- docs/Disaggregated/MEASUREMENTS.md | 103 +++++++++++++++++++++++++++++ docs/Disaggregated/README.md | 23 ++++++- docs/Disaggregated/ROADMAP.md | 44 ++++++++++-- 4 files changed, 202 insertions(+), 10 deletions(-) diff --git a/docs/Disaggregated/ARCHITECTURE.md b/docs/Disaggregated/ARCHITECTURE.md index 21968a4a..0bf56d13 100644 --- a/docs/Disaggregated/ARCHITECTURE.md +++ b/docs/Disaggregated/ARCHITECTURE.md @@ -1,6 +1,6 @@ # MGPipe 设计与架构 -> 本文描述**已决定**的设计。每条决定附一行理由;数字凡有实测的取实测(见 `MEASUREMENTS.md`)。落地状态以 `feat/disaggregated@458ccde1` 为准:标注"P0 已落地"的是树里的代码,其余是后续阶段要实现的形状(阶段号见 `ROADMAP.md`)。 +> 本文描述**已决定**的设计。每条决定附一行理由;数字凡有实测的取实测(见 `MEASUREMENTS.md`)。落地状态以 `feat/disaggregated@eec0e836` 为准:P0–P5 已落地;明确标成 P6+ 的仍是后续形状(阶段号见 `ROADMAP.md`)。 ## 1. 边界 @@ -93,6 +93,8 @@ CSO 在 client 侧内容寻址(Mesa `cso_cache` 先例):每类一张 `ska: include 闭包里,而 G1 不允许那里有任何符号位移。`--check` 与 `--self-test`(11 条阴性对照)与 `gen_pipe.py` 的同样在 CI 里跑。 +**G1 的 P5 codegen 陷阱(实测,`c1-v2.md` §9)。** capability gate 若替换的是指针表达式,就必须保留它的**表达式形状**,不能只保留真假值。两个 `if (const auto f = TABLE.GL.Slot)` 被 Bool-valued macro 改写成 block 后,pull `.text` **−144 B**,`GLImpl::GetInteger64v` −150、`GLImpl::GetIntegerv` +2;语义没变,G1 仍正确地判红。落地形状分成 `MGL_BACKEND_SLOT_CAP` 与 pointer-valued `MGL_BACKEND_SLOT_PTR_CAP` / `_PTR_LOCAL`,后者在 pull 展开回原 slot expression。规则不是“宏结果相等”,而是“byte-identical gate 必须保住 init-statement / short-circuit / pointer 的原表达式形状”。 + ### 3.2 分组与计数 | Class | 条 | 内容 | @@ -587,12 +589,44 @@ MobileGL/MG_Remote/ 仅 MOBILEGL_BUILD_DISAGGREGATED ``` - CMake option(`CMakeLists.txt:23`):`MOBILEGL_BUILD_DISAGGREGATED`(默认 OFF)追加 `MG_Remote/**` 进 `SOURCE_FILES`(`CMakeLists.txt:454-469`)并定义 `-DMOBILEGL_BUILD_DISAGGREGATED=1`;OFF 时 `MG_Config::Transport` 是 `constexpr Monolith`,`Init.cpp` 的分支编译期消失。`3rdparty/flatbuffers/include` 缺失时把 option 强制回 OFF 并 `message(WARNING)`(`CMakeLists.txt:440-451`)。`MobileGL` 与 `MobileGL_s` 都拿到同一份源。`MG_Test/Wire` 只在该 option 下注册(`MobileGL/MG_Test/CMakeLists.txt:93-95`)。 -- `MOBILEGL_BUILD_DISAGGREGATED_INPROC`(尚不存在):CI/调试形态,隐含开启前者,额外加角色隔离 shim。MGPipe 让需要角色分身的进程全局从四个(`pGLContext`、`gBackendFunctionsTable`、`pActiveBackendObject`、`pDefaultFramebufferInfo`)降到**两个**(pipe 表与 `pActiveBackendObject`):server 角色不再读 `pGLContext`(三道纯度门就是这个断言),`pDefaultFramebufferInfo` 由保留句柄 `{0,1}` + `OnSurfaceChanged` 取代。两个 shim 都不在 GL 热路径的每次访问上——这是 `inproc` 从"成本可疑的实验"变成"可交付形态"的直接原因(Android 上 dlopen 的库无法可靠用 initial-exec TLS,`pGLContext->` 在 `MG_Impl` 有 1494 处)。 +- `MOBILEGL_BUILD_DISAGGREGATED_INPROC`(P5 已落地):CI/调试形态,隐含开启前者并加角色隔离。MGPipe 让需要角色分身的进程全局从四个(`pGLContext`、`gBackendFunctionsTable`、`pActiveBackendObject`、`pDefaultFramebufferInfo`)降到**两个**(pipe 表与 `pActiveBackendObject`):server 角色不读 `pGLContext`,`pDefaultFramebufferInfo` 由保留句柄 `{0,1}` + surface forwarder 取代。两个角色通过 apply thread 与控制 mailbox 分开,而不是给 1494 个 `pGLContext->` 读点加 TLS。 - `MobileGLServer`:桌面 `add_executable` 链接 `MobileGL_s`;Android `add_executable` 改名 `lib*.so` 链接共享 `MobileGL`,由 AGP 打进 `jniLibs`。 - `MOBILEGL_TRANSPORT = monolith | inproc | spawn | unix: | pipe:`(P5 起在 `ConfigLoader.cpp` 解析),免费换来 ctest `ENVIRONMENT` 变体、trace-replay 的 `setenv` 块、FCL 用户可编辑 env、plugin APK 的 V2 开关表、`/data/local/tmp` CTS 路径。 - 测试接线陷阱:ctest `ENVIRONMENT` 是替换而非追加、`;` 必须转义、property 覆盖 job env,必须用 `mgl_itest_join_environment(... ${MGL_ITEST_COMMON_ENV})` 构造;`add_trace_replay_test` 加 `SPLIT` 后缀(否则与同 case+backend 重名)并加 `-DTRACE_TRANSPORT=` 给 `run_trace_case.cmake` 消费。 - CI(`.github/workflows/test.yml:1538` `pipe-gates`,P0 已落地):`gen_pipe.py` 重生成 + diff;`MG_Backend`/`MG_State` 下禁止 stdio 插桩的 grep 门;`gen_pipe_dirty_surface.py --check` + `--self-test`(**P2 起成为门**,`.github/workflows/test.yml:1601-1604`,取代原来信息性的 `--summary` 步骤);`check_doc_citations.py`(警告级,文档定稿后 `--strict`)。独立 job `flatc-check`。已落地:`include-graph-check`(P0.5)、`monolith-symbol-report`、`build-linux-verify` / `integration-verify` / `retrace-verify`(P1)。 +## 17. P5 落地形状:lockstep `inproc`、reply 与 apply-thread server + +### 17.1 verb barrier 与诚实的同地址空间传输 + +P5 的 `inproc` 是真第二线程,但还是 **lockstep**:每条 class-B verb 发射后,client 的 `EmitAndWait` 等到 `appliedSeq == emitSeq`;`MOBILEGL_IPC_VERB_BARRIER=1` 默认开启。理由不是吞吐,而是 27 个 BARRIER-PULLED 字段尚无记录载体;在这些字段退役前让两线程同时跑,会让 server 读到 client 的“未来值”(R-1,ID-4)。barrier 是逐族可退役对象,不是 P6 transport 的要求。 + +同一地址空间不得成为旁路(R-2):encoder 把 `MGHostSpan::Ptr` 恒写成 `nullptr`,内容 blob 必须带真实 `SEG_STAGE` / 段内 offset / 非零 size;decoder 对四种形状分别 `Fatal{ProtocolCorruption}`——非空 `Ptr`、内容记录 size 为 0、非零 size 却无 segment、`offset+size` 越界。`MapPersistent` 在 split 恒 decline;`MOBILEGL_IPC_AUDIT=1` 在 retire 后把 staging 填 `0xDD`,让跨 applier 返回持针的实现下一次读取时可见地失败。四个 Fatal 与 audit 使 `inproc` 和未来 spawn 表达同一份所有权。 + +reply mailbox 以**记录序号作为 slot id**(R-3):slot header 是 `{Seq, Status, Size}`,acceptance 的四个 Bool 答案与 `MapPersistent` decline 都在既有 verb wait 内读取,不增加第二次等待。`Status=ERROR` 一律 `Fatal{ReplyError}`,不能退化成 false;读到 reply 之前先由 `appliedSeq` 证明该记录已离开 applier(R-5)。P9 才把这套同步 mailbox 推广成异步池。 + +### 17.2 71 槽的路由、caps 与 tight readback + +client 表只有三类(`CONTRACT-P5.md` §7):2 个 getter 从 caps mirror 本地回答;5 个 P5 verb 发射;其余 64 个 `Fatal{UnmigratedVerb}`。实现不是手写 switch:**33 条**走 generated route tables,**4 条 escape** 保留专用 reply/资源语义,`PipeCatalogueTest` 同时钉住 **37**。R-17 的第一轮只替换 `MGPipeApply(` 调用表达式,漏掉了五个 `&MGPipeApply` 地址获取点,四条 delete/resource row 于是仍在 GL thread 同步执行;落地门因此既扫调用也扫 address-of。教训:生成路由的完备性必须覆盖值调用与函数地址,两者不是同一张 grep 表(ID-58/61)。 + +能力存活只读 `CapsMirror` 中的 `MGPCaps::CallMask`;server 的 consumer mask 由 backend 类型显式设置并以实际 op table 校验,**永不**从进程级 `MGPipeGetResourceOps()` 推导。41 个旧 slot-null probe 也改读对应 cap,否则 non-null emit table 会把所有 fallback 错判成“支持”(R-8)。成功的 `MakeEGLCurrent` / `InitCapabilities` 重新发布 snapshot;client 按 generation 采纳(R-12)。 + +`ReadPixels` 在线上恒为 **tight**:server 临时设 neutral pack(row length / skips = 0,alignment = 1),只向 reply 写 `width*height*bytesPerPixel`;client 用自己持有的 pack state scatter,row gap 保持原字节(ID-49)。绑定 PACK PBO 时 P5 在 client 侧、发射和解引用之前 `Fatal{UnmigratedVerb, "ReadPixels+PACK_BUFFER"}`;server 直接写 buffer resource 并 `MarkGpuWritten` 是 P6+ 的真实形状(ID-57)。reply 单槽 payload 上限是 `2 MiB - 16`,更大 readback 的 carrier 同样留给 P6+。 + +### 17.3 server 角色、shadow 与退出 + +`ServerLoop` 的 `mgl-srv-apply` 是 native context 的终身 owner。`ServerMakeEGLCurrent` 对 `(dpy, draw, read, ctx)` tuple **只 bind 一次**;相同 tuple 是 no-op,client release 只记账、绝不让 apply thread native-unbind,context 直到 destroy / context loss 才离开该线程(ID-54)。罕见 EGL 操作经 caller-serialised one-slot control mailbox 进 apply thread;十二个 `Server*` forwarder 是唯一缝,其中 make-current 与 init-caps 成功时执行 R-12 republish。mailbox 在 stop 判定与 publish 上共用 mutex;有 backend 却无 thread 时 `Fatal{ApplyThreadNotRunning}`,不允许回落到 app thread。 + +`PipeApplier::ApplyOne` 依次 stamp verb、decode/apply、清 stamp;只有 `SessionConsumer::ApplyOne` 每条记录把 `appliedSeq` 加一,pad 不计数。五条没有 `MGPipeApply*` 的 class-B verb 由 `ServerVerbSink` 消费。`StagedShadowStore` 按 resource twin 复制并合并**精确覆盖范围**;`Ops_H_SubData` / flush / respecify 只把 server-owned copy 交给后端,任何 widened read 先过 `RequireStagedCoverageForPendingRanges`,否则 `Fatal{StageSnapshotTooNarrow}`(R-11)。 + +退出顺序承重:`ShutdownSplitRoles` 先让 client publish 并 bounded-drain,再 shutdown transport / 唤醒 wait,随后 bounded-join apply thread;线程在仍持 context 时 detach decoder、销毁 private backend,最后才销毁 emitter 与 transport。这样 stats dump、`pActiveBackendObject.reset()` 与 ring unmap 都发生在 thread 退出之后。 + +### 17.4 lane 隔离、G5 与阶段边界 + +每条 `DirectGLES.Split.*` entry 有独立 `MOBILEGL_LOG_FILE_PATH`;E1/E3 控制从被选 entry 的私有文件取 Fatal,不能从并行 lane 的公共日志“借红”(ID-53)。G5 的第十一行 `FlushPendingRangesFrom` 因 P5 的 range 参数化被承认,但两份 untouched-region script 都**始终**对固定 pin `172b0222…` 比较;re-pin 必须同时改两份脚本,不能让 ref-a/ref-b 漂移掩掉改变(ID-41)。 + +P5 到此只声称 reduced path:private lane、barrier、reply、shadow 与 context ownership 已落地;class-C verb、27 个 readback/query wrong-answer、`rsp` residual inputs、PACK-PBO 真 carrier、>2 MiB readback、GetCaps 的两个 server→client blob carrier 与 ABI fingerprint 的 segment-size 混入都属于 P6+ / P7 / P3b-P4b,逐项见 `ROADMAP.md`,不由 `inproc` 同地址空间替它们背书。 + ## 附 A:开关 CMake: @@ -601,7 +635,7 @@ CMake: |---|---|---| | `MOBILEGL_BUILD_DISAGGREGATED` | OFF | 已落地 | | `MOBILEGL_BUILD_SERVER_SPIKE` | OFF(仅 Android) | 已落地(spike A,非出货) | -| `MOBILEGL_BUILD_DISAGGREGATED_INPROC` | OFF | 计划(P5) | +| `MOBILEGL_BUILD_DISAGGREGATED_INPROC` | OFF | 已落地(P5) | | `MOBILEGL_PIPE_VERIFY` | OFF | 计划(P1;构建期开关,编译进 `SnapshotFromGLContext()` 与 G4 比对器,P13 后保留) | | `MOBILEGL_PIPE_LEGACY_MEMOS` | ON(P2..P13) | 已落地(P2;`CMakeLists.txt:36`,OFF 时不编译 pre-handle 臂;`MOBILEGL_PIPE_PUSH=OFF` 会把它强制回 ON 并 `message(STATUS)`,`CMakeLists.txt:476-479`,因为 pull 构建里 pre-handle 臂就是唯一的实现) | | `MOBILEGL_FLATC_EXECUTABLE` | 空 | 已落地(只服务 `flatc-check`) | @@ -621,7 +655,7 @@ CMake: | `MOBILEGL_PIPE_STATS_PERIOD` | 120(1–10⁶) | 每多少帧一条汇总行 | | `MOBILEGL_PIPE_STATS_FILE` | 空 | teardown 时的 JSON 转储路径 | -运行时,传输与 IPC(计划,P5+):`MOBILEGL_TRANSPORT`(monolith)、`MOBILEGL_IPC_SERVER_PATH`、`MOBILEGL_IPC_RING_MB`(8)、`MOBILEGL_IPC_STAGE_MB`(32)、`MOBILEGL_IPC_PRESENT_CREDIT`(1)、`MOBILEGL_IPC_SPIN_US`(50)、`MOBILEGL_IPC_POLL_ESCALATE`(64)、`MOBILEGL_IPC_PERSISTENT_BLOCK_KB`(64)、`MOBILEGL_IPC_ADOPT_TIER`(auto)、`MOBILEGL_IPC_SHADOW_SHM`(1,Phase 2 起)、`MOBILEGL_IPC_INLINE_PAYLOADS`(0,负面对照)、`MOBILEGL_IPC_SERVER_AFFINITY`(auto)、`MOBILEGL_IPC_STRICT_ERRORS`(0)、`MOBILEGL_IPC_AUDIT`(0)、`MOBILEGL_IPC_TRACE`(0)、`MOBILEGL_IPC_ATTACH`、`MOBILEGL_IPC_RESPAWN`(0)、`MOBILEGL_IPC_IDLE_EXIT_S`(30)。显式不设立:`MOBILEGL_IPC_PROGRAM`(没有 relink 档)、`MOBILEGL_IPC_VALIDATE_SERVER`(server 没有 `MG_Impl` 校验器)。既有负面对照开关(`MOBILEGL_ESPRYT_DISABLE_{UBO,UNPACK,UPLOAD}_RING`、`_INVALIDATE_FLUSH`、`MOBILEGL_DISABLE_LARGE_BUFFER_ADOPTION`、`MOBILEGL_COHERENT_AS_FLUSH`)全部保留。 +运行时,传输与 IPC(P5 已落地的核心项):`MOBILEGL_TRANSPORT`(monolith)、`MOBILEGL_IPC_VERB_BARRIER`(1)、`MOBILEGL_IPC_RING_MB`(8)、`MOBILEGL_IPC_STAGE_MB`(32)、`MOBILEGL_IPC_PERSISTENT_BLOCK_KB`(64)、`MOBILEGL_IPC_ADOPT_TIER`(2)、`MOBILEGL_IPC_STRICT_ERRORS`(0)、`MOBILEGL_IPC_AUDIT`(0)、`MOBILEGL_IPC_SERVER_AFFINITY`(auto)。`MOBILEGL_IPC_SERVER_PATH`、present credit、poll escalation、shadow shm、attach/respawn/idle-exit 等随 P6+ 生效。显式不设立:`MOBILEGL_IPC_PROGRAM`(没有 relink 档)、`MOBILEGL_IPC_VALIDATE_SERVER`(server 没有 `MG_Impl` 校验器)。既有负面对照开关全部保留。 ## 附 B:边界计数器(`MobileGL/MG_Util/Metrics/PipeStats.h:46-122`,P0 已落地) diff --git a/docs/Disaggregated/MEASUREMENTS.md b/docs/Disaggregated/MEASUREMENTS.md index a69a02cf..ab09a2ea 100644 --- a/docs/Disaggregated/MEASUREMENTS.md +++ b/docs/Disaggregated/MEASUREMENTS.md @@ -632,3 +632,106 @@ P4a 的契约改了七次(`c0b`…`c0g`),外加一轮缝类审计与一轮 `mc_vanilla_draw` 上:**espryt T1(`0x1fff` − pull)= +1076.1 ns/draw,T2(`0x1ff` − pull)= +1064.7,T1 − T2 = +11.4**;magma T1 = +629.0、T2 = +452.6、T1 − T2 = +176.4。blend toggle:espryt +964.5 / magma +738.9 ns per toggle pair;pass switch:espryt +8766.9、magma −4555.7(后者符号为负,属该项的噪声量级)。 **桌面这台机上,"P4a 自己"落在本 bench 的噪声底以下,所以不要单独引用它。** 同一份脚本在 `6035c9d7`(终审修复前)上跑出的是 espryt T1 +1269.7 / T2 +1135.8 / **T1 − T2 = +133.9**,本轮是 +1076.1 / +1064.7 / **+11.4**——**两臂的绝对值在两轮之间各自漂了 ~200 ns,而它们的差只有 10–130 ns**,也就是说这个 bench 分辨不出 P4a 这一档的增量。真正可引用的是:(1) **T1 ≈ +1.1 µs/draw 的总边界**(对 pull 基线,Espryt;这条在两轮之间是稳的);(2) **设备侧的三臂表(§24)**——那里 P4a 自己是 +3.4 ~ +5.7 个百分点、0.05–0.38 ms/帧,样本全部在验证过的定频窗口里。**Magma 的 T1 − T2 = +176.4 ns 不是"Magma 在跑 P4a"**:c0f 的消费者门让它一条 P4a 记录都不发(设备侧 §24 的 Magma 两臂差也在噪声内),这 176 ns 是 tracker 多算的那几个快门加噪声。 + +--- + +## 26. P5 五部分门(joint `e61d0012`)与 landed quick gate(`eec0e836`) + +P5 的完整记录来自 `~/w7/notes/p5/p5-results/joint-v1.md` §2 与 ID-63;脚本按 **1 → 5 → 3 → 2 → 4** 的顺序跑。`e61d0012` 是 joint scratch head,随后同一组 landed 代码进入 `feat/disaggregated@eec0e836`;landing 后按 ID-66 只跑 quick gate,不把 quick gate 写成第二次 full gate。 + +| 部分 | joint 实测 | +|---|---| +| **1 接口纯度** | include closure **4 probes / 0 problems**;pull `MG_Remote` symbol **0**、split **610**;G1 `.text` **10806611 → 10806611**、defined symbols **27814 → 27814**、**0 added / 0 removed / 0 resized / 0 renamed**;P3a/P4a G5 对 `ff2994d9` byte-identical | +| **5 coverage / generator** | `gen_pipe` up-to-date、self-test **9/9**;dirty-surface **27/27**(`UseProgram` 一项明确为 UNDECIDED,不伪装成 absence proof);field ownership up-to-date、**15/15**;emitter/CSO **185/185**;verify controls **4/4** | +| **3 行为 A/B** | G2 name diff **0**;G14 **0 removed / 42 added**;unit **1816/1816 ×3**、split **2038/2038**;Wire **58/58**;integration-gpu pull **1128/1128**、push **1128/1128**、split-monolith **1149/1149**;split-inproc **426 pass / 185 skip / 511 abort / 27 fail / 0 segfault**;`integration-split` **21/21(19 run、2 design-skip)**;persistent arm **2/2**,split `pmap=2160.00, mpr=1`,push `pmap=0.00, mpr=1` | +| **2 语义 / retrace** | integration-verify **930/930**、`Fatal{` **0**;OpenRA inproc **2/2**、两后端 SSIM **1.0**、`Fatal{` **0**;push retrace **79/79**;verify retrace **79/79**、armed **79/79**、`Fatal{` **0** | +| **4 设备** | **未跑**;joint package 明确禁止 adb。设备 A/B 留在 §30,不用 desktop 数代填 | + +landed quick gate(ID-66):split unit **2038/2038**;`integration-split` inproc **22/22**(比 joint 多 `SplitLogPaths`);push integration-gpu **1128/1128**;G1 **0/0/0/0、`.text +0`**。这组数证明 landing 没丢 joint 的 reduced path,不声称重跑了 Part 2/4。 + +## 27. P5 退出门 E1–E6:门、阴性控制与处置 + +| 门 | 实测与“为什么会红” | +|---|---| +| **E1 barrier** | default reduced path 三次均 **19 pass / 2 skip / 0 fail**;`MOBILEGL_IPC_VERB_BARRIER=0` 选中的 **14/14** 全 abort,且每个 private lane file 都有自己的 `Fatal{BarrierViolation, ""}`(ID-53/65,`joint-v1.md` §3) | +| **E2 OpenRA** | baseline **2/2、SSIM 1.0**;pull library + `inproc` 的 transport control 会红。`MOBILEGL_IPC_E2_DROP_CLEAR=1` 已武装但仍 **1/1、SSIM 1.0、mismatchPixels=0**,所以 clear-drop 控制**没有**因自己的理由变红,pending x2;不能据此反推 wire bypass(ID-65) | +| **E3 persistent map** | (a) block KB=0:**6 selected / 4 red / 2 skip**,红在第二次无 GL 宣告写的 pixel assertion;(b) 该无宣告写在场景内;ID-42 的 emulated membership 经 green → `return false` **3 red** → restored green,已 VERIFIED;(c) counting lane `pmap=2160.00 / mpr=1`,push `0.00 / 1`;(d) split apply 恒 decline,不返回 host pointer;(e) SmallRing entry 跑过,但无 wrap/wait counter,pending x2(ID-42/63/65) | +| **E4 field ownership** | generator **15/15** own-message controls;strict lane **19 abort / 2 skip / 0 pass**,首条具名 `Fatal{UnmigratedPipeInput, "GetTextureContextId@Clear"}`。这是 BARRIER-PULLED 债务的响亮读法,不是 default regression(ID-65) | +| **E5 honest inproc** | `MOBILEGL_IPC_AUDIT=1`:**19 pass / 2 skip / 0 fail**;production SubData copy 与 `0xDD` poison 两条 shipped test 通过。五条机制已在代码门上覆盖,但“corrupt staged upload 再 draw”这个 R-16 半边未执行,**pending v1 round 3**,不把 audit-green 扩写成它已自证(ID-65) | +| **E6 phase gate** | G1 **0/0/0/0**、G2 name diff **0**、G14 **0 removed / 42 added**;verify **930/930**;push/verify retrace **79/79**。broad inproc census是 **426/185/511/27/0**,其 attribution 如下;reduced path 与记录债务分开结算(ID-63/65) | + +E6 的 27 个普通失败逐条复跑均 `rc=1`、private log **0 Fatal**;8 个争议项又各跑三次,无 flake。归属不是从测试名猜的,而是 `joint-v1.md` §4 的 probe: + +| family | 条 | 记录去向 | +|---|---:|---| +| `LayeredAttachmentShapeScenario` | 14 | P4b/P7 layered texture readback | +| packed depth/stencil `GetTexImage` | 3 | P4b/P7 texture-shadow readback | +| framebuffer `HandleRecycleScenario` | 5 | P4b/P7 readback;不是已证明的 handle recycle bug | +| `PrimitivesGeneratedNoXfbScenario` | 3 | P7 query / primitive accounting | +| `TextureParamsWithoutASamplerView` | 1 | P6 shared-backend inspection forwarder | +| `P4aFinalFixScenario` FBO/RBO delete | 1 | P6 shared-backend lifetime after transported delete | + +511 个 abort 只**计数**,没有逐条诊断;抽样两条分别是 `DrawElements` 与 `BeginTransformFeedback` 的 class-C `Fatal{UnmigratedVerb}`。因此“511 全是 class C”不作为测量结论(`joint-v1.md` §4)。 + +## 28. R-10、逐帧 ledger 与内存口径 + +`joint-v1.md` §5 在 `MOBILEGL_PIPE_STATS_PERIOD=1` 下只取真实非零窗口: + +| entry | frame/window | `pmap` bytes/frame | `mpr` | `rsp` | +|---|---|---:|---:|---:| +| Triangle redraw | 1/1 | 0.00 | 0 | 35 | +| Triangle redraw | 2/1 | 0.00 | 0 | 35 | +| Persistent map write-after-frame | 1/1 | 600.00 | 1 | 35 | +| Persistent map write-after-frame | 2/1 | 600.00 | 0 | 35 | + +| entry | server accept peak RSS | client handshake peak RSS | client teardown peak RSS | +|---|---:|---:|---:| +| Triangle | 11,526,144 B | 11,706,368 B | 141,451,264 B | +| Persistent map | 11,464,704 B | 11,649,024 B | 141,422,592 B | + +`inproc` 两角色共享一个进程,所以上表是**进程**峰值,不是两个独立 physical peak;server 只在 accept 时取样,不能声称 full-run server peak,也没有 N-frame RSS slope。日志另报 `roleMapped=58,990,592 B`、`allRolesMapped=117,981,184 B`。规范 ledger 由执行过的 `ProtocolSmokeTest` 钉住:**SEG_CMD 8 MiB / SEG_STAGE 32 MiB / SEG_REPLY 16 MiB / SEG_EVENT 256 KiB**(ID-47/65)。 + +**max record bytes:没有场景测量,pending x2。** encoder 有 `MaxRecordBytesSeen`,但 scenario telemetry 未发布;单元只证明它远小于 half-ring,不提供这两个场景的数。这里不从 struct size 或 ring capacity 反推(`joint-v1.md` §5)。 + +## 29. 复审作为过程测量:跨族发现、轮次与身份修复 + +wave-1 跨模型族复审的十项全部经独立 perturbation **CONFIRMED(10/10,0 refuted,0 partial)**;它量到的不是“代码行多”,而是十个本轮同族门没有捕获的具体缝(ID-46/48,`wave1-codex-review.md` + `wave1-codex-verify.md`): + +| # | confirmed finding | +|---:|---| +| 1 | empty `SEG_STAGE` wrap arithmetic 拒绝本可容纳的 blob | +| 2 | 旧 8-slot reply geometry 连 512×512 RGBA8 都差 16 B | +| 3 | blob validation 接受任意 mapped segment,audit poison 覆盖不全 | +| 4 | wire `MapPersistent` decline 绕过 adoption-tier refusal | +| 5 | pad-bit control 经 `Reserve` 后根本没有发送该 bit | +| 6 | ABI sensitivity gate 驱动的是 production 已不调用的 helper | +| 7 | null-union control 没到达 handshake guard | +| 8 | 三个 CI negative control 接受任意非零退出 | +| 9 | `gen_pipe.py expect_trip` 接受任意 `SystemExit` | +| 10 | 两个 death control 使用空 diagnostic regex | + +后续 v1/c1 review 的数量本身也入账,作为门质量的过程数据: + +| 决策 | review 读数 | 处置 | +|---|---|---| +| ID-52(v1 两审合并) | cross-family **2 blockers + 8 majors + 1 minor**;same-family 另给 M-1..M-7、m-1..m-7,五个 codex-only 项先执行验证 | confirmed set 进入 v1 round 2 | +| ID-58(c1 round 2) | **3 blockers / 8 majors / 6 minors** | round 3;若仍有 blocker 才拆包 | +| ID-62(v1 round-2 review) | **7 closed / 7 partial / 1 not closed**,新增 **5 majors** | round 3;E1 skip-false-green 转 j0 | +| ID-64(c1 round-3 review) | **8 closed / 6 partial / 1 not closed**,新增 **0 blockers / 5 majors / 4 minors** | 判 MERGEABLE;剩余 gate debt 转 c1f | + +这些数字解释了流程为何在 P5 尾声改变:用户要求不再在轮次中做过度验证;以后 package 自门 + quick gate 落地,每阶段末只做一次 Codex adversarial review,发现进入下一阶段首轮(ID-66)。 + +另一个过程事实是 identity rewrite(ID-40):Sep 8 以后 WSL repo-local `rereview ` 污染了 **81** 个被 Windows 重写的提交与 **58** 个 wave-1 提交;本地谱系经 parent/env rewrite 接到 GitHub `ff2994d9`,验证 **59** 个重写提交全部是 `Swung0x48 `,tree/patch 等价。自此每阶段首个 commit 前必须先跑 `git var GIT_COMMITTER_IDENT`。这是过程修复,不计成 P5 实现产量。 + +## 30. P5 four-arm A/B on the Redmi(pending `ab-v1.md`) + +本节只冻结协议,不填数字。设备是项目指定 Redmi;四臂用同一 native code 对照: + +| arm | APK / env | 回答的问题 | +|---|---|---| +| pull | pull APK | 既有 pull 基线 | +| push | push APK | monolith push 边界 | +| split-inproc | split APK + `MOBILEGL_TRANSPORT=inproc` | 第二 apply 线程的 barrier / codec / copy 总成本 | +| split-no-env control | 同一 split APK,不设 transport env | Gradle flavour 本身是否改变 monolith;应与 push 对照 | + +沿用设备协议:reboot-clean、同一风扇档与已验证定频、四臂交错配对;每个 trace 记录尾 200 帧 p50/p99、逐线程 CPU、`pmap/mpr/rsp`、进程 `VmHWM`,并保留 pin check 与 private log。A/B 后台运行、不阻塞 landing(ID-66);在 `~/w7/notes/p5/p5-results/ab-v1.md` 出现前,本节保持 **pending**,不从 desktop gate 或旧 APK 推算。 diff --git a/docs/Disaggregated/README.md b/docs/Disaggregated/README.md index 626de969..3d7f2da4 100644 --- a/docs/Disaggregated/README.md +++ b/docs/Disaggregated/README.md @@ -1,6 +1,6 @@ # MGPipe:MobileGL 前后端拆分 -> 状态:**P0、P0.5、P1、P2、P3a、P4a 已落地**(`feat/disaggregated@8c458cd5`,基线 `dev@9eae9858`)。第 43 天 GO/NO-GO 判定为**继续**。P4a(handle wave 2:Espryt 的 FBO / 纹理 / sampler / program 身份与描述符)已交付,Espryt 的对象类读点至此全部走句柄;**下一步 P3b/P4b**(深化:memo 重键、发射游标、raw-depth-fetch sampler 原生化)与 **P5**(传输 + inproc applier,也只依赖 P4a)。见 `ROADMAP.md`。 +> 状态:**P0、P0.5、P1、P2、P3a、P4a、P5 已落地**(`feat/disaggregated@eec0e836`)。P5 已在完整门上跑出缩减路径首个 IPC 帧:`Clear` / `DrawArrays` / `ReadPixels` / `BlitFramebuffer` / `Present` 经同一套 wire codec 到同进程第二个 apply 线程,OpenRA 在 `inproc` 下两后端 2/2、SSIM 1.0。其余 64 个 class-C 槽仍具名 `Fatal{UnmigratedVerb}`;下一阶段先按 census 迁移它们,把真实负载完整搬上 apply 线程,再做 spawn。见 `ROADMAP.md`。 > > 性能纪律(2026-09-08 起):逐线程 CPU 与 tracker 绝对 ns **对着 pull 臂基线记录**,不再作阻塞门(push 比 pull 多约 10% 逐线程 CPU 已被接受;该读数出自 -O0 APK,Release 基准线见 `MEASUREMENTS.md` §20),专门的优化阶段排在路线图推完之后。 @@ -25,6 +25,27 @@ MGPipe 是 MobileGL 前端(`MG_State` + `MG_Impl`)与后端(`MG_Backend` 三种构建/运行形态共用**同一份 backend 实现**:`monolith`(默认,接口在进程内直调)、`inproc`(同进程两个线程,CI 形态与渲染线程交付物)、`spawn`(`fork`+`execve` 出 server 进程,SPSC 共享内存 ring + FlatBuffers 控制面)。 +## 运行 P5 split lane + +本地门使用四个 flavour:`build-linux`(pull)、`build-push`(monolith push)、`build-verify`(影子比对)与 `build-split`(disaggregated + inproc)。split 运行时显式设置: + +```text +MOBILEGL_TRANSPORT=inproc ctest --test-dir build-split -L integration-split --output-on-failure +MOBILEGL_TRANSPORT=inproc ctest --test-dir build-split -L integration-gpu --output-on-failure +``` + +缩减路径的具名入口是 `DirectGLES.Split.*`;每条 entry 都带独立的 `MOBILEGL_LOG_FILE_PATH`,日志不共享,阴性控制也只读被选 entry 的私有文件。常用运行时旋钮: + +| 变量 | 默认 | 用途 | +|---|---:|---| +| `MOBILEGL_IPC_VERB_BARRIER` | `1` | 每个 verb 等 `appliedSeq == emitSeq`;`0` 只作 E1 阴性控制 | +| `MOBILEGL_IPC_AUDIT` | `0` | apply 返回后以 `0xDD` 填退休 staging,查跨返回持针 | +| `MOBILEGL_IPC_STRICT_ERRORS` | `0` | 把 BARRIER-PULLED residual input 提升为具名 Fatal | +| `MOBILEGL_IPC_ADOPT_TIER` | `2` | P5 split 使用 emulated persistent-map 路径;接受 `auto/0/1/2` | +| `MOBILEGL_IPC_PERSISTENT_BLOCK_KB` | `64` | persistent-map 保守块推送粒度;`0` 是 E3(a) 阴性控制 | + +Android 有三份 APK flavour:pull、push 与 split-inproc;构建映射分别由 Gradle properties `mobilegl.pipePush`、`mobilegl.buildDisaggregated`、`mobilegl.buildDisaggregatedInproc` 驱动。split APK 仍需运行环境 `MOBILEGL_TRANSPORT=inproc`;不设置时是 split build 的 monolith control arm。 + ## 文件地图 | 文件 | 内容 | diff --git a/docs/Disaggregated/ROADMAP.md b/docs/Disaggregated/ROADMAP.md index 95b86d79..8fda5655 100644 --- a/docs/Disaggregated/ROADMAP.md +++ b/docs/Disaggregated/ROADMAP.md @@ -1,12 +1,12 @@ # MGPipe 路线图 -> 状态:P0、P0.5、P1、P2、P3a 已落地(`feat/disaggregated@fde5fda3`)。第 43 天 GO/NO-GO 判定为**继续**,下一步 P4a。设计见 `ARCHITECTURE.md`,实测见 `MEASUREMENTS.md`。天数是各阶段所含子系统行的求和(低端 / 高端),总计 **267–337 人天**(不含 CTS 周转);两个工程师、P7 与 P5/P6/P8 并行约 7–9 个月,真正的约束是两台设备的争用。 +> 状态:**P0、P0.5、P1、P2、P3a、P4a、P5 已落地**(`feat/disaggregated@eec0e836`)。P5 的完整五部分门已经观察到“缩减路径首个 IPC 帧”:OpenRA `inproc` 两后端 2/2、SSIM 1.0(ID-63)。下一阶段先在 `inproc` 下按 class-C 普查迁移 verb,把真实负载完整搬上 apply 线程;spawn transport 后移。设计见 `ARCHITECTURE.md`,实测见 `MEASUREMENTS.md`。 ## 通用纪律(每个 commit) 默认 ALL target 必须完整构建;禁止提交热路径插桩(CI grep 门);**每个门必须能因它存在的理由变红**;Windows 机器不是正确性门;设备对比走 reboot-clean + 同热窗口配对 A/B,CPU 定频按项目协议;每阶段出口跑一次五部分门;每阶段性能判据是**逐线程 CPU 时间**;**拆分不借机顺手修 `dev` 的 bug**——句柄化过程中发现的 `dev` 侧缺陷记录成独立条目、独立 PR,不混进本分支(下文课题 15、17 各是一例)。 -两条跑道:**monolith 跑道** P0 → P0.5 → P1 → P2 → P3a → P4a → P3b/P4b → P7 → P8 → P13,每段可独立交付、可随时中止且 monolith 严格好于起点;**IPC 跑道** P5 → P6 → P9 → P10 → P11 → P12。 +两条跑道仍然分开:**monolith 跑道** P0 → P0.5 → P1 → P2 → P3a → P4a → P3b/P4b → P7 → P8 → P13,每段可独立交付、可随时中止;**IPC 跑道** P5 → `inproc` verb migration → P6 → P9 → P10 → P11 → P12。这里只调整 IPC 跑道内部的先后,不把 spawn 混进 monolith 深化。 ## 阶段 @@ -18,8 +18,9 @@ | **P2** 渲染状态 CSO + 第一片 Track H + 残余值块 | 18–26 | ✅(`738b289d`)`MG_Impl/Pipe/Tracker`(dirty 位、5 个聚合世代、抑制器骨架);`gen_pipe_dirty_surface.py` 首轮映射成门(73 个 mutator 全映射,45 条 render-state 答案 + 8 条 (mutator, bit) 答案逐字段导出,0 COARSE / 0 UNDECIDED,`.github/workflows/test.yml:1601-1604`);`MGPipeRenderStateSpans` chunk 表 **7 个 pipeline chunk / 396 B + 8 个 dynamic chunk / 772 B = 1168**(`MobileGL/MG_Pipe/MGPipeRenderStateSpans.h:190-191`)+ G7 setter 一致性测试;`CsoCache`(**64 项**,键 = pipeline 子集,`MobileGL/MG_Impl/Pipe/CsoCache.h:53`);`create/bind_render_state` + `set_dynamic_state`(Espryt `SyncRenderState` 一行不动;Magma `ComputePipelineStateHash`/`GetOrCreatePipeline`/`ApplyDynamicDrawStateTail` 改从 CSO 与动态 payload 取);`set_pixel_pack_state`、`set_patch_state`、`set_vertex_attrib_defaults`;`set_residual_value_state` + `ResidualValueBlock` 绊线(**棘轮 1248 → 8**,`MobileGL/MG_Pipe/MGPipeTypes.h:546`);**第一片 Track H**:Espryt 0b(`SlotAllocator` + 6 个 registry → slot 数组 + 删 `TwinLookupMemo`×3/`OwnerEquals`/`g_fbSlotCache`/GC)与 Magma 子系统 4(`VertexInputStateFactory`/`VaoDrawMemo` 重键,删前端 VAO 里的后端裸指针)——共 **11 条身份 memo 直接删除 + 2 条重键**(`ARCHITECTURE.md` §9.5 的普查),pre-handle 臂在 `MOBILEGL_PIPE_LEGACY_MEMOS` 下并存到 P13;`MOBILEGL_PIPE_LEGACY_MEMOS`(`CMakeLists.txt:36`);补**三个** capability 存储 `FramebufferSrgb`/`DepthClamp`/`TextureCubeMapSeamless`(`MobileGL/MG_Pipe/MGPipeValueTypes.h:303-305`) | ✅ 集成 × 2 后端 × {pull, push} 逐名相同(名差 0);pull 构建符号 0 增 / 0 删 / 0 重命名、四个已认定 resize、`.text` +160 B;`RenderStateImpl` sha 不变;单元 1566 × {pull, push, verify};`integration-gpu` 916/916(pull、push、`MOBILEGL_PIPE_PUSH=0` 三臂);79 例 retrace push 下 79/79、verify 下 79/79 零分歧;`integration-verify` 828 条零 `Fatal{`;verify 构建的三组对照(`PoisonOmitted`、`VerifyCorrupted`、`HandleRecycle`)44/44,且 ABA 对照关掉句柄身份即红;G7 负面对照按设计变红并点名 `SetColorMask`;`CsoContentAddressing` 6/6;测试名 0 删除 / +119。设备(小米 Adreno 830,配对、定频、尾 200 帧 p50):push 比 pull 多约 10% 逐线程 CPU——**用户 2026-09-08 决定接受**(**勘误**:这批 APK 是 -O0 构建,见 `MEASUREMENTS.md` §10 勘误;Release 基准线以 §20 的 P3a 表为准),性能自此对 pull 基线只记录不设门;两机各 16 行在案(小米 p50 +8–14%,Oppo Espryt +8–18% / Magma +10–11%);桌面 `DriverBench` T1 = +322 / +345 ns/draw(Espryt / Magma),T1 − T2 = −228 / −354(P2 比它替掉的 P1 残余填充便宜),blend-toggle +4.7% / +3.5%,见 `MEASUREMENTS.md` §9–§14 | P1 | | **P3a** handle wave 1(Espryt):buffer、VAO | 18–23 | ✅(`fde5fda3`,44 个提交,`git diff --stat 5cb826b0 3e298c9a` = 37 文件 / +9273 / −230)**九条 `resource_*` 调用**接线:`ResourceCreate`、`ResourceRespecify`、`ResourceSubData`、`BufferSubDataResident`(`kOptional`,Magma 仍不注册)、`ResourceFlushRange`、`ResourceReadback`、`ResourceDestroy`、`MapPersistent`、`UnmapPersistent`——全部在 GL 调用时刻从 `BufferObject` 的十一处分发点发射(`ARCHITECTURE.md:157` 的唯一例外),落到新的句柄形后端表 `MGPipeResourceOps`(`MobileGL/MG_Pipe/PipeApply.h`,签名里没有任何 `MG_State` 类型);Espryt 的 `Ops_*` 行为逐条不变,只换了读输入的地方。**五条 vertex-input 调用**:`CreateVertexElements`/`BindVertexElements`/`DeleteVertexElements`(blob 两个视图都带,`MGPVertexAttribWire[]` + `MGPVertexBindingPointWire[]`,`ARCHITECTURE.md:132`)、`SetVertexBuffers`(`BaseInstance` 显式字段,取代 `g_pendingFetchBaseInstance` 那个 ambient 全局)、`SetIndexBuffer`。**第七张 Espryt slot 表**:`GLESBufferResource` 搬出 `PipeResource::m_backend`,进 `BackendSlotTable`(`MobileGL/MG_Backend/DirectGLES/Managers.h:691-692`)——第一张按 client 铸造、随调用过线的句柄索引的表。`kNeedsAck` 首次落到目录里(`ResourceRespecify`,逐记录谓词 `MGPipeResourceRespecifyNeedsAck(desc) == (desc.Immutable != 0)`,`MobileGL/MG_Pipe/MGPipeTypes.h:680`)。pool、延迟释放与三条 ring 原样搬(G5 十个函数逐字节相同);Adreno 禁用属性 SIGSEGV workaround 保留。**memo 普查(`ARCHITECTURE.md` §9.5)**:句柄臂上**退役 6 条身份 memo 成员**——VAO twin 的 `m_hasSyncedConfigVersion` / `m_syncedConfigVersion` / `m_syncedAttributeVersions`(`Array`,本阶段最大的一处)/ `m_syncedIndexBufferVersion` / `m_syncedIndexBufferObject`(裸前端指针),加 `ConvertedFloat64Stream::sourceLifetimeId`(即普查里 11 条直接删除的最后一条,`ConvertedVertexStreamKey` 的 `sourcePin`)。六条都**仍在 `MOBILEGL_PIPE_LEGACY_MEMOS` 下编译**(`Managers.h:1109-1130`、`:1067-1075`),pull 构建强制该开关 ON,所以 `sizeof` 一处不动——G1 的 0/0/0/0 就是这条的度量,真正的删除随 pull 路径在 P13 发生;**4 条重键** —— twin 的同步门(config version + 32 组逐属性版本 → `{elementsHandle, elementsSerial}` + `VertexBuffersSerial`)、twin 的索引槽 memo(回绕 `Uint16` + 裸 `BufferObject*` → 一个 `Uint64 m_syncedIndexSerial`)、`ResolvedDrawBuffers`(`configVersion` → `{elementsHandle, elementsSerial, buffersSerial}`,`Entry` 与 `iboFrontend` 各加 `MGPipeHandle`)、`ConvertedFloat64Stream`(前端 lifetime id + change serial → buffer `{slot, gen}` + applier `Serial`),另 `GLESBufferResource::syncedChangeSerial` 从镜像 `BufferObject::GetChangeSerial()` 改为镜像 applier 的 `Serial`;**保留不删** —— `g_pendingFetchBaseInstance` / `SetPendingFetchBaseInstance` / `GetPendingFetchBaseInstance` / `ScopedFetchBaseInstance` 与它的三个 scope 仍在 `MOBILEGL_PIPE_LEGACY_MEMOS` 下编译(`Managers.h:1189-1200`、`DirectGLES.cpp:5293`),因为真删会从 pull 构建移走两个符号 = 直接的 G1 破坏,计划里"grep 为空"那条在 P3a **不可达**;`PipeResource::m_backend` / `SetBackendResource` / `ReleaseBackend` / `BackendBufferResource` 同理(D-K,`ARCHITECTURE.md:286`),push 下只是不再被写,随 pull 路径在 P13 退役。**`map-persistent-roundtrips`(`mpr`)**:定义为**每一次 `MapPersistent` 发射,铸成或拒绝都算**(`ARCHITECTURE.md:485`),所以 monolith 下非零、可断言。子系统位 7(resources)与位 8(vertex input),push 默认 `0x7f` → **`0x1ff`**(`kMGPipeSubsystemsMigratedAtP3a`) | ✅ 五部分门(本地 `~/w7/pipe`):**G1** pull 符号 0 增 / 0 删 / 0 重命名 / **0 resize**、`.text` 10806323 字节不变(P3a 的认定 resize 集为空);**G5** 十个 pool / 延迟释放 / ring / flush-drain 函数对 `44c2b5cf` **与** `5cb826b0` 都逐字节相同,self-test 两个阴性对照(`ClearBufferPool`、`FlushPendingRangesNow`)都按名变红;`RenderStateImpl` sha 仍不变;**G2** pull 与 push 逐名相同(名差 0);**G14** 测试名 0 删除 / **+58**;单元 **1619** × {pull, push, verify};`integration-gpu` **958/958** × {pull、push、`MOBILEGL_PIPE_PUSH=0`、`0x7f`(G12 的子系统关闭臂)、`MOBILEGL_ESPRYT_DISABLE_INVALIDATE_FLUSH=1`};buffer/VAO 族 **202/202**;`RenderStateSpans`/`Residual`/`VertexInputEmit`/`ResourceEmit` **48/48**;`HandleRecycle`(verify)**60/60**,含新的 buffer 用例三臂;`CsoContentAddressing` + `ResourceSubsystemControl` **10/10**,controls **64/64**;**G7** 两个阴性对照都按设计变红,vertex-input 那个**点名 `IsBgra`**;`integration-verify` **842/842**,零 `Fatal{`;`gen_pipe` 与 `gen_pipe_dirty_surface`(scan root 已扩到 `MG_State/GLState`,75 个 mutator 全映射、0 COARSE / 0 UNDECIDED)`--check` + `--self-test` 全绿。79 例 retrace:push 下 **79/79**,`MOBILEGL_PIPE_VERIFY=1` 下 **79/79 全部 armed、零分歧、零 `Fatal{`**;G3b 的具名五条都在这两次扫描里通过,**但单独那次具名扫描因门脚本把正则写成了逗号清单而选中 0 例**,且设备侧仍按开放问题 17 排除 `create-indirect`,所以 G3b 记为"桌面全绿、设备侧部分"——详见 `MEASUREMENTS.md` §16。**性能只记录不设门(用户 2026-09-08 规则 (a))**:MC 26.3 在 Adreno 上的 p99 对与两机 p50/p99 表见 `MEASUREMENTS.md` §20。**再基线检查点 1:实际日历 1 天(2026-09-08,四个包),远低于 27 天,不触发重定基线** | P2 | | **P4a** handle wave 2(Espryt):FBO / 纹理 / sampler / program 身份与描述符 | 26–34 | ✅(`8c458cd5`,101 个提交,`git diff --stat 37da3c3a 8c458cd5` = 86 文件 / +30066 / −384)**十四条族调用接线**:`set_framebuffer_state`、`create/delete_sampler_state`、`create/delete_sampler_view`、`set_texture_params`、`set_sampler_views`、`bind_sampler_states`、`set_shader_images`、`create/delete_shader_state`、`set_draw_program`/`set_dispatch_program`、`set_global_constants`;纹理与 renderbuffer 的 create/respecify/subdata/destroy **复用 P3a 的四条 `resource_*` 行**(D-D1),所以目录仍是 **71 条,P4a 一行未加**。**六种 kind 按 `{slot, gen}` 重键**:Texture、Renderbuffer、Framebuffer、SamplerCso、SamplerViewCso、ShaderCso——其中 sampler **state** 内容寻址(容量 256、引用计数防 LRU 驱逐掉仍被 `MGPTextureParams::BuiltinSampler` 指着的项),sampler **view** 按纹理对象身份寻址(D-F2),framebuffer 只有句柄没有 wire 生命期(D-I2)。子系统位 **9(framebuffer)/ 10(纹理资源)/ 11(sampler)/ 12(program)**,`MOBILEGL_PIPE_PUSH` 默认 `0x7f`→`0x1ff`→**`0x1fff`**。`CompositeResolver` 落地(program pipeline 解析成保留段里的一条 shader CSO,按 `(ContextId, 管线名)` 记忆——进程级单例配每上下文 GL 名,是复审在真实 make-current 序列上打出来的)。**契约改了七次才对**(`c0b`…`c0g`):birth 半边 hook 与发布 latch、`wants()` 看 wired 常量、四个缝的编码一次性写死(`MGPSubData::Target` 低字节资源目标 + 高字节上传目标、`DepthStencilMode` 0/1、`MGPSurface::Kind`、`Pad0`→`TextureTarget`)、Tracker 两个看不见自己主体的快门(`glBindSampler` 只动位 12 的世代、SSO 下 `GetCurrentProgram()` 恒 null)、`MGPipeFramebufferTarget::Named = 3`(**记录改按 framebuffer 句柄存**:原设计只存两个"当前绑定"记录,于是 `BlitNamedFramebuffer` / `ClearNamedFramebuffer*` 在句柄臂上打进一个从没拿到附件的 FBO)、**消费者门**(四族必须"某后端注册了 `MGPipeResourceOps`"才发射——Magma 没有 P4a twin,客户端却按 acceptance 清了 dirty,直接让 66 条 DirectVulkan 用例丢上传)、**客户端侧 D-K2 依赖表**(位 10 要位 7 和位 11,位 9 要位 10;只在服务端拒绝时客户端已经把标记清了)。emulation 在 split 下显式 Fatal 直到 P8(`MGPipeUnmigratedEmulation` 在 `Managers.cpp` 2 处、`DirectGLES.cpp` 4 处) | ✅ 全门(`6035c9d7` 全量 + `8c458cd5` 上复跑):**G1** pull 符号 0 增 / 0 删 / 0 重命名 / 0 resize、`.text` 字节不变(P4a 认定 resize 集为空;Espryt 侧靠预处理器宏而非 lambda/局部量改写——插一个 lambda 就会把同函数里九个既有 `$_N` 全部重编号);**G5** P3a 的十一函数 + P4a 自己的 **17 个区**(`scripts/p4a_untouched_regions.sh`,8 个阴性对照全部按名变红);**G2** pull 与 push 逐名相同 **2902**;**G14** 0 删除 / **+314**;单元 **1785 × 3**;`integration-gpu` **1117/1117 × 七臂**(默认 `0x1fff`、`0x1ff`、`0`、`0x9ff`、`0x5ff`、pull、`ESPRYT_DISABLE_INVALIDATE_FLUSH=1`),其中 DirectVulkan **559/559**;`integration-verify` **920/920 零 `Fatal{`**;79 例 retrace 在 verify 臂 **79/79 全 armed 零分歧**、push 臂 **79/79**、G3b 具名 **12/12**;**八族拒绝普查 = 0**(按族逐条 grep 服务端拒绝句,工具 `wsl_p4a_refusal_census.sh`;`ctest -V` 的普查是假零——console sink 被编译掉了,必须逐用例取自己的日志)。**G9 的"落地前必须红"没能通过公共 GL 达成**:回读模拟自己会设 `GL_DEPTH_STENCIL_TEXTURE_MODE`,`IsDrawSyncClean` 又在首次采样时推参数,所以该场景改成**白盒断言**(新增 `MG_IntegrationTest/Harness/PipeApplyPeek.{h,cpp}` 直接读 applier 的参数记录 + Espryt 已应用状态,变异下 4/4 变红),D 的 `SanityTest` 另加一条覆盖"推迟到首次取视图"的那半。真正红过再绿的是终审抓出的两条:per-level respecify 丢上传(`L0; draw(other); L1; draw(T)` 在 `0x1fff` 下读回全黑、`0x1ff` 下正常)与 delete-then-draw(`glTexImage2D; glDeleteTextures; draw` 在句柄臂 SIGABRT `pure virtual method called`)。**CTS `direct_state_access.framebuffers*` / `packed_pixels`(G15)未跑**,判在关键路径外,随 P3b/P4b 补。**再基线检查点 1b:实际 1 天(2026-09-08 起,六包并行 + 契约七修 + 两轮终审修复),远低于 39 天,未触发** | P3a | -| **P5** 传输 + inproc applier + 发射表 | 12 | `MG_Remote/Client` 发射表;`Server/PipeApplier`、`ServerLoop`(`mgl-srv-io` + `mgl-srv-apply`);`Init.cpp` 单一 hook 装 `BackendObject_Remote`;`MGPCaps` 快照;阻塞 `read_pixels`;client 侧保守 `MarkGpuWritten`;**client 侧块粒度 persistent-map 推送**;`InProcessTransport` 走与 spawn 相同的 G3 编解码;trace-replay `SPLIT` 后缀 + `-DTRACE_TRANSPORT=`;`MOBILEGL_TRANSPORT` 解析 | `DirectGLES.Split.*(ClearThenReadPixels|Triangle)` 在 `inproc` 下绿;OpenRA trace split SSIM ≥ 0.99;`PersistentCoherentMapScenario` 绿;两个角色峰值 RSS 在案;`persistent-map-push` 出数;未迁移字段读 = `Fatal{UnmigratedPipeInput}`。**第 99 天:首个 IPC 帧(缩减路径)** | P4a | -| **P6** spawn transport | 5 | `SocketTransport`(socketpair + fork/execve,envp 剔除 + 强制 monolith 双保险);`ServerMain`;`MOBILEGL_IPC_SERVER_PATH` + `dladdr` 兜底;有界重试握手;EOF 即时退出;device-lost latch | P5 全部测试在 `spawn` 下绿;进程树只多一个子进程;`HeadlessGL` fork 预检无孤儿;OpenRA 在 Adreno 830 上 split SSIM ≥ 0.99。**第 104 天:首个跨进程帧** | P5 | +| **P5** 传输 + inproc applier + 发射表 | 12 | ✅(`ff2994d9..eec0e836`,**128 files / +26161 / −594**)同一 G3 codec 上的 `InProcessTransport`;client 发射表、caps mirror 与 reply mailbox;`mgl-srv-apply` 终身持有 native context;lockstep verb barrier;诚实 host/blob 审计;tight `ReadPixels`;persistent-map 块推送;私有 lane 日志与 split CI/APK 接线。八个实现包见下表 | ✅ 完整门上 OpenRA `inproc` 2/2、SSIM 1.0;split unit 2038/2038;`integration-split` 21/21;E1/E3/E4 的具名阴性对照、G1 0/0/0/0、G2 名差 0、G14 0 删除。27 个普通 wrong-answer 与两个尚不能自证的控制按债务记录,不伪装成已绿。**缩减路径首个 IPC 帧已观察到**(ID-63/65) | P4a | +| **P5.5** `inproc` verb migration(下一阶段) | 待普查 | 先由 `~/w7/notes/p6/census-classC.md` 按 slot / family / Minecraft trace 统计 class-C abort(文件若尚未生成则本项保持 pending),再按命中次序迁移 subsystem;同时消减 `rsp` 指向的 P3b/P4b/P7 输入债 | `inproc` lane 的 abort 数只降不升,直到四个设备 trace 在 Redmi 上全程由 apply 线程渲染;每个迁移族以自己的 class-C Fatal 消失为门 | P5 | +| **P6** spawn transport(后移) | 5 | `SocketTransport`(socketpair + fork/execve,envp 剔除 + 强制 monolith 双保险);`ServerMain`;`MOBILEGL_IPC_SERVER_PATH` + `dladdr` 兜底;有界重试握手;EOF 即时退出;device-lost latch | P5.5 的完整渲染路径在 `spawn` 下绿;进程树只多一个子进程;`HeadlessGL` fork 预检无孤儿;OpenRA 在 Adreno 830 上 split SSIM ≥ 0.99 | P5.5 | | **P3b / P4b** 深化(Espryt) | 29–38 | **P4a 已经付掉的,别再排进来**:`ResolvedDrawBuffers` 重键(P3a 就做了)、Tracker 的集合 hash 抑制器(三个 unit set 各自带内容 hash,clientsp)、`g_unitTextureSyncList` 那条 unit-bindings epoch(E 的验证轮换成 sampler set serial,顺带重键四条下游 memo)、`MGPSubRegion` 的跨步描述符(P4a 的 `resource_subdata` 已经逐区携带 `SrcOffset`/`SrcRowStride`/`SrcSliceStride`)。**仍然欠着**:按存储属主键控的发射游标与 index 重映射(dirty 归属反转,P4a 只落了 drain list);`g_fboTextureSyncList`;`ResolvedTextureBindingMemo` / `SamplerPassMemo` / image sweep / program registry 的重键;XFB scatter 搬到 client;删 fragColor 重推导 workaround 与 `g_broadcastMemo*`;**raw-depth-fetch sampler 原生化**(`g_rawDepthFetchSamplerState` 至今是 `MG_Backend` 里的一个前端 `SamplerObject`,是句柄臂上唯一一处有意保留的前端读——P4a 终审的 R-6);回读 / pack state;外加 P4a 记下的 R-3(三处只活在包头里的编码收进契约)、R-4(`UseProgram`/`BindVertexArray` 两行的 G9 推导)、R-5(D-K2 依赖表两侧各一份,要合成一处)、R-7(三通道 CPU mipmap 重武装缺口)、R-10(六种新 kind 的 ABA 对照目前是惰性的,缺一个消费者 `if`)、R-11(`TextureUploadShapeScenario` 仍是"只记录不设门") | ~25 个纹理场景、21 个 program 场景 + `MG_Test/ShaderTranspiler`;两台设备 `KHR-GL46.texture_*`/`internalformat.texture2d.*`/`shader_image_*`/`packed_pixels` 在 pull 基线 0.5 pp 内;每一个 Iris trace;**`TextureUploadShapeScenario`**(形状金标,Mali 帧时增量必须发布);view/owner 发射游标别名场景;verify 保留模式下 subdata 形状逐项相等;XFB 场景 + `capture_special_interleaved_test` | P4a | | **P7** DirectVulkan(Magma)全量迁移 | 80–104 | §5.5 其余 10 个子系统(子系统 1、4 已在 P2):`SetupDrawSnapshot` 探测字段塌成 dirty mask;占位纹理原生化(~120 行删除);具名 UBO host payload(D-B8,`kCapNeedsHostUboBytes`);blit/depth-mipmap 内部 shader 烘焙 + 新鲜度测试;`VertexInputStateFactory` 裸指针写回删除;D18 容器纪律原样保留 | 集成 + 40 trace 在 Magma 的 push 与 split 下全绿;verify 零分歧;**`nm -D libMobileGLServer.so | grep glslang` 为空**;Iris trace 上 `stage-ubo-named` 逐帧字节发布;两台设备 CTS 0.5 pp 内。**再基线检查点 2:中点(第 40–52 工作日)完成子系统 < 40% 立即重定基线** | P0.5、P2;可与 P5/P6/P8 并行 | | **P8** emulation 下放 + 索引宿主镜像 + 协议广度 | 12–16 | `MG_Impl/Pipe/HostResolve.cpp`(client 数组范围、最大索引扫描、`*IndirectCount` 解析,各带逐站点 reconcile);`MGHostSpan` split 填法;`Server/IndexHostMirror`;CopyImage 镜像搬到 client;`draw_vbo` 收编 multi-draw 族(分档仍在 server);viewport-array 回放验证;`generate_mipmap` 计划 + CPU 回退纹素;G3 分块路径;无 present fence tick + 无 present split 用例;`kCapDriverOrderedXfbCapture` | `'^DirectGLES\.Split\.'` 与 `'^DirectGLES\.'` 逐名相同(DirectVulkan 同);40 trace split 双后端 SSIM ≥ 0.99 含两个 `coherent_as_flush` Create fixture;`ClientArrayAfterComputeWriteScenario` 绿(去掉等待必须见几何缺失);`create-indirect` 上 `roundtrips-per-frame` 读零;`index-mirror-bytes`/`index-bytes-shipped` 逐用例发布。**第 145 天:全功能 split** | P6、P3b/P4b | @@ -29,6 +30,19 @@ | **P12** Android 生产窗口路径 | 10 | `android:process=":mgl"` Service 收 Java `Surface` → `ANativeWindow_fromSurface`;server 生命周期绑 Activity;FCL 用户 env 与 plugin APK V2 开关表接线 | Minecraft 经 FCL 在 spawn 模式下于 Adreno 830 双后端入世界;配对 reboot-clean bench + 输入延迟直方图;杀 server 产生干净 device-lost latch;SIGKILL 故障注入 | P11 | | **P13** 退役 pull 路径 | 8–12 | 删 `SnapshotFromGLContext()` 非 verify 分支、`MGB_CTX`、`MOBILEGL_PIPE_PUSH`、`MOBILEGL_PIPE_LEGACY_MEMOS`;保留 `MOBILEGL_PIPE_VERIFY`;MGPipe recorder 金标模式;删 `set_residual_value_state`;`MG_Backend` 的 `MG_State` include 收缩到 `MGPipeValueTypes.h`;在计数器活着的情况下重调幸存缓存容量(`VaoDrawMemo` 2048、`SetupDrawSnapshot` 4、pipeline memo 8、`syncedTextureMemo` 8)并变成带 env 覆盖的调优参数;最终符号/尺寸/CPU 报告 | `static_assert(sizeof(ResidualValueBlock) == 0)` 编译通过;三道纯度门在非 verify 构建上转绿;verify 构建仍零分歧;recorder 金标在 40 trace 上建立;全套门(集成 × 2 后端 × {monolith, split}、单元、40 trace、两台设备 CTS 在 `81b17c0b` 基线 0.5 pp 内);**monolith 逐线程 CPU 在两台设备 p50/p99 上不差于 P0 基线** | P7、P8、P12 | +P5 的八个实现包(报告在 `~/w7/notes/p5/p5-results/`): + +| 包 | 落地物 | +|---|---| +| c0 | wire/segment 契约、四种 build、71 槽三分类、acceptance reply-slot 与 respecify scope carrier | +| w1 | 记录 codec、blob/tail 边界检查、call/ring flag 翻译、R-10 记录上限 | +| s1 | session/ring/handshake、caps snapshot、reply pool(16 MiB / 8×2 MiB)与 ABI fingerprint | +| p1 | 63 字段 + 7 sticky forward 的四类归属、poison/strict、`rsp` stamp 与生成器门 | +| b1 | persistent-map tracker、保守块推送、GPU-write 标记与 G5 第十一行重钉 | +| t1 | split 测试/CI/retrace/APK 车道、五部分 gate 与阴性控制脚手架 | +| c1 | 33 条生成路由 + 4 escape、37-row catalogue、caps 本地回答、tight readback client scatter 与 PACK-PBO 具名拒绝 | +| v1 | `ServerLoop` / `PipeApplier`、apply-thread EGL 生命周期、server shadow、12 条 forwarder 与 caps republish | + 累计(低端):P0 9 → P0.5 15 → P1 25 → P2 43 → P3a 61 → P4a 87 → P5 99 → P6 104 → P3b/P4b 133 → P8 145 → P9 155 → P10 161 → P11 169 → P12 179 → P13 187;P7 另 80–104,单跑道累计 267。 **CTS 周转单独计价**:`gl44to46` 约 56,271 例。逐阶段只跑该阶段可能影响的具名块(P4a `packed_pixels`、P3b/P4b `texture_*`/`shader_image_*`、P9 `transform_feedback*`);完整 caselist 只在五个架构边界(P0.5、P3a、P4a、P3b/P4b、P13)与每次合并 `dev` 之前跑,放 CI 不放关键路径。若周转仍主导排期,加宽估时而不是削弱门。 @@ -38,7 +52,8 @@ - **第 25 天(P1 出口)**:verify harness 逐 draw 逐字段证明"推送等价于拉取"。零产品风险,**不是** GO/NO-GO。 - **第 43 天(P2 出口):GO/NO-GO —— 判定继续**。P2 的五部分门全绿(逐名相同、零分歧、负面对照能红),逐线程 CPU 代价约 +10% 被接受,下一步立即开 P3a。 - **P3a 出口(2026-09-08)**:五部分门全绿,pull 构建仍 0/0/0/0,`resource_*` 与 vertex-input 两个家族在 Espryt 上换到句柄寻址。**实际日历口径:1 天**——contract / wire / client / espryt / gates 四个包(五轮)全部在 2026-09-08 内完成、集成并跑完门,对着"P3a > 27 天"的绊线是 **1 / 27**,不触发重定基线,也不需要 `inproc` 的证伪数字(`:70`)。Track H 单位成本的完整普查见 `MEASUREMENTS.md` §19。 -- 第 99 天:首个 `inproc` IPC 帧(缩减路径);第 104 天:首个跨进程帧;第 145 天:全功能 split;第 187 / 267 天:三道纯度门转绿。 +- **P5 出口(2026-09-16)**:完整五部分门在 scratch joint head 上观察到首个 `inproc` IPC 帧;同一代码随后落到 `feat/disaggregated@eec0e836`,quick gate 为 split unit 2038/2038、`integration-split` 22/22、push 1128/1128、G1 0/0/0/0(ID-63/66)。 +- 原“第 104 天跨进程帧”后移到 `inproc` verb migration 之后;目标先是完整 separate-thread rendering,再是 separate-process transport(ID-66)。第 145 天全功能 split与第 187 / 267 天纯度门仍是方向,不据此伪造新日历。 ## 第 43 天 GO/NO-GO 清单 @@ -65,10 +80,29 @@ |---|---| | P3a > 27 天 | "窄句柄化"的前提错了,P4a 开始前重定基线。**未触发**:P3a 实际 **1 天**(2026-09-08,四个包全部落地),`MEASUREMENTS.md` §19 记了逐包口径 | | P4a > 39 天 | 同上。**未触发**:P4a 实际 **1 天**(2026-09-08 起;六个包并行、契约七次修正 `c0b`…`c0g`、缝类审计一轮、终审修复一轮)。逐包口径与每一次修正的因由见 `MEASUREMENTS.md` §22 与 `~/w7/notes/p4a/INTEGRATOR-DECISIONS.md`(ID-1…56) | +| P5 > 计划窗口 | **未触发重定基线**:实际日历 **2026-09-11 至 2026-09-16**。Sep 11 的模型配额 outage 中断 wave 2;Sep 16 又把本地 `rereview@local` 谱系重写到用户身份并重新接到 GitHub `ff2994d9`,两项都是过程时间,不是实现规模(ID-40/45/66) | | P7 中点(第 40–52 工作日)完成子系统 < 40% | 立即重定基线(P3a 的检查点发现不了 Magma 特有的超期) | 任一触发,先跑 `inproc` 的证伪数字再决定是否继续。 +## P5 出口记录的债务 + +这些行是 ID-65 的处置,不是把 broad `integration-gpu` 的红偷换成 P5 reduced-path 的绿: + +| 债务 | 去向 / 当前口径 | +|---|---| +| 27 个普通 inproc wrong-answer | 14 layered + 3 packed depth/stencil + 5 framebuffer recycle → **P4b/P7 texture readback**;3 primitive-generated → **P7 query**;`TextureParamsWithoutASamplerView` → **P6 shared-backend inspection forwarder**;`P4aFinalFixScenario` FBO/RBO delete lifetime → **P6 shared-backend lifetime** | +| `rsp` residual inputs | 逐帧基线已记录;按字段声明的 P3b/P4b/P7 retirement phase 清零,不能以 barrier 永久遮住 | +| `SEG_REPLY` 2 MiB payload cap | 当前 `MaxReplyBytes = 2 MiB - 16`;更大 readback 需要 P6+ chunking 或专用 carrier(ID-47) | +| `GetCaps` 的两个 blobref | 目前不骑 record;一旦运输,必须有 server→client carrier rule,不能套 `SEG_STAGE`(ID-48) | +| PACK-PBO readback | P5 具名拒绝;真实形状是 server 写 buffer resource、client `MarkGpuWritten`,列入 P6(ID-57) | +| ABI fingerprint | 目前不混 segment sizes;改变 8/32/16 MiB + 256 KiB ledger 时仍欠这项(ID-47) | +| max record bytes telemetry | encoder 有计数而场景 stats 不发布;**pending x2** | +| E2 clear-drop control | baseline 2/2 绿,但 clear-drop 保持 SSIM 1.0,未因自己的理由变红;**pending x2** | +| E3 small-ring wait telemetry | 小 ring 场景运行过,但没有 wrap/wait 数;**pending x2** | +| E3(a) 具名 block-zero diagnostic | 目前靠 pixel assertion 变红;**pending x2** | +| E5 corrupt-staged-upload → draw 控制 | audit lane 绿不等于该控制已自证;**pending v1 round 3** | + ## 仍然开放的问题 P0 已回答的不再列出(spike A 的域、spike B 的分档、`posix_spawn` 不可用、OOM 探测惯用法、`GetInteger64i_v`/`GetProgramiv` 退役、D21 与 `RenderbufferObject` lifetime id、动态 accessor 基线)。