Files

200 lines
65 KiB
Markdown
Raw Permalink 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.
# CONTRACT-P7 — Magma(DirectVulkan)全量走完拆分路径
权威:本文,与 `CONTRACT-P5.md`(表 0、字节载体、字段归属、R-1…R-17)、`CONTRACT-P5C.md`(规则 E、两个具名豁免、`SEG_EVENT`、守卫)、`CONTRACT-P5E.md`(规则 F、等待规则)、`CONTRACT-MAGMA-RUNAHEAD.md`、`CONTRACT-P6.md`(规则 G/H、死亡漏斗、§12 记录债)、`CONTRACT-P65.md`(两轴传输、布局指纹、`LinkTerms`)并列;与它们不一致处以本文为准,§10 逐条列出。基线:`feat/disaggregated @ e8b2c4bd`。`file:line` 均在该提交上读取;路径在 `MobileGL/` 下,除非以 `docs/` 开头。
**怎么改。** 集成者的文件。wave 2 的实现包(A / B / C / D / E / F,§9)需要改行时经集成者;包从第一天起就对着下面的行编译。
**写自什么。** [`notes/p7/PLAN-PH-P34B-P7.md`](../notes/p7/PLAN-PH-P34B-P7.md)(三行逐项审计,66 个代理,每个 done / unnecessary 判定两名反驳者)与 [`notes/p7/INTEGRATOR-DECISIONS-P7.md`](../notes/p7/INTEGRATOR-DECISIONS-P7.md)(ID-P7-1..11)。本文只写**规范**——树必须变成什么样、门怎么判;为什么这样定在裁定日志里。
---
## §0 P7 是什么,以及一条高于每一行的规则
P7 收官 = 五道出口门同时成立(§2–§7),而不是路线图 P7 行的交付列逐字做完。路线图行里两项**本阶段拒做**(ID-P7-5:`SetupDrawSnapshot` 探测字段塌 dirty mask、`VertexInputStateFactory` 内容寻址 CSO——它们是 monolith 路径上的性能项,`VulkanRenderer::SetupDraw` 在任何非 monolith 传输下分支到 `SetupWireDraw`,split 下不跑,且改它们动 pull `.text`),一项只做窄半(内部 shader 烘焙的 monolith 臂退役与直通 TCS 烘焙归 P13 / OQ-7 宽半)。
**规则 I — Magma 的 wire 臂上没有 `std::abort()`。** 树上每一处 `@P7` 具名拒绝今天都是 `MGLOG_F` + `std::abort()`,绕过 `Session::Fail`、不给对端 `SessionFault` 帧、普查门看不见。P7 之后,`MG_Backend/DirectVulkan` 下任何 wire 臂的拒绝只有两种形状:(a) **decline**——`MGLOG_E_ONCE` + 返回失败 + 与 monolith 臂相同的可观测(屏蔽属性、跳过 blit……);(b) **具名 Fatal**——经 `MG_Pipe` 的 `MGPipeSessionFailHook` 进 `MG_Remote::SessionFail`,带 `FatalFamilies.def` 里的家族词。`MG_Backend` 不 include `MG_Remote`(include 闭包门),hook 由 server 角色初始化安装。规则 I 的牙齿是 §3 的普查门:扩到 server 镜像后,`MG_Backend` 下裸 abort 的站点数只能下降。
**规则 J — 每个修复先在两进程臂上红一次。** wave 0 之前树上没有一条车道能让 Magma 两进程用例变红(四条 split 臂硬编码 `MOBILEGL_BACKEND_TYPE=DirectGLES`,`MG_IntegrationTest/CMakeLists.txt:2002/:2025/:2031`),所以 ID-P7-10:车道落地前不派 `@P7` 退役的活;落地后每个退役都带一条在 `integration-magma-spawn`(或 `-tcp`)上先红后绿的用例。
---
## §1 与前序契约的关系:什么已经不是 P7 的
| 项 | 谁做了 | 证据 |
|---|---|---|
| `DynamicBackendParameters` / `MGPCaps` / `RenderStateParameters` 定宽 + 布局指纹 | P6.5 wf(`fe28bdb5`) | `CONTRACT-P65.md` 「握手与布局」;`CONTRACT-P6.md:666` 仍记在 P7 名下,§10 更正 |
| server buffer consumers、Android 旋转 blit | `38919d45` | `notes/p5f/magma-inproc-fix.md` |
| Magma run-ahead、GPU 延迟退休、VAO 身份碰撞 | `194382c9` | `notes/p5f/magma-runahead.md`;`MG_Backend/Init.cpp:101` `kMGPipeMagmaRunAheadReady = true` |
| packed-float mip 原生 blit、resolve、占位图像、copy-image 端点、XFB 捕获 span、纹理回读 | `ed507b1a..d56c0e83`(随 `e1bf3677` 合入) | `WirePlaceholderImages.inc`、`WireFramebuffer.inc:634-636` |
| 颜色 blit 烘焙(仅 split 臂) | P5f fv | `Renderer/WireColorBlit.{vert,frag,inc}`、`WireColorBlitSpirv.h` |
| D-B8 具名 UBO 的载体 | 直绑常驻 `VkBuffer` range 的备选已在树上 | `UniformManager.cpp:2375` 直绑分支;欠一次 `stage-ubo-named` 字节/帧测量后关 OQ-6 |
---
## §2 出口门 1:集成 + trace 在 Magma 的 push 与 split 下全绿
**2.1 车道形状(wave 0 包 L)。** `mgl_itest_register_split_arms` 获得后端维度;DirectVulkan × {Split(inproc), Spawn, Tcp} 三臂的环境为 `MGL_ITEST_VULKAN_{SPLIT,SPAWN,TCP}_ENVIRONMENT`(`MOBILEGL_BACKEND_TYPE=DirectVulkan` + monolith `DirectVulkan.` 臂已用的 ICD 钉)。两档标签:
- **gating**:`integration-magma-split`(inproc,含义不变)、`integration-magma-spawn`、`integration-magma-tcp`——今天 `integration-magma-split` 的 73 条(及 magma-buffers / magma-runahead / magma-caches 子标签)重铺三臂;三臂**同数绿**才算门 1 的集成半边成立。
- **informational**:`integration-magma-{spawn,tcp}-all`——所有经共享宏注册的场景 × DirectVulkan × 两进程。首轮红名单是 wave 2 的工作清单;不进 CI 门,直到某条目连续绿后**按名**升入 gating(`test.yml` 的 require_green 名单)。
名集合纪律不变:`scripts/ci/spawn_lane_parity.py` 规范化后 split / spawn / tcp 三集合相等;G14 只增不删;每条目私有角色日志(`SplitLogPaths.cmake.in` 由宏累积)。
**2.2 臂证明。** Magma 两进程条目必须在 client 私有日志里同时有 `transport=spawn`(或 `control=tcp data=stream`)与 `backend=DirectVulkan`(或等价的 ConfigLoader 行)——一条 Magma 车道静默跑成 GLES 会满足其它所有门。`junit_tally.py` 的 `--require-spawn` / `--require-tcp-ran` 各加后端断言。
**2.3 死亡通知前提。** `MG_Remote/Client/WireTables.cpp:740` 只在 DirectGLES 下安装对象死亡通知,Magma 两进程 client 的 `ObjectDeaths` 恒为 0。改成后端无关(wave 0 L);Magma 自己的 `StateObjectDeathOps` 表(wave 2-C)落地前,`CtWireScenario` 的死亡用例在 Magma 臂上按名 skip 并列入 §12。
**2.4 trace 半边。** `retrace-split` 已是 39 case × 2 后端 × {inproc, spawn}(`test.yml:2449/:2470`);欠 `iterationrp × DirectVulkan` 的三个旋钮在 retrace-split 作业里与 `:2183` 同样导出(wave 0 L)。门 1 的 trace 半边 = 78 条 DirectVulkan split 行全部达阈值。
**2.5 门 1 的判据。** gating 三臂同数绿(wave 0 落地时:magma-split 73 = 71 PASS + 2 SKIP,magma-spawn 52 = 50 + 2,magma-tcp 54 = 52 + 2 fixture——`MagmaRunAheadScenario`(14)与 `MagmaWireCacheScenario`(7)只注册 inproc 臂,`spawn_lane_parity.py` 的 `MAGMA_INPROC_ONLY` 具名例外;跨进程的 hold / cache peek 是 §12 的债)+ parity 通过 + retrace-split 78 条 DV 行达阈值 + informational 车道(`integration-magma-full-{split,spawn,tcp}`,整个集成二进制:510 / 510 / 512 条,首轮 453 PASS / 57 SKIP / **0 红**,tcp 臂 1 红 = `IterationRPProgram203Scenario` 的 exposure texel,StreamLink × Magma 交叉项,见 `notes/p7/magma-two-process-first-run.md`)的红名单为空或每条按名归入 P8 / P9 / P13 并在 `notes/p7/` 有票。**主机 lavapipe 打不到任何 `@P7` 拒绝**(驱动能力差异),门 1 的红名单实际来自真机(§7.1 的设备窗口)。
---
## §3 `@P7` 具名拒绝:普查、退役与 decline
**3.1 普查数(钉住)。** 基线上 `grep -rn "@P7" MobileGL/ --include=*.cpp --include=*.inc --include=*.h` = **15 处 / 4 文件**(wave 0 包 C 以 `fatal_census.py --json` 钉住;两次审计的 14 串 / 13 语句是把一个三元里的两个串算成一条),全部在 `Renderer/UniformManager.cpp`、`Renderer/WireFramebuffer.inc`、`Renderer/WireDraw.inc`、`Renderer/VulkanRenderer.cpp`。另有两项 P7 归属但不带 `@P7` 字样:`Fatal{RoleViolation, "buffer-legacy-arm"}`(`WireDraw.inc:14/:204`)与「Magma 无 `StateObjectDeathOps`」(两条集成用例 skip)。
**3.2 退役表。** 「退役」= 实现该形状,与 monolith 臂同可观测;「decline」= 规则 I (a)。
| 名 | 站点 | 判定 | 形状 |
|---|---|---|---|
| `uniform-buffer-byte-tail` | `UniformManager.cpp:2391` | **退役**(真机 MC 报告 §5 的活边界) | 拷贝窗口外扩到 4 字节边界进 transient slice,再 `vkCmdCopyBuffer` 对齐内部到正确子字目的;`CopyWireBufferRangeToSlice` 扩 |
| `copy-image-in-place` | `VulkanRenderer.cpp` | **已退役(wave 2-B,`4f857aa5`)** | `srcImage == dstImage` 时单次转 `GENERAL`、两侧布局 GENERAL、一次写回;同 mip+layer 且矩形重叠 → decline(GL 未定义) |
| `mipmap-shader-format-or-shape`(真机 iterationt 的死因) | `WireFramebuffer.inc` | **已退役(P8-F,ID-P8-6)**:wire 臂改具名拒绝 `MipmapShaderFormatOrShape`,与 monolith 一致;此前半退役(wave 2-B,`c4be3800`) | Fatal 行带上格式 / 尺寸 / aspect / 缺的 feature 位;image 不能作附件时经 scratch 走颜色 shader mip;仍无 shader 臂的形状**具名**拒绝 |
| **wire 回读竞态(曾被当作 OpenRA 真机 0.976494 的原因;真因是下一行)** | `WireFramebuffer.inc` / `VulkanRenderer.cpp` | **已修(wave 2-B,`76b74693`)** |
| **wire 臂 completed-frame-serial floor 不健全(OpenRA 真机 0.976494 / 0.988998 的真因,ID-P7-33)** | `VulkanRenderer.cpp` `OnSubmitsCompletedUpTo`、`VkBufferManager.cpp` `WriteWireBuffer` | **已修(wave 2-B3,`511c1f16` / `c9164f25`)** | 一个 serial 两次提交(S1 pooled-fence 中帧 flush、S2 Present),逐记录退休把 floor 提前 → 帧 N 首次 `glBufferSubData` 走无序 host memcpy 被 S2(N-1) 的排队拷贝盖掉;floor 只升到可证 serial + 有读者在飞的 buffer 一律有序;计数 26→0,red-once `StreamedBufferSubDataBeforeEachDrawIsOrdered` Split+Spawn;32 处静默出口具名(`WireDeclines.def`)。留:三处单 fence 退休站点的聚合等待(B4) | `ReadWirePixels` 在 pooled fence 下 flush 帧内 draw 但不等待,拷贝是后一次提交,二者只靠一条 barrier 相连,而 `UNDEFINED` / `PRESENT_SRC_KHR` 的源 scope 映射成 `TOP_OF_PIPE`+0 = 空 scope——刚 present 过的默认 framebuffer正是这个布局;修后回读等它读的那一帧(red-once:`fences=2` vs `1`,`completed=71` 不变)。真机验收 = 三次 `pm clear` 冷跑 1.0 |
| `vertex-layout` | `WireDraw.inc:245`、`VertexInputStateFactory.cpp:439-500` | **已拆分(wave 2-C,`f1b04edf`)** | `buffer-window` 一种保留为协议错(经 hook 的具名 Fatal,家族词暂留 `UnmigratedVerb`——改成更诚实的 `ProtocolCorruption` 要在 `MG_Pipe::MGPipeFatalFamily` 加一行 + 适配臂,§3.3 未花这笔,记 §12);其余**七**种形状/格式原因(含审计之后新增的 `offset-overflow`)改 monolith 的「屏蔽该属性继续画」,`BuildWireVertexInput` 出 `unsupportedAttribMask`;`vertex-format-conversion` 同包改 decline。`WireDraw.inc` 从此无 `@P7`(普查 15 → 13 处 / 3 文件) |
| `default-color-blit-shape` | `WireFramebuffer.inc` | **已退役(wave 2-B,`583e12de`)** | 恒等变换回落 `vkCmdBlitImage` 臂;非恒等旋转先 resolve/copy 进 scratch 2D RGBA 再 shader blit;四旋转之外 decline。顺手修出拒绝背后的活 bug:普通 blit 臂对 3D 源按 layer 寻址(z 0..1 读到 slice 0)。旋转半边只在真机可证 |
| `multisample-blit-shape` / `-aspect` | `WireFramebuffer.inc` | **已退役(wave 2-B2,`452d33b5` / `51fa513d`)**:shape 走 scratch resolve + blit、MS 目的地具名 decline;aspect 走 `VK_KHR_depth_stencil_resolve` 或第三套烘焙 shader resolve(`WireMultisampleResolve.*`),无臂的采样数形状具名 decline;树上 `@P7` 拒绝站点 **0**(剩 19 行全是注释) | shape:resolve 进 scratch 单采样再 `vkCmdBlitImage` 缩放/翻转;aspect:MS 深/模板 → 单采样用 `VK_KHR_depth_stencil_resolve`(`DynamicBackendParameters` 里查扩展)或 `texelFetch(sampler2DMS)` 写 `gl_FragDepth` 的 shader resolve;单采样 → MS 尺寸不同 decline |
| `depth-stencil-mipmap` | `WireFramebuffer.inc` | **已退役(wave 2-B,`227d8220`)** | DEPTH-only 走 `WireDepthMipmap.{vert,frag}` 烘焙程序((A));`MOBILEGL_BAKED_INTERNAL_SHADERS` 表驱动新鲜度门((D))覆盖树内全部烘焙头——首跑抓出 iterationRP witness 头过期;D\|S 合并 / 仅 stencil → decline |
| `uniform-buffer-byte-tail` → **已退役(wave 2-A,`450d3075`)** | — | — | 见上行;真正可达的形状是 `size` 非 4 倍数(offset 非对齐在前端就 `GL_INVALID_VALUE`) |
| `texel-buffer-native-format` | `UniformManager.cpp` | **已 decline(wave 2-A)** | 零占位 view,fetch 读零;只在 Adreno 上有可观测(`GL_RGB32F` buffer texture) |
| `unaligned-{texel,storage,atomic}-buffer-range` | `UniformManager.cpp` | **已具名 decline(wave 2-A)** | 跳过该绑定;可写 range 的 copyback 不做(分不出 readonly、录制点在 B/C 的 draw 尾,配方 `magma-a.md` §3.1);真正活的缺口是 `glBindBufferRange(GL_ATOMIC_COUNTER_BUFFER, …, 4, 4)`,Adreno 64 字节对齐 |
| `storage-buffer-native-range` / `uniform-block-native-range` | `UniformManager.cpp` | **已钳制(wave 2-A)** | 钳到 `maxStorageBufferRange` / `maxUniformBufferRange` |
| `uniform-buffer-dynamic-offset` | `UniformManager.cpp` | **已 decline(wave 2-A)** | >4 GiB transient ring 跳过绑定 |
| `storage-buffer-offset-overflow` / `texel-buffer-offset-overflow` | `UniformManager.cpp` | 具名 Fatal(经 hook) | 算术不变量,不带 `@P7` |
| `vertex-format-conversion` | `WireDraw.inc:272` | decline | 可选:packed converter |
| `buffer-legacy-arm` | `WireDraw.inc:14/:204` | 具名 Fatal 经 hook(`RoleViolation`) | 已是拒绝,只换漏斗 |
**3.3 普查门(wave 0 包 C)。** `scripts/ci/fatal_census.py` 的扫描根扩到 server 镜像(`MG_Remote`、`MG_Pipe`、`MG_Backend`、`MG_Impl/Pipe`、`MG_State`,MG_Test / MG_Benchmark / MG_IntegrationTest 除外);`abort_sites` 成为**向下棘轮**;每个 `Fatal{Word` 在 `FatalFamilies.def` 有行;`Refuse{Word}` 对 `protocol.fbs` `RefuseCode` 校验。baseline 数(wave 0 落地,`8bb6309a`):**79 处 abort / 20 文件**(扩容前 4 / `MG_Remote` 一处),**43 个家族词**(补了 11 行 `.def`,`InitialBytesNotCarried` 并入 `UncarriedInitialBytes`),3 个 `Refuse{}` 词,0 无标记;`ResourceUnavailable` 投影到 `ServerCrashed` 而非 `SegmentMismatch`(段几何没错的时候不说段错了);Magma 三个 wire fatal 漏斗(`MagmaWireFatal` / `WireDescriptorFatal` / `WireBufferLegacyFatal`)经 `MG_Pipe` 的 `MGPipeSessionFailHook` 进 `SessionFail`,家族词保持 `UnmigratedVerb`(改名会作废自 P5b 起的拒绝普查计数);`Refuse{AuthenticationRequired}` 改为词表里的 `Authentication`。
---
## §4 出口门 4:184 符号棘轮下降 P7 的 101 个
**4.1 定义(wave 0 包 R,`scripts/link_ratchet.py`)。** 在 disaggregated 构建的对象树上:按 `notes/p6/a6-link-experiment-data.md` 的路径规则把 `MobileGL.dir` 下对象分 SERVER / FRONTEND / SHARED;集合 = (SERVER 未定义) − (SERVER 已定义) − (SHARED 已定义) ∩ (FRONTEND 已定义)。baseline 存**符号列表**(`scripts/data/link_ratchet_baseline.txt`),`--assert-monotone` 只对新增符号红,消失只提示重基线(ID-P7-7)。基线数(`scripts/data/link_ratchet_baseline.txt`,`8bb6309a`):**186** = `p7-magma` **101** / `p3b-p4b-espryt` **14** / `both-backends` **60** / `p6-core` 11——三个后端桶与 a6 的手数逐符号相同,多出的 2 个是 P6.5 的 `ClientSession::StartSpawned()` 与 `ProgramArtifactsSchemaFingerprint()`。Espryt 桶里 5 个是 `MG_Impl/Pipe/SlotAllocator.cpp.o` 定义的 `MG_Pipe::` 符号,按文件位置落在前端桶、只有 P13 的模块搬迁能清(标 `# P13`),所以 P3b/P4b 的可清数是 9 不是 14。`MG_Remote/FatalFunnel.cpp.o` 归 SHARED(两个角色都经它);`MG_Backend/Init.cpp.o` 按 a6 规则归 SERVER,虽然它引用 5 个 client-session 符号。CI 步骤在 `build-linux-split` 作业;baseline 由参考工具链生成,CI 的 `clang++-20` 内联决策可能让首轮红——三元规则:已列类的兄弟方法、无新引用对象 = 内联,重基线;新引用对象永远不是内联。**重基线(wave 2-B2,`3c2867d3`):186 → 173** = `p7-magma` **88** / `p3b-p4b-espryt` 18 / `both-backends` 56 / `p6-core` 11;(B′) 清掉 13 个(`ProgramObject::*`、`SamplerObject::Set*`、`ShaderObject::*` 的编译/链接路径)。剩下的 88 不是「隐藏程序」而是 **monolith draw 路径**(`UniformManager.cpp.o` 引用 `TextureObjectBase`/`TextureObjectWithOneMipmap` 等 62 个、`VkSamplerManager.cpp.o` 引用 `SamplerObject` 14 个 getter、12 个单例),`SetupDraw` 在运行时才分支到 `SetupWireDraw`(ID-P7-5 保留该运行时)——`#if` 掉这些 monolith 臂与 (B′) 是同一种操作、属 wave 3,故不标 `# P13`;§4.2 的「降到 0」对 (B′) 单独不成立。**收口(ratchet88,`p7/ratchet88`):173 → 171** = `p7-magma` **86** / `p3b-p4b-espryt` 18 / `both-backends` 56 / `p6-core` 11;上一句的「不标 `# P13`」作废。先定的事实:disaggregated 构建的缺省传输就是 monolith(`ConfigLoader.cpp:322-330`;`SetupDraw` 只在非 monolith 下转 `SetupWireDraw`,`VulkanRenderer.cpp:7064-7073`),而它被三道门消费——CI `integration (monolith-control)` 在 disaggregated 主工件上跑 `integration-gpu`(`test.yml:303-309/:390`)、`build-linux-split` 的 monolith 遍是硬门(`test.yml:1531`)、设备 monolith-control 回放用同一 disaggregated APK 且是门 3 的同会话参照臂(`apk.yml:356-367`、§7.2)——所以 `#if` 掉 monolith 臂不是 (B′) 那种等价替换,而是删掉参照臂。逐重定位归因(`~/w7/logs/ratchet88/ATTRIBUTION.md`):清掉的 2 个是 `MGPipeTextureLegacyArmScope` ctor/dtor——P5c 给 `VkTextureManager::SyncTexture` 的具名豁免,monolith 下空操作(`OnApplyThread()` 恒假),非 monolith 下临时探针(豁免一武装即 abort)在 magma split/spawn/tcp 与三条 full 车道零触发,连同类、计数器和守卫里的豁免行退役(守卫本身留)。余 86 全在活的 monolith 臂上,按名记 §12、baseline 标 `# P13`。B2 §4.3 的归因更正:对应用纹理的读经 `ITextureObject` 虚派发、不产生链接符号;`UniformManager.cpp.o` 的 62 个**全部**来自后端**构造**前端纹理当占位(`GetFallbackTexture` / `GetFallbackMultisampleTexture` / `MakePlaceholderTextureObject`,6 个无外联 key function 的纹理类的 vtable 在本对象就地发射,引出 52 个虚函数)。
**4.2 P7 的目标。** `--bucket` 归到 DirectVulkan 引用对象的那一桶(a6 的 101)降到 **0**。唯一还在构造前端 `SamplerObject` / `ShaderObject` / `ProgramObject` 的 DirectVulkan 站点是 `VulkanRenderer.cpp:4526-4583 / 4624-4684`(monolith 臂的隐藏 blit / 深度 mip 程序),其 early-return 是运行时而非 `#if`,所以符号仍被引用。降到 0 的路径:在 **disaggregated 构建里** monolith 臂也改用烘焙模块(§5.2 的 (B')),pull 构建的代码逐语句原样——G1 不动。若 wave 3 实测某桶符号无法在不动 pull `.text` 的前提下消失,按名记入 §12 并在 baseline 里 `# P13` 标注,不算门 4 失败。**2026-09-23(ID-P7-55):** P13 判据扩为「不动 pull `.text` 去不掉,**或**去掉会改变 disaggregated 构建 monolith 参照臂的可观测行为」;`p7-magma` 桶余 86 全按后者标 `# P13`(逐组见 §12),门 4 以此读法关闭。
**4.3 与 P3b/P4b 的 14 个。** 不是 P7 的目标;棘轮落地后 P3b/P4b 的 Tier 1 才可调度。
---
## §5 内部 shader、反射归档与 resident 上传(wave 2-B / 2-C 的规范)
**5.1 烘焙 (A)(D)。** `WireDepthMipmap.{vert,frag}` + 生成的 `WireDepthMipmapSpirv.h` + `WireDepthMipmap.inc`(深度附件 render pass + pipeline),`GenerateWireMipmap`(`WireFramebuffer.inc:752-769`)深度分支接入;新鲜度门 `MOBILEGL_BAKED_INTERNAL_SHADERS`:MG_Test 用树内 glslang 重编每个签进树的 SPIR-V 头并逐字节比对,**表驱动**,覆盖 `MG_Util/SelfTest/DriverPostIterationRPWitnessSpv.h` 与 `PrimitivesGeneratedNoXfbProbeSpv.h`。
**5.2 (B') disaggregated 构建的 monolith 臂改用烘焙模块**(§4.2 需要):`InitializeBlitResources` / `InitializeDepthMipmapResources` / `BlitWithShader` / `GenerateDepthMipmapWithShader` 在 `#if MOBILEGL_BUILD_DISAGGREGATED` 下走烘焙管线;`#else` 分支保留今天的代码逐语句原样。两处 `MagmaP7AllocatorDebtScope` 随之删除(先用临时断言证明它们在非 monolith 传输下从不武装)。
**5.3 OQ-8。** `LinkArtifacts` 加有序 `storageBlocks{name, binding, dataSize}`(顺序 = stage 序 → 规范化描述符名 → binding,跨 stage 去重,与 `MagmaProgramSource.h:194-195` / `DirectVulkan.cpp:220-260` 今天的资源查询序相同),进 `Visit()`,archive codec 版本 +1(`wireFingerprint` 随之变);`MagmaProgramSource::EnsureStorageBlocks` 与 `DirectVulkan.cpp` 的 `GetProgramResourceCache` 都改读它;`g_programResourceCaches` 及其 rehash 悬挂风险随之删。单测:多 stage + 数组 SSBO + atomic-counter block 的索引序与 SPIRV-Reflect 逐项相同。
**5.4 OQ-10。** `MG_Backend/Init.cpp` `InitServerRoleCommon` 按 **server 自己的 wire 资源表** 判 `SubDataResident != nullptr` 发布 `kCapResidentSubData`(不按后端枚举猜——ID-39 的教训);`RemoteClientTest.cpp:594` 改期望;integration-magma-buffers 与 integration-split 各一条白盒用例证明发出的是 `BufferSubDataResident(49)`;26.3 in-world 回归。
**5.5 Magma `StateObjectDeathOps`。** 镜像 `DirectGLES/Managers.cpp:335-474` 的表,从 DirectVulkan 后端注册处安装;删六处 Magma 特判;`PipeSlotPeek` 断言死亡后槽位回落;red-once = 卸表看槽位泄漏断言红。
---
## §6 出口门 2:verify 零分歧
verify **在 wave 3 之前**只在 monolith 臂注册(`MG_IntegrationTest/CMakeLists.txt` 的 `if (MOBILEGL_PIPE_VERIFY)` 块);wave 3 V1 起同一文件的 `if (MOBILEGL_BUILD_DISAGGREGATED AND MOBILEGL_PIPE_VERIFY)` 块把整套用例在 split(inproc) 臂上也注册了一遍,所以这句只是门 2 的**起点**记录,不是现状。门 2 = **verify × {monolith, split(inproc)} × Magma 零分歧**,且两条既有负控(`VERIFY_CORRUPT`、`POISON_OMIT`)在 split 臂上各红一次。前置债:verify+split 撞旧 `PipeRespecifyScope` 断言(`notes/p65/validation-status.md:118`)——放宽为「按 level 的 producer 各自覆盖其宣告范围」,正反单测各一。trace 侧:8 个 verify case × DirectVulkan × inproc 一轮,分歧计数 0。**状态(wave 3 V1,`p7/verify-split`,`notes/p7/verify-split.md`)**:前置债已清(`70f86064`),`integration-verify-split` 双后端 1082/1082(集成树 verify 构建,M2 之后) 绿、两条负控在 split 臂上各红一次(POISON_OMIT 用 split 臂自己的 `ReadPixels:GetPixelStoreParameters` 配对)、trace 侧 8 case 分歧 0,门 2 在主机上成立;唯一的真实分歧是 server `read_pixels` 的 ID-49 中性 pack 窗口,按预言而非规则处理,spawn 不在门内也无法测(比对器是同进程仪器)。
---
## §7 出口门 3 与 5:真机与 CTS
**7.1 门 3 分母(ID-P7-4,设备窗口 #1 E0a 补正)。** `trace_cases.json` 的 CI split 子集(39)× DirectVulkan × `--use-pbuffer`,排除**在 monolith 臂上就红的 case**——它们按定义不是分离缺陷:`create-indirect`(OQ-17:`dev@81b17c0b` 起在 Adreno 830 上就坏)、`minecraft-1.21.11-main-menu`(trace 自身 UBO offset 不满足本机 32 字节对齐,`notes/p65/mainmenu-trace-difference.md`)、`minecraft-1.21.4-fabric-iris-photon-v1.3b-in-world`(Adreno 830 的 `vkCreateGraphicsPipelines` 对 photon 程序返回 `VK_ERROR_UNKNOWN`,`Renderer/PipelineFactory.cpp:660`,monolith SSIM 0.0089;桌面 lavapipe 绿;按 OQ-17 先例归 `dev` 侧独立条目);rd12 in-world 不在 `--matrix` 里。**分母 = 36**。`iterationrp` 带 `apk.yml:460-462` 的三个 Magma 旋钮。排除表随每次窗口更新于 `notes/p7/device-window-<n>/E0-attribution/exclusions.md`。
**7.2 门 3 判据(逐 case 对 monolith 臂,不是对 golden 的平 1.0)。** E0a 实测 monolith × DirectVulkan 对 golden 的 SSIM 在 0.996–1.000(iris 光影 case 最低 0.99617),只有 OpenRA 是 1.000000——golden 来自桌面,阈值吸收驱动差。所以门 3 = 同一 reboot-clean 会话内,对分母中每个 case:inproc **与** spawn(或 tcp)三遍**逐位相同**(零翻转),且每遍 `ssim vs golden` ≥ 该 case 阈值 **且与 monolith 臂同会话读数之差 ≤ 0.0005**(OpenRA 两臂都必须 1.000000、mismatch 0);`RUN_AHEAD=0` 对照臂一遍记录。证据目录 `docs/Disaggregated/notes/p7/device-window-<n>/`。定位实验顺序 E0–E6 见计划 §3 wave 1。**状态(W4,p7w4 = `ae3f43ab`,ID-P7-25)**:inproc 一遍 34/36 与 monolith 差 ≤ 0.0005;OpenRA 是与温度无关的约 50% 竞态(两张图,lockstep 第三张),验收改为 run-ahead 与 lockstep 各 6 遍全等 golden;bsl-esc-menu inproc OOM 待 spawn 臂。**状态(W5,p7w5 = `713bea9a`,ID-P7-34)**:OpenRA **27/27 golden**(inproc/spawn × run-ahead/lockstep × 冷/热 + FIF=8 + dump-armed)——项关闭;余 bsl-esc-menu(M2)与终局形式(p7w6:分母 36 三遍逐位 + spawn 臂同会话)。**状态(W6,p7w6 = `0e16ca27`,ID-P7-49)**:bsl-esc-menu inproc/spawn 各三遍 6/6,实际图像六张逐位相同且与 E0a monolith SHA 相同,M2 的设备正确性项关闭;36 例的终局三遍及同会话 monolith 对照仍待跑。W6 APK 在 M3 之前构建,不能代替 M3 后的设备回归。
**7.2 修订(ID-P7-62,g3det,2026-09-23;集成者终裁):monolith 自身不可逐位复现的 case。** 「三遍逐位相同」预设同会话参照可逐位复现。门 3 的 monolith 臂自此每例 **×5**(split 臂仍 ×3);同会话 monolith ≥ 3 遍而图不全逐位相同的 case,split 臂的逐位条款换成分布判据:(1) split 每遍对本会话**每个** monolith 读数 `|ssim − m| ≤ 0.0005`;(2) **最近 monolith**:split 每遍到其最近 monolith 遍(差异像素最少,相同取逐通道差小者,逐通道差也量到这一遍)须 **`px ≤ 1.25·D_mm + 32` 且 `Δ ≤ 1.5·Δ_mm + 8`**,`D_mm` / `Δ_mm` = 各 monolith 遍到另一 monolith 遍的最近差异像素数 / 逐通道差的最大值(留一法,按遍)。monolith 逐位相同或不足 3 遍的 case 仍按逐位判;按分布判的 case 按名列出,其跨臂不同由 (2) 判、不需裁定行。p7w7-6afa077f(boot `8e396037…`)列名 `iris-sundial-lite`、`iris-bliss`、`iris-derivative-main-d24.4.14`(derivative 已定位:光影包的计算降噪在同一 `glDispatchCompute` 内 `imageLoad` halo 后 `imageStore` 回同一图像,workgroup 间竞争;`RUN_AHEAD=0`、lockstep、monolith `FRAMESINFLIGHT=1` 都不能逐位复现,主机 sync validation 两臂 0 hazard;不是分离缺陷)。同会话图模拟(monolith 池任取 K 遍作参照,配门 3 自己的 split 6 遍)的 (1)+(2) 通过率:×3 sundial / bliss / derivative = 100% / 97.5% / 95%,×5 = 100% / 99.9% / 100%,纯噪声对照(另取 monolith 遍当 split)95–100%;首版「全部图对 ≤ 1.25 倍」bliss ×3 只 16.2%,过渡版 `Δ ≤ Δ_mm + 1` derivative 0%、纯噪声 70%。窗口 2 按终裁只读重判(加 g3det 同 boot 的 monolith 遍)36/36,只余 create-instancing 的跨臂裁定(`PASS-NEEDS-ADJUDICATION`,非判据)。证据 `~/w7/logs/g3det/`、`~/w7/logs/g3-tools-rev2/`,判读见 `docs/Disaggregated/notes/p7/device-window-2/tools/RUNBOOK.md` §3 / §6。
**窗口 1b p7w6 DirectGLES × TCP(ID-P7-52,独立调查而非门 3)**:38 例含 9 个缓存跳过,29 次尝试为 16 pass、8 case-level idle timeout、2 visual fail、3 missing-result timeout。missing-result 与 timeout 不作视觉红;Photon v1.3b / D24.4.14 的两张真实错图及逐例 phone reap 状态见 notes/p65/window-1b-p7w6.md。B4 host gate 与未知子集尝试重叠,timeout 墙钟可能受 CPU 竞争影响;M3 后设备门 3 和 CTS 仍待最终 APK。
**状态(终局窗口 3,`p7w8-78e71f2b`,ID-P7-63)**:门 3 **PASS 36/36**(33 例四臂逐位同;三例 Iris 按 ID-P7-62 分布判据通过;OpenRA 四臂 1.000000)。
**7.3 门 5。** 块与 caselist(`tools/cts/caselists/`,取自 VK-GL-CTS `0b04c470e`,即设备上 glcts 的源):`KHR-GL46.texture_*`(1055)、`KHR-GL46.shader_image*`(125)、`KHR-GL46.packed_pixels`(4732)、`KHR-GL46.direct_state_access.*`(371)、**UBO 在 KHR 包里没有专属组**——取 GTF 模块 `GTF-GL46.gtf31.GL3Tests.uniform_buffer_object*`(90,设备 glcts 若未含 GTF 则该块记 unrun)、另加 `KHR-GL46.shader_storage_buffer_object`(124,簇 A 的 unaligned / native-range 拒绝实际打的是 SSBO range)。`KHR-GL46` 在 Android 跑器上未证实可开 4.6 core context:窗口先探针;不行则 `$BASE` 与 AFTER 都用 `gl33` 变体(texture 868 / packed_pixels 4732 / ubo 90;GL 3.3 无 shader image、无 DSA),**同一 API 版本比 0.5 pp**。先 monolith × DirectVulkan 基线入库 `tools/cts/baselines/`,迁移落地后 inproc × DirectVulkan 比对:每块 pass 率差 ≤ 0.5 pp(Pass/(Pass+Fail),NS 不入分母),**新增 crash = 0 单独硬红**。完整 caselist(~56k)在合 `dev` 前跑,不在关键路径。
---
## §8 子系统分母与 40% 检查点
分母 9 项:(1) Magma 两进程车道 gating 三臂同数绿;(2) §3.2 的六个「退役」;(3) `StateObjectDeathOps`;(4) 烘焙 (A)(D);(5) OQ-8;(6) OQ-10;(7) verify×split(**wave 3 V1 已完成**,`notes/p7/verify-split.md`;中点检查点那次计数时它还未成,两句说的是不同时刻);(8) 真机 ssim 1.0;(9) CTS 五块 ≤0.5 pp。**中点** = wave 2 的 A / B / C 三簇全部合入集成树之时;届时完成 < 4/9 → 停下重定基线(路线图唯一幸存的再基线绊线,此前没有分母、不可执行)。**检查点已过(2026-09-22,B2 落地时):6/9 成**——(1) 三臂 93/72/74 绿、(2) 六退役全成(ID-P7-31)、(3)(5)(6) 随 C、(4) 随 B;未成:(7) verify×split(wave 3)、(8) 真机(OpenRA 归 B3、bsl-esc-menu 归 M1)、(9) CTS AFTER(wave 4)。不触发重定基线。
---
## §9 wave 2 的文件分区与合并规则
| 簇 | 文件 | 内容 |
|---|---|---|
| A | `Renderer/UniformManager.cpp` | §3.2 的 UBO / SSBO / texel 对齐与 native-range 行 |
| B | `Renderer/WireFramebuffer.inc`、`Renderer/VulkanRenderer.cpp`、新 `WireDepthMipmap.*` | copy-image、default-color-blit、multisample、深度 mip 烘焙 (A)(D)、(B')、AllocatorDebtScope |
| C | `Renderer/VertexInputStateFactory.cpp`、`Renderer/WireDraw.inc`、`Renderer/VkBufferManager.cpp`、`MG_Backend/Init.cpp`、`Renderer/MagmaProgramSource.h`、`MG_State/.../ProgramArtifacts.h` | vertex-layout 拆分、`StateObjectDeathOps`、OQ-10、OQ-8 |
| D | `MG_Backend/DirectGLES/*`、`MG_State/*`、`MG_Impl/Pipe/*`、`MG_IntegrationTest/*` | P3b/P4b 的 Espryt 项(与 A/B/C 零重叠) |
| E | `docs/`、契约 | 已落地项更正、OQ 关闭、`MEASUREMENTS.md` §7.2 同名重跑 |
| F | `MG_Remote/Transport/*`、`MG_Remote/Server/ServerMain.cpp`、`Handshake.h`、`Protocol/protocol.fbs` | Ph 小件(令牌、D11 五处、PH-8 单测) |
合并规则:集成者串行合并;每次合并后 gating 三臂 + `link_ratchet.py --assert-monotone` + G1 + parity;**棘轮上升的提交拒收**;每包一条 red-once 记在 `notes/p7/`;性能只记录不设门(ID-P7-9)。实现方 `opus`、审查方 `fable`(ID-P7-8)。
---
## §10 对前序契约的更正
1. `CONTRACT-P6.md:666`(§10.3)把 `DynamicBackendParameters` 定宽记在 P7 名下——已由 P6.5 wf 落地,P7 不再持有。
2. `CONTRACT-P6.md` §12.1「棘轮在 CI 里重算 184 并断言只降」——由 wave 0 包 R 落地为 `scripts/link_ratchet.py`;「移交 P6.5」从未被 P6.5 接收,归 P7。
3. `CONTRACT-P6.md` §12.2 D11 把五行中的三行分给 P6 `dl`——树上五处全部仍在原状态(`PipeApply.cpp:1575`、`PipeApplier.cpp:444`、`ProgramArtifactsCodec.cpp:309`、`StagedTextureStore.h:440`、`SlotTables.h:347/:371`);归 Ph(计划 §1.1 D11),wave 2-F 做。**wave 2-F 实况(`notes/p7/ph-f.md`)**:落地的是 PH-7 (1)–(4)(常量时间令牌合一、≥16 字节、无令牌只 loopback、`Welcome.dataNonce` 绑定数据连接——`protocol.fbs` 变、`wireFingerprint` 随 `MOBILEGL_PROTOCOL_CONTROL_REVISION` 变)与 PH-8 单测;**在本条写作时点,D11 五处与 PH-2 仍未落地(状态后来由 ID-P7-54 更新)**:每处都要一条字节来自对端的两进程负控,而树上没有能越过 client 自身守卫伪造记录的对端驱动(= fuzz 臂 2 的骨架),按计划停止规则未开工。
4. `CONTRACT-P6.md` §5.2「`Session::Fail` 在 P6 保持 `[[noreturn]]`」——Ph 的翻转按 ID-P7-1 重定界,不做 98 站点的字面翻转;`Server/StagedShadow.h:115`、`Server/StagedTextureStore.h:344` 两处旁路由 wave 0 包 C 收进漏斗。
5. `ARCHITECTURE.md` §8.5「XFB scatter 在 client」——树上取的是 server staged shadow + `OnBufferWriteback`(P5c/P5f);`OnXfbScatterReady` 是死声明,删除。
6. **PH-4 mutable texture-level bounds (2026-09-23, F2 commits `2c6a6e25` / `51480f8c`).** A named `ResourceRespecify` carries exact per-level W/H/D alongside `(uploadTarget, level)`; on split the dimension words reuse `MGPResourceDesc.BufOffset/BufSize` only for non-buffer named image levels, and `WireFingerprint` mixes the extent-carrier revision. The applier stores the exact accepted extent, clears the transient range carrier from its descriptor copy, and passes the extent/target/fallback internal format into `StagedTextureStore.NoteLevelDefined`. The store derives its byte bound under server device limits before uploads; every `ResourceSubData` slab must match that saved extent and stay within the derived byte bound. TexBuffer and multisample targets have no legal subimage producer and staged byte records for them are named `ProtocolCorruption`. **PH-5** charges `sizeof(value_type)` for a vector count against `ReadCursor::Remaining()` before resize, using division to avoid count multiplication overflow. **PH-2** sends out-of-range and backwards-generation handle refusals through `MGPipeSessionFail` at reachable DirectGLES adoption guards; the slot table keeps the same named refusal as a backstop. Luna integration review = land; 49 targeted cases pass, including raw-peer extent, MS, slot and stale-generation controls; strict pull G1 against `adf2cbe0` is byte-identical (`.text=0xa52203`, no symbol changes). |
7. **PH-1 (3)(4) 每会话闩(F2 latch 包,2026-09-23,ID-P7-1)——对 `CONTRACT-P5.md` R-2 与 `CONTRACT-P6.md` §5.2 的更正。** 在 **spawn / TCP 会话子进程**里(`ServerMain::RunSession` 调 `ArmSessionLatch()`,全树唯一的武装点),输入字节来自对端、且已在 `Bool` / `MobileGLResult` 函数里的站点(R-2 的诚实臂与解码边界、`PipeApplier.cpp` 七处、`ServerLoop.cpp` 四处、`SurfaceOpCodec.cpp` 三处,逐站表见 `scripts/ci/ph_latch_sites.py --table`)不再 abort:`SessionLatch` 记同一行 `Fatal{Family, "word"}`、发同一个 SessionFault 帧(只发第一个),记下**第一个**故障后返回 false;`DrainRing` 在每次 pop 之前查一次闩(codex closeout finding 6 把控制线程闩的窗口缩窄到「查闩到 pop」,没有关闭:落在其间的闩再放过已放行的那一条,会话在下一次检查结束),apply 线程离开循环,控制泵与 `ServerApplyWireSurfaceOp` 对闩后的控制帧回 `PROTOCOL_MISMATCH` 不派发,`RunSession` 的控制等待按 100 ms 切片、见闩即关会话,以 **`kSessionLatchedExitCode = 75`** 退出;supervisor 的 reap 行记 `exit=75 (latched fault)` 并计入 `sessionsFaulted`,照常服务下一连接。所以 R-2 的「Fatal{ProtocolCorruption}」在会话子进程里读作「闩 + exit 75」。**inproc 保持 Fatal**:client 与 server 同进程,闩住而存活的 server 会让同进程 client 的 GL 线程停在屏障上、没有 EOF 可醒,也没有 supervisor 去服务「下一连接」——进程就是会话;client 从不武装。未武装时 `SessionLatch` 逐字节等于 `SessionFail`,死亡测试全部保持。负控:`MG_Test/Wire/PeerLatchTest.cpp`(每站一行 raw-peer,外加批内闩与静默对端两例)与 `ServerLoopTest.cpp` 的 `ServerLoopLatchTest.*`;`ph_latch_sites.py` 的 MECHANICS 表把每一道「拒绝其余工作 / 关会话」的检查对到它的负控。
---
## §11 不是 P7 的
- `SetupDrawSnapshot` dirty mask、`VertexInputStateFactory` 内容寻址 CSO(ID-P7-5)→ 优化阶段。
- 内部 shader 烘焙的 pull 构建 monolith 臂退役(动 pull `.text`)、直通 TCS 烘焙(64 模块)→ P13 / OQ-7 宽半。
- `MG_Util` 头/对象库拆分 → P13。
- Ph 的 fuzz 臂 2(对端字节可达站点逐站点闩转换)、PH-6、PH-7 (4)(5) → P7 之后(计划 wave 5)。
- P3b/P4b 的 CTS AFTER → 该阶段。Iris 普查的 P9 例外(`texture-remint-pull`):wave 3 包 E1 在 `3c80cd62` 的主机普查里**为空**——77 行 × {monolith, inproc, spawn} 231 次全活,该站点一次未到达(`docs/Disaggregated/notes/p7/iris-census-3c80cd62.md` §4);设备侧读数留 wave 4。
---
## §12 记录债
- **F2 latch(PH-1 (3)(4)、fuzz 臂 2,§10 第 7 条)留下的记录债**:(1) **第二批对端可达站点仍是 Fatal**——`PipeApplier.cpp` 的 `WireProtocolFatal`:`OnFenceCreate` / `Destroy` / `Status` / `Wait` / `WaitServer` 的 `Fence.*` 七处、`OnClear` 的 `Clear.Fbo` / `MGPClear::ValueClass` / `MGPClear::Kind`、`OnGetTextureImage` 的 `.extent` / `.range`(`.reply` 是 server 内部不变量,不算)、`OnDrawVbo` 的 `MGPDrawInfo::IndexSize` 与 `DrawVbo.userIndices`、`OnDeleteStreamOutput`;`QueryServer.inc` 的 `Query*` 十一处(`OnQueryCreate` / `Begin` / `End` / `Counter` / `Available` / `Result` / `Destroy` / `Timestamp`)。它们**都在读对端字节的 `Bool On*` 动词里**,满足 PH-1 (3) 的判据,只是不在计划点名的「`PipeApplier.cpp` 七处」里;只有 `FindFence`(`PipeApplier.cpp:115`)与 `FindQuery`(`QueryServer.inc:2`)返回引用、无诚实拒绝。今天它们 abort(signal 6),supervisor 照常服务下一连接;机械转换 + 每站一行 raw-peer,归 Ph。(2) **PH-2 仍是 Fatal**:`SlotTables.h`、`Managers.cpp`、`Managers.h` 的 `BackendSlotTable.{HandleSlot,Generation}` 八处走 `MGPipeSessionFail`;它们全在 `#if MOBILEGL_BUILD_DISAGGREGATED` 里,改闩**不动** pull `.text`。没改的原因:`MG_Backend` 到漏斗只有单向的 `MGPipeSessionFail` 缝,改闩要一条新的 `MGPipeSessionLatch` 缝,外加证明每个调用者拿到 null twin 后不会在同一记录里撞上另一处 abort——pull 臂的 `return m_nullTwin` / `return nullptr` 是从没在 split 下审过的 release 兜底。八处标 `@Ph-declined (ID-P7-1)`,`PeerLatchTest` 的两行 D11 slot 负控断言 `signal=6` + 具名首行 + 下一会话照常渲染。(3) 生成门 `MGPipeWireProtocolFatal`(`MG_Pipe/generated/PipeWire.inc`)与 `MG_Pipe` 里的 D11 钩(`PipeApply.cpp` 的 CSO slot、respecify 载体)未武装时照旧 abort;武装的会话子进程由 `AdmitsTheGeneratedGate` 先问生成门的两个问题(opcode、最短长度),`PipeApplier::ApplyOne` 在打 verb 戳、读 `MGPipeBarriered` 之前就先问。(4) 返回引用的 9 处 `SessionFail`(`ServerSession.cpp` 七处、`Client/WireTables.cpp:118`、`Client/EmitTables.cpp:212`)标 `@Ph-declined (ID-P7-1)`,保持 Fatal。(5) `ProgramArtifactsCodecTest.AVectorCountCannotReservePastTheRemainingArchiveBytes` 把 PH-5 的界退回旧的按字节界照样绿(计数只比 PH-5 的界多 1,元素读取先把字节读完);有判别力的是 `ProgramArtifactsCodecTest.ACountOnlyTheByteBoundAdmitsIsRefusedBeforeItsResize` 与 `PeerLatchTest` 的 `D11ArchiveVectorCount`(在会话子进程的 `RLIMIT_AS` 之下,旧界的 resize 以 `std::bad_alloc` 死)。
- ~~Magma 两进程 client 在 `StateObjectDeathOps` 落地前 `ObjectDeaths=0`:`CtWireScenario` 死亡用例在 Magma 臂按名 skip(§2.3)。~~ **已退(wave 2-C,`dce9953d`)**:两处 skip 守卫删除,死亡用例在三臂全绿;泄漏的是 **server 的 twin**(framebuffer 无 wire delete opcode,用例现在两边计数都读)。
- §5.4 的「白盒断言发出的是 `BufferSubDataResident(49)`」在本树**不可达**:resident 发射需要 GPU 常驻存储 → 需要 `AdoptPersistentMap` → split 下 T2 对每次采纳都 decline(`PipeApply.cpp:2331`),T0/T1 归 P11。本阶段证明到「server 按自己的表发布该位、client 镜像在双后端 × 三传输收到」为止;`rsd=` PipeStats 计数器在 P11 落地采纳档后会显示这些记录。
- §5.3 的 `g_programResourceCaches` 删除只在 disaggregated 构建:monolith 消费者已改读归档(red-once 在 monolith 臂也红),但归档成员必须 `#if MOBILEGL_BUILD_DISAGGREGATED` 才能守 G1,pull 构建保留旧缓存——钉子挪动那天一行改完,**P13**。
- 门 3 的「不确定」在 wave 1 E0 之前无法区分「同 case 多次结果不同」与「不同 case 各自低于阈值」;两个历史点值(0.988998 / 0.976494)无 per-case 归属。**已归属(W4,ID-P7-25)**:两个点值都是 OpenRA 同一帧里被静默丢掉的一段 draw(9836 / 14658 像素),run-ahead 与 spawn 臂都掷硬币;修复归 B3。
- ~~`TextureViewAliasScenario.TheOwnerUploadDrainRunsAgainAfterADrawBoundary` 在三条 split 臂按名排除~~ **已退(wave 2-D3,`1e1f4bad`)**:split 的 clean gate 改读 storage record(`IsDrawSyncCleanByRecord` 走 `Desc.ViewOf` 到 owner,与 Magma 的 `ResolveWireTextureStorage` 同形),排除删除,三臂 3 红 → 14/14;P5E §5.2 改写。留下的:该场景的 Magma 臂只在 informational 层;`KHR-GL46.texture_view.coherency` 无 split 读数(门 5)。
- **OpenRA 调查裁判点名的 Magma 债(`notes/p7/device-window-1/W4-verify/README.md` §3b、`~/w7/notes/evidence/w4/judge.json`),B3 不动、记在这里**:`TryDrainFrameTransients` 的 wire-only 块 `CollectWireObjects(m_completedSubmitCounter, /*all=*/true)`(`VulkanRenderer.cpp:13526`)无视 `RetireWireObjects` 给未来对象打的 `submitIndex = m_submitCounter + 1` 标签(`WireDraw.inc:113-118, 175-194`);`HasPendingRecordedWork`(`:13435-13441`)看不见 pre-pass 命令流;`FlushWirePendingCommandsForTextureUpdate = !HasPendingRecordedWork() || FlushPendingCommands()`(`VulkanRenderer.h:148-152`)把「此刻没在录」当「没在飞」;`StagedTextureStore::AdoptRun` 在 extent 变化时不重置 `Bytes`/`Covered`(`StagedTextureStore.h:234-243` vs `:270-284`,一条单测即红);`FrameContext::WaitAndAcquireNextImage`(`FrameContext.cpp:303-312`)只凭槽 fence 释放中帧 flush 过的命令缓冲。本 trace 都不可达,但都是真债。
- **bsl-esc-menu-854 在 wire 臂上是内存缺陷不是排除项**(W4 §2/§3c):monolith 807 MiB 峰值通过;spawn 下 server 进程涨到 ~1.2 GiB 在 scudo 二级分配器 `internal map failure (Out of memory)` 死(系统尚余 8 GB → 是映射数或每进程上限,不是系统 OOM),client 同时 543 MiB;inproc 1.6 GiB 也死。**M1 已归因(主机 lavapipe,三臂同图)**:不是 store 的字节复制,是 split server **无界累积死 `VkBuffer`**——`RespecifyWireBuffer`(`VkBufferManager.cpp:189-224`)对每个 `glBufferData` 无条件 `DeferRelease` 进按帧槽分桶的 `m_deferredBufferReleases`,而桶只在 `BeginFrame`/`CollectAllDeferredReleases` 扫(`TryDrainFrameTransients` 与 present 路径),**pbuffer 回放整跑 server 只收到 1 条 present 记录**、drain 约每分钟成功两次 → 25 935 个活 VkBuffer(25 923 死)对 monolith 7 个,lavapipe 上每个一条 `memfd` 映射(27.4k → 41k),+12.2 MiB / 7.2 s,与真机 server 527 → 1162 MB 的爬升同量;轻例 `iris-bsl-in-world` 同形 1000×小(93 活 / 74 死)——随帧界之间的调用数增长,不是结构性。`StagedTextureStore` 是每层第二份拷贝(43 MiB)但平台;`PendingUploads` 0 字节(与 B3 无关);monolith 式条件 orphan 无效(6701 vs 6702)。附带:`CollectGarbage()`/`pruneWire`(`VulkanRenderer.cpp:6989`)在 `SetupDraw` 转 `SetupWireDraw` 之后、wire 臂从不执行。修复包 **M2**(`VkBufferManager.{h,cpp}`:按 serial 的 fence-gated 回收 + `MOBILEGL_IPC_WIRE_DEFERRED_MB` 水位线;red-once = 单帧内 N 次 respecify 后活 VkBuffer ≤ k×wire buffers)。**未答**:真机上是 `vm.max_map_count` 还是字节(采样器没记 maps 行数;Adreno 的 VMA 256 MiB 块推测是字节)。**「只有一条 present」已答(P-present 调查)**:不是 pbuffer、不是 client 决定——该 fixture 1 303 535 次调用里只有 **2 次 `eglSwapBuffers`**(call 1 301 789 与目标 call 1 303 534),harness 在目标 swap 上先快照后 `exit`(apitrace `retrace_main.cpp` 的 `frameComplete` → `mobilegl_apitrace_exit` 抛出(`tools/trace_replay/CMakeLists.txt:186-189` 的宏替换),在 apitrace `glretrace_egl.cpp` 的 `frame_complete` 里、`drawable->swapBuffers()` 之前解栈),所以到达 MobileGL 的 swap = 1、present 记录 = 1、`OnPresent` = 1;规则 `presents = (swaps with call_no ≤ target) − 1`(OpenRA 30 → 29,B3 测得 33 次 `BeginFrame` = 29 present + 起会 + drain)。client 的 `SwapBuffers → BackendObject::SwapEGLBuffers → Present()` 链无任何 surface-kind 分支,credit 只停不丢。结论:**server 侧任何挂在 present 上的回收都不能假设一帧有界**(一帧可以是 1.3 M 调用的 Iris 换包重编译),M2 的按 serial 回收正是这个形状;`TryDrainFrameTransients` 的两道闸 + 每 8 次的节流不是替代品。附带债:`BackendObject.cpp:385-388` 单活动面守卫对多 surface 的 client 会静默丢 present(本 trace 不触发)——该给 `MGLOG_E_ONCE`/计数。
- ~~`MEASUREMENTS.md` §7.2 的同名比对自 P5b 起未重跑;「22 → texture readback」「3 → query」两行债务的关闭以 wave 3 的重跑为准。~~ **已退(wave 3 包 E1,`3c80cd62`)**:同名重跑 27 条 inproc 1 passed / 26 failed → 24 passed / 3 aborted / 0 failed(spawn 同)。22 条 readback 中 19 绿;3 条是 `MOBILEGL_PIPE_PUSH=0` 对照臂的具名停止(`PipeLegacyMemosDisabled` / `Magma:clear-framebuffer-record`),不是错答;3 条 query 全绿(`MEASUREMENTS.md` §7.2 末)。
- **spawn 上的 verify 需要一个跨进程的比对形状**(wave 3 V1,`notes/p7/verify-split.md` §3.3 / §6):比对器是同地址空间的仪器,两臂是 applier 写的 `gPipeInputs` 与前端 `GLContext`。`MobileGLServer` 链的是整个库,所以 `MGPipeVerifyReadHook` 在 server 进程里**在**、也被调用;缺的是**预言**——server 的 main 只走到 `MG_Backend::InitServerRoleForSpawn()`,不调 `MG_State::Init()`,`MG_State::pGLContext` 为空,hook 在 `ctx == nullptr` 处返回。补它 = server 把读到的字段值随回复带回由 client 比(或 server 侧立等价镜像),不是注册也不是链接问题。门 2 的文本是 `{monolith, split(inproc)}`,不含 spawn,所以这是记录债而不是门内项。
- Android 模拟器 retrace 车道每轮 2–6 例 flaky(含 monolith 对照):门 1 的任何模拟器证据都先过「连续 N 轮绿」再算数。
- **X1 之后的两条残余**(`notes/p7/x1-persistent-map-segv.md`):(a) 首次 push 后页面在映射存活期间重新武装,一个**屏蔽了 SIGSEGV 的非 GL 线程写者**仍会被杀——彻底关闭要把武装改成逐 buffer 可选,是设计改动;(b) `RefaultOfANonWriteAccess` 仍可能拒掉同 `(tid, pc, address)` 的一次合法重复写——延后武装后不再常见,未观察到,记录。
- **P5d 的 Adreno 数字存疑**:X1 证明在 arm64 上注册总是加保护、handler 总是不认领(bionic 指针标记 0xb4 与内核的 `si_addr` 比较),即 mprotect 臂在 Android 上**从未**回答过一次 fault;P5d 报告里把收益记在 mprotect 臂上的设备测量需要重取(`P5D-INPROC-PERFORMANCE.md`)。归优化阶段,不阻塞 P7。
- **B2 返工留下的记录债(`notes/p7/magma-b2.md` §6,ID-P7-35/37)**:(1) 多重采样颜色 resolve 的第二段 `vkCmdBlitImage` 不裁剪目的矩形(越界 = `VUID-vkCmdBlitImage-dstOffset-00248/00249`),与 monolith 臂共有,两臂一起裁是独立的活;(2) `MagmaWireFatal("multisample-resolve-region")` 对源矩形越出读缓冲的 blit 仍带走会话——GL 说那些像素未定义而非错误,应 decline 或钳制;(3) 1 → N 等矩形 decline 拒绝的是合法形状(18.3.1 样本复制),Vulkan 无对应命令、monolith 的可观测本身是 VU 违规;(4) tcp 车道带不了 server 端测试旋钮——六条 knob 条目只在 split + spawn,`spawn_lane_parity.py` 的 `MAGMA_SERVER_ENV_KNOB_NO_TCP` 具名;收回要 `tcp_server_fixture.py` 接受每用例 server 环境;(5) X 镜像与跨格式 Y 镜像的深 / 模板 resolve 以规则 I(a) 具名 decline(合法 blit);跨格式那条在 renderbuffer 与 texture 的 D24S8 映射到不同 `VkFormat` 的设备上会让 `MsFlip.` 变红;(6) **monolith `DirectVulkan` 臂的翻转 / 缩放多重采样深度 blit**:翻转被拒(目的地保持清屏值、无错误)、缩放被执行(GL 说 `INVALID_OPERATION`)——wire 与 monolith 的分歧,wire 臂是更正确的一方,monolith 修正归 P13 / G1;(7) **Espryt(DirectGLES)的翻转 / 缩放多重采样深度 resolve**:翻转什么也不写且不报错、缩放不报错——P3b / P4b 债;用例在这两类条目上以具名理由 `Skipped`。
- **B3 / B4 记录债(ID-P7-36/37/51)**:B3 指出的 WaitForFrameSerial 首 fence 假成功、pre-pass pending、FlushWirePendingCommandsForTextureUpdate 单 fence 假设、Present 槽回收前缺完整提交等待和 AdoptRun extent 覆盖,已由 B4 在 pipe b19297d5 修复并通过完整门;集成者 review land 见 notes/handoff/review-b4.md。
- **V1 留下的记录债(`notes/p7/verify-split.md` §6,ID-P7-37)**:verify 在 spawn 上需要一个跨进程的比对形状(server 把读到的字段值随回复带回、client 比),门 2 的文本是 `{monolith, split(inproc)}`;server `ReadPixels` 窗口里 pack 半边的字段级盲区很窄(只有恰等于中性 pack 的错推送不可见);DirectGLES × split 上四条 Magma decline 契约用例按名从 verify-split 车道减掉——Espryt 是否 decline 这些形状归 P3b / P4b。
- **F 片 1–3 之后(`notes/p7/ph-f.md` §4,ID-P7-37)**:`wireFingerprint` 随 `Welcome.dataNonce` / `DataBind` 变更(`0x7d4470f241d23539` → `0x03c4c293a54b3e08`),新 server 对旧 client 回 `Refuse{WireFingerprint}`,旧 server 对新 client **没有 Refuse 帧**(旧 `AcceptPair` 等第二条连接 2000 ms 后关闭,client 只见 `no Welcome … (rc=6)`)——设备上的 server 必须随下一个 APK 重部署;D11 五处 + PH-2、PH-6、PH-1 (3)(4)、PH-7 (5)(fork 前认证;**已由包 f2-auth 做,见本节末条**)未做,前置是一个能向 spawn / TCP server 投递畸形记录的对端字节驱动(fuzz 臂 2 的第一块),包 **F2**。
- **F 修复轮之后的两条小债(集成者审查,ID-P7-44)**:`ServerMain.cpp` `RunSession` 在 `Accept()` 返回到 `close(sourceFd)` 之间,supervisor 已转发的一条 DataBind 随 child 队列关闭而得不到具名拒绝(微秒级窗口;**已关,包 f2-auth**:child 先 `shutdown(SHUT_RD)` 再收尽已排队的描述符逐条具名拒绝,Accept 之前的每条退出路径经 `ExitBeforeAccept` 同样处理,只剩 child 崩溃仍会丢);`protocol_revision_pin.py` 对 `protocol.fbs` 的注释 / 空白改动同样要求 bump(严格、有意)。
- **M2 r2 的代价(集成者审查,ID-P7-46)**:wire store 的 destroy epoch 进了 `UniformManager` 的两个 descriptor memo,所以每次销毁事件之后同 program 的第一笔 draw 多一次 descriptor set 分配 + 写(bsl 整跑 ~2.6 万次销毁 / 130 万次调用),并把 §6.1 的长帧内 descriptor set 增长每事件多推一个 set——这是 M3 的分母,不是新债;`ServerSpawn.cpp` 放行名单(`WIRE_DEFERRED_MB` / `SPIN_US` / `SERVER_AFFINITY` / `AUDIT`)之外的 `MOBILEGL_IPC_*` 仍被清洗,都是协议形状(双方必须一致)或 client 读的值,不记债。
- **M3 长帧 descriptor 游标(ID-P7-50)**:M2 r2 上述额外 descriptor 写入的增长已由 M3 的 2048 Acquire 边界回卷约束;证明在 graphics queue idle 后才重写 set,避免依赖 B4 尚未修的单 fence 退休。主机 bsl 确有一次回卷(缓存总数 3555 是多个 layout 的历史集合,2048 是单 epoch Acquire 上限,不是进程全局缓存上限)。queue idle stall 是性能记录项;M3 后 APK 的真机回归仍待取。
- **Ph 簇 F 的两处预认证暴露**(**(a)(b) 已由包 f2-auth 关,见本节末条;本地对端债与指纹不对称仍在**)(wave 2-F 之前就在树上,F 修复轮审查核实,归 PH-7 (5)「有界的预认证工作 + 失败退避」,P7 之后):(a) TCP supervisor 对每条连接**先 fork 再读 Hello**(`ServerMain.cpp` `RunSession`),未认证对端每连接可让 server 付出一次 fork + 最长 10 s 的 Hello 等待;(b) `ValidatePeerHandshake` 在 `AuthenticatePeerToken` **之前**跑,`Refuse{ProtocolVersion|WireFingerprint|BuildFingerprint}` 把本端 wire 指纹与 build stamp 送给了未认证的对端。同一轮记下的本地对端债:unix 端点仍按到达顺序配对(`AcceptPair` 250 ms / 2000 ms 窗口),抽象命名空间套接字没有 mode bits,「只有本 app 能连」靠 SELinux 域隔离而非传输层。**指纹不对称**(§10 第 3 条的 `wireFingerprint` 变化):新 server 对旧 client 回 `Refuse{WireFingerprint}`;旧 server 对新 client **没有任何 Refuse 帧**——旧 `AcceptPair` 等第二条连接 2000 ms 后关掉两条,client 只看到 `no Welcome ... (rc=6)`,即设备上的 server 未随本次构建重装。细节 `notes/p7/ph-f.md` §2.2 / §6.4。
- **门 4 的 `p7-magma` 余 86 = `# P13`(ratchet88,§4.1)**:全在 disaggregated 构建的缺省传输 monolith 臂上(CI monolith-control 与门 3 参照臂在用),不动那条臂就消不掉;P13 删 `MGB_CTX`、建 server 镜像边界时随 monolith 臂离开 server 对象。四组:
- 占位纹理 62(`UniformManager.cpp.o`):`TextureObject{1D,2D,2DCube,3D}` ctor、`TextureObjectBase` ctor/dtor、`TextureObjectWithOneMipmap` vtable/typeinfo、`TextureObjectBase::*` 32 + `TextureObjectWithOneMipmap::*` 20 个虚函数、`MGPipeNoteAggregate` / `TextureObjectBase::PipePublishParams`;唯一不等 P13 的路是让 monolith 臂改用 wire 原生占位(`WirePlaceholderImages.inc`),但那会改参照臂的可观测(未绑 1D/3D/立方/数组采样器由丢 draw 变成画、shadow 占位 RGBA8 → D16、MS 占位由未定义变成 (0,0,0,1)),须单独裁定 + 设备窗口。
- 采样器 14:`SamplerObject::Get{BorderColor,BorderColorI,BorderColorUI,CompareMode,MagFilter,MaxAnisotropy,MaxLod,MinFilter,MinLod,MipmapMode,SamplerCompareFunc,WrapR,WrapS,WrapT}`(`VkSamplerManager` 的 `<SamplerObject>` 实例化;5 个另由 `UniformManager::ProgramSamplesOnlySingleLevelTextures`)。
- 对象读 6:`FramebufferAttachmentObject::{GetSize,IsComplete,IsValid}`、`RenderbufferObject::IsAllocated`、`VertexArrayObject::GetIndexBufferBindingSlot`、`BufferObject::EnsureGpuResidentStorage`(monolith render pass / clear / blit / 回读 / 索引 / SSBO 与 XFB)。
- `MGB_CTX` 访问 4:`PipeInputs::{GetTextureObject,HasOpenTransformFeedbackSpan,InvalidateCompileEnv}`、`GLImpl::CopyTextureImageToClientOrPBO_State`。
- **PH-6 / fuzz 臂 3 的记录债(包 f2-event,ID-P7-2;细节 `~/w7/logs/f2-event/NOTE.md` §5)**:(1) **被丢事件所属的那条记录照常完成**:它仍贴 `kStatusOk` 回复并推进 `appliedSeq`(`ServerLoop.cpp` `DrainRing` 在该记录之后才停),而 `Client/` 里没有任何代码读 `eventDropped`(`EventRingConsumer::DroppedEvents()` 只有测试在读)——一个先发不等的 readback、事后才等其回复的 client,可能在看到 EOF 之前读到缺那次 writeback 的影子。只有不等待的 client 走得到(等待中的 client 在等待里就排空了)。修法候选是让触发 forfeit 的记录回复失败,或 client 在 `eventDropped != 0` 时闩 device-lost;两者都动 client 的等待 / 回复契约,未做。(2) **inproc 不被告知**:forfeit 停掉 apply 线程,同进程的 client 收不到任何信号。这不是「只有测试能到达」(初版 NOTE 的说法不对):(3) 的场景在 inproc 上同样可达,之后 client 的下一次等待在 120 s 后以 `Fatal{BarrierTimeout}` 死——诊断名字是错的,真因是 forfeit(审查指出,本包未测)。(3) **默认 2000 ms 未在设备上测过**:合法 client 停调 GL 超过旋钮(如 Android 退后台)且队里有一环以上未等待的事件(XFB writeback)会丢会话;此前是 30 s 后 Fatal。(4) **lockstep 臂不变**:无 `kCapRunAheadApply` 的 server(Magma)满环仍是 P5C 的 `Fatal{EventRingOverflow}`,不排空的对端让会话 SIGABRT(计入 `sessionsFaulted`,下一连接照常服务);fuzz 臂 3 没有 Magma 臂。(5) **对端不被告知原因**:没有 forfeit 的 wire 词,client 只见 EOF → device-lost;加 `Refuse` / `Fatal` 码会改 `wireFingerprint`。(6) **shm 的 `eventRingFull` 在对端可写的页上**:持续写 0 的对端让保留循环不 park 地自旋,直到旋钮耗尽后以 `TooSlow` forfeit 或会话被 Stop(有界,只记)。(7) **stream 面的阻塞 `sendmsg`**(`StreamLink.cpp:158`,apply 线程经 `StreamLink::Flush` 同步 `SendEvents`)**不是无界的**:数据套接字带 `TCP_USER_TIMEOUT=5000`(`SocketTransport.cpp:99-112`)。实测(临时探针,未入库):对端 SIGSTOP、旋钮 8000,stream 面 5327 ms 时链路被内核判死 → `ReverseChannelForfeit{PeerGone}`、exit 0;shm 面等满 8006 ms → `NotDraining`、exit 0。即 stream 面上一个冻住(活着但不 ACK)的对端约 5 s 被叫作 PeerGone,与旋钮无关;apply 线程恰好卡在 send 里时控制流又结束,`Stop()` 的 5000 ms join 与这 5 s 赛跑——未构造出,记录。(8) forfeit 后队列里剩下的记录不再应用(有意);`RunSession` 每会话每 100 ms 轮询一次控制套接字(包内原为 250 ms;与 PH-1 (3) 闩的控制切片在 p7/f2-all 合并为一个 100 ms 切片,每轮先问闩、再问 apply 线程),且每一轮都查 apply 线程(记录,不是门)。(9) 排在 PH-6 等待后面的 surface op:server 的控制线程阻塞在 apply 线程的邮箱里(P5e 的等待谓词不含控制请求,本包未改),对端活着但只是不排空时,这个 op 要等旋钮耗尽才被回答;对端死亡则由数据 bell 报告(shm 面靠 server bell 挂在控制套接字上的死亡见证,镜像 CONTRACT-P6 D5c)。(10) **server bell 的死亡见证改变了 shm 会话的正常收尾顺序**:client 的 `ShutdownSend`(`ClientSession::Stop` 第 2 步)现在可能先让 apply 线程从 park 里以 dead bell 退出、再由控制循环读到 EOF;两条路走同一个退出块,每次 shm 会话正常收尾 server 日志多一行 INFO `the peer hung up (witness fd …, revents=0x2000)`(其措辞是为 client 侧写的)。
- **PH-7 (5) + fuzz 臂 1 + ID-P7-44(包 f2-auth,分支 `p7/f2-auth`,含 fix round)关掉的与留下的**。关掉:TCP `--serve` supervisor 在 fork 前读首帧、先验令牌(未认证对端只得 `Refuse{Authentication}`,无指纹 / stamp,无 fork);首帧期限 `MOBILEGL_IPC_PREAUTH_MS`(默认 2000);待认证连接上限 `MOBILEGL_IPC_PREAUTH_MAX`(默认 8,钳到 256),满时按地址公平让位(新连接顶掉占位最多地址的最老一条,`Refuse{Busy}` "displaced ...");失败退避 `MOBILEGL_IPC_AUTH_BACKOFF_AFTER`(默认 5,0 关)/ `MOBILEGL_IPC_AUTH_BACKOFF_MS`(默认 1000,逐次翻倍,上限 60 s,认证成功清零,10 分钟遗忘,最多跟踪 256 个地址),失败 = 错令牌、畸形首帧、超时、静默占位超过 250 ms 后关闭或被顶掉;accept 遇 EMFILE/ENFILE/ENOBUFS/ENOMEM 暂停 100 ms 而非退出 73;`RunSession` 自读首帧(单会话 TCP、unix 端点)每条出路具名,令牌先于 `ValidatePeerHandshake`;ID-P7-44 见上。无 schema 变化,`MOBILEGL_PROTOCOL_CONTROL_REVISION` 与 pin 不动;**对端可见的行为变化要设备 server 随下一个 APK 重部署**:未认证的错指纹 Hello 得 Authentication 而非 WireFingerprint;静默连接 2 s 后得 Authentication(原来是 fork + 10 s 后 exit 67 且计入 faulted);会话忙时静默的第二条连接得期限拒绝而非 Busy;满队时的让位拒绝。**留下的记录债**:(1) unix 端点仍对每个 `AcceptPair` 先 fork 再读 Hello(本地对端债,不在远端威胁模型内;只补了具名拒绝);(2) 退避按对端地址计,NAT / `adb forward` 回环后的对端共用一个键,同址合法 client 会被连坐;轮换地址(如 IPv6)只受上限、期限与按地址让位约束;(3) nonce 不符的 DataBind(child 内比较)与找不到活会话的 DataBind 不计入退避(后者可能是合法的陈旧连接),两者都不花 fork;(4) 会话建立后的畸形控制帧只以日志行结束会话(child exit 0),握手之后没有给对端的帧;(5) 四个旋钮只读环境变量,不在任何配置表或 Android service extra 里;(6) ID-P7-44 的控制依赖 Linux af_unix dgram 语义(接收端 `SHUT_RD` 后 `sendmsg` 得 EPIPE),child 崩溃时已排队的 DataBind 仍被内核丢弃;(7) 测试专用杠杆 `MOBILEGL_TEST_DELAY_SESSION_HANDSHAKE_MS`(与 `MOBILEGL_TEST_DELAY_FIRST_CAPS_MS` 同类,未设不生效)。
- **门 5 修复(ID-P7-58)的记录债**:(1) 探针判坏的设备上 shader resolve 臂 decline(无 `VK_EXT_shader_stencil_export` 的模板面、格式不带 `SAMPLED_IMAGE | DEPTH_STENCIL_ATTACHMENT`、pipeline 失败)时回落到已知不写目标的 render-pass 臂,读回 0、无 GL 错误(POST 上是 UNFIXABLE 行,只有 `MGLOG_W_ONCE`);(2) 探针只测 4×、4×4、整幅 render area,别的采样数 / 尺寸 / 子矩形上坏的驱动看不到;(3) 非 1:1 单采样深度 blit(缩放 / 翻转 / default FB)在无 `BLIT_DST` 的深度格式上仍调 `vkCmdBlitImage`(monolith 同样);(4) 跨格式 wire 深度 blit 不走 monolith 的 `BlitDepthAcrossFormats`;(5) tcp 带不了 `MsResolveBug.` / `MsFlipBug.` / `MsResolveElide.`(server 环境旋钮,`MAGMA_SERVER_ENV_KNOB_NO_TCP` 具名);(6) 图像单元:无效 / 空的存储单元在有 VK_EXT_robustness2 `nullDescriptor` 处绑 null 描述符(即 GL 8.26 的确切语义),否则绑该单元私有的占位(残余:同一 draw 内同一单元先存后读读到所存值;私有占位满 `kWirePrivateStoragePlaceholderCap` = 384 个后新的 (形状, 单元) 回落到按形状共享);在存储里但落在 [BASE, MAX] 之外或 mip 不完整的 level 仍按原样绑定(与 monolith 一致,门 5 以它为参照);(7) 切带回读在 Magma 上每带一次完整 `ReadWirePixels`(flush + 转换 + 等待),2400×1080 截图 5 带——只记性能。
- **`ArmPrimGenReroute` 仍是每进程首设备裁决**:`VulkanRenderer.cpp` 的 `static const PrimitivesGeneratedNoXfbMeasurement s_measurement` 让同进程后来的设备沿用第一个设备的测量;这段编进 pull 构建,改它动 G1,只记(深度 resolve 的同形问题已按设备身份记忆,codex closeout finding 7)。
- **回调里闩住的记录仍推进 `appliedSeq`**:`SessionRings.h` 的 `SessionConsumer::ApplyOne` 在回调返回后照常 `++m_appliedSeq` 并发布,等这条记录的 client 在 SessionFault / 会话关闭前短暂看到它已应用;只影响本会话,与上文 PH-6 债 (1) 同形。
- **PH-5 的最小编码界只管 vector**:`ProgramArtifactsCodec.cpp` 对 vector 计数按元素最小编码字节数(`MinEncodedBytes`)对剩余字节设界,伪造计数的瞬时内存约为归档的 4×(最坏 `String` 32 / 8);map / set 解码仍按每项 1 字节计、逐项增长,4× 不是整个解码的上界。
- **窗口 2 reducer 的已知偏差(记录,不判)**:`$BASE` 为 Fail、AFTER 变 NotSupported 的用例离开分母、抬高 rate,可抵消真正的 Pass→Fail(dsa 2 例 Pass→Fail 同时 2 例 `$BASE` Fail→NS 读 −0.003 pp 通过,没有后者是 −0.541 pp 失败);只有 `WARN <block>: NotSupported …` 行可见。
- **Adreno 上的 robustness2 启用待下一设备窗口确认**:wire 臂现对每个非 monolith 设备启用 VK_EXT_robustness2 `nullDescriptor`,设备窗口 2 跑的 `6afa077f` 不含它;下一窗口核对 Redmi server 日志的 `Magma wire: an invalid or unbound image unit binds …` 行是哪一臂、`KHR-GL46.shader_image_load_store.incomplete_textures` 仍 Pass、门 3 不受影响。