[Docs] (P12): the three rows that keep their wait get a ruling with evidence behind it, and the handoff's open op-split question gets its numbers

- What was open: the handoff's 4.3 called ResourceCreate and SetTextureParams "a second problem of
  the same kind, needing its own analysis" and left it there; 5.4 said the per-op split of the
  55,428 waits was never quantified. The measurement committed beside this answers both.
- The ruling (CONTRACT-P5E.md 2.5): ResourceCreate, ResourceRespecify and SetTextureParams stay
  kWaitReply, because each answer carries a fact about the SERVER's record table that the client's
  own tracker provably cannot know. RespecifyOnce says it outright - "the applier's REFUSAL is the
  only signal that says 'I hold nothing for this handle'", which is the scope
  MGPipeApplierReleaseObjectRecords leaves for a served context whose frontend objects live on;
  EmitTextureParams uses its own refusal as the same self-heal trigger; PublishCreate may not take
  the publication latch on a refused create (D-I1/c0b) or the death path emits a resource_destroy
  for a record that does not exist. Unlike the upload half these are not "the emission gate already
  answered it" rows, so removing their waits removes the signal.
- The numbers, both shapes a stitcher produces: an atlas stitch goes 64 -> 0 reply waits; a texture
  per sprite goes 384 -> 320 with ResourceSubData now zero and the remainder all three of those
  rows. So item B hits exactly the shape it was aimed at, and what is left is per-resource /
  per-call rather than per-chunk.
- notes/p12/DEVICELOST-AND-UPLOAD-WAITS.md gains 2.4 with the table and the ruling, and its "second
  problem of the same kind" entry is closed rather than carried; MEASUREMENTS.md records the two
  shapes beside the 1 -> 0 single-upload number.
