mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-11-07 01:53:24 +09:00
[Docs] (Disaggregated): P5f f0 - the eight-way read-only census reports
Statics, reverse channel, Magma cross-role sites, EGL control plane, registry rekey, build/gate mechanics, the 15 BARRIER_PULLED fields' carrier rulings, and the dual-block mechanism's design recon.
This commit is contained in:
@@ -0,0 +1,428 @@
|
||||
# P5f / f0 — f1 双块机制设计侦察(只读普查)
|
||||
|
||||
> 只读普查产物,未修改任何代码、未构建、未跑测试。所有 `file:line` 以
|
||||
> `feat/disaggregated @ 8b68b92c`(代码头 `25fba0d5`)为准。
|
||||
> 姊妹篇:`f0-env.md`(旋钮/车道环境一览)、`f0-statics.md`、`f0-magma.md` 等。
|
||||
> 本文聚焦 `gPipeInputs` 的双块拆分(P5F-WIRE-COMPLETENESS.md §4)的设计侦察,
|
||||
> 回答任务单 (1)–(7)。
|
||||
|
||||
---
|
||||
|
||||
## 1 `gPipeInputs` 的声明与全树引用点分类
|
||||
|
||||
### 1.1 声明
|
||||
|
||||
`MobileGL/MG_Backend/MGPipe/PipeInputs.h:835`:
|
||||
|
||||
```cpp
|
||||
inline PipeInputs& gPipeInputs = *new PipeInputs();
|
||||
```
|
||||
|
||||
- inline 引用变量,无 .cpp 定义;**leak-at-exit**(`PipeInputs.h:818-834`):O 类成员是指向前端
|
||||
对象的 `SharedPtr`,退出时析构会沿 slot allocator / applier / twin 表触发不安全的销毁链,
|
||||
所以永不释放。
|
||||
- 尺寸预算 `static_assert(sizeof(PipeInputs) < 20 * 1024)`(`PipeInputs.h:843`)。
|
||||
- 该头只在 `MOBILEGL_PIPE_PUSH` 构建里经由 `MG_Pipe/PipeInputsSwitch.h:17-21` 进入 include
|
||||
闭包(`PipeInputs.h:39-43` 的论证);`PipeInputs.cpp` 也只在此条件下编译
|
||||
(`PipeInputs.cpp:11-13`)。**pull 构建从符号层面就看不见它**,双块改动若同样关在
|
||||
`MOBILEGL_BUILD_DISAGGREGATED` 里则对 G1 零扰动。
|
||||
|
||||
### 1.2 引用点全清单(非测试代码),按角色/线程分类
|
||||
|
||||
**写者 — client 角色(GL 线程,`MG_Impl`)**:
|
||||
|
||||
| 位置 | 写什么 |
|
||||
|---|---|
|
||||
| `MG_Impl/Pipe/PipeFill.cpp:3348`(`MGPipeValidateForVerb`) | 绑定 `PipeInputs& inputs = gPipeInputs;`;`:3385` SetIdentity(**每个 verb 都写**,含 run-ahead);`:3423` `++CurrentVerbSerial`;`:3437` `MGPipeServerClearVerbBoundary()`;`:3439` SetVerb;`:3746-3763` step 4 残余填充 `CopyField` + `FilledGen[i] = CurrentVerbSerial`(sticky 一次性盖 1,`:3753`) |
|
||||
| `PipeFill.cpp:2204-2230`(`MGPipeLeaveVerb`) | `:2225` 再 bump serial(无 verb 时使旧戳过期);`:2228` clear;`:2230` SetVerb(kVerbCount) |
|
||||
| `PipeFill.cpp:702-724`(`MGPipeNoteFrontendMutation`) | 单字段刷新(P3a D-H2 类:后端在自己 verb 内改了前端对象后的回填) |
|
||||
| `PipeFill.cpp:1217`(`MGPipeReleaseResidualFillPins`) | 释放四个 O 类 SharedPtr |
|
||||
| `PipeFill.cpp:565-566, 627, 653, 663, 679`(verify 臂) | `g_snapshot` / `g_readScratch` 与 `gPipeInputs` 的入口比较、compare-at-read hook(`MOBILEGL_PIPE_VERIFY` 构建限定) |
|
||||
|
||||
**写者 — server 角色(apply 线程 `mgl-srv-apply`)**:
|
||||
|
||||
| 位置 | 写什么 |
|
||||
|---|---|
|
||||
| `MG_Backend/MGPipe/PipeInputs.cpp:265-305`(`MGPipeServerStampVerbBoundary`) | bump serial、SetVerb、按 `AnswerableMaskForVerb` 盖 RECORD_SUPPLIED/APPLIER_DERIVED、**清零** BARRIER_PULLED/FATAL 的戳(`:300-303`)、置 `m_serverStampedVerb`(`:304`)。调用点:`MG_Remote/Server/PipeApplier.cpp:1255-1258`(仅 Clear/DrawVbo/ReadPixels/Blit 四个 verb 形 op) |
|
||||
| `PipeInputs.cpp:307`(`MGPipeServerClearVerbBoundary`) | 清 `m_serverStampedVerb`。**双角色都调它**:server 侧 `PipeApplier::LeaveApplier`(`PipeApplier.cpp:1261`),client 侧 `PipeFill.cpp:3437` 与 `:2228` |
|
||||
| `MG_Pipe/PipeApply.cpp`(经 `MGPipeApplyAccess` 门,`PipeInputs.h:732-738`) | `:1524-1534` BindRenderState、`:1552-1557` SetDynamicState、`:1561` SetPixelPackState、`:1573` SetContextValues、`:1584-1638` SetPatchState、`:1643-1690` SetVertexAttribDefaults;`:1724` 是读(trip wire 比较)。**故意不盖戳**(`PipeInputs.h:735-737`) |
|
||||
|
||||
**读者 — server 角色(apply 线程)**:
|
||||
|
||||
- **后端主通道**:`MG_Pipe/PipeInputsSwitch.h:19-21` 的 `MGB_CTX` 宏 = `&gPipeInputs`。
|
||||
全树 `MGB_CTX` 共 **379 处 / 24 文件**;生产后端:DirectGLES.cpp 103、Managers.cpp 24、
|
||||
MultiDraw.cpp 7、Utils.cpp 2、DirectVulkan.cpp 48、VulkanRenderer.cpp 133、
|
||||
UniformManager.cpp 23 等。inproc 下这些读全部发生在 `PipeApplier::ApplyOne`
|
||||
(`PipeApplier.cpp:1159-1243`)内部,即 apply 线程;monolith 下在 GL 线程。
|
||||
- 直读(不经宏):`MG_Backend/DirectGLES/DirectGLES.cpp:267`(`gPipeInputs.CurrentVerb()`);
|
||||
`PipeApplier.cpp:402`(GetPixelStoreParameters)、`:505`(GetMaxTouchedTextureUnit)、
|
||||
`:858-859`(GetTextureObject)。
|
||||
- 七个 F 类 sticky forward 的函数体在 `PipeFill.cpp:2131-2186`,**逻辑上被 server 调用、
|
||||
物理上在 apply 线程执行**(经后端调用进入),体内 `MGP_STICKY_FORWARD_PULL` 调
|
||||
`MGPipeStickyForwardPull`,后者读 `gPipeInputs.ServerStampedVerb()/CurrentVerb()`
|
||||
(`PipeInputs.cpp:376-377`)。
|
||||
|
||||
**读者 — client 角色(GL 线程)**:
|
||||
|
||||
- `PipeFill.cpp:3016`(`VertexAttribDefaultsOf`,emitter 自查)、`:629`(Fatal 消息取名);
|
||||
verify hook 的 `&self == &gPipeInputs` 判等(`:663`)。
|
||||
- `EmitTables.cpp:1200` 的注释明确反面规则:client 侧**不得**经 MGB_CTX 问问题
|
||||
("on this side of a split MGB_CTX is gPipeInputs, which is the SERVER's view")。
|
||||
|
||||
**测试引用**(非生产,但双块要决定它们落在哪块上):`MG_Test/Pipe/PipeInputsTest.cpp`、
|
||||
`FieldOwnershipTest.cpp`(约 40 处)、`RenderStateSpansTest.cpp`、`MG_Test/Wire/
|
||||
ServerLoopTest.cpp:572-586`、`PipeWireCodecTest.cpp:1366-1380`、`RemoteClientTest.cpp:
|
||||
2053-2364`(角色守卫死亡测试)。测试均跑 monolith/单线程形态,knob 关闭时行为必须逐字节不变。
|
||||
|
||||
**纯注释引用**(无代码语义):`PipeMutation.h:223,248`、`PipeApply.h:21,802,1086`、
|
||||
`ClientSession.h:25,150,191-199`、`WireTables.h:29`、`CapsMirror.cpp:37`、
|
||||
`Init.cpp:80`、`PipeStats.h:172`、`SlotAllocator.cpp:428`、`EmitTables.cpp:256,812,1394`、
|
||||
`PipeWireCodec.cpp:2257`、`Config.h:523`、`ConfigLoader.cpp:386`。
|
||||
|
||||
**今天为何能工作**:两角色同进程同对象,verb barrier 保证 {GL 线程, apply 线程} 同一时刻至多
|
||||
一个可运行(`PipeInputs.h:904-908` 引 CONTRACT-P5 表 3 的裁定),所以单写者成立;client 填、
|
||||
server 读,BARRIER_PULLED 就是"读对方留在同一块里的残余"。
|
||||
|
||||
**P5f 处置建议**:写侧/读侧的代码边界恰好是文件边界 —— **所有 client 写集中在
|
||||
`MG_Impl/Pipe/PipeFill.cpp`,所有 server 写在 `MG_Backend/MGPipe/PipeInputs.cpp` +
|
||||
`MG_Pipe/PipeApply.cpp`,所有后端读经 `MGB_CTX` 一个宏**。这意味着双块不需要碰 379 个后端
|
||||
读点,只需要:(a) 在 `PipeInputs.h` 加第二块与选择函数;(b) 把 PipeFill.cpp 里约 10 处
|
||||
`gPipeInputs` 拼写换成 client 块;(c) server 侧保持 `gPipeInputs` 拼写不变。详见 §7。
|
||||
|
||||
---
|
||||
|
||||
## 2 运行时如何知道当前角色
|
||||
|
||||
### 2.1 server 侧:线程身份
|
||||
|
||||
- `ServerLoop::OnApplyThread()`(`MG_Remote/Server/ServerLoop.h:246-252`):一次 relaxed
|
||||
读 `Detail::g_applyThreadKey` + 与当前线程 key 比较;key 由 apply 线程自己在
|
||||
`ApplyThreadMain` 第一行发布(`ServerLoop.cpp:337`),在与 `m_running` 清空的**同一个临界
|
||||
区**内清零(`ServerLoop.cpp:519`,理由见 `:496-518` 的长注释——身份必须与 m_running 同死)。
|
||||
key==0 时短路返回 false,所以 monolith/未启动/已停止都答"否"(`ServerLoop.h:248-251`)。
|
||||
- 别名 `RunsAsTheServerRole() = OnApplyThread()`(`MG_Remote/Client/WireTables.cpp:87`),
|
||||
WireTables 每条发射路径用它分流 monolith/split。
|
||||
- 它是**全树每个角色守卫的谓词**(`ServerLoop.h:89-92` 列举:BufferObject 的 legacy-arm
|
||||
守卫、PersistentMapTracker::OnServerRole、RefusePipeInputsTouchWhileApplierOwnsIt、
|
||||
分配器守卫等)。
|
||||
|
||||
### 2.2 client 侧:会话标志 + 线程局部
|
||||
|
||||
- `thread_local Bool g_inBarrierWait`(`MG_Remote/Client/ClientSession.cpp:117`,由
|
||||
`BarrierWaitScope` :120-122 举起)——"本线程是否在等 barrier",**必须** thread_local
|
||||
(`ClientSession.cpp:1278-1284` 的论证)。
|
||||
- `std::atomic<Bool> g_applyThreadInsideApplier`(`:118`),由 applier 侧的
|
||||
`ScopedApplierEntry` 在整个 `ApplyOne` 期间举起(`PipeApplier.cpp:1173`;
|
||||
Note* 函数在 `ClientSession.cpp:1381-1386`)。
|
||||
- `ClientSessionInstance().RunAheadArmed()` / `BarrierArmed()` / `Started()`:
|
||||
会话级标志,守卫链按"常量优先"顺序短路(`ClientSession.cpp:1292-1305`)。
|
||||
|
||||
### 2.3 P5c 角色守卫本体
|
||||
|
||||
`ClientSession::RefusePipeInputsTouchWhileApplierOwnsIt`(`ClientSession.cpp:1290-1380`),
|
||||
被 PipeFill 的四个写点调用:`:723`(NoteFrontendMutation)、`:2214`(LeaveVerb)、
|
||||
`:3412`(ValidateForVerb phase 1)、`:3737`(ValidateForVerb/residual phase 2)。
|
||||
run-ahead 臂(`:1318-1354`):除"barriered fill 且 applier 不在 applier 内"外,GL 线程触碰
|
||||
即 `Fatal{RoleViolation, "gPipeInputs"}`;lockstep + BatchWaits==0 臂(`:1361-1379`)同理。
|
||||
|
||||
**读侧线程**:server 读全部在 apply 线程(`mgl-srv-apply`,`ServerLoop.cpp:338` 命名),
|
||||
具体在 `PipeApplier::ApplyOne` 的 stamp→decode→LeaveApplier 区间内。
|
||||
**填侧线程**:GL 线程(app 调 GL 入口 → `MGPipeValidateForVerb`)。
|
||||
|
||||
**今天为何能工作**:角色判定不靠"你在哪个进程",而靠"你在哪条线程 + 会话是否 started/armed"。
|
||||
双块机制可以复用同一套谓词,但更稳的轴是**代码位置**(§1.2:填侧全在 PipeFill.cpp)而不是
|
||||
线程——线程谓词在 monolith 下会把 client 和 backend 判成同一角色,恰好正确;在 inproc 下
|
||||
分开,也恰好正确。两条轴等价,代码位置轴更便宜(无热路径谓词)。
|
||||
|
||||
**P5f 处置建议**:块选择函数内部**不需要**调 `OnApplyThread()`(它在 P5d 被压成一次 relaxed
|
||||
load 但仍是每 draw 的成本,`ServerLoop.h:89-99`)。填侧站点是静态已知的(PipeFill.cpp 的
|
||||
~10 处拼写),直接拼 `MGPipeClientInputs()`;读侧拼 `gPipeInputs`(server 块)。线程谓词只在
|
||||
调试断言/新守卫里用。
|
||||
|
||||
---
|
||||
|
||||
## 3 poison / Fatal{UnmigratedPipeInput} / FilledGen 世代戳机制
|
||||
|
||||
### 3.1 戳的结构与判定
|
||||
|
||||
- `MGPipeFilledState { Uint64 CurrentVerbSerial; Uint64 FilledGen[63]; }`
|
||||
(`MG_Pipe/generated/PipeFilled.inc:407-410`;63 = `kMGPipeInputFieldCount`,`:96`)。
|
||||
- 新鲜判定 `MGPipeInputFieldIsFresh`(`PipeFilled.inc:421-426`):`gen==0` → false
|
||||
("从未填过",两个分支同义);sticky → true;否则 `gen == CurrentVerbSerial`。
|
||||
- **serial 从 1 起**(`PipeFill.cpp:3420-3423` 与 `PipeInputs.cpp:280-283` 两处同述):
|
||||
0 保留给"从未填过",所以 server 的清零(`PipeInputs.cpp:300-303`)是真撤回而不是"碰巧旧"。
|
||||
|
||||
### 3.2 Fatal 形态
|
||||
|
||||
- 基础版:`MGPipeInputPoisonFatal`(`PipeFilled.inc:412-416`)——
|
||||
`MGPipe: Fatal{UnmigratedPipeInput, "<field>@<verb>"}` + `std::abort()`;verb 取名经
|
||||
`MGPipeInputPoisonFatalForVerb`(`PipeInputs.cpp:30-32`,首个 verb 前是 `"<none>"`)。
|
||||
- split 四路裁定 `MGPipeInputUnfreshRead`(`PipeInputs.cpp:312-345`):
|
||||
非 server-stamped → monolith 语义 Fatal;server-stamped + RECORD_SUPPLIED/APPLIER_DERIVED →
|
||||
`Fatal{RoleViolation, "<field>"}`(P5c gt,`:333-341`);BARRIER_PULLED 且在本 verb 类内 →
|
||||
`CountBarrierPull`;其余 → poison Fatal。
|
||||
- `CountBarrierPull`(`PipeInputs.cpp:162-187`)三臂:**未设障记录 → 无条件 Fatal**
|
||||
(`:163-165`,无旋钮可豁免,理由 `:122-132`——值按构造是 torn 的);设障 + strict + 未被
|
||||
静态表或 escalation 承认 → `StrictBarrierPullFatal`(`:182-184`,消息尾
|
||||
`[BARRIER-PULLED, MOBILEGL_IPC_STRICT_ERRORS=1, retires in <phase>]`);设障 + admitted →
|
||||
每进程每 (field,verb) 一条 `Admitted{...}` MGLOG_W(`AdmittedBarrierPullOnce` :96-117,
|
||||
静态位图去重)。
|
||||
- 参数窄化:`MGPipeInputArgumentRead`(`PipeInputs.cpp:347-370`),今天唯一行是
|
||||
GetPixelStoreParameters 的 unpack 半 → FATAL(`PipeInputs.h:439-447`)。
|
||||
- sticky 七字段无 `MGP_INPUT_CHECK`(声明的例外,`PipeInputs.h:683-688`),由
|
||||
`MGPipeStickyForwardPull`(`PipeInputs.cpp:372-378`)在 server-stamped verb 内手工计入
|
||||
`rsp`/strict。
|
||||
|
||||
### 3.3 MOBILEGL_PIPE_POISON 旋钮在哪解析
|
||||
|
||||
**它不是运行时环境变量**:`PipeInputs.h:30-35` 的编译期派生宏——
|
||||
`MOBILEGL_PIPE_PUSH && (LOG<=DEBUG || MOBILEGL_BUILD_DISAGGREGATED || MOBILEGL_PIPE_VERIFY)`。
|
||||
运行时旋钮只有毒化家族的**测试**旋钮:`MOBILEGL_PIPE_POISON_OMIT=<Verb>:<Field>`
|
||||
(阴性对照 B)→ `Features.PipePoisonOmit`(`Config.h:373-378` 定义、`ConfigLoader.cpp:278`
|
||||
解析)→ `PipeFill.cpp:518-543` `ParsePoisonOmissionKnob` 在每次 Validate 时查对,
|
||||
未知名 `BadKnob` Fatal。
|
||||
|
||||
**今天为何能工作**:戳是每 verb 的代际号而非位图(`PipeFilled.inc:19-23`:位图看不见
|
||||
"上一 draw 填的、这一 verb 没重填"),stamp/withdraw 双写者靠 verb barrier 串行化。
|
||||
|
||||
**P5f 处置建议**:双块下 server 块的 BARRIER_PULLED 戳**已经被 server stamp 清零**——freshness
|
||||
语义原样成立。关键缺口在**值**:knob 开时 server 块的 15 个 BARRIER_PULLED 字段没人填,
|
||||
O 类 SharedPtr 字段(GetBoundVertexArray/GetProgramForDraw/GetTransformFeedbackProgram)
|
||||
**没有 null 检查**(对比有检查的 `GetTextureUnitObject`,`PipeInputs.h:669-676`),直接返回
|
||||
空 SharedPtr 给后端解引用 → 是**无名崩溃而不是具名 marker**。所以 f1 必须把
|
||||
`CountBarrierPull` 在双块臂下改成无条件 `StrictBarrierPullFatal`(why 串填
|
||||
`MOBILEGL_IPC_ROLE_SPLIT_STATE=1`,marker 语法与 `split_log_paths.py:44-46` 的
|
||||
`MARKER_RE` 天然兼容)——这是"红就是清单"成立的前提,否则 O 类字段的红是段错误。
|
||||
|
||||
---
|
||||
|
||||
## 4 MOBILEGL_IPC_* 旋钮与新旋钮的最小接线
|
||||
|
||||
### 4.1 现有机制
|
||||
|
||||
- 声明:`Config.h:468-547` `struct IpcTable`(`MOBILEGL_BUILD_DISAGGREGATED` 内);
|
||||
实例 `MG_Config::Ipc`(`ConfigLoader.cpp:35`)。
|
||||
- 解析:`MG_ConfigLoader::InitIpc()`(`ConfigLoader.cpp:356-416`),由 `Init()` 在
|
||||
`InitTransport()` 后调(`:428-429`)。env 收集是前缀制
|
||||
(`IsAcceptedPrefix`,`ConfigLoader.cpp:42-44`),**任何 MOBILEGL_ 前缀自动可见,
|
||||
无需登记**(`:257-259` 注释明说)。
|
||||
- 工具函数:`QueryEnvFlag`(truthy:非空、非 "0"、非 "false")、`QueryEnvUint32`(带
|
||||
min/max 与非法值 WARN 回落)、`QueryEnvVariable`。
|
||||
- 纪律:**新旋钮必须进 Config 由集成者落地,不得在调用点现造**
|
||||
(`EmitTables.h:109` 原文:调用点发明的 MOBILEGL_IPC_ 形名字是反模式;
|
||||
`Config.h:463-467`:P5 只落自己包读的旋钮,后续阶段的旋钮由集成者加在这里)。
|
||||
|
||||
### 4.2 `MOBILEGL_IPC_ROLE_SPLIT_STATE=1` 的最小接线
|
||||
|
||||
1. `Config.h` IpcTable 尾部加 `Bool RoleSplitState = false;` + 注释(照 `StrictErrors`
|
||||
`Config.h:534-537` 的形状:一句话说从什么升成什么)。
|
||||
2. `ConfigLoader.cpp` `InitIpc()` 加 `ipc.RoleSplitState = QueryEnvFlag("MOBILEGL_IPC_ROLE_SPLIT_STATE");`,
|
||||
并进 `:400-406` 的那行汇总日志。
|
||||
3. 两个交互要写死:
|
||||
- **monolith 下无效**:`:397` 已有 `if (Transport == Monolith) return;`,选择函数处再判一次
|
||||
`Transport != Monolith`(monolith 的 backend 与 fill 同线程,分块会立刻饿死所有读)。
|
||||
- **与 PipeVerify 互斥**:verify 的入口比较比的是填侧块、compare-at-read hook 以
|
||||
`&self == &gPipeInputs` 判活块(`PipeFill.cpp:663`),双块下 hook 会在 apply 线程重读
|
||||
活 context。照 `:388`(`if (PipeVerify) ipc.RunAhead = 0;`)的形状强制
|
||||
`RoleSplitState = 0` 并留一行 WARN。
|
||||
4. 消费点:`PipeInputs.h` 的选择函数(§7)。`PipeInputs.cpp` 已 include `Config.h`
|
||||
(`:20`),无新依赖方向问题。
|
||||
|
||||
**今天为何能工作**(对"加旋钮"这件事):前缀收集 + IpcTable + InitIpc 三点一线,StrictErrors
|
||||
/ Audit 两个 Bool 旋钮是逐字可照抄的样例。
|
||||
|
||||
---
|
||||
|
||||
## 5 CONTRACT-P5E §3.2 的 per-role stamp 存储:内容与归属裁定
|
||||
|
||||
### 5.1 内容(承诺了什么)
|
||||
|
||||
`MG_Remote/CONTRACT-P5E.md:224-229`(§3.2):`MGPipeServerStampVerbBoundary` 应把
|
||||
`m_filled` / `m_currentVerb` / `m_serverStampedVerb` 写进 **applier 私有存储而绝不写共享块**;
|
||||
client 侧的 `MGPipeServerClearVerbBoundary()`(PipeFill 的两处调用)在 transport 下删除;
|
||||
`SetIdentity` 移到 `ApplyOne`、每会话一次。
|
||||
|
||||
### 5.2 落地状态与编号归属(任务单说"ID-120",需更正)
|
||||
|
||||
- §3.2 **UNLANDED**,且契约自己在 ra2 修正里点名了它(`CONTRACT-P5E.md:266-272`):
|
||||
"§3.2 above, which is UNLANDED and was never marked as such",stamp 今天仍写进
|
||||
`gPipeInputs` 本体。拆 stamp 在契约里被记为 **P11 项**,并附关键告诫:**只有 per-role
|
||||
stamp 而不给 BARRIER_PULLED 值做版本化,比今天更坏**——会把具名 Fatal 变成静默旧值读
|
||||
(`:270-272`)。
|
||||
- 编号:`CURRENT_STAGE_PROGRESS.md:232` 把这一行挂为 "ID-120 / ID-135"。逐条核对后:
|
||||
**ID-120 是 §8 修正 8**(`GetBufferBindingPointCount` 的 forward → FATAL 子句,
|
||||
`CONTRACT-P5E.md:606-617` 标 UNLANDED、Lands with P7/P13);**§3.2 的 per-role stamp
|
||||
对应 ID-135**(ra2 元数据竞态的裁定,`notes/p5e/FACTS-P5E.md:178`、ROADMAP.md:39
|
||||
P11 行:"当时那次竞态在元数据标量上,修法是让守卫测事实而非断言未来(ID-135)……
|
||||
契约 §3.2 的 per-role stamp 存储仍未落地")。任务单的"ID-120"应读作"ID-135(§3.2)"。
|
||||
|
||||
### 5.3 双块机制应不应该顺手落它
|
||||
|
||||
**应该,而且是 f1 的必然子集而非可选项**,理由:
|
||||
|
||||
1. 双块一落地,server 块**就是** applier 私有存储(client 再也摸不到它)——§3.2 第一句在
|
||||
knob 开的臂上自动成立;knob 关的臂保持共享块旧语义(A/B 所需)。
|
||||
2. §3.2 的另两条在双块下**变成 moot 而非要删的代码**:client 侧的
|
||||
`MGPipeServerClearVerbBoundary`(`PipeFill.cpp:3437, 2228`)清的是自己块的 flag
|
||||
(无人读过它,无害),不需要"删除",只需要让它作用于 client 块(见 §7 的参数化)。
|
||||
3. **唯一真正要做的搬运是 `SetIdentity`**:今天 client 每个 verb 都写
|
||||
`m_live`/`m_contextIdentity`(`PipeFill.cpp:3377-3385`,这是 §3.1 的具名偏离,因为
|
||||
"本进程有没有活 GL context"不属于任何字段的归属类)。双块后 server 块的
|
||||
`MGB_CTX_LIVE`/`MGB_CTX_IDENTITY`(`PipeInputsSwitch.h:20-21`)**永远答 false/nullptr**,
|
||||
后端所有 null-context 守卫会立刻全部翻车。f1 必须在 server 侧给 server 块置一次身份
|
||||
——契约 §3.2 末句"SetIdentity moves to ApplyOne, once per session"正是答案,
|
||||
落点在 `PipeApplier::Attach`(`PipeApplier.cpp:1140-1149`)或首个 verb boundary。
|
||||
4. 契约的告诫(§3.2 不配版本化就更坏)在双块下由 §3.3 的"CountBarrierPull 改无条件
|
||||
Fatal"兑现:BARRIER_PULLED 值在 server 块里**不存在**(不是旧值),读它具名死——
|
||||
恰好是契约要求的方向。
|
||||
|
||||
---
|
||||
|
||||
## 6 integration-split 车道的声明方式与双块车道形状
|
||||
|
||||
### 6.1 现有车道形态(样例)
|
||||
|
||||
- **标签纪律(ID-131)**:`ctest -L` 是**正则**,所以"必须从 integration-split 里搬出去的
|
||||
条目"的标签不能含子串 `integration-split`。样例:
|
||||
- `integration-magma-split`(`MG_IntegrationTest/CMakeLists.txt:2422-2450`,理由
|
||||
`:2428-2433`:曾实测 `integration-split-magma` 拼法会被 `-L integration-split` 匹配中);
|
||||
对应预期红 CI 步 `test.yml:1360-1381`:红 + 私有日志里必须有具名 marker,绿也是错。
|
||||
- `integration-clientarrays-split`(`CMakeLists.txt:2216-2225`);CI 步
|
||||
`test.yml:1405-1440` 是**三臂**:run-ahead 未武装却红 = 真回归;武装却绿 = 具名拒绝没
|
||||
打响;红 + 日志带 `Fatal{UnmigratedVerb, "...+CLIENT_ARRAYS"}` = 通过。
|
||||
- **strict 车道**:主要形态是**环境变量车道而非 ctest 标签**(`f0-env.md:196`:
|
||||
`MOBILEGL_IPC_STRICT_ERRORS=1 ctest -L integration-split` + marker 直方图);另有少量
|
||||
自带旋钮的注册条目(`StrictProgramArm.*`,`CMakeLists.txt:2327-2352`,LABELS 故意只写
|
||||
`integration-split`,理由 `:2321-2326`)。
|
||||
- **预期红的双态门**:`test.yml:1207-1317`——同一车道在翻转常量
|
||||
(`kMGPipeP5eRunAheadReady`,grep 自 `Init.cpp`)前是"红即设计、跑 census",后是"硬绿 +
|
||||
两侧棘轮"。
|
||||
- **私有日志**:每条 split 条目一个 `MOBILEGL_LOG_FILE_PATH`,
|
||||
`SplitLogPaths.PrivateAndDistinct`(`CMakeLists.txt:2360-2364` +
|
||||
`Harness/split_log_paths.py` 的 `check` 模式 :183-188)断言私有且互不相同;
|
||||
markers 模式的日志集来自 `ctest --show-only=json-v1` 而非目录猜测(:49-70,ID-119)。
|
||||
- **棘轮**:`Harness/strict-expected-markers.txt`(52 行,10 对活着的
|
||||
Admitted `<field>@<verb>`)+ `split_log_paths.py` 的 `markers` 模式(:116-178)——
|
||||
**两侧都咬人**:出现了表里没有的对 → 红;表里的对不再出现 → 红(防"烂绿",:122-126)。
|
||||
Fatal 侧在 `no-fatal` 臂下任何一对即红。
|
||||
|
||||
### 6.2 双块车道的具体形状
|
||||
|
||||
1. **标签**:`integration-dualblock-split`(不含 `integration-split` 子串,正则安全;
|
||||
同样不带 `integration-gpu`,因为该标签下的车道必须能绿,`:2210-2211` 同论证)。
|
||||
2. **条目**:`gtest_discover_tests` 同一批 split scenario,`TEST_PREFIX
|
||||
"DirectGLES.Split.DualBlock."`——前缀仍在 `DirectGLES.Split.` 下,
|
||||
`split_log_paths.py` 的 `paths()`(:19 的前缀判定)与 markers 模式继续覆盖,无需改工具。
|
||||
env = split env + `MOBILEGL_IPC_ROLE_SPLIT_STATE=1` + 每条目私有
|
||||
`MOBILEGL_LOG_FILE_PATH`(照 F1./Ct. 块形态,`CMakeLists.txt:2367-2404`)。
|
||||
3. **CI 步**(test.yml 新增,照 :1360-1381 的双态形状):f1 落地时**预期红**——
|
||||
步断言 (a) 车道非绿,(b) 私有日志带 `Fatal{UnmigratedPipeInput, "..."} [BARRIER-PULLED,`
|
||||
形 marker(语法已在 `MARKER_RE`,`split_log_paths.py:44-46`),(c) 把 census 对表提交为
|
||||
新的预期文件(见 §7.4)。收口包翻转一个常量(照 `kMGPipeP5eRunAheadReady` 的
|
||||
grep-源码判臂法,`test.yml:1215`)后同一批条目转硬绿。
|
||||
4. **阴性对照(§6 出口门,ID-122 教训:对照要证伪机制本身)**:关掉双块必须让至少一条具名
|
||||
用例由绿转红。单纯"共享块恢复旧行为"是全绿的,所以对照用例必须是**为双块专门写的确定性
|
||||
断言**,建议:`DualBlockScenario.TheTwoRolesHaveDistinctBlocks`——经新增 Harness peek
|
||||
(照 `SplitRuntimePeek.h`/`PipeApplyPeek.h` 形态)断言两块地址不同、且 client 填一个 verb
|
||||
后 server 块的 `CurrentVerbSerial` 不动。knob 开 → 绿;knob 关(`ROLE_SPLIT_STATE=0`)→
|
||||
同一块 → 断言失败 → 红。确定性、无时序、不在 pre-flight 里 skip(ID-62),形状可直接进
|
||||
`scripts/ci/split_negative_controls.sh` 的 `run_control`(该脚本现成的合同:基线必须绿、
|
||||
红必须带被选条目自己的断言文本,`split_negative_controls.sh:60-99, 118-168`)。
|
||||
|
||||
---
|
||||
|
||||
## 7 f1 落地方案
|
||||
|
||||
### 7.1 块的分区形态(最小改动面)
|
||||
|
||||
`PipeInputs.h:835` 一带(`MOBILEGL_BUILD_DISAGGREGATED` 内,保持 pull/verify 构建符号不动):
|
||||
|
||||
```cpp
|
||||
// server 角色的块:后端经 MGB_CTX 读的、applier 写的、stamp 落的那一块。
|
||||
// gPipeInputs 的拼写不变,379 个 MGB_CTX 读点与 PipeApply.cpp 的写点零改动。
|
||||
inline PipeInputs& gPipeInputsServerBlock = *new PipeInputs();
|
||||
// client 角色的块:MG_Impl 的残余填充写的那一块。
|
||||
inline PipeInputs& gPipeInputsClientBlock = *new PipeInputs();
|
||||
inline PipeInputs& gPipeInputs = gPipeInputsServerBlock; // 读侧拼写,向后兼容
|
||||
|
||||
inline Bool MGPipeRoleSplitActive() {
|
||||
return MG_Config::Ipc.RoleSplitState &&
|
||||
MG_Config::Transport != MG_Config::TransportMode::Monolith;
|
||||
}
|
||||
// 填侧唯一新拼写。knob 关或 monolith:同一块,行为逐字节不变。
|
||||
inline PipeInputs& MGPipeClientInputs() {
|
||||
return MGPipeRoleSplitActive() ? gPipeInputsClientBlock : gPipeInputs;
|
||||
}
|
||||
```
|
||||
|
||||
- 两块都 `*new`(leak-at-exit,`PipeInputs.h:818-834` 的论证原样适用,每条目多一块几 KB)。
|
||||
- `MGPipeRoleSplitActive` 是进程寿命内常量;若要省热路径读,可在首次调用处 latch
|
||||
(照 `ArmVerify` 的 re-arm 形态,`PipeFill.cpp:598-624`),但 Config 读本身只是两次
|
||||
全局 load,填侧每 verb 一次,可接受。
|
||||
|
||||
### 7.2 拼写替换清单(client 侧,全在 `MG_Impl/Pipe/PipeFill.cpp`)
|
||||
|
||||
`gPipeInputs` → `MGPipeClientInputs()`:`:627`(ReportDivergence 的 serial/verb 取名)、
|
||||
`:653`(SnapshotFromGLContext 的 SetVerb)、`:703`(NoteFrontendMutation)、`:1217`
|
||||
(ReleaseResidualFillPins)、`:2205`(LeaveVerb)、`:3016`(VertexAttribDefaultsOf 读)、
|
||||
`:3348`(ValidateForVerb)。
|
||||
**例外留着不动**:`:663` 的 `&self == &gPipeInputs` 判等——hook 只该钉在**读侧块**上
|
||||
(server 块),knob 开时它与 verify 互斥(§4.2),knob 关时两块同物,两种臂都正确。
|
||||
|
||||
### 7.3 server 侧需要的三处配合
|
||||
|
||||
1. **`MGPipeServerClearVerbBoundary` 参数化**(`PipeInputs.cpp:307`):今天写死 gPipeInputs。
|
||||
拆成 `MGPipeServerClearVerbBoundaryFor(PipeInputs&)`(或直接让两个调用角色各调各的):
|
||||
applier 的 `LeaveApplier`(`PipeApplier.cpp:1261`)清 server 块;client 的两处
|
||||
(`PipeFill.cpp:3437, 2228`)清 client 块。knob 关时同一块,语义不变。
|
||||
2. **server 块身份**:`PipeApplier::Attach` 或首个 `StampVerbBoundary` 处给 server 块
|
||||
`SetIdentity`/置 `m_live`(§5.3.3),否则 `MGB_CTX_LIVE` 恒 false。
|
||||
3. **`CountBarrierPull` 的双块臂**(`PipeInputs.cpp:162-187`):`MGPipeRoleSplitActive()` 时
|
||||
无条件 `StrictBarrierPullFatal(field, verb, "MOBILEGL_IPC_ROLE_SPLIT_STATE=1")`——
|
||||
这是把 §3.3 的"无名崩溃"变成"具名红"的一行,也是计划 §4 "立刻变成 Fatal" 的机制落点。
|
||||
`rsp` 计数语义不变(knob 开时 BARRIER_PULLED 读都死了,rsp 恒 0,与出口门 3 的
|
||||
"rsp 逐帧读零"自洽)。
|
||||
|
||||
### 7.4 "红就是清单":红怎么收成清单
|
||||
|
||||
1. f1 落地当天跑双块车道,每条目在自己的私有日志里留下
|
||||
`Fatal{UnmigratedPipeInput, "<field>@<verb>"} [BARRIER-PULLED, MOBILEGL_IPC_ROLE_SPLIT_STATE=1, retires in <phase>]`。
|
||||
2. `split_log_paths.py markers` 已能解析并按条目归类 Fatal 对(:83-113, :133-136)。
|
||||
需要一个小扩展:一个 `expect-fatal` 臂,把 **Fatal 集合**与提交的预期文件做与
|
||||
`strict-expected-markers.txt` 完全同构的双向棘轮(出现新对 → 红;旧对消失 → 红)。
|
||||
新文件建议 `Harness/dualblock-expected-fatals.txt`,首版内容由 census 生成,
|
||||
**不由手写**(strict 文件 `:15-20` 的刷新规程照抄)。
|
||||
3. 之后每个 f 包(fm/fc/fs/fr/fv)退掉自己名下字段的载体,删对应行;文件空 + 车道绿 =
|
||||
收口(对照 P5f §5 的包图与 §6 出口门 1/2)。strict 车道的
|
||||
`strict-expected-markers.txt` 棘轮继续独立存在——双块车道管"值不在对面",
|
||||
strict 车道管"读没被承认",两者是同一债务的两个探针。
|
||||
|
||||
### 7.5 守卫与测试的兼容
|
||||
|
||||
- `RefusePipeInputsTouchWhileApplierOwnsIt`(`ClientSession.cpp:1290-1380`)保持原样:
|
||||
它守的是"GL 线程碰 server 拥有的块",knob 开时填侧不再碰 server 块,守卫自然不再触发;
|
||||
knob 关时行为不变。RemoteClientTest 的死亡测试(:2071, :2295, :2347)不受影响。
|
||||
- 单测/集成 GPU 车道 knob 默认关 → 单块,逐字节不变;FieldOwnershipTest/PipeInputsTest
|
||||
直读 `gPipeInputs`(server 块拼写),monolith 下同物。
|
||||
- G1:新增符号全部关在 `MOBILEGL_BUILD_DISAGGREGATED` 内(该头本就只在 PUSH 闭包,
|
||||
`PipeInputs.h:39-43`),pull 构建符号集不动。
|
||||
|
||||
---
|
||||
|
||||
## 8 风险与待确认
|
||||
|
||||
1. **O 类 SharedPtr 字段无 null 检查**(§3.3)——f1 的第一批红若不走 §7.3.3 的 Fatal 化,
|
||||
会以段错误形态出现,census 收集不到 marker。**这是 f1 落地顺序里必须先落的一行。**
|
||||
2. **`m_live`/`m_contextIdentity` 不属于任何字段归属类**(`PipeFill.cpp:3377-3385` 的具名
|
||||
偏离):双块后 server 块的身份由 §7.3.2 置一次,但**失语境**(context destroy)时谁把
|
||||
server 块的 `m_live` 打回 false——契约 §3.2 没说,f1 设计时要点名(候选:applier_reset
|
||||
控制记录,P5c ct 已让它过线)。
|
||||
3. **Magma 臂**:Magma 不发布 `kCapRunAheadApply`、跑 lockstep,九个 BARRIER_PULLED 字段
|
||||
全是它的读者。双块车道建议**双后端各注册一份**(照 `NamedBlit` 的双后端循环,
|
||||
`CMakeLists.txt:2423-2449`),Magma 侧的红就是 fm 包的清单——这与 §6 出口门 4
|
||||
"Magma 与 Espryt 跑同一套判据"直接对齐。
|
||||
4. **verify 构建**:`MOBILEGL_PIPE_VERIFY` 强制 lockstep + 全填(`ConfigLoader.cpp:377, 388`),
|
||||
与双块互斥的建议已写进 §4.2;若未来要 verify 双块臂,compare-at-read hook 的块归属要重做。
|
||||
5. `MG_Test` 里直接调 `MGPipeServerStampVerbBoundary` 的用例(PipeWireCodecTest :1366-1380、
|
||||
FieldOwnershipTest 约 15 处、RemoteClientTest :2393-2413)stamp 的都是 server 块拼写,
|
||||
knob 关时不变;若有人想在单测里开 knob,需要新 peek——目前无此需求,标为**未知/待 f1 定**。
|
||||
@@ -0,0 +1,228 @@
|
||||
# f0-egl — P5f §2.3 EGL / surface 控制面普查(只读)
|
||||
|
||||
> 基线 `feat/disaggregated @ 8b68b92c`(代码头 `25fba0d5`)。路径在 `MobileGL/` 下。
|
||||
> 本文件只读普查,不改任何代码。对应 P5c 审计 G4 行(`docs/Disaggregated/ROADMAP.md:71`),
|
||||
> 原计划记给 P6(`MobileGL/MG_Remote/CONTRACT-P5C.md:537`),P5f 计划 §2.3
|
||||
> (`docs/Disaggregated/P5F-WIRE-COMPLETENESS.md:181-186`)将其收回 P5f 的包 `fc`。
|
||||
|
||||
---
|
||||
|
||||
## 1. 单槽邮箱机制(现状)
|
||||
|
||||
**机制本体。** 十二个 forwarder 共享同一条过线通道:
|
||||
|
||||
- `ServerLoop.h:220`:`using ControlWork = MobileGLResult (*)(void* user);` —— 一个**裸函数指针**
|
||||
加一个 `void*`。注释(`ServerLoop.h:217-219`、`ServerLoop.cpp:784-787`)说明为何不是
|
||||
`std::function`:析构路径不得分配(ID-8)。
|
||||
- `ServerLoop.cpp:788-793`:每个 forwarder 用同一个无捕获 trampoline
|
||||
`+[](void* user) { return static_cast<Args*>(user)->Run(); }` 和**指向调用者栈上 `Args` 的
|
||||
指针**调用 `RunOnApplyThread`。
|
||||
- `ServerLoop.cpp:645-707`(`RunOnApplyThread`):`m_callerMutex` 串行化多个投递者(:653);
|
||||
在 `m_controlMutex` 临界区内发布 `{m_controlWork, m_controlUser, m_controlPending}` 四元组,
|
||||
影子位 `m_controlPosted` 最后发(:685-694);发布后再敲门铃(:702-704,先发布后敲门是
|
||||
Doorbell 的硬规则);然后阻塞在 `m_controlDone.wait`(:705),返回 `m_controlResult`(:706)。
|
||||
- `ServerLoop.cpp:533-568`(`PumpControlRequest`):apply 线程在 drain 批之间取走请求、
|
||||
执行 `work(user)`(:560)、回填结果并 notify(:561-567)。
|
||||
- 泵点:`ApplyThreadMain` 主循环 :397-398 与退出前 :453;park 谓词 `ready`(:391-395)把
|
||||
`ControlIsPending()` 列为唤醒条件之一。
|
||||
- 重入臂:`RunOnApplyThread:651`——apply 线程自己投递就内联执行(拆构路径
|
||||
`~BackendObject_DirectGLES` → `ReleaseEGLResources` 走这里)。
|
||||
- 无线程时的行为:backend 活着而线程不在 → `Fatal{ApplyThreadNotRunning}`(:670-676);
|
||||
backend 已不在 → 返回 `MOBILEGL_ERR_NOT_INITIALIZED`(:678-683)。
|
||||
|
||||
**邮箱字段**在 `ServerLoop.h:367-389`:`m_callerMutex` / `m_controlMutex` /
|
||||
`m_controlPosted`(一位影子)/ `m_controlDone` / `m_controlWork` / `m_controlUser` /
|
||||
`m_controlResult` / `m_controlPending` / `m_controlFinished`。
|
||||
|
||||
**今天为何能工作**:两个角色同进程、同地址空间。函数指针是双方都能执行的代码地址;
|
||||
`&args`、`major`/`minor`、`&handle` 指向的栈帧由阻塞握手(:705 的 wait)保证在整个执行期间存活;
|
||||
单槽够用是因为"verb barrier 同一时刻只留一个 client 线程可运行"(`ServerLoop.h:367-368`
|
||||
注释),加上 `m_callerMutex` 兜底。跨进程后这三样全部失效:代码地址、栈地址、以及"阻塞等待
|
||||
同进程另一个线程"本身。
|
||||
|
||||
**P5f 处置建议**:邮箱机制在 inproc 下整体保留(它是 apply 线程归属语义的一部分),但在其上
|
||||
叠一层"帧化"接缝:每个 forwarder 的参数先收敛成一个**纯值结构**(见 §4 草案),inproc 臂把该
|
||||
结构放在栈上传指针(现状等价),spawn 臂把同一结构编码进 `SurfaceOp` 帧。这样 fc 包不动
|
||||
线程模型,只动载荷的形状。
|
||||
|
||||
## 2. 十二个 forwarder 逐个点名
|
||||
|
||||
声明在 `ServerLoop.h:459-475`,定义在 `ServerLoop.cpp:802-1033`。下表"载荷"一列标出
|
||||
**值**(可直接序列化)/ **栈上指针**(指向调用者存储)/ **句柄值**(client 进程的 EGL 句柄,
|
||||
跨进程无意义)。
|
||||
|
||||
| # | forwarder(定义行) | 签名 | 语义 | 载荷分类 |
|
||||
|---|---|---|---|---|
|
||||
| 1 | `ServerInitializeEGLDisplay`(`ServerLoop.cpp:802-816`) | `Bool (EGLDisplay dpy, EGLint* major, EGLint* minor)` | 初始化 server 后端的 EGL display,回写主/次版本 | dpy 句柄值;**major/minor 是指向 client 栈的 out 指针**(:805-806) |
|
||||
| 2 | `ServerCreateEGLWindowSurface`(:818-834) | `Bool (EGLSurface surface, const WindowHandle& handle)` | 建窗口 surface;成功后 `ForgetCurrentTuple()`(:829,N-3) | surface 句柄值;**`&handle` 是指向 client 存储的指针**(:821/:832),且 `WindowHandle::Handle` 本身是 `void*`(`BackendObject.h:561-566`) |
|
||||
| 3 | `ServerResizeEGLWindowSurface`(:836-850) | `Bool (EGLSurface, Uint32 w, Uint32 h)` | 重建 swapchain | 全为值。**最干净的一个** |
|
||||
| 4 | `ServerCreateEGLPbufferSurface`(:852-870) | `Bool (EGLSurface, EGLint w, EGLint h)` | 建 pbuffer;成功后 `ForgetCurrentTuple()`(:865) | 全为值 |
|
||||
| 5 | `ServerMakeEGLCurrent`(:872-918) | `Bool (EGLDisplay, EGLSurface draw, EGLSurface read, EGLContext ctx)` | 经 `ApplyMakeCurrent`(:269-316)三分支:NativeBind / RepeatNoOp / ClientRelease(ID-54);真绑定后重发布 caps 快照(:899-913,R-12) | 四个全是句柄值 |
|
||||
| 6 | `ServerSwapEGLBuffers`(:920-933) | `Bool (EGLDisplay, EGLSurface draw)` | 直接在 server 上 present | 句柄值。**无生产调用者**(见 §5 发现 F8) |
|
||||
| 7 | `ServerSetEGLSwapInterval`(:935-946) | `void (Int interval)` | 设置交换间隔;返回值被丢弃(:945) | 值 |
|
||||
| 8 | `ServerReleaseEGLSurface`(:948-965) | `void (EGLSurface)` | 释放 surface;`ForgetCurrentTupleIfItNames`(:960,N-3) | 句柄值 |
|
||||
| 9 | `ServerReleaseEGLResources`(:967-989) | `void ()` | **按契约阻塞**(:967-973 注释:之后 `MobileGL::Destroy()` 立即走);`ForgetCurrentTuple()`(:984) | 无参数 |
|
||||
| 10 | `ServerInitCapabilities`(:991-1007) | `Bool ()` | 跑 server 端 `InitCapabilities` 并 `PublishCapsSnapshot()`(:999-1002) | 无参数 |
|
||||
| 11 | `ServerInitWindowSurface`(:1009-1020) | `Bool ()` | server 端 `InitWindowSurface` | 无参数。**无任何调用者**(见 §5 发现 F8) |
|
||||
| 12 | `ServerSetWindowHandle`(:1022-1033) | `void (const WindowHandle& handle)` | 把窗口句柄交给 server 后端 | **`&handle` 是指向 client 存储的指针**(:1024/:1031),内含 `void*` |
|
||||
|
||||
参数里的**函数指针**:每一条都是同一个 trampoline(:790-791),它承载的唯一信息是"调哪个
|
||||
`Args::Run`",即**操作的身份**。参数里的**栈上 `void*`**:`&args` 永远存在(:792),其内部再
|
||||
含 client 栈指针的是 #1(major/minor)、#2 与 #12(handle)。
|
||||
|
||||
**今天为何能工作**:同进程;阻塞握手(`RunOnApplyThread:705`)使调用者栈帧在整个 RPC 期间
|
||||
存活,所以 out 指针与引用参数安全。EGL 句柄值两边通用是因为同一个驱动在同一进程里认它们。
|
||||
|
||||
**P5f 处置建议(共性)**:
|
||||
- 函数指针 → `SurfaceOp.kind` 枚举(操作身份显式化)。
|
||||
- out 指针 → 帧的 reply 字段(#1 已被 `SurfaceReply.eglMajor/eglMinor` 覆盖)。
|
||||
- `WindowHandle` → `windowKind` + `nativeToken` + `width`/`height` 值字段;`Handle` 指针在
|
||||
Android 上是 `ANativeWindow*`(`P6-SPAWN-PLAN.md:38`),**子进程无意义,真窗口到达是 P12**,
|
||||
P5f 只在 server 侧落具名拒绝 `Fatal{UnmigratedSurface, "AndroidNativeWindow@P12"}`
|
||||
(`P6-CONTRACT-DRAFT.md:43` 已预写这个 Fatal 名)。
|
||||
- EGL 句柄值 → client 铸造的 token(`DisplayToken/SurfaceToken/ContextToken`,
|
||||
`P6-CONTRACT-DRAFT.md:41` 已裁定:client 铸造、从 1 起稠密、session 内不复用,server 持
|
||||
token → native 映射,永远不见 client 的 EGL 值)。`protocol.fbs:189-190` 的
|
||||
`display/surface: ulong` 已经就是 token 的宽度。
|
||||
|
||||
## 3. client 侧调用面(`BackendObject_Remote.cpp`)
|
||||
|
||||
九个 EGL 虚函数的路由(:196-310),核心纪律是"先转发,再跑基类"(:198-201 注释):
|
||||
|
||||
- `InitializeEGLDisplay`(:203-207)→ forwarder #1。
|
||||
- `CreateEGLWindowSurface`(:209-225)→ 先 #12(:214,"唯一告诉 server 窗口是哪扇的途径"),
|
||||
再 #2(:215),随后 `DrainPublishedEvents()`(:223)——surface-changed 事件走 SEG_EVENT。
|
||||
- `ResizeEGLWindowSurface`(:227-234)→ #3 + drain(:232)。
|
||||
- `CreateEGLPbufferSurface`(:236-243)→ #4 + drain(:241)。
|
||||
- `MakeEGLCurrent`(:245-276)→ #5,成功后 `PumpControlPlane()` + drain + 刷新格式能力
|
||||
(:265-274,codex 12:把 R-12 的"第二次到达即失效"闭合在造成它的那次 make-current 上)。
|
||||
- `SwapEGLBuffers`(:278-287)→ **不转发**:present 走记录(class-B `Present` emitter),
|
||||
注释明确 #6 "在 P5 没有调用者,这是刻意的"(:284-285)。
|
||||
- `SetEGLSwapInterval`(:289-297)→ #7。覆写的理由:基类会调 class-C 的 `SetSwapInterval`
|
||||
槽而 Fatal(:291-295 注释)。
|
||||
- `ReleaseEGLSurface`(:299-302)→ #8。
|
||||
- `ReleaseEGLResources`(:304-310)→ #9,按契约阻塞。
|
||||
- 另有 `InitCapabilities`(:106-142)调 #10(:125);`InitWindowSurface`(:144-149)是
|
||||
**纯 client no-op,不调 #11**。
|
||||
|
||||
**每个 forwarder 调用前的 fence**:`WaitForApplyBeforeEglForwarder`(:189-193),对除
|
||||
#6 外的每一次转发先 `WaitForApplyToCatchUp`。理由(:176-187 注释、CONTRACT-P5E §2.5
|
||||
`CONTRACT-P5E.md:177-180`):邮箱在 drain 批之间被泵,run-ahead 下控制请求会插进两条
|
||||
在飞记录之间。client 入口侧:九个虚函数由 `MG_Impl/EGLImpl/EGLImpl.cpp` 的 EGL 入口调
|
||||
(如 eglMakeCurrent → :284、eglSwapBuffers → :178、eglSwapInterval → :448、eglTerminate 路径
|
||||
→ :328)。
|
||||
|
||||
**P5f 处置建议**:这个 fence 在 spawn 下**仍然是必要的**——socket 控制通道与 SEG_CMD 是两条
|
||||
通道,控制帧一样可以超过在飞记录。fc 包的帧化必须保留"发帧前先等 `appliedSeq` 追上
|
||||
`LastPublishedSeq`"这一步;它不属于 wire 格式而属于发送纪律,应写进 fc 的契约行。
|
||||
|
||||
## 4. schema 对照与 wire 帧设计草案
|
||||
|
||||
### 4.1 现有 schema(`Protocol/protocol.fbs`,已定但无人使用)
|
||||
|
||||
- `SurfaceOpKind`(:166-175):`None / InitializeDisplay / CreateWindowSurface /
|
||||
CreatePbufferSurface / ResizeWindowSurface / ReleaseSurface / MakeCurrent / ReleaseCurrent`,
|
||||
共 7 个有效枚举。
|
||||
- `WindowKind`(:177-184):`None / AndroidNativeWindow / X11 / Win32Hwnd / Surfaceless /
|
||||
Pbuffer`。
|
||||
- `SurfaceOp`(:186-196):`seq, kind, display, surface, windowKind, nativeToken, width,
|
||||
height, swapInterval`。`nativeToken` 注释自述"Android transfers the window out of band"。
|
||||
- `SurfaceReply`(:198-204):`seq, ok, eglMajor, eglMinor, defaultFb`。
|
||||
- 信封:`CtrlMsg` union(:273-284)已含 `SurfaceOp/SurfaceReply`;`CtrlEnvelope`(:286-288)。
|
||||
|
||||
### 4.2 映射表:12 forwarder → op
|
||||
|
||||
| forwarder | 映射 | 缺的枚举 / 字段 |
|
||||
|---|---|---|
|
||||
| #1 InitializeEGLDisplay | `InitializeDisplay`;reply 的 `eglMajor/eglMinor` 正是 major/minor out 参数的线形 | 无 |
|
||||
| #2 CreateEGLWindowSurface | `CreateWindowSurface` + `windowKind`/`nativeToken`/`width`/`height` | 无(P12 前 server 具名拒绝 `AndroidNativeWindow`) |
|
||||
| #3 ResizeEGLWindowSurface | `ResizeWindowSurface` + `width`/`height` | 无 |
|
||||
| #4 CreateEGLPbufferSurface | `CreatePbufferSurface` + `width`/`height` | 无 |
|
||||
| #5 MakeEGLCurrent | `MakeCurrent`;其 ClientRelease 臂(`ClassifyEglMakeCurrent`,:257-267)落 `ReleaseCurrent` | **`SurfaceOp` 缺 `readSurface` 与 `context` 两个字段**:MakeCurrent 的语义单元是 (dpy, draw, read, ctx) 四元组,schema 只有一个 `surface` 且没有 context 字段 |
|
||||
| #6 SwapEGLBuffers | **不配帧**:present 走记录(`BackendObject_Remote.cpp:278-287`) | — |
|
||||
| #7 SetEGLSwapInterval | 用 `swapInterval` 字段 | **缺 `SetSwapInterval` 枚举** |
|
||||
| #8 ReleaseEGLSurface | `ReleaseSurface` | 无 |
|
||||
| #9 ReleaseEGLResources | 无参数,阻塞 | **缺 `ReleaseResources` 枚举** |
|
||||
| #10 InitCapabilities | **不配 SurfaceOp**:应答物是 `CapsSnapshot` 帧(server 已在 :999-1002 发布;`P6-CONTRACT-DRAFT.md:88-89` 同此裁定) | —(但 client 需要一个"等下一帧 CapsSnapshot"的同步点,见 4.4) |
|
||||
| #11 InitWindowSurface | **不配帧**:client 侧 no-op(:144-149),无调用者 | — |
|
||||
| #12 SetWindowHandle | 用 `windowKind`/`nativeToken`/`width`/`height` | **缺 `SetWindowHandle` 枚举** |
|
||||
|
||||
缺的三个枚举与 `P6-SPAWN-PLAN.md:76` 的预测("预期缺 `SetSwapInterval`/`ReleaseResources`/
|
||||
`SetWindowHandle`")**逐字吻合,本普查确认**。9 个需映射的 forwarder = 7 个现有枚举 + 3 个新增
|
||||
枚举 - 1 个复用(ReleaseCurrent 由 #5 的释放臂复用),与 `P6-CONTRACT-DRAFT.md:87-90` 的
|
||||
"the remaining nine map onto the seven plus §1's additions" 一致。追加枚举必须 append-only
|
||||
(schema 自述纪律在 :268-272 的 union 注释与 `CapsSnapshot` 的 deprecated 先例 :115-127)。
|
||||
|
||||
另外发现 **schema 缺的字段**(P6 计划未预写):
|
||||
1. `SurfaceOp` 缺 `readSurface`(MakeCurrent 需要 draw≠read 的场景)。
|
||||
2. `SurfaceOp` 缺 `context` token 字段(MakeCurrent/ReleaseCurrent 都要指名 ctx)。
|
||||
3. `WindowKind` 与 `WindowBackend`(`BackendObject.h:551-559`:Android/X11/MetalLayer/Win32)
|
||||
**不互洽**:`WindowKind` 缺 `MetalLayer`,多 `Surfaceless`/`Pbuffer`(那两个不是窗口后端)。
|
||||
需要一张显式映射表,不能按整数值直译。
|
||||
|
||||
### 4.3 帧设计草案(逐 forwarder)
|
||||
|
||||
公共规则:`seq` 由 client 单调铸造,`SurfaceReply.seq` 回声;携带 `ok=false` 等价今天的
|
||||
`Bool false` 或 void 丢弃结果;server 侧答不出来等价 `ServerBackendOrNull()` 为 null
|
||||
(:798)时的 `MOBILEGL_ERR_NOT_INITIALIZED`。token 化按 `P6-CONTRACT-DRAFT.md:41`。
|
||||
|
||||
| op 帧 | 载荷 | reply | 阻塞性 |
|
||||
|---|---|---|---|
|
||||
| `InitializeDisplay` | `display`(token) | `ok` + `eglMajor/eglMinor` | 阻塞(今天返回 Bool) |
|
||||
| `CreateWindowSurface` | `surface`(token) + `windowKind` + `nativeToken` + `width/height` | `ok` + `defaultFb`(见 4.4 的路线冲突) | 阻塞。可并入 #12(见下) |
|
||||
| `ResizeWindowSurface` | `surface` + `width/height` | `ok` + `defaultFb` | 阻塞 |
|
||||
| `CreatePbufferSurface` | `surface` + `width/height`(`windowKind=Pbuffer`) | `ok` | 阻塞 |
|
||||
| `MakeCurrent` | `display` + `surface`(draw) + **新字段 `readSurface`** + **新字段 `context`** | `ok` | 阻塞 |
|
||||
| `ReleaseCurrent` | `display`(其余 token 为 0 合法,`P6-CONTRACT-DRAFT.md:41` 已许) | `ok` | 阻塞(今天经同一个 Bool 通道) |
|
||||
| `SetSwapInterval`(新枚举) | `surface` + `swapInterval` | 可无 reply | **可改 fire-and-forget**:今天虽经阻塞邮箱但结果被丢弃(:945) |
|
||||
| `ReleaseSurface` | `surface` | 可无 reply | 可 fire-and-forget(:964 丢结果);但须保序 |
|
||||
| `ReleaseResources`(新枚举) | 无 | `ok` | **必须阻塞**:`ServerLoop.cpp:967-973` 的契约,`Destroy()` 在它返回后立即走 |
|
||||
| `SetWindowHandle`(新枚举) | `windowKind` + `nativeToken` + `width/height` | 可无 reply | 可 fire-and-forget,**但必须在后续 `CreateWindowSurface` 之前生效**——同一有序通道即保证 |
|
||||
|
||||
两个可合并项(供 fc 包定夺,本普查只摆出):① `CreateWindowSurface` 的 schema 字段已自带
|
||||
`windowKind/nativeToken/width/height`,#12 可以并进 #2,`SetWindowHandle` 枚举只在"先给窗口
|
||||
后建 surface"的时序必须拆开时才需要——今天的代码就是拆开的(:214-215),保守做法是两者都
|
||||
落;② `ReleaseCurrent` 也可用 `MakeCurrent` + 全 0 token 表达,现有枚举分开更好,保留。
|
||||
|
||||
### 4.4 三个与 schema 相邻的副作用,必须在 fc 的契约里点名
|
||||
|
||||
1. **caps 重发布**:#5 的真绑定臂(:899-913)和 #10(:999-1002)都会让 server 主动推一帧
|
||||
`CapsSnapshot`。跨进程后这是 server 自发帧;client 今天的吸收点(RPC 返回后
|
||||
`PumpControlPlane()`,:266-272 与 :130)天然对应"reply 之后泵控制通道"。schema 无需加
|
||||
字段,但 fc 应写明"MakeCurrent reply 到达后、下一帧 CapsSnapshot 到达前"的语义窗口。
|
||||
2. **`SurfaceReply.defaultFb` 与 SEG_EVENT 是重复路线**:P5c 已把默认 FBO 形状做成
|
||||
surface-changed 事件,client 在 create/resize reply 后 `DrainPublishedEvents()`
|
||||
(:223/:232/:241)。`defaultFb` 字段(:154-160, :203)若启用是第二载体。建议 fc 二选一
|
||||
并写明,不要两个都活(`CapsSnapshot` 的 deprecated 字段教训,:101-127,就是两种拼法
|
||||
并存的代价)。
|
||||
3. **server 死亡的具名化**:今天的 `Fatal{ApplyThreadNotRunning}`(:670-676)在 spawn 下
|
||||
对应 `FatalCode::ServerCrashed`(:240 已分配,`P6-CONTRACT-DRAFT.md:44` 已点名 P6 是
|
||||
第一个能产生它的阶段)。fc 的 client 侧发送路径要把"发不出去/等不到 reply"接到这个码上。
|
||||
|
||||
## 5. 其他发现
|
||||
|
||||
- **F8:两个 forwarder 没有生产调用者。** `ServerSwapEGLBuffers` 只有单测调用
|
||||
(`MG_Test/Wire/ServerLoopTest.cpp:1549`、`:1568`);`ServerInitWindowSurface` 全树无调用者
|
||||
(client 的 `InitWindowSurface` 是纯 no-op,`BackendObject_Remote.cpp:144-149`)。
|
||||
处置建议:fc 不为它们配帧(§4.2 已按此映射);`ServerLoop.h:469`/`:474` 的声明与
|
||||
实现在 spawn 世界只剩 inproc 测试价值,fc 可具名删除或标注 inproc-only。
|
||||
- **F9:文档行号漂移。** `ROADMAP.md:71`(P5c 审计 G4 行)引 `ServerLoop.cpp:543-598`,
|
||||
当前 HEAD 的邮箱是 `:533-568`(PumpControlRequest)+ `:645-707`(RunOnApplyThread);
|
||||
`CONTRACT-P5C.md:537` 把控制面记给 P6,P5f 计划 §2.3(`P5F-WIRE-COMPLETENESS.md:181-186`)
|
||||
已改记 P5f 的包 `fc`(`P5F-WIRE-COMPLETENESS.md:252`)。处置建议:fc 落地时顺手刷新
|
||||
ROADMAP G4 行的行号与去向。
|
||||
- **F10:`ServerLoop.h:436` 与 `ServerLoop.cpp:968` 引的 `EGLImpl.cpp` 行号**(:284/:326)
|
||||
与当前 `MG_Impl/EGLImpl/EGLImpl.cpp` 基本相符(:284 是 eglMakeCurrent;ReleaseEGLResources
|
||||
实际在 :328)。仅为记录,不构成缺陷。
|
||||
|
||||
## 6. 结论汇总
|
||||
|
||||
1. 十二个 forwarder 的过线载荷里,真正"不可序列化"的只有两类:**trampoline 函数指针**
|
||||
(操作身份 → `SurfaceOp.kind`)和**三处 client 栈/堆指针**(#1 的 major/minor → reply 字段;
|
||||
#2/#12 的 `WindowHandle*` → windowKind+nativeToken 值字段,Android 的 `ANativeWindow*`
|
||||
归 P12,P5f 只落具名拒绝)。其余参数全是值或 client 句柄值(→ token)。
|
||||
2. schema 缺口清点确认:**缺 3 个枚举**(SetSwapInterval / ReleaseResources /
|
||||
SetWindowHandle,与 P6-SPAWN-PLAN 的预测逐字一致)+ **缺 2 个字段**(MakeCurrent 的
|
||||
readSurface 与 context token)+ WindowKind/WindowBackend 需要显式映射表。
|
||||
3. #6、#10、#11 不配 SurfaceOp 帧:present 走记录、InitCapabilities 走 CapsSnapshot、
|
||||
InitWindowSurface 无调用者。需要帧化的是 9 个 forwarder、10 种 op(含 ReleaseCurrent)。
|
||||
@@ -0,0 +1,309 @@
|
||||
# P5f / f0 — 构建与门禁机制侦察(实现代理 cheatsheet)
|
||||
|
||||
> 只读普查产物。仓内引用以 `feat/disaggregated @ 8b68b92c`(代码头 `25fba0d5`)为准;
|
||||
> WSL 侧文件不在仓内,引用给绝对路径。未发起任何构建。
|
||||
|
||||
---
|
||||
|
||||
## 1 WSL 侧布局:树在哪、是什么关系
|
||||
|
||||
所有 WSL 树都是**同一个仓的 git worktree**(不是拷贝)。主 gitdir 在 `/home/swung/mgl/MobileGL`,
|
||||
`git worktree list` 在任何一棵树里都能看到全部(证据:在 `~/w7/p5e-int` 里执行
|
||||
`git worktree list` 列出 pipe / p5-* / p5e-* / land 等 20 棵树)。
|
||||
|
||||
| 树 | 分支 @ HEAD | 角色 |
|
||||
|---|---|---|
|
||||
| `~/w7/pipe` | `feat/disaggregated @ 11ac3de6` | **集成树**。比 Windows HEAD 落后 105 个提交(`11ac3de6..HEAD`),P5c 开工时的位置。`docs/Disaggregated/notes/p0/BRIEF-P0.md:13`:"DO NOT edit `~/w7/pipe` or `~/w7/base` - they are read-only references" |
|
||||
| `~/w7/base` | `dev @ 81b17c0b` | **性能锚点**,dev 分支,不动(`docs/Disaggregated/notes/p1/BRIEF-P1.md:3`) |
|
||||
| `~/w7/land` | `dev @ 81b17c0b` | 与 base 同 commit,带 `build-android/` 目录。用途未见诸阶段文档——**未知**(从目录形态看是 dev 的 Android 构建树;P5f 用不到) |
|
||||
| `~/w7/p5e-int` | `p5e-wave2-landed @ f23fbc1b` | P5e 集成树,**已过时**,见 §6.1 |
|
||||
| `~/w7/p5e-<slug>` | `p5e/<slug>` | P5e 各包树(c0e/fb/fix1/gl/id/mv/pa/pg/ra/ra2/sb/tx2/vi),每棵一个包 |
|
||||
| `~/w7/p5e-logs/` | — | p5e_gate.sh 的日志输出目录(每步一个 `<slug>-<step>.log`) |
|
||||
|
||||
每棵树的 remote 都有两个:`origin = /home/swung/mgl/MobileGL`(WSL 内部主仓),
|
||||
`winrepo = /mnt/c/Users/geekerwan/AndroidStudioProjects/FoldCraftLauncher/MobileGL`(Windows 侧)。
|
||||
|
||||
**Windows 侧关系**:工作区 `MobileGL-disagg` 是 `FoldCraftLauncher` 仓里 `MobileGL` 子模块的
|
||||
worktree(`MobileGL-disagg/.git` 文件指向
|
||||
`C:/Users/geekerwan/AndroidStudioProjects/FoldCraftLauncher/.git/modules/MobileGL/worktrees/MobileGL-disagg`)。
|
||||
Windows 侧(在该 worktree 执行 `git remote -v`)配了 **`wslpipe = //wsl.localhost/Arch/home/swung/w7/pipe`**,
|
||||
以及 `origin = github.com/MobileGL-Dev/MobileGL.git`、`hitmoe`、`fu3`。
|
||||
|
||||
### 1.1 代码怎么同步(非对称,两个方向机制不同)
|
||||
|
||||
- **Windows → WSL**:按 SHA fetch。`~/w7/notes/tools/p5e_new.sh` 的核心三行:
|
||||
```bash
|
||||
cd ~/w7/pipe
|
||||
git fetch -q /mnt/c/Users/geekerwan/AndroidStudioProjects/FoldCraftLauncher/.git/modules/MobileGL "$SHA"
|
||||
git branch -f "p5e/$SLUG" "$SHA" && git worktree add ~/w7/p5e-$SLUG "p5e/$SLUG"
|
||||
```
|
||||
前提是**先在 Windows 侧 commit**——WSL 看到的是 gitdir 里的对象,工作区改动过不去。
|
||||
p5e_new.sh 随后做 `git submodule update --init --recursive`、补拷
|
||||
`3rdparty/glslang/External`(worktree 不带子模块的嵌套 External)、写
|
||||
`.git/info/exclude` 加 `.p5e/`,然后直接调 `p5e_gate.sh $SLUG configure` + `build`。
|
||||
- **WSL → Windows**:在 Windows worktree 里 `git fetch wslpipe <branch>`。
|
||||
出处:`docs/Disaggregated/notes/p5e/FLIP-CHECKLIST.md:3-5`——"Integration tree
|
||||
`~/w7/p5e-int`(branch `p5e-wave2-landed`, fetched to the Windows worktree
|
||||
`MobileGL-disagg` via remote `wslpipe`, pushed to `origin/feat/disaggregated`)"。
|
||||
- **LFS 纪律**(trace fixture):`git worktree add` 不 hydrate LFS,新树里 41 个 fixture 有 40 个是
|
||||
130 字节指针。从 `~/w7/pipe/tools/trace_replay/fixtures` **拷贝**,并标 assume-unchanged;
|
||||
**永远不在 WSL 里 `git lfs pull` / `git lfs checkout`**(额度是既有规矩,且 checkout 会把真文件
|
||||
覆盖成指针)。出处:`~/w7/notes/tools/wsl_p5_gate.sh` Part 2 头注释("FIXTURES FIRST…"段)。
|
||||
- `~/w7/notes/` 本身已搬家:`~/w7/notes/MOVED-TO-REPO.txt`——415 篇 .md 已入仓
|
||||
`docs/Disaggregated/notes/<phase>/`(commit `2acea1b1`);留在 WSL 的只有脚本、日志、census
|
||||
数据、patch、APK、nm dump(含绝对路径与设备序列号,不入仓)。
|
||||
|
||||
### 1.2 现成脚本(`~/w7/notes/tools/`)
|
||||
|
||||
| 脚本 | 作用 |
|
||||
|---|---|
|
||||
| `p5e_new.sh <slug> <sha>` | 按 SHA 建 `~/w7/p5e-<slug>` 包树并 configure+build(见 §1.1) |
|
||||
| `p5e_gate.sh <slug> <step> [regex]` | **P5e 门禁本体**,step ∈ configure/build/unit/isplit/gpu/strict/flavours/gens/one,逐条见 §4 |
|
||||
| `wsl_p5_gate.sh [quick]` | P5 时代的五段式大门(env `MGL_P5_TREE` 指定树),四 flavour 全建 + retrace 扫描;`flavours` 的 verify 臂只在它这里有(§3) |
|
||||
| `wsl_p4a_gate.sh` / `wsl_p3a_gate.sh` / `wsl_p2_gate.sh` 等 | 各阶段同构老门,仅供考古 |
|
||||
| `wsl_integrate.sh` / `p5_merge_*.sh` / `p5_land_*.sh` | 各阶段合并/落地脚本,阶段专用 |
|
||||
|
||||
WSL 工具链事实(`docs/Disaggregated/notes/p1/BRIEF-P1.md:3`):clang 22.1.6 在
|
||||
`/usr/sbin/clang++`(没有 `clang++-20`);28 核;ccache 配 `CCACHE_BASEDIR=/home/swung/w7`。
|
||||
|
||||
---
|
||||
|
||||
## 2 仓内门脚本(`scripts/` 与 `scripts/ci/`)
|
||||
|
||||
全部入口都是 `python3 scripts/xxx.py` / `bash scripts/ci/xxx.sh`,在仓根跑。P5e gate 的 `gens`
|
||||
步(`~/w7/notes/tools/p5e_gate.sh`)就是这套的最小全集:
|
||||
|
||||
```bash
|
||||
python3 scripts/gen_pipe.py --check # G1..G7 七个生成器,产物已提交,重生成有 diff 即红
|
||||
python3 scripts/gen_pipe.py --self-test # 生成器自己的阴性对照
|
||||
python3 scripts/gen_pipe_field_ownership.py --check # 70 行归属表(63 字段 + 7 sticky)每行恰好一类,有洞即红
|
||||
python3 scripts/gen_pipe_field_ownership.py --self-test
|
||||
python3 scripts/gen_pipe_field_ownership.py --print-admitted # 打印"允许 admitted 的 <field>@<verb>",strict 棘轮的另一臂
|
||||
python3 scripts/check_include_closure.py # 三个头的 include 闭包(value/artifacts/wire 三 probe)
|
||||
python3 scripts/gen_pipe_dirty_surface.py --check # 脏面扫描:mutator→aggregate generation 映射
|
||||
python3 scripts/gen_pipe_dirty_surface.py --self-test
|
||||
python3 scripts/check_doc_citations.py --rev HEAD --strict docs/Disaggregated/*.md MobileGL/MG_Remote/CONTRACT-P5*.md
|
||||
```
|
||||
|
||||
细节:
|
||||
|
||||
- `scripts/gen_pipe.py`(文件头 docstring):读 `PipeCalls.def / PipeFields.def / Coverage.def /
|
||||
FillPoints.def` + `scripts/data/backend_read_inventory.md`,写 `MobileGL/MG_Pipe/generated/*.inc`,
|
||||
产物是**提交的**,CI 重生成比对。无参 = 写文件;`--check` = 只校验;`--self-test` = 阴性对照。
|
||||
- `scripts/gen_pipe_field_ownership.py`(文件头):RECORD_SUPPLIED 是**推导**出来的
|
||||
(Coverage.def emitted − PipeFill.cpp 拒绝表),所以映射文件不可能与 emitter 漂移还保持绿。
|
||||
- `scripts/check_include_closure.py`(文件头):`--mode text|clang|both`,ctest 里跑的是 text
|
||||
(CI test job 的 runner 没子模块);`--require-all` 把 SKIP 变红(棘轮)。
|
||||
- `scripts/gen_protocol.py`:flatc 解析顺序 `--flatc` / `MOBILEGL_FLATC_EXECUTABLE` → 从
|
||||
`3rdparty/flatbuffers` 源码建到仓外 `<repo>/../flatc-build`(`MOBILEGL_FLATC_BUILD_DIR` 可改)。
|
||||
- `scripts/check_doc_citations.py`:解析 md 里的 `path:line` 引用对 git rev 校验,只有
|
||||
`--strict` 才非零退出。
|
||||
- `scripts/ci/split_negative_controls.sh [--self-test]`:E1(verb barrier)与 E3(a)(persistent-map
|
||||
push)的阴性对照,是 `.github/workflows/test.yml:1097` 那个 step 的本体。环境变量
|
||||
`CTEST`(默认 ctest)等覆盖点在文件头 usage 段。两条规矩(文件头):基线必须绿(数 PASSED)、
|
||||
红必须带被选条目自己的失败文本。
|
||||
- `scripts/ci/control_smoke_test.sh`:用 stub ctest 跑上面真脚本,验证"该红时红"(R-16)。
|
||||
- `scripts/ci/redcheck_control_smoke_test.sh`:smoke test 自己的 red-once(临时 revert 证据检查)。
|
||||
- `scripts/ci/retrace_pull_library_control.sh` / `retrace_drop_draw_control.sh`:retrace-split
|
||||
车道的两个阴性对照(pull 库必须红于 transport 断言;丢 draw 必须红于 SSIM)。retrace 本体是
|
||||
WSL 侧 `~/w7/retrace_gate.py`(不在仓内)。
|
||||
- `scripts/ci/census_junit.py` / `junit_tally.py`:JUnit XML 统计辅助。
|
||||
- CI 接线点:`.github/workflows/test.yml:1097`(negative controls)、`:1249`
|
||||
(`--print-admitted`)、`:2568-2576`(gen_pipe)、`:2616-2617`(dirty surface)、
|
||||
`:2637-2638`(field ownership)、`:2736`(doc citations,`|| true` 非 strict)。
|
||||
|
||||
---
|
||||
|
||||
## 3 四个构建 flavour 与 CMake 强制规则
|
||||
|
||||
开关定义在仓根 `CMakeLists.txt`:
|
||||
|
||||
| 开关 | 行 | 含义 |
|
||||
|---|---|---|
|
||||
| `MOBILEGL_BUILD_DISAGGREGATED` | `CMakeLists.txt:23` | 编译 MG_Remote 传输层(两进程形态) |
|
||||
| `MOBILEGL_BUILD_DISAGGREGATED_INPROC` | `CMakeLists.txt:30` | 编译 inproc 角色隔离 shim;蕴含上一项(`:460-463`) |
|
||||
| `MOBILEGL_PIPE_PUSH` | `CMakeLists.txt:35` | 后端经 PipeInputs 块读前端状态 |
|
||||
| `MOBILEGL_PIPE_VERIFY` | `CMakeLists.txt:36` | 编译 SnapshotFromGLContext + G4 逐 verb 影子比对器;蕴含 PUSH(`:506-508`),永不发布 |
|
||||
|
||||
**强制规则(全是普通变量遮蔽,不写 cache——所以 CMakeCache 不可信,见 ID-124)**:
|
||||
|
||||
- `DISAGGREGATED=ON` 缺 flatbuffers 子模块 → 静默降级为 OFF(`:471-486`)。
|
||||
- **`DISAGGREGATED=ON` 强制 `PIPE_PUSH=ON`(`:496-500`)——不存在 "disaggregated pull"**。
|
||||
同时传 `-DMOBILEGL_PIPE_PUSH=OFF` 会被静默覆盖,cache 里仍显示 OFF。
|
||||
- `PIPE_PUSH=OFF` 强制 `PIPE_LEGACY_MEMOS=ON`(`:513-517`)——pull 构建里 legacy 臂是唯一臂。
|
||||
- 生效宏落点:`:652-659`(`-DMOBILEGL_BUILD_DISAGGREGATED=1` / `-DMOBILEGL_PIPE_PUSH=1` /
|
||||
`-DMOBILEGL_PIPE_VERIFY=1`)。
|
||||
|
||||
四个 flavour 的配法(`~/w7/notes/tools/wsl_p5_gate.sh:63-66` 与 `p5e_gate.sh` 的 flavours 步):
|
||||
|
||||
| flavour | 构建目录 | 开关 |
|
||||
|---|---|---|
|
||||
| pull | `build-linux`(老门)/ `build-pull`(p5e) | `DISAGGREGATED=OFF INPROC=OFF PIPE_PUSH=OFF` |
|
||||
| push | `build-push` | `DISAGGREGATED=OFF INPROC=OFF PIPE_PUSH=ON` |
|
||||
| verify | `build-verify` | `PIPE_VERIFY=ON` + `MOBILEGL_ITEST_REQUIRE_GPU=ON`(只在 wsl_p5_gate.sh 里有;p5e_gate.sh 不建它) |
|
||||
| split | `build-split` | `PIPE_PUSH=ON DISAGGREGATED=ON DISAGGREGATED_INPROC=ON` |
|
||||
|
||||
**ID-124 教训(p5e_gate.sh flavours 步头注释原文)**:每个 flavour 配完后必须先从
|
||||
`compile_commands.json` 里 grep 生效宏再放行构建——cache 条目会说谎:
|
||||
|
||||
```bash
|
||||
got=$(grep -o 'MOBILEGL_PIPE_PUSH=1' "build-$fl/compile_commands.json" | head -1)
|
||||
# pull 要 absent,push 要 1,不等就 ARM CHECK FAILED
|
||||
```
|
||||
|
||||
公共 configure 串(p5e_gate.sh 的 `$COMMON`):`-G Ninja`、clang/clang++、ccache、
|
||||
`-DCMAKE_BUILD_TYPE=Release`、`-DMOBILEGL_LOG_ACTIVE_LEVEL=MOBILEGL_LOG_LEVEL_INFO`、
|
||||
`-DMOBILEGL_BUILD_TEST=ON -DMOBILEGL_BUILD_INTEGRATION_TEST=ON -DMOBILEGL_BUILD_BENCHMARK=OFF`、
|
||||
`-DMOBILEGL_ITEST_EGL_VENDOR=/usr/share/glvnd/egl_vendor.d/50_mesa.json`、
|
||||
`-DMOBILEGL_ITEST_VK_ICD=/usr/share/vulkan/icd.d/lvp_icd.json`、
|
||||
`-DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_POLICY_VERSION_MINIMUM=3.5`。
|
||||
|
||||
**C-10 教训**(wsl_p5_gate.sh:8-11 头注释):构建结果要用产物的存在来证明——`cmake --build`
|
||||
拿错目录时 `$?` 也是 0(cmake 打 usage),所以每个 build 目录先查 `CTestTestfile.cmake`
|
||||
再采信(`wsl_p5_gate.sh:74`)。
|
||||
|
||||
**`~/w7/p5e-int` 实测**(只读查 compile_commands.json):`build-pull` 无任何宏、`build-push`
|
||||
有 `MOBILEGL_PIPE_PUSH=1`、`build-split` 有 `PUSH=1 + DISAGGREGATED=1`、`build-nodisagg` 无宏;
|
||||
**没有 build-verify**。`verify` flavour 在 P5e 只在 retrace 扫描里以
|
||||
`MOBILEGL_PIPE_VERIFY=1` 环境变量的形式出现(wsl_p5_gate.sh:270)。
|
||||
|
||||
---
|
||||
|
||||
## 4 车道与 P5e gate 脚本
|
||||
|
||||
### 4.1 ctest 标签
|
||||
|
||||
| 标签 | 注册处 | 说明 |
|
||||
|---|---|---|
|
||||
| `unit` | `MobileGL/MG_Test/**/CMakeLists.txt`(gtest_discover_tests … LABELS unit,如 `MobileGL/MG_Test/Pipe/CMakeLists.txt:215`) | 单测 |
|
||||
| `integration-split` | `MobileGL/MG_IntegrationTest/CMakeLists.txt:1917-2364` | 拆分车道。大多数条目同时带 `integration-gpu`(`:1917` 等 `LABELS "integration-gpu\;integration-split"`);**`integration-split` 不等于 "split transport"**——`:1944` 注释明说 monolith 臂也带这个标签。strict 专用条目只带 `integration-split`(`:2339, :2349`)。`integration-split-clientarrays` 会被 `-L integration-split` 匹配(子串,`:2206-2209`) |
|
||||
| `integration-gpu` | 同上 | GPU 车道。`MOBILEGL_ITEST_REQUIRE_GPU` 把"无 GPU 全 skip"从绿变红,否则该标签不可证伪(`MobileGL/MG_IntegrationTest/CMakeLists.txt:280-285`) |
|
||||
| `integration-verify` | `:1695-1726` | verify comparator 车道(`-DMOBILEGL_PIPE_VERIFY=ON` 构建里跑) |
|
||||
|
||||
### 4.2 p5e_gate.sh 各 step(`~/w7/notes/tools/p5e_gate.sh`,逐字摘录的命令)
|
||||
|
||||
用法:`bash ~/w7/notes/tools/p5e_gate.sh <slug> <step> [regex]`,在 `~/w7/p5e-<slug>` 里跑,
|
||||
日志落 `~/w7/p5e-logs/<slug>-<step>.log`。集成树就是 slug=int(FLIP-CHECKLIST.md:5:
|
||||
"`G` = `bash /home/swung/w7/notes/tools/p5e_gate.sh int <step>`")。
|
||||
|
||||
| step | 命令本体 |
|
||||
|---|---|
|
||||
| `configure` | `cmake -S . -B build-split $COMMON -DMOBILEGL_BUILD_DISAGGREGATED=ON -DMOBILEGL_BUILD_DISAGGREGATED_INPROC=ON -DMOBILEGL_PIPE_PUSH=ON` |
|
||||
| `build` | 无 cache 先 configure;`cmake --build build-split -j $(nproc)` |
|
||||
| `unit` | `GLIBC_TUNABLES=glibc.malloc.tcache_count=0 ctest --test-dir build-split -L unit -j $(nproc) --output-on-failure`(GLIBC_TUNABLES 是 ID-18/21:让 exit-order UAF 可复现) |
|
||||
| `isplit` | `ctest --test-dir build-split -L integration-split --no-tests=error -j 4 --output-on-failure` |
|
||||
| `gpu` | `ctest --test-dir build-split -L integration-gpu --no-tests=error -j 4 --output-on-failure`。**不是可选**:它是 push 构建的 monolith 运行时臂(ID-109 的洞——137 个 SEGFAULT 在 38 个 scenario 上无门通过,fix1/ID-107);-j4 约 10 分钟,定为集成分支步骤而非每包步骤 |
|
||||
| `strict` | `MOBILEGL_IPC_STRICT_ERRORS=1 ctest --test-dir build-split -L integration-split --no-tests=error -j 4 --output-on-failure`,然后 `grep -ho 'Fatal{UnmigratedPipeInput, "[^"]*"}'` 做首错直方图。**strict 是环境变量车道,不是独立 ctest 标签** |
|
||||
| `flavours` | rm -rf 重建 build-pull / build-push,每个先过 compile_commands.json 宏断言再构建(§3 ID-124) |
|
||||
| `gens` | §2 列的那串 + `check_doc_citations.py --rev HEAD --strict` |
|
||||
| `one <regex>` | `ctest --test-dir build-split -R <regex> --output-on-failure`,附 Fatal 上下文摘录 |
|
||||
|
||||
关键运行时环境变量:
|
||||
|
||||
| 变量 | 取值/出处 |
|
||||
|---|---|
|
||||
| `MOBILEGL_TRANSPORT` | `monolith \| inproc \| spawn \| unix:<path> \| pipe:<name>`(`MobileGL/Config.h:425`);pull 库里 `inproc` 被静默忽略(`Config.h:436`)——这就是 retrace pull 阴性对照存在的理由 |
|
||||
| `MOBILEGL_IPC_STRICT_ERRORS` | `MobileGL/ConfigLoader.cpp:393` 读取;BARRIER-PULLED 读由计数升为 Fatal(`MobileGL/Config.h:534`、`MobileGL/MG_Backend/MGPipe/PipeInputs.cpp:183`) |
|
||||
| `MOBILEGL_IPC_RUN_AHEAD` | `MobileGL/Config.h:508`(P5e,`MG_Remote/CONTRACT-P5E.md` §1) |
|
||||
| `MOBILEGL_IPC_AUDIT` | `=1` 时读 `0xDD` 毒化即红(BRIEF-P5E.md §3.7) |
|
||||
| `MOBILEGL_IPC_VERB_BARRIER=0` | **不要拿它当对照**(ID-114,FLIP-CHECKLIST.md Phase E):它是 `RunAheadArmed()` 的第一合取项,副作用是关掉 run-ahead,且秒死于 `Fatal{UnmigratedPipeInput, "IsCapabilityEnabled@ClientWaitSync"}` |
|
||||
| `MOBILEGL_ITEST_REQUIRE_GPU` | 见 §4.1;configure 期 option 会注入公共 ENVIRONMENT(`MobileGL/MG_IntegrationTest/CMakeLists.txt:303-306`) |
|
||||
| `MOBILEGL_LOG_FILE_PATH` | 每个 split 条目一条私有库日志(split_log_paths.py 的职责就是校验私有且互不重名);库的 Fatal 只在私有日志里——**ctest 控制台 transcript 对库输出是假零**(split_log_paths.py evidence 模式错误信息原文) |
|
||||
|
||||
### 4.3 P5e 的落地流程(FLIP-CHECKLIST 摘要)
|
||||
|
||||
`docs/Disaggregated/notes/p5e/FLIP-CHECKLIST.md`:Phase A 按 gl→pa→mv 序合并,每次合并后
|
||||
`G build && G unit && G isplit && G gpu && G gens`;Phase B 翻转前记录四条基线
|
||||
(`RUN_AHEAD=0` / `IPC_AUDIT=1` 各 116/116、`STRICT_ERRORS=1 ctest -L unit` 2250/2250、
|
||||
默认臂日志含 "run-ahead requested … running lockstep");Phase D 翻转是一个 commit 同时落
|
||||
`kMGPipeP5eRunAheadReady`(`MG_Backend/Init.cpp:98`)与
|
||||
`kMGPipeP5eClientWaitRuleLanded`(`MG_Pipe/PipeApply.h:969`)——分开落会死。
|
||||
|
||||
---
|
||||
|
||||
## 5 strict-expected-markers.txt 刷新流程
|
||||
|
||||
文件:`MobileGL/MG_IntegrationTest/Harness/strict-expected-markers.txt`。当前 HEAD 上活着
|
||||
**10 对** `<field>@<verb>`(`:34-52`:8 对表 admission + 2 对 escalation-only 的
|
||||
client-vertex-array 条目,P8 的)。
|
||||
|
||||
它是双向棘轮(`:11-13`):表里有的对子若不再出现 → 在退役它的那个 commit 里删行;
|
||||
出现新对子 → 加行并注明归哪个阶段,或直接退役它。
|
||||
|
||||
**刷新流程(`:15-20` 原文,禁止手猜)**:
|
||||
|
||||
```bash
|
||||
ctest -L integration-split --show-only=json-v1 > lane.json
|
||||
MOBILEGL_IPC_STRICT_ERRORS=1 ctest -L integration-split -j 4
|
||||
python3 MobileGL/MG_IntegrationTest/Harness/split_log_paths.py markers lane.json . \
|
||||
<(python3 scripts/gen_pipe_field_ownership.py --print-admitted) strict-expected-markers.txt
|
||||
# 把它打印的 census 贴回文件
|
||||
```
|
||||
|
||||
配套事实:
|
||||
|
||||
- marker 语法在写出处定义(`split_log_paths.py` 的 `MARKER_RE`):
|
||||
`Fatal{UnmigratedPipeInput, "<F>@<V>"}` 与 `Admitted{UnmigratedPipeInput, "<F>@<V>"}
|
||||
[BARRIER-PULLED, ADMITTED[-ESCALATED], retires in <phase>]` 两种形态。
|
||||
- 日志集来自 `ctest --show-only=json-v1` 而不是目录扫描(ID-119:目录是猜测,F1. readback
|
||||
块、NamedBlit、Ct.、PersistentMapArm、DirectVulkan 条目都不在 split-logs/ 目录里)。
|
||||
- census 是**首错直方图不是清单**(FLIP-CHECKLIST.md Phase B.2):退役一行后那 62 个条目不会
|
||||
变绿,会推进到各自的下一个 pull。
|
||||
- 重测 census 要按 `ctest -L integration-split -N -V` 逐条驱动环境——`PersistentMapArm.` /
|
||||
`SmallRing.` / `NamedBlit.` 前缀带额外 ENVIRONMENT,固定环境的 census 对 14/109 是错的
|
||||
(FLIP-CHECKLIST.md Phase B.1)。
|
||||
|
||||
---
|
||||
|
||||
## 6 P5f 开工前必须知道的三件事
|
||||
|
||||
### 6.1 `~/w7/p5e-int` 不能直接用
|
||||
|
||||
p5e-int 停在 `f23fbc1b`(branch `p5e-wave2-landed`),它**不是** Windows HEAD `8b68b92c` 的
|
||||
祖先(`git merge-base --is-ancestor` 判定否)——P5e 收官后 Windows 侧分支被重述过
|
||||
(同一批提交、中英文两版 message、不同 SHA;`f23fbc1b` 对象本身在 Windows 仓里存在,
|
||||
`git cat-file -t f23fbc1b` = commit)。P5f 的正确姿势是从当前代码头 `25fba0d5`(或 HEAD
|
||||
`8b68b92c`,其后的提交只动文档)按 §1.1 新建树:`p5e_new.sh` 的形态照抄,slug 换 `p5f-<x>`。
|
||||
|
||||
### 6.2 gate 脚本要复制改造,不要直接复用
|
||||
|
||||
`p5e_gate.sh` 硬编码 `~/w7/p5e-$SLUG` 与 `~/w7/p5e-logs`。P5f 应复制为 `p5f_gate.sh`
|
||||
(树改 `~/w7/p5f-$SLUG`)。P5f 新增的双块车道(P5F-WIRE-COMPLETENESS.md §4,旋钮建议名
|
||||
`MOBILEGL_IPC_ROLE_SPLIT_STATE=1`)应加成新 step,形态照 `strict` 步(环境变量 + 同一标签 +
|
||||
marker 直方图)。**阴性对照要写进脚本**:关掉双块必须让至少一条具名用例由绿转红
|
||||
(P5F §6,ID-122 的教训)——参照 `scripts/ci/split_negative_controls.sh` 的"红必须带被选条目
|
||||
自己的失败文本"规矩。
|
||||
|
||||
### 6.3 门禁的已知盲点都是"未被构建/未被运行的臂"
|
||||
|
||||
ID-107/109(运行时臂无门:push 构建的 monolith 臂漏跑 → 137 SEGFAULT)、ID-123(gate 只配
|
||||
build-split,pull flavour 根本没建)、ID-124(cache 与生效宏不符的伪 pull 构建)、ID-122
|
||||
(对照要证伪机制本身)。P5f 加任何新车道时按这个清单自查:**配了吗、建了吗、跑了吗、宏对吗、
|
||||
红过一次吗**。
|
||||
|
||||
---
|
||||
|
||||
## 7 常用命令速查(Windows 侧发起,WSL 执行)
|
||||
|
||||
```bash
|
||||
# 建 P5f 包树(先在 Windows worktree commit,拿到 SHA)
|
||||
wsl.exe -e bash -lc 'bash ~/w7/notes/tools/p5e_new.sh <slug> <sha>' # 脚本内路径写死 p5e-,P5f 需先复制改造
|
||||
|
||||
# 集成树门禁(P5e 形态,P5f 复制后替换 slug 前缀)
|
||||
wsl.exe -e bash -lc 'bash ~/w7/notes/tools/p5e_gate.sh int build'
|
||||
wsl.exe -e bash -lc 'bash ~/w7/notes/tools/p5e_gate.sh int unit'
|
||||
wsl.exe -e bash -lc 'bash ~/w7/notes/tools/p5e_gate.sh int isplit'
|
||||
wsl.exe -e bash -lc 'bash ~/w7/notes/tools/p5e_gate.sh int gpu' # 集成分支步骤,非每包
|
||||
wsl.exe -e bash -lc 'bash ~/w7/notes/tools/p5e_gate.sh int strict'
|
||||
wsl.exe -e bash -lc 'bash ~/w7/notes/tools/p5e_gate.sh int flavours'
|
||||
wsl.exe -e bash -lc 'bash ~/w7/notes/tools/p5e_gate.sh int gens'
|
||||
wsl.exe -e bash -lc 'bash ~/w7/notes/tools/p5e_gate.sh int one <regex>'
|
||||
|
||||
# 仓内门(Windows 侧 Git Bash 也能跑,与 WSL 等价)
|
||||
python3 scripts/gen_pipe.py --check && python3 scripts/gen_pipe.py --self-test
|
||||
python3 scripts/gen_pipe_field_ownership.py --check && python3 scripts/gen_pipe_field_ownership.py --self-test
|
||||
python3 scripts/gen_pipe_dirty_surface.py --check && python3 scripts/gen_pipe_dirty_surface.py --self-test
|
||||
python3 scripts/check_include_closure.py
|
||||
python3 scripts/check_doc_citations.py --rev HEAD --strict docs/Disaggregated/*.md MobileGL/MG_Remote/CONTRACT-P5*.md
|
||||
bash scripts/ci/split_negative_controls.sh # 需要 build-split 与 bin 目录环境,一般在 WSL 跑
|
||||
```
|
||||
@@ -0,0 +1,408 @@
|
||||
# f0 普查 · §2.1 BARRIER_PULLED 15 字段逐字段载体判定
|
||||
|
||||
> 基线:`feat/disaggregated @ 8b68b92c`(代码头 25fba0d5 之后的提交只动文档)。行号均以该 HEAD 为准。
|
||||
> 范围:`MobileGL/MG_Pipe/FieldOwnership.def` 的 21 行 / 15 个 BARRIER_PULLED 字段,逐字段回答
|
||||
> (a) 存储形态 (b) 后端读者与 verb (c) 现有记录载体 (d) P5f 处置 (e) 依赖 / 工作量 / 包归属。
|
||||
> 只读普查:本文件是唯一产物,未改任何代码、未构建、未跑测试。
|
||||
> 姊妹篇:`f0-magma.md`(Magma 侧逐站点清单,本报告与之交叉引用而不重复)、`f0-reverse-channel.md`。
|
||||
|
||||
## 0. 总判断
|
||||
|
||||
1. **15 个字段全部涉及 client 地址空间,无一例外**。9 个非 sticky 行的存储全部是前端堆引用——
|
||||
4 个 `SharedPtr`(`m_boundVertexArray` / `m_programForDraw` / `m_programForDispatch` /
|
||||
`m_transformFeedbackProgram`,PipeInputs.h:807、:812-814)+ 5 个裸指针基址/槽
|
||||
(`m_bufferBindingSlot[15]` :808、`m_bufferBindingPointBase[15]` :809、`m_framebufferBindingSlot[2]`
|
||||
:810、`m_imageTextureBindingBase` :811、`m_textureUnitBase` :815);6 个 sticky forward 里
|
||||
3 个直接交出前端对象或写前端(GetProgramObject / GetTextureObject / RecordError),
|
||||
3 个是值类查找(GetBufferBindingPointCount / HasOpenTransformFeedbackSpan / ValidateProgramName)。
|
||||
21 行 = 15 字段行 + 6 行 forward 行(FieldOwnership.def:53-165 与 :178-192)。
|
||||
2. **P5e 的教训("数据已在线上,读者没跟上")在 15 个里命中 11 个**。已有记录供给或线上已带等价
|
||||
句柄的:GetBoundVertexArray(`bind_vertex_elements`→`BoundVertexElements`)、GetBufferBindingPoint
|
||||
(`set_shader_buffers`→`BoundShaderBuffers`,**XFB 目标除外**)、GetFramebufferBindingSlot
|
||||
(`set_framebuffer_state`→`BoundFramebuffer`)、GetImageTextureBinding(`set_shader_images`)、
|
||||
GetProgramForDraw / GetProgramForDispatch(`set_draw_program` / `set_dispatch_program`→
|
||||
`DrawProgram` / `DispatchProgram`)、GetTextureUnitObject(`set_sampler_views`→`BoundSamplerViews`)、
|
||||
GetTextureObject(`MGPCopyRegion.Src/Dst` 句柄就在 GL 名旁边,MGPipeTypes.h:1468-1476)、
|
||||
GetProgramObject + ValidateProgramName(`MGPStorageBlockBinding.ShaderCso` 就在 GlName 旁边,
|
||||
MGPipeTypes.h:1791-1792)、RecordError(`kEventGlError` 已接线,PipeFill.cpp:2166-2178)。
|
||||
**需要扩记录或新载体的只有 4 个**:GetTransformFeedbackProgram(`begin_stream_output` 只带
|
||||
PrimitiveMode,CONTRACT-P5B.md:215)、GetBufferBindingSlot 的 7 个无载体 target
|
||||
(Coverage.def:47-57)、HasOpenTransformFeedbackSpan(需 applier 自推)、GetBufferBindingPointCount
|
||||
(需 applier 自推或 constexpr 表)。
|
||||
3. **包归属有一个真实的缺口**:计划 §5 的 fm 字面只覆盖"Magma 的 9 个字段",而 15 个里
|
||||
GetBufferBindingPointCount / HasOpenTransformFeedbackSpan / GetProgramObject / ValidateProgramName /
|
||||
RecordError / GetBufferBindingSlot 六个不在其字面范围;fm 9 字段的 **Espryt 残余**(strict 车道
|
||||
活着的 8 对非 escalation 标记里的 6 对,见 §2 各字段)也无家。建议 fm 改写为"对象类字段
|
||||
(双后端)",或新增一个字段包;RecordError 归 fv 是计划 §2.6 文本直接支持的唯一年配。
|
||||
4. **顺序约束(ID-112 的绊线仍然有效)**:`PipeFill.cpp:2789-2809` 的两个 `static_assert`
|
||||
(`NoPointerBackedRowIsWhollySupplied` 等)把 7 个指针残余行钉在"仍被拉"状态;退役任一行的
|
||||
`EmittedCallSuppliesTheWholeField` FALSE 臂(PipeFill.cpp:2582-2602)必须与**该字段最后一批
|
||||
后端读者的迁移同提交落地**,否则 Magma 第一帧 draw 就解引用空镜像——注释 :2773-2781 明说
|
||||
"This residual fill is MAGMA'S ONLY SOURCE"。这不是建议,是构建期强制。
|
||||
5. **strict 车道活标记 10 对与字段的映射**(strict-expected-markers.txt:34-52):8 对表驱动
|
||||
(disjunct 1/2)+ 2 对仅 escalation(disjunct 3)。出口门要求该文件清空,而
|
||||
`GetBoundVertexArray@DrawArrays` / `GetBufferBindingPoint@DrawArrays` 两对的退役题材是 P8 的
|
||||
client vertex arrays(P5F 计划 §2.1 自注),**P8 不在 P5f 的六个包里**——要么 P5f 把这两对的
|
||||
载体(client VA staging)拉进来,要么出口门 2 需要为 escalation 对留一个具名豁免。这是
|
||||
计划文本内部的张力,f0 只记账不裁决。
|
||||
|
||||
## 1. 方法与口径
|
||||
|
||||
- **三重证据**:(i) `MG_IntegrationTest/Harness/strict-expected-markers.txt` 的 10 对活标记
|
||||
(车道实测,含 admitted 判据);(ii) `rsp` 计数路径——非 sticky 读经 `MGP_INPUT_CHECK` →
|
||||
`MGPipeInputUnfreshRead` → `CountBarrierPull`(PipeInputs.cpp:344),sticky forward 经
|
||||
`MGPipeStickyForwardPull`(PipeInputs.cpp:372-378,七个本体在 PipeFill.cpp:2119-2186 各自
|
||||
第一行调 `MGP_STICKY_FORWARD_PULL`);(iii) 全树 grep `MGB_CTX-><Field>` / sink 侧
|
||||
`gPipeInputs.<Field>`。
|
||||
- **推导规则**(scripts/gen_pipe_field_ownership.py):RECORD_SUPPLIED 是推导值 =
|
||||
Coverage.def 的 `MGP_COVERAGE_EMITTED_LIST`(48 行,Coverage.def:256-304)减去
|
||||
`EmittedCallSuppliesTheWholeField` 的 8 个拒绝(PipeFill.cpp:2582-2602,解析见
|
||||
gen 脚本 `parse_supplies_whole_field` :157-175;self-test 钉住"恰为 8 个",:972-974)。
|
||||
BARRIER_PULLED 手维护且每行必须有退役相(gen 脚本 :460-462 无相即构建失败)。
|
||||
admitted 表是 ID-116 推导:BARRIER_PULLED ∧ 在 verb 类 may-read 罩内 ∧(verb 的 op 静态设障
|
||||
∨ 退役相不含本阶段)(gen 脚本 `build_admitted` :312-375),再加运行时的 disjunct 3
|
||||
(escalation,PipeInputs.cpp:170-174 起)。
|
||||
- **"今天为何能工作"的公共答案**(下文各字段只写增量):inproc 下两角色共享一份
|
||||
`gPipeInputs`(PipeInputs.h:835),client 的第 4 步残余填充(PipeFill.cpp:3712-3762,
|
||||
:3759 的 `CopyField`)写入、server 在同一块上读,verb barrier 保证同一时刻只有一个写者
|
||||
(PipeInputs.h:902-908 的裁定)。strict 下这类读要么具名 admitted 要么 Fatal。
|
||||
- **行号漂移记录**(内容判断不受影响,但引用者须知):FieldOwnership.def:68-71 引的
|
||||
`PipeFill.cpp:1902-1905` 对应现为 `CopyField` 的 GetBoundVertexArray 臂 :128-130;
|
||||
FieldOwnership.def:82-84 引的 `SyncCurrentFBO (:2995)` 现为 DirectGLES.cpp:4465,
|
||||
`BindCurrentFBO (:4303-4353)` 现为 :5860 起;计划 §1.1 引的 PipeFill.cpp:245/:274 现为
|
||||
:244-245/:272-274,:2591-2592 现为 :2591-2597(均只差一两行)。P5F 计划 §2.2 引的
|
||||
T5 站点 `VulkanRenderer.cpp:1562-1590` 据 f0-magma.md §2 已漂移到 :1590-1637。
|
||||
|
||||
## 2. 逐字段判定
|
||||
|
||||
### 2.1 GetBoundVertexArray
|
||||
|
||||
- **(a) 形态**:`SharedPtr<VertexArrayObject> m_boundVertexArray`(PipeInputs.h:807;
|
||||
accessor :597-601)。O 类,`ReleaseObjectPins` 四行之一(PipeFill.cpp:95-100)。
|
||||
- **(b) 读者**:Espryt 普通 draw 已走记录——`fromRecords` 三元 DirectGLES.cpp:2275、:2442、
|
||||
:6185,`VertexInputReadsRecords()` 门 :2195;**残余读点**全在 escalation 路径:
|
||||
`SyncClientSideVertexArraysForDrawArrays` :2207/:2213(client VA,P8)、:8539、
|
||||
MultiDraw.cpp:223。Magma 6 站:DirectVulkan.cpp:805、:929,VulkanRenderer.cpp:6446、:6852、
|
||||
:12550、:12651(逐站分析见 f0-magma.md §4.1;:805 的 LINE_LOOP 臂还叠加
|
||||
`buffer-legacy-arm` Fatal)。**strict 标记**:`GetBoundVertexArray@DrawArrays`
|
||||
(markers:51,仅 disjunct 3 escalation;普通 draw 对已在 P5e mv 退役,markers 头注 :29-30
|
||||
与 ROADMAP P5e 行 item 5 的 red-once 证明:"倒掉 mv 的臂得 GetBoundVertexArray@DrawArrays")。
|
||||
- **(c) 载体**:`bind_vertex_elements` → applier `BoundVertexElements`(PipeApply.h:662;
|
||||
写入 PipeApply.cpp:2348)+ `VertexElementsCsos` 记录表。Espryt 读者已跟上;Magma 的
|
||||
fast-path 句柄臂存在但句柄仍由前端对象推出(f0-magma.md §4.1,VulkanRenderer.cpp:6452-6458)。
|
||||
- **(d) 处置**:Magma 六站改读 `BoundVertexElements` → 升 RECORD_SUPPLIED(Coverage.def:36、
|
||||
:262 的 emitted 行早已在;删 PipeFill.cpp:2585 的 FALSE 臂,与 ID-112 绊线同提交)。
|
||||
Espryt 残余的 client-VA 对是 P8 题材——见 §0.5 的门张力。
|
||||
- **(e)** 依赖:Magma vertex-input 句柄化(与 fm 的 GetBufferBindingPoint 同源)。
|
||||
工作量:中。包:fm;escalation 对无包(缺口)。
|
||||
|
||||
### 2.2 GetBufferBindingPoint
|
||||
|
||||
- **(a) 形态**:裸指针基址 `BindingSlotRange1D<BufferObject>* m_bufferBindingPointBase[15]`
|
||||
(PipeInputs.h:809;accessor :615-627,`base[index]` 即前端活槽 :624-626)。填充
|
||||
PipeFill.cpp:141-147(只填 `BufferBindPointTargets`)。
|
||||
- **(b) 读者**:Espryt 的记录臂已落——`SyncBufferBindingPointsByRecord` 读
|
||||
`st.BoundShaderBuffers`(DirectGLES.cpp:670-713,:684)、`SyncAtomicCounterBuffers` 记录臂
|
||||
:873-926、UBO 循环读 `BoundShaderBuffers[Uniform]`(:7295;CONTRACT-P5E.md:476)。
|
||||
legacy 臂仍在:SyncBufferBindingPoints :724-761(:737)、:928-936(:935)。
|
||||
**XFB 目标无载体**:SyncTransformFeedbackBindingPoints :796、StartPendingTransformFeedback
|
||||
:1891(kXfbSpan 罩住这两个字段,FillPoints.def:315-316)。Magma 3 站:
|
||||
UniformManager.cpp:1205、:2035,VulkanRenderer.cpp:11764(XFB)。**strict 标记**:
|
||||
`GetBufferBindingPoint@DrawArrays`(markers:52,仅 disjunct 3)。该对的具体读点本普查
|
||||
不能从静态证据唯一确定——候选是 XFB-span draw 的 :1891(escalation (i),open span)或
|
||||
client-VA draw;计划 §2.1 把两对都记为 client-VA(P8)。双块演习会直接报名字,f0 不裁决。
|
||||
- **(c) 载体**:`set_shader_buffers` → `BoundShaderBuffers[3][84]`(PipeApply.h:776-779;
|
||||
写入 PipeApply.cpp:2918)。**XFB 目标明确不搭这条记录**(CONTRACT-P5E.md:770:
|
||||
"XFB does NOT ride this state");`set_stream_output_targets` 今天"has no applier and no
|
||||
producer"(CONTRACT-P5B.md:215)。
|
||||
- **(d) 处置**:Magma 三站改读 `BoundShaderBuffers`;XFB 目标的载体是 `set_stream_output_targets`
|
||||
的实体化(P9 题材,落入 P5f 范围)。之后删 PipeFill.cpp:2597 的 FALSE 臂、字段翻
|
||||
RECORD_SUPPLIED(Coverage.def:71、:263 已在)。
|
||||
- **(e)** 依赖:同 2.1 + XFB 记录实体化。工作量:中。包:fm + XFB 半无包(缺口)。
|
||||
|
||||
### 2.3 GetFramebufferBindingSlot
|
||||
|
||||
- **(a) 形态**:裸指针 `BindingSlot<FramebufferObject>* m_framebufferBindingSlot[2]`
|
||||
(PipeInputs.h:810;accessor :628-637,空槽即 poison Fatal)。填充 PipeFill.cpp:190-195。
|
||||
- **(b) 读者**:Espryt——记录臂 `SyncCurrentFBOByRecord`(DirectGLES.cpp:4479)+ 强制臂具名
|
||||
abort `RefuseFramebufferBindingSlotRead`(:4490-4492,P5e fb);pre-handle 回落臂仍在
|
||||
SyncCurrentFBO(:4465 起,:4507 的 `GetFramebufferBindingSlotChecked`,helper 在 :184-202),
|
||||
直接站点 :3261、:3491、:4212、:4426、:4507、:5625、:5923、:6096、:8349、:10696-10697,
|
||||
Managers.cpp:9903、:10299。FieldOwnership.def:82-84 的"8 Espryt + 13 Magma"与本次 grep
|
||||
量级一致。**strict 标记**:`GetFramebufferBindingSlot@ReadPixels`(markers:40,disjunct 1——
|
||||
`read_pixels` 是 kWaitReply);读点是 ReadPixels 内的 `FramebufferImpl::SyncCurrentFBO()`
|
||||
(DirectGLES.cpp:14275)回落臂。Magma 13 站:UniformManager.cpp:560;VulkanRenderer.cpp:3158、
|
||||
:5510、:5730、:6360、:6465、:6845、:7648、:8016、:9024-9025、:9660、:10371。
|
||||
- **(c) 载体**:`set_framebuffer_state` → `BoundFramebuffer[2]` + `FramebufferRecords`
|
||||
(PipeApply.h:714;写入 PipeApply.cpp:2558-2562),P5e fb 已给 Espryt 记录臂。
|
||||
- **(d) 处置**:Espryt 的 ReadPixels 回落为何仍命中(记录臂 decline 的路径)需 f1 双块演习
|
||||
定位——静态看 :4479 应先试记录臂,标记存活说明 ReadPixels 场景走了回落;Magma 13 站改读
|
||||
`BoundFramebuffer`/`FramebufferRecordFor`(PipeApply.cpp:2609-2613)。之后删
|
||||
PipeFill.cpp:2586 FALSE 臂、翻 RECORD_SUPPLIED(Coverage.def:84、:274 已在)。
|
||||
- **(e)** 依赖:fm 的 FBO 记录消费是 Espryt 已铺好的路。工作量:中(Magma 站点多但形状单一)。
|
||||
包:fm。
|
||||
|
||||
### 2.4 GetImageTextureBinding
|
||||
|
||||
- **(a) 形态**:裸指针 `ImageTextureBinding* m_imageTextureBindingBase`(PipeInputs.h:811;
|
||||
两个 const 重载 :638-653,null 基址即 poison Fatal)。填充 PipeFill.cpp:196-199。
|
||||
- **(b) 读者**:Espryt——pre-handle 形式 SyncImageTextureBinding :3842(注释 :3831-3837 自明
|
||||
"what the pull build compiles and what a push-monolith build runs");transport 下已删的
|
||||
MarkWritableImageBufferTexturesGpuWritten :3891(门 :3885-3887);Managers.cpp:11963(.Format)。
|
||||
Magma 4 站:UniformManager.cpp:1021、:1312、:1867、:1955。**strict 标记:无**。
|
||||
- **(c) 载体**:`set_shader_images` → `BoundShaderImages`(PipeApply.h:751;写入
|
||||
PipeApply.cpp:2857);Espryt 的记录重载已在(DirectGLES.cpp:3810-3829,`ResolveShaderImageRecord`
|
||||
+ `SyncTextureToBackendByHandle`)。`bind_shader_image` verb 记录的参数全景也在线上
|
||||
(PipeApplier.cpp:889-900 的注释)。
|
||||
- **(d) 处置**:Magma 四站改读 `BoundShaderImages`;Espryt 残余是 monolith 臂,不动。
|
||||
删 PipeFill.cpp:2587 FALSE 臂、翻 RECORD_SUPPLIED(Coverage.def:85、:275 已在)。
|
||||
- **(e)** 依赖:无超出 fm 的。工作量:小-中。包:fm。
|
||||
|
||||
### 2.5 GetProgramForDraw
|
||||
|
||||
- **(a) 形态**:`SharedPtr<ProgramObject> m_programForDraw`(PipeInputs.h:813;accessor
|
||||
:659-663)。填充 PipeFill.cpp:244-245(计划 §1.1 的 :245 引文)。O 类 pin 四行之一。
|
||||
- **(b) 读者**:Espryt——PrepareForDraw 的提升读 :6215(`programFromRecords` 三元;
|
||||
:6132-6134 的注释记了这条提升本身的历史);记录臂:`GetCurrentBackendProgram` 读
|
||||
`MGPipeApplier().DrawProgram`(:7659-7670),legacy 臂 :7672/:7744 只在
|
||||
`ProgramHandleArm()==false` 时跑;:7029 同样带三元。Magma 3 站:VulkanRenderer.cpp:6384、
|
||||
:6815、:6853。**strict 标记:无**——P5e pa 退役,markers 头注 :25-27 记了棘轮曾指名它消失,
|
||||
ROADMAP P5e 行 item 5 的 red-once 即此对。
|
||||
- **(c) 载体**:`set_draw_program` → `DrawProgram`(PipeApply.h:759)+ `bind_shader_state` →
|
||||
`BoundShaderCso`(:761;写入 PipeApply.cpp:3208-3214);ShaderCso 记录带逐 link 的
|
||||
ProgramArchive(ROADMAP P5e 行:"最大的那次迁移没有改动任何线上结构:记录早就带着逐 link
|
||||
的 ProgramArchive,缺的是仍去问前端对象的读者"——本字段就是那句话的原主)。
|
||||
- **(d) 处置**:Magma 三站改读记录即毕。删 PipeFill.cpp:2589 FALSE 臂、翻 RECORD_SUPPLIED
|
||||
(Coverage.def:100、:289 已在)。
|
||||
- **(e)** 依赖:无。工作量:小-中。包:fm。
|
||||
|
||||
### 2.6 GetProgramForDispatch
|
||||
|
||||
- **(a) 形态**:`SharedPtr<ProgramObject> m_programForDispatch`(PipeInputs.h:812;accessor
|
||||
:654-658)。填充 PipeFill.cpp:241-243。O 类 pin 四行之一。
|
||||
- **(b) 读者**:Espryt——PrepareForCompute :8142(记录三元)+ :8154-8157 的
|
||||
`DispatchProgram` 句柄臂。Magma 2 站:VulkanRenderer.cpp:7424、:7476。**strict 标记:无**
|
||||
(markers 头注 :28:pa 在它被写进表之前就退役了它)。
|
||||
- **(c) 载体**:`set_dispatch_program` → `DispatchProgram`(PipeApply.h:760)。
|
||||
- **(d) 处置**:与 2.5 同形。删 PipeFill.cpp:2590 FALSE 臂、翻 RECORD_SUPPLIED
|
||||
(Coverage.def:99、:288 已在)。
|
||||
- **(e)** 依赖:无。工作量:小。包:fm。
|
||||
|
||||
### 2.7 GetTextureUnitObject
|
||||
|
||||
- **(a) 形态**:裸指针 `TextureUnit* m_textureUnitBase`(PipeInputs.h:815;accessor :669-676,
|
||||
null 基址即 poison Fatal——计划 §1.1 的 :669 引文)。填充 PipeFill.cpp:272-274(计划的 :274)。
|
||||
- **(b) 读者**:FieldOwnership.def:89-90 记"13 Espryt + 8 Magma",与 grep 一致。Espryt 13 站:
|
||||
DirectGLES.cpp:2679、:2707、:2742、:2755、:3451、:6425、:6754、:7535、:10760、:11771、
|
||||
:11887、:12120、:14451。Magma 8 站:UniformManager.cpp:511、:787、:826、:852、:1679;
|
||||
VulkanRenderer.cpp:9652、:11157、:11401。**strict 标记**:`GetTextureUnitObject@CopyTexImage2D`
|
||||
(markers:43,disjunct 1——`copy_framebuffer_to_texture` 记为 verb CopyTexImage2D,
|
||||
FieldOwnership.def:285)。读点值得点名:`UpdateTextureBindingAtTarget` 的
|
||||
`MGB_CTX->GetTextureUnitObject(unit)`(DirectGLES.cpp:10760)**在 transport 分流检查
|
||||
(:10769-10774 的 `VerbCopyTexDst` 句柄臂)之前无条件执行**——句柄臂已经在了,读却先发生;
|
||||
这正是"数据已在线上,读者顺序没跟上"的最小样本,也是双块演习下第一批会红的站点之一。
|
||||
- **(c) 载体**:`set_sampler_views` → `BoundSamplerViews`(PipeApply.h:741;写入
|
||||
PipeApply.cpp:2828)+ `BoundSamplerStates`(:746)。
|
||||
- **(d) 处置**:Espryt 13 站 + Magma 8 站逐个改读记录(:10760 这类"读在分流前"的站点要
|
||||
把读取移进各自臂内)。删 PipeFill.cpp:2588 FALSE 臂、翻 RECORD_SUPPLIED(Coverage.def:115、
|
||||
:295 已在)。
|
||||
- **(e)** 依赖:无超出 fm 的;站点数量是 fm 里最大的一批。工作量:中-大。包:fm。
|
||||
|
||||
### 2.8 GetTextureObject
|
||||
|
||||
- **(a) 形态**:sticky forward,无存储;按 GL 名返回 `const SharedPtr<ITextureObject>&`
|
||||
(PipeInputs.h:691;本体 PipeFill.cpp:2131-2135,null context 时返回 `NullShared`)。
|
||||
- **(b) 读者**:Espryt 后端本体**无站点**(DirectGLES 全树 grep 无 `MGB_CTX->GetTextureObject`);
|
||||
读者在 **server sink**:`ServerVerbSink::OnResourceCopyRegion` 用 GL 名重建两端点
|
||||
(PipeApplier.cpp:853-859,注释明说"`rsp` counts every one of these and P7 is what retires
|
||||
them")。Magma:VkTextureManager.cpp:810。**strict 标记**:`GetTextureObject@CopyImageSubData`
|
||||
(markers:42,disjunct 2 退役相 P7;gen 脚本 self-test :998-999 与 :1039-1041 钉住它同时被
|
||||
disjunct 1/2 覆盖)。
|
||||
- **(c) 载体**:`MGPCopyRegion.Src/Dst` 句柄与 GL 名**并排在线**(MGPipeTypes.h:1468-1476;
|
||||
注释 :1455-1464 自明"A GL name is NEVER an identity; it is the lookup key of a pull the
|
||||
handle beside it will replace")。
|
||||
- **(d) 处置**:sink 改按 `Src/Dst` 句柄经 server 纹理 twin 表解析(FieldOwnership.def:183-184
|
||||
的 forward 机制"a server-side texture handle table"),forward 删除、字段行出表。
|
||||
Magma 的 :810 同批。无法升 RECORD_SUPPLIED——sticky forward 没有存储可供"供给",退役形态
|
||||
是 accessor 死亡(仿 InvalidateCompileEnv 的 P5c ev 处置,FieldOwnership.def:157-163:
|
||||
字段行改 FATAL + forward 删除)。
|
||||
- **(e)** 依赖:server 端纹理句柄表(fm 的公共底座,与 2.7 共用)。工作量:小-中。
|
||||
包:fm 边缘——读者在"后端无关的 sink"而非 Magma 本体,fm 的字面范围需要为此扩写(§0.3)。
|
||||
|
||||
### 2.9 GetBufferBindingPointCount
|
||||
|
||||
- **(a) 形态**:sticky forward,值类 `SizeT`,按 target 查前端绑定点的**容量**(不是绑定状态)。
|
||||
Coverage.def:145 的 sticky 理由原文:"keyed by target: a constexpr capacity table, not verb
|
||||
state"。本体 PipeFill.cpp:2119-2123。
|
||||
- **(b) 读者**:Espryt 1 站:SyncAtomicCounterBuffers 的 legacy 臂 :928——它的记录臂
|
||||
(:873-877)已改读 applier 窗口,注释 :871-872 自明"the bound is the applier's window
|
||||
instead of GetBufferBindingPointCount's sticky forward, which is the row this site was the
|
||||
Espryt reader of"(即 Espryt 侧读者已退役,legacy 臂只在非记录配置跑)。Magma 2 站:
|
||||
UniformManager.cpp:1200、:2030。**strict 标记:无**。
|
||||
- **(c) 载体**:`set_shader_buffers` 的窗口(`ShaderBufferStart/Count`,PipeApply.h:778-779)
|
||||
回答"用到哪";容量本身是常量(线上 84,`kMGPipeMaxBufferBindingPoints`;CONTRACT-P5E.md:650)。
|
||||
- **(d) 处置**:升 **APPLIER_DERIVED**——applier 从 `BoundShaderBuffers` 窗口/容量表回答,
|
||||
读者改读之(FieldOwnership.def:179-180 的 forward 机制就是"a server-side binding-point
|
||||
table")。注意它与 2.2 同源:Magma 的 UniformManager 两站把 count 与 point 成对读
|
||||
(:1200/:1205、:2030/:2035),应同批迁移。
|
||||
- **(e)** 依赖:同 2.2。工作量:小。包:fm(字面外,缺口)。退役相标 P7/P13——P13 的半边
|
||||
(transfer target 的容量)本普查未见读者,未见不等于没有,标**未知**。
|
||||
|
||||
### 2.10 HasOpenTransformFeedbackSpan
|
||||
|
||||
- **(a) 形态**:sticky forward,值类 `Bool`,按 lifetime id 查前端 XFB span 开闭
|
||||
(PipeInputs.h:692;本体 PipeFill.cpp:2137-2141;前端实现 Core.cpp:1299)。
|
||||
- **(b) 读者**:仅 Magma 1 站:VulkanRenderer.cpp:11684(`m_xfbCounterSlotOwner` 的归并检查)。
|
||||
Espryt 无后端读者。**strict 标记:无**。
|
||||
- **(c) 载体**:无现成字段级载体,但 applier 已具备自推的全部输入:`begin/end/pause/resume
|
||||
_stream_output` 四个 verb 记录(FieldOwnership.def:267-270 的 stamp 行)+ `bind_stream_output`
|
||||
(带 `LifetimeId`,MGPipeTypes.h:1773-1778)+ 已有的 `MGPipeApplierState::IsTransformFeedbackActive`
|
||||
镜像(PipeApply.h:805,P5e §2.1 escalation (i) 的两侧同值论证是先例)。注意
|
||||
CONTRACT-P5B.md:215 记 `begin_stream_output` 的 sink 今天是 stub——自推依赖 t2 sink 实体化。
|
||||
- **(d) 处置**:升 **APPLIER_DERIVED**(FieldOwnership.def:185-186 的机制"server-side XFB span
|
||||
state"):applier 从 stream-output 记录序列维护 per-lifetime-id 的 span 开闭集。
|
||||
- **(e)** 依赖:t2 sink 实体化(P9 题材的边缘)。工作量:小-中。包:无明确归属(缺口;
|
||||
读者在 Magma,可挂 fm)。
|
||||
|
||||
### 2.11 GetTransformFeedbackProgram
|
||||
|
||||
- **(a) 形态**:`SharedPtr<ProgramObject> m_transformFeedbackProgram`(PipeInputs.h:814;
|
||||
accessor :664-668)。填充 PipeFill.cpp:285-287。O 类 pin 四行之一。
|
||||
- **(b) 读者**:Espryt 1 站:StartPendingTransformFeedback(DirectGLES.cpp:1871,PrepareForDraw
|
||||
尾部,kDraw 罩住——FieldOwnership.def:102-105 的"XFB itself is off the reduced path, but
|
||||
kDraw's may-read mask carries it"即此);同一函数 :1891 还读 GetBufferBindingPoint(2.2 的
|
||||
XFB 半)。Magma 2 站:VulkanRenderer.cpp:11725、:11824。**strict 标记**:
|
||||
`GetTransformFeedbackProgram@DrawArrays`(markers:44;gen 脚本 self-test :996-997 钉住它由
|
||||
disjunct 2 承载,CONTRACT-P5E §5.7 把 XFB 整体划出 P5e)。
|
||||
- **(c) 载体**:**今天没有**。`begin_stream_output` 只带 `PrimitiveMode`(CONTRACT-P5B.md:215
|
||||
原文,且同文明说 capture program 靠的就是 kXfbSpan/kDraw 的 BARRIER-PULLED 读);
|
||||
`set_stream_output_targets` 无 producer 无 applier。这是 15 个里"线上没数据"的代表。
|
||||
- **(d) 处置**:升 RECORD_SUPPLIED 需要先造数据:给 `begin_stream_output` 增加 capture
|
||||
program 的 ShaderCso handle(或实体化 `set_stream_output_targets` 并让它带上),两端读者
|
||||
(Espryt :1871、Magma 两站)改读 applier 状态。**不能降 FATAL**:kDraw 罩内有活读者,
|
||||
缩减路径(OpenRA 的 XFB 用例)会读。
|
||||
- **(e)** 依赖:记录 schema 扩展(线上行,须与 gen 管线同步)+ 双后端读者。工作量:中。
|
||||
包:fm(Magma 半);Espryt 半的退役相标的是 P3b/P4b(与 fr 的 registry 重键同源不同事,
|
||||
归属需在 §5 定包时裁决)。
|
||||
|
||||
### 2.12 GetProgramObject
|
||||
|
||||
- **(a) 形态**:sticky forward,按 GL 名返回 `const SharedPtr<ProgramObject>&`
|
||||
(PipeInputs.h:690;本体 PipeFill.cpp:2125-2129)。
|
||||
- **(b) 读者**:Espryt 2 站,成对出现在 ValidateProgramName 之后:GetBackendProgramId :8204
|
||||
(+:8199)、ShaderStorageBlockBinding :12600(+:12599;:12603-12606 还背着 G6 的
|
||||
`MGPipeFrontendKeyedRegistryScope`)。Magma 1 站:DirectVulkan.cpp:278(+:275)。
|
||||
sink 侧注释 PipeApplier.cpp:921-923 自明"Both backends resolve the PROGRAM through the
|
||||
barrier-pulled GetProgramObject(GlName) … retired by P9"。**strict 标记**:
|
||||
`GetProgramObject@ShaderStorageBlockBinding`(markers:41;`SetStorageBlockBinding` 是
|
||||
kWaitApplied,PipeCalls.def:297,disjunct 1/2 皆覆盖)。
|
||||
- **(c) 载体**:`MGPStorageBlockBinding.ShaderCso` 已在记录里(MGPipeTypes.h:1791-1792:
|
||||
"the identity, and what P7 dispatches on"),名字 blob 走 SEG_STAGE。数据已在线上。
|
||||
- **(d) 处置**:两端 backend 的 `ShaderStorageBlockBinding` slot 改按 ShaderCso 句柄解析
|
||||
(esp. Espryt :12597-12620 的"只推给已建成 program"优化路径,其 frontend-keyed `Find` 一并
|
||||
随 fr 的 registry 重键消失);forward 删除,字段行参照 2.8 的 ev 形态出表。无法升
|
||||
RECORD_SUPPLIED(无存储),处置形态是 accessor 死亡。
|
||||
- **(e)** 依赖:fr 的 program registry 句柄化(:12603 的 scope 是 P5c G6 债,两包有交集)。
|
||||
工作量:小-中。包:无明确归属(缺口;可挂 fm 或并入 fr 的 program 半边)。
|
||||
|
||||
### 2.13 ValidateProgramName
|
||||
|
||||
- **(a) 形态**:sticky forward,值类 `Bool`,前端名表探测(PipeInputs.h:694;本体
|
||||
PipeFill.cpp:2158-2162;Coverage.def:127 记 kClientResolved——"the server is never asked"
|
||||
是设计答案,今天的实现恰恰相反)。
|
||||
- **(b) 读者**:与 GetProgramObject 完全同点成对(2.12 的四处;另 DirectGLES.cpp:8199)。
|
||||
**strict 标记**:`ValidateProgramName@ShaderStorageBlockBinding`(markers:45)。
|
||||
- **(c) 载体**:不需要载体——发射端 client 在发 `set_storage_block_binding` 时名字必然已过
|
||||
前端 validator;server 按 ShaderCso 解析到记录即隐含合法(与 2.12 是同一笔迁移)。
|
||||
- **(d) 处置**:随 2.12 一起退役;字段行出表(机制"a client-resolved program name probe",
|
||||
FieldOwnership.def:187-188)。
|
||||
- **(e)** 依赖:同 2.12。工作量:小(与 2.12 同提交)。包:同 2.12。
|
||||
|
||||
### 2.14 RecordError
|
||||
|
||||
- **(a) 形态**:sticky forward,**写前端**(错误队列)(PipeInputs.h:695-697;本体
|
||||
PipeFill.cpp:2164-2186)。15 个里唯一的写方向。
|
||||
- **(b) 读者(写者)**:Espryt:Managers.cpp:14858、DirectGLES.cpp:11124(另有
|
||||
Managers.cpp:14949-14951 自明"NO RecordError HERE"的反向先例)。Magma:DirectVulkan.cpp:735、
|
||||
VulkanRenderer.cpp:1367、:1371、:1427。**strict 标记:无**(车道不制造 GL 错误)。
|
||||
**rsp 口径注意**:`MGPipeStickyForwardPull(RecordError)` 在分流**之前**(PipeFill.cpp:2165),
|
||||
所以即使 transport 臂根本不碰前端(:2166-2178 直接 `PostGlError` 后 return),server-stamped
|
||||
verb 内的每次调用仍被记一次 pull——这是机制的保守,不是残余的债。
|
||||
- **(c) 载体**:**已存在且已接线**:P5c ev 把 transport 臂改成了 `kEventGlError` 事件
|
||||
(CONTRACT-P5C §4.2;`ServerSessionInstance().PostGlError`,PipeFill.cpp:2174-2177),
|
||||
错误消息经 `info->toString()` 过线。剩在 P9 的只是**有序化**(:2170-2173 的注释:
|
||||
"preserves per-thread program order of error-then-read but not cross-verb interleaving")。
|
||||
- **(d) 处置**:这是 15 个里唯一"载体已通、剩记账口径"的字段。建议仿 InvalidateCompileEnv 的
|
||||
P5c ev 处置(FieldOwnership.def:157-163 的先例与 :160-161 的"FieldOwnership 只描述 split
|
||||
server 的读"论证):把 `MGP_STICKY_FORWARD_PULL` 移到分流之后(或 transport 臂豁免计数),
|
||||
字段行降 FATAL——论据是"transport 下分流臂已是完整答案,server 侧再落到 monolith 臂即真
|
||||
缺陷"。需先逐写者核 `OnGlError` 覆盖(消息保真已由 :2174 保证,有序化仍是 P9 的)。
|
||||
若 f1 双块演习显示monolith 臂在 split 下仍被合法触达,则退而维持 BARRIER_PULLED 到 P9——
|
||||
双块的红就是判据。
|
||||
- **(e)** 依赖:fv 的反向通道有序化口径(P9 的边界划分)。工作量:小。包:**fv**——计划
|
||||
§2.6 明列"OnGlError 有序化",其中触碰 client 内存的部分归 P5f,而本字段 transport 下已不
|
||||
触碰,剩余正好是有序化与计数口径。
|
||||
|
||||
### 2.15 GetBufferBindingSlot
|
||||
|
||||
- **(a) 形态**:裸指针 `BindingSlot<BufferObject>* m_bufferBindingSlot[15]`(PipeInputs.h:808;
|
||||
accessor :605-614,空槽/越界即 poison Fatal)。填充 PipeFill.cpp:131-140——只填
|
||||
`GlobalBufferTargets`,**Index 恒 null**(:132-136 的注释:GLContext 经绑定 VAO 的
|
||||
element-buffer 槽推导,"a derivation no FillPoints.def row can copy")。
|
||||
- **(b) 读者**:按 target 分片(Coverage.def:37-70 的权威拆分:8 个 target 有记录载体,7 个
|
||||
没有——CopyRead/CopyWrite/PixelPack/PixelUnpack/Texture/DispatchIndirect/Query)。
|
||||
Espryt:PixelPack 读回 6 站(DirectGLES.cpp:12809、:13791、:14028、:14338、:14769,
|
||||
Utils.cpp:2353);DrawIndirect/Parameter 族 :440、:976、:1422、:8921、:8999-9000、:9078、
|
||||
:9151-9152、:9332、:9381,MultiDraw.cpp:308。Magma:DirectVulkan.cpp:283、:407、:470、:538、
|
||||
:591;VulkanRenderer.cpp:7520(DispatchIndirect)、:11126、:12560-12561、:12661-12662、:12731。
|
||||
**strict 标记两对**:`GetBufferBindingSlot@ReadPixels`(markers:34,disjunct 1——读点是
|
||||
ReadPixels 的 PBO 槽 :14337-14338 一带)与 `GetBufferBindingSlot@DrawArrays`(markers:36-39,
|
||||
wave-3 新:multi-draw 五档被强制后 indirect 档浮出,注释 :35-38 记了它"was named before it
|
||||
could fire")。
|
||||
- **(c) 载体**:`set_indirect_buffers` 只带 DrawIndirect+Parameter 对(Coverage.def:44-45,
|
||||
MGPIndirectBuffers 的自明范围);`resource_readback`(MGPReadback)带目标 buffer 句柄
|
||||
(MGPipeTypes.h:1439-1443)但 PixelPack 槽的读在 verb 内、不在记录解析点。PixelPack 等
|
||||
7 target **无任何 call**(Coverage.def:47-57 逐 target 点名)。
|
||||
- **(d) 处置**:本字段不能一次翻类,只能按 target 渐进——而字段按 Coverage.def:61-69 的裁定
|
||||
必须保持一行,所以 P5f 的工具是 **`MGP_FIELD_OWNERSHIP_ARG_LIST` 的按参数例外行**
|
||||
(GetPixelStoreParameters,1 已是先例,FieldOwnership.def:214-216):有载体的 target 逐批窄出
|
||||
BARRIER_PULLED,Query target 两端都无读者(Coverage.def:56-57)可率先窄为 FATAL,
|
||||
DispatchIndirect(Coverage.def:50-55 点名"两端都读且无 call")需要扩 `set_indirect_buffers`
|
||||
或新记录(P13 题材),PixelPack 随 readback 记录改造(P9 题材)。全部窄完后字段行出表。
|
||||
- **(e)** 依赖:readback 记录化(P9)+ indirect 扩 target(P8/P13)——**三个退役相分属三个
|
||||
后续阶段,是 15 个里唯一需要 P5f 主动把外阶段题材拉进来的字段**。工作量:大
|
||||
(15 target × 双后端 × 三类载体)。包:无归属(缺口最大的一行)。
|
||||
|
||||
## 3. 包归属汇总与缺口
|
||||
|
||||
| 字段 | 处置方向 | 载体状态 | 包(计划 §5 字面) | 建议包 |
|
||||
|---|---|---|---|---|
|
||||
| GetBoundVertexArray | 升 RECORD_SUPPLIED | bind_vertex_elements 已在 | fm(Magma 半);escalation 对无包 | fm + P8 议题 |
|
||||
| GetBufferBindingPoint | 升 RECORD_SUPPLIED | set_shader_buffers 已在(XFB 除外) | fm;XFB 半无包 | fm |
|
||||
| GetFramebufferBindingSlot | 升 RECORD_SUPPLIED | set_framebuffer_state 已在 | fm | fm |
|
||||
| GetImageTextureBinding | 升 RECORD_SUPPLIED | set_shader_images 已在 | fm | fm |
|
||||
| GetProgramForDraw | 升 RECORD_SUPPLIED | set_draw_program 已在 | fm | fm |
|
||||
| GetProgramForDispatch | 升 RECORD_SUPPLIED | set_dispatch_program 已在 | fm | fm |
|
||||
| GetTextureUnitObject | 升 RECORD_SUPPLIED | set_sampler_views 已在 | fm | fm |
|
||||
| GetTextureObject | accessor 死亡(FATAL 形态) | MGPCopyRegion 句柄已在 | fm 边缘(读者在 sink) | fm |
|
||||
| GetBufferBindingPointCount | 升 APPLIER_DERIVED | applier 窗口可推 | 无 | fm |
|
||||
| HasOpenTransformFeedbackSpan | 升 APPLIER_DERIVED | 需 applier 自推(依赖 t2 sink) | 无 | fm 或 P9 边界 |
|
||||
| GetTransformFeedbackProgram | 升 RECORD_SUPPLIED(**须先造数据**) | 无——begin_stream_output 需扩 | fm(Magma 半);Espryt 半标 P3b/P4b | fm + fr 裁决 |
|
||||
| GetProgramObject | accessor 死亡 | MGPStorageBlockBinding.ShaderCso 已在 | 无 | fm 或 fr |
|
||||
| ValidateProgramName | accessor 死亡 | 随 2.12,无需载体 | 无 | 同 2.12 |
|
||||
| RecordError | 降 FATAL(计数口径修正后) | kEventGlError 已接线 | fv(唯一字面匹配的) | fv |
|
||||
| GetBufferBindingSlot | 按 target 渐进(ARG 例外行) | 8/15 有载体,7/15 无 | 无(P8/P9/P13 混合) | 缺口最大 |
|
||||
|
||||
**三个给 §5 定包的输入**:
|
||||
1. fm 的字面"Magma 的 9 个字段"应扩写为"对象类字段的双后端退役"——否则 6 个字段无家可归,
|
||||
且 fm 内部还混着 sink 侧读者(GetTextureObject)与 Espryt 残余读者(GetTextureUnitObject@
|
||||
CopyTexImage2D、GetFramebufferBindingSlot@ReadPixels、GetTransformFeedbackProgram@DrawArrays)。
|
||||
2. 两个 escalation 对(client VA,P8 题材)与 GetBufferBindingSlot 的 P8/P9/P13 混合退役,
|
||||
是出口门 2(markers 清空)与范围表 §3(不取 P8 staging)之间唯一的正面冲突,需要计划
|
||||
层面裁决:拉进来,或为 escalation 对留具名豁免。
|
||||
3. 顺序约束:7 个指针行的 `EmittedCallSuppliesTheWholeField` FALSE 臂删除(PipeFill.cpp
|
||||
:2582-2602)与对应最后一批改者必须同提交,ID-112 的两个 `static_assert`
|
||||
(PipeFill.cpp:2789-2809)是构建期强制执行者;`FieldOwnershipTest` 钉住非 sticky
|
||||
BARRIER_PULLED 清单(FieldOwnership.def:66-67),gen 脚本 self-test 钉住拒绝集恰为 8
|
||||
(scripts/gen_pipe_field_ownership.py:972-974)——每退役一行,这三处与
|
||||
strict-expected-markers.txt 的同提交更新是机械义务。
|
||||
@@ -0,0 +1,175 @@
|
||||
# f0 普查 · §2.2 Magma(DirectVulkan)跨角色读写
|
||||
|
||||
> 基线:`feat/disaggregated @ 8b68b92c`(代码头 25fba0d5 之后的提交只动文档)。行号均以该 HEAD 为准。
|
||||
> 范围:`MobileGL/MG_Backend/DirectVulkan/**` 一侧所有直接读前端对象 / 读 client 镜像 / 写 client 存储的站点。
|
||||
> 只读普查:本文件是唯一产物,未改任何代码、未构建、未跑测试。
|
||||
|
||||
## 0. 总判断
|
||||
|
||||
1. **Magma 在 transport 下的 draw/dispatch 路径今天根本跑不通**,这不是推断而是守卫的直接后果:顶点/索引/UBO/SSBO/indirect 缓冲的同步都汇到 `VkBufferManager::AcquireResidentSlice` / `AcquireStreamedSlice`,两者第一动作就是 `bufferObject->SyncPersistentMappedRange()`(VkBufferManager.cpp:657、:708),而该方法在 transport + apply 线程下无条件 `Fatal{RoleViolation, "buffer-legacy-arm"}`(MG_State/GLState/BufferState/BufferObject.cpp:415-440,守卫条件 `PushIsArmed() && OnServerRole()`,PersistentMapTracker.cpp:526-535)。CI 侧与之一致:`integration-magma-split` 只有两条 `DirectVulkan.Split.NamedBlit.*`,且**预期红**(在 Clear 的 `GetFramebufferBindingSlot` 读处就 abort,MobileGL/MG_IntegrationTest/CMakeLists.txt:2411-2437,.github/workflows/test.yml:1353-1381)。所以"今天为何能工作"对本目录绝大多数站点的诚实回答是:**monolith / pull 臂下同线程直接成立;inproc split 下只有不设缓冲的 verb(Clear/Blit/GenerateMipmap 等)能走到,凡是 draw 带缓冲即具名 Fatal**。lockstep(Magma 永不发布 `kCapRunAheadApply`,CONTRACT-P5E.md:68、§6.1)保证每条记录设障,使未加守卫的 BARRIER_PULLED 读在非 strict 下作为 admitted pull 计数(`rsp`)而非 Fatal——但 guard 类站点不吃这一套,任何 strictness 下都 abort。
|
||||
2. **T5 属实且已被 P5c tx 处理**:transport 下生成 mip 改写 server 的 staged shadow,不再写 client 的 level 存储;但留了两处具名残余(见 §2)。
|
||||
3. **C1 四处 caps 读属实已改走 server backend**(见 §3)。
|
||||
4. 对象类 BARRIER_PULLED 字段的 Magma 读点全部在 draw/dispatch/blit/readback/mipmap/XFB 路径上,逐字段清单见 §4。其中 8 行的退役标签是 "P5e (Espryt unbarriered), P7 (Magma)",第 9 个 `GetTextureObject` 标的是裸 "P7"(Espryt 半边已在 P5c tx 退役),合计 9 个 Magma 相关行——与任务书的"9 个"一致。
|
||||
5. 除字段读外,Magma 还有三类结构性跨角色耦合:前端地址键控的 twin registry / memo(§5)、server 在 apply 线程上创建与销毁前端类型对象(§6)、以及对前端 BufferObject 的直接方法调用(§7)。
|
||||
|
||||
## 1. 术语与机制前提(本次普查用到的)
|
||||
|
||||
- `MGB_CTX` 在 push 构建下 = `&MG_Pipe::gPipeInputs`(MG_Pipe/PipeInputsSwitch.h:17-21);在 pull 构建下 = `MG_State::pGLContext`。Magma 全树的对象访问器读都经此宏。
|
||||
- lockstep:Magma 臂的 `CallMask` 不含 `kCapRunAheadApply`,每条记录都设障,apply 时 client 停在 `WaitForApplied`。
|
||||
- 双层守卫(均在 `MOBILEGL_BUILD_DISAGGREGATED` 下):`Fatal{RoleViolation, "MGPipeSlots"}`(MG_Impl/Pipe/SlotAllocator.cpp:32-70,挂在 Acquire/FindByLifetimeId/Free,:330/:317/:339);`Fatal{RoleViolation, "buffer-legacy-arm"}`(BufferObject.cpp:47-66,挂在 MappedData :992 / ShadowAllocationBytes :1002 / GetChangeSerial :1025 / HasDefinedContent :1034 / IsMapped :1049,以及 SyncPersistentMappedRange :434-437);`Fatal{RoleViolation, "texture-legacy-arm"}`(MipmapStorage.cpp:30-60,被 `MGPipeTextureLegacyArmScope` 豁免)。
|
||||
- Magma 的具名豁免 scope:`MagmaP7AllocatorDebtScope`(MG_Impl/Pipe/SlotAllocator.h:223-248,P5e ruling 12 改名后专指 Magma 的四笔 apply 线程分配器债,且按后端种类 keyed——Espryt 借用即 Fatal,SlotAllocator.cpp:47-55)。
|
||||
|
||||
## 2. T5(P5c 审计行,ROADMAP.md:57)核验:属实,已迁移,但有残余
|
||||
|
||||
**原状**(ROADMAP 记的行号已漂移):`EnsureGenerateMipmapStorageAllocated` 现在位于 `Renderer/VulkanRenderer.cpp:1590-1637`(非 :1562-1590),其中 `texture.AllocateStorage(...)` :1632、`texture.MarkStorageDirty(...)` :1633 直接写前端 `TextureObjectMipmap` 的 level 存储。调用点在 `GenerateMipmap`(def :11378,非 :11317)。
|
||||
|
||||
**现状(P5c tx 已落地)**:`GenerateMipmap` 在 transport 下改走 `EnsureGenerateMipmapShadowAllocated`(:1655-1682),把生成的链定义在 server 的 `StagedTextureStore` 上、以 twin 地址为键(`KeyForTwinAddress`,:1670);monolith 臂保持原样(:11464-11474 的三元)。**跨进程:成立。**
|
||||
|
||||
**残余两处,均已被代码注释自认**:
|
||||
- 形状读:`EnsureGenerateMipmapShadowAllocated(*resource, baseMipLevel, texture->GetUploadTargets())`(:11470)——upload-target 列表仍读前端对象。注释 :1653-1654 明说:"The upload-target list still comes from the frontend object - a shape READ, the P7 registry's residual"。
|
||||
- `GenerateMipmap` 头部仍经 `MGB_CTX->GetTextureUnitObject(...)`(:11401,BARRIER_PULLED)取得前端纹理,并把它传给 `m_textureManager->NeedsMipChainGrowth(*texture)`(:11420)与 `SyncTextureAndGetDescriptor(*texture)`(:11442、:11477)——后者进入 §5.1 的前端地址键控同步,读 client mip shadow(见 §5.2 的 texture-legacy-arm)。
|
||||
|
||||
**P5f 处置建议**:T5 本体无需再做;残余随 §4 的 `GetTextureUnitObject` / §5.1 的纹理 twin 迁移一起消失(upload-target 列表可随纹理描述符/记录供给)。
|
||||
|
||||
## 3. C1(ROADMAP.md:67)核验:四处全部属实,已改走 server backend
|
||||
|
||||
四处均在 `MG_Config::Transport != Monolith` 时读 `MG_Remote::Server::ServerLoopInstance().Backend()->GetDynamicParameters()`(server 自己的 backend),monolith 臂保持 `pActiveBackendObject`:
|
||||
|
||||
| # | 站点(当前 HEAD) | 函数 |
|
||||
|---|---|---|
|
||||
| 1 | `DirectVulkan.cpp:714-733`(split 臂 :723-728) | `ShaderStorageBlockBinding`,`MaxShaderStorageBufferBindings` 越界检查 |
|
||||
| 2 | `Renderer/VulkanRenderer.cpp:676-712`(split 臂 :682-693) | `ApplyLineWidthState`,aliased line-width clamp |
|
||||
| 3 | `Renderer/VertexInputStateFactory.cpp:252-264` | `narrowFloat64Arrays` 判定(`SupportsFloat64VertexAttributes`) |
|
||||
| 4 | `Renderer/VertexInputStateFactory.cpp:515-527` | `ToVkVertexFormat` 的 Long 臂同一 flag 判定 |
|
||||
|
||||
(ROADMAP 记的 :713-715 / :667-676 / :249-250 / :502-503 均略有漂移,指向相同代码。)`DirectVulkan.cpp:20` 起的 `#if MOBILEGL_BUILD_DISAGGREGATED` include 即为此。**跨进程:成立**(server 读自己的 backend 对象)。Espryt 回落臂(`DirectGLES/Utils.cpp:50`)不在本普查范围。
|
||||
|
||||
## 4. 九个 "P7 (Magma)" BARRIER_PULLED 字段的 Magma 读点
|
||||
|
||||
字段归属以 `MG_Pipe/FieldOwnership.def:53-107`(非 sticky 行)与 :178-192(sticky forward 行)为准。"draw 路径"= 经 `SetupDraw`(:6801) / `TrySetupDrawFastPath`(:6375) / `GetOrCreatePipeline`(:5250) 到达;"dispatch 路径"= 经 `DispatchCompute`(:7419) / `DispatchComputeIndirect`(:7472) 到达。
|
||||
|
||||
### 4.1 `GetBoundVertexArray` — 6 站,全部在 draw 路径
|
||||
|
||||
- `DirectVulkan.cpp:805`(`BuildClosedLineLoopIndices`,LINE_LOOP 重写):读出 VAO 的索引缓冲绑定后 **`indexBufferShared->SyncPersistentMappedRange()` + `MappedData()`**(:814-815)——后者在 transport 下是 `Fatal{buffer-legacy-arm}`。即 Magma 的 LINE_LOOP indexed draw 在 split 下今天是具名 Fatal,不是静默错。
|
||||
- `DirectVulkan.cpp:929`(`MultiDrawElementsImpl` :913):只读 EBO 绑定存在性以决定逐条回放。
|
||||
- `VulkanRenderer.cpp:6446`(`TrySetupDrawFastPath`)、`:6852`(`SetupDraw`):每次 draw 的对象读;fast path 还有 handle 臂(`ResolveVaoHandle`,:6452-6458,`MagmaPipeTrackHArmIsHandles`),但 handle 由 `vao.GetLifetimeId()` 推出——仍以前端对象为源。
|
||||
- `VulkanRenderer.cpp:12550`(`MultiDrawElementsIndirectCount` :12528)、`:12651`(`MultiDrawElementsIndirect` :12629)。
|
||||
|
||||
**今天为何能工作**:同地址空间 + 每条记录设障;值是 client 残余填充的 `SharedPtr<VertexArrayObject>`(PipeFill.cpp:1902-1905 一带,见 FieldOwnership.def:68-71 的引文)。**跨进程:不成立**(前端堆 SharedPtr)。
|
||||
|
||||
### 4.2 `GetBufferBindingPoint` / `GetBufferBindingPointCount` — 3 + 2 站,draw 与 dispatch 都到
|
||||
|
||||
- `UniformManager.cpp:1205` + `:1200`(`ResolveStorageBufferDescriptor` :1157,SSBO/原子计数器绑定解析,draw/dispatch 共享)。
|
||||
- `UniformManager.cpp:2035` + `:2030`(`ResolveUniformBufferPayload` :1979,UBO 绑定解析,draw/dispatch 共享)。
|
||||
- `VulkanRenderer.cpp:11764`(`BeginXfbCaptureForDraw` :11714,`BufferTarget::TransformFeedback`,XFB 捕获,draw 路径)。
|
||||
|
||||
三处取到绑定点后都继续 `bindingPoint.GetBoundObject()` 拿前端 `BufferObject`,再走 §7 的 buffer legacy arm。**跨进程:不成立**(`BindingSlotRange1D` 前端基址指针,PipeFill.cpp:2591-2592 的 "four raw bases" 同类)。`GetBufferBindingPointCount` 是 sticky forward(FieldOwnership.def:147-148),退役标签 "P7/P13"。
|
||||
|
||||
### 4.3 `GetFramebufferBindingSlot` — 13 站
|
||||
|
||||
- draw 路径:`VulkanRenderer.cpp:3158`(`GetShaderTransformFlags`,判 default FBO 决定 Y-flip 编译位)、`:5510`、`:5730`(`GetOrCreatePipeline`,深度/模板附件门控与 render-pass 相容性)、`:6360`(`GetBaseTransformFlagsRaw` 的 MOBILEGL_ASSERT——**注意:INFO 级别下断言被编译掉,这站不总是执行**)、`:6465`(fast path)、`:6845`(`SetupDraw`)、`UniformManager.cpp:560`(`ResolveSamplerDescriptor` 的 feedback-loop 诊断)。
|
||||
- 其余 verb:`VulkanRenderer.cpp:7648`(`Clear`——**这就是 integration-magma-split 预期红的那一对 `GetFramebufferBindingSlot@Clear`,CMakeLists.txt:2411**)、`:8016`(`QueueClearBufferPayload`)、`:9024`/`:9025`(`BlitFramebuffer` 的 bound 臂;具名臂见 §5.4)、`:9660`(`CopyTexSubImage2D`,Read 靶)、`:10371`(`ReadPixels`,Read 靶)。
|
||||
|
||||
读到的都是前端 `BindingSlot<FramebufferObject>` 指针再 `GetBoundObject()`。**跨进程:不成立**。
|
||||
|
||||
### 4.4 `GetImageTextureBinding` — 4 站,draw 与 dispatch 都到
|
||||
|
||||
- `UniformManager.cpp:1021`(`ResolveStorageTexelBufferDescriptor` :991)、`:1312`(`ResolveStorageImageDescriptor` :1276)、`:1867`(`CollectStorageImageTextures` :1818)、`:1955`(`CollectSamplerImageFeedback` :1900,sampler/image 子资源重叠危害探测)。
|
||||
|
||||
返回前端 `ImageTextureBinding` 基址后的元素。**跨进程:不成立**。
|
||||
|
||||
### 4.5 `GetTextureUnitObject` — 8 站(与 FieldOwnership.def:90 的 "8 Magma sites" 吻合)
|
||||
|
||||
- `UniformManager.cpp:511`(`ResolveSamplerDescriptor`,随后 `textureUnit.GetSamplerObject()` :512 取前端 SamplerObject)、`:787`(`ProgramSamplesOnlySingleLevelTextures`)、`:826`(`ResolveSamplerTexture`)、`:852`(`ResolveSamplerTextureRaw`)、`:1679`(`ResolveSampledBinding`)。
|
||||
- `VulkanRenderer.cpp:9652`(`CopyTexSubImage2D`)、`:11157`(`GetTexImage`——class C,split 下 client 侧已具名拒绝,EmitTables.cpp:1673-1675)、`:11401`(`GenerateMipmap`)。
|
||||
|
||||
指针指进 client 的纹理单元数组(PipeInputs.h:669 的解引用 + PipeFill.cpp:274 的裸指针填充)。**跨进程:不成立**。
|
||||
|
||||
### 4.6 `GetProgramForDraw` / `GetProgramForDispatch` — 3 + 2 站
|
||||
|
||||
- draw:`VulkanRenderer.cpp:6384`(`TrySetupDrawFastPath`)、`:6815`、`:6853`(`SetupDraw`)。
|
||||
- dispatch:`VulkanRenderer.cpp:7424`(`DispatchCompute`)、`:7476`(`DispatchComputeIndirect`)。(FieldOwnership.def:97 注里写的 :7327 已漂移。)
|
||||
|
||||
拿到前端 `SharedPtr<ProgramObject>` 后整个 program 内容都从前端对象读:`ProgramFactory::ComputeHash`/`GetOrCreateProgram` 读 `program.GetGeneratedSpirv()`(ProgramFactory.cpp:2469-2473、:3472);`ResolveUniformBufferPayload` 读 `program.GetUBOData()/GetUBOSize()`(UniformManager.cpp:1992-1993,具名 UBO host payload,ROADMAP.md:142 D-B8 的已知形状)、`GetUniformBlockBinding`/`GetUniformBlockName`/`GetUBOSizeAt`(:2028/:2033/:2063)。**跨进程:不成立**。Espryt 在 P5e pa 的证明("记录早就带着逐 link 的 ProgramArchive",ROADMAP.md:31)对 Magma 尚未接线——Magma 的 program 记录载体是否存在,本次未核实到(**未知**,fm 包第一天要回答)。
|
||||
|
||||
### 4.7 `GetTransformFeedbackProgram` — 2 站,draw 路径
|
||||
|
||||
- `VulkanRenderer.cpp:11725`(`BeginXfbCaptureForDraw`)、`:11824`(`EndXfbCaptureForDraw`)。前端 `SharedPtr<ProgramObject>`。**跨进程:不成立**。配套读还有 `IsTransformFeedbackActive/Paused`(:11716/:11722,值类,记录供给,成立)、`GetBoundTransformFeedbackLifetimeId`(:11667,值类)、`GetTransformFeedbackGeneration`(:11801,值类)、`HasOpenTransformFeedbackSpan`(:11684,sticky forward,"P7/P9"——前端 XFB span 表查询,**不成立**)。
|
||||
|
||||
### 4.8 `GetTextureObject` — 1 站
|
||||
|
||||
- `VkTextureManager.cpp:810`(`SyncTextureAndGetDescriptor` 内):按 GL name 查前端纹理对象表以注册 alive 追踪;查不到就 `texture.weak_from_this()`(:821)——把前端对象的 control block 存进 server 侧 map。sticky forward,退役标签裸 "P7"。**跨进程:不成立**。
|
||||
|
||||
### 4.9 相邻但非 "P7 (Magma)" 的字段读(列出以免漏网)
|
||||
|
||||
- `GetBufferBindingSlot`("P8/P9/P13",15 靶合一数组)13 站:`DirectVulkan.cpp:283`(`ResolveIndirectCommandBytes`,DrawIndirect)、`:407`(`MultiDrawArraysIndirect`)、`:470`(`MultiDrawArraysIndirectCount`,Parameter)、`:538`(`DrawElementsIndirect`)、`:591`(`DrawArraysIndirect`);`VulkanRenderer.cpp:7520`(`DispatchComputeIndirect`,DispatchIndirect)、`:11126`(`GetTexImage`,PixelPack)、`:12560`/`:12566`、`:12661`、`:12731`(multi-draw indirect 各站);`UniformManager.cpp:910`(`ResolveTexelBufferDescriptor`)、`:1053`(`ResolveStorageTexelBufferDescriptor`——这两站是 `textureBuffer->GetBufferBindingSlot()`,前端 TextureBuffer 对象自身的方法)。indirect 路径取到前端 buffer 后普遍跟 `SyncPersistentMappedRange()`(如 DirectVulkan.cpp:285、VR:7525)——transport 下具名 Fatal。
|
||||
- `GetTransformFeedbackPausedPrimitiveCounter`(FATAL 行):`DirectVulkan.cpp:1256`(`GetQueryResult64` :1228)、`:1303`(`BeginXfbPrimitivesQuery` :1294)。两个入口都是 class C(EmitTables.cpp:1690-1693),split 下 client 侧先拒绝,**今天不可达**;行为一致,归 §2.7,不归 P5f。
|
||||
- `GetPixelStoreParameters(false)`(pack 半,APPLIER_DERIVED):`VulkanRenderer.cpp:11127`。**跨进程成立**。注意 FieldOwnership.def:208 注里写的 ":10980" 已漂移(该行现在是 MOBILEGL_ASSERT),真身在 :11127——文档漂移,供 fm 顺手修注。
|
||||
|
||||
## 5. 前端地址 / 前端身份键控的 twin registry 与 memo
|
||||
|
||||
### 5.1 `VkTextureManager`(纹理 twin)
|
||||
|
||||
- `TextureIdentity{ ITextureObject* texture, Uint64 lifetimeId }`(VkTextureManager.h:126-145),`m_textureResources` / `m_aliveObjects` 均以此为主键;hash 混入前端对象地址(:144)。`m_syncedTextureMemo[8]` 直接以 `const ITextureObject*` 比较(:708-715,"A dead-then-reused texture address cannot false-hit: the new object carries a new lifetime id")。
|
||||
- `SyncTexture`(VkTextureManager.cpp:1745-1796+)整体裹在 `MGPipeTextureLegacyArmScope`(:1755)里——注释 :1748-1754 自认:"Magma's texture sync still reads - and clears - the CLIENT's mip shadow: the dirty scan below, and UploadDirtyMipLevels' texel reads / region reads / MarkStorageDirty(false) clears"。**这是 §2.2 意义上最大的一块:读 client 纹素字节 + 写(清脏)client 存储**,guard 是 texture-legacy-arm,scope 内豁免。
|
||||
- `ToStorageMipLevel/ToStorageArrayLayer(ITextureObject*)`(VkTextureManager.h:113-126)全树散布,读前端 view 父链。
|
||||
|
||||
**跨进程:不成立**。P5c 只迁了 Espryt 的 sync(走 StagedTextureStore 句柄键),Magma 的 server 侧 sync 是 P7 的活。
|
||||
|
||||
### 5.2 `VkBufferManager`(缓冲 twin)
|
||||
|
||||
- twin 指针直接**写进前端对象**:`bufferObject->SetBackendResource(resource)`(VkBufferManager.cpp:305、:601),读出对称(:291、:299)。`GetBackendResource/SetBackendResource` 是前端 BufferObject 上的 mutable 槽——server 写 client 内存。
|
||||
- `AcquirePersistentMap`(:598-646)seed 时读 `bufferObject.MappedData()`(:631)。
|
||||
- `AcquireResidentSlice`(:649-699)/ `AcquireStreamedSlice`(:701-):每 draw 调用,含 `SyncPersistentMappedRange()`(:657/:708)、`GetSize()`、`GetChangeSerial()`(:726)、`MappedData()` 整缓冲上传(:682/:768)。transport 下第一行即 `Fatal{buffer-legacy-arm}`(见 §0.1)。
|
||||
|
||||
**跨进程:不成立**;且今天是 guarded Fatal,不是静默读。
|
||||
|
||||
### 5.3 draw 路径上的地址键控小缓存
|
||||
|
||||
- `ConvertedVertexStreamKey.buffer = sourceBufferShared.get()`(VulkanRenderer.cpp:4106-4116)+ `GetChangeSerial()`(:4107)+ `MappedData()` 转换读(:4127)——`UploadAndBindVertexStreams` 内,前端 buffer 地址做 memo 键 + 读 client 字节。
|
||||
- `UploadAndBindIndexBuffer` 的 restart-index 替换臂:`indexBufferShared->SyncGpuWrites()` + `MappedData()` + `GetSize()`(:4421-4423)——`SyncGpuWrites` 在 transport 下有反向通道臂(BufferObject.cpp:571-590 起),但那是 client 线程语义;apply 线程调它的行为本次未逐行走通(**未知**,大概率在 `MappedData()` 的 guard 处先 Fatal)。
|
||||
- `SetupDrawSnapshot`(VR:6388-6470 一带)以 `program.GetLifetimeId()`、前端 VAO/FBO 的地址与 lifetime id 做键(:6446-6468)。
|
||||
- `UniformManager.h` 的 memo 族(`SamplerResolveMemo` :418 起、`FastRebindMemo` :363、`m_globalUboMemo` :392、`m_descriptorReuseMemo` :341)以 binding + program lifetime id / 版本号做键——键本身是值,但值全部来自前端对象读出。
|
||||
- `ProgramFactory::ProgramLookupCache.program` 存前端 `ProgramObject*`(ProgramFactory.h:582-587)。
|
||||
- `g_programResourceCaches`(DirectVulkan.cpp:103-108)以 **GL name** 为键、lifetime id 校验(:166-189)——`GetShaderStorageBlockIndex/Binding`(:313/:332,client 侧查询路径)与 `ShaderStorageBlockBinding`(:714,P5b 的 `set_storage_block_binding` 记录会经 sink 在 apply 线程到达)都会进 `TryGetDirectVulkanProgram`(:274-280),后者调 sticky forward `ValidateProgramName` + `GetProgramObject`("P9" 行)——**server 按名字探前端名字表,跨进程不成立**;GL-name 键控的 cache 本身是 server 内存,成立。
|
||||
|
||||
### 5.4 具名 blit 端点解析(P5c G6 / P5e ruling 12)
|
||||
|
||||
- `VulkanRenderer.cpp:8974-9022`:split 臂从 applier 的 verb 句柄工作区取 `ReadFbo/DrawFbo` 句柄(:8989-8991),随后 `resolveEndpoint` lambda 里**直接拼写 `MG_State::pGLContext`**(:8997、:9004、:9005)与 `MG_Pipe::MGPipeSlots()`(:8999、:9003),全程裹 `MagmaP7AllocatorDebtScope`(:8995)。这是 DirectVulkan 全树仅有的 `pGLContext` 与 `MGPipeSlots()` 直接拼写。**跨进程:不成立**(前端 FBO 池 + client 分配器)。P7 的退役方式注释已写明(:8983-8984:"P7 retires it by carrying the object identity in the record")。
|
||||
- 附带发现:**ARCHITECTURE.md:460 的 C 门原文是 `grep -c 'pGLContext' MG_Backend/ == 0`,当前 HEAD 实测非零**——`VulkanRenderer.cpp` 3 处代码命中(:8997/:9004/:9005)+ `MGPipe/PipeInputs.h:822` 一处注释。该门是否仍在 CI 以 ==0 执行、还是已改为白名单,本次未核实(**未知**);若仍按字面执行则门已破,值得在 f1 之前确认。
|
||||
|
||||
### 5.5 `MagmaPipeIdentityTables`(MagmaPipeArms.h:370-529)
|
||||
|
||||
Magma 自己的 {slot, gen} mint,按 lifetime id 把前端 VAO/Buffer 映到 server 侧 slot 表(`HandleOf` :515-518);`VertexInputStateFactory::VaoBackendMemos` 以该 handle 为键(VertexInputStateFactory.h:149-171)。表本身是 server 私有内存,**成立**;但 lifetime id 的获取仍靠读前端对象(`vao.GetLifetimeId()`),身份源是 §4.1 的字段读—— mint 干净、水源脏。P5f 无需动表,动的是喂它的字段。
|
||||
|
||||
## 6. server 在 apply 线程上创建 / 销毁前端类型对象
|
||||
|
||||
- 隐藏 blit 资源:`VulkanRenderer.cpp:4475-4491`(`ShaderObject`/`ProgramObject`)、`:4527-4540`(两个 `SamplerObject`);隐藏 depth-mipmap 资源:`:4567-4617`。销毁走 `ShutdownBlitResources`/`ShutdownDepthMipmapResources`(:4544-4562、:4634-4643),已裹 `MagmaP7AllocatorDebtScope`(:4559、:4640)——注释自认析构会触发 client death helper 的 lifetime-id 探测。注释 :4552-4553 明说:"P7 gives these resources storage that is not a frontend object"。
|
||||
- UniformManager 占位纹理:`MakePlaceholderTextureObject`(UniformManager.cpp:167-189,`GetFallbackTexture` :1410/:1435、`GetFallbackMultisampleTexture` :1451/:1497-1499 调用),以及 `m_unboundStorageImageTextures`(UniformManager.h:317)。**每构造一个纹理对象都会 `MGPipeMintTextureHandle` + `MGPipeEmitTextureResourceCreate`**(TextureObject.cpp:143-170,push 构建下无条件 mint)——apply 线程上的 `MGPipeSlots().Acquire` 无 scope 即 `Fatal{RoleViolation, "MGPipeSlots"}`(SlotAllocator.cpp:330、:47-59)。也就是说 **Magma 的占位纹理在 split 下首次需要时即具名 Fatal**(与 ROADMAP.md:127 "占位纹理原生化 → P7" 互证)。ProgramObject/ShaderObject/SamplerObject 的构造不 mint(grep 无命中),故隐藏 program/sampler 的创建本身今天不触发守卫,只有析构的探测被 scope 兜住。
|
||||
|
||||
**跨进程:不成立**(server 进程里没有 MG_Impl/MG_State 的 client 语义,构造出的对象也不该存在)。P5f 处置:随 P7 的"内部 shader 烘焙 / 占位纹理原生化"换成 server 原生资源;f1 双块演习时这些点会以 Fatal 形式自己报出来。
|
||||
|
||||
## 7. 对前端 BufferObject 的直接方法调用(写 client 存储的边)
|
||||
|
||||
- `bufferObject->MarkGpuWritten()` 直写臂已按 P5c ev 改掉:三处都是 `#if MOBILEGL_PIPE_PUSH → MGPipeAnnounceBufferGpuWritten(...) / #else 直写` 双臂(UniformManager.cpp:1077-1085、:1241-1247;VulkanRenderer.cpp:11774-11781)。announce 内部经 `MagmaP7AllocatorDebtScope` 做 client 分配器的 lifetime-id 反查(ResourceTracker.h:710-743,:728 是"第四笔债"),找到句柄则走 `OnGpuWritten` → SEG_EVENT;找不到句柄则**回退直写 client 对象**(:738)。**跨进程:announce 主臂成立;回退臂不成立;scope 内的分配器探测不成立**。
|
||||
- `bufferObject->EnsureGpuResidentStorage()`(UniformManager.cpp:1077、:1239;VulkanRenderer.cpp:11774):transport 下早退 `return false`(BufferObject.cpp:877-880,R-6 的第二道门),但早退之前已读 `m_resource.IsGpuResident()` / `m_isMapped`(:856、:866)——**无守卫的 client 内存读**,今天靠同地址空间成立,跨进程不成立。
|
||||
- `indexBufferShared->SyncGpuWrites()`(VR:4421):见 §5.3。
|
||||
|
||||
## 8. 写 client 存储 / 读 client 镜像的其他站点
|
||||
|
||||
- `RecordError`(写前端错误队列):`VulkanRenderer.cpp:1367`、`:1371`(`RecordClearBufferError`/`RecordTextureCopyError` 帮手)、`:1427`(`RecordUnsupportedFramebufferError`)、`DirectVulkan.cpp:735`(`ShaderStorageBlockBinding`)。transport 下 `PipeInputs::RecordError` 改走 `ServerSessionInstance().PostGlError` → SEG_EVENT `kEventGlError`(PipeFill.cpp:2164-2178),client 在 drain 点入账。**跨进程:成立**(排序是 P9 的已知债)。
|
||||
- `InvalidateCompileEnv`(写前端编译环境):`BackendObject_DirectVulkan.cpp:390`(`InitCapabilities`)、`:788`(`ApplyVulkanCapabilitiesForTesting`)。transport 下 `PipeInputs::InvalidateCompileEnv` 已删除为空操作(PipeFill.cpp:2143-2156,失效改由 caps 重发布完成)。**跨进程:成立**。
|
||||
- `SwapchainObject` 默认 FBO 附件写:transport 下走 `OnSurfaceChanged` → SEG_EVENT(SwapchainObject.cpp:323-339),monolith 臂仍直写 `pDefaultFramebufferInfo` 的 `AllocateStorage/SetInternalFormat`(:342-358)。**跨进程:成立**(P5c ev / R3 已处理,ROADMAP.md:64)。
|
||||
- `GetTextureImage`(VR:11162-11209+):入口签名直接吃前端 `SharedPtr<ITextureObject>`(DirectVulkan.h:101 声明、DirectVulkan.cpp:768 入口),内部 `SyncTextureAndGetDescriptor(*textureObject)`(:11175)+ 无 GPU 存储时回退 `MG_Impl::GLImpl::CopyTextureImageToClientOrPBO_State`(:11205)——直接调 MG_Impl 函数读 client shadow 写 client 目标缓冲。但两个 GL 入口都是 class C(EmitTables.cpp:1673-1675),split 下 client 先拒绝,**今天不可达**;若 P9 要放开,这是整条臂的跨进程形状问题,先记着。
|
||||
|
||||
## 9. 值类读取(非债,列出备查)
|
||||
|
||||
以下 accessor 在 DirectVulkan 下被读、但属 RECORD_SUPPLIED / APPLIER_DERIVED,跨进程成立,不需 P5f 动作:`IsCapabilityEnabled(Indexed)`、`GetRenderStateParameters(Version)`、`GetPipelineStateVersion`、`GetStencilState`、`GetColorMaskIndexed`、`GetDepthMask`、`GetBlendColor/Func/Equation`、`GetScissorBox`、`GetViewportIndexed`、`GetDepthRangeIndexed`、`GetClearColor/Depth/Stencil`、`GetActiveTextureUnit`、`GetCurrentVertexAttribute`、`GetPatch*`、`GetPolygon*`、`GetLineWidth`、`GetLogicOp`、`GetCullFaceMode`、`GetDepthFunc`、`GetMinSampleShadingValue`、`GetClampReadColor`、`GetProvokingVertexMode`(VR:13229)、`IsTransformFeedbackActive/Paused`、`GetTransformFeedbackGeneration`、`GetBoundTransformFeedbackLifetimeId`,以及三 shutter `GetTextureBindGeneration`/`GetSamplingResolutionGeneration`(VR:6477/:6659/:6907/:6913/:7031/:7050/:7361/:7387 等,P5c rv 起 APPLIER_DERIVED,server 自答)。`g_dynamicStateShadow`(VR:290-409)是纯 server 侧 memo,成立。
|
||||
|
||||
## 10. 未知 / 留给 fm 的问题
|
||||
|
||||
1. Magma 的 program 内容(SPIR-V、UBO 初值)是否已有记录载体(类似 Espryt 的 ProgramArchive)——本普查未在 wire schema 侧核实;若没有,4.6 是 fm 最大的一块。
|
||||
2. `SyncGpuWrites` 从 apply 线程在 transport 下的实际行为(BufferObject.cpp:590 起的臂是否假设 GL 线程)。
|
||||
3. ARCHITECTURE.md:460 的 C 门是否仍以 ==0 执行(当前实测非零,见 §5.4)。
|
||||
4. indirect draw 无绑定缓冲(client 指针)时,记录在 wire 上如何携带命令字节、server 端 `ResolveIndirectCommandBytes` 的 client 指针解引用在严格双块下的归类(指针随记录过来,值无意义——大概率走 client-memory 升级的具名拒绝,待 f1 的红来确认)。
|
||||
5. `GetTexImage`/`GetTextureImage` 若 P9 放开,VR:11162 起的整条前端对象签名链需要重新设计(§8)。
|
||||
@@ -0,0 +1,225 @@
|
||||
# f0 普查子项:§2.5 registry 重键普查(`MGPipeFrontendKeyedRegistryScope`)
|
||||
|
||||
> 基线 `feat/disaggregated @ 8b68b92c`(代码头 `25fba0d5`,其后仅文档提交)。只读普查,未改动任何代码。
|
||||
> 路径省略前缀 `MobileGL/`。行号均在当前 HEAD 实测。
|
||||
|
||||
## 0. 范围与实际 grep 结果
|
||||
|
||||
计划 §2.5 写"约 12 个活跃站点(:2542、:4505、:5540、:5659、:5940、:6490、:6576、:7092、:7682、:8213、:11231 …)"。
|
||||
当前 HEAD 实测(`rg MGPipeFrontendKeyedRegistryScope`,全树):
|
||||
|
||||
- **`MG_Backend/DirectGLES/DirectGLES.cpp`:13 处构造点**(比计划列举多 `:12606`、`:14461`),另有 `:2090` 一处注释记录 vi 家族已删除的 VAO 站点(非构造点)。
|
||||
- `MG_Backend/DirectGLES/Managers.cpp`:**11 处**(`:3063`、`:4689`、`:6101`、`:8934`、`:9762`、`:9816`、`:10953`、`:10974`、`:14102`、`:14463`、`:14760`)。
|
||||
- `MG_Test/Wire/RemoteClientTest.cpp:1930`、`:1948`:测试夹具,非生产路径。
|
||||
|
||||
本报告主体是 DirectGLES.cpp 的 13 处;Managers.cpp 的 11 处在 §4 作附注(同一豁免家族,超出本子项口径但处置同类)。
|
||||
|
||||
## 1. 机制回顾:scope 保护的是什么,P5e id 已经重键了什么
|
||||
|
||||
**scope 的语义**(`MG_Impl/Pipe/SlotAllocator.h:250-282`、`:272-282`;实现 `SlotAllocator.cpp:146-157`):
|
||||
P5c G6 的具名豁免——豁免的探测是"server 私有、但以**前端身份**键控的 twin registry 查询"。
|
||||
P5e(id,CONTRACT-P5E §4.4)把豁免条件收窄为**仅当前记录设障**(`MGPipeApplierCurrentRecordIsBarriered()`):
|
||||
设障时 client 停在 `WaitForApplied` 里,其分配器与 lifetimeId→slot 映射静止,只读探测无撕裂;
|
||||
未设障记录下 scope 不豁免任何东西,探测即 `Fatal{RoleViolation, "MGPipeSlots"}`(`SlotAllocator.cpp:32-70`)。
|
||||
monolith 下守卫首行即返回(`SlotAllocator.cpp:33`),scope 只是标记。
|
||||
|
||||
**被保护的"键"是什么**。七张 twin 表全部是 `BackendSlotTable`/`TwinRegistry`(`Managers.h:1579` VAO、`:2118` 纹理、`:2297` FBO、`:3110` 程序、`:3298` 采样器、`:3383` sampler-view、`:3449` renderbuffer)。
|
||||
P5e(id)之后,**每张表都已按 {slot,gen} 键控**:`FindByHandle` / `GetOrCreate(MGPipeHandle)` / `ReleaseByHandle` 全部存在
|
||||
(`SlotTables.h:351`、`:461`、`:486`),Gen 检查、composite band、ABA 语义齐备(CONTRACT-P5E §4.2/§4.3)。
|
||||
scope 站点走的是**前端身份半边**:
|
||||
|
||||
- `Find(StateObject*)` ≡ `FindByHandle(HandleOf(stateObj))`(`SlotTables.h:477-480`),
|
||||
`HandleOf` = `stateObj->GetLifetimeId()` → `MGPipeSlots().FindByLifetimeId(kind, lifetimeId)`(`SlotTables.h:501-517`)——
|
||||
即"前端对象指针 → lifetime-id → client 分配器探测"。这是 **lifetime-id 键**,不是裸地址键;
|
||||
- `GetOrCreate(const StatePtr&)` 在 miss 时经 `Acquire` **铸造**句柄(`SlotTables.h:263-298` 一带,守卫在 `:298`);
|
||||
- legacy-memos 臂(`MOBILEGL_PIPE_LEGACY_MEMOS`)才是裸前端地址键 + weak_ptr owner-equality(`DirectGLES.cpp:75-153`)。
|
||||
|
||||
所以每张表"按句柄重键缺什么"的答案**不是缺表的句柄能力**——表早就有了——缺的是**句柄到达该站点**:
|
||||
要么记录已带句柄但 sink/后端没用它(:2542、:12606),要么该站点整个是 monolith glue,transport 下根本不该到达(其余 11 处)。
|
||||
|
||||
**P5e id 已删的**:draw path 上约 20 处 scope 构造点连同其包裹的前端探测(CONTRACT-P5E §4.4 列旧行号;
|
||||
当前 HEAD 的 `DirectGLES.cpp:2086-2093` 注释记录了 VAO 站点是"删除而非收窄",删除使回退变响)。
|
||||
**存活的口径**(CONTRACT-P5E §4.4 末段):设障行站点(CopyTex / GetTexImage / mipmap-shape /
|
||||
set_storage_block_binding / detach-walk)、两个 verify 臂、`BufferImpl::HandleOfBuffer`。
|
||||
注意该列举写于 P5e 基线,当前 HEAD 已有漂移:CopyTex 与 mipmap 已改走句柄(见下),GetTexImage 在 wire 上已是 class-C 具名拒绝(见 :14461 条)。
|
||||
|
||||
## 2. 总结论
|
||||
|
||||
13 个站点分两类:
|
||||
|
||||
- **A 类(P5f 跨进程必需,2 处)**:transport 下真实活着、被设障行保护的前端键探测——
|
||||
**`:2542`(resource_copy_region 的 CopyImage 端点同步)** 与 **`:12606`(set_storage_block_binding)**。
|
||||
这两处的记录**早已携带句柄**(`MGPCopyRegion::Src/Dst`,`MGPipeTypes.h:1469`;`MGPStorageBlockBinding::ShaderCso`,`:1792`),
|
||||
sink/后端没用而已。
|
||||
- **B 类(P3b/P4b 债 / monolith glue,11 处)**:在受支持的 transport 配置下不可达——或前面已有 transport 具名拒绝/abort
|
||||
(:6576、:7682),或臂选择器先行分流到 by-handle 臂(:4505、:5540、:5659、:5940、:6490、:7092),
|
||||
或 wire 槽位本身是 class-C 拒绝(:14461),或仅 monolith 路径调用(:11231),或**全树无调用者**(:8213)。
|
||||
这些在"拆进程后还成立吗"的判据下**不成立也无妨**,因为它们到达不了 server 进程;
|
||||
它们退役的正确归属是 P3b/P4b 的 monolith-glue 清理,而不是 P5f 的 wire 完备性。
|
||||
|
||||
## 3. DirectGLES.cpp 逐站点
|
||||
|
||||
### A 类:transport 下活着的设障行站点
|
||||
|
||||
#### :2542 — `TextureImpl::SyncTextureObjectToBackend(textureObject, imageBindable)`
|
||||
|
||||
- **保护的 registry**:`TextureImpl::g_backendTextureObjects`(纹理 twin 表)。
|
||||
- **今天的键**:`Find(textureObject.get())` / `GetOrCreate(textureObject)`(`:2544-2546`),slot 臂上经 lifetime-id 探测;
|
||||
注释自己写明(`:2538-2541`)。
|
||||
- **对象是否已有 {slot,gen}**:有。纹理的句柄由 `resource_create/respecify` 先行(CONTRACT-P5E §4.2 纹理行)。
|
||||
- **transport 下谁到达这里**:`DirectGLES::CopyImageSubData` → `MakeGLESCopyImageEndpoint`(`:12301`),
|
||||
其端点由 sink `ServerVerbSink::OnResourceCopyRegion`(`MG_Remote/Server/PipeApplier.cpp:856-859`)
|
||||
用 **BARRIER_PULLED 的 `gPipeInputs.GetTextureObject(GlName)`** 重建出前端对象再传入。
|
||||
`resource_copy_region` 是 `kWaitApplied`(`PipeCalls.def:254`),client 停车,故探测合法。
|
||||
(视图嵌套同步 `SyncTextureViewToBackend` 也调它,但 transport 臂已改为 `SyncTextureViewToBackendByRecord` 走 `Desc.ViewOf` 句柄,`Managers.cpp:7178-7195`。)
|
||||
- **按句柄重键缺什么**:不缺载体——`MGPCopyRegion` 已同时携带 `Src/Dst` 句柄与 `SrcGlName/DstGlName`(`MGPipeTypes.h:1468-1476`)。
|
||||
缺:(1) sink 用 `Src/Dst` 句柄而非 `GetTextureObject` 重建端点(顺带退役 FieldOwnership 的 `GetTextureObject` P7 行);
|
||||
(2) `CopyImageEndpoint` 增加句柄形态或后端函数表加 by-handle 入口;(3) `MakeGLESCopyImageEndpoint` 改 `SyncTextureToBackendByHandle`。
|
||||
- **今天为何能工作**:设障(kWaitApplied)+ scope 豁免,client 停着,lifetime-id 探测读的是静止的 client 分配器。
|
||||
- **P5f 处置建议**:归 fr 包。这是 §2.1 `GetTextureObject`(P7 行)在 Espryt 侧的**实际消费点**,句柄已在记录里,属纯接线;
|
||||
做完后 `:2542` 的 scope 在 transport 下失去最后一个到达者,可随 P3b/P4b 的 monolith-glue 清理一起删。
|
||||
|
||||
#### :12606 — `ShaderStorageBlockBinding(program, storageBlockName, binding)`
|
||||
|
||||
- **保护的 registry**:`PrgramImpl::g_backendProgramObjects`(程序 twin 表,kind ShaderCso)。
|
||||
- **今天的键**:sink `OnSetStorageBlockBinding`(`PipeApplier.cpp:910-928`)按 GL 名调后端表项,
|
||||
后端先 `MGB_CTX->ValidateProgramName` / `GetProgramObject(program)`(两个 BARRIER_PULLED 行,P9),
|
||||
再 `Find(programObject.get())`(`:12608`)→ lifetime-id 探测。
|
||||
- **对象是否已有 {slot,gen}**:有;且**记录已带**——`MGPStorageBlockBinding::ShaderCso`(`MGPipeTypes.h:1791-1796`)。
|
||||
CONTRACT-P5E §2.2 本就写着"trailing: pg resolves through `MGPStorageBlockBinding::ShaderCso` and flips it",未落。
|
||||
- **按句柄重键缺什么**:sink/后端按 `ShaderCso` 解析 twin(`ResolveProgramTwin`/`FindShaderCsoRecord` 已在),
|
||||
跳过 `GetProgramObject` 全程;该行 `kWaitApplied`(`PipeCalls.def:297`)即可翻 `kWaitNone`。
|
||||
注意当前实现的"不同步即返回"语义(`:12614-12617`,link version 落后则留给 reseed)在 by-handle 臂需用
|
||||
`m_syncedShaderCsoSerial` vs `record->Serial` 重述。
|
||||
- **今天为何能工作**:同上——设障 + scope。
|
||||
- **P5f 处置建议**:归 fr 包(或与 §2.1 的 `GetProgramObject`/`ValidateProgramName` P9 行一起:这两行的 Espryt 消费点主要就是这里)。
|
||||
契约已预定、记录已带句柄,是 13 处里**收益最直接**的一处。
|
||||
|
||||
### B 类:monolith glue / transport 下不可达(P3b/P4b 债)
|
||||
|
||||
#### :4505 — `SyncCurrentFBO()` 的 legacy 循环
|
||||
|
||||
- registry:`FramebufferImpl::g_backendFramebufferObjects`;键:前端 FBO 指针(经 `HandleOf`)。
|
||||
- transport 下:`SyncCurrentFBOByRecord()` 先行(`:4479`),decline 且 fb 位开启时直接
|
||||
`RefuseFramebufferBindingSlotRead()` abort(`:4490-4492`)。fb 位关 + transport 的组合里,
|
||||
环绕它的 `GetFramebufferBindingSlot` 读本身已是 BARRIER_PULLED 具名红——**非受支持配置**。
|
||||
- 处置:P3b/P4b 删 monolith glue 时连同 `GetOrCreate(currentFBO)` 臂一起删;P5f 不需要动它。
|
||||
|
||||
#### :5540 — `ResolveGlobalConstantsRecord(program)`(monolith-glue 半边)
|
||||
|
||||
- registry:`g_backendProgramObjects`;键:`HandleOf(program)`(`:5546`)。
|
||||
- 注释自带结论(`:5530-5533`):"Reached only when `Transport == Monolith`";transport 的唯一调用点
|
||||
走 `ResolveGlobalConstantsRecordForHandle`(`:7139`)。
|
||||
- 处置:P3b/P4b。P5f 无需动。
|
||||
|
||||
#### :5659 — `SyncCurrentProgram(SharedPtr<ProgramObject>)`
|
||||
|
||||
- registry:`g_backendProgramObjects`(另含两张表的 `CollectGarbageIfNeeded`,`:5661-5662`);键:前端程序指针(`:5679-5680`)。
|
||||
- transport 下:调用点全部按 `ProgramHandleArm()`(`Managers.h:714-716`)分流到
|
||||
`SyncCurrentProgramByHandle`(draw `:6260-6265`,dispatch `:8156-8161`)。
|
||||
- 处置:P3b/P4b。
|
||||
|
||||
#### :5940 — `BindCurrentFBO(target)` 的 legacy 臂
|
||||
|
||||
- registry:`g_backendFramebufferObjects`;键:前端 FBO 指针 `Find(currentFBO.get())`(`:5947`)。
|
||||
- transport + fb 位:记录臂 `FindByHandle(record.Fbo)`(`:5889`)先行返回;decline 则 abort(`:5917-5919`)。
|
||||
- 处置:P3b/P4b。
|
||||
|
||||
#### :6490 — `ResolveAndBindUnitTextures()` 的 legacy 单元绑定 pass
|
||||
|
||||
- registry:`g_backendTextureObjects`;键:前端纹理指针 `Find(textureObject.get())`(`:6492`)。
|
||||
- transport + 纹理位:`UnitTexturesByHandle()` 的记录臂在 `:6397` 返回,走不到这里(`:6341-6398`)。
|
||||
- 处置:P3b/P4b。该 legacy pass 的外层读 `MGB_CTX->GetTextureUnitObject(unit)`(`:6425`)本身是 §2.1 的
|
||||
BARRIER_PULLED 行(Magma 的那半归 fm),scope 只是其中 registry 探测的标记。
|
||||
|
||||
#### :6576 — `ResolveUnitSamplerBackend(unit, samplerObject)`
|
||||
|
||||
- registry:`SamplerImpl::g_backendSamplerObjects`;键:`HandleOf(samplerObject)`(`:6582`)。
|
||||
- transport 下:**scope 之前就已 abort**——`Fatal{ProtocolCorruption, "BindSamplerStates.Count"}`(`:6565-6573`),
|
||||
P5e(tx2)按 CONTRACT-P5E §4.2 把 identity sampler 家族在 transport 下整个删除。
|
||||
- 处置:scope 已形同虚设(只为 monolith 编译期保留),随 P3b/P4b 删。
|
||||
|
||||
#### :7092 — `BindCurrentProgramWithResources` 的 else 臂
|
||||
|
||||
- registry:`g_backendProgramObjects`;键:前端程序指针 + stash 比较(`:7096-7104`)。
|
||||
- transport 下:`handleArm` 走句柄 stash / `ResolveProgramTwin`(`:7080-7083`)。注释自标"monolith glue only"(`:7088-7091`)。
|
||||
- 处置:P3b/P4b。
|
||||
|
||||
#### :7682 — `GetCurrentBackendProgram()`
|
||||
|
||||
- registry:`g_backendProgramObjects`;键:前端程序指针 `Find(currentProgram.get())`(`:7684`)。
|
||||
- transport 下:`ProgramHandleArm()` 臂(`:7659-7670`)在 scope 之前返回,scope 不可达(注释 `:7680-7681` 自明)。
|
||||
- 处置:P3b/P4b。
|
||||
|
||||
#### :8213 — `GetBackendProgramId(GLuint program)`
|
||||
|
||||
- registry:`g_backendProgramObjects`;键:前端程序指针(`:8215-8217`,Find-or-create + 按需 `SyncToBackend`)。
|
||||
- **特殊发现:当前 HEAD 全树无任何调用者。** 该函数无头文件声明(`DirectGLES.h`、`Managers.h` 均无),
|
||||
不在后端函数表(`BackendObject.h` 无此槽),MG_Impl/MG_Remote/MG_Pipe 无引用;唯一同名物是
|
||||
`BackendProgramObjectImpl::GetBackendProgramId()` 成员(`Managers.h:2894`)。CONTRACT-P5E §5.5 称它
|
||||
"is a monolith entry",但至少在当前 HEAD 它是**死代码**(唯一旁证:`MG_Test/Program/ProgramInterfaceTest.cpp:1411`
|
||||
的注释提到这个名字描述一种既有危害)。
|
||||
- 处置:先确认删除(或恢复声明与调用者),与 P5f 无关;若保留则归 P3b/P4b。**建议 fr 包顺手删掉它连带这处 scope**——
|
||||
这是普查发现的唯一"scope 保护着没人调的函数"。
|
||||
|
||||
#### :11231 — `ScopedDetachedTextureFramebufferAttachments(SharedPtr<ITextureObject>)`(detach-walk 前端构造)
|
||||
|
||||
- registry:`g_backendTextureObjects`(`Find(texture.get())`,`:11233`)+ `g_backendFramebufferObjects.ForEachLive`
|
||||
内经 `StateForHandle` 取回前端 FBO 对象(注释 `:11243-11255`)。
|
||||
- transport 下:唯一 transport 调用者是 `GenerateMipmapByRecord` 的**句柄构造**(`:12076`,走 `:11162` 的
|
||||
`MGPipeHandle` 重载,纯记录走查);前端构造只被 monolith 的 `GenerateMipmap` 调用(`:12151`),
|
||||
而 transport 的 `GenerateMipmap` 在 `:12105-12117` 已整臂分流到 `VerbMipRes`。
|
||||
- 处置:P3b/P4b(fb 包的 reverse index 已是句柄臂的替代)。注意 `StateForHandle` 这半边由
|
||||
`MGPipeRefuseFrontendKeyedRegistryFromUnbarrieredApply` 单独把守(`SlotAllocator.cpp:76-86`),
|
||||
scope 删后该守卫仍在。
|
||||
|
||||
#### :14461 — `GetTexImage(target, level, format, type, pixels)`
|
||||
|
||||
- registry:`g_backendTextureObjects`;键:前端纹理指针(`:14463`),其前端对象来自
|
||||
`GetActiveTextureUnit`/`GetTextureUnitObject`/`GetBindingSlot` 三连 BARRIER_PULLED 读(`:14448-14453`)。
|
||||
- transport 下:**不可达**——wire 槽 `GetTexImage` 在 `MGR_UNMIGRATED_TAIL_SLOTS`(`EmitTables.cpp:1673`),
|
||||
即 class-C 具名拒绝 `Fatal{UnmigratedVerb, "GetTexImage"}`;DSA 的 `GetTextureImage` 由前端自己的 CPU 读回
|
||||
应答(`SlotCaps.h:57`),其后端槽同样在拒绝名单(`EmitTables.cpp:1674-1676`)。
|
||||
即 CONTRACT-P5E §4.4 当年列举的"GetTexImage 设障行站点"在当前 HEAD 已漂移成 class-C。
|
||||
- 处置:本分两个阶段看——P9 若把 GetTexImage 接上 wire,`MGPReadbackInfo::Res`(`MGPipeTypes.h:1555`)已带句柄,
|
||||
届时按 :2542 同款模式重键;在那之前此 scope 仅 monolith 可达,归 P3b/P4b。**当前不属于 P5f 跨进程必需。**
|
||||
|
||||
## 4. 附注:Managers.cpp 的 11 处(同一豁免家族,本子项口径外)
|
||||
|
||||
均为 G6 注释标记的同类探测,除注明外均为 monolith-glue 性质:
|
||||
|
||||
| 行 | 函数 | registry / 键 |
|
||||
|---|---|---|
|
||||
| `:3063` | `BufferImpl::HandleOfBuffer` | buffer twin 表;lifetime-id。CONTRACT-P5E §4.4 点名的残留("until vi/sb carry the handle");当前调用者在 monolith 臂(`DirectGLES.cpp:1076`、`:1366`) |
|
||||
| `:4689` | `SamplerViewImpl::HandleOfSamplerViewForTexture` | sampler-view 表;lifetime-id |
|
||||
| `:6101` | 纹理 remint-pull 的 rearm(`RequireImageBindableStorage` 族) | 纹理表 `HandleOf` |
|
||||
| `:8934` | `BackendTextureObject::ResolveOwnRecord` | 纹理表 `HandleOf`;by-handle 臂靠 `m_pushedSyncHandle` 不探测 |
|
||||
| `:9762` / `:9816` | `SyncAttachmentObject` | 纹理 / renderbuffer 表 Find-or-Create;legacy FBO 附件走查 |
|
||||
| `:10953` / `:10974` | FBO verify 臂(`glGetFramebufferAttachmentParameteriv` 断言走查) | 纹理 / renderbuffer 表 Find;debug 断言代码 |
|
||||
| `:14102` | 程序 `SyncToBackend` 的 serial 盖章 | 程序表 `HandleOf`;注释自标 "MONOLITH GLUE ONLY" |
|
||||
| `:14463` | 采样器参数同步的"未注册"判别 | 采样器表 `HandleOf`;后端自产采样器(raw-depth-fetch)靠它区分 |
|
||||
| `:14760` | renderbuffer `SyncToBackend` 的记录解析 | renderbuffer 表 `HandleOf`;注释自标 monolith glue |
|
||||
|
||||
建议 fr 包把 Managers.cpp 这 11 处与 DirectGLES.cpp 的 B 类一起列入"随 P3b/P4b 删除"清单,避免 P5f 范围漂移。
|
||||
|
||||
## 5. 对 P5f(fr 包)的处置清单(汇总)
|
||||
|
||||
1. **fr 的真工作只有两处接线**:`:2542`(resource_copy_region 改吃 `MGPCopyRegion::Src/Dst` 句柄,
|
||||
顺带退役 `GetTextureObject` 拉取)与 `:12606`(set_storage_block_binding 改吃 `MGPStorageBlockBinding::ShaderCso`,
|
||||
顺带退役 `GetProgramObject`/`ValidateProgramName` 拉取,WaitClass 可翻 `kWaitNone`)。
|
||||
两者记录都已带句柄,registry 都已是句柄键——**是接线,不是重键**。
|
||||
2. `:8213`(`GetBackendProgramId`)疑似死代码,建议确认后随 fr 删除。
|
||||
3. 其余 11 处(DirectGLES.cpp)+ 11 处(Managers.cpp)是 monolith glue / P3b/P4b 债,
|
||||
**不应计入 P5f 的跨进程判据**;双块演练(§4)不会让它们变红,因为它们根本不被 transport 到达。
|
||||
计划的 fr 包若按"删全部 scope"来写会虚胖;按本普查,fr 的 red-once 判据应落在 A 类两处。
|
||||
4. 口径修正建议:P5F-WIRE-COMPLETENESS.md §2.5 写"registry 仍按前端地址 / lifetime-id 键控"——
|
||||
实测 **registry 本体已全部按 {slot,gen} 键控**(P5e id 完成),残存的是"调用点仍用前端身份解析",
|
||||
且 13 处中 11 处在 transport 下不可达。建议把 §2.5 措辞改为"两个设障行调用点仍经前端身份解析"。
|
||||
|
||||
## 6. 未知项(如实记录)
|
||||
|
||||
- `:2542` 的 `SyncTextureObjectToBackend` 在 transport 下是否还有 CopyImageSubData 之外的到达者,
|
||||
本普查核对了其全部 8 个调用点(`DirectGLES.cpp:3323`、`:3458`、`:3543`、`:3852`、`:10363-10364`、
|
||||
`:12131`、`:12134`、`:12301`,加 `Managers.cpp:7104`):其余均在 monolith/legacy 臂或被
|
||||
`FramebufferRecordArmIsMandatory` 之类的分流挡住;若臂选择器被操作员改出非默认组合,可达性会变化——未逐组合穷举。
|
||||
- `:8213` "无调用者"的结论基于全树文本检索;若存在经函数指针表以外的动态到达(如 dlopen 符号),未能覆盖——未发现任何迹象。
|
||||
@@ -0,0 +1,259 @@
|
||||
# f0-reverse-channel — P5f §2.6 反向通道逐站点普查(只读)
|
||||
|
||||
> 基线 `feat/disaggregated @ 8b68b92c`(代码头 `25fba0d5`)。路径在 `MobileGL/` 下。
|
||||
> 本文件为只读普查,未改任何代码、未发起构建、未跑测试。对应 P5f 计划 §2.6
|
||||
> (`docs/Disaggregated/P5F-WIRE-COMPLETENESS.md:200-204`)与 P5c 审计表 R1–R5 / B4 行
|
||||
> (`docs/Disaggregated/ROADMAP.md:61-66`)。
|
||||
>
|
||||
> **行号漂移提醒**:任务与 `ROADMAP.md:61`(B4 行)引用的 XFB scatter 旧行号
|
||||
> (`DirectGLES.cpp:950/1060/1077/1081/1172/1185`)在当前 HEAD 已落在
|
||||
> `SyncVaoAttributeBuffersByHandle/ByRecord`(VBO memo)区域,与 XFB 无关。XFB 机制现位于
|
||||
> `DirectGLES.cpp:1508-2072`(`XfbImpl` 命名空间),scatter 本体在 `:1726-1839`,
|
||||
> 非 scatter 回读臂在 `:1602-1684`。下文一律以当前 HEAD 行号为准。
|
||||
|
||||
---
|
||||
|
||||
## 0 总览:十个回调,四类有接线
|
||||
|
||||
`MGPipeCallbacks` 共 10 个成员(`MG_Pipe/MGPipeCallbacks.h:27-51`,个数有 static_assert 把守)。
|
||||
逐个数 producer / consumer / 事件种类:
|
||||
|
||||
| 回调 | EventKind | server producer | client consumer | 状态 |
|
||||
|---|---|---|---|---|
|
||||
| `OnBufferWriteback` | `kEventBufferWriteback`(`EventRing.h:49`) | `ServerSession.cpp:264-292` | `ClientSession.cpp:266-291` → `ResourceTracker.h:575-638` | **已接线** |
|
||||
| `OnGpuWritten` | `kEventGpuWritten`(`EventRing.h:50`) | `ServerSession.cpp:294-312` | `ClientSession.cpp:292-314` → `ResourceTracker.h:645-667` | **已接线** |
|
||||
| `OnSurfaceChanged` | `kEventSurfaceChanged`(`EventRing.h:51`) | `ServerSession.cpp:314-330` | `ClientSession.cpp:315-329`(→ `:211-252`) | **已接线** |
|
||||
| (`OnGlError`) | `kEventGlError`(`EventRing.h:52`,P5c 新增) | **不经回调表**:`PipeInputs::RecordError` 直调 `ServerSession::PostGlError` | `ClientSession.cpp:330-365` | **已接线,但绕过回调表**(见 §5) |
|
||||
| `OnTextureWriteback` | 无 | 无 | 无 | 零接线 |
|
||||
| `OnTexturePullRequest` | 无 | 无 | 无 | 零接线(终止符见 §7) |
|
||||
| `OnMipLevelsGenerated` | 无 | 无 | 无 | 零接线(见 §6) |
|
||||
| `OnXfbScatterReady` | 无 | 无 | 无 | 零接线(见 §3) |
|
||||
| `OnCapsInvalidated` | 无 | 无 | 无 | 零接线,由 R-12 caps 重发布取代(`CapsMirror.cpp:78-80`) |
|
||||
| `OnLog` | 无 | 无 | 无 | 零接线 |
|
||||
|
||||
安装/卸载:`ServerSession::Accept` 只装三个(`ServerSession.cpp:538-542`),`Close` 对称卸载
|
||||
(`:622-632`);双重安装是 `Fatal{RoleViolation,"callback-double-install"}`(`:337-347`)。
|
||||
consumer 侧按名直调、绝不回走 `gMGPipeCallbacks`(`ClientSession.cpp:203-206` 注释,防
|
||||
layer-2 反调)。这套所有制关系本身**没有跨进程问题**,P5f 无需动。
|
||||
|
||||
---
|
||||
|
||||
## 1 XFB scatter(Espryt)——字节路径已迁移,对象身份仍指回 client
|
||||
|
||||
这是 B4 行的本体。当前 HEAD 的传输臂形状(P5c 已落):
|
||||
|
||||
- **非 scatter 臂** `ReadbackCapturedRanges`(`DirectGLES.cpp:1602-1684`):transport 下
|
||||
(`:1616-1656`)按 handle 解析 server 资源(`:1622-1623`)、map 的是 **ES 驱动 buffer**
|
||||
(`:1626-1629`)、字节经 `OnBufferWriteback` 事件回传(`:1638-1643`),serial/epoch 的
|
||||
序按 Ops_H_Readback 同款规则(`:1650-1654`)。monolith 臂(`:1658-1680`)仍直接
|
||||
`target.buffer->WritebackFromBackend`(`:1675`),被 transport 分支的 `continue` 隔开。
|
||||
- **scatter 臂** `ScatterCapturedRecords`(`:1726-1839`):transport 下预捕获字节读的是
|
||||
**server 自己的 staged shadow**(`:1776-1794`,含 `RequireStagedCoverage` 覆盖断言
|
||||
`:1789-1790`),拼好的范围同样经 `OnBufferWriteback` 回传(`:1807-1819`),不再读改写
|
||||
client shadow。monolith 臂 `target.buffer->MappedData()` / `WritebackFromBackend`
|
||||
(`:1794`/`:1826`)保留。
|
||||
|
||||
**仍触碰 / 依赖 client 内存的残余**(都在 transport 臂上,逐条列):
|
||||
|
||||
1. **`scatterProgram` 是 client `ProgramObject` 的 `SharedPtr`,并在 apply 线程上直接调它的
|
||||
方法。** 来源:`MGB_CTX->GetTransformFeedbackProgram()`(`:1871`,BARRIER_PULLED 字段,
|
||||
`FieldOwnership.def:106` 记 "P3b/P4b (Espryt), P7 (Magma)"),存进
|
||||
`XfbObjectState::scatterProgram`(`:1538`、`:1925`)。scatter 时读
|
||||
`GetTransformFeedbackPackedStride()`(`:1731`)、`GetTransformFeedbackStride()`(`:1766`)、
|
||||
`GetTransformFeedbackVaryings()`(`:1796`)——**这些都是对 client 对象体的读**,
|
||||
字节不走 client shadow 了,但 layout 元数据仍从 client 对象上现取。
|
||||
- 今天为何能工作:同地址空间 + barrier/run-ahead 窗口内对象存活(P5e 的 `ReleaseFillPins`
|
||||
机制钉住 O 类 SharedPtr,`ClientSession.cpp:1073-1082`)。
|
||||
- P5f 处置:这是 §2.1 `GetTransformFeedbackProgram` 字段退役(P3b/P4b 债)与 fv 的交集——
|
||||
scatter 所需的三个 layout 答案应随 XFB span 记录上 wire(值类,可推导),server 侧只留
|
||||
快照。**分类:P5f(fv,依赖 §2.1 字段的载体判定先行)。**
|
||||
2. **`XfbCaptureTarget::buffer` 持有 client `BufferObject` 的 `SharedPtr` 跨整个 span**
|
||||
(`:1511`,填充于 `StartPendingTransformFeedback` 的 `:1890-1901`,后者同时读
|
||||
BARRIER_PULLED 的 `GetBufferBindingPoint`,`:1891`)。transport 臂只取
|
||||
`HandleOfBuffer(target.buffer.get())`(`:1622`/`:1779`)——**用 client 裸地址当 registry
|
||||
键**,落在具名豁免 `MGPipeFrontendKeyedRegistryScope` 内(`Managers.cpp:3063`)。
|
||||
- 今天为何能工作:同地址空间下地址即身份;scope 把债记名。
|
||||
- P5f 处置:targets 快照应改持 `{handle, range}` 纯值,地址键探针随 §2.5 registry 重键
|
||||
(包 `fr`)一起退。**分类:P5f(fr ∩ fv;本身不是字节读写,但跨进程后"client 地址"无意义)。**
|
||||
3. **`MGB_CTX->GetTransformFeedbackCapturedVertices()`(`:1733`)是 RECORD_SUPPLIED**
|
||||
(`PipeFieldOwnership.inc:85`,emitter `SetContextValues`,`PipeFill.cpp:3064`),
|
||||
**不是债**,列出以免后人重复普查。
|
||||
|
||||
另外 XFB 整条机制读 `GetBufferBindingPoint`(BARRIER_PULLED,`FieldOwnership.def:80`)的退役
|
||||
属 §2.1 字段包,fv 不应重复记账,只登记依赖关系。
|
||||
|
||||
## 2 `OnBufferWriteback` —— 已接线,blobref 约定闭环(无 P5f 债)
|
||||
|
||||
**producer(后端侧,3 处)**,全部以 `Seg = kMGHostSpanSegNone` + host 地址形态交出:
|
||||
`Managers.cpp:2451-2455`(`Ops_H_Readback`,readback verb 的字节)、
|
||||
`DirectGLES.cpp:1638-1643`(XFB 非 scatter 臂)、`DirectGLES.cpp:1809-1814`(XFB scatter 臂)。
|
||||
三处都带"无通道则 MGLOG_E_ONCE 丢弃"的显式降级(如 `:1644-1648`),不是静默。
|
||||
|
||||
**producer(会话侧)** `ServerOnBufferWriteback`(`ServerSession.cpp:264-292`):
|
||||
入参 `Seg != kMGHostSpanSegNone` 即 `Fatal{ProtocolCorruption,"OnBufferWriteback.Seg"}`
|
||||
(`:266-276`)——**后端边界只接受 host span,段拷贝是这个函数自己的活**;字节 memcpy 进
|
||||
SEG_EVENT 记录内联尾(`:285-290`)。满环经 `ReserveEventOrBlock`(`:211-262`):
|
||||
run-ahead 服务器park 等 client 排空(CONTRACT-P5E §2.6 流控),lockstep 服务器
|
||||
`Fatal{EventRingOverflow}`。整 buffer 大于半环的切片在 client 侧发起处完成
|
||||
(`BufferObject.cpp:593-612`,`BufferWritebackSliceBytes`)。
|
||||
|
||||
**consumer** `DrainEventRing`(`ClientSession.cpp:266-291`):在此构造
|
||||
`Seg = kSegEvent` + `OffsetInSegment` 的 blobref(`:282-285`),交给
|
||||
`MGPipeClientOnBufferWriteback`(`ResourceTracker.h:575-638`)——三臂校验齐备:
|
||||
kSegEvent 经 client 自己的 SegmentTable 界检解析(`:596-617`)、transport 下 host span 即
|
||||
Fatal(`:584-593`)、其他 Seg 即 Fatal(`:618-624`)。写 client shadow 的是 GL 线程上的
|
||||
`WritebackFromBackend`(`:636-637`),归属正确。
|
||||
|
||||
- 今天为何能工作:约定是**双向 Fatal 执法**的,不只是文档;wire 形态(`Seg=kSegEvent`)
|
||||
在跨越点被强制。
|
||||
- P5f 处置:**无**。剩余课题(批处理、epoch 排序、`eventRingFull`/`eventDropped` 的丢弃
|
||||
策略)都是 P9 的,`EventRing.h:14-16` 与 `CONTRACT-P5C.md §4.4` 已记名。
|
||||
|
||||
## 3 `OnXfbScatterReady` —— 声明存在,全仓零接线
|
||||
|
||||
仅有声明(`MGPipeCallbacks.h:50`,注释写明设计意图:server 交回 packed scratch、
|
||||
**client 自己 scatter**,`:48-49`)。grep 全仓无 producer、无 consumer、无对应 EventKind;
|
||||
`CONTRACT-P5C.md:536` 把它与 `OnTexturePullRequest` 并列记给 P9。
|
||||
|
||||
- 今天为何能工作:P5c 选的是另一条路——scatter 留在 server,读 server staged shadow、
|
||||
结果走 `OnBufferWriteback`(§1)。两条路在语义上互斥地覆盖同一个需求。
|
||||
- P5f 处置:**无**。是否改走"client scatter"是纯设计/异步化选择(P9 的题材,
|
||||
`ROADMAP.md:34` P3b/P4b 行也记了"XFB scatter 搬到 client")。若 P9 采纳,§1 的
|
||||
残余 1/2 自然消解(layout 与 targets 都回到 client 侧)。**分类:P9。**
|
||||
|
||||
## 4 `OnGpuWritten` —— Espryt 已净,Magma 有一处响亮但真实的直捅兜底
|
||||
|
||||
**Espryt**:`MarkBufferGpuWritten`(`Managers.cpp:3072-3103`)在资源族臂上只发事件
|
||||
(`:3101-3102`,单条 `kMGPipeWholeBuffer` 全量 range——收窄是 P8/P9 的,
|
||||
`ResourceTracker.h:646-656` 有 assert 把守形状)。它在 transport 下的三个原调用点已被
|
||||
P5e 删除或门控:`MarkShaderStorageBuffersGpuWritten` 整体早退(`DirectGLES.cpp:845`,
|
||||
注释 `:828-844` 说明 client 的 `MarkShaderStorageBindings` 已覆盖同集);
|
||||
`SyncAtomicCounterBuffers` 的记录臂不标(`:918-924`);
|
||||
`MarkWritableImageBufferTexturesGpuWritten` transport 下整函数 return(`:3884-3887`)。
|
||||
client 侧保守集在 `MG_Remote/Client/GpuWritePending.{h,cpp}`(GL 线程自标,窄化通道反向
|
||||
运行)。**Espryt 无 P5f 债。**
|
||||
|
||||
**Magma**:三个站点经 `MGPipeAnnounceBufferGpuWritten` 走回调——
|
||||
`UniformManager.cpp:1082`(storage texel buffer,Access != READ_ONLY)、
|
||||
`UniformManager.cpp:1244`(storage block)、`VulkanRenderer.cpp:11778`(XFB capture)。
|
||||
但 helper 本体(`ResourceTracker.h:710-743`)有两个跨进程不成立的点:
|
||||
|
||||
1. handle 来自 **`MGPipeSlots().FindByLifetimeId`(client 分配器探针)**,套在
|
||||
`MagmaP7AllocatorDebtScope` 具名豁免里(`:717-730`)——P7 债,记名在册。
|
||||
2. **handle 缺失时的兜底是直捅 client 对象**(`:732-739`:MGLOG_E_ONCE 之后
|
||||
`bufferObject->MarkGpuWritten()`——apply 线程写 client 内存)。注释说这是"响亮而非
|
||||
静默"的故意选择;在 P5f 双块演练下这一支会成为真实违规。
|
||||
|
||||
- 今天为何能工作:同地址空间;Magma 跑 lockstep(不发布 `kCapRunAheadApply`),
|
||||
client 停在 barrier 后,直捅恰好安全——ID-132 的那类掩盖。
|
||||
- P5f 处置:**分类 P5f(fm/fv)**。把兜底从"直捅"改成具名 Fatal 或携带性降级
|
||||
(例如记 pending 由 client 侧保守集兜底——反正 GpuWritePending 已按绑定走查标过同集,
|
||||
这个兜底在语义上是冗余保险而非正确性来源);lifetime-id 探针随 Magma 的 §2.2/fm 一并退。
|
||||
|
||||
## 5 `OnGlError` —— 已接线,但绕过了回调表;顺序保证是"下一个 drain 点"
|
||||
|
||||
**路径不经 `gMGPipeCallbacks.OnGlError`**:该成员声明于 `MGPipeCallbacks.h:29`,
|
||||
**全仓零安装、零调用**。实际路径是 `PipeInputs::RecordError`(`PipeFill.cpp:2164-2186`)
|
||||
在 transport 下直接调 `ServerSessionInstance().PostGlError`(`:2175`),后者产
|
||||
`kEventGlError`(`ServerSession.cpp:709-739`,消息内联、NUL 随带、producer 侧截断到
|
||||
1024,`EventRing.h:108-115`)。consumer `ClientSession.cpp:330-365` 校验
|
||||
`MessageBytes != 0` 且 NUL 在位(否则 `Fatal{ProtocolCorruption,"kEventGlError"}`),
|
||||
在 GL 线程写 client 自己的 `pGLContext->RecordError`(`:353-357`)。
|
||||
|
||||
**顺序保证的现状**(任务点名项):所有 producer 都在 apply 线程上、共用同一个 SEG_EVENT
|
||||
环,所以 **错误事件与 writeback/gpu-written/surface 事件之间保 FIFO**;client 侧的
|
||||
保证是"**下一个 drain 点观测到**"——保住的是同线程的 error-then-read 程序序,
|
||||
不保跨 verb 交错。这是写明并接受的局面,不是漏洞:`PipeFill.cpp:2168-2173`、
|
||||
`ClientSession.cpp:350-352`、`CONTRACT-P5C.md §4.2`(表格第 4 行,`:281`)、
|
||||
run-ahead 下的松弛在 `CONTRACT-P5E.md:181`。`FieldOwnership.def:164` 的 `RecordError`
|
||||
行仍挂 BARRIER_PULLED "P9"(`MGP_STICKY_FORWARD_PULL` 宏仍执行、计数仍走 `rsp`),
|
||||
有序化退役排在 P9。
|
||||
|
||||
- 今天为何能工作:错误队列是 client 状态,写它发生在 GL 线程 drain 时(归属正确);
|
||||
序的保证靠 drain 点分布(§8)而非锁。
|
||||
- P5f 处置:**本体分类 P9**(有序化、`OnGlError` 回调成员的正式接线)。P5f 只需登记一个
|
||||
不对称事实:**错误事件不走回调表,因此 §4.1 的双重安装守卫与"十个成员"的记账方式
|
||||
盖不住它**;若 fv 收口时顺手把 `gMGPipeCallbacks.OnGlError` 也接到
|
||||
`ServerSession::PostGlError`(或显式标注该成员永不经表),可消除"将来有人调
|
||||
`OnGlError` 而静默空转"的坑。这是可选的整洁项,不是跨进程缺陷。
|
||||
|
||||
## 6 `OnTextureWriteback` / `OnMipLevelsGenerated` —— 零接线,且今天的 Espryt 不需要它们
|
||||
|
||||
全仓零 producer / consumer / EventKind。核查它们"本该服务"的路径在 transport 下的现状:
|
||||
|
||||
- `glGenerateMipmap`(Espryt):transport 下走记录臂(`DirectGLES.cpp:12093-12117` →
|
||||
`GenerateMipmapByRecord :12037-12081`),纹理由 `VerbMipRes` 句柄解析,存储形状由
|
||||
**前端自己分配**、server 侧只做描述符核验(`EnsureGenerateMipmapStorageDescribed`,
|
||||
`:11040`起)——这正是 `OnMipLevelsGenerated` 注释里"SHAPE ONLY, client owns the CPU
|
||||
shadow"(`MGPipeCallbacks.h:39-41`)想要的形态,只是由"前端先行分配"实现而非事件通知。
|
||||
- 唯一真正需要 texel 回流的是 RGB16F/RGB32F 的 CPU filter
|
||||
(`GenerateThreeChannelFloatMipmapOnCpu` 读改写 client level shadow):transport 下已被
|
||||
具名拒绝 `MGPipeUnmigratedEmulation("generate-mipmap-cpu-filter")`(`:12051-12056`),
|
||||
记录保持 barriered(`:12030-12036` 注释)。**它就是 `OnTexturePullRequest` /
|
||||
`OnTextureWriteback` 的未来用户**,`CONTRACT-P5B.md:255/:379` 也这么记。
|
||||
- Magma 侧 mip 直写 client level 存储是 T5(`ROADMAP` §2.2 引用
|
||||
`VulkanRenderer.cpp:1562-1590` 一带),属包 `fm`,此处只登记交叉。
|
||||
|
||||
- P5f 处置:**无**。**分类:P9**(纹理拉取与 texel 回写通道的建设);T5 归 fm。
|
||||
|
||||
## 7 `OnTexturePullRequest` 与终止符 `ResourceSubDataComplete` —— 整条未建
|
||||
|
||||
零 producer。终止符 `ResourceSubDataComplete` 有 wire 位(opcode 50,
|
||||
`PipeCalls.def:229`、`PipeWire.inc:122`)但 **applier 是显式空 stub**:
|
||||
`PipeWireCodec.cpp:2070-2073`("There is no client producer and no applier; the
|
||||
reverse channel is P7/P9",`return false`)。`PipeCatalogueTest.cpp:1425` 注释同样记为
|
||||
later phase。
|
||||
|
||||
- 今天为何能工作:缩减路径上没有纹理重铸(recast)场景;唯一会需要的 CPU filter 已被
|
||||
具名拒绝(§6)。
|
||||
- P5f 处置:**无**。**分类:P9**(`ROADMAP.md:37` P9 行"纹理拉取四条缓解 + 终止符")。
|
||||
|
||||
## 8 排空点清单(顺序保证的现实基础,P5f 不需要动)
|
||||
|
||||
`kEventGlError` 与所有事件的"下一个 drain 点"具体是(全部在 GL 线程):
|
||||
`ClientSession.cpp:170`(事件积压唤醒的二级退出)、`:908`(SEG_CMD 空间等待出口)、
|
||||
`:1071`(`EmitAndWait` barrier 之后——主排空点,注释 `:1066-1070`)、`:1206`
|
||||
(`WaitForApplyToCatchUp`,run-ahead 强制对齐)、`:1256`/`:1269`(present credit 等待)、
|
||||
`:1456`(公共 `DrainEvents`);另有第二类:阻塞式 EGL 生命周期 RPC 返回处
|
||||
(`CONTRACT-P5C.md §4.1`,`:263-272`)。每个等待出口都排空是 P5e 流控死锁论证的支柱
|
||||
(`:1202-1206`)。**这些是全值的拆分机语义,跨进程成立,P5f 无处置。**
|
||||
|
||||
## 9 SEG_EVENT blobref 约定核验结论(任务点名项)
|
||||
|
||||
约定(`CONTRACT-P5C.md:54`):wire 上的 `OnBufferWriteback` blobref 必须
|
||||
`Seg = kSegEvent` + 段内偏移,**永不**是 host 地址;monolith 下保持 `kMGHostSpanSegNone`。
|
||||
逐环核验:**约定被所有 producer 遵守,且每一环都有执法**——
|
||||
|
||||
1. 后端 → 会话:3 处 producer 全部交 `kMGHostSpanSegNone` host span(§2);
|
||||
2. 会话入口:`Seg` 检查是 Fatal(`ServerSession.cpp:266-276`);
|
||||
3. wire 形态构造:只有 `DrainEventRing` 一处造 `kSegEvent` blobref(`ClientSession.cpp:282-285`);
|
||||
4. consumer 三臂:kSegEvent 界检解析 / host span 在 transport 下 Fatal / 其他 Fatal
|
||||
(`ResourceTracker.h:583-624`)。
|
||||
|
||||
**无违规、无缺口。**
|
||||
|
||||
## 10 汇总
|
||||
|
||||
| # | 站点 | 触碰 client 内存? | 分类 |
|
||||
|---|---|---|---|
|
||||
| 1 | XFB scatter 字节路径(Espryt) | 否(P5c 已改走 server shadow + writeback 事件) | 无债 |
|
||||
| 2 | XFB `scatterProgram`(client ProgramObject 的 SharedPtr,apply 线程直读 layout) | **是(读)** | **P5f(fv,依赖 §2.1 `GetTransformFeedbackProgram` 载体判定)** |
|
||||
| 3 | XFB `targets` 持 client BufferObject SharedPtr + `HandleOfBuffer` 地址键探针 | 身份依赖(不读字节) | **P5f(fr ∩ fv)** |
|
||||
| 4 | `OnBufferWriteback` 全链 | 否 | 无债(批处理/排序归 P9) |
|
||||
| 5 | `OnXfbScatterReady` | —(零接线) | P9 |
|
||||
| 6 | `OnGpuWritten` Espryt 臂 | 否 | 无债(收窄归 P9) |
|
||||
| 7 | `OnGpuWritten` Magma 兜底(handle 缺失时直捅 `MarkGpuWritten`) | **是(写,响亮兜底)** | **P5f(fm/fv)** |
|
||||
| 8 | `OnGlError`(`RecordError` → `kEventGlError`) | 否(GL 线程写 client 队列) | 有序化归 P9;回调表不对称是可登记的小项 |
|
||||
| 9 | `OnTextureWriteback` / `OnMipLevelsGenerated` | —(零接线;唯一用户被具名拒绝) | P9 |
|
||||
| 10 | `OnTexturePullRequest` + 终止符 | —(整条未建) | P9 |
|
||||
| 11 | `OnSurfaceChanged` 全链(Espryt `:14959-14970` / Magma `SwapchainObject.cpp:324-339` → `ClientSession.cpp:211-252`) | 否(GL 线程写 client `pDefaultFramebufferInfo`,R3 归属正确) | 无债 |
|
||||
| 12 | `OnCapsInvalidated` / `OnLog` | —(零接线;前者由 R-12 重发布取代) | P9/无需 |
|
||||
|
||||
**P5f 实收三条**(#2、#3、#7),全部集中在 XFB 对象身份与 Magma 兜底;反向通道的
|
||||
**字节面在 P5c 之后已经干净**。monolith 臂上的直写点(`DirectGLES.cpp:1675/:1826/:12820/
|
||||
:14391/:14806`、`Utils.cpp:2398`、`Managers.cpp:2025`)逐一核过:transport 下或被
|
||||
`#else` 臂隔开,或被 client 侧具名拒绝把门(`EmitTables.cpp:976-980` 的
|
||||
`UnmigratedVerbFatal("ReadPixels+PACK_BUFFER")`、`PipeWireCodec.cpp:2105-2108` 的
|
||||
`GetTextureImage` class-C 拒绝),均不可达,不构成 P5f 项(其中 `ReadPixels` 服务器臂对
|
||||
`GetBufferBindingSlot(PixelPack)` 的残余读,`DirectGLES.cpp:14337`,属 §2.1
|
||||
`GetBufferBindingSlot` 行 "P9 (readback)",不在本清单重复记账)。
|
||||
@@ -0,0 +1,305 @@
|
||||
# P5f f0 普查(只读):进程级静态量中语义属于 per-context / per-session 的成员
|
||||
|
||||
> 基线:`feat/disaggregated @ 8b68b92c`(代码头 `25fba0d5`,其后提交仅动文档)。
|
||||
> 范围:`MobileGL/` 下 `MG_Backend`(DirectGLES / DirectVulkan / MGPipe)、`MG_Impl`、`MG_Pipe`、
|
||||
> `MG_Remote`、`MG_State`。方法:`g_`/`s_` 前缀可变全局、函数级 `static`、`inline X& = *new X()`
|
||||
> 单例、命名空间级非常量变量的全面 grep,再逐族读代码确认键控方式与重置时机。
|
||||
> 本报告只读产生;未修改任何代码、未构建、未跑测试。
|
||||
|
||||
## 0. 判定框架
|
||||
|
||||
P5f 关心的是「换一个地址空间还成立吗」的一个变体:**这个静态量的语义寿命跟谁走**。
|
||||
|
||||
| 分类 | 含义 |
|
||||
|---|---|
|
||||
| **【A】P5f 必须按角色+世代分区** | 语义属于某个 GL context / session,且当前的重置时机挂在错误的钟上(或不挂任何钟),f1 双块演练下必须暴露 |
|
||||
| **【B】进程级即可** | 拆进程后每个进程自然各有一份,且持有者在 spawn 后唯一(server 独有 / client 独有),或纯诊断/配置 |
|
||||
| **【C】已是 per-context / per-session** | 已有世代键控自失效或已有 per-session 重置机制,只需 f1 确认 |
|
||||
| **【D】未知** | 只读普查不足以判定,标出待 f1/后续包回答的问题 |
|
||||
|
||||
一条贯穿全报告的事实先记住:**拆进程后 Espryt/Magma 后端代码只在 server 进程运行,MG_Impl 只在
|
||||
client 进程运行**(P5F-WIRE-COMPLETENESS.md §1.4:「MG_Impl 根本不在 server 进程里」)。所以
|
||||
`MG_Backend` 下的静态量天然只需 server 一份、`MG_Impl` 下的天然只需 client 一份——**单看拆进程,
|
||||
绝大多数成员是安全的**。真正的缺陷集中在两类:(1) 重置时机挂错钟(挂 server 自己的钟,而语义属于
|
||||
client context 世代);(2) 静态量里存着 client 对象的指针/SharedPtr。
|
||||
|
||||
## 1. 已知成员的核实与重置时机
|
||||
|
||||
### S1 `g_syncedRenderStateParameters` 家族 —— 【A】
|
||||
|
||||
- 证据:`MG_Backend/DirectGLES/DirectGLES.cpp:4584-4617`,同族 `g_syncedRenderStateVersion`、
|
||||
`g_hasSyncedRenderState`、`g_syncedBackendViewport`、`g_syncedBackendScissorBox`、
|
||||
`g_syncedSrgbFramebufferWrites`、`g_forceFullRenderStateResync`、`g_syncedColorMaskAlphaWidenMask`、
|
||||
`g_dualSourceDeclinedBlendStates`。
|
||||
- 语义:「后端 ES context 上当前已推下去的渲染状态」的影子,diff 对象是**前端**
|
||||
`RenderStateParameters` 的版本与字节(`DirectGLES.cpp:4629-4660`)。即 per-(client context ×
|
||||
server ES context) 的乘积状态。
|
||||
- **重置时机**:`RenderStateImpl::InvalidateSyncedRenderState()`(`DirectGLES.cpp:4618-4623`,置
|
||||
`g_forceFullRenderStateResync=true` 并清空四个影子)。调用点两个:
|
||||
(a) `DirectGLES::MakeCurrent()` 尾部(`DirectGLES.cpp:15375`)——每次 backend make-current;
|
||||
(b) viewport-routing 回放收尾(`DirectGLES.cpp:7930`)。注意 `DestroyEGLContext()`
|
||||
(`DirectGLES.cpp:15896-15912`)的清理清单**不含**它——靠「新 context 必经 MakeCurrent」兜底。
|
||||
- 今天为何能工作:inproc 单进程,ES context 与 client context 同在;ID-67 裁定「相同 tuple 的
|
||||
make-current 跳过 native bind」,但 `BackendObject_DirectGLES.cpp:40-42` 的注释写明不同 virtual
|
||||
context 仍进 `MakeCurrent`,七个缓存在那里统一失效。
|
||||
- P5f 处置建议:拆进程后只有 server 持有、重置挂在 server 自己的 MakeCurrent 上,跨进程方向成立;
|
||||
但 (1) f1 双块下必须把它归入 server 角色的块,使 push-monolith 臂与 server 臂不共享;(2) 待验证点:
|
||||
server ES context 换代(`g_backendContextGeneration` 在 `DirectGLES.cpp:15912` 自增)而 client
|
||||
tuple 不变时,`MakeCurrent` 是否仍被调用——若是则靠 :15375 兜住,若否则此家族缺一条挂
|
||||
`g_backendContextGeneration` 的失效线。**该点只读无法定论,标为待 f1 双块回答。**
|
||||
|
||||
### S2 `ScopedDefaultUnpackState::s_synced` 家族 —— 【A】(全清单里最弱的一个)
|
||||
|
||||
- 证据:`MG_Backend/DirectGLES/Managers.cpp:6258-6325`。类内 `static inline` 成员:
|
||||
`s_synced`(:6319)、`s_alignment/s_rowLength/s_skipRows/s_skipPixels/s_imageHeight/s_skipImages`
|
||||
(:6320-6325)。
|
||||
- 语义:后端 ES context 的 GL_UNPACK_* 像素存储状态影子(纹理上传路径的 save/restore)。
|
||||
- **重置时机:没有。** `s_synced` 在 `EnsureShadowSynced()`(:6283-6300)里一次性置 true 后**永不
|
||||
清除**;`grep` 确认它不在 `DestroyEGLContext()`(`DirectGLES.cpp:15896-15912`)的清理清单里,
|
||||
也不在任何 `Invalidate*` 里(`PixelStoreImpl::InvalidatePackStateCache`
|
||||
(`Managers.cpp:11565-11567`)只动 PACK 侧的 `g_packState`)。
|
||||
- 今天为何能工作:两层巧合——(a) 本类是唯一写者且析构恒恢复到 resting default (4,0,0,0,0,0)
|
||||
(类注释 :6250-6257);(b) 新 ES context 的 GL 默认值恰好等于该 resting default,故 ES context
|
||||
销毁重建后影子「恰好仍对」。CONTRACT-P5C.md:538-539 已把它与 S1 一起记为 P6 的债,P5f §2.4 收回。
|
||||
- P5f 处置建议:把 `s_synced=false` 挂进 `DestroyEGLContext` 的清理清单(或挂
|
||||
`g_backendContextGeneration` 比较),这是「进程级静态无世代重置」的最小确定样本;f1 双块下归入
|
||||
server 块。
|
||||
|
||||
## 2. 新发现
|
||||
|
||||
### S3 后端 memo/影子大家族:键控 (client 上下文 id/serial, `g_backendContextGeneration`),自失效 —— 【C】
|
||||
|
||||
Espryt 的 per-draw memo 几乎全部自带双键:前端侧键(`ContextId`/`ContextSerial`/内容哈希)加
|
||||
`g_backendContextGeneration`(`DirectGLES.cpp:15912` 处自增)。全部成员(grep 命中的完整枚举):
|
||||
|
||||
| 成员 | 位置 | 键 / 重置 |
|
||||
|---|---|---|
|
||||
| `g_observedUnitBindings` 等 5 个 | DirectGLES.cpp:2767-2771 | (contextId, bind generation, maxUnit) 三重门,见 :2773-2782 注释 |
|
||||
| `g_unitTextureSyncListByHandle*` 7 个 | DirectGLES.cpp:3002-3008 | 记录臂:(ContextSerial, ViewsSerial, g_backendContextGeneration),:3372/:3404 |
|
||||
| `g_unitTextureSyncList*` 7 个 | DirectGLES.cpp:3010-3016 | 前端臂:(ContextId, epoch, generation),:3420/:3464 |
|
||||
| `g_fboTextureSyncList*` 8 个 | DirectGLES.cpp:3046-3066 | {Fbo, ContentHash, ContextSerial, g_backendContextGeneration},:3522/:3550 |
|
||||
| `g_fboAttachmentSyncList*` 6 个 | DirectGLES.cpp:3073-3078 | 同上,:3231/:3236 |
|
||||
| `g_readFboAttachmentSyncList*` 6 个 | DirectGLES.cpp:3082-3087 | :3249/:3254 |
|
||||
| `g_readFboTextureSyncList*` 8 个 | DirectGLES.cpp:3182-3189 | :3299/:3330 |
|
||||
| `g_imageSweep*` 7 个 | DirectGLES.cpp:4013-4024 | 记录臂三 serial + generation,:4074-4094;前端臂 :4088 |
|
||||
| `g_unitSamplerWalk*` 5 个 | DirectGLES.cpp:6627-6631 | (contextId, epoch, maxUnit, generation) + 行 memcmp,:6640/:6770 |
|
||||
| `g_resolvedTextureBindingMemos` + cursor | DirectGLES.cpp:6859-6860 | 每 program 4 槽 round-robin memo,键含 lifetime id(:6921-6940) |
|
||||
| `g_fboSyncedSerials` / `g_fboRecordsTrusted` | DirectGLES.cpp:4137 / :4146 | `InvalidateFramebufferHandleArmMemos()`(:4161-4164),挂 `InvalidateFramebufferBindingCache`(Managers.cpp:9726)→ MakeCurrent(:15367)与 DestroyEGLContext(:15903-15905) |
|
||||
| broadcast memo 7 个 | DirectGLES.cpp:5324-5350 | `PrgramImpl::InvalidateBroadcastMemo()`(:5358),挂 MakeCurrent(:15364)与 DestroyEGLContext(:15909) |
|
||||
| `g_currentDrawFrontendProgram`/`g_currentDrawBackendProgram`/`g_currentDrawProgramHandle` | DirectGLES.cpp:5300-5313 | 每次 PrepareForDraw/Compute 顶部重写(:5291-5298 注释) |
|
||||
| `g_passes`/`g_passCount`/`g_routedProgram` | DirectGLES.cpp:7779-7780 | 每次 replay 收尾清零(:7915-7924) |
|
||||
|
||||
- 今天为何能工作:双键俱备且失效方向保守(键不符即重建);`MGPipeApplierReset()` 在 context 切换
|
||||
时清空 applier 的记录表,令 `ContextSerial`/记录哈希侧的键同时移动(DirectGLES.cpp:1022-1029
|
||||
的注释即此论证)。
|
||||
- P5f 处置建议:**进程级即可,不新增机制**。spawn 后只有 server 跑 Espryt;记录臂的键全部来自
|
||||
applier 自己的 serial(APPLIER_DERIVED),不依赖 client 内存。f1 双块只需验证 client 角色下无人
|
||||
读写它们(双块会把这种访问变成具名红)。
|
||||
|
||||
### S4 纯 server-ES-context 状态(绑定影子 / scratch / 池 / 环 / EGL 句柄)—— 【B】
|
||||
|
||||
描述的是 server 自己的 ES context 或 renderer,不携带任何 client 身份。重置统一挂在
|
||||
`DestroyEGLContext()`(`DirectGLES.cpp:15896-15912`,依次调 `BufferImpl::OnBackendContextDestroyed`
|
||||
(Managers.cpp:3146)、`XfbImpl::OnBackendContextDestroyed`(DirectGLES.cpp:2067-2074)、
|
||||
`MultiDrawImpl::OnBackendContextDestroyed`(MultiDraw.cpp:1086)、`OnRestartSubstitutionContextDestroyed`、
|
||||
`ScratchFBOImpl::OnBackendContextDestroyed`(Managers.cpp:11509)、`ReleasePackedWordScratchTexture`
|
||||
(DirectGLES.cpp:14150-14155))与 `MakeCurrent()` 的 Invalidate 序列(:15363-15375):
|
||||
|
||||
- DirectGLES.cpp:`g_Display/g_Context/g_Surface/g_Config`(:14834-14837,后端唯一 ES context
|
||||
句柄)、`g_requestedSwapInterval`(:15229,per-surface,surface 重建时重放)、
|
||||
`g_backendContextOwnerThread`(:15289)、`g_syncContextGeneration`(:15295)、frame fence 环
|
||||
(:15304-15312)、`g_eglVerifiedFrameSerial/g_eglVerifiedContextGeneration`(:15415-15416)、
|
||||
resolve scratch(:9462-9467,自含世代守卫 :9488)、`ReplicateBlitImpl`(:9777-9789,守卫 :9851)、
|
||||
`DepthStencilSamplingReadImpl`(:12876-12911,守卫 :12941)、packed-word scratch
|
||||
(:14101-14103)、restart scratch(:8395-8396)、`g_writableImageBufferUnits/g_writableImageBufferUnitCount`
|
||||
(:3605-3607)与 `g_imageUnitHighWaterMark`(:3633,自恢复语义见 :3600-3604 注释)。
|
||||
- Managers.cpp:`g_backendContextGeneration`(:68)、绑定影子(:811-822、:3777-3791、
|
||||
:4790-4791、:9578-9582 `g_activeTextureUnit`/`g_boundTexturesCache`、:11520-11521 pack 影子)、
|
||||
`g_bufferContextGeneration`(:826)、deferred releases(:852-857)、buffer pool(:873-875)、
|
||||
三条 persistent ring(:1017-1039)、`g_backendBufferResources`(:2873)、`g_bufferBackendIdGeneration`
|
||||
(:3108)、`g_backendSamplerViews`(:4649)、`g_pendingFetchBaseInstance`(:4850)、alpha/integer
|
||||
mask(:9929-9930)、`g_attachmentBackendIdGeneration`(:11277)、scratch FBO(:11283-11287)、
|
||||
program 影子(:11571-11574)。
|
||||
- MultiDraw.cpp:scratch 组(:329-344)、tier 解析(:464-466)、`g_announcedTiers`(:483)。
|
||||
- BackendObject_DirectGLES.cpp:54-58:native bind 影子,注释自述「Process-wide like the native
|
||||
state it mirrors; under split exactly one DirectGLES object exists (the server's)」。
|
||||
- 处置建议:进程级即可;唯一待办是 f1 双块下确认 client 角色不触碰(EsprytSlotTablesEnabled 等
|
||||
latch 已在拆分臂下拒绝)。
|
||||
|
||||
### S5 `TwinLookupMemo` 三件套 + `g_fbSlotCache*`(legacy 臂)—— 【B】,含一条 D
|
||||
|
||||
- 证据:DirectGLES.cpp:143-152(`g_vaoTwinLookupMemo`/`g_programTwinLookupMemo`/`g_fboTwinLookupMemo`,
|
||||
仅 `MOBILEGL_PIPE_LEGACY_MEMOS` 编译)、:167-168(`g_fbSlotCacheContext`/`g_fbSlotCache`,pull 或
|
||||
legacy 臂编译)。
|
||||
- 语义:以前端对象地址为键的 memo——**per-client-context 语义**,因为键是 client 堆地址。
|
||||
- 今天为何能工作:memo 注释(:88-102)论证 registry 条目跨 ES context 存活、弱指针防地址复用;
|
||||
`g_fbSlotCache` 以 context 裸指针为键、指针比较即失效(:155-163)。
|
||||
- P5f 处置建议:拆进程后 server 进程没有 client 对象,这两条臂在 server 上根本不编译/不被选中
|
||||
(句柄臂取代);进程级即可。但留一条【D】:push-monolith 构建下它们仍在运行,若 f1 双块让
|
||||
monolith 臂与 server 臂同进程共存,这两个 memo 属于 client 角色块——f1 的归属判定要覆盖它们。
|
||||
|
||||
### S6 XfbImpl 家族:map 里存 client 对象的 SharedPtr —— 【A】
|
||||
|
||||
- 证据:`g_xfbObjects`(`DirectGLES.cpp:1548`)、`g_currentXfbName`/`g_currentXfbState`
|
||||
(:1549/:1558)、`g_scatterBufferId/g_scatterBufferSize`(:1545-1546)。`XfbObjectState` 持有
|
||||
`SharedPtr<MG_State::GLState::BufferObject> buffer`(:1511)与
|
||||
`SharedPtr<MG_State::GLState::ProgramObject> scatterProgram`(:1538)。
|
||||
- 语义:按前端 XFB 对象名键控的捕获状态——per-client-context 语义,且**值里嵌着 client 对象指针**。
|
||||
- 重置时机:`XfbImpl::OnBackendContextDestroyed()`(:2067-2074)清表——只随 server ES context
|
||||
销毁走,**不随 client context 世代走**。
|
||||
- 今天为何能工作:同地址空间 + lockstep;P5c 已把 XFB scatter 的读改到 server staged shadow,但
|
||||
这个 map 的 SharedPtr 成员本身仍在。
|
||||
- P5f 处置建议:此项同时落在 §2.6(反向通道触碰 client 内存)的 fv 包里;从静态量角度它必须
|
||||
按角色+世代分区,且成员里的 `SharedPtr` 要在 spawn 前换成句柄或 server 侧对象——与 T5 同类的
|
||||
跨角色持指针。
|
||||
|
||||
### S7 `g_rawDepthFetchSamplerState`/`g_rawDepthFetchSamplerBackend` —— 【D】
|
||||
|
||||
- 证据:DirectGLES.cpp:72-73;惰性构造于 `GetRawDepthFetchSampler()`(:301-313),**无重置点**。
|
||||
- 语义:raw-depth-fetch 替代的 server 自建 frontend SamplerObject 及其 backend twin(P4a D-F3 具名
|
||||
保留,:284-300 注释)。
|
||||
- 今天为何能工作:内容是不可变常量(构造即定型),`SyncToBackend` 每次调用重同步。
|
||||
- P5f 处置建议:跨进程后 server 自建自持有,方向成立;但 `BackendSamplerObject` 内部是否持有
|
||||
driver sampler id(若是,则 ES context 换代后失效无人清)只读未确认——**未知**,留给 fs 包顺手
|
||||
回答(一行 grep `BackendSamplerObject` 的成员即可)。
|
||||
|
||||
### S8 `g_anyProgramRoutesViewportIndex` —— 【B】,附注
|
||||
|
||||
- 证据:Managers.cpp:87;置位点 :14028(program link),**只升不降,无重置**。
|
||||
- 语义:「本进程编译过的 program 里是否有任何一个路由 viewport index」的 latch。
|
||||
- 今天为何能工作:方向保守——latch 卡在 true 只会多做 viewport routing 检查,不会错画面。
|
||||
- P5f 处置建议:进程级即可(per-server-进程的程序集合属性);不需修,仅记录它是「无重置 latch」
|
||||
家族的一员。
|
||||
|
||||
### S9 `g_bufferMutationEpoch` —— 【C】(已被角色守卫钉在 server 侧)
|
||||
|
||||
- 证据:Managers.cpp:834;bump 点全在 `Ops_*Tracked` 与 DirectGLES 的 6 处
|
||||
(DirectGLES.cpp:1654/:1679/:1828/:12828/:14393/:14808);P5e 已加
|
||||
`Fatal{RoleViolation, "CurrentBufferMutationEpoch"}` 守卫(Managers.cpp:2853)把 GL 线程 bump
|
||||
钉死(仅 bring-up 例外,:2811-2830 注释),并有 `g_bufferMutationEpochBumpsOffTheApplyThread`
|
||||
(:2803)计数供测试断言为零。
|
||||
- 语义:memo 干净探针的跳过时钟;语义上「谁的 buffer 变了」是 client 事件,但 P5e 已裁定 bump 只
|
||||
发生在 apply 线程(server 角色)。
|
||||
- P5f 处置建议:已是角色正确;进程级即可。f1 双块下它归入 server 块。
|
||||
|
||||
### S10 Magma(DirectVulkan)—— 【B】
|
||||
|
||||
- `g_rendererGeneration`(DirectVulkan.cpp:44):per-renderer 世代,renderer 重建时
|
||||
`BumpRendererGeneration()`(:51-53);server 独有。
|
||||
- `g_activeBufferManager`(Renderer/VkBufferManager.cpp:56):当前 renderer 的 manager 指针;
|
||||
server 独有。
|
||||
- `g_dynamicStateShadow`(Renderer/VulkanRenderer.cpp:409):Vulkan 动态状态影子,per-renderer。
|
||||
- `s_warnedHalfTessellatedPipeline`(PipelineFactory.cpp:528)、`s_warnedInstanceIndexUnsupported`
|
||||
(ProgramFactory.cpp:2635):log-once,进程级。
|
||||
- 注:VulkanRenderer.h:718-730 的 `s_vkResetQueryPool` 等是**类成员**(无 `static`),不在普查范围。
|
||||
- 处置建议:进程级即可。Magma 臂的 lockstep 掩盖问题属 §2.2(fm 包),静态量本身无跨角色语义。
|
||||
|
||||
### S11 MG_Impl/Pipe 的 client 侧单例群(16 个 Instance() 泄漏单例 + PipeFill 静态)—— 【B】
|
||||
|
||||
- 证据:`MG_Impl/Pipe/` 下 `CompositeResolver.h:266`、`CsoCache.h:202`、`FramebufferEmit.h:656`、
|
||||
`ImageEmit.h:194`、`PipeFill.cpp:1711`(publication latch)、`ProgramEmit.h:725`、
|
||||
`ResourceTracker.h:550`、`SamplerEmit.h:522`(cso cache)/`:679`(opaque units)/`:1082`、
|
||||
`SetHashSuppressor.h:130`、`ShaderBufferEmit.h:273`、`SlotAllocator.cpp:433`、`TextureEmit.h:1344`、
|
||||
`Tracker.h:932`、`VertexInputEmit.h:441`;`CsoCache.h:129` `s_hashForTest`(测试钩子)。
|
||||
- PipeFill.cpp 的命名空间静态:`g_lastFillWasBarriered`(:486)、`g_omission` 三件套(:509-511,
|
||||
verify 旋钮)、`g_snapshot`/`g_readScratch`(:565-566,verify 双臂)、`g_verify`(:591)、
|
||||
`g_deferredDestroy*`(:1166-1168)、`g_fillPlan`(:2822)、`g_attribDefaultRepairs`(:2942)、
|
||||
`g_attribDefaultLastHeader`(:2949)、`g_residualDue`(:3152)、verify 探针 `probe`(:2664)。
|
||||
- 语义:per-client-context/session 的发射与追踪状态。
|
||||
- 重置时机:make-current 边沿(`tracker.FreshlyPrimed()`,`PipeFill.cpp:2654-2667`;该处同时是
|
||||
`applier_reset` 记录的 producer,并做 client 侧的 CsoCache / hash suppressor / vertex-input
|
||||
emitter 重置,CONTRACT-P5C §5.1)。
|
||||
- 今天为何能工作:单 client、单 context/session(P5c 裁定每 session 恰一个 context)。
|
||||
- P5f 处置建议:进程级即可——spawn 后它们只存在于 client 进程;f1 双块时归入 client 块。多
|
||||
context 是 P6+ 的形状,`MGPApplierReset::ContextSerial` 的注释(CONTRACT-P5C §1)已写明。
|
||||
|
||||
### S12 `MG_Impl/GLImpl` 的 query/sync 注册表 —— 【B】,含一条多 context 语义债
|
||||
|
||||
- 证据:`GL_Query.cpp:55-66`(`g_queryObjectsMutex`/`g_liveQueryObjects`/`g_nextQueryId` 与四个
|
||||
active-query id + pipeline-statistics map)、`GL_Sync.cpp:34-37`(`g_syncObjectsMutex`/
|
||||
`g_liveSyncObjects`)。
|
||||
- 语义:GL 对象(query/sync)按规范是 per-context 的,这里存成进程级注册表。
|
||||
- 今天为何能工作:单 context/单 session;mutex 防多 JVM 线程迁移(:50-54 注释)。
|
||||
- P5f 处置建议:拆进程后天然在 client 进程,进程级即可;但「语义 per-context、存储 per-process」
|
||||
这一事实记入 P6+ 多 context 债(与 S11 同一形状)。
|
||||
|
||||
### S13 MG_Remote 会话单例与跨角色标量 —— 混合
|
||||
|
||||
- `ClientSession::g_active`(Client/ClientSession.cpp:48)、`ServerSession::g_active`
|
||||
(Server/ServerSession.cpp:349)、`ServerLoop` 单例(Server/ServerLoop.cpp:774):每进程一个
|
||||
session 是设计本身——【B】。
|
||||
- `g_applyThreadInsideApplier`(Client/ClientSession.cpp:118):**inproc 下 server 写、client 读的
|
||||
跨角色共享原子**,语义 per-session。CONTRACT-P5C §6(A1)已具名其为 barrier 簿记。拆进程后
|
||||
client 那份恒 false,但它是 wait-rule 机制的一部分而非状态内容——【A】:f1 双块/角色分区时必须
|
||||
按角色拆开,否则会漏掉「client 读自己那份永远 false」的假绿。同址的 `thread_local
|
||||
g_inBarrierWait`(:117)是 client 线程本地,【B】。
|
||||
- `WireTables.cpp:91-104`(`g_emitted`/`g_declined`/`g_clientTablesUninstalled`)、
|
||||
`WireTables.cpp:642`(`g_applierResetContextSerial`,per-session 计数器,client 侧)、
|
||||
`EmitTables.cpp:91-96`(drop 旋钮与 `g_presentOrdinal`/`g_publishedMaxRecordBytes`)、
|
||||
`EmitTables.cpp:1543`(`g_fenceMutex`)、`EmitTables.cpp:711`(发射 scratch)、
|
||||
`CapsMirror.cpp:34-40`(hook + 计数器)、`CapsMirror.cpp:169`(单例)、
|
||||
`GpuWritePending.cpp:171`(producer marks)、`PersistentMapTracker.cpp:85-136`(SIGSEGV 追踪):
|
||||
全部 client 侧,【B】。
|
||||
- `PipeWireCodec.cpp:347` `g_processResolverTable`:本进程段表指针,每进程各一份即正确,【B】;
|
||||
同文件 :355 的 `t_activeDecoder` 是 thread_local,已是 per-thread。
|
||||
- `ServerLoop.h:126` `g_applyThreadKey`:server 线程身份戳,【B】。
|
||||
- `StagedShadowStore` 单例(Managers.cpp:1160-1165):server 侧暂存影子,注释自述「ONE PER
|
||||
PROCESS」,【B】。
|
||||
- Transport 的 `g_spinItersPerUs`(Doorbell.cpp:37)、`g_shmCounter`(ShmSegmentPosix.cpp:47)、
|
||||
`g_sectionCounter`(ShmSegmentWin32.cpp:36):进程级校准/命名计数器,【B】。
|
||||
|
||||
### S14 MG_Pipe 核心 —— 已知项定位
|
||||
|
||||
- `gPipeInputs`(MG_Backend/MGPipe/PipeInputs.h:835):P5f §1 的主对象,双块机制(§4)就是为它造
|
||||
的;不重述。
|
||||
- `g_applier`(MG_Pipe/PipeApply.cpp:433):server-private(CONTRACT-P5C §5.1 裁定),重置经
|
||||
`applier_reset` 记录(opcode 77,`MGPipeApplierReset()`,`PipeApply.cpp:1202-1300`)——【C】
|
||||
已是 per-session。
|
||||
- `PipeRoute.cpp:63-69`(`g_arm`/`g_monolithScreen`/`g_monolithContext`/`g_monolithEscapes`):
|
||||
路由表与配置,【B】。
|
||||
- `PipeInputs.cpp:55` `g_residualPulls`:诊断计数器(rsp),server 侧读,【B】。
|
||||
|
||||
### S15 MG_State —— 【B】
|
||||
|
||||
- 七个 lifetime-id 分配器(BufferObject.cpp:31、RenderbufferObject.cpp:25、
|
||||
FramebufferObject.cpp:20、ProgramObject.cpp:27、SamplerObject.cpp:20、TextureObject.cpp:26、
|
||||
VertexArrayObject.cpp:18)与 `s_nextTextureStateContextId`(TextureState.cpp:24):client 侧单调
|
||||
分配器;spawn 后只存在于 client 进程,句柄经记录过线,进程级即可。
|
||||
- `g_stateObjectDeathOps`(StateObjectDeathNotice.h:52)、`g_bufferBackendOps`
|
||||
(BufferObject.cpp:79):进程级函数表,【B】。
|
||||
- `g_textureLegacyArmScopeDepth`(MipmapStorage.cpp:112)、`g_magmaP7AllocatorDebtScopeDepth`
|
||||
(SlotAllocator.cpp:118)、`g_frontendKeyedRegistryScopeDepth`(SlotAllocator.cpp:143):P5c 具名
|
||||
豁免 scope 的深度计数,守卫簿记而非状态,【B】。
|
||||
- `ProgramSpirvTask.cpp:35-36`:log-once atomic_flag,【B】。
|
||||
- `MG_Impl/NSOpenGLImpl/NSOpenGLImpl.cpp:32-33`:macOS IMP 缓存,与拆分无关,【B】。
|
||||
|
||||
## 3. 汇总
|
||||
|
||||
| 分类 | 条目 |
|
||||
|---|---|
|
||||
| 【A】P5f 必须按角色+世代分区 | S1(g_syncedRenderStateParameters 家族)、S2(s_synced 家族)、S6(XfbImpl)、S13 中的 `g_applyThreadInsideApplier` |
|
||||
| 【B】进程级即可 | S4、S5(附一条 D)、S8、S10、S11、S12(附多 context 债)、S13 大部、S14 的 PipeRoute/rsp、S15 |
|
||||
| 【C】已是 per-context/per-session | S3(自失效双键)、S9(角色守卫已钉)、S14 的 `g_applier` |
|
||||
| 【D】未知 | S7(BackendSamplerObject 是否持 driver id);S1 的「ES context 换代且 tuple 不变时 MakeCurrent 是否仍被调用」 |
|
||||
|
||||
**最重要的三条结论**:
|
||||
|
||||
1. **两个已知成员里,`ScopedDefaultUnpackState::s_synced` 比 `g_syncedRenderStateParameters` 弱得多**:
|
||||
前者没有任何重置点(`Managers.cpp:6319` 一次置位永不清,不在 `DestroyEGLContext` 清单里),正确性
|
||||
靠「新 ES context 的 GL 默认值恰好等于影子值」的巧合;后者有 `MakeCurrent()`(DirectGLES.cpp:15375)
|
||||
的每次强制全量重推兜底,但兜底链条上「tuple 相同而 ES context 换代」一格只读无法确认(D 项)。
|
||||
2. **Espryt 的 per-draw memo 大家族(S3)已经自带 (client 键, `g_backendContextGeneration`) 双键
|
||||
自失效,是本普查中最大的一类,且全部不需要新机制**——f1 双块对它们只需证明 client 角色不触碰。
|
||||
P5f §2.4 的「完整清单未知」可以收口为:真正缺世代钟的只有 S2、S6、S7 三个小点。
|
||||
3. **S6(`g_xfbObjects` 持有 client 对象 `SharedPtr`,DirectGLES.cpp:1511/:1538/:1548)是本次普查
|
||||
新发现的、唯一一个「进程级 map 的值里嵌 client 指针」的成员**,重置只挂 server ES context 销毁
|
||||
(:2067-2074),同时属于 §2.6 fv 包的范畴——两个包的清单里都应有它。
|
||||
|
||||
### 附:普查方法的可复现性
|
||||
|
||||
```
|
||||
grep -rn -E '^\s*(inline\s+)?(static\s+)?[A-Za-z_][A-Za-z0-9_:<>,\s\*&]*[ \*&](g_|s_)[A-Za-z_0-9]+\s*(=[^=]|\[|\{|;)' \
|
||||
MG_Backend MG_Impl MG_Pipe MG_Remote MG_State --include='*.cpp' --include='*.h'
|
||||
# 加上 '= *new ' / 'Instance()' 单例模式的第二轮 grep;全部命中均已人工过目并归入上表。
|
||||
# 3rdparty/ 子模块不在范围内(其 git 报错为既有噪音)。
|
||||
```
|
||||
Reference in New Issue
Block a user