This commit is contained in:
BZLZHH
2026-09-25 20:17:22 +08:00
parent fa8b500ba8
commit ce0235b190
3 changed files with 43 additions and 4 deletions
+14 -1
View File
@@ -206,7 +206,20 @@ non-wire arm keep the old answer, which is what G1 measures. Gate:
`RemoteClientControls.TextureUploadsStopOwningAReplyOnTheWireArm` reads back the same `wait_replies`
the P65LinkMetrics line prints (`LinkMetricsReplyWaits`), asserting a pure upload moves it by ZERO
while a `resource_create` row still moves it; red-once with the arm test removed is exit 142.
`ResourceCreate` and `SetTextureParams` stay `kWaitReply` — same question, asked per resource and
**`ResourceCreate`, `ResourceRespecify` and `SetTextureParams` stay `kWaitReply`, and that is a
ruling rather than an omission**: each one's answer carries a fact about the SERVER's record table
that the client's own tracker provably cannot know. `RespecifyOnce` (`TextureEmit.h:1332-1356`) says
it in one line - "the applier's REFUSAL is the only signal that says 'I hold nothing for this
handle'", which is the scope `MGPipeApplierReleaseObjectRecords` leaves behind for a served
context whose frontend objects live on; `EmitTextureParams` (`:991-1017`) uses its own refusal as
the same self-heal trigger; and `PublishCreate` (`:1303-1319`, D-I1/c0b) may not take the
publication latch on a create the applier refused, or the death path emits a `resource_destroy` for
a record that does not exist. Removing any of those waits removes the signal, so unlike the upload
half they are not "the emission gate already answered this" rows. Measured split for the two shapes
a stitcher produces (P12, `RemoteClientControls.ReplyWaitsByOpForAnAtlasShapedLoad`): an atlas
stitch - 64 sub-uploads into one level - goes from 64 reply waits to **0**, while a texture per
sprite - 64 textures, 256 `glTexParameteri` - goes from 384 to 320, with the remainder all three of
these rows (create 76, respecify 130, params 130).
per call, analyzed separately rather than swept in here.
### 2.6 Event-ring flow control (ARCHITECTURE §11.7 made real)
+5 -1
View File
@@ -11,7 +11,11 @@
- **拆成两进程**:真机上与两线程成本持平;两者的 GL 线程 CPU 都比单线程少约 30%(后端工作挪到了另一线程 / 进程)(P6)。
- **跨机**:电脑经 TCP 连手机,102 个集成用例全过;server 被杀后 133 ms、Wi-Fi 断开后约 5 s 干净报"设备丢失"(P6.5)。
- **纹理上传的等待粒度(P12,handoff §4)**:wire 臂上纹理那半 `resource_subdata` 不再逐条买回包,等待点从「每条记录」落到「SEG_STAGE 窗口用尽」。
单条纯上传占用的回包等待 **1 → 0**(`wait_replies`,正是 §3.2 里"一帧 55,428"的那个计数器);**真机端到端墙钟仍待复测**,见 [`notes/p12/DEVICELOST-AND-UPLOAD-WAITS.md`](notes/p12/DEVICELOST-AND-UPLOAD-WAITS.md)。
单条纯上传占用的回包等待 **1 → 0**(`wait_replies`,正是 §3.2 里"一帧 55,428"的那个计数器)。
按形状实测(`RemoteClientControls.ReplyWaitsByOpForAnAtlasShapedLoad`,主机 inproc,按 op 计数):**图集拼接**(一张 level 上 64 次 `glTexSubImage2D`)**64 → 0** 次回包等待;
**每 sprite 一张纹理**(64 张纹理 + 256 次 `glTexParameteri`)**384 → 320**,剩下的全是 per-resource / per-call 的三条 row(create 76 / respecify 130 / params 130)——
它们的拒绝是「服务端没有这个 handle」的唯一客户端可见信号,删掉等待就是删掉信号,裁定见 `CONTRACT-P5E.md` §2.5。
**真机端到端墙钟仍待复测**,见 [`notes/p12/DEVICELOST-AND-UPLOAD-WAITS.md`](notes/p12/DEVICELOST-AND-UPLOAD-WAITS.md)。
- **画面正确**:Vulkan 后端真机画面检查 36/36 通过,CTS 五块相对基线没有超过 0.5 个百分点的退步、没有新崩溃(P7);server 自有窗口上屏两后端 SSIM 1.0(P12)。
## 按阶段
@@ -94,6 +94,28 @@ DECLINED 有三条**完全不含损坏**的产生路径,两条在服务端、
其余改动全部落在 split-only 的翻译单元(`MG_Remote/**`)或 `PipeFill.cpp` 的 `#if MOBILEGL_BUILD_DISAGGREGATED` 块内。
因此 pull 臂预处理后与改动前**逐字符相同**。**本机已按 G1 跑过 A/B**:pull 配置(Release、`/usr/bin/c++`、`MOBILEGL_BUILD_DISAGGREGATED=OFF`、`--target MobileGL`)在 `c984d952`(基线)与 `3b9f536d`(本轮)各建一次,结果 `.text` **13872002 = 13872002** 字节、符号 **31746 / 31746**、**0 增 0 删**,逐符号 `comm` 无任何差异(两棵树留在 `build-g1-base/`、`build-g1-head/`,符号表在各自的 `syms.txt`)。
### 2.4 「同类第二条」到底该不该等(handoff §4.3 末条 + §5.4)
handoff §5.4 说「55,428 次等待里各 op 各占多少」未量化,§4.3 说 `ResourceCreate` / `SetTextureParams` 是同类第二条、"需单独分析"。本轮把两件事一起做了:
给 `LinkMetrics` 加了**按 op 的回包等待计数**(`LinkMetricsBeginReply(wantsReply, op)` / `LinkMetricsReplyWaitsFor(op)`,与总计数一样只在 `MOBILEGL_PIPE_STATS=1` 时记),
并用它量了拼接器会产生的两种形状(`RemoteClientControls.ReplyWaitsByOpForAnAtlasShapedLoad`,先打印后断言,好让红的那一次也留下数字):
| 形状 | 没有 B | 有 B |
|---|---|---|
| **图集拼接**:一张 level 上 64 次 `glTexSubImage2D` | **64** 次回包等待 | **0** |
| **每 sprite 一张纹理**:64 张纹理 + 256 次 `glTexParameteri` | **384**(`ResourceSubData` 占 128) | **320**(`ResourceCreate` 76 + `ResourceRespecify` 130 + `SetTextureParams` 130,sub-data 0) |
结论两条:
1. **B 正好打在它要打的地方**:上传密集的形状(MC 的图集拼接就是这种)里,回包等待从「每次上传一次」变成 **0**——handoff §4.2 的预期在机制层面成立,
与「96 MiB ÷ 32 MiB 窗口 ≈ 3 次等待」是同一件事的两面(剩下的是 stage 退休,不再是回包)。
2. **剩下三个 row 不能照搬 B**,因为它们等的不是「你收下这批字节了吗」,而是**服务端的记录表里还有没有这个 handle**:
- `RespecifyOnce`(`TextureEmit.h:1332-1356`)原文:「the applier's REFUSAL is the only signal that says 'I hold nothing for this handle'」——
served context 的 teardown(`MGPipeApplierReleaseObjectRecords`)会丢掉对象记录而前端对象还活着,这件事**客户端本地推不出来**;
- `EmitTextureParams`(`:991-1017`)用同一个拒绝做自愈触发器(context 的默认纹理就是这条路径救回来的);
- `PublishCreate`(`:1303-1319`,D-I1/c0b)不能在「被拒绝的 create」上落发布闩,否则死亡路径会为一个不存在的记录发 `resource_destroy`。
所以这三条按 per-resource / per-call 结算,不是 per-chunk:**删掉等待就是删掉信号**。这条裁定已写进 `CONTRACT-P5E.md` §2.5。
## 3. 本轮**没有**做的(下一轮的直接入口)
1. **真机端到端**:A 需要「进世界不再崩」、B 需要「那一帧墙钟 249 s → 约 20 s」的真机数字。需要重出 APK(`assembleTraceRelease` + 签名)、手机 `90cee93` 起 server、跨机 TCP 起 MC 26.2。
@@ -102,8 +124,8 @@ DECLINED 有三条**完全不含损坏**的产生路径,两条在服务端、
`launch-mc.py`(按版本 JSON 重建启动命令,已干跑校验:95 条 classpath 条目全部存在、Java 25 由 `javaVersion.majorVersion` 选出)、
`gl-aliases.sh`(把五个别名建在 `mgl-device/gl-aliases/`,**不放 build 目录**:那里的 `libEGL.so` 会让 server dlopen 自己)、
`start-phone-server.sh`(装包 + 起 Activity + handoff 那两个就绪判据)。
2. **同类第二条**:`ResourceCreate` 与 `SetTextureParams` 也是逐条 kWaitReply(handoff §4.3 末条)。B 只处理了上传那半,
这两条要单独分析(创建是按资源一次,参数设置是每次调用——收益与风险不同);`CONTRACT-P5E.md` §2.5 已把这一条写成 "analyzed separately"。
2. ~~同类第二条~~ **已分析完毕(§2.4)**:`ResourceCreate` / `ResourceRespecify` / `SetTextureParams` 保持 kWaitReply 是有据的裁定,
不是遗漏——它们的拒绝是「服务端没有这个 handle」的唯一客户端可见信号。**这条不再是待办。**
3. **主机门的环境前提**:本机 shell 沙箱没有 `/dev/dri`,所以任何需要真 EGL pbuffer 的车道(`SpawnLane.EventForfeitPeer`、`TcpLane.EventForfeitPeer`)在本会话里必然红(子进程 `kEglSurface = 12`),与本轮改动无关;`EventForfeitPeerTest.cpp:134` 的注释本来就写明这种 runner 会「every case reds before the ring ever fills」。
## 4. 证据位置