mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-11-07 01:53:24 +09:00
[Docs] (Disaggregated): the top-level files become short indexes in plain language; the design text moves to design/, the how-to and reference material to guide/
- README, ARCHITECTURE, ROADMAP, MEASUREMENTS and CURRENT_STAGE_PROGRESS are rewritten as indexes (84 KB -> 15 KB): what the project is, where it stands, and where each detail lives. ROADMAP is one plain sentence per phase; MEASUREMENTS is the five conclusions that matter plus one line per phase; CURRENT_STAGE_PROGRESS is the P12 goal, a checklist and the next steps. - ARCHITECTURE's body moves unchanged into ten design/ files, keeping §1-§17 and appendices A/B, so `ARCHITECTURE.md §N` references in code resolve through the table in ARCHITECTURE.md. - guide/ holds build-and-run, code map, glossary and the engineering discipline (from README and ROADMAP), and the device pinning record that used to sit in devices/. - The open-debt table moves to notes/DEBTS.md; the detailed current-stage tables (with decision IDs, the open line items and evidence locations) move into notes/p12/README.md. notes/README.md maps every moved path and section. - The documentation citation lint in test.yml also covers design/ and guide/, which describe the current tree as the top-level files did.
This commit is contained in:
@@ -3702,7 +3702,7 @@ jobs:
|
||||
- name: Documentation citation lint
|
||||
run: |
|
||||
shopt -s nullglob
|
||||
documents=(docs/Disaggregated/*.md)
|
||||
documents=(docs/Disaggregated/*.md docs/Disaggregated/design/*.md docs/Disaggregated/guide/*.md)
|
||||
if [ ${#documents[@]} -eq 0 ]; then
|
||||
echo "no disaggregation documents to check"
|
||||
exit 0
|
||||
|
||||
@@ -1,458 +1,35 @@
|
||||
# MGPipe 设计与架构
|
||||
# 设计与架构(索引)
|
||||
|
||||
> 只写**已决定**的设计,每条附一句理由。章节编号稳定(代码注释按 `ARCHITECTURE.md §N` 引用它们)。阶段号与状态见 [`ROADMAP.md`](ROADMAP.md),实测见 [`MEASUREMENTS.md`](MEASUREMENTS.md);各阶段的落地细节、偏差与当时的论证在 `notes/<阶段>/README.md`(§17 是索引)。wire 契约原文在 `MobileGL/MG_Remote/CONTRACT-*.md`,与本文冲突时以更新的契约为准。
|
||||
> 这一页只有设计要点和章节去向;每一部分的完整设计在 [`design/`](design/)。章节编号 §1–§17、附 A / B 沿用至今,代码注释里写的 `ARCHITECTURE.md §N` 按下表找到对应文件。wire 契约原文在 `MobileGL/MG_Remote/CONTRACT-*.md`,与设计文字冲突时以更新的契约为准。
|
||||
|
||||
## 1. 边界
|
||||
## 一句话
|
||||
|
||||
### 1.1 一句话
|
||||
后端本来就维护着一台贴着 GLES / Vulkan 的状态机,缺的只是"我被告知了什么"。MGPipe 让前端在每条做事的命令之前,把状态变化以**记录**的形式推给后端;后端只认这些记录、不读前端内存,所以两者之间可以隔一个线程、一个进程或一根网线。
|
||||
|
||||
`MG_Backend` 本来就是一台贴着目标 API 的状态机(Espryt = DirectGLES,Magma = DirectVulkan),缺的是一份"我被告知了什么"的显式声明。MGPipe 就是这份声明:前端在每条 verb 之前把变化**推**过去,后端不再拉 `MG_State::pGLContext`。接口从两个后端自己维护的关键结构反推(`SetupDrawSnapshot`、`DrawTextureSyncKeys`、`ResolvedVertexBindings`、`g_syncedRenderStateParameters`、`BufferBackendOps` …);gallium 是词汇的目的地,不是推导前提。
|
||||
## 设计要点
|
||||
|
||||
### 1.2 两张函数指针表
|
||||
1. **一份调用目录生成一切**:`PipeCalls.def` 一行一个调用,线上编号就是行序、只追加;两张函数指针表、编解码、校验器都由它生成。
|
||||
2. **对象用句柄 `{slot, gen}` 寻址**:由前端分配,后端从不回传,所以创建对象不需要往返。
|
||||
3. **状态在命令之前一次性推送**,不是每个 GL setter 都推;整块数据过线、按内容去重,没变就不发。
|
||||
4. **同一份后端实现,三种拓扑**:单线程直调 / 同进程两线程 / 两个进程(可跨机)。
|
||||
5. **反向消息是具名回调**(错误、GPU 写回、窗口尺寸……),走一条与命令同样有序的事件通道。
|
||||
6. **着色器以 SPIR-V + 反射信息过线**:编译在前端,转译与按状态特化在后端。
|
||||
7. **传输分两层**:控制面(握手、EGL 控制)与数据面(命令、批量数据),各自可选本机共享内存或字节流 / TCP,可以混搭;上层代码不区分链路种类。
|
||||
8. **client 发完就走(run-ahead)**:只有需要答案的命令才等;靠 present 配额限速。
|
||||
9. **出错不崩**:server 崩溃或断线时 client 进入"设备丢失"状态;外来连接要令牌,畸形输入被拒绝而不是让 server 退出。
|
||||
10. **可证明的迁移**:每个门都必须能变红;单进程构建的二进制在整个拆分过程中逐字节不变(G1)。
|
||||
|
||||
`MGPipeScreen`(share-group 作用域:caps、resource、persistent map、fence)与 `MGPipeContext`(其余全部),由 `PipeCalls.def` 生成。
|
||||
## 章节去向
|
||||
|
||||
- 函数指针 struct 而非虚基类:null 项已表示"未实现,前端回退",`MG_Test` 整表替换即可 mock。
|
||||
- 两张表从第一天分开,事后拆会给记录重新编号;v1 只有一个 screen、一个 context、一条 flow。
|
||||
- EGL 生命周期与 caps 面留在 `pActiveBackendObject` 的虚函数上(罕见路径,走控制面)。
|
||||
|
||||
### 1.3 三种形态,一份后端
|
||||
|
||||
| 形态 | 表里装的是什么 | 用途 |
|
||||
| § | 内容 | 文件 |
|
||||
|---|---|---|
|
||||
| `monolith`(默认) | 后端自己的函数,回调直调 `MG_State` | 出货 |
|
||||
| `inproc` | 发射器 → 同进程第二个线程上的 applier | CI 主验收;也就是 monolith 的**渲染线程** |
|
||||
| `spawn` | 发射器 → 数据面 → 另一个进程的 applier → 同一批后端函数 | 两进程形态;控制面可以是 `fork` / `unix:` / `tcp://`(§11.9),server 可在另一台机器上 |
|
||||
|
||||
唯一 hook 点是 `MG_Backend::Init()` 里一个 `#if MOBILEGL_BUILD_DISAGGREGATED` 分支:`MG_Config::Transport != Monolith` 时装 `MG_Remote::BackendObject_Remote`。`MG_Impl` 的边界调用点零 `#ifdef`。
|
||||
|
||||
## 2. 对象模型
|
||||
|
||||
### 2.1 句柄 = `{slot, gen}`(`MG_Pipe/MGPipeHandles.h`)
|
||||
|
||||
- 8 字节 POD;**client 铸造,server 永不返回句柄** → 整份目录零创建 round trip(对 gallium 的偏离 D1)。
|
||||
- slot 按 kind 稠密分配(free list + 高水位),server 对象表是数组;`gen` 只在 slot 复用时 ++。
|
||||
- kind:`Buffer, Texture, Renderbuffer, Framebuffer, Xfb, RenderStateCso, VertexElementsCso, SamplerCso, SamplerViewCso, ShaderCso, Fence, Query, Context`。保留:`{0,0}` = null;`Framebuffer {0,1}` = 默认帧缓冲;`ShaderCso` 高段给 program pipeline 合成体。
|
||||
- GL name 只作诊断(`GlNameForDiag`),永不做身份、memo 键或 content hash。
|
||||
|
||||
### 2.2 两种世代,严格分开
|
||||
|
||||
| | 拥有者 | 回答 | 过线 |
|
||||
|---|---|---|---|
|
||||
| `MGPipeHandle::Gen` | client | "还是同一个 GL 对象吗?" | 是 |
|
||||
| `MGGen`(后端纪元,如 `g_bufferMutationEpoch`) | server | "我自己是否重铸了驱动对象?" | **永不**(只以纹理拉取请求的形式出现,§8.4) |
|
||||
|
||||
client 的回绕 `Uint16` 版本计数器永远不是新鲜度的唯一证明:过线时要么加宽,要么与 `{slot, gen}` 同行。
|
||||
|
||||
### 2.3 CSO 与可变对象
|
||||
|
||||
- CSO(render state、vertex elements、sampler、sampler view、shader)在 **client 侧内容寻址**(Mesa `cso_cache` 先例),LRU 淘汰时发 `delete_*`;render-state 容量 64(P13 重调)、sampler state 256。
|
||||
- Buffer / Texture / Renderbuffer 走 create / respecify / subdata / destroy;Framebuffer / Xfb 是 per-context 身份 + `set_*` payload。
|
||||
- 两条偏离:**D-G1** Espryt 的 vertex-elements 按 VAO 身份寻址(twin 持驱动 VAO 名,不能共享);**D-F2** Espryt 的 sampler view 按纹理身份寻址,sampler state 内容寻址且带引用计数(被 `BuiltinSampler` 指着的项不许被 LRU 挤掉)。
|
||||
|
||||
## 3. 调用目录
|
||||
|
||||
### 3.1 单一真相源
|
||||
|
||||
`MobileGL/MG_Pipe/PipeCalls.def`:一行一个 `X(Name, Payload, Class, Flags, WaitClass)`。**线上 opcode 就是行序**,只能追加、退役保留槽位;行数由 `MGP_CALL_LIST_DOCUMENTED_COUNT`(今天 81)与 `PipeCatalogueTest` 钉住。生成器产物提交进树,CI `pipe-gates` 重生成并 `git diff --exit-code`:
|
||||
|
||||
| | 产物 | 内容 |
|
||||
|---|---|---|
|
||||
| G1 | `PipeTables.inc` | 两张函数指针表 |
|
||||
| G2 | `PipeThunks.inc` | monolith 直调 thunk |
|
||||
| G3 | `PipeWire.inc` | wire 记录、尺寸 `static_assert`、applier 边界检查 |
|
||||
| G4 | `PipeVerify.inc` | `MOBILEGL_PIPE_VERIFY` 逐字段比对器(`PipeFields.def`) |
|
||||
| G5 | `PipeFilled.inc` | `PipeInputs` 字段 id 与逐 verb 世代 poison |
|
||||
| G6 | `PipeCoverage.inc` | 后端读点 → 调用映射(`Coverage.def`),0 UNMAPPED 是门 |
|
||||
| G7 | `PipeSpanTable.inc` | render-state pipeline 子集成员表 |
|
||||
| G8 | `PipeFieldOwnership.inc` | 每个 `PipeInputs` 字段恰属 `RECORD-SUPPLIED / APPLIER-DERIVED / BARRIER-PULLED / FATAL` 之一(P5f 起 0 BARRIER-PULLED) |
|
||||
|
||||
另有 `FillPoints.def`(填充点)与 `DirtySurface.def`(§5.2)。G1 codegen 规则:capability gate 替换指针表达式时保留**表达式形状**,否则 pull `.text` 漂移。
|
||||
|
||||
### 3.2 分组与 flag
|
||||
|
||||
- Class:`kScreen`(caps、resource、persistent map、fence、`ApplierReset`)、`kCtxQuery`、`kCtxCso`(create/bind/delete × 五种 CSO)、`kCtxState`(`Set*` 状态、`SetContextValues`、`SetProgramBindings`、迁移期的 `SetResidualValueState`)、`kCtxObject`(subdata、readback、mip、`ObjectDeath` …)、`kCtxVerb`(blit、clear、`DrawVbo`、dispatch、XFB、`Present` …)。
|
||||
- Flags:`kNeedsAck`、`kHasBlob`、`kVarTail`、`kHostSpan`、`kReplySlot`、`kOptional`。第五列 `WaitClass`(P5e)决定 run-ahead 下 client 等不等(§11.6)。
|
||||
- 20 个 draw 入口塌成 `DrawVbo` 一条;`Clear` 一条判别式;sampler 集合**没有 stage 维度**(192 单元合并空间);`SetTextureParams` 按资源寻址、与 sampler view 分开(D10);`SetIndexBuffer` 独立于 VAO 配置(D5)。
|
||||
- 显式不移植:`GetIntegeri_v` / `GetInteger64i_v` / `GetProgramiv`、`set_pixel_unpack_state`(前端已解析)、压缩格式概念、`pipe_transfer`。
|
||||
|
||||
### 3.3 能力位(`MGPCapBit`)
|
||||
|
||||
`CallMask` 取代"槽位是否为 null"这一隐式能力探测;`MGPCaps` = `DynamicBackendParameters` + `CallMask` + 格式能力表与 renderer 字符串两个 blob,握手后快照、`MakeCurrent` / `InitCapabilities` 后按 generation 重发布。位包括 viewport array、fp64 顶点、`kCapResidentSubData`、XFB / query 家族、`kCapNeedsHostIndexBytes`、`kCapNeedsHostUboBytes`、`kCapBackendOwnsXfbCapture`、`kCapRunAheadApply`(`MGPipeRunAheadCapBitsFor`:Espryt 自 P5e、Magma 自 P5f 之后的 Magma run-ahead 起发布,后者契约 `MG_Remote/CONTRACT-MAGMA-RUNAHEAD.md`)。**不存在"归属开关"(D-B7)**:multi-draw 分档与 restart 重写永远由 server 拥有,client 只在 caps 要求时提供索引字节。
|
||||
|
||||
## 4. 记录与 payload 约定(`MG_Pipe/MGPipeTypes.h`)
|
||||
|
||||
- 每个 payload 是平坦 POD、显式 padding、`static_assert` 精确尺寸;**永不含指针**。
|
||||
- `MGPBlobRef{Offset, Size, Seg}` 指向 blob 区;`MGHostSpan` 是唯一随传输而变的形状(monolith 下是指针,split 下字节在 `SEG_STAGE`),只进变长尾。
|
||||
- wire 记录头 `MGPWireRecHeader{Op, Flags, Size}`(8 B);**没有逐记录序号**,seq 就是记录序数。
|
||||
- 单条记录上界 = ring 一半,**记录本身永不分块**(R-10);**内容侧分块**:会超出 `SEG_STAGE` 的 blob 按 `MGPipeStageChunkBytes()`(默认 segment/4 = 8 MiB)切成多条记录(buffer 范围走查、纹理整宽 slab,server 拼回整级)。
|
||||
|
||||
### 4.1 关键 payload
|
||||
|
||||
| payload | 要点 |
|
||||
|---|---|
|
||||
| `MGPResourceDesc` | buffer / 纹理 / renderbuffer 一个判别式 create/respecify 形状;`ImageBindableHint`、`ViewOf`(视图的存储属主)、`HasDefinedContent` |
|
||||
| `MGPRenderStateDesc` / `MGPBindRenderState` / `MGPDynamicState` | §5.3 |
|
||||
| `MGPVertexElements` | blob 同时带解析后的属性与绑定点视图(记录自洽) |
|
||||
| `MGPSamplerView` / `MGPTextureParams` | view 只带视图限制;纹理参数挂纹理对象,`BuiltinSampler` 句柄不许为空(D-E1) |
|
||||
| `MGPProgramDesc` | 逐 stage SPIR-V + 反射归档(§7) |
|
||||
| `MGPFramebufferState` | 8 color + depth + stencil + client 解析后的 read surface;`ContentHash` 同时是 render-pass memo 键与发射抑制器;`Target` 含 `Named = 3`,applier 按 framebuffer 句柄存 |
|
||||
| `MGPSubData` / `MGPSubRegion` | §6 |
|
||||
| `MGPDrawInfo` / `MGPDrawRange` | `DrawVbo`;multi-draw 范围与用户索引在变长尾,indirect 的 `DrawCount` 由 client 解析 |
|
||||
|
||||
每条 `kVarTail` 的 `set_*` 带 `ContentHash`,hash 未变就不发(§5.4)。
|
||||
|
||||
## 5. 前端 state tracker(`MG_Impl/Pipe/Tracker`)
|
||||
|
||||
### 5.1 推送发生在 verb 之前的 validate 时刻,不在 GL setter 里
|
||||
|
||||
per-setter 推送会把 Blaze3D 每个 batch 的冗余 `glEnable/glDisable` 变成接口调用,严格慢于今天;正确形态是 gallium `st_validate_state`。落地为唯一的 `MGPipeValidateForVerb(MGPipeVerb)`(九个 validate 类,`FillPoints.def`),恰在每次经表调用之前、每个提前返回之后。只有今天就在 GL 调用时刻分发的资源 op(`BufferBackendOps` 的七个 hook)在调用时刻推送。
|
||||
|
||||
### 5.2 dirty 位:值类零新增记账,对象类 5 个聚合世代
|
||||
|
||||
- 值类组(render state、pixel pack、patch、attrib defaults、shader bindings …)直接复用既有版本计数器作快门。
|
||||
- 对象类组(vertex buffers、framebuffer、sampler views / samplers / images、const / shader buffers、SO targets)靠 5 个**聚合世代**把快门降成一次 `Uint64` 比较——没有聚合就回答不了"有没有哪张已绑定纹理动了"。
|
||||
- 完整性由 `scripts/gen_pipe_dirty_surface.py` 保证(每个 mutator → 必须 bump 的世代,`--check` + `--self-test` 进 CI)。**快门看不见自己的主体**是踩过三次的坑:新增字段必须带"记录字段 → setter → 快门"一行,优先混入已有世代(新计数器会撑大 pull 对象,G1 不允许)。
|
||||
|
||||
### 5.3 渲染状态:整块 blob 过线,身份只取 pipeline 子集(D-B1)
|
||||
|
||||
```
|
||||
create_render_state(cso, pipelineSubsetChunks) // 只带 pipeline 子集
|
||||
bind_render_state(cso, version, pipelineVersion) // 稳态 12 B
|
||||
set_dynamic_state(dynamicChunks, version) // 只带变化的动态 chunk
|
||||
```
|
||||
|
||||
- `RenderStateParameters`(1168 B)整块过线:Espryt 做三段 memcmp,字段顺序承重;拆成多个 CSO 要手工维护 ~150 字段划分且无绊线。
|
||||
- 身份只取 pipeline 子集,否则 `glViewport` / `glScissor` 每次都铸新 CSO、冲掉 server 的 pipeline memo。划分只写在一处:`MGPipeRenderStateSpans` 的 chunk 表(7 pipeline chunk 396 B + 8 dynamic chunk 772 B,`static_assert` 完整性),G7 断言"子集 hash 变 ⟺ `m_pipelineStateVersion` 变"。
|
||||
- Espryt 的 `SyncRenderState` 一行不动;Magma 的 pipeline memo 键是 `cso.slot`。
|
||||
|
||||
### 5.4 验证不变式、合并与抑制器
|
||||
|
||||
规范(D-B3):**一条 verb 的全部 `set_*` / `bind_*` 在该 verb 之前完成;server 在 verb 处从已推送状态惰性特化 shader 与 pipeline。**除"资源 create 先于对它的 bind"外,`set_*` 之间无顺序要求。合并:整块优于逐字段;高水位直接是 `count` 实参;只发 program 解析过的集合;**集合 hash 抑制器**在 client 侧去抖(原先在后端的 ~175 行)。
|
||||
|
||||
### 5.5 sampler view 在 client 侧解析
|
||||
|
||||
shader 看见哪个纹理取决于 sampler uniform 类型、mipmap 完备性与未定义默认纹理;解析留在 client(带 memo),gallium "每槽一个 view" 就是解析后的形态。两处后端特定后处理留在 server:Espryt 的 raw-depth-fetch sampler 替换、Magma 的 feedback-loop 检测。
|
||||
|
||||
### 5.6 生命周期、共享组、composite program
|
||||
|
||||
- `resource_create` 在前端对象构造时发,存储由 `resource_respecify` 惰性定义,`resource_destroy` 在析构时发;死亡转发给每个 emitter(只释放 slot 曾导致 UAF)。view 先于存储属主销毁、FBO attachment 钉住纹理、buffer texture 钉住 buffer。
|
||||
- program pipeline 合成体(`CompositeResolver`)完全在前端合成、推一个句柄;释放恰好一次(resolver 换掉或 `~ProgramObject`,D-H7);记忆按 `(ContextId, 管线 GL 名)` 键控。
|
||||
|
||||
### 5.7 emulation 的归属
|
||||
|
||||
规则:**驱动表达不了的变换在 tracker 里 lowering,硬件 / 驱动强加的变换在 driver 里 lowering。**
|
||||
|
||||
| 归 client(读前端字节的纯 CPU 变换) | 归 server |
|
||||
|---|---|
|
||||
| client 顶点 / 索引数组(字节过线,永不是指针)、最大索引扫描、`*IndirectCount` 计数解析、CopyImage shadow 镜像、XFB CPU 图元计数、压缩纹理 / unpack 规整 | primitive-restart 重写、multi-draw 分档与展平(从索引宿主镜像读,§10.3)、viewport-array 回放、fp64 顶点窄化、image-bindable 存储加宽、生成 mipmap(client 分配 level 存储) |
|
||||
|
||||
**陈旧索引纪律是逐站点表**:client 侧扫描前的 reconcile 必须逐字复现 monolith 的集合(最大索引扫描 = `SyncPersistentMappedRange()` + `SyncGpuWrites()`;`*IndirectCount` 只前者)。
|
||||
|
||||
## 6. 纹理 subdata 与 dirty 归属
|
||||
|
||||
- 纹理上传由 Espryt 在 sync 时刻按**累积**区域做(Mali 按作业数计价,~100 个 rect 对一个 union box 实测 +6 ms/帧),逐 `glTexSubImage` 发记录会复现这个悬崖。
|
||||
- 因此 client 在自己的 rect 模型里累积,在下一个 validate / flush 点发**一条** `ResourceSubData`,同时携带 union box 与 region 列表,**由 server 选上传形状**(决策留在付 GPU 代价的一侧)。步长显式携带,不再靠指针比较。
|
||||
- **dirty 归属反转**:client 维护发射游标、发射后清自己的标志;清 dirty 要等 applier 的 acceptance(被拒的 level 留在脏表里);server 自己的重新变脏是 server 的事。
|
||||
- `TextureUploadShapeScenario` 把上传形状录成金标(P3b/P4b D2 起是门;Mali 帧时增量无设备,按 Adreno 记录)。
|
||||
|
||||
## 7. Shader state = SPIR-V + 反射归档
|
||||
|
||||
- `CreateShaderState` 过线的是逐 stage SPIR-V + 反射归档,**不是源码**:glslang 全在 client、SPIRV-Cross 全在 server,文件级切割;server 侧无编译池。归档序列化器 `ProgramArtifactsCodec`,monolith 只在 verify 构建里调它(D-H3)。
|
||||
- server 在 verb 时刻从已推送状态**惰性特化**(D-B2:draw FBO clamp mask、fragColor 广播数、storage-block 绑定签名、image 格式、patch 参数等)——正是两个后端原来的做法。
|
||||
- link / compile 失败不需要同步返回:`GL_LINK_STATUS` 由 client 从 `ProgramObject` 回答,后端失败以 `OnLog` ≥ERROR 无损呈现。
|
||||
- 内部 shader(颜色 blit、深度 mip、multisample resolve)烘焙成签进树的 SPIR-V(`Wire*Spirv.h`),新鲜度门 `MOBILEGL_BAKED_INTERNAL_SHADERS`(`MG_Test/SelfTest/BakedInternalShadersTest.cpp`)重跑树内 glslang 逐字节比对(P7)。
|
||||
|
||||
## 8. 反向通道
|
||||
|
||||
### 8.1 `MGPipeCallbacks`(`MG_Pipe/MGPipeCallbacks.h`)
|
||||
|
||||
九个具名回调 + 一个正向终止符(`ResourceSubDataComplete`),取代后端直接 poke 前端对象的 95 个调用点(具名化是有意偏离 D8)。monolith 下直调,split 下是 `SEG_EVENT` 上的记录。
|
||||
|
||||
| 回调 | 作用 |
|
||||
|---|---|
|
||||
| `OnGlError` | 延迟的 GL 错误;**必须对命令流有序** |
|
||||
| `OnGpuWritten` | GPU 写过的 buffer 范围(收窄 client 保守自建的 pending 集) |
|
||||
| `OnBufferWriteback` | PBO 回读、XFB 捕获结果;必须与 epoch bump 有序 |
|
||||
| `OnTextureWriteback` | CPU 回退生成 mip 的纹素 |
|
||||
| `OnTexturePullRequest` | §8.4 |
|
||||
| `OnMipLevelsGenerated` | 只带形状 |
|
||||
| `OnSurfaceChanged` | 默认帧缓冲的格式与尺寸(server 拥有显示时 client 靠它得知窗口尺寸) |
|
||||
| `OnCapsInvalidated` | 取代 `InvalidateCompileEnv` |
|
||||
| `OnLog` | ≤WARN 有损,≥ERROR 无损 + 速率限制 |
|
||||
|
||||
后端凭空造的前端对象(Magma 占位纹理、swapchain 默认 FB 占位)改为 server 原生;pull 构建里的 `m_backend` 类成员真删会动 `sizeof`(G1),随 P13 退役(D-K)。
|
||||
|
||||
### 8.2 有序性是正确性要求
|
||||
|
||||
每次 writeback 之后的 epoch bump 必须在任何后续读该 handle 的命令之前被 server 应用。**反向通道需要与正向通道相同的有序保证。**
|
||||
|
||||
### 8.3 错误、ack 与日志
|
||||
|
||||
- **唯一允许同步 ack 的入口是 `glBufferStorage`**:`ResourceRespecify` 的 `kNeedsAck` 由逐记录谓词收窄到"不可变 **buffer**"(D-A2),纹理 OOM 在 monolith 里本来就推迟到 sync 时刻。respecify 带逐 level 作用域与"只换元数据"的形式,不误丢已接受的待上传。
|
||||
- 其余错误一律晚到,走有序的 `OnGlError`。
|
||||
|
||||
### 8.4 唯一的新停顿类:server 发起的纹理重铸拉取(D-B6)
|
||||
|
||||
server 不保留纹素;image-bindable 重铸、整格式再生、view 源重铸会要求重发 level。缓解:`ImageBindableHint` 预防主因;拉取异步(阻塞 apply 线程,不阻塞应用线程);保留 LRU 默认关(`MOBILEGL_PIPE_TEXEL_RETAIN_MB=0`);显式终止符可带零个 region。真实语料上发生率可忽略(780 个统计窗口 2 次);split 下这条路径今天仍是具名 `Fatal{UnmigratedEmulation, "texture-remint-pull"}`,归 P9。
|
||||
|
||||
### 8.5 XFB scatter 留在 server
|
||||
|
||||
补丁循环需要"捕获前的字节",而拆分后权威影子归 server(`StagedShadow`),所以 scatter 在 server 原地跑完;回程只有一条 `OnBufferWriteback`,载补好的整段,按 `SEG_EVENT` 容量的四分之一切片(`MGPipeBufferWritebackSliceBytes`),最后一片落地即蕴含之前每一片。孤儿目标(`HasDefinedContent == 0`)零填充后照常散射。`OnXfbScatterReady` 已删除(回调 10 → 9)。落地论证见 [`notes/p34b/README.md`](notes/p34b/README.md)(espryt D1)。
|
||||
|
||||
## 9. 后端状态机改造
|
||||
|
||||
### 9.1 原样不动的东西
|
||||
|
||||
Espryt 的 persistent ring、buffer pool、fallback-repack、scratch FBO、驱动绑定影子、Adreno / Mali workaround、SPIRV-Cross 会话、restart 重写与 multi-draw 分档;Magma 的 `VulkanRenderer` memo、`PipelineFactory`、`ProgramFactory`、`UniformManager`、五个 `Vk*Manager`、`SwapchainObject`、D18 的节点式容器纪律。"输入变了、算法不许动"的那一类由 **G5 字节一致门**把关(`scripts/p3a_untouched_regions.sh` 十一函数、`scripts/p4a_untouched_regions.sh` 17 区),re-pin 必须两份同改。
|
||||
|
||||
### 9.2 strangler 脚手架:`PipeInputs` + 逐 verb 填充 + poison 世代(P1)
|
||||
|
||||
```cpp
|
||||
// MG_Backend/MGPipe/PipeInputs.h —— 按 memo 键组织,访问器类型与后端原来读到的完全一致
|
||||
#if MOBILEGL_PIPE_PUSH
|
||||
# define MGB_CTX (&::MobileGL::MG_Pipe::gPipeInputs)
|
||||
#else
|
||||
# define MGB_CTX (::MG_State::pGLContext)
|
||||
#endif
|
||||
```
|
||||
|
||||
三步:A 别名(P1,机械替换 + 逐 verb 类填充点,`nm` 不变)→ B 推送(P2,tracker 填 `gPipeInputs`,verify 构建逐 draw 比对快照版)→ C 句柄化(P3a–P4a、P7,`SharedPtr<前端对象>` → 句柄 + 描述符,写回变回调)。poison 是**逐 verb 世代**:读一个当前 verb 未填的字段是 `Fatal{UnmigratedPipeInput, "<Getter>@<Verb>"}`。
|
||||
|
||||
### 9.3 Track V / Track H
|
||||
|
||||
Track V(值类型:render state、pixel store、capability 位、标量)是机械迁移;Track H(167 个 `SharedPtr<MG_State…>` 读点)是真活。74% 的读点是翻译输入,所以"bump 一个版本让 server 自己拉"行不通,值本身必须过去。
|
||||
|
||||
### 9.4 残余值块
|
||||
|
||||
迁移期临时调用 `SetResidualValueState`(`ResidualValueBlock`):尺寸只降不升(1248 → 8,只剩 `CapabilityBits`),P13 变成 `static_assert(sizeof == 0)`;布局逐成员 `offsetof` 断言;stats 单独计字节。
|
||||
|
||||
### 9.5 身份 memo 的重键
|
||||
|
||||
`{slot, gen}` + 显式 destroy 让 21 条身份 memo 中 **11 条直接删除**、2 条的去抖搬到 client(§5.4)、7 条重键成更便宜的比较、1 条(D18)不动。统一事实:进 memo 键的版本计数器要么会回绕,要么根本不被它害怕的 mutation bump,身份比较是堵洞的补丁。
|
||||
|
||||
### 9.6 A/B 与口径收窄
|
||||
|
||||
`MOBILEGL_PIPE_PUSH` 子系统位图在阶段 B 是真 A/B;阶段 C 之后位清零时后端仍跑重键后的代码,所以**编译期** `MOBILEGL_PIPE_LEGACY_MEMOS`(默认 ON)保留 pre-handle 臂,随 pull 路径在 P13 退役。位依赖两侧都拒(客户端族门里**根本不发射**,否则会"客户端已清 dirty、服务端却走旧臂"而丢上传);没有消费者的后端一条不发。退役的 twin 成员仍在 pull 构建里编译——真删会动 G1。
|
||||
|
||||
## 10. server 侧
|
||||
|
||||
### 10.1 对象表与 applier
|
||||
|
||||
`MG_Remote/Server/PipeApplier`:解码 → 更新对象表与 `PipeInputs` → 调后端。server 不持有 buffer 的完整副本(只有 staged shadow)、不持有前端对象图;任何传输下都不得有 `SharedPtr` 或裸前端指针跨过 applier 边界(P5c 角色守卫、P5e 规则 F)。`InProcessTransport` 与 spawn 走完全相同的编解码路径。
|
||||
|
||||
### 10.2 monolith 侧的净收益
|
||||
|
||||
即使 IPC 永不上线:复用地址 ABA 一整类不可表达;FBO → program 排序 hazard 消失;`SwapchainObject` 写 `MG_Impl` 的分层倒置消失;`inproc` = 渲染线程。monolith 净代码量是增加的,论据只看逐线程 CPU。
|
||||
|
||||
### 10.3 索引宿主镜像(`Server/IndexHostMirror`,P8,待重裁)
|
||||
|
||||
设计:`kCapNeedsHostIndexBytes` 下,server 从本来就要收的 `ResourceCreate/Respecify/SubData` 流增量维护 element-array buffer 的宿主镜像,零额外流量;预算 `MOBILEGL_PIPE_INDEX_MIRROR_MB`(默认 64),超预算退化为逐 draw 经 `MGHostSpan` 传送。**P6 的 a6 审计实测宿主索引 span 无 producer、client 索引已是 owned buffer**,所以 P8 要按真实消费者重新裁定,不照抄本节([`notes/p8/README.md`](notes/p8/README.md))。
|
||||
|
||||
## 11. 传输与数据面(`MobileGL/MG_Remote/`)
|
||||
|
||||
### 11.1 段
|
||||
|
||||
| 段 | 拥有者 | 默认 | 内容 |
|
||||
|---|---|---|---|
|
||||
| `SEG_CMD` | client(server 只读) | 8 MiB | `RingControl` 页 + POD 记录 + 小内联负载 |
|
||||
| `SEG_STAGE` | client | 32 MiB | bulk 字节:buffer / 纹理 subdata、UBO、client 数组、persistent-map 脏块 |
|
||||
| `SEG_REPLY` | server | 16 MiB,8 × 2 MiB slot | readback 像素、acceptance 答案(ID-47) |
|
||||
| `SEG_EVENT` | server | 256 KiB SPSC ring | 回调事件 |
|
||||
| `SEG_SHADOW` / `SEG_ADOPT` | client / server | — | 零拷贝 shadow(Phase 2)/ ≥16 MiB 采纳(P11);stream 数据面上具名拒绝 |
|
||||
|
||||
段在同机上由 `ShmSegment` 创建(Android `ASharedMemory`、Linux `memfd`、Windows `CreateFileMappingW`),描述符经 `SCM_RIGHTS` 传递;stream 数据面上两端各持一份同尺寸的私有段(§11.9)。
|
||||
|
||||
### 11.2 `RingControl`(`Ring.h`)
|
||||
|
||||
一页 4 KiB,每个争用组一条 cache line,按写者分组(P6 lk):producer 线 `cmdHead` / `submittedSeq`;consumer 线两个 tail;四个 consumer 写的水位收成 `Progress`(`appliedSeq` / `retiredSeq` / `completedFrameSerial` / `presentAckSerial`);最后是 epoch、generation、park 标志与 `eventRingFull` / `eventDropped`。游标是单调字节计数、2 的幂掩码、永不重置;pad 记录处理 wrap;不可能的头 → `Fatal{ProtocolCorruption}`。
|
||||
|
||||
### 11.3 双向 doorbell(`Doorbell.h`)
|
||||
|
||||
两个方向都是"先自旋、再置 parked、再阻塞",发布方仅当对端 parked 时敲;丢失唤醒由两个 `seq_cst` fence 关闭。`CondVarDoorbell`(同进程,带 `Kill()`)与 `SocketDoorbell`(跨进程,对端关闭即死亡检测)。stream 数据面上门铃是本地条件变量,由读线程敲。
|
||||
|
||||
### 11.4 控制面(`protocol.fbs`、`Framing.h`、`ITransport.h`)
|
||||
|
||||
- 一份 FlatBuffers schema:热路径是 `struct` 直接进 ring(与 POD 逐条 `static_assert` 对齐);罕见 / 变长 / 需演进的走控制连接的 `CtrlMsg`(`Hello`、`Welcome`、`Refuse`、`CapsSnapshot`、`SurfaceOp/SurfaceReply`、`SessionFault`、`LogLine` …),union tag 只追加,控制修订号钉在 `sha256(protocol.fbs)` 上。
|
||||
- 生成物提交进树,CI `flatc-check` 重生成并 diff;codegen 不进默认构建图。
|
||||
- 封帧 `[u32 'MGLF'][u32 len][payload]`,64 MiB 上限,读时校验,坏帧立即闩住失败。
|
||||
|
||||
### 11.5 WAR 危害、拷贝账与背压
|
||||
|
||||
Phase 1(今天):GL 调用时刻把字节拷进 stage slot,slot 到 apply 越过它为止不可变,危害按构造消除,代价一次 memcpy;Phase 2(`SEG_SHADOW` 零拷贝)未做。server 没有第二份 `BufferObject`,不存在中间拷贝。分配失败升级:扩容 → 对最老未退休批次有界等待 → 硬 drain + generation bump(之后 tracker 全部重推)。
|
||||
|
||||
### 11.6 publish、序号与 credit
|
||||
|
||||
- 每条记录 release-store `cmdHead`,仅当 consumer parked 时敲门铃;显式门铃点:`present`、`kNeedsAck`、`eglMakeCurrent`、`glFlush`、stage 余量不足、轮询类入口。
|
||||
- seq = 记录序数;字节 credit 与 present credit 两个独立窗口。
|
||||
- **等待规则由 `WaitClass` 列决定(P5e)**:`kWaitReply` / `kWaitPresent` / `kWaitApplied` / `kWaitNone`;run-ahead 武装时 `kWaitNone` 发布即返回。barriered 谓词(`MGPipeBarriered`)client 与 server 用同一个函数、同一份数据算。
|
||||
- 强制等待点:`MapBuffer(READ)` / `GetBufferSubData` 源走 `SyncGpuWrites`;`glFinish` = 等 `appliedSeq` 追上再排空反向通道;每个 `Server*` EGL forwarder 之前等一次。`glGetError` 最多晚一个 present credit。
|
||||
|
||||
### 11.7 事件回传与溢出
|
||||
|
||||
client 在 `glGetError`、query / sync 查询、`eglSwapBuffers`、buffer 回读与每轮等待循环中排空 `SEG_EVENT`。日志有损;语义事件无损:环满时 server 置 `eventRingFull`、在记录边界停 apply、敲 client,client 排空后清标志并敲回(两半都在,才不死锁)。run-ahead 下 producer 阻塞等排空,整笔预算 `MOBILEGL_IPC_EVENT_WAIT_MS`;client 不排空 / 已走 / 会话停止 → `ReverseChannelForfeit{…}` 弃投并闩住(Ph PH-6),而不是 `Fatal` 打掉 server。不发布 `kCapRunAheadApply` 的 server(lockstep 臂)保留 P5c 的 Fatal。
|
||||
|
||||
### 11.8 fence 与无 present 负载(P10)
|
||||
|
||||
fence 完成度必须来自真的逐 fence 退休,不是 present 水位(DirectGLES 的 `g_completedFrameSerial` 今天只在 `Present` 前进);无 present 循环需要 server 插非 present fence tick。Magma 那半已随 run-ahead 落地。
|
||||
|
||||
### 11.9 传输栈的两根轴(P6.5)
|
||||
|
||||
终局(2026-09-22):client 与 server 可在不同机器、不同 OS / 架构上经 TCP 连接,同机 `spawn` 保留。传输栈拆成**两根独立可选、握手协商、可混搭**的轴,上层只见两个接口:
|
||||
|
||||
| 轴 | 接口 | 实现 | 选项(`MOBILEGL_IPC_CONTROL` / `MOBILEGL_IPC_DATA`) |
|
||||
|---|---|---|---|
|
||||
| 控制面 | `ITransport`(只承载控制帧,不承载描述符) | `SocketTransport` | `fork`(继承 fd)、`unix:<path>`、`tcp://host:port` |
|
||||
| 数据面 | `ILink`(记录预留 / 提交、水位、reply、event、span 解析) | `ShmLink`(共享段 + `RingControl` + 门铃)、`StreamLink`(分块封帧,水位与 reply 变消息,发送窗口) | `shm`、`stream`、`auto` |
|
||||
|
||||
- 配对在会话建立时选定一次;`LinkTerms`(数据面、窗口尺寸、`maxReplyBytes` …)由 **server 陈述**,client 请求可被钳制。
|
||||
- 共享段交付与控制面解耦:`ShmLink` 自带 AF_UNIX aux 汇合名,在 `Welcome` 里公布,所以同机"TCP 控制 + 共享段数据"是合法配对。`auto` 的判据是**段实际交付成功**,回落到 `stream` 必须具名进日志。
|
||||
- **上层零链路种类分支**:`ShmLink` / `StreamLink` / `RingControl` 等只出现在 `Transport/` 与其测试(纯度 grep 门);角色谓词回答"谁在哪个进程",不回答"用什么链路"。
|
||||
- 两端是不同二进制:`wireFingerprint`(`PipeFields.def` 派生的逐成员布局摘要 + 目录摘要 + 字节序 / 指针宽度)永远比较;`buildFingerprint` 只在 `Dial == Fork`(或 `MOBILEGL_IPC_REQUIRE_SAME_BUILD=1`)下比较;不一致是 `Refuse`,不是 abort。
|
||||
- TCP 上每条 `kWaitReply` 就是一个 RTT、`SEG_STAGE` 字节对的是 30–100 MB/s 的链路:这两列从"记录项"变成可行性数(开放问题 19、20)。
|
||||
|
||||
第一波(全 TCP 控制 + stream 数据)已落地;同机混搭(nd `auto` / sd)是第二波。细节 [`notes/p65/README.md`](notes/p65/README.md),契约 `MobileGL/MG_Remote/CONTRACT-P65.md`。
|
||||
|
||||
## 12. persistent map 与 ≥16 MiB 采纳
|
||||
|
||||
`AcquirePersistentMap` 是永久的地址空间捐赠(≥16 MiB 可变 store 自动采纳,MC 26.3 p99 163 → 21 ms),整个 monolith 改造期不动(D-B4)。拆分下三档由 POST 探针选择(spike B 实测):
|
||||
|
||||
| 档 | 形态 | 结论 |
|
||||
|---|---|---|
|
||||
| T0 | client 分配 `AHardwareBuffer` BLOB,server 导入(Vulkan / GLES) | 唯一在两台设备、两个后端上都完整读写的档(P11 主攻) |
|
||||
| T1 | server 导出 opaque fd | 仅 Adreno 的 Vulkan 路径 |
|
||||
| T2 | 拒绝,client 侧推送 | 永久正确回退;**今天 split 用的档**(`MOBILEGL_IPC_ADOPT_TIER=2`),stream 数据面上强制 |
|
||||
|
||||
T2 下 client 侧推送三件套:不做 map/unmap 命令对(payload 带 `hasLiveHostWrites`);在每个 validate 点按块(`MOBILEGL_IPC_PERSISTENT_BLOCK_KB`,默认 64)推送可达的已映射 buffer,脏页追踪 + 哈希抑制只发变化的块(P5d);门 `PersistentCoherentMapScenario`。`mpr` 数的是每一次 `MapPersistent` 发射(铸成或拒绝都算),所以在 monolith 下也可断言。
|
||||
|
||||
## 13. 回读、roundtrip 清单与验证
|
||||
|
||||
### 13.1 稳态零 roundtrip 与不可避免的阻塞点
|
||||
|
||||
- 零 roundtrip:全部 draw / clear / blit / copy / dispatch / XFB / bind / CSO / `set_*` / 上传 / `present`;全部 caps 站点;`glGetError` / `glFinish` / `glFlush`;fence 与 query 的创建和非阻塞轮询;`eglSwapBuffers`;restart / multi-draw。
|
||||
- 不可避免(罕见):握手;surface 生命周期;`glReadPixels` 到客户内存;GPU-write pending 的 buffer 首次 CPU 读;带超时的 sync / query 结果;`glBufferStorage` 的 ack;纹理拉取;ring / stage 耗尽与 present credit。
|
||||
- 跨机上每条 `kWaitReply`(`ResourceCreate`、`SetTextureParams`、纹理 `ResourceSubData`)都是一个 RTT:逐帧次数是 P6.5 必测数,减少它是 P9 / P10 的活。
|
||||
|
||||
### 13.2 五部分验证门(取代 monolith 的字节一致门)
|
||||
|
||||
1. **接口纯度**:A 门 include 图(`scripts/check_include_closure.py`)、B 门 server 镜像不引用 `MG_State::GLState` 与 glslang(P6 的 a6 实测 server 仍链 `MG_Impl`,由 `scripts/link_ratchet.py` 的下行棘轮追踪,P13 转绿)、C 门 `MG_Backend` 里零 `pGLContext`;外加"每个后端 memo 键都是 `{slot, gen}`"(`HandleRecycleScenario`)。
|
||||
2. **语义影子比对** `MOBILEGL_PIPE_VERIFY=1`:tracker 再用 `SnapshotFromGLContext()` 填一份 `PipeInputs`,G4 逐字段每 draw 比对,打印第一个分歧;verify 构建永不出货,活过 P13。
|
||||
3. **行为 A/B**:trace 语料在 monolith-pull / monolith-push / split 下 SSIM ≥ 0.99;`DirectGLES.` 与 `DirectGLES.Split.` 等名集合逐名相同(G2)、测试名只增不删(G14);CTS 逐后端 0.5 pp 内;上传形状金标。
|
||||
4. **性能**:Redmi `2f7cbe2e` reboot-clean、同热窗口配对 A/B,指标是**逐线程 CPU 时间**;对着 pull 臂**只记录、不设门**(用户 2026-09-08)。
|
||||
5. **覆盖 + poison + 句柄纪律**:G6 0 UNMAPPED、dirty-surface 0 未映射、G8 归属完备、逐 verb poison、G7、残余块棘轮。
|
||||
|
||||
两条幸存的字节级等式:`MOBILEGL_BUILD_DISAGGREGATED=OFF` 时库里零 `MG_Remote` 符号;**G1** pull 构建的符号集与 `.text` 对基线恒等(每阶段 0 增 / 0 删 / 0 resize / 0 重命名)。**每个门必须能因它存在的理由变红**(R-16):带阴性对照并真跑过一次红;公共 GL 看不见的改白盒断言;拒绝普查逐用例读私有日志(`ctest -V` 是假零)。
|
||||
|
||||
### 13.3 长期语义门:MGPipe recorder(P13)
|
||||
|
||||
`MG_Test` 的 mock 后端变成 MGPipe recorder,在一组 fixture 上录每 draw 的已推送状态做金标;它只覆盖推送内容,不覆盖后端对它的解释(开放问题 12)。
|
||||
|
||||
## 14. Present、线程与帧节奏
|
||||
|
||||
- `eglSwapBuffers` → `present{frameSerial}` → publish + 敲门铃 → 返回,除非超出 credit;**`present` 与 `eglSwapBuffers` 严格 1:1**(后端的帧边界排空只在 `Present` 内发生)。
|
||||
- `MOBILEGL_IPC_PRESENT_CREDIT` 默认 1(range 1..8):credit 是 run-ahead 唯一的稳态背压,`FrameSerial` 由 client 铸造、1 起算,server 在 `Present()` 返回后归还 credit;等待发生在**编码之前**。credit > 1 从未量过(P10)。
|
||||
- 预算(~850 draw/帧):`SEG_CMD` ~70 KB/帧,`SEG_STAGE` ~1 MB/帧;跨机时这 ~1 MB/帧对着的是 Wi-Fi。
|
||||
- 线程:client v1 不加线程,编码在 GL 线程直接写 ring;server 有 io 线程与**终身持有原生 context** 的 `mgl-srv-apply`。全库无有效亲和性控制(Redmi 内核忽略 app 线程的 `sched_setaffinity`)。
|
||||
- 拆机顺序:publish + server 排空 → 停 apply 线程 → 关传输 → client 排空编译池 → `MobileGL::Destroy()`(落地形状见 §17)。
|
||||
|
||||
## 15. 进程、EGL 与平台(P6 / P6.5 / P12)
|
||||
|
||||
### 15.1 启动与握手
|
||||
|
||||
- 端点:`fork`(`socketpair` + `fork`/`execve`,fd 3 = socket;server 由 `MOBILEGL_IPC_SERVER_PATH` 或 `dladdr` 同目录的 `libMobileGLServer.so` 定位)、`unix:<path>`、`tcp://host:port`。设备侧 supervisor `mobilegl_server_main <endpoint> --serve`:listen → 认证 → 每会话 fork 一个子进程(或进程内线程,P12),同时只服务一个会话,第二个连接 `Refuse{Busy}`。
|
||||
- server 进程里 `Transport` 读作 `Spawn`,**不强制 monolith**(两百多处 `Transport != Monolith` 判断靠它选 server 臂;`CONTRACT-P6.md` §3.1 修订了原"子进程强制 monolith"的规则)。防无界 fork 链靠不继承的 `Dial == No`(`MOBILEGL_IPC_DIAL=no`),spawn 时构造的 envp 剔除 `MOBILEGL_TRANSPORT` 与 `MOBILEGL_IPC_*` 作第二道独立保险;server 主程序自设 `MOBILEGL_IPC_ROLE=server`。
|
||||
- `Hello`(abi、后端类型、指纹、配置、令牌、`LinkTerms` 请求)→ `Welcome`(server 陈述的尺寸、段 / aux 名、`dataNonce`、真实 pid)或 `Refuse{code, detail, 对端值}`;握手路径零 abort。非 loopback 监听必须有令牌(≥16 字节、常量时间比较、数据面按 `dataNonce` 绑定到已认证的控制连接、fork 前认证;Ph,OQ-21 裁定不做 TLS)。
|
||||
- TCP keepalive / 心跳失败**由传输层报告为挂断**,进 device-lost 闩;"闩绝不取自 apply 超时"不因此松动。冷启动期 server 发 `SurfaceProgress`,client 预算这段沉默(控制修订 2)。
|
||||
|
||||
### 15.2 Android
|
||||
|
||||
- 交付链(spike A 已证):APK 唯一可 exec 的位置是 `lib/<abi>/`,server 以 `add_executable` + `lib*.so` 命名构建;从 `untrusted_app` 进程 `fork`+`execve` 同域、零 avc denial;`posix_spawn` 在 minSdk 26 不可用。一份共享库两个角色(stub `dlopen` + `dlsym("mobilegl_server_main")`)。
|
||||
- 离屏 server:前台 `MobileGLServerService`(`:mglsrv`),从 `nativeLibraryDir` exec,fork-per-session,EGL 是 pbuffer / surfaceless。
|
||||
- **上屏 server(P12 子集)**:窗口归 **server 自己**(client 的窗口不跨进程)。`MobileGLDisplayActivity`(`:mglwin`)持全屏 SurfaceView,在自己的进程里用一个线程跑 TCP server(会话串行、会话间重置闩、后端按进程钉住);`ServerDisplay` 以 acquire / release / 几何三个钩子持有窗口,apply 线程以**租约**使用它。client 设 `MOBILEGL_IPC_SURFACE=server` 后**无头**:`eglCreateWindowSurface(NULL)` 发一帧 `WindowKind::ServerOwned`(控制修订 3),不发 `SetWindowHandle`,尺寸由 reply 带回,resize 变成对 server 窗口的几何请求。一台设备同一时刻一个 server;一个会话一种表面模式(`SurfaceModeMismatch`);无显示的 server 对 ServerOwned 具名拒绝(`NoServerDisplay`)。
|
||||
- 失窗:`surfaceDestroyed` 有界(3 s)阻塞,直到 apply 线程(批内每条记录前也检查)放掉后端表面并以 `Fatal{ServerWindowLost}` 闩住会话;server 继续监听。进程内会话结束时手动做进程退出本来白给的清理(Espryt twin 与单元影子、swap interval、Magma renderer)。
|
||||
- `HeadlessGL` 的 fork 预检会产生孤儿 server:server 的 EOF 检测即时且无条件退出。
|
||||
|
||||
### 15.3 Linux / Windows / 崩溃
|
||||
|
||||
- Linux / WSL / CI 永不开窗(`EGL_PLATFORM=surfaceless`);Wayland 不支持。Windows / 桌面 client 在 TCP 终局下是真实形态(Windows 是第二批,开放问题 22);macOS 不拆分;Windows 机器不是正确性门。
|
||||
- server 死:client 读到挂断 → **device-lost 闩**(GL 调用 no-op、`eglSwapBuffers` 返回 `EGL_CONTEXT_LOST`、reset status `GL_UNKNOWN_CONTEXT_RESET`);`Session::Fail` 漏斗向对端发 `SessionFault` 帧命名家族(`FatalFamilies.def`)。**不重启**:`MOBILEGL_IPC_RESPAWN` 今天是具名拒绝。client 死:server 读到 EOF 立即销毁 context 并退出(supervisor 继续服务下一连接)。
|
||||
|
||||
## 16. 构建布局
|
||||
|
||||
```
|
||||
MobileGL/MG_Pipe/ 永远进构建:*.def 目录与表、MGPipe*.h payload / 句柄 / 回调、PipeApply、PipeRoute、generated/*.inc
|
||||
MobileGL/MG_Impl/Pipe/ Tracker、PipeFill、SlotAllocator、CsoCache、CompositeResolver、ResourceTracker、SetHashSuppressor、*Emit.h
|
||||
MobileGL/MG_Backend/MGPipe/ PipeInputs.{h,cpp}
|
||||
MobileGL/MG_Remote/ 仅 MOBILEGL_BUILD_DISAGGREGATED:CONTRACT-*.md、CapsCodec、FatalFamilies.def / FatalFunnel、Handshake.h
|
||||
Protocol/ protocol.fbs、generated/、SurfaceOpCodec、mg_protocol_base.h
|
||||
Transport/ ITransport、SocketTransport、InProcessTransport、ILink、ShmLink、StreamLink、Ring、SessionRings、ReplySlot、EventRing、
|
||||
Doorbell、ShmSegment(+Posix/Win32)、FdPassing、Framing、AuthToken、ControlInbox、LinkMetrics、WireLog
|
||||
Wire/ PipeWireCodec(记录编解码、WireVerbSink)
|
||||
Client/ BackendObject_Remote、ClientSession、EmitTables、WireTables、CapsMirror、PersistentMapTracker、GpuWritePending
|
||||
Server/ PipeApplier、ServerLoop、ServerSession、ServerMain / ServerEntry / ServerSpawn、PreAuthGate、StagedShadow、StagedTextureStore、
|
||||
SurfaceControlFrame、ServerDisplay、InProcessServer、DisplayServerJni
|
||||
```
|
||||
|
||||
- `MOBILEGL_BUILD_DISAGGREGATED`(默认 OFF)追加 `MG_Remote/**`;OFF 时 `MG_Config::Transport` 是 `constexpr Monolith`。`MOBILEGL_BUILD_DISAGGREGATED_INPROC` 隐含前者并加角色隔离:两个角色靠 apply 线程与控制邮箱分开,而不是给 1494 个 `pGLContext->` 读点加 TLS。
|
||||
- `MOBILEGL_TRANSPORT = monolith | inproc | spawn` 是**拓扑**(G1 相关);控制面与数据面由 §11.9 的两个变量选。**split build 不设 `MOBILEGL_TRANSPORT` 时是 monolith 对照臂**。
|
||||
- 四个构建 flavour:pull(默认)、push、verify、split;Android 三份 APK flavour(pull / push / split)。
|
||||
- 测试接线:ctest `ENVIRONMENT` 用 `mgl_itest_join_environment(...)` 构造;每条 split / spawn / tcp 条目带独立日志路径,日志按角色分文件(`<base>.client.log` / `<base>.server.log`);spawn、tcp 车道与 split 名集合一致(`scripts/ci/spawn_lane_parity.py`),每条目记 arm 证明。
|
||||
- CI(`.github/workflows/test.yml`、`apk.yml`):`pipe-gates`(G1–G8、生成器 self-test、符号报告、dirty-surface、字段归属、G5、文档引用 lint)、`flatc-check`、include 闭包、各 flavour 的 build / integration / retrace 车道、APK + AVD。两份 workflow 里的 `feat/disaggregated` 触发器是临时的,合入 `dev` 前移除。
|
||||
|
||||
## 17. 阶段落地形状(索引)
|
||||
|
||||
各阶段落地时的形状、偏差与论证原文在 notes 里;下表只列仍然生效的规则。
|
||||
|
||||
| 原节 | 主题 | 仍生效的规则 | 全文 |
|
||||
|---|---|---|---|
|
||||
| 17.1 | verb barrier 与诚实的同地址空间传输(P5) | 同地址空间不得成为旁路(R-2):encoder 把 `MGHostSpan::Ptr` 恒写 `nullptr`;reply 以记录序号为 slot id(R-3);等待看门狗 120 s 只判死锁,不是性能门 | [`notes/p5/README.md`](notes/p5/README.md) |
|
||||
| 17.2 | 后端槽三类、caps、tight readback(P5) | A 本地答 / B 发射 / C 具名拒绝,永不回落 monolith(R-4);能力只读 `CallMask`(R-8);`ReadPixels` 线上恒 tight,client 按自己的 pack state 散射 | 同上 |
|
||||
| 17.3 | server 角色、shadow 与退出(P5) | apply 线程终身持有 native context,EGL 罕见操作经控制邮箱进 apply 线程;staged shadow 只交 server-owned 拷贝;有界退出顺序 | 同上 |
|
||||
| 17.4 | lane 隔离(P5) | 每条 split 条目私有日志;E1 / E3 控制把"选中条目被跳过"判为失败 | 同上 |
|
||||
| 17.5 | class-C verb 迁移(P5b) | 迁移顺序由动态普查决定;一次迁移 = 一个发射器 + 一个 sink 体;记录逐字带 GL 调用参数,未测量的形式按名拒绝(规则 D) | [`notes/p5b/README.md`](notes/p5b/README.md) |
|
||||
| 17.6 | 共享地址空间访问归零(P5c) | server 端纹理 staged shadow;`SEG_EVENT` 是唯一反向通道;sink / twin 按记录句柄解析;`applier_reset` / `object_death` 控制记录;角色守卫 `Fatal{RoleViolation}` | [`notes/p5c/README.md`](notes/p5c/README.md) |
|
||||
| 17.7 | run-ahead(P5e) | 规则 F:unbarriered 记录的 apply 不读任何 client 内存(违反即具名 Fatal,与 strict 开关无关);`gPipeInputs` 是 server 角色内存;`RunAheadArmed()` 在首次 caps 采纳时锁存,只能关不能开;`RUN_AHEAD=0` 是 A/B 对照,`VERB_BARRIER=0` 会连带关掉 run-ahead | [`notes/p5e/README.md`](notes/p5e/README.md) |
|
||||
|
||||
## 附 A:开关
|
||||
|
||||
CMake:`MOBILEGL_BUILD_DISAGGREGATED`(OFF)、`MOBILEGL_BUILD_DISAGGREGATED_INPROC`(OFF,隐含前者)、`MOBILEGL_PIPE_PUSH`(OFF;push / verify / split flavour 打开)、`MOBILEGL_PIPE_VERIFY`(OFF;隐含 push,永不出货)、`MOBILEGL_PIPE_LEGACY_MEMOS`(ON)、`MOBILEGL_BUILD_SERVER_SPIKE`(仅 Android spike)、`MOBILEGL_FLATC_EXECUTABLE`(只服务 `flatc-check`)。
|
||||
|
||||
运行时,MGPipe(`MobileGL/Config.h`、`ConfigLoader.cpp`):
|
||||
|
||||
| 变量 | 默认 | 说明 |
|
||||
|---|---|---|
|
||||
| `MOBILEGL_PIPE_PUSH` | pull `0`;push `0x1fff` | 子系统位图(`MG_Pipe/MGPipe.h`,位永不复用;依赖两侧都拒);位 63 是"关 CSO 内容寻址"的行为对照 |
|
||||
| `MOBILEGL_PIPE_VERIFY` / `_VERIFY_FATAL` / `_VERIFY_CORRUPT` / `_POISON_OMIT` | 0 / 1 / 空 / 空 | 影子比对与两个阴性对照(verify 构建才有) |
|
||||
| `MOBILEGL_PIPE_HANDLE_ABA_CONTROL` | 0 | 故意打掉句柄身份的阴性对照 |
|
||||
| `MOBILEGL_PIPE_STATS` / `_STATS_PERIOD` / `_STATS_FILE` | 0 / 120 / 空 | 边界计数器(附 B);dump 按角色写 `<base>.client.json` / `<base>.server.json` |
|
||||
| `MOBILEGL_PIPE_LEGACY_MEMOS` / `_TEXEL_RETAIN_MB` / `_INDEX_MIRROR_MB` | ON / 0 / 64 | pre-handle 臂、纹理拉取保留、索引镜像预算(P8) |
|
||||
|
||||
运行时,传输与 IPC:
|
||||
|
||||
| 变量 | 默认 | 说明 |
|
||||
|---|---|---|
|
||||
| `MOBILEGL_TRANSPORT` | `monolith` | 拓扑:`monolith` / `inproc` / `spawn` |
|
||||
| `MOBILEGL_IPC_CONTROL` / `MOBILEGL_IPC_DATA` | `fork` / `auto` | 控制面端点与数据面(§11.9);`MOBILEGL_IPC_SERVER_PATH` 定位 fork 出的 server |
|
||||
| `MOBILEGL_IPC_TOKEN` / `MOBILEGL_IPC_REQUIRE_SAME_BUILD` | 空 / 0 | 令牌(≥16 字节;无令牌只允许 loopback);要求两端同一构建(车道纪律) |
|
||||
| `MOBILEGL_IPC_PREAUTH_*` / `MOBILEGL_IPC_AUTH_BACKOFF_*` | — | server 预认证的上限与失败退避(Ph) |
|
||||
| `MOBILEGL_IPC_CONTROL_TIMEOUT_MS` / `MOBILEGL_IPC_COLD_START_MS` | 5000 / 20000 | 控制回复的稳态上限与冷启动预算 |
|
||||
| `MOBILEGL_IPC_LOG_FORWARD` | 远端时开 | server 日志经控制连接前送,client 写进自己的 `<base>.server.log` |
|
||||
| `MOBILEGL_IPC_SURFACE` | `offscreen` | `server` = 使用 server 自有窗口,client 无头(P12) |
|
||||
| `MOBILEGL_IPC_RING_MB` / `MOBILEGL_IPC_STAGE_MB` | 8 / 32 | `SEG_CMD` / `SEG_STAGE`;stage 的 1/4 是内容分块预算 |
|
||||
| `MOBILEGL_IPC_RUN_AHEAD` | 1 | P5e run-ahead;只是合取式的一半(server 须发布 `kCapRunAheadApply`);`0` 是 A/B 对照 |
|
||||
| `MOBILEGL_IPC_VERB_BARRIER` | 1 | `0` 只作阴性对照,且同时关掉 run-ahead |
|
||||
| `MOBILEGL_IPC_PRESENT_CREDIT` | 1 | 1..8,§14 |
|
||||
| `MOBILEGL_IPC_BATCH_WAITS` | 1 | lockstep 下值类记录发布即返回;verify 强制 0 |
|
||||
| `MOBILEGL_IPC_EVENT_WAIT_MS` | 2000 | 反向事件遇满环时 server 等 client 排空的整笔预算(§11.7) |
|
||||
| `MOBILEGL_IPC_WIRE_DEFERRED_MB` | 64 | Magma wire 臂延迟回收的水位线;`0` 是 M2 阴性对照 |
|
||||
| `MOBILEGL_IPC_PERSISTENT_BLOCK_KB` / `_PERSISTENT_HASH_SUPPRESS` | 64 / 1 | persistent-map 推送粒度 / 只推变化的块;`0` 是对照 |
|
||||
| `MOBILEGL_IPC_ADOPT_TIER` | 2 | `auto/0/1/2`;T0 / T1 今天是 Fatal-at-use |
|
||||
| `MOBILEGL_IPC_STRICT_ERRORS` / `MOBILEGL_IPC_AUDIT` / `MOBILEGL_IPC_ROLE_SPLIT_STATE` | 0 / 0 / 0 | 残余输入读升级为 Fatal / 退休 staging 填 `0xDD` / 双块演练(P5f) |
|
||||
| `MOBILEGL_IPC_SPIN_US` / `MOBILEGL_IPC_SERVER_AFFINITY` | 50 / `auto` | park 前自旋预算 / apply 线程亲和(Redmi 内核忽略) |
|
||||
| `MOBILEGL_IPC_RESPAWN` | — | 具名拒绝:没有阶段实现 server 重启后的全量重推 |
|
||||
|
||||
显式不设立:`MOBILEGL_IPC_PROGRAM`(没有 relink 档)、`MOBILEGL_IPC_VALIDATE_SERVER`。计划中未接线:`MOBILEGL_IPC_POLL_ESCALATE`(P10)、`MOBILEGL_IPC_IDLE_EXIT_S`(未解析)。
|
||||
|
||||
## 附 B:边界计数器(`MobileGL/MG_Util/Metrics/PipeStats.h`)
|
||||
|
||||
关闭时每站点一次全局 load + 一条永不命中的分支。字节类(stage buffer / texture / UBO / client 数组、`pmap`、`resid`、`csob-blob`、`seg` = 写进 `SEG_STAGE` 的字节)、调用类(`draws`、`accessor-calls`、纹理上传 emit / box / rect / jobs、`csom` / `csob`、`mpr`、`trp`、`rsp`、`wrec`、`maxrec` / `ringwraps` / `ringpads` / `ringwaits`、门铃 `srv` / `srvpark` / `cli` / `clipark`)与六个 memo 门的 hit/miss。每 `MOBILEGL_PIPE_STATS_PERIOD` 帧一条 `MGPipe stats:` 汇总行,按角色写日志。读法陷阱:`accessor-calls` 是静态下界,判性能只看 CPU 时间;spawn 下 client 不推进帧窗口(`frames=0`),窗口化字段在 spawn client 上无效,run total 仍有效([`notes/p6/README.md`](notes/p6/README.md) §12.1)。站点清单(哪些路径**没有**接线)写在 `PipeStats.cpp` 头部,那份清单是契约。
|
||||
| 1–2 | 边界:两张表、三种拓扑;对象模型:句柄、两种代数、CSO | [`design/01-boundary-and-objects.md`](design/01-boundary-and-objects.md) |
|
||||
| 3–4 | 调用目录、生成器 G1–G8、能力位;记录与 payload 格式 | [`design/02-catalogue-and-records.md`](design/02-catalogue-and-records.md) |
|
||||
| 5–7 | 前端 tracker(何时推、推什么、怎么去重);纹理上传;着色器 | [`design/03-frontend.md`](design/03-frontend.md) |
|
||||
| 8 | 反向通道:九个回调、有序性、错误与 ack、纹理重铸拉取、XFB | [`design/04-reverse-channel.md`](design/04-reverse-channel.md) |
|
||||
| 9–10 | 后端改造(`PipeInputs`、memo 重键、A/B 臂);server 侧 applier | [`design/05-backend-and-server.md`](design/05-backend-and-server.md) |
|
||||
| 11 | 传输:共享段、环、门铃、控制面、背压、等待规则、事件、两根轴 | [`design/06-transport.md`](design/06-transport.md) |
|
||||
| 12–13 | persistent map 与大缓冲采纳;回读、往返清单、五部分验证门 | [`design/07-memory-readback-verification.md`](design/07-memory-readback-verification.md) |
|
||||
| 14–15 | 帧节奏与线程;进程、握手、EGL、Android(含上屏 server)、崩溃处理 | [`design/08-runtime-and-platform.md`](design/08-runtime-and-platform.md) |
|
||||
| 16、附 A、附 B | 源码目录与构建选项;全部开关;边界计数器 | [`design/09-build-switches-counters.md`](design/09-build-switches-counters.md) |
|
||||
| 17 | 各阶段落地时的形状与仍生效的规则(链到 `notes/`) | [`design/10-phase-shapes.md`](design/10-phase-shapes.md) |
|
||||
|
||||
@@ -1,51 +1,27 @@
|
||||
# 当前阶段进展 — P12「server 自有屏幕窗口」子集
|
||||
# 当前阶段:P12 — server 自己开窗口上屏
|
||||
|
||||
> 只记**当前阶段**的摘要:在哪、落地了什么、门到哪一步、下一步、阻塞。细节与证据都在 [`notes/p12/`](notes/p12/README.md)(计划与交接 [`PLAN-P12.md`](notes/p12/PLAN-P12.md),裁定 [`INTEGRATOR-DECISIONS-P12.md`](notes/p12/INTEGRATOR-DECISIONS-P12.md))。已收官阶段见 [`ROADMAP.md`](ROADMAP.md) 阶段表。随每次合并 / 真机窗口更新。
|
||||
> **更新 2026-09-24**。这一页只有摘要;带裁定编号的明细、余项清单、证据位置在 [`notes/p12/README.md`](notes/p12/README.md)(计划与交接 [`PLAN-P12.md`](notes/p12/PLAN-P12.md))。上一阶段 P7 已于 2026-09-23 完成。
|
||||
|
||||
**更新:2026-09-24** · 分支 `feat/disaggregated`(P12 子集与审查轮已并入)· 上一阶段 P7 已于 2026-09-23 收官(ID-P7-63)
|
||||
## 目标
|
||||
|
||||
## 1. 一句话
|
||||
Android 上的 server 应用自己开一个全屏窗口,把收到的渲染流直接画到屏幕上;client 不需要有窗口(完全无头)。窗口没了(比如按 HOME)时,client 干净地得到"设备丢失",server 继续等下一个连接。
|
||||
|
||||
Android 上的 server 自建窗口(自己的 SurfaceView)把 IPC 渲染流**上屏**,client 以 `WindowKind::ServerOwned` **完全无头**接入。子集已实现并入,**未收官**:审查轮修复之后欠一次真机复测,两个出口门都还没打。
|
||||
## 完成情况
|
||||
|
||||
## 2. 已落地
|
||||
| 项 | 状态 |
|
||||
|---|---|
|
||||
| 功能:server 自有窗口、无头 client、窗口丢失处理、一台设备同时只一个 server | ✅ 已并入主分支 |
|
||||
| 代码审查发现的 10 个问题 | ✅ 已修 |
|
||||
| 主机测试;单进程构建二进制不变(G1) | ✅ |
|
||||
| 真机 7 项检查(两个后端上屏、排队与拒绝、窗口丢失、离屏不受影响……) | ✅ 修复前通过;⏳ **修复后未复测** |
|
||||
| 验收 A:同一台手机上用 FCL 以两个后端进游戏,杀掉 server 后干净报"设备丢失" | ❌ 未做 |
|
||||
| 验收 B:另一台电脑经 TCP 连手机进游戏,并记录链路数据 | ❌ 未做 |
|
||||
|
||||
| 面 | 内容 | 出处 |
|
||||
|---|---|---|
|
||||
| wire | `WindowKind::ServerOwned`、控制修订 2 → 3、`SurfaceReply` 带回窗口真实尺寸或具名拒绝 | PLAN-P12 §1 |
|
||||
| server 窗口 | `ServerDisplay`(钩子 + 租约 + 有界 3 s `Detach`);会话表面模式首次创建即闩定(`SurfaceModeMismatch`);无显示具名拒绝(`NoServerDisplay`);失窗 → `Fatal{ServerWindowLost}` → client 读干净 device-lost | ID-P12-2、ID-P12-3 |
|
||||
| client | `MOBILEGL_IPC_SURFACE=server` → 无头窗口 surface,不发 `SetWindowHandle`;resize 变成对 server 窗口的几何请求,采用 server 回的真实尺寸 | ID-P12-10 |
|
||||
| 进程内 server | `MobileGLDisplayActivity`(`:mglwin`)里一个线程跑 TCP server:会话串行、会话间重置闩、后端按进程钉住;与离屏 `MobileGLServerService` 互斥,一台设备同一时刻一个 server | ID-P12-4 |
|
||||
| 审查轮 | 10 个问题已修;两个大项:批内失窗检查(`DrainRing` 每次 pop 前)、会话间 Espryt 单元影子悬空;另有推流中停机、会话结束清理、陈旧尺寸、日志转发 | ID-P12-5 … 11 |
|
||||
## 下一步
|
||||
|
||||
## 3. 门
|
||||
1. 真机复测(需要连着 Redmi 的机器)。
|
||||
2. 验收 B,再验收 A。
|
||||
3. 收尾:写 `MG_Remote/CONTRACT-P12.md`、修门脚本里误导的一行输出、做一次收官审查。
|
||||
4. 之后按 [`ROADMAP.md`](ROADMAP.md):P8;P9 → P10 → P11。
|
||||
|
||||
| 门 | 状态 | 证据 |
|
||||
|---|---|---|
|
||||
| G1 | ✅ pull 构建同机前后 `.text` 一致、符号 0 增 0 减 | ID-P12-15 |
|
||||
| 主机门 | ✅ unit 2605/2605,spawn / tcp 与三条 Magma 双进程车道全绿;剩余红全部归容器的 CMake/CTest 版本 | ID-P12-14 |
|
||||
| 真机七项检查(Redmi `2f7cbe2e`) | ✅ 审查轮之前(`29b7b284`):双后端上屏 ssim 1.0、串行 + `Refuse{Busy}`、失窗 device-lost、离屏不回归、无显示具名拒绝、一台设备一个 server;⏳ **审查轮之后未复测** | PLAN-P12 §2、ID-P12-13 |
|
||||
| 出口门 (a) | ❌ FCL 同机 spawn、双后端入世界、**杀 server** 产生干净 device-lost(本轮的驱动是 trace_replay,device-lost 由按 HOME 触发) | PLAN-P12 §3 |
|
||||
| 出口门 (b) | ❌ **另一台机器**的 client 经 TCP 在 Redmi 入世界,并记录 P6.5 必测数(本轮 client 是 `adb forward` 环路) | PLAN-P12 §3 |
|
||||
|
||||
## 4. 在跑
|
||||
|
||||
无。
|
||||
|
||||
## 5. 下一步
|
||||
|
||||
1. **真机复测**(ID-P12-13):重建 APK(控制修订 3),重跑七项检查,外加"推流中按 HOME:回调返回前 surface 已释放,并记耗时";在与原门同版本的 CMake/CTest 上复跑一次主机门。
|
||||
2. **出口门**:先 (b) 跨机 TCP 入世界 + P6.5 必测数,再 (a) FCL + 杀 server。
|
||||
3. **收尾**:写 `MG_Remote/CONTRACT-P12.md`;修 `notes/p12/gate.sh` 的 G1 符号比较(与基准不同源,会印出误导的 `added=N removed=N`);按惯例做一次收官审查。
|
||||
4. **阶段表行里的余项**(交接时在树上核过,均未做):in-process server 的 `unix:` 监听、DirectGLES `g_Display` / `g_Surface` / `g_Context` 去全局、cached-app freezer、多 context、FCL env 与 plugin 开关表接线、D8 窗口种类白名单。
|
||||
5. 其后按 [`ROADMAP.md`](ROADMAP.md):monolith 跑道 P8;IPC 跑道 P9 → P10 → P11;P6.5 残余(39 例 device 矩阵 + 必测数)与 P3b/P4b 余项并行;`CONTRACT-P7.md` §12 的记录债按阶段认领。
|
||||
|
||||
## 6. 阻塞 / 需要人
|
||||
|
||||
- 没有需要用户决策的阻塞。真机复测需要一台装有 NDK、连着 Redmi 的机器(审查轮所在的云容器没有)。
|
||||
- 已知环境敏感项:`IterationRPProgram203Scenario`(`integration-magma-full-split`)偶发红,按 ID-P7-22 / ID-P12-12 读作"既有、环境敏感"(钉 lavapipe ICD 即绿),不阻塞 P12。
|
||||
|
||||
## 7. 真机与证据
|
||||
|
||||
- 当前 APK `26.09.29b7b28-trace`(控制修订 3,与 P7 终局窗口的 p7w8 server 不兼容);设备 Redmi `2f7cbe2e`(Adreno 830),定频协议 [`devices/pin-verification-2026-09-07.md`](devices/pin-verification-2026-09-07.md)。
|
||||
- 真机证据 `notes/p12/device/`(`evidence-strip.png` 九格拼图,`trial-*/` 是按 head 归档的旧轮次);主机门日志 `notes/p12/gate/`;设备脚本 `notes/p12/android/`。
|
||||
**阻塞**:没有需要决策的事项。
|
||||
|
||||
@@ -1,134 +1,33 @@
|
||||
# 实测记录(摘要)
|
||||
# 实测(索引)
|
||||
|
||||
> 每个阶段只留头条数字与结论。完整的表、命令、协议与读法在对应 `notes/<阶段>/README.md` 的"实测"节,**小节编号 §N.x 原样保留**(代码与旧笔记里的 `MEASUREMENTS.md §7.2` 这类引用,按下面每节的"全文"链接去找)。
|
||||
>
|
||||
> **设备**:`35d0befa` = Xiaomi 24129PN74C(SM8750 / Adreno 830v2)与 `3B159D009VZ00000` = Oppo PLG110(MT6993 / Mali)用于 P0–P3a;`2f7cbe2e` = Redmi M332BF(SM8750 / Adreno 830v2)是 P4a 起唯一的 A/B 设备。定频、风扇与热协议见 [`devices/pin-verification-2026-09-07.md`](devices/pin-verification-2026-09-07.md);Redmi 的 GPU 钉频点随时期变过(2026-09-11 为 1050 MHz,P5b 时 1100 MHz,P6 起本板实测钳在 1050 MHz),跨钉频口径的活动不可比钟频,只有同会话配对可比。
|
||||
>
|
||||
> **口径**(用户 2026-09-08 起):性能对着 pull 臂**记录**、不作阻塞门;正确性门是硬门;主指标是**逐线程 CPU 时间**。三条读数纪律:`acc/draw` 是静态下界,判性能只看 CPU 时间;拒绝普查必须逐用例读私有日志(`ctest -V` 是假零);spawn 下 client 不推进帧窗口,窗口化字段只在推进帧的那条日志上有效。
|
||||
> 这一页只有结论和去向;完整的表、命令、协议与读法在各阶段 `notes/<阶段>/README.md` 的"实测"节。代码和旧笔记里的 `MEASUREMENTS.md §N` 按下表"§"列找到对应阶段(小节编号在那里原样保留)。
|
||||
|
||||
## 1. P0(2026-09-05)
|
||||
**口径**:性能只记录、不设门;看**逐线程 CPU 时间**,不看整机帧率。性能对比只用一台手机(Redmi `2f7cbe2e`,Adreno 830),定频、同热窗口、两臂交替跑([`guide/pin-verification-2026-09-07.md`](guide/pin-verification-2026-09-07.md))。
|
||||
|
||||
- spike A:Adreno 与 Mali 上,`untrusted_app` 进程 `fork`+`execve` `libMobileGLServer.so` 均 OK(同域、零 avc denial)。
|
||||
- spike B:**T0**(AHB BLOB 交接)是唯一在两设备、两后端上完整读写的档;T1 opaque fd 仅 Adreno;GLES memobj 在 Adreno 上 map 失败;host-pointer 在 Mali 只读。
|
||||
- 计数器基线:真机稳态动态 accessor 每 draw 6.5–9.3 次;Magma 在 MC 26.3 每帧重打包 331 KB 具名 UBO;同样 185 次纹理发射,Espryt 整 box 635 KB/帧 vs Magma rect 40 KB/帧。
|
||||
## 关键结论
|
||||
|
||||
全文 §1.1–§1.5:[`notes/p0/README.md`](notes/p0/README.md)。
|
||||
- **接口本身的代价**:单进程里改成"前端推、后端收",CPU 多约 6–12%(P2),已接受;顶点数组切换密集的场景更高(P3a,+27–30%)。
|
||||
- **拆成两线程**:起初游戏里只有 7–13 fps;优化后 103–106 fps(P5d);client 发完就走以后,重负载(渲染距离 32)下与单线程持平(P5e)。
|
||||
- **拆成两进程**:真机上与两线程成本持平;两者的 GL 线程 CPU 都比单线程少约 30%(后端工作挪到了另一线程 / 进程)(P6)。
|
||||
- **跨机**:电脑经 TCP 连手机,102 个集成用例全过;server 被杀后 133 ms、Wi-Fi 断开后约 5 s 干净报"设备丢失"(P6.5)。
|
||||
- **画面正确**:Vulkan 后端真机画面检查 36/36 通过,CTS 五块相对基线没有超过 0.5 个百分点的退步、没有新崩溃(P7);server 自有窗口上屏两后端 SSIM 1.0(P12)。
|
||||
|
||||
## 2. P1(lavapipe / llvmpipe)
|
||||
## 按阶段
|
||||
|
||||
- 规模:277 个 `pGLContext->` 站点 + 58 行非箭头、63 个 `PipeInputs` 字段、83 条填充点。
|
||||
- verify 通道找到 9 处缺填充行与 3 个"verb 内后端改前端"字段(→ push-on-mutation)。
|
||||
- 验收:pull 符号 0/0/0/0;单元 1485×3;`integration-verify` 818 零 `Fatal{`;79 例 retrace verify 零分歧。
|
||||
|
||||
全文 §2.1–§2.3:[`notes/p1/README.md`](notes/p1/README.md)。
|
||||
|
||||
## 3. P2(`738b289d`)
|
||||
|
||||
- 门:G1 4 处认定 resize、`.text` +160 B;单元 1566×3;`integration-gpu` 916 三臂;retrace push / verify 79/79。
|
||||
- DriverBench(桌面):边界 T1 = Espryt +322 / Magma +345 ns/draw;P2 自己的 tracker + CSO 比它替掉的 P1 残余填充便宜(−228 / −354 ns)。
|
||||
- 设备 A/B 的首轮是 -O0 构建(勘误,不作基准);Release 基准见 §4.4。
|
||||
|
||||
全文 §3.1–§3.5:[`notes/p2/README.md`](notes/p2/README.md)。
|
||||
|
||||
## 4. P3a(`fde5fda3`)
|
||||
|
||||
- 五部分门全绿:G1 0/0/0/0、`.text +0`;G5 十一函数逐字节相同;单元 1619×3;`integration-gpu` 958 五臂。
|
||||
- **Release 两机配对 A/B**:P2 边界 +6–12%(两机两后端一致);P3a 在 26.3 / sodium 上几乎不再加价,**在 rd12 上把差距推到 +27–30%**(VAO 切换密)。MC 26.3 在 Adreno 上 p99 +3.4%。
|
||||
- DriverBench:P3a 自己每 draw Espryt +699 / Magma +422 ns。
|
||||
|
||||
全文 §4.1–§4.5:[`notes/p3a/README.md`](notes/p3a/README.md)。
|
||||
|
||||
## 5. P4a(`8c458cd5`)
|
||||
|
||||
- 全门:G1 0/0/0/0;G5 17 区;单元 1785×3;`integration-gpu` 1117 七臂;verify 920;八族拒绝普查 0。
|
||||
- **Redmi 三臂 A/B**:P4a 自己在 Espryt 上 +3.4 – +5.7 pt(0.05–0.38 ms/帧),Magma 在噪声内(消费者门让它一条不发);MC 26.3 p99 +6.1%;上传形状 pull 与 push 逐项相同。
|
||||
- 桌面 DriverBench 分辨不出 P4a 这一档;可引用的是总边界 ≈ +1.1 µs/draw(Espryt)。
|
||||
|
||||
全文 §5.1–§5.4:[`notes/p4a/README.md`](notes/p4a/README.md)。
|
||||
|
||||
## 6. P5(`e61d0012` → `37fc4fdb`)
|
||||
|
||||
- joint:OpenRA inproc 2/2 SSIM 1.0;`integration-split` 21/21;E1 barrier 阴性对照 14/14 全红;E2 丢 758 条 `DrawVbo` 后 SSIM 0.000036(丢 clear 被 overdraw 掩盖,不能作控制)。
|
||||
- 普查:inproc 1149 项中 27 个 wrong-answer(后续去向见 §7.2 末:2026-09-22 重跑时 24 绿、3 条变为具名停止)。
|
||||
- `maxrec` = 784 B(默认 cap 4 MiB 的 0.019%);ledger SEG_CMD 8 MiB / SEG_STAGE 32 MiB / SEG_REPLY 16 MiB / SEG_EVENT 256 KiB。
|
||||
- Redmi 四臂:split 臂全部停在索引 draw,barrier tax 未测。
|
||||
|
||||
全文 §6.1–§6.5:[`notes/p5/README.md`](notes/p5/README.md)。
|
||||
|
||||
## 7. P5b(主机门 `348d22a4`,设备 `82683d4a`)
|
||||
|
||||
- 主机:`integration-split` 107/107;合并 inproc 普查 1267 项 811 / 203 / 62 / 191,旧 passes 零回退;79 trace 72 / 6 / 1。同名重跑 @ `3c80cd62`(2026-09-22):27 个 wrong-answer → 24 绿 / 3 具名停止;1267 项 → 1051 / 172 / 37 / 7(非绿全是钉了非默认 `MOBILEGL_PIPE_PUSH` 的对照臂);trace 77 / 0 / 0。
|
||||
- **Redmi**:正确性 8/8;**barrier tax**(split − push 逐线程 CPU p50)+5.9% – +18.2%。
|
||||
|
||||
全文 §7.1–§7.4:[`notes/p5b/README.md`](notes/p5b/README.md)。
|
||||
|
||||
## 8. P5c(`11ac3de6..b88e8487`)
|
||||
|
||||
- 守卫开启下 unit 2187/2187、`integration-split` 111/111,每层守卫 red-once;纹理 `0xDD` audit 在两条 in-world trace 零 Fatal。
|
||||
- `rsp` 按帧:bsl 948.5/帧、complementary 1591.9/帧,残留全为对象类 15 行(值类 0)。
|
||||
- G1:3 个认定 resize、`.text` −16 B。Redmi 四臂复测未做。
|
||||
|
||||
全文:[`notes/p5c/README.md`](notes/p5c/README.md)。
|
||||
|
||||
## 9. P5d(Redmi,FCL + Minecraft 26.3,VD12,DirectGLES)
|
||||
|
||||
| 臂 | fps p50 | client ms/帧 | apply ms/帧 |
|
||||
| § | 阶段 | 一句结论 | 全文 |
|
||||
|---|---|---|---|
|
||||
| inproc 起点(`56a77348`) | 64.3 | 15.03 | 12.42 |
|
||||
| inproc 三轮(`1f8de61b`) | 103-106 | 9.2 | 7.0-7.4 |
|
||||
| monolith | 115(120 Hz 封顶)/ 206(未封顶一次) | 5.0-6.2 | — |
|
||||
|
||||
全文:[`notes/p5d/README.md`](notes/p5d/README.md)(报告 [`notes/p5d/P5D-INPROC-PERFORMANCE.md`](notes/p5d/P5D-INPROC-PERFORMANCE.md))。
|
||||
|
||||
## 10. P5e 设备验证一轮(`3cc4e1ec`)
|
||||
|
||||
- `integration-gpu` 两条运行时臂从 1157/1294(137 SEGFAULT,当时没有任何门会看到)修到 1294/1294——此后它是常设步。
|
||||
- 设备:monolith 修复前 65 s 后进程消失,修复后两臂都存活进世界。
|
||||
|
||||
全文:[`notes/p5e/README.md`](notes/p5e/README.md)。
|
||||
|
||||
## 11. P5e run-ahead 武装后的设备矩阵(`25fba0d5`)
|
||||
|
||||
- VD12(~850 draws/帧,CPU 定频):run-ahead 相对 lockstep client −18%、apply −33%、fps +6%;monolith 与 run-ahead 都撞 120 Hz 上限,只有 lockstep 撞不到。
|
||||
- **VD32(~3550 draws/帧)**:monolith 58.5 / run-ahead **59.9** / lockstep 35.5 fps(中位数)——**inproc 与 monolith 齐平**;run-ahead 相对 lockstep p50 +69%、client CPU −40%。两侧不平衡(apply 11–12 ms vs client 15.7 ms)是下一个优化方向(开放问题 18)。
|
||||
- 未定频的逐线程 CPU 不能跨臂比较(walt governor 会把 policy0 从 2745 拉到 748 MHz)。
|
||||
|
||||
全文 §11、§11.1:[`notes/p5e/README.md`](notes/p5e/README.md)。
|
||||
|
||||
## 12. P6 出口测量(2026-09-22,Redmi + WSL)
|
||||
|
||||
- **门 7 配对 A/B**(rd12 in-world,~1471 draws/帧,两会话各一次 reboot):inproc vs spawn 的 client CPU p50 **−0.15% / +0.16%**,在臂内散布内,判读 **tie**;monolith 约贵 30%。
|
||||
- **门 8①** 门铃:client 121.1 waits/帧两传输逐字相同;server park 率 0.042–0.06%;socket 阻塞读既不更便宜也不更贵。
|
||||
- **门 8②③**(inproc 桌面 retrace):`SEG_STAGE` 稳态中位数 OpenRA 130.8 KB/帧、rd12 816.2 KB/帧;分块后记录/帧 211 / 7 497;`maxrec=1808`,默认 8 MiB chunk 预算从未被打到。负控 `STAGE_MB=1` 下 `seg` 运行总量逐字节不变。
|
||||
|
||||
全文 §12.1–§12.7:[`notes/p6/README.md`](notes/p6/README.md)。
|
||||
|
||||
## 13. P3b/P4b wave 2-D 包 D2(`cf7ca59f`)
|
||||
|
||||
- 上传形状金标四臂(monolith / inproc / spawn / tcp)同为 `emit=9 box=9 rect=0 jobs=9`;零补丁 red-once:`MOBILEGL_ESPRYT_DISABLE_UNPACK_RING=1` 翻成 `box=3 rect=6 jobs=183`。
|
||||
- Mali 帧时增量不可调度(无 Mali 设备),按 Adreno 记录。
|
||||
|
||||
全文 §13.1–§13.3:[`notes/p34b/README.md`](notes/p34b/README.md)。
|
||||
|
||||
## 14. P6.5 第一波(`fe28bdb5`)
|
||||
|
||||
- 主机 unit 2399、integration-tcp loopback 102/102;**Redmi device-102 102/102,逐条 run-ahead ARMED**;代表性 retrace OpenRA 1.0、startup 0.999999511、in-world 0.999995438。
|
||||
- 真实远端 kill **133 ms**、真实 Wi-Fi 中断 **5053 ms** 闩住 device loss。G1 0/0/0/0。
|
||||
- 未测:39 例 device golden 矩阵、逐帧 `kWaitReply` / RTT、`PRESENT_CREDIT` 1/2/3、tcp-vs-spawn 逐线程 CPU。
|
||||
|
||||
全文:[`notes/p65/README.md`](notes/p65/README.md)(`performance.md`、`validation-status.md`、`wire-and-validation.md`)。
|
||||
|
||||
## 15. P7(2026-09-23)
|
||||
|
||||
- 门 3(终局窗口 3,`p7w8-78e71f2b`)真机 ssim **36/36**:33 例四臂逐位同,三例 Iris 按分布判据(ID-P7-62),OpenRA 四臂 1.000000。
|
||||
- 门 5 CTS 五块 AFTER:shader-image +4.35 pp、texture +0.96 pp、其余持平,0 新 crash。
|
||||
- Magma 两进程车道 split / spawn / tcp 102 / 81 / 71;M2 后主机 bsl spawn server 825 → 546 MiB、映射 41k → 5.8k。
|
||||
|
||||
全文:[`notes/p7/README.md`](notes/p7/README.md)(证据 `notes/p7/device-window-1/`、`device-window-2/`)。
|
||||
|
||||
## 16. P12 子集(2026-09-24,未收官)
|
||||
|
||||
- 真机七项检查(审查轮之前的 `29b7b284`):Espryt / Magma 上屏 OpenRA ssim 1.000000,同进程第二会话 0.999999511;失窗后新会话 1.0;离屏 service 1.0。审查轮之后未复测。
|
||||
- 主机门(审查轮)unit 2605/2605;G1 同机前后 `.text` 一致、符号 0/0。
|
||||
|
||||
全文:[`notes/p12/README.md`](notes/p12/README.md)、[`notes/p12/PLAN-P12.md`](notes/p12/PLAN-P12.md) §2。
|
||||
| 1 | P0 | 能从应用进程拉起第二个进程;跨进程共享 GPU 内存只有一条路在所有设备上可用 | [`notes/p0`](notes/p0/README.md) |
|
||||
| 2 | P1 | 逐 draw 核对推送状态,79 条 trace 零分歧 | [`notes/p1`](notes/p1/README.md) |
|
||||
| 3 | P2 | 桌面基准:新 tracker 比它替掉的旧填充更便宜 | [`notes/p2`](notes/p2/README.md) |
|
||||
| 4 | P3a | 两机真机 Release:接口代价 +6–12%,VAO 切换密集的场景 +27–30% | [`notes/p3a`](notes/p3a/README.md) |
|
||||
| 5 | P4a | 换到 Redmi;P4a 自己只加 3–6 个百分点 | [`notes/p4a`](notes/p4a/README.md) |
|
||||
| 6 | P5 | 两线程第一帧画对;真机两线程臂还跑不完 | [`notes/p5`](notes/p5/README.md) |
|
||||
| 7 | P5b | 真机两线程跑通四条游戏 trace;两线程额外代价 +6–18% | [`notes/p5b`](notes/p5b/README.md) |
|
||||
| 8 | P5c | 两线程之间零共享内存后,全部测试仍绿 | [`notes/p5c`](notes/p5c/README.md) |
|
||||
| 9 | P5d | 游戏内 7–13 → 103–106 fps,client CPU 15.0 → 9.2 ms/帧 | [`notes/p5d`](notes/p5d/README.md) |
|
||||
| 10–11 | P5e | 发完就走比每条都等快 69%;重负载下与单线程持平 | [`notes/p5e`](notes/p5e/README.md) |
|
||||
| 12 | P6 | 两进程与两线程持平;每帧数据量与记录数首次实测 | [`notes/p6`](notes/p6/README.md) |
|
||||
| 13 | P3b/P4b | 纹理上传形状在四种拓扑下完全一致 | [`notes/p34b`](notes/p34b/README.md) |
|
||||
| 14 | P6.5 | 跨机 TCP 102/102;断线约 5 s 检测到 | [`notes/p65`](notes/p65/README.md) |
|
||||
| 15 | P7 | 真机画面 36/36;server 内存无界增长收住(主机 825 → 546 MiB) | [`notes/p7`](notes/p7/README.md) |
|
||||
| 16 | P12 | 上屏两后端 SSIM 1.0;审查修复后待真机复测 | [`notes/p12`](notes/p12/README.md) |
|
||||
|
||||
@@ -1,78 +1,30 @@
|
||||
# MGPipe:MobileGL 前后端拆分
|
||||
|
||||
> **状态(2026-09-24)**:P0 … P7 已收官;P6.5 第一波已落地;**当前阶段 P12**「server 自有屏幕窗口」子集,已实现、未收官——[`CURRENT_STAGE_PROGRESS.md`](CURRENT_STAGE_PROGRESS.md)。阶段表见 [`ROADMAP.md`](ROADMAP.md)。
|
||||
>
|
||||
> 性能纪律(2026-09-08 起):逐线程 CPU 对着 pull 臂**只记录**、不作阻塞门;专门的优化阶段排在路线图推完之后。
|
||||
**做什么**:在 MobileGL 的前端(处理应用的 GL 调用)和后端(调用 GLES / Vulkan 驱动)之间立一份显式接口——前端把状态变化**推**给后端,后端不再直接读前端的内存。有了这份接口,前后端可以放在两个线程、两个进程,甚至两台机器上运行。
|
||||
|
||||
## 是什么
|
||||
**做到哪了**(2026-09-24):接口与拆分已完成到"两个进程、可经 TCP 跨机"。当前在做 **P12:让 Android 上的 server 自己开窗口,把画面直接显示到屏幕上**——功能已实现,还差真机复测和两项验收。→ [`CURRENT_STAGE_PROGRESS.md`](CURRENT_STAGE_PROGRESS.md)
|
||||
|
||||
MGPipe 是 MobileGL 前端(`MG_State` + `MG_Impl`)与后端(`MG_Backend`:Espryt = DirectGLES、Magma = DirectVulkan)之间的一份**显式接口**:gallium 形状、句柄寻址、只推不拉。它取代后端每 draw 直接读 `MG_State::pGLContext` 的做法,让后端拥有自己的状态机,并在此之上把前后端拆到**两个线程**(`inproc`)、**两个进程**(`spawn`),乃至经 TCP 的**两台机器**。
|
||||
|
||||
接口本身是可独立交付的产物:即使 IPC 永不上线,`inproc` 就是 monolith 的渲染线程。
|
||||
|
||||
## 架构(一段)
|
||||
## 一张图
|
||||
|
||||
```
|
||||
应用 GL 调用
|
||||
→ MG_Impl(GL 语义、错误、shadow)
|
||||
→ MG_Impl/Pipe/Tracker:在每条 verb 之前 validate,把变化推成 MGPipe 调用
|
||||
→ MGPipeScreen / MGPipeContext(两张函数指针表,单一真相源 PipeCalls.def)
|
||||
monolith:直调后端 split:发射器写记录 → 数据面(共享段 / 字节流)→ server 的 applier(apply 线程)
|
||||
→ server 对象表(按 {slot, gen} 句柄索引)+ PipeInputs(后端被推送的状态块)
|
||||
→ MG_Backend(Espryt / Magma),两个后端的 ring / pool / memo / lowering 原样不动
|
||||
← MGPipeCallbacks(9 个具名反向回调 + 1 个正向终止符,split 下走 SEG_EVENT)
|
||||
应用的 GL 调用
|
||||
→ 前端 MG_Impl:处理 GL 语义;在每条"做事的命令"(draw、clear…)之前,把状态变化整理成记录
|
||||
→ MGPipe 接口(两张函数指针表,由调用目录 PipeCalls.def 生成)
|
||||
→ 三选一:直接调用(单线程)│ 队列 → 另一个线程 │ 队列 → 另一个进程 / 另一台机器
|
||||
→ 后端 MG_Backend:Espryt(GLES)/ Magma(Vulkan),按记录更新自己的状态并调用驱动
|
||||
← 反向消息(错误、GPU 写回、窗口尺寸)按顺序回到前端
|
||||
```
|
||||
|
||||
三种拓扑共用**同一份后端实现**:`monolith`(默认)、`inproc`(同进程两线程,CI 主验收;client 在 draw 路径上 run-ahead——Espryt 自 P5e、Magma 自 P5f 之后)、`spawn`(两进程;控制面 `fork` / `unix:` / `tcp://` × 数据面共享段 / 字节流,可跨机)。设计全文见 [`ARCHITECTURE.md`](ARCHITECTURE.md)。
|
||||
## 文档导航
|
||||
|
||||
## 构建与运行
|
||||
|
||||
四个 flavour:`build-linux`(pull,默认)、`build-push`(`-DMOBILEGL_PIPE_PUSH=ON`)、`build-verify`(+`MOBILEGL_PIPE_VERIFY`,影子比对)、`build-split`(+`MOBILEGL_BUILD_DISAGGREGATED` +`MOBILEGL_BUILD_DISAGGREGATED_INPROC`)。split 构建必须显式选拓扑,不设时是 monolith 对照臂:
|
||||
|
||||
```text
|
||||
MOBILEGL_TRANSPORT=inproc MOBILEGL_ITEST_REQUIRE_GPU=1 ctest --test-dir build-split -L integration-split --output-on-failure
|
||||
ctest --test-dir build-split -L 'integration-(spawn|tcp)' --output-on-failure # 条目自带 spawn / tcp 环境与私有日志
|
||||
```
|
||||
|
||||
常用旋钮(全表见 [`ARCHITECTURE.md`](ARCHITECTURE.md) 附 A):`MOBILEGL_TRANSPORT`(拓扑)、`MOBILEGL_IPC_CONTROL` / `MOBILEGL_IPC_DATA`(控制面 / 数据面)、`MOBILEGL_IPC_TOKEN`(非 loopback 必需)、`MOBILEGL_IPC_RUN_AHEAD`、`MOBILEGL_IPC_STAGE_MB`(默认 32,1/4 是内容分块预算)、`MOBILEGL_IPC_SURFACE=server`(使用 server 自有窗口,P12)、`MOBILEGL_PIPE_STATS=1`(边界计数器)。
|
||||
|
||||
Android:三份 APK flavour(pull / push / split,Gradle 属性 `mobilegl.pipePush`、`mobilegl.buildDisaggregated`、`mobilegl.buildDisaggregatedInproc`)。设备侧 server 有离屏的前台 Service 与上屏的 `MobileGLDisplayActivity` 两种形态。设备 A/B 只用 Redmi `2f7cbe2e`,定频协议见 [`devices/pin-verification-2026-09-07.md`](devices/pin-verification-2026-09-07.md)。
|
||||
|
||||
## 文件地图
|
||||
|
||||
| 文件 | 内容 |
|
||||
| 想知道 | 看 |
|
||||
|---|---|
|
||||
| [`CURRENT_STAGE_PROGRESS.md`](CURRENT_STAGE_PROGRESS.md) | 当前阶段:落地了什么、门、下一步、阻塞(摘要;细节链到 `notes/<当前阶段>/`) |
|
||||
| [`ROADMAP.md`](ROADMAP.md) | 纪律、两条跑道、阶段表(每阶段一行摘要 + 细节链接)、里程碑、开放的债务与问题 |
|
||||
| [`ARCHITECTURE.md`](ARCHITECTURE.md) | 已定稿的设计(§1–§16)、阶段落地形状索引(§17)、开关(附 A)与计数器(附 B) |
|
||||
| [`MEASUREMENTS.md`](MEASUREMENTS.md) | 每阶段的头条实测数字,全文在各阶段 notes |
|
||||
| [`notes/`](notes/README.md) | **阶段细节**:每阶段一个目录,`README.md` 是该阶段的汇总(阶段表原行、逐门数字、落地形状),其余是计划、契约草稿、裁定、审计、报告与证据 |
|
||||
| [`devices/`](devices/pin-verification-2026-09-07.md) | 设备定频档案与核验(跨阶段共用) |
|
||||
| 现在做到哪、下一步 | [`CURRENT_STAGE_PROGRESS.md`](CURRENT_STAGE_PROGRESS.md) |
|
||||
| 设计要点与各部分设计 | [`ARCHITECTURE.md`](ARCHITECTURE.md) → [`design/`](design/) |
|
||||
| 分几个阶段、每个阶段做了什么 | [`ROADMAP.md`](ROADMAP.md) → [`notes/<阶段>/`](notes/README.md) |
|
||||
| 性能与验证的关键数字 | [`MEASUREMENTS.md`](MEASUREMENTS.md) |
|
||||
| 怎么构建、运行、测试 | [`guide/build-and-run.md`](guide/build-and-run.md) |
|
||||
| 代码在哪 / 术语 / 工程纪律 | [`guide/code-map.md`](guide/code-map.md)、[`guide/glossary.md`](guide/glossary.md)、[`guide/discipline.md`](guide/discipline.md) |
|
||||
| 各阶段的原始计划、裁定、报告、证据 | [`notes/`](notes/README.md) |
|
||||
|
||||
代码地图:
|
||||
|
||||
| 路径 | 作用 |
|
||||
|---|---|
|
||||
| `MobileGL/MG_Pipe/` | 调用目录与各 `.def` 表、payload / 句柄 / 回调头、`PipeApply`、`PipeRoute`、`generated/*.inc`(G1–G8 产物,提交进树) |
|
||||
| `MobileGL/MG_Impl/Pipe/` | Tracker、`PipeFill`、`SlotAllocator`、`CsoCache`、`CompositeResolver`、`ResourceTracker`、各族 `*Emit.h` |
|
||||
| `MobileGL/MG_Backend/MGPipe/` | `PipeInputs`(后端被推送的状态块) |
|
||||
| `MobileGL/MG_Remote/` | 仅 `MOBILEGL_BUILD_DISAGGREGATED`:`Protocol/`、`Transport/`(`ITransport` / `ILink` 两轴)、`Wire/`、`Client/`、`Server/`;各阶段 wire 契约 `CONTRACT-*.md` |
|
||||
| `scripts/` | `gen_pipe.py`(G1–G7)、`gen_pipe_field_ownership.py`(G8)、`gen_pipe_dirty_surface.py`、`gen_protocol.py`、`symbol_report.py`(G1)、`check_include_closure.py`、`check_doc_citations.py`(本目录 `file:line` lint)、`link_ratchet.py`、G5 保护区脚本、`ci/` |
|
||||
| `MobileGL/MG_Util/Metrics/PipeStats.{h,cpp}` | 边界计数器,`MOBILEGL_PIPE_STATS=1` 开启 |
|
||||
| `MobileGL/MG_Test/Pipe/`、`MG_Test/Wire/`、`MG_IntegrationTest/Harness/*Peek` | 目录 / tracker / emitter 单元、wire 与 remote 单元、白盒断言 |
|
||||
| `tools/trace_replay/`、`tools/device_bench/` | retrace 跑器(含 TCP 矩阵)、设备定频与配对 A/B 工具 |
|
||||
|
||||
## 术语
|
||||
|
||||
- **client / server**:前端角色 / 后端角色;`inproc` 下是两个线程,`spawn` 下是两个进程(可在两台机器上)。
|
||||
- **verb**:会让 server 做事的命令(draw、dispatch、clear、blit、readback、XFB 跨度、query、纹理操作);推送只发生在 verb 之前的 validate 时刻。
|
||||
- **class A / B / C**:后端槽的三类——A 从 caps mirror 本地回答,B 发射记录,C 具名拒绝(`Fatal{UnmigratedVerb}`),永不回落到 monolith。
|
||||
- **CSO**:常量状态对象(render state、vertex elements、sampler、sampler view、shader),client 侧内容寻址,server 侧按句柄缓存。
|
||||
- **Track V / Track H**:值类读点的迁移(整块 POD 过线)/ 对象类读点的迁移(`SharedPtr<前端对象>` → 句柄)。
|
||||
- **lockstep / run-ahead**:每条 verb 等 server 应用完 / client 发布完即走,只有 barriered 记录才等(P5e)。
|
||||
- **`MGGen`**:server 私有的"我重铸了驱动对象"纪元,永不过线;与句柄里的 client 世代严格分开。
|
||||
- **G1 / G2 / G5 / G14**:pull 符号恒等 / pull == push 测试名 / 保护区字节一致 / 测试名只增不删。**barrier tax**:split − push 的逐线程 CPU 增量。
|
||||
|
||||
## 历史
|
||||
|
||||
本目录最早是一份 328 KB 的实施计划加 135 KB 的设计竞赛与评审记录;设计定稿后只保留设计与架构,评审与早期草案在 git 历史(`8b31de2f`、`1794ac94`、`8349babe`、`87ee17c6`),以及 `notes/recovered/` 里捞回的独有稿。2026-09-24 再次整理:四份索引文档只留摘要,各阶段细节(阶段表原行、逐门数字、落地形状、当时的进度页)移入 `notes/<阶段>/README.md`,散在本层的阶段报告与计划(P5d / P5e / P5f / P6)移入各自阶段目录;整理前的版本见 `1fb18d9`。
|
||||
整理记录:2026-09-24 两次整理——顶层只留索引,设计正文拆到 `design/`,上手与参考放 `guide/`,阶段细节放 `notes/<阶段>/`;整理前的版本见提交 `1fb18d9`。更早的设计竞赛与评审稿在 git 历史(`8b31de2f`、`1794ac94`、`8349babe`、`87ee17c6`)。
|
||||
|
||||
@@ -1,107 +1,45 @@
|
||||
# MGPipe 路线图
|
||||
# 路线图(索引)
|
||||
|
||||
> **状态(2026-09-24)**:P0 … P7 已收官(含 P5c–P5f 与 P6);P6.5 第一波已落地(残余交 CI / 后续);Ph 的必需小件随 P7 落地;P3b/P4b 余项并行。**当前阶段是 P12**「server 自有屏幕窗口」子集:已实现并入 `feat/disaggregated`,未收官——见 [`CURRENT_STAGE_PROGRESS.md`](CURRENT_STAGE_PROGRESS.md)。
|
||||
>
|
||||
> 本页只留摘要。每个阶段的完整阶段表行、逐门数字、设计与报告在 `notes/<阶段>/README.md`(下表"细节"列);设计见 [`ARCHITECTURE.md`](ARCHITECTURE.md),实测摘要见 [`MEASUREMENTS.md`](MEASUREMENTS.md)。
|
||||
> **2026-09-24**:P0 到 P7 已完成;当前阶段 **P12**(server 自己开窗口上屏)功能已实现、待验收。每个阶段的计划、验收结果、实测与报告在 `notes/<阶段>/`(点阶段名进入)。
|
||||
|
||||
## 终局
|
||||
## 目标
|
||||
|
||||
client 与 server 可以在**不同机器、不同 OS / 架构**上经 **TCP** 连接;同机的 `spawn`(AF_UNIX + 共享段)保留为本机形态。传输栈是控制面(`ITransport`)× 数据面(`ILink`)两根独立可选、握手协商、可混搭的轴,上层不按链路种类分支([`ARCHITECTURE.md`](ARCHITECTURE.md) §11.9)。这一改判(2026-09-22,取代此前 AVF pVM / `AF_VSOCK` 的设想)让 P6.5 成为 IPC 跑道的关键路径,并让 Ph(不可信对端加固)成为非 loopback 监听的前提。
|
||||
client(跑应用的一方)与 server(跑驱动的一方)可以在**不同机器、不同系统**上经 TCP 相连;在同一台机器上则用两个进程 + 共享内存。传输分控制面与数据面两层,可各自选择、混搭。
|
||||
|
||||
## 通用纪律(每个 commit)
|
||||
## 两条路线
|
||||
|
||||
- 默认 ALL target 必须完整构建;禁止提交热路径插桩(CI grep 门)。
|
||||
- **每个门必须能因它存在的理由变红**:带阴性对照并真跑过一次红(R-16);公共 GL 看不见的改白盒断言。
|
||||
- 正确性门是硬门;**性能对着 pull 臂只记录、不作阻塞门**(用户 2026-09-08),但数字必须真的采到。专门的优化阶段排在路线图推完之后。
|
||||
- 每阶段出口跑一次五部分门([`ARCHITECTURE.md`](ARCHITECTURE.md) §13.2);性能判据是**逐线程 CPU 时间**;设备对比走 reboot-clean + 同热窗口配对 A/B,只用 Redmi `2f7cbe2e`([`devices/pin-verification-2026-09-07.md`](devices/pin-verification-2026-09-07.md))。
|
||||
- G1:pull 构建的符号集与 `.text` 对基线恒等(每阶段 0/0/0/0);G2:pull 与 push 的 ctest 名集合相同;G14:测试名只增不删;G5:保护区字节一致。
|
||||
- **拆分不借机顺手修 `dev` 的 bug**:发现的 `dev` 侧缺陷记成独立条目、独立 PR。
|
||||
- 过程(用户 2026-09-16,ID-66):不过度验证;每个阶段收官由异模型族审一次,发现进入下一阶段首轮;实现方与审查方错开模型族。
|
||||
- **单进程路线**:先把接口建起来,让单进程版本也受益(后端拥有自己的状态、可以挪到渲染线程)。P0 → P0.5 → P1 → P2 → P3a → P4a → P3b/P4b → P7 → P8 → P13。
|
||||
- **拆分路线**:在接口之上逐步拆开前后端。P5 → P5b → P5c → P5d → P5e → P5f → P6 → P6.5 → Ph → **P12** → P9 → P10 → P11。
|
||||
|
||||
## 两条跑道
|
||||
## 阶段一览
|
||||
|
||||
- **monolith 跑道**:P0 → P0.5 → P1 → P2 → P3a → P4a → P3b/P4b → P7 → P8 → P13。每段可独立交付、可随时中止;接口本身即使 IPC 永不上线也有价值。
|
||||
- **IPC 跑道**:P5 → P5b → P5c → P5d → P5e → P5f → P6 → P6.5 → Ph → **P12** → P9 → P10 → P11。顺序的理由:先有完整的独立渲染线程(P5b),再让 `inproc` 成为只经 wire 交换的诚实两角色(P5c / P5f),把 CPU 代价压到可接受(P5d)并退役 draw path 的 lockstep(P5e),此时 P6 才只是传输替换;之后传输栈两轴化并跨机(P6.5)、能安全接受外来连接(Ph)、server 成为拥有显示的常驻应用(P12);异步反向通道与节奏(P9、P10)建在 P6.5 的消息式 reply 之上;同机 ≥16 MiB 采纳(P11)最后。
|
||||
|
||||
## 阶段
|
||||
|
||||
| 阶段 | 状态 | 摘要 | 细节 |
|
||||
|---|---|---|---|
|
||||
| **P0** 卫生、度量、门、骨架 | ✅ 2026-09-05 | 边界计数器、调用目录 + 生成器 G1–G7 + CI `pipe-gates`、`MG_Remote` 骨架;spike A(应用进程 exec 第二个原生可执行文件)两台设备成立;spike B:只有 T0(AHB 交接)在两设备两后端完整读写 | [`notes/p0`](notes/p0/README.md) |
|
||||
| **P0.5** 值头与制品头抽取 | ✅ `5d99ee43` | `MGPipeValueTypes.h`、`ProgramArtifacts.h`、include 闭包门、符号报告;`.text` 不变 | [`notes/p05`](notes/p05/README.md) |
|
||||
| **P1** `PipeInputs` 与 verify harness | ✅ | 277 + 58 处读点换成 `MGB_CTX`;逐 verb 填充与 poison;G4 影子比对;verify 818 / 79 trace 零分歧 | [`notes/p1`](notes/p1/README.md) |
|
||||
| **P2** 渲染状态 CSO + Track H 首片 | ✅ `738b289d` | Tracker、render-state CSO + 动态状态、`CsoCache`、残余块 1248 → 8、11 条 memo 删除;**GO/NO-GO:继续**(Release 边界代价 +6–12%,已接受) | [`notes/p2`](notes/p2/README.md) |
|
||||
| **P3a** 句柄 wave 1:buffer、VAO | ✅ `fde5fda3` | `resource_*` / vertex-input 句柄化;五部分门全绿、G1 0/0/0/0;1 天 | [`notes/p3a`](notes/p3a/README.md) |
|
||||
| **P4a** 句柄 wave 2:FBO / 纹理 / sampler / program | ✅ `8c458cd5` | 十四族接线、六种 kind 重键、`CompositeResolver`;全门绿;1 天;Redmi 上 P4a 自己 +3.4–5.7 pt(Espryt) | [`notes/p4a`](notes/p4a/README.md) |
|
||||
| **P5** 传输 + inproc applier + 发射表 | ✅ `ff2994d9..37fc4fdb` | `InProcessTransport`、四种 flavour、lockstep verb barrier、persistent-map 块推送、G8;OpenRA inproc SSIM 1.0;收尾全门停在 E3(a),审查发现转 P5b | [`notes/p5`](notes/p5/README.md) |
|
||||
| **P5b** inproc verb 迁移 | ✅ `37fc4fdb..82683d4a` | 43 槽 + sync / blit / mip 迁移,发射表 A=2 / B=54 / C=15;`integration-split` 107/107;**Redmi 首次以独立 apply 线程渲染四条目标 trace**,barrier tax +5.9–18.2% | [`notes/p5b`](notes/p5b/README.md) |
|
||||
| **P5c** 共享内存读点归零 | ✅ `11ac3de6..b88e8487` | 审计出的 59 处直接访问归零:纹理 staged shadow、`SEG_EVENT` 反向通道、按句柄解析、控制记录、角色守卫;契约 `CONTRACT-P5C.md` | [`notes/p5c`](notes/p5c/README.md) |
|
||||
| **P5d** inproc 性能专项 | ✅ `1f8de61b` | VD12 Minecraft 上 inproc 7-13 → 103-106 fps,client 线程 15.0 → 9.2 ms/帧 | [`notes/p5d`](notes/p5d/README.md) |
|
||||
| **P5e** 退役 draw path 的 lockstep(run-ahead) | ✅ 2026-09-19(附具名未决:E1 对照需重新定义,开放问题 24) | 规则 F、`WaitClass` 列、present credit;十二包三波;strict 179/179;VD32 下 inproc 与 monolith 齐平,run-ahead 相对 lockstep p50 +69% | [`notes/p5e`](notes/p5e/README.md) |
|
||||
| **P5f** 一切状态上 wire | ✅ 2026-09-20 | 字段归属 41 / 6 / **0 BARRIER_PULLED** / 16;双块演练;双后端逐帧 `rsp=0`;Redmi 六个 clean-boot 臂通过。后续:Magma run-ahead(`194382c9`)、CI / inproc 功能补齐(`e1bf3677`,2026-09-21) | [`notes/p5f`](notes/p5f/README.md) |
|
||||
| **P6** spawn transport | ✅ 2026-09-22 | 十包(a6 … t6);`integration-spawn` 102/102;device-lost 闩与 `Session::Fail` 漏斗;门 7 真机配对 A/B **tie**(inproc vs spawn ±0.16%);门 8 三个强制性能数已采 | [`notes/p6`](notes/p6/README.md) |
|
||||
| **P6.5** 传输栈两轴化 + 跨机 | 第一波已落地(`fe28bdb5`),未宣布收官 | 全 TCP 控制 + `StreamLink` 数据,WSL client ↔ Redmi server;`wireFingerprint` / `Refuse` / 令牌;device-102 102/102 全 run-ahead ARMED。**残余**:39 例 device golden 矩阵与链路必测数;第二波(同机 TCP + 共享段)未开工 | [`notes/p65`](notes/p65/README.md) |
|
||||
| **Ph** 不可信对端加固 + 配对 | 必需小件已落地(随 P7 F / F2,ID-P7-57) | 每会话闩、常量时间令牌、`dataNonce` 数据面绑定、预认证上限与退避、`SEG_EVENT` 弃投闩、fuzz 三臂进 CI;OQ-21 裁定令牌足够、不做 TLS | [`notes/p7`](notes/p7/README.md)(Ph 行) |
|
||||
| **P3b / P4b** Espryt 深化 | D1 / D2 / D3 已落地,余项并行 | XFB 两洞、回调 10 → 9、上传形状成门、view 陈旧缺陷修复。余:R-3 另一半、`texture_view.coherency` 的 split 读数、CTS AFTER | [`notes/p34b`](notes/p34b/README.md) |
|
||||
| **P7** DirectVulkan(Magma)全量迁移 | ✅ 2026-09-23(ID-P7-63) | §8 九项全绿:Magma 两进程三臂、`@P7` 拒绝归零、`StateObjectDeathOps`、verify × split、门 3 真机 ssim 36/36、门 5 CTS 五块 ≤ 0.5 pp;链接棘轮 186 → 171 | [`notes/p7`](notes/p7/README.md) |
|
||||
| **P12** Android 生产窗口路径 | **当前**:子集已实现、未收官 | server 自建窗口上屏、`WindowKind::ServerOwned` 无头 client、失窗 device-lost;真机七项检查已过(审查轮后待复测)。出口门 (a) FCL + 杀 server、(b) 跨机 TCP 入世界均未打 | [`notes/p12`](notes/p12/README.md) |
|
||||
| **P8** emulation 下放 + 索引宿主镜像 + 协议广度 | 待排 | client 侧 `HostResolve`、multi-draw client indices、CopyImage 镜像、mip CPU 回退、`texture-remint-pull` 下放;索引镜像按真实消费者重裁 | [`notes/p8`](notes/p8/README.md) |
|
||||
| **P9** 反向通道 | 待排(P6.5 之后) | 异步 reply、chunked readback、PACK-PBO fire-and-forget、武装剩余回调、XFB 命名空间 | [`notes/p9`](notes/p9/README.md) |
|
||||
| **P10** sync / query / present 节奏 | 待排 | 轮询入口成门铃点、逐 fence 退休、`PRESENT_CREDIT > 1` 实测、roundtrip 计数器、`SetSwapInterval` | [`notes/p10`](notes/p10/README.md) |
|
||||
| **P11** persistent map 与 ≥16 MiB 采纳 | 待排(同机臂专属) | T0 / T1 档、`SEG_ADOPT`、`gPipeInputs` 版本化 | [`notes/p11`](notes/p11/README.md) |
|
||||
| **P13** 退役 pull 路径 | 待排 | 删 `MGB_CTX` / `MOBILEGL_PIPE_PUSH` / legacy memo 臂 / 残余块;recorder;模块边界 | [`notes/p13`](notes/p13/README.md) |
|
||||
|
||||
CTS 周转单独计价(`gl44to46` 约 56,271 例):逐阶段只跑该阶段可能影响的具名块;完整 caselist 只在架构边界与合并 `dev` 之前跑,放 CI 不放关键路径。
|
||||
|
||||
## 里程碑
|
||||
|
||||
- **2026-09-08**:P2 出口 GO/NO-GO 判定继续;P3a、P4a 各 1 天,未触发重定基线。
|
||||
- **2026-09-16**:P5 出口(首个 IPC 帧,全门停在 E3(a));P5b 出口(Redmi 正确性 8/8,barrier tax 首测)。
|
||||
- **2026-09-17 / 18 / 19**:P5c 同日收官;P5d 三轮;P5e 收官(run-ahead 武装)。
|
||||
- **2026-09-20 / 21**:P5f 收官;CI / inproc 功能补齐收官(`e1bf3677`)。
|
||||
- **2026-09-22**:P6 收官;终局改判为 TCP 跨机 + 两轴传输栈;P6.5 第一波落地;P7 wave 2 落地(B3 找到 OpenRA 真机分歧的真因)。
|
||||
- **2026-09-23**:P7 收官。
|
||||
- **2026-09-24**:P12 子集并入,未收官。
|
||||
|
||||
仍是方向、不据此伪造日历:全功能 split(P8 之后)、纯度门在非 verify 构建上转绿(P13)。各里程碑原文在对应的 `notes/<阶段>/README.md`。
|
||||
|
||||
## 仍开放的债务
|
||||
|
||||
| 债务 | 去向 |
|
||||
|---|---|
|
||||
| `SEG_REPLY` 2 MiB 单槽 payload cap、PACK-PBO 回读的 fire-and-forget | P9(ID-47、ID-57);stream 链路上 cap 变成 `maxReplyBytes` 窗口 |
|
||||
| 未接入内容分块的 record 类型:program archive 与 `draw_vbo` range 尾 | P6.5 sl 与 P8 共用的分片(开放问题 11) |
|
||||
| class-C 余项与仿真路径:`SetSwapInterval`(P10)、`DeleteTransformFeedback`(P9);multi-draw client indices、RGB CPU mip、`texture-remint-pull` 等具名拒绝 | P8 / P9 |
|
||||
| `GetCaps` 的两个 blobref | 一旦运输,必须有 server → client 的 carrier rule,不能套 `SEG_STAGE` |
|
||||
| E1 对照的重定义(ID-122) | 无主(开放问题 24) |
|
||||
| `MOBILEGL_IPC_IDLE_EXIT_S` 未解析;`CONTRACT-P6.md` 仍把 `DynamicBackendParameters` 定宽记在 P7 名下(已由 P6.5 wf 落地) | 文档 / 小修 |
|
||||
| P12 文档债:`MG_Remote/CONTRACT-P12.md` 未写;`protocol.fbs` 的窗口注释仍是旧说法 | P12 收尾 |
|
||||
| 树外脚本:普查跑器、`wsl_p5_gate.sh` 等在 `~/w7/notes/`,git merge 不会传播 | 需要时入库 |
|
||||
| 临时 CI trigger | 合入 `dev` 前删除 `test.yml` / `apk.yml` 的 `feat/disaggregated` 触发器 |
|
||||
|
||||
已关闭的历史债(P5 的 27 个 wrong-answer、`rsp` 残余读、P5b 的 inproc 依赖、ABI 指纹、默认 staging 装不下 128 MiB 上传、184 符号棘轮 CI 门)及当时的完整债务表见 [`notes/p5b/README.md`](notes/p5b/README.md) 末节。
|
||||
|
||||
## 开放问题
|
||||
|
||||
每题的完整陈述与已答部分的出处在 [`notes/OPEN-QUESTIONS.md`](notes/OPEN-QUESTIONS.md)(编号稳定)。
|
||||
|
||||
| # | 问题 | 状态 / 去向 |
|
||||
| 阶段 | 做什么 | 状态 |
|
||||
|---|---|---|
|
||||
| 3 | spike B 在 `untrusted_app` 域复核 | P11 前做;只对同机臂的 T0 / T1 有意义 |
|
||||
| 6 | 具名 UBO host payload 的形状(D-B8) | P7 走了直绑常驻 `VkBuffer` range 的备选,欠一次测量与裁定 |
|
||||
| 7 | `MG_Util` 的 Transpile-vs-Reflect 切割缝 | 未审计 |
|
||||
| 9 | viewport-array 回放能否塞进一次 `draw_vbo` | P8 |
|
||||
| 11 | `SEG_STAGE` 的上限(未分片的 program archive 与 range 尾;MC in-world 的占用分布) | P6.5 sl / P8 |
|
||||
| 12 | P13 之后 split-only 渲染 bug 的 server 侧第二意见 | 开放 |
|
||||
| 13 | 烘焙内部 shader 的表达力 | 颜色 blit、深度 mip、MS resolve 已答;直通 TCS 未盘点 |
|
||||
| 14 | 推送模型改变了哪些缓存命中率 | P13 重调 |
|
||||
| 15 | monolith 的 `*IndirectCount` 不调 `SyncGpuWrites()` | 独立 `dev` 问题 |
|
||||
| 16 | 索引宿主镜像的实际内存占用 | P8 |
|
||||
| 17 | `create-indirect` 在 Adreno 830 失败、rd12 + Magma 的 `scudo` 崩溃 | `dev` 侧既有问题,排除在设备 A/B 之外 |
|
||||
| 18 | split 两侧的负载平衡(VD32 下 client 是瓶颈,apply 有余量) | 路线图推完后的优化阶段 |
|
||||
| 19 | 链路带宽预算与压缩 | 等门 8 分布与跨机实测 |
|
||||
| 20 | 大单次上传在慢链路上的停顿 | 跨机臂上记录,或随 P9 异步化 |
|
||||
| 22 | client 平台范围(Windows client 第二批、macOS 不拆分) | 开放 |
|
||||
| 24 | E1 对照的重定义 | 无主 |
|
||||
| [P0](notes/p0/README.md) | 打地基:计数器、调用目录与生成器、CI 门;验证 Android 能拉起第二个进程 | ✅ 09-05 |
|
||||
| [P0.5](notes/p05/README.md) | 抽出前后端共用的数据类型头,切断后端对前端头文件的依赖 | ✅ |
|
||||
| [P1](notes/p1/README.md) | 后端改读一个"输入块",并能逐 draw 核对它与前端真实状态一致 | ✅ |
|
||||
| [P2](notes/p2/README.md) | 前端开始主动推送渲染状态;接口代价实测 +6–12%,决定继续 | ✅ 09-08 |
|
||||
| [P3a](notes/p3a/README.md) | GLES 后端的 buffer、顶点数组改用句柄 | ✅ 09-08 |
|
||||
| [P4a](notes/p4a/README.md) | GLES 后端的帧缓冲、纹理、sampler、program 改用句柄 | ✅ 09-08 |
|
||||
| [P5](notes/p5/README.md) | 第一次拆成两线程:命令队列 + 后端线程(每条命令都等) | ✅ 09-16 |
|
||||
| [P5b](notes/p5b/README.md) | 把剩余命令迁到队列上;真机首次以独立后端线程跑通目标游戏 | ✅ 09-16 |
|
||||
| [P5c](notes/p5c/README.md) | 两线程之间不再偷偷共享内存,只经队列交换 | ✅ 09-17 |
|
||||
| [P5d](notes/p5d/README.md) | 两线程版本的性能:游戏内 7–13 fps → 103–106 fps | ✅ 09-18 |
|
||||
| [P5e](notes/p5e/README.md) | client 发完就走,不再每条命令等后端;重负载下与单线程持平 | ✅ 09-19 |
|
||||
| [P5f](notes/p5f/README.md) | 所有状态都经队列传递,为拆进程扫清最后障碍;Vulkan 后端也能发完就走 | ✅ 09-20 |
|
||||
| [P6](notes/p6/README.md) | 拆成两个进程;真机上两进程与两线程成本持平 | ✅ 09-22 |
|
||||
| [P6.5](notes/p65/README.md) | 传输分控制面 / 数据面;电脑当 client、手机当 server 经 TCP 跑通 | 第一波 ✅,余项交 CI |
|
||||
| [Ph](notes/p7/README.md) | server 能安全地接受外来连接:令牌、限额、坏数据不致崩溃 | 必需项 ✅(随 P7) |
|
||||
| [P3b / P4b](notes/p34b/README.md) | GLES 后端的深化与收尾 | 大部分 ✅,余项并行 |
|
||||
| [P7](notes/p7/README.md) | Vulkan 后端完整迁移;真机画面检查 36/36 通过 | ✅ 09-23 |
|
||||
| [**P12**](notes/p12/README.md) | **server 自己开窗口上屏,client 不需要窗口** | **进行中**:已实现,待复测与验收 |
|
||||
| [P8](notes/p8/README.md) | 把剩余的仿真路径挪到正确的一侧,补齐协议 | 待排 |
|
||||
| [P9](notes/p9/README.md) | 反向通道异步化(回读、写回不再同步等待) | 待排 |
|
||||
| [P10](notes/p10/README.md) | 同步对象、查询与帧节奏 | 待排 |
|
||||
| [P11](notes/p11/README.md) | 同机大缓冲零拷贝共享 | 待排 |
|
||||
| [P13](notes/p13/README.md) | 删掉旧的"后端直接读前端"路径 | 待排 |
|
||||
|
||||
已答:1(client dirty 走查的代价,P2–P4a)、2(纹理重铸拉取率,可忽略)、4(渲染状态 wire 粒度)、5(无存储的 capability)、8(反射归档服务三个消费者,P7 C 补齐 `storageBlocks`)、10(`kCapResidentSubData` 由 server 发布,P7 C)、21(令牌足够、不做 TLS,ID-P7-3)、23(半开连接由 TCP keepalive / 超时在传输层报告为挂断,P6.5 ct)。
|
||||
## 另见
|
||||
|
||||
- 工程纪律与验证门:[`guide/discipline.md`](guide/discipline.md)
|
||||
- 仍开放的债务:[`notes/DEBTS.md`](notes/DEBTS.md);开放问题:[`notes/OPEN-QUESTIONS.md`](notes/OPEN-QUESTIONS.md)
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
# 边界与对象模型(原 ARCHITECTURE §1–§2)
|
||||
|
||||
> 设计细节。设计要点与全部章节的索引见 [`../ARCHITECTURE.md`](../ARCHITECTURE.md)。章节编号沿用原 `ARCHITECTURE.md`,代码注释里的 `ARCHITECTURE.md §N` 按编号在这里找到。
|
||||
|
||||
## 1. 边界
|
||||
|
||||
### 1.1 一句话
|
||||
|
||||
`MG_Backend` 本来就是一台贴着目标 API 的状态机(Espryt = DirectGLES,Magma = DirectVulkan),缺的是一份"我被告知了什么"的显式声明。MGPipe 就是这份声明:前端在每条 verb 之前把变化**推**过去,后端不再拉 `MG_State::pGLContext`。接口从两个后端自己维护的关键结构反推(`SetupDrawSnapshot`、`DrawTextureSyncKeys`、`ResolvedVertexBindings`、`g_syncedRenderStateParameters`、`BufferBackendOps` …);gallium 是词汇的目的地,不是推导前提。
|
||||
|
||||
### 1.2 两张函数指针表
|
||||
|
||||
`MGPipeScreen`(share-group 作用域:caps、resource、persistent map、fence)与 `MGPipeContext`(其余全部),由 `PipeCalls.def` 生成。
|
||||
|
||||
- 函数指针 struct 而非虚基类:null 项已表示"未实现,前端回退",`MG_Test` 整表替换即可 mock。
|
||||
- 两张表从第一天分开,事后拆会给记录重新编号;v1 只有一个 screen、一个 context、一条 flow。
|
||||
- EGL 生命周期与 caps 面留在 `pActiveBackendObject` 的虚函数上(罕见路径,走控制面)。
|
||||
|
||||
### 1.3 三种形态,一份后端
|
||||
|
||||
| 形态 | 表里装的是什么 | 用途 |
|
||||
|---|---|---|
|
||||
| `monolith`(默认) | 后端自己的函数,回调直调 `MG_State` | 出货 |
|
||||
| `inproc` | 发射器 → 同进程第二个线程上的 applier | CI 主验收;也就是 monolith 的**渲染线程** |
|
||||
| `spawn` | 发射器 → 数据面 → 另一个进程的 applier → 同一批后端函数 | 两进程形态;控制面可以是 `fork` / `unix:` / `tcp://`(§11.9),server 可在另一台机器上 |
|
||||
|
||||
唯一 hook 点是 `MG_Backend::Init()` 里一个 `#if MOBILEGL_BUILD_DISAGGREGATED` 分支:`MG_Config::Transport != Monolith` 时装 `MG_Remote::BackendObject_Remote`。`MG_Impl` 的边界调用点零 `#ifdef`。
|
||||
|
||||
## 2. 对象模型
|
||||
|
||||
### 2.1 句柄 = `{slot, gen}`(`MG_Pipe/MGPipeHandles.h`)
|
||||
|
||||
- 8 字节 POD;**client 铸造,server 永不返回句柄** → 整份目录零创建 round trip(对 gallium 的偏离 D1)。
|
||||
- slot 按 kind 稠密分配(free list + 高水位),server 对象表是数组;`gen` 只在 slot 复用时 ++。
|
||||
- kind:`Buffer, Texture, Renderbuffer, Framebuffer, Xfb, RenderStateCso, VertexElementsCso, SamplerCso, SamplerViewCso, ShaderCso, Fence, Query, Context`。保留:`{0,0}` = null;`Framebuffer {0,1}` = 默认帧缓冲;`ShaderCso` 高段给 program pipeline 合成体。
|
||||
- GL name 只作诊断(`GlNameForDiag`),永不做身份、memo 键或 content hash。
|
||||
|
||||
### 2.2 两种世代,严格分开
|
||||
|
||||
| | 拥有者 | 回答 | 过线 |
|
||||
|---|---|---|---|
|
||||
| `MGPipeHandle::Gen` | client | "还是同一个 GL 对象吗?" | 是 |
|
||||
| `MGGen`(后端纪元,如 `g_bufferMutationEpoch`) | server | "我自己是否重铸了驱动对象?" | **永不**(只以纹理拉取请求的形式出现,§8.4) |
|
||||
|
||||
client 的回绕 `Uint16` 版本计数器永远不是新鲜度的唯一证明:过线时要么加宽,要么与 `{slot, gen}` 同行。
|
||||
|
||||
### 2.3 CSO 与可变对象
|
||||
|
||||
- CSO(render state、vertex elements、sampler、sampler view、shader)在 **client 侧内容寻址**(Mesa `cso_cache` 先例),LRU 淘汰时发 `delete_*`;render-state 容量 64(P13 重调)、sampler state 256。
|
||||
- Buffer / Texture / Renderbuffer 走 create / respecify / subdata / destroy;Framebuffer / Xfb 是 per-context 身份 + `set_*` payload。
|
||||
- 两条偏离:**D-G1** Espryt 的 vertex-elements 按 VAO 身份寻址(twin 持驱动 VAO 名,不能共享);**D-F2** Espryt 的 sampler view 按纹理身份寻址,sampler state 内容寻址且带引用计数(被 `BuiltinSampler` 指着的项不许被 LRU 挤掉)。
|
||||
@@ -0,0 +1,55 @@
|
||||
# 调用目录与记录格式(原 ARCHITECTURE §3–§4)
|
||||
|
||||
> 设计细节。设计要点与全部章节的索引见 [`../ARCHITECTURE.md`](../ARCHITECTURE.md)。章节编号沿用原 `ARCHITECTURE.md`,代码注释里的 `ARCHITECTURE.md §N` 按编号在这里找到。
|
||||
|
||||
## 3. 调用目录
|
||||
|
||||
### 3.1 单一真相源
|
||||
|
||||
`MobileGL/MG_Pipe/PipeCalls.def`:一行一个 `X(Name, Payload, Class, Flags, WaitClass)`。**线上 opcode 就是行序**,只能追加、退役保留槽位;行数由 `MGP_CALL_LIST_DOCUMENTED_COUNT`(今天 81)与 `PipeCatalogueTest` 钉住。生成器产物提交进树,CI `pipe-gates` 重生成并 `git diff --exit-code`:
|
||||
|
||||
| | 产物 | 内容 |
|
||||
|---|---|---|
|
||||
| G1 | `PipeTables.inc` | 两张函数指针表 |
|
||||
| G2 | `PipeThunks.inc` | monolith 直调 thunk |
|
||||
| G3 | `PipeWire.inc` | wire 记录、尺寸 `static_assert`、applier 边界检查 |
|
||||
| G4 | `PipeVerify.inc` | `MOBILEGL_PIPE_VERIFY` 逐字段比对器(`PipeFields.def`) |
|
||||
| G5 | `PipeFilled.inc` | `PipeInputs` 字段 id 与逐 verb 世代 poison |
|
||||
| G6 | `PipeCoverage.inc` | 后端读点 → 调用映射(`Coverage.def`),0 UNMAPPED 是门 |
|
||||
| G7 | `PipeSpanTable.inc` | render-state pipeline 子集成员表 |
|
||||
| G8 | `PipeFieldOwnership.inc` | 每个 `PipeInputs` 字段恰属 `RECORD-SUPPLIED / APPLIER-DERIVED / BARRIER-PULLED / FATAL` 之一(P5f 起 0 BARRIER-PULLED) |
|
||||
|
||||
另有 `FillPoints.def`(填充点)与 `DirtySurface.def`(§5.2)。G1 codegen 规则:capability gate 替换指针表达式时保留**表达式形状**,否则 pull `.text` 漂移。
|
||||
|
||||
### 3.2 分组与 flag
|
||||
|
||||
- Class:`kScreen`(caps、resource、persistent map、fence、`ApplierReset`)、`kCtxQuery`、`kCtxCso`(create/bind/delete × 五种 CSO)、`kCtxState`(`Set*` 状态、`SetContextValues`、`SetProgramBindings`、迁移期的 `SetResidualValueState`)、`kCtxObject`(subdata、readback、mip、`ObjectDeath` …)、`kCtxVerb`(blit、clear、`DrawVbo`、dispatch、XFB、`Present` …)。
|
||||
- Flags:`kNeedsAck`、`kHasBlob`、`kVarTail`、`kHostSpan`、`kReplySlot`、`kOptional`。第五列 `WaitClass`(P5e)决定 run-ahead 下 client 等不等(§11.6)。
|
||||
- 20 个 draw 入口塌成 `DrawVbo` 一条;`Clear` 一条判别式;sampler 集合**没有 stage 维度**(192 单元合并空间);`SetTextureParams` 按资源寻址、与 sampler view 分开(D10);`SetIndexBuffer` 独立于 VAO 配置(D5)。
|
||||
- 显式不移植:`GetIntegeri_v` / `GetInteger64i_v` / `GetProgramiv`、`set_pixel_unpack_state`(前端已解析)、压缩格式概念、`pipe_transfer`。
|
||||
|
||||
### 3.3 能力位(`MGPCapBit`)
|
||||
|
||||
`CallMask` 取代"槽位是否为 null"这一隐式能力探测;`MGPCaps` = `DynamicBackendParameters` + `CallMask` + 格式能力表与 renderer 字符串两个 blob,握手后快照、`MakeCurrent` / `InitCapabilities` 后按 generation 重发布。位包括 viewport array、fp64 顶点、`kCapResidentSubData`、XFB / query 家族、`kCapNeedsHostIndexBytes`、`kCapNeedsHostUboBytes`、`kCapBackendOwnsXfbCapture`、`kCapRunAheadApply`(`MGPipeRunAheadCapBitsFor`:Espryt 自 P5e、Magma 自 P5f 之后的 Magma run-ahead 起发布,后者契约 `MG_Remote/CONTRACT-MAGMA-RUNAHEAD.md`)。**不存在"归属开关"(D-B7)**:multi-draw 分档与 restart 重写永远由 server 拥有,client 只在 caps 要求时提供索引字节。
|
||||
|
||||
## 4. 记录与 payload 约定(`MG_Pipe/MGPipeTypes.h`)
|
||||
|
||||
- 每个 payload 是平坦 POD、显式 padding、`static_assert` 精确尺寸;**永不含指针**。
|
||||
- `MGPBlobRef{Offset, Size, Seg}` 指向 blob 区;`MGHostSpan` 是唯一随传输而变的形状(monolith 下是指针,split 下字节在 `SEG_STAGE`),只进变长尾。
|
||||
- wire 记录头 `MGPWireRecHeader{Op, Flags, Size}`(8 B);**没有逐记录序号**,seq 就是记录序数。
|
||||
- 单条记录上界 = ring 一半,**记录本身永不分块**(R-10);**内容侧分块**:会超出 `SEG_STAGE` 的 blob 按 `MGPipeStageChunkBytes()`(默认 segment/4 = 8 MiB)切成多条记录(buffer 范围走查、纹理整宽 slab,server 拼回整级)。
|
||||
|
||||
### 4.1 关键 payload
|
||||
|
||||
| payload | 要点 |
|
||||
|---|---|
|
||||
| `MGPResourceDesc` | buffer / 纹理 / renderbuffer 一个判别式 create/respecify 形状;`ImageBindableHint`、`ViewOf`(视图的存储属主)、`HasDefinedContent` |
|
||||
| `MGPRenderStateDesc` / `MGPBindRenderState` / `MGPDynamicState` | §5.3 |
|
||||
| `MGPVertexElements` | blob 同时带解析后的属性与绑定点视图(记录自洽) |
|
||||
| `MGPSamplerView` / `MGPTextureParams` | view 只带视图限制;纹理参数挂纹理对象,`BuiltinSampler` 句柄不许为空(D-E1) |
|
||||
| `MGPProgramDesc` | 逐 stage SPIR-V + 反射归档(§7) |
|
||||
| `MGPFramebufferState` | 8 color + depth + stencil + client 解析后的 read surface;`ContentHash` 同时是 render-pass memo 键与发射抑制器;`Target` 含 `Named = 3`,applier 按 framebuffer 句柄存 |
|
||||
| `MGPSubData` / `MGPSubRegion` | §6 |
|
||||
| `MGPDrawInfo` / `MGPDrawRange` | `DrawVbo`;multi-draw 范围与用户索引在变长尾,indirect 的 `DrawCount` 由 client 解析 |
|
||||
|
||||
每条 `kVarTail` 的 `set_*` 带 `ContentHash`,hash 未变就不发(§5.4)。
|
||||
@@ -0,0 +1,64 @@
|
||||
# 前端:state tracker、纹理上传、着色器(原 ARCHITECTURE §5–§7)
|
||||
|
||||
> 设计细节。设计要点与全部章节的索引见 [`../ARCHITECTURE.md`](../ARCHITECTURE.md)。章节编号沿用原 `ARCHITECTURE.md`,代码注释里的 `ARCHITECTURE.md §N` 按编号在这里找到。
|
||||
|
||||
## 5. 前端 state tracker(`MG_Impl/Pipe/Tracker`)
|
||||
|
||||
### 5.1 推送发生在 verb 之前的 validate 时刻,不在 GL setter 里
|
||||
|
||||
per-setter 推送会把 Blaze3D 每个 batch 的冗余 `glEnable/glDisable` 变成接口调用,严格慢于今天;正确形态是 gallium `st_validate_state`。落地为唯一的 `MGPipeValidateForVerb(MGPipeVerb)`(九个 validate 类,`FillPoints.def`),恰在每次经表调用之前、每个提前返回之后。只有今天就在 GL 调用时刻分发的资源 op(`BufferBackendOps` 的七个 hook)在调用时刻推送。
|
||||
|
||||
### 5.2 dirty 位:值类零新增记账,对象类 5 个聚合世代
|
||||
|
||||
- 值类组(render state、pixel pack、patch、attrib defaults、shader bindings …)直接复用既有版本计数器作快门。
|
||||
- 对象类组(vertex buffers、framebuffer、sampler views / samplers / images、const / shader buffers、SO targets)靠 5 个**聚合世代**把快门降成一次 `Uint64` 比较——没有聚合就回答不了"有没有哪张已绑定纹理动了"。
|
||||
- 完整性由 `scripts/gen_pipe_dirty_surface.py` 保证(每个 mutator → 必须 bump 的世代,`--check` + `--self-test` 进 CI)。**快门看不见自己的主体**是踩过三次的坑:新增字段必须带"记录字段 → setter → 快门"一行,优先混入已有世代(新计数器会撑大 pull 对象,G1 不允许)。
|
||||
|
||||
### 5.3 渲染状态:整块 blob 过线,身份只取 pipeline 子集(D-B1)
|
||||
|
||||
```
|
||||
create_render_state(cso, pipelineSubsetChunks) // 只带 pipeline 子集
|
||||
bind_render_state(cso, version, pipelineVersion) // 稳态 12 B
|
||||
set_dynamic_state(dynamicChunks, version) // 只带变化的动态 chunk
|
||||
```
|
||||
|
||||
- `RenderStateParameters`(1168 B)整块过线:Espryt 做三段 memcmp,字段顺序承重;拆成多个 CSO 要手工维护 ~150 字段划分且无绊线。
|
||||
- 身份只取 pipeline 子集,否则 `glViewport` / `glScissor` 每次都铸新 CSO、冲掉 server 的 pipeline memo。划分只写在一处:`MGPipeRenderStateSpans` 的 chunk 表(7 pipeline chunk 396 B + 8 dynamic chunk 772 B,`static_assert` 完整性),G7 断言"子集 hash 变 ⟺ `m_pipelineStateVersion` 变"。
|
||||
- Espryt 的 `SyncRenderState` 一行不动;Magma 的 pipeline memo 键是 `cso.slot`。
|
||||
|
||||
### 5.4 验证不变式、合并与抑制器
|
||||
|
||||
规范(D-B3):**一条 verb 的全部 `set_*` / `bind_*` 在该 verb 之前完成;server 在 verb 处从已推送状态惰性特化 shader 与 pipeline。**除"资源 create 先于对它的 bind"外,`set_*` 之间无顺序要求。合并:整块优于逐字段;高水位直接是 `count` 实参;只发 program 解析过的集合;**集合 hash 抑制器**在 client 侧去抖(原先在后端的 ~175 行)。
|
||||
|
||||
### 5.5 sampler view 在 client 侧解析
|
||||
|
||||
shader 看见哪个纹理取决于 sampler uniform 类型、mipmap 完备性与未定义默认纹理;解析留在 client(带 memo),gallium "每槽一个 view" 就是解析后的形态。两处后端特定后处理留在 server:Espryt 的 raw-depth-fetch sampler 替换、Magma 的 feedback-loop 检测。
|
||||
|
||||
### 5.6 生命周期、共享组、composite program
|
||||
|
||||
- `resource_create` 在前端对象构造时发,存储由 `resource_respecify` 惰性定义,`resource_destroy` 在析构时发;死亡转发给每个 emitter(只释放 slot 曾导致 UAF)。view 先于存储属主销毁、FBO attachment 钉住纹理、buffer texture 钉住 buffer。
|
||||
- program pipeline 合成体(`CompositeResolver`)完全在前端合成、推一个句柄;释放恰好一次(resolver 换掉或 `~ProgramObject`,D-H7);记忆按 `(ContextId, 管线 GL 名)` 键控。
|
||||
|
||||
### 5.7 emulation 的归属
|
||||
|
||||
规则:**驱动表达不了的变换在 tracker 里 lowering,硬件 / 驱动强加的变换在 driver 里 lowering。**
|
||||
|
||||
| 归 client(读前端字节的纯 CPU 变换) | 归 server |
|
||||
|---|---|
|
||||
| client 顶点 / 索引数组(字节过线,永不是指针)、最大索引扫描、`*IndirectCount` 计数解析、CopyImage shadow 镜像、XFB CPU 图元计数、压缩纹理 / unpack 规整 | primitive-restart 重写、multi-draw 分档与展平(从索引宿主镜像读,§10.3)、viewport-array 回放、fp64 顶点窄化、image-bindable 存储加宽、生成 mipmap(client 分配 level 存储) |
|
||||
|
||||
**陈旧索引纪律是逐站点表**:client 侧扫描前的 reconcile 必须逐字复现 monolith 的集合(最大索引扫描 = `SyncPersistentMappedRange()` + `SyncGpuWrites()`;`*IndirectCount` 只前者)。
|
||||
|
||||
## 6. 纹理 subdata 与 dirty 归属
|
||||
|
||||
- 纹理上传由 Espryt 在 sync 时刻按**累积**区域做(Mali 按作业数计价,~100 个 rect 对一个 union box 实测 +6 ms/帧),逐 `glTexSubImage` 发记录会复现这个悬崖。
|
||||
- 因此 client 在自己的 rect 模型里累积,在下一个 validate / flush 点发**一条** `ResourceSubData`,同时携带 union box 与 region 列表,**由 server 选上传形状**(决策留在付 GPU 代价的一侧)。步长显式携带,不再靠指针比较。
|
||||
- **dirty 归属反转**:client 维护发射游标、发射后清自己的标志;清 dirty 要等 applier 的 acceptance(被拒的 level 留在脏表里);server 自己的重新变脏是 server 的事。
|
||||
- `TextureUploadShapeScenario` 把上传形状录成金标(P3b/P4b D2 起是门;Mali 帧时增量无设备,按 Adreno 记录)。
|
||||
|
||||
## 7. Shader state = SPIR-V + 反射归档
|
||||
|
||||
- `CreateShaderState` 过线的是逐 stage SPIR-V + 反射归档,**不是源码**:glslang 全在 client、SPIRV-Cross 全在 server,文件级切割;server 侧无编译池。归档序列化器 `ProgramArtifactsCodec`,monolith 只在 verify 构建里调它(D-H3)。
|
||||
- server 在 verb 时刻从已推送状态**惰性特化**(D-B2:draw FBO clamp mask、fragColor 广播数、storage-block 绑定签名、image 格式、patch 参数等)——正是两个后端原来的做法。
|
||||
- link / compile 失败不需要同步返回:`GL_LINK_STATUS` 由 client 从 `ProgramObject` 回答,后端失败以 `OnLog` ≥ERROR 无损呈现。
|
||||
- 内部 shader(颜色 blit、深度 mip、multisample resolve)烘焙成签进树的 SPIR-V(`Wire*Spirv.h`),新鲜度门 `MOBILEGL_BAKED_INTERNAL_SHADERS`(`MG_Test/SelfTest/BakedInternalShadersTest.cpp`)重跑树内 glslang 逐字节比对(P7)。
|
||||
@@ -0,0 +1,40 @@
|
||||
# 反向通道(原 ARCHITECTURE §8)
|
||||
|
||||
> 设计细节。设计要点与全部章节的索引见 [`../ARCHITECTURE.md`](../ARCHITECTURE.md)。章节编号沿用原 `ARCHITECTURE.md`,代码注释里的 `ARCHITECTURE.md §N` 按编号在这里找到。
|
||||
|
||||
## 8. 反向通道
|
||||
|
||||
### 8.1 `MGPipeCallbacks`(`MG_Pipe/MGPipeCallbacks.h`)
|
||||
|
||||
九个具名回调 + 一个正向终止符(`ResourceSubDataComplete`),取代后端直接 poke 前端对象的 95 个调用点(具名化是有意偏离 D8)。monolith 下直调,split 下是 `SEG_EVENT` 上的记录。
|
||||
|
||||
| 回调 | 作用 |
|
||||
|---|---|
|
||||
| `OnGlError` | 延迟的 GL 错误;**必须对命令流有序** |
|
||||
| `OnGpuWritten` | GPU 写过的 buffer 范围(收窄 client 保守自建的 pending 集) |
|
||||
| `OnBufferWriteback` | PBO 回读、XFB 捕获结果;必须与 epoch bump 有序 |
|
||||
| `OnTextureWriteback` | CPU 回退生成 mip 的纹素 |
|
||||
| `OnTexturePullRequest` | §8.4 |
|
||||
| `OnMipLevelsGenerated` | 只带形状 |
|
||||
| `OnSurfaceChanged` | 默认帧缓冲的格式与尺寸(server 拥有显示时 client 靠它得知窗口尺寸) |
|
||||
| `OnCapsInvalidated` | 取代 `InvalidateCompileEnv` |
|
||||
| `OnLog` | ≤WARN 有损,≥ERROR 无损 + 速率限制 |
|
||||
|
||||
后端凭空造的前端对象(Magma 占位纹理、swapchain 默认 FB 占位)改为 server 原生;pull 构建里的 `m_backend` 类成员真删会动 `sizeof`(G1),随 P13 退役(D-K)。
|
||||
|
||||
### 8.2 有序性是正确性要求
|
||||
|
||||
每次 writeback 之后的 epoch bump 必须在任何后续读该 handle 的命令之前被 server 应用。**反向通道需要与正向通道相同的有序保证。**
|
||||
|
||||
### 8.3 错误、ack 与日志
|
||||
|
||||
- **唯一允许同步 ack 的入口是 `glBufferStorage`**:`ResourceRespecify` 的 `kNeedsAck` 由逐记录谓词收窄到"不可变 **buffer**"(D-A2),纹理 OOM 在 monolith 里本来就推迟到 sync 时刻。respecify 带逐 level 作用域与"只换元数据"的形式,不误丢已接受的待上传。
|
||||
- 其余错误一律晚到,走有序的 `OnGlError`。
|
||||
|
||||
### 8.4 唯一的新停顿类:server 发起的纹理重铸拉取(D-B6)
|
||||
|
||||
server 不保留纹素;image-bindable 重铸、整格式再生、view 源重铸会要求重发 level。缓解:`ImageBindableHint` 预防主因;拉取异步(阻塞 apply 线程,不阻塞应用线程);保留 LRU 默认关(`MOBILEGL_PIPE_TEXEL_RETAIN_MB=0`);显式终止符可带零个 region。真实语料上发生率可忽略(780 个统计窗口 2 次);split 下这条路径今天仍是具名 `Fatal{UnmigratedEmulation, "texture-remint-pull"}`,归 P9。
|
||||
|
||||
### 8.5 XFB scatter 留在 server
|
||||
|
||||
补丁循环需要"捕获前的字节",而拆分后权威影子归 server(`StagedShadow`),所以 scatter 在 server 原地跑完;回程只有一条 `OnBufferWriteback`,载补好的整段,按 `SEG_EVENT` 容量的四分之一切片(`MGPipeBufferWritebackSliceBytes`),最后一片落地即蕴含之前每一片。孤儿目标(`HasDefinedContent == 0`)零填充后照常散射。`OnXfbScatterReady` 已删除(回调 10 → 9)。落地论证见 [`notes/p34b/README.md`](../notes/p34b/README.md)(espryt D1)。
|
||||
@@ -0,0 +1,52 @@
|
||||
# 后端改造与 server 侧(原 ARCHITECTURE §9–§10)
|
||||
|
||||
> 设计细节。设计要点与全部章节的索引见 [`../ARCHITECTURE.md`](../ARCHITECTURE.md)。章节编号沿用原 `ARCHITECTURE.md`,代码注释里的 `ARCHITECTURE.md §N` 按编号在这里找到。
|
||||
|
||||
## 9. 后端状态机改造
|
||||
|
||||
### 9.1 原样不动的东西
|
||||
|
||||
Espryt 的 persistent ring、buffer pool、fallback-repack、scratch FBO、驱动绑定影子、Adreno / Mali workaround、SPIRV-Cross 会话、restart 重写与 multi-draw 分档;Magma 的 `VulkanRenderer` memo、`PipelineFactory`、`ProgramFactory`、`UniformManager`、五个 `Vk*Manager`、`SwapchainObject`、D18 的节点式容器纪律。"输入变了、算法不许动"的那一类由 **G5 字节一致门**把关(`scripts/p3a_untouched_regions.sh` 十一函数、`scripts/p4a_untouched_regions.sh` 17 区),re-pin 必须两份同改。
|
||||
|
||||
### 9.2 strangler 脚手架:`PipeInputs` + 逐 verb 填充 + poison 世代(P1)
|
||||
|
||||
```cpp
|
||||
// MG_Backend/MGPipe/PipeInputs.h —— 按 memo 键组织,访问器类型与后端原来读到的完全一致
|
||||
#if MOBILEGL_PIPE_PUSH
|
||||
# define MGB_CTX (&::MobileGL::MG_Pipe::gPipeInputs)
|
||||
#else
|
||||
# define MGB_CTX (::MG_State::pGLContext)
|
||||
#endif
|
||||
```
|
||||
|
||||
三步:A 别名(P1,机械替换 + 逐 verb 类填充点,`nm` 不变)→ B 推送(P2,tracker 填 `gPipeInputs`,verify 构建逐 draw 比对快照版)→ C 句柄化(P3a–P4a、P7,`SharedPtr<前端对象>` → 句柄 + 描述符,写回变回调)。poison 是**逐 verb 世代**:读一个当前 verb 未填的字段是 `Fatal{UnmigratedPipeInput, "<Getter>@<Verb>"}`。
|
||||
|
||||
### 9.3 Track V / Track H
|
||||
|
||||
Track V(值类型:render state、pixel store、capability 位、标量)是机械迁移;Track H(167 个 `SharedPtr<MG_State…>` 读点)是真活。74% 的读点是翻译输入,所以"bump 一个版本让 server 自己拉"行不通,值本身必须过去。
|
||||
|
||||
### 9.4 残余值块
|
||||
|
||||
迁移期临时调用 `SetResidualValueState`(`ResidualValueBlock`):尺寸只降不升(1248 → 8,只剩 `CapabilityBits`),P13 变成 `static_assert(sizeof == 0)`;布局逐成员 `offsetof` 断言;stats 单独计字节。
|
||||
|
||||
### 9.5 身份 memo 的重键
|
||||
|
||||
`{slot, gen}` + 显式 destroy 让 21 条身份 memo 中 **11 条直接删除**、2 条的去抖搬到 client(§5.4)、7 条重键成更便宜的比较、1 条(D18)不动。统一事实:进 memo 键的版本计数器要么会回绕,要么根本不被它害怕的 mutation bump,身份比较是堵洞的补丁。
|
||||
|
||||
### 9.6 A/B 与口径收窄
|
||||
|
||||
`MOBILEGL_PIPE_PUSH` 子系统位图在阶段 B 是真 A/B;阶段 C 之后位清零时后端仍跑重键后的代码,所以**编译期** `MOBILEGL_PIPE_LEGACY_MEMOS`(默认 ON)保留 pre-handle 臂,随 pull 路径在 P13 退役。位依赖两侧都拒(客户端族门里**根本不发射**,否则会"客户端已清 dirty、服务端却走旧臂"而丢上传);没有消费者的后端一条不发。退役的 twin 成员仍在 pull 构建里编译——真删会动 G1。
|
||||
|
||||
## 10. server 侧
|
||||
|
||||
### 10.1 对象表与 applier
|
||||
|
||||
`MG_Remote/Server/PipeApplier`:解码 → 更新对象表与 `PipeInputs` → 调后端。server 不持有 buffer 的完整副本(只有 staged shadow)、不持有前端对象图;任何传输下都不得有 `SharedPtr` 或裸前端指针跨过 applier 边界(P5c 角色守卫、P5e 规则 F)。`InProcessTransport` 与 spawn 走完全相同的编解码路径。
|
||||
|
||||
### 10.2 monolith 侧的净收益
|
||||
|
||||
即使 IPC 永不上线:复用地址 ABA 一整类不可表达;FBO → program 排序 hazard 消失;`SwapchainObject` 写 `MG_Impl` 的分层倒置消失;`inproc` = 渲染线程。monolith 净代码量是增加的,论据只看逐线程 CPU。
|
||||
|
||||
### 10.3 索引宿主镜像(`Server/IndexHostMirror`,P8,待重裁)
|
||||
|
||||
设计:`kCapNeedsHostIndexBytes` 下,server 从本来就要收的 `ResourceCreate/Respecify/SubData` 流增量维护 element-array buffer 的宿主镜像,零额外流量;预算 `MOBILEGL_PIPE_INDEX_MIRROR_MB`(默认 64),超预算退化为逐 draw 经 `MGHostSpan` 传送。**P6 的 a6 审计实测宿主索引 span 无 producer、client 索引已是 owned buffer**,所以 P8 要按真实消费者重新裁定,不照抄本节([`notes/p8/README.md`](../notes/p8/README.md))。
|
||||
@@ -0,0 +1,67 @@
|
||||
# 传输与数据面(原 ARCHITECTURE §11)
|
||||
|
||||
> 设计细节。设计要点与全部章节的索引见 [`../ARCHITECTURE.md`](../ARCHITECTURE.md)。章节编号沿用原 `ARCHITECTURE.md`,代码注释里的 `ARCHITECTURE.md §N` 按编号在这里找到。
|
||||
|
||||
## 11. 传输与数据面(`MobileGL/MG_Remote/`)
|
||||
|
||||
### 11.1 段
|
||||
|
||||
| 段 | 拥有者 | 默认 | 内容 |
|
||||
|---|---|---|---|
|
||||
| `SEG_CMD` | client(server 只读) | 8 MiB | `RingControl` 页 + POD 记录 + 小内联负载 |
|
||||
| `SEG_STAGE` | client | 32 MiB | bulk 字节:buffer / 纹理 subdata、UBO、client 数组、persistent-map 脏块 |
|
||||
| `SEG_REPLY` | server | 16 MiB,8 × 2 MiB slot | readback 像素、acceptance 答案(ID-47) |
|
||||
| `SEG_EVENT` | server | 256 KiB SPSC ring | 回调事件 |
|
||||
| `SEG_SHADOW` / `SEG_ADOPT` | client / server | — | 零拷贝 shadow(Phase 2)/ ≥16 MiB 采纳(P11);stream 数据面上具名拒绝 |
|
||||
|
||||
段在同机上由 `ShmSegment` 创建(Android `ASharedMemory`、Linux `memfd`、Windows `CreateFileMappingW`),描述符经 `SCM_RIGHTS` 传递;stream 数据面上两端各持一份同尺寸的私有段(§11.9)。
|
||||
|
||||
### 11.2 `RingControl`(`Ring.h`)
|
||||
|
||||
一页 4 KiB,每个争用组一条 cache line,按写者分组(P6 lk):producer 线 `cmdHead` / `submittedSeq`;consumer 线两个 tail;四个 consumer 写的水位收成 `Progress`(`appliedSeq` / `retiredSeq` / `completedFrameSerial` / `presentAckSerial`);最后是 epoch、generation、park 标志与 `eventRingFull` / `eventDropped`。游标是单调字节计数、2 的幂掩码、永不重置;pad 记录处理 wrap;不可能的头 → `Fatal{ProtocolCorruption}`。
|
||||
|
||||
### 11.3 双向 doorbell(`Doorbell.h`)
|
||||
|
||||
两个方向都是"先自旋、再置 parked、再阻塞",发布方仅当对端 parked 时敲;丢失唤醒由两个 `seq_cst` fence 关闭。`CondVarDoorbell`(同进程,带 `Kill()`)与 `SocketDoorbell`(跨进程,对端关闭即死亡检测)。stream 数据面上门铃是本地条件变量,由读线程敲。
|
||||
|
||||
### 11.4 控制面(`protocol.fbs`、`Framing.h`、`ITransport.h`)
|
||||
|
||||
- 一份 FlatBuffers schema:热路径是 `struct` 直接进 ring(与 POD 逐条 `static_assert` 对齐);罕见 / 变长 / 需演进的走控制连接的 `CtrlMsg`(`Hello`、`Welcome`、`Refuse`、`CapsSnapshot`、`SurfaceOp/SurfaceReply`、`SessionFault`、`LogLine` …),union tag 只追加,控制修订号钉在 `sha256(protocol.fbs)` 上。
|
||||
- 生成物提交进树,CI `flatc-check` 重生成并 diff;codegen 不进默认构建图。
|
||||
- 封帧 `[u32 'MGLF'][u32 len][payload]`,64 MiB 上限,读时校验,坏帧立即闩住失败。
|
||||
|
||||
### 11.5 WAR 危害、拷贝账与背压
|
||||
|
||||
Phase 1(今天):GL 调用时刻把字节拷进 stage slot,slot 到 apply 越过它为止不可变,危害按构造消除,代价一次 memcpy;Phase 2(`SEG_SHADOW` 零拷贝)未做。server 没有第二份 `BufferObject`,不存在中间拷贝。分配失败升级:扩容 → 对最老未退休批次有界等待 → 硬 drain + generation bump(之后 tracker 全部重推)。
|
||||
|
||||
### 11.6 publish、序号与 credit
|
||||
|
||||
- 每条记录 release-store `cmdHead`,仅当 consumer parked 时敲门铃;显式门铃点:`present`、`kNeedsAck`、`eglMakeCurrent`、`glFlush`、stage 余量不足、轮询类入口。
|
||||
- seq = 记录序数;字节 credit 与 present credit 两个独立窗口。
|
||||
- **等待规则由 `WaitClass` 列决定(P5e)**:`kWaitReply` / `kWaitPresent` / `kWaitApplied` / `kWaitNone`;run-ahead 武装时 `kWaitNone` 发布即返回。barriered 谓词(`MGPipeBarriered`)client 与 server 用同一个函数、同一份数据算。
|
||||
- 强制等待点:`MapBuffer(READ)` / `GetBufferSubData` 源走 `SyncGpuWrites`;`glFinish` = 等 `appliedSeq` 追上再排空反向通道;每个 `Server*` EGL forwarder 之前等一次。`glGetError` 最多晚一个 present credit。
|
||||
|
||||
### 11.7 事件回传与溢出
|
||||
|
||||
client 在 `glGetError`、query / sync 查询、`eglSwapBuffers`、buffer 回读与每轮等待循环中排空 `SEG_EVENT`。日志有损;语义事件无损:环满时 server 置 `eventRingFull`、在记录边界停 apply、敲 client,client 排空后清标志并敲回(两半都在,才不死锁)。run-ahead 下 producer 阻塞等排空,整笔预算 `MOBILEGL_IPC_EVENT_WAIT_MS`;client 不排空 / 已走 / 会话停止 → `ReverseChannelForfeit{…}` 弃投并闩住(Ph PH-6),而不是 `Fatal` 打掉 server。不发布 `kCapRunAheadApply` 的 server(lockstep 臂)保留 P5c 的 Fatal。
|
||||
|
||||
### 11.8 fence 与无 present 负载(P10)
|
||||
|
||||
fence 完成度必须来自真的逐 fence 退休,不是 present 水位(DirectGLES 的 `g_completedFrameSerial` 今天只在 `Present` 前进);无 present 循环需要 server 插非 present fence tick。Magma 那半已随 run-ahead 落地。
|
||||
|
||||
### 11.9 传输栈的两根轴(P6.5)
|
||||
|
||||
终局(2026-09-22):client 与 server 可在不同机器、不同 OS / 架构上经 TCP 连接,同机 `spawn` 保留。传输栈拆成**两根独立可选、握手协商、可混搭**的轴,上层只见两个接口:
|
||||
|
||||
| 轴 | 接口 | 实现 | 选项(`MOBILEGL_IPC_CONTROL` / `MOBILEGL_IPC_DATA`) |
|
||||
|---|---|---|---|
|
||||
| 控制面 | `ITransport`(只承载控制帧,不承载描述符) | `SocketTransport` | `fork`(继承 fd)、`unix:<path>`、`tcp://host:port` |
|
||||
| 数据面 | `ILink`(记录预留 / 提交、水位、reply、event、span 解析) | `ShmLink`(共享段 + `RingControl` + 门铃)、`StreamLink`(分块封帧,水位与 reply 变消息,发送窗口) | `shm`、`stream`、`auto` |
|
||||
|
||||
- 配对在会话建立时选定一次;`LinkTerms`(数据面、窗口尺寸、`maxReplyBytes` …)由 **server 陈述**,client 请求可被钳制。
|
||||
- 共享段交付与控制面解耦:`ShmLink` 自带 AF_UNIX aux 汇合名,在 `Welcome` 里公布,所以同机"TCP 控制 + 共享段数据"是合法配对。`auto` 的判据是**段实际交付成功**,回落到 `stream` 必须具名进日志。
|
||||
- **上层零链路种类分支**:`ShmLink` / `StreamLink` / `RingControl` 等只出现在 `Transport/` 与其测试(纯度 grep 门);角色谓词回答"谁在哪个进程",不回答"用什么链路"。
|
||||
- 两端是不同二进制:`wireFingerprint`(`PipeFields.def` 派生的逐成员布局摘要 + 目录摘要 + 字节序 / 指针宽度)永远比较;`buildFingerprint` 只在 `Dial == Fork`(或 `MOBILEGL_IPC_REQUIRE_SAME_BUILD=1`)下比较;不一致是 `Refuse`,不是 abort。
|
||||
- TCP 上每条 `kWaitReply` 就是一个 RTT、`SEG_STAGE` 字节对的是 30–100 MB/s 的链路:这两列从"记录项"变成可行性数(开放问题 19、20)。
|
||||
|
||||
第一波(全 TCP 控制 + stream 数据)已落地;同机混搭(nd `auto` / sd)是第二波。细节 [`notes/p65/README.md`](../notes/p65/README.md),契约 `MobileGL/MG_Remote/CONTRACT-P65.md`。
|
||||
@@ -0,0 +1,37 @@
|
||||
# persistent map、回读与验证(原 ARCHITECTURE §12–§13)
|
||||
|
||||
> 设计细节。设计要点与全部章节的索引见 [`../ARCHITECTURE.md`](../ARCHITECTURE.md)。章节编号沿用原 `ARCHITECTURE.md`,代码注释里的 `ARCHITECTURE.md §N` 按编号在这里找到。
|
||||
|
||||
## 12. persistent map 与 ≥16 MiB 采纳
|
||||
|
||||
`AcquirePersistentMap` 是永久的地址空间捐赠(≥16 MiB 可变 store 自动采纳,MC 26.3 p99 163 → 21 ms),整个 monolith 改造期不动(D-B4)。拆分下三档由 POST 探针选择(spike B 实测):
|
||||
|
||||
| 档 | 形态 | 结论 |
|
||||
|---|---|---|
|
||||
| T0 | client 分配 `AHardwareBuffer` BLOB,server 导入(Vulkan / GLES) | 唯一在两台设备、两个后端上都完整读写的档(P11 主攻) |
|
||||
| T1 | server 导出 opaque fd | 仅 Adreno 的 Vulkan 路径 |
|
||||
| T2 | 拒绝,client 侧推送 | 永久正确回退;**今天 split 用的档**(`MOBILEGL_IPC_ADOPT_TIER=2`),stream 数据面上强制 |
|
||||
|
||||
T2 下 client 侧推送三件套:不做 map/unmap 命令对(payload 带 `hasLiveHostWrites`);在每个 validate 点按块(`MOBILEGL_IPC_PERSISTENT_BLOCK_KB`,默认 64)推送可达的已映射 buffer,脏页追踪 + 哈希抑制只发变化的块(P5d);门 `PersistentCoherentMapScenario`。`mpr` 数的是每一次 `MapPersistent` 发射(铸成或拒绝都算),所以在 monolith 下也可断言。
|
||||
|
||||
## 13. 回读、roundtrip 清单与验证
|
||||
|
||||
### 13.1 稳态零 roundtrip 与不可避免的阻塞点
|
||||
|
||||
- 零 roundtrip:全部 draw / clear / blit / copy / dispatch / XFB / bind / CSO / `set_*` / 上传 / `present`;全部 caps 站点;`glGetError` / `glFinish` / `glFlush`;fence 与 query 的创建和非阻塞轮询;`eglSwapBuffers`;restart / multi-draw。
|
||||
- 不可避免(罕见):握手;surface 生命周期;`glReadPixels` 到客户内存;GPU-write pending 的 buffer 首次 CPU 读;带超时的 sync / query 结果;`glBufferStorage` 的 ack;纹理拉取;ring / stage 耗尽与 present credit。
|
||||
- 跨机上每条 `kWaitReply`(`ResourceCreate`、`SetTextureParams`、纹理 `ResourceSubData`)都是一个 RTT:逐帧次数是 P6.5 必测数,减少它是 P9 / P10 的活。
|
||||
|
||||
### 13.2 五部分验证门(取代 monolith 的字节一致门)
|
||||
|
||||
1. **接口纯度**:A 门 include 图(`scripts/check_include_closure.py`)、B 门 server 镜像不引用 `MG_State::GLState` 与 glslang(P6 的 a6 实测 server 仍链 `MG_Impl`,由 `scripts/link_ratchet.py` 的下行棘轮追踪,P13 转绿)、C 门 `MG_Backend` 里零 `pGLContext`;外加"每个后端 memo 键都是 `{slot, gen}`"(`HandleRecycleScenario`)。
|
||||
2. **语义影子比对** `MOBILEGL_PIPE_VERIFY=1`:tracker 再用 `SnapshotFromGLContext()` 填一份 `PipeInputs`,G4 逐字段每 draw 比对,打印第一个分歧;verify 构建永不出货,活过 P13。
|
||||
3. **行为 A/B**:trace 语料在 monolith-pull / monolith-push / split 下 SSIM ≥ 0.99;`DirectGLES.` 与 `DirectGLES.Split.` 等名集合逐名相同(G2)、测试名只增不删(G14);CTS 逐后端 0.5 pp 内;上传形状金标。
|
||||
4. **性能**:Redmi `2f7cbe2e` reboot-clean、同热窗口配对 A/B,指标是**逐线程 CPU 时间**;对着 pull 臂**只记录、不设门**(用户 2026-09-08)。
|
||||
5. **覆盖 + poison + 句柄纪律**:G6 0 UNMAPPED、dirty-surface 0 未映射、G8 归属完备、逐 verb poison、G7、残余块棘轮。
|
||||
|
||||
两条幸存的字节级等式:`MOBILEGL_BUILD_DISAGGREGATED=OFF` 时库里零 `MG_Remote` 符号;**G1** pull 构建的符号集与 `.text` 对基线恒等(每阶段 0 增 / 0 删 / 0 resize / 0 重命名)。**每个门必须能因它存在的理由变红**(R-16):带阴性对照并真跑过一次红;公共 GL 看不见的改白盒断言;拒绝普查逐用例读私有日志(`ctest -V` 是假零)。
|
||||
|
||||
### 13.3 长期语义门:MGPipe recorder(P13)
|
||||
|
||||
`MG_Test` 的 mock 后端变成 MGPipe recorder,在一组 fixture 上录每 draw 的已推送状态做金标;它只覆盖推送内容,不覆盖后端对它的解释(开放问题 12)。
|
||||
@@ -0,0 +1,33 @@
|
||||
# 帧节奏、进程、EGL 与平台(原 ARCHITECTURE §14–§15)
|
||||
|
||||
> 设计细节。设计要点与全部章节的索引见 [`../ARCHITECTURE.md`](../ARCHITECTURE.md)。章节编号沿用原 `ARCHITECTURE.md`,代码注释里的 `ARCHITECTURE.md §N` 按编号在这里找到。
|
||||
|
||||
## 14. Present、线程与帧节奏
|
||||
|
||||
- `eglSwapBuffers` → `present{frameSerial}` → publish + 敲门铃 → 返回,除非超出 credit;**`present` 与 `eglSwapBuffers` 严格 1:1**(后端的帧边界排空只在 `Present` 内发生)。
|
||||
- `MOBILEGL_IPC_PRESENT_CREDIT` 默认 1(range 1..8):credit 是 run-ahead 唯一的稳态背压,`FrameSerial` 由 client 铸造、1 起算,server 在 `Present()` 返回后归还 credit;等待发生在**编码之前**。credit > 1 从未量过(P10)。
|
||||
- 预算(~850 draw/帧):`SEG_CMD` ~70 KB/帧,`SEG_STAGE` ~1 MB/帧;跨机时这 ~1 MB/帧对着的是 Wi-Fi。
|
||||
- 线程:client v1 不加线程,编码在 GL 线程直接写 ring;server 有 io 线程与**终身持有原生 context** 的 `mgl-srv-apply`。全库无有效亲和性控制(Redmi 内核忽略 app 线程的 `sched_setaffinity`)。
|
||||
- 拆机顺序:publish + server 排空 → 停 apply 线程 → 关传输 → client 排空编译池 → `MobileGL::Destroy()`(落地形状见 §17)。
|
||||
|
||||
## 15. 进程、EGL 与平台(P6 / P6.5 / P12)
|
||||
|
||||
### 15.1 启动与握手
|
||||
|
||||
- 端点:`fork`(`socketpair` + `fork`/`execve`,fd 3 = socket;server 由 `MOBILEGL_IPC_SERVER_PATH` 或 `dladdr` 同目录的 `libMobileGLServer.so` 定位)、`unix:<path>`、`tcp://host:port`。设备侧 supervisor `mobilegl_server_main <endpoint> --serve`:listen → 认证 → 每会话 fork 一个子进程(或进程内线程,P12),同时只服务一个会话,第二个连接 `Refuse{Busy}`。
|
||||
- server 进程里 `Transport` 读作 `Spawn`,**不强制 monolith**(两百多处 `Transport != Monolith` 判断靠它选 server 臂;`CONTRACT-P6.md` §3.1 修订了原"子进程强制 monolith"的规则)。防无界 fork 链靠不继承的 `Dial == No`(`MOBILEGL_IPC_DIAL=no`),spawn 时构造的 envp 剔除 `MOBILEGL_TRANSPORT` 与 `MOBILEGL_IPC_*` 作第二道独立保险;server 主程序自设 `MOBILEGL_IPC_ROLE=server`。
|
||||
- `Hello`(abi、后端类型、指纹、配置、令牌、`LinkTerms` 请求)→ `Welcome`(server 陈述的尺寸、段 / aux 名、`dataNonce`、真实 pid)或 `Refuse{code, detail, 对端值}`;握手路径零 abort。非 loopback 监听必须有令牌(≥16 字节、常量时间比较、数据面按 `dataNonce` 绑定到已认证的控制连接、fork 前认证;Ph,OQ-21 裁定不做 TLS)。
|
||||
- TCP keepalive / 心跳失败**由传输层报告为挂断**,进 device-lost 闩;"闩绝不取自 apply 超时"不因此松动。冷启动期 server 发 `SurfaceProgress`,client 预算这段沉默(控制修订 2)。
|
||||
|
||||
### 15.2 Android
|
||||
|
||||
- 交付链(spike A 已证):APK 唯一可 exec 的位置是 `lib/<abi>/`,server 以 `add_executable` + `lib*.so` 命名构建;从 `untrusted_app` 进程 `fork`+`execve` 同域、零 avc denial;`posix_spawn` 在 minSdk 26 不可用。一份共享库两个角色(stub `dlopen` + `dlsym("mobilegl_server_main")`)。
|
||||
- 离屏 server:前台 `MobileGLServerService`(`:mglsrv`),从 `nativeLibraryDir` exec,fork-per-session,EGL 是 pbuffer / surfaceless。
|
||||
- **上屏 server(P12 子集)**:窗口归 **server 自己**(client 的窗口不跨进程)。`MobileGLDisplayActivity`(`:mglwin`)持全屏 SurfaceView,在自己的进程里用一个线程跑 TCP server(会话串行、会话间重置闩、后端按进程钉住);`ServerDisplay` 以 acquire / release / 几何三个钩子持有窗口,apply 线程以**租约**使用它。client 设 `MOBILEGL_IPC_SURFACE=server` 后**无头**:`eglCreateWindowSurface(NULL)` 发一帧 `WindowKind::ServerOwned`(控制修订 3),不发 `SetWindowHandle`,尺寸由 reply 带回,resize 变成对 server 窗口的几何请求。一台设备同一时刻一个 server;一个会话一种表面模式(`SurfaceModeMismatch`);无显示的 server 对 ServerOwned 具名拒绝(`NoServerDisplay`)。
|
||||
- 失窗:`surfaceDestroyed` 有界(3 s)阻塞,直到 apply 线程(批内每条记录前也检查)放掉后端表面并以 `Fatal{ServerWindowLost}` 闩住会话;server 继续监听。进程内会话结束时手动做进程退出本来白给的清理(Espryt twin 与单元影子、swap interval、Magma renderer)。
|
||||
- `HeadlessGL` 的 fork 预检会产生孤儿 server:server 的 EOF 检测即时且无条件退出。
|
||||
|
||||
### 15.3 Linux / Windows / 崩溃
|
||||
|
||||
- Linux / WSL / CI 永不开窗(`EGL_PLATFORM=surfaceless`);Wayland 不支持。Windows / 桌面 client 在 TCP 终局下是真实形态(Windows 是第二批,开放问题 22);macOS 不拆分;Windows 机器不是正确性门。
|
||||
- server 死:client 读到挂断 → **device-lost 闩**(GL 调用 no-op、`eglSwapBuffers` 返回 `EGL_CONTEXT_LOST`、reset status `GL_UNKNOWN_CONTEXT_RESET`);`Session::Fail` 漏斗向对端发 `SessionFault` 帧命名家族(`FatalFamilies.def`)。**不重启**:`MOBILEGL_IPC_RESPAWN` 今天是具名拒绝。client 死:server 读到 EOF 立即销毁 context 并退出(supervisor 继续服务下一连接)。
|
||||
@@ -0,0 +1,69 @@
|
||||
# 构建布局、开关与计数器(原 ARCHITECTURE §16、附 A、附 B)
|
||||
|
||||
> 设计细节。设计要点与全部章节的索引见 [`../ARCHITECTURE.md`](../ARCHITECTURE.md)。章节编号沿用原 `ARCHITECTURE.md`,代码注释里的 `ARCHITECTURE.md §N` 按编号在这里找到。
|
||||
|
||||
## 16. 构建布局
|
||||
|
||||
```
|
||||
MobileGL/MG_Pipe/ 永远进构建:*.def 目录与表、MGPipe*.h payload / 句柄 / 回调、PipeApply、PipeRoute、generated/*.inc
|
||||
MobileGL/MG_Impl/Pipe/ Tracker、PipeFill、SlotAllocator、CsoCache、CompositeResolver、ResourceTracker、SetHashSuppressor、*Emit.h
|
||||
MobileGL/MG_Backend/MGPipe/ PipeInputs.{h,cpp}
|
||||
MobileGL/MG_Remote/ 仅 MOBILEGL_BUILD_DISAGGREGATED:CONTRACT-*.md、CapsCodec、FatalFamilies.def / FatalFunnel、Handshake.h
|
||||
Protocol/ protocol.fbs、generated/、SurfaceOpCodec、mg_protocol_base.h
|
||||
Transport/ ITransport、SocketTransport、InProcessTransport、ILink、ShmLink、StreamLink、Ring、SessionRings、ReplySlot、EventRing、
|
||||
Doorbell、ShmSegment(+Posix/Win32)、FdPassing、Framing、AuthToken、ControlInbox、LinkMetrics、WireLog
|
||||
Wire/ PipeWireCodec(记录编解码、WireVerbSink)
|
||||
Client/ BackendObject_Remote、ClientSession、EmitTables、WireTables、CapsMirror、PersistentMapTracker、GpuWritePending
|
||||
Server/ PipeApplier、ServerLoop、ServerSession、ServerMain / ServerEntry / ServerSpawn、PreAuthGate、StagedShadow、StagedTextureStore、
|
||||
SurfaceControlFrame、ServerDisplay、InProcessServer、DisplayServerJni
|
||||
```
|
||||
|
||||
- `MOBILEGL_BUILD_DISAGGREGATED`(默认 OFF)追加 `MG_Remote/**`;OFF 时 `MG_Config::Transport` 是 `constexpr Monolith`。`MOBILEGL_BUILD_DISAGGREGATED_INPROC` 隐含前者并加角色隔离:两个角色靠 apply 线程与控制邮箱分开,而不是给 1494 个 `pGLContext->` 读点加 TLS。
|
||||
- `MOBILEGL_TRANSPORT = monolith | inproc | spawn` 是**拓扑**(G1 相关);控制面与数据面由 §11.9 的两个变量选。**split build 不设 `MOBILEGL_TRANSPORT` 时是 monolith 对照臂**。
|
||||
- 四个构建 flavour:pull(默认)、push、verify、split;Android 三份 APK flavour(pull / push / split)。
|
||||
- 测试接线:ctest `ENVIRONMENT` 用 `mgl_itest_join_environment(...)` 构造;每条 split / spawn / tcp 条目带独立日志路径,日志按角色分文件(`<base>.client.log` / `<base>.server.log`);spawn、tcp 车道与 split 名集合一致(`scripts/ci/spawn_lane_parity.py`),每条目记 arm 证明。
|
||||
- CI(`.github/workflows/test.yml`、`apk.yml`):`pipe-gates`(G1–G8、生成器 self-test、符号报告、dirty-surface、字段归属、G5、文档引用 lint)、`flatc-check`、include 闭包、各 flavour 的 build / integration / retrace 车道、APK + AVD。两份 workflow 里的 `feat/disaggregated` 触发器是临时的,合入 `dev` 前移除。
|
||||
|
||||
## 附 A:开关
|
||||
|
||||
CMake:`MOBILEGL_BUILD_DISAGGREGATED`(OFF)、`MOBILEGL_BUILD_DISAGGREGATED_INPROC`(OFF,隐含前者)、`MOBILEGL_PIPE_PUSH`(OFF;push / verify / split flavour 打开)、`MOBILEGL_PIPE_VERIFY`(OFF;隐含 push,永不出货)、`MOBILEGL_PIPE_LEGACY_MEMOS`(ON)、`MOBILEGL_BUILD_SERVER_SPIKE`(仅 Android spike)、`MOBILEGL_FLATC_EXECUTABLE`(只服务 `flatc-check`)。
|
||||
|
||||
运行时,MGPipe(`MobileGL/Config.h`、`ConfigLoader.cpp`):
|
||||
|
||||
| 变量 | 默认 | 说明 |
|
||||
|---|---|---|
|
||||
| `MOBILEGL_PIPE_PUSH` | pull `0`;push `0x1fff` | 子系统位图(`MG_Pipe/MGPipe.h`,位永不复用;依赖两侧都拒);位 63 是"关 CSO 内容寻址"的行为对照 |
|
||||
| `MOBILEGL_PIPE_VERIFY` / `_VERIFY_FATAL` / `_VERIFY_CORRUPT` / `_POISON_OMIT` | 0 / 1 / 空 / 空 | 影子比对与两个阴性对照(verify 构建才有) |
|
||||
| `MOBILEGL_PIPE_HANDLE_ABA_CONTROL` | 0 | 故意打掉句柄身份的阴性对照 |
|
||||
| `MOBILEGL_PIPE_STATS` / `_STATS_PERIOD` / `_STATS_FILE` | 0 / 120 / 空 | 边界计数器(附 B);dump 按角色写 `<base>.client.json` / `<base>.server.json` |
|
||||
| `MOBILEGL_PIPE_LEGACY_MEMOS` / `_TEXEL_RETAIN_MB` / `_INDEX_MIRROR_MB` | ON / 0 / 64 | pre-handle 臂、纹理拉取保留、索引镜像预算(P8) |
|
||||
|
||||
运行时,传输与 IPC:
|
||||
|
||||
| 变量 | 默认 | 说明 |
|
||||
|---|---|---|
|
||||
| `MOBILEGL_TRANSPORT` | `monolith` | 拓扑:`monolith` / `inproc` / `spawn` |
|
||||
| `MOBILEGL_IPC_CONTROL` / `MOBILEGL_IPC_DATA` | `fork` / `auto` | 控制面端点与数据面(§11.9);`MOBILEGL_IPC_SERVER_PATH` 定位 fork 出的 server |
|
||||
| `MOBILEGL_IPC_TOKEN` / `MOBILEGL_IPC_REQUIRE_SAME_BUILD` | 空 / 0 | 令牌(≥16 字节;无令牌只允许 loopback);要求两端同一构建(车道纪律) |
|
||||
| `MOBILEGL_IPC_PREAUTH_*` / `MOBILEGL_IPC_AUTH_BACKOFF_*` | — | server 预认证的上限与失败退避(Ph) |
|
||||
| `MOBILEGL_IPC_CONTROL_TIMEOUT_MS` / `MOBILEGL_IPC_COLD_START_MS` | 5000 / 20000 | 控制回复的稳态上限与冷启动预算 |
|
||||
| `MOBILEGL_IPC_LOG_FORWARD` | 远端时开 | server 日志经控制连接前送,client 写进自己的 `<base>.server.log` |
|
||||
| `MOBILEGL_IPC_SURFACE` | `offscreen` | `server` = 使用 server 自有窗口,client 无头(P12) |
|
||||
| `MOBILEGL_IPC_RING_MB` / `MOBILEGL_IPC_STAGE_MB` | 8 / 32 | `SEG_CMD` / `SEG_STAGE`;stage 的 1/4 是内容分块预算 |
|
||||
| `MOBILEGL_IPC_RUN_AHEAD` | 1 | P5e run-ahead;只是合取式的一半(server 须发布 `kCapRunAheadApply`);`0` 是 A/B 对照 |
|
||||
| `MOBILEGL_IPC_VERB_BARRIER` | 1 | `0` 只作阴性对照,且同时关掉 run-ahead |
|
||||
| `MOBILEGL_IPC_PRESENT_CREDIT` | 1 | 1..8,§14 |
|
||||
| `MOBILEGL_IPC_BATCH_WAITS` | 1 | lockstep 下值类记录发布即返回;verify 强制 0 |
|
||||
| `MOBILEGL_IPC_EVENT_WAIT_MS` | 2000 | 反向事件遇满环时 server 等 client 排空的整笔预算(§11.7) |
|
||||
| `MOBILEGL_IPC_WIRE_DEFERRED_MB` | 64 | Magma wire 臂延迟回收的水位线;`0` 是 M2 阴性对照 |
|
||||
| `MOBILEGL_IPC_PERSISTENT_BLOCK_KB` / `_PERSISTENT_HASH_SUPPRESS` | 64 / 1 | persistent-map 推送粒度 / 只推变化的块;`0` 是对照 |
|
||||
| `MOBILEGL_IPC_ADOPT_TIER` | 2 | `auto/0/1/2`;T0 / T1 今天是 Fatal-at-use |
|
||||
| `MOBILEGL_IPC_STRICT_ERRORS` / `MOBILEGL_IPC_AUDIT` / `MOBILEGL_IPC_ROLE_SPLIT_STATE` | 0 / 0 / 0 | 残余输入读升级为 Fatal / 退休 staging 填 `0xDD` / 双块演练(P5f) |
|
||||
| `MOBILEGL_IPC_SPIN_US` / `MOBILEGL_IPC_SERVER_AFFINITY` | 50 / `auto` | park 前自旋预算 / apply 线程亲和(Redmi 内核忽略) |
|
||||
| `MOBILEGL_IPC_RESPAWN` | — | 具名拒绝:没有阶段实现 server 重启后的全量重推 |
|
||||
|
||||
显式不设立:`MOBILEGL_IPC_PROGRAM`(没有 relink 档)、`MOBILEGL_IPC_VALIDATE_SERVER`。计划中未接线:`MOBILEGL_IPC_POLL_ESCALATE`(P10)、`MOBILEGL_IPC_IDLE_EXIT_S`(未解析)。
|
||||
|
||||
## 附 B:边界计数器(`MobileGL/MG_Util/Metrics/PipeStats.h`)
|
||||
|
||||
关闭时每站点一次全局 load + 一条永不命中的分支。字节类(stage buffer / texture / UBO / client 数组、`pmap`、`resid`、`csob-blob`、`seg` = 写进 `SEG_STAGE` 的字节)、调用类(`draws`、`accessor-calls`、纹理上传 emit / box / rect / jobs、`csom` / `csob`、`mpr`、`trp`、`rsp`、`wrec`、`maxrec` / `ringwraps` / `ringpads` / `ringwaits`、门铃 `srv` / `srvpark` / `cli` / `clipark`)与六个 memo 门的 hit/miss。每 `MOBILEGL_PIPE_STATS_PERIOD` 帧一条 `MGPipe stats:` 汇总行,按角色写日志。读法陷阱:`accessor-calls` 是静态下界,判性能只看 CPU 时间;spawn 下 client 不推进帧窗口(`frames=0`),窗口化字段在 spawn client 上无效,run total 仍有效([`notes/p6/README.md`](../notes/p6/README.md) §12.1)。站点清单(哪些路径**没有**接线)写在 `PipeStats.cpp` 头部,那份清单是契约。
|
||||
@@ -0,0 +1,17 @@
|
||||
# 各阶段的落地形状(原 ARCHITECTURE §17)
|
||||
|
||||
> 设计细节。设计要点与全部章节的索引见 [`../ARCHITECTURE.md`](../ARCHITECTURE.md)。章节编号沿用原 `ARCHITECTURE.md`,代码注释里的 `ARCHITECTURE.md §N` 按编号在这里找到。
|
||||
|
||||
## 17. 阶段落地形状(索引)
|
||||
|
||||
各阶段落地时的形状、偏差与论证原文在 notes 里;下表只列仍然生效的规则。
|
||||
|
||||
| 原节 | 主题 | 仍生效的规则 | 全文 |
|
||||
|---|---|---|---|
|
||||
| 17.1 | verb barrier 与诚实的同地址空间传输(P5) | 同地址空间不得成为旁路(R-2):encoder 把 `MGHostSpan::Ptr` 恒写 `nullptr`;reply 以记录序号为 slot id(R-3);等待看门狗 120 s 只判死锁,不是性能门 | [`notes/p5/README.md`](../notes/p5/README.md) |
|
||||
| 17.2 | 后端槽三类、caps、tight readback(P5) | A 本地答 / B 发射 / C 具名拒绝,永不回落 monolith(R-4);能力只读 `CallMask`(R-8);`ReadPixels` 线上恒 tight,client 按自己的 pack state 散射 | 同上 |
|
||||
| 17.3 | server 角色、shadow 与退出(P5) | apply 线程终身持有 native context,EGL 罕见操作经控制邮箱进 apply 线程;staged shadow 只交 server-owned 拷贝;有界退出顺序 | 同上 |
|
||||
| 17.4 | lane 隔离(P5) | 每条 split 条目私有日志;E1 / E3 控制把"选中条目被跳过"判为失败 | 同上 |
|
||||
| 17.5 | class-C verb 迁移(P5b) | 迁移顺序由动态普查决定;一次迁移 = 一个发射器 + 一个 sink 体;记录逐字带 GL 调用参数,未测量的形式按名拒绝(规则 D) | [`notes/p5b/README.md`](../notes/p5b/README.md) |
|
||||
| 17.6 | 共享地址空间访问归零(P5c) | server 端纹理 staged shadow;`SEG_EVENT` 是唯一反向通道;sink / twin 按记录句柄解析;`applier_reset` / `object_death` 控制记录;角色守卫 `Fatal{RoleViolation}` | [`notes/p5c/README.md`](../notes/p5c/README.md) |
|
||||
| 17.7 | run-ahead(P5e) | 规则 F:unbarriered 记录的 apply 不读任何 client 内存(违反即具名 Fatal,与 strict 开关无关);`gPipeInputs` 是 server 角色内存;`RunAheadArmed()` 在首次 caps 采纳时锁存,只能关不能开;`RUN_AHEAD=0` 是 A/B 对照,`VERB_BARRIER=0` 会连带关掉 run-ahead | [`notes/p5e/README.md`](../notes/p5e/README.md) |
|
||||
@@ -0,0 +1,36 @@
|
||||
# 构建、运行与测试
|
||||
|
||||
> 上手说明。文档导航见 [`../README.md`](../README.md);全部开关见 [`../design/09-build-switches-counters.md`](../design/09-build-switches-counters.md) 附 A。
|
||||
|
||||
## 四种构建
|
||||
|
||||
| 构建目录 | CMake | 用途 |
|
||||
|---|---|---|
|
||||
| `build-linux` | 默认(pull) | 出货形态;G1 的基准——它的二进制必须逐字节不变 |
|
||||
| `build-push` | `-DMOBILEGL_PIPE_PUSH=ON` | 单进程,但后端改读前端推来的状态 |
|
||||
| `build-verify` | push + `-DMOBILEGL_PIPE_VERIFY=ON` | 每个 draw 把推来的状态与前端实际状态逐字段比对;慢,永不出货 |
|
||||
| `build-split` | push + `-DMOBILEGL_BUILD_DISAGGREGATED=ON -DMOBILEGL_BUILD_DISAGGREGATED_INPROC=ON` | 可以拆成两线程 / 两进程 / 两台机器 |
|
||||
|
||||
## 选拓扑
|
||||
|
||||
split 构建在运行时用 `MOBILEGL_TRANSPORT` 选拓扑;**不设就是单进程(monolith)对照**:
|
||||
|
||||
| `MOBILEGL_TRANSPORT` | 形态 |
|
||||
|---|---|
|
||||
| 不设 / `monolith` | 单线程直调 |
|
||||
| `inproc` | 同一进程两个线程(CI 的主验收形态) |
|
||||
| `spawn` | 两个进程;再用 `MOBILEGL_IPC_CONTROL`(`fork` / `unix:<path>` / `tcp://host:port`)与 `MOBILEGL_IPC_DATA`(`auto` / `shm` / `stream`)选控制面与数据面,跨机时还要 `MOBILEGL_IPC_TOKEN`(≥16 字节) |
|
||||
|
||||
```text
|
||||
MOBILEGL_TRANSPORT=inproc MOBILEGL_ITEST_REQUIRE_GPU=1 ctest --test-dir build-split -L integration-split --output-on-failure
|
||||
ctest --test-dir build-split -L 'integration-(spawn|tcp)' --output-on-failure # 条目自带 spawn / tcp 环境与私有日志
|
||||
```
|
||||
|
||||
其他常用旋钮:`MOBILEGL_IPC_RUN_AHEAD`(默认 1,client 发完就走)、`MOBILEGL_IPC_STAGE_MB`(默认 32,其 1/4 是大块上传的分块预算)、`MOBILEGL_IPC_SURFACE=server`(使用 Android server 自己的窗口)、`MOBILEGL_PIPE_STATS=1`(边界计数器,日志里的 `MGPipe stats:` 行)。
|
||||
|
||||
## Android
|
||||
|
||||
- 三份 APK flavour:pull / push / split(Gradle 属性 `mobilegl.pipePush`、`mobilegl.buildDisaggregated`、`mobilegl.buildDisaggregatedInproc`)。split APK 同样要设 `MOBILEGL_TRANSPORT`,不设就是单进程对照。
|
||||
- 设备侧 server 有两种形态:离屏的前台 Service(`MobileGLServerService`)与上屏的 `MobileGLDisplayActivity`;同一时刻只有一个。
|
||||
- 电脑当 client、手机当 server 的跨机配置(起 server、设令牌、`adb forward` 备用路线)见 [`../notes/p65/README.md`](../notes/p65/README.md) 的"两边怎么配"。
|
||||
- 性能对比只用 Redmi `2f7cbe2e`,按 [`pin-verification-2026-09-07.md`](pin-verification-2026-09-07.md) 定频,reboot-clean、同热窗口、配对 A/B。
|
||||
@@ -0,0 +1,14 @@
|
||||
# 代码地图
|
||||
|
||||
> 参考。文档导航见 [`../README.md`](../README.md);目录布局与构建选项见 [`../design/09-build-switches-counters.md`](../design/09-build-switches-counters.md) §16。
|
||||
|
||||
| 路径 | 作用 |
|
||||
|---|---|
|
||||
| `MobileGL/MG_Pipe/` | 接口本身:调用目录 `PipeCalls.def` 与其他 `.def` 表、payload / 句柄 / 回调头、`PipeApply`、`PipeRoute`、`generated/*.inc`(生成物,提交进树) |
|
||||
| `MobileGL/MG_Impl/Pipe/` | 前端:Tracker、`PipeFill`、`SlotAllocator`、`CsoCache`、`CompositeResolver`、`ResourceTracker`、各族 `*Emit.h` |
|
||||
| `MobileGL/MG_Backend/MGPipe/` | `PipeInputs`:后端读的、被前端推来的状态块 |
|
||||
| `MobileGL/MG_Remote/` | 拆分专用(仅 `MOBILEGL_BUILD_DISAGGREGATED`):`Protocol/`(控制面 schema)、`Transport/`(控制面 `ITransport` × 数据面 `ILink`)、`Wire/`(记录编解码)、`Client/`、`Server/`;各阶段的 wire 契约 `CONTRACT-*.md` |
|
||||
| `MobileGL/MG_Util/Metrics/PipeStats.{h,cpp}` | 边界计数器(`MOBILEGL_PIPE_STATS=1`) |
|
||||
| `MobileGL/MG_Test/Pipe/`、`MG_Test/Wire/`、`MG_IntegrationTest/Harness/*Peek` | 目录 / tracker / 发射器单元测试、wire 与 remote 单元测试、白盒断言 |
|
||||
| `scripts/` | 生成器(`gen_pipe.py`、`gen_pipe_field_ownership.py`、`gen_pipe_dirty_surface.py`、`gen_protocol.py`)、门脚本(`symbol_report.py`、`check_include_closure.py`、`link_ratchet.py`、G5 保护区脚本、`check_doc_citations.py`)、`ci/` |
|
||||
| `tools/trace_replay/`、`tools/device_bench/` | trace 回放(含 TCP 矩阵跑器)、设备定频与配对 A/B 工具 |
|
||||
@@ -0,0 +1,21 @@
|
||||
# 工程纪律与验证门
|
||||
|
||||
> 参考。验证门的设计见 [`../design/07-memory-readback-verification.md`](../design/07-memory-readback-verification.md) §13.2;各阶段实际跑门的结果在 `../notes/<阶段>/README.md`。
|
||||
|
||||
## 每个提交
|
||||
|
||||
- 默认 ALL target 必须完整构建;禁止提交热路径插桩(CI grep 门)。
|
||||
- **每个门必须能因它存在的理由变红**:带阴性对照并真跑过一次红(R-16);公共 GL 看不见的改用白盒断言。
|
||||
- 正确性门是硬门;**性能对着 pull 臂只记录、不设门**(用户 2026-09-08),但数字必须真的采到。专门的优化阶段排在路线图推完之后。
|
||||
- **拆分不顺手修 `dev` 的 bug**:发现的 `dev` 侧缺陷记成独立条目、独立 PR。
|
||||
|
||||
## 每个阶段出口
|
||||
|
||||
- 跑一次五部分门:接口纯度、语义影子比对、行为 A/B、性能记录、覆盖与句柄纪律。
|
||||
- 四条字节 / 名字级等式:**G1** pull 构建的符号集与 `.text` 对基线恒等(0 增 / 0 删 / 0 resize / 0 重命名);**G2** pull 与 push 的测试名集合相同;**G5** 受保护区域逐字节一致;**G14** 测试名只增不删。
|
||||
- 性能判据是**逐线程 CPU 时间**;设备对比走 reboot-clean + 同热窗口配对 A/B,只用 Redmi `2f7cbe2e`([`pin-verification-2026-09-07.md`](pin-verification-2026-09-07.md));Windows 机器不是正确性门。
|
||||
- 收官由异模型族审查一次,发现进入下一阶段首轮;实现方与审查方错开模型族;不过度验证(用户 2026-09-16,ID-66)。
|
||||
|
||||
## CTS
|
||||
|
||||
`gl44to46` 约 56,271 例,单独计价:逐阶段只跑该阶段可能影响的具名块;完整 caselist 只在架构边界与合并 `dev` 之前跑,放 CI 不放关键路径。
|
||||
@@ -0,0 +1,23 @@
|
||||
# 术语
|
||||
|
||||
> 参考。文档导航见 [`../README.md`](../README.md)。
|
||||
|
||||
| 术语 | 意思 |
|
||||
|---|---|
|
||||
| 前端 / 后端 | 前端 = `MG_State` + `MG_Impl`,处理应用的 GL 调用;后端 = `MG_Backend`,真正调用驱动 |
|
||||
| Espryt / Magma | 两个后端:Espryt 走 OpenGL ES(DirectGLES),Magma 走 Vulkan(DirectVulkan) |
|
||||
| client / server | 拆分后的前端角色 / 后端角色;可以是两个线程、两个进程或两台机器 |
|
||||
| monolith / inproc / spawn | 三种拓扑:单线程直调 / 同进程两线程 / 两个进程(可跨机) |
|
||||
| pull / push | 后端自己去读前端状态(旧做法)/ 前端把状态推给后端(MGPipe) |
|
||||
| verb | 会让后端真正做事的命令:draw、dispatch、clear、blit、回读、XFB、query、纹理操作。状态只在 verb 之前推送 |
|
||||
| 句柄 `{slot, gen}` | 对象的身份:client 分配的槽号 + 复用代数,server 从不回传 |
|
||||
| CSO | 常量状态对象(渲染状态、顶点格式、sampler、sampler view、shader):client 按内容去重,server 按句柄缓存 |
|
||||
| 控制面 / 数据面 | 握手与罕见控制消息走的连接 / 命令记录与批量数据走的通道;两者可分别选(共享内存或字节流、本机或 TCP) |
|
||||
| lockstep / run-ahead | client 每条命令都等 server 做完 / client 发完就走,只有需要答案的命令才等 |
|
||||
| barrier tax | 拆成两线程后多出来的逐线程 CPU 代价(split − push) |
|
||||
| device-lost | server 崩溃或断线后 client 进入的状态:GL 调用变空操作、交换缓冲返回"上下文丢失",而不是崩溃或卡死 |
|
||||
| class A / B / C | 后端函数表的槽:A 本地回答,B 发成记录,C 还没迁移、按名拒绝 |
|
||||
| Track V / Track H | 值类读点的迁移(整块数据过线)/ 对象类读点的迁移(对象指针换成句柄) |
|
||||
| G1 / G2 / G5 / G14 | 验证门:单进程构建二进制逐字节不变 / pull 与 push 测试名集合相同 / 受保护的后端函数逐字节不变 / 测试名只增不删 |
|
||||
| red-once(R-16) | 每个门都必须真的跑红过一次,证明它能因为它要抓的问题而失败 |
|
||||
| `MGGen` | server 私有的"我重建了驱动对象"纪元,永不过线;与句柄代数严格分开 |
|
||||
@@ -0,0 +1,17 @@
|
||||
# 仍开放的债务(跨阶段)
|
||||
|
||||
> 路线图只列"有债务、看这里"。每条写明去向;已关闭的历史债与当时的完整债务表见 [`p5b/README.md`](p5b/README.md) 末节。开放问题另见 [`OPEN-QUESTIONS.md`](OPEN-QUESTIONS.md)。更新:2026-09-24。
|
||||
|
||||
| 债务 | 去向 |
|
||||
|---|---|
|
||||
| `SEG_REPLY` 2 MiB 单槽 payload cap、PACK-PBO 回读的 fire-and-forget | P9(ID-47、ID-57);stream 链路上 cap 变成 `maxReplyBytes` 窗口 |
|
||||
| 未接入内容分块的 record 类型:program archive 与 `draw_vbo` range 尾 | P6.5 sl 与 P8 共用的分片(开放问题 11) |
|
||||
| class-C 余项与仿真路径:`SetSwapInterval`(P10)、`DeleteTransformFeedback`(P9);multi-draw client indices、RGB CPU mip、`texture-remint-pull` 等具名拒绝 | P8 / P9 |
|
||||
| `GetCaps` 的两个 blobref | 一旦运输,必须有 server → client 的 carrier rule,不能套 `SEG_STAGE` |
|
||||
| E1 对照的重定义(ID-122) | 无主(开放问题 24) |
|
||||
| `MOBILEGL_IPC_IDLE_EXIT_S` 未解析;`CONTRACT-P6.md` 仍把 `DynamicBackendParameters` 定宽记在 P7 名下(已由 P6.5 wf 落地) | 文档 / 小修 |
|
||||
| P12 文档债:`MG_Remote/CONTRACT-P12.md` 未写;`protocol.fbs` 的窗口注释仍是旧说法 | P12 收尾 |
|
||||
| 树外脚本:普查跑器、`wsl_p5_gate.sh` 等在 `~/w7/notes/`,git merge 不会传播 | 需要时入库 |
|
||||
| 临时 CI trigger | 合入 `dev` 前删除 `test.yml` / `apk.yml` 的 `feat/disaggregated` 触发器 |
|
||||
|
||||
已关闭的历史债(P5 的 27 个 wrong-answer、`rsp` 残余读、P5b 的 inproc 依赖、ABI 指纹、默认 staging 装不下 128 MiB 上传、184 符号棘轮 CI 门)及当时的完整债务表见 [`notes/p5b/README.md`](p5b/README.md) 末节。
|
||||
@@ -1,6 +1,6 @@
|
||||
# 阶段笔记(notes)
|
||||
|
||||
这一层放**各阶段的全部细节**:每个阶段一个目录,目录里的 `README.md` 是该阶段的**汇总**(摘要、阶段表原行、逐门实测、落地形状、当时的进度页),其余文件是那个阶段的原始工作笔记——包计划(`BRIEF-*.md` / `TASK-*.md`)、契约草稿、集成者逐条裁定(`INTEGRATOR-DECISIONS-*.md`)、只读审计与普查、包报告、设备记录与证据。上层四份索引([`../ROADMAP.md`](../ROADMAP.md)、[`../ARCHITECTURE.md`](../ARCHITECTURE.md)、[`../MEASUREMENTS.md`](../MEASUREMENTS.md)、[`../CURRENT_STAGE_PROGRESS.md`](../CURRENT_STAGE_PROGRESS.md))只留摘要并链到这里。
|
||||
这一层放**各阶段的全部细节**:每个阶段一个目录,目录里的 `README.md` 是该阶段的**汇总**(摘要、阶段表原行、逐门实测、落地形状、当时的进度页),其余文件是那个阶段的原始工作笔记——包计划(`BRIEF-*.md` / `TASK-*.md`)、契约草稿、集成者逐条裁定(`INTEGRATOR-DECISIONS-*.md`)、只读审计与普查、包报告、设备记录与证据。顶层的索引([`../ROADMAP.md`](../ROADMAP.md)、[`../MEASUREMENTS.md`](../MEASUREMENTS.md)、[`../CURRENT_STAGE_PROGRESS.md`](../CURRENT_STAGE_PROGRESS.md))只留一句话并链到这里;不属于某个阶段的设计正文在 [`../design/`](../design/),上手与参考在 [`../guide/`](../guide/)。
|
||||
|
||||
## 按阶段
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
| **P12**(当前)server 自有屏幕窗口 | [`p12/`](p12/README.md) | `PLAN-P12.md`、`INTEGRATOR-DECISIONS-P12.md`、`device/` |
|
||||
| P8 / P9 / P10 / P11 / P13(待排) | [`p8/`](p8/README.md)、[`p9/`](p9/README.md)、[`p10/`](p10/README.md)、[`p11/`](p11/README.md)、[`p13/`](p13/README.md) | 目前只有路线图上的完整范围与出口门 |
|
||||
|
||||
跨阶段:[`OPEN-QUESTIONS.md`](OPEN-QUESTIONS.md)(开放问题全文,编号稳定);[`handoff/`](handoff/)(P7 的集成者审查稿,路径被 P7 交接文档引用,保持原位);[`recovered/`](recovered/)(从失败的 workflow 运行里捞回的 P0–P2 期材料,`wf1` 设计定稿前的计划草案、`wf2` P0、`wf3` P0.5、`wf4` P1、`wf5` P2;与阶段目录逐字节重复的副本已于 2026-09-24 删除,只留独有的)。
|
||||
跨阶段:[`DEBTS.md`](DEBTS.md)(仍开放的债务与去向)、[`OPEN-QUESTIONS.md`](OPEN-QUESTIONS.md)(开放问题全文,编号稳定);[`handoff/`](handoff/)(P7 的集成者审查稿,路径被 P7 交接文档引用,保持原位);[`recovered/`](recovered/)(从失败的 workflow 运行里捞回的 P0–P2 期材料,`wf1` 设计定稿前的计划草案、`wf2` P0、`wf3` P0.5、`wf4` P1、`wf5` P2;与阶段目录逐字节重复的副本已于 2026-09-24 删除,只留独有的)。
|
||||
|
||||
## 旧引用怎么找
|
||||
|
||||
@@ -39,6 +39,10 @@
|
||||
| `docs/Disaggregated/P6-SPAWN-PLAN.md`、`P6-CONTRACT-DRAFT.md`、`P6-ENDSTATE-REVIEW.md` | [`p6/`](p6/README.md) 同名文件 |
|
||||
| `docs/Disaggregated/CURRENT_STAGE_STATUS.md` | 并入 [`../CURRENT_STAGE_PROGRESS.md`](../CURRENT_STAGE_PROGRESS.md);其中 P7 的出口门 / 已落地 / 真机三节在 [`p7/README.md`](p7/README.md),P12 部分在 [`p12/README.md`](p12/README.md) |
|
||||
| 旧 `CURRENT_STAGE_PROGRESS.md` 的 P6.5 第一波与 P6 收官快照 | [`p65/README.md`](p65/README.md)、[`p6/README.md`](p6/README.md) |
|
||||
| 当前阶段页的明细(带裁定编号的表、余项清单、证据位置) | [`p12/README.md`](p12/README.md) 的"当前状态详情"节 |
|
||||
| `docs/Disaggregated/devices/pin-verification-2026-09-07.md` | [`../guide/pin-verification-2026-09-07.md`](../guide/pin-verification-2026-09-07.md) |
|
||||
| `ARCHITECTURE.md` 的设计正文(§1–§17、附 A / B) | [`../design/`](../design/) 下十个文件,编号不变;对照表在 [`../ARCHITECTURE.md`](../ARCHITECTURE.md) |
|
||||
| 旧 `README.md` 的构建运行、代码地图、术语;旧 `ROADMAP.md` 的通用纪律 | [`../guide/`](../guide/) |
|
||||
|
||||
**按编号引用的旧章节**(小节编号在新位置原样保留):
|
||||
|
||||
@@ -47,8 +51,8 @@
|
||||
| `MEASUREMENTS.md` §1 / §2 / §3 / §4 / §5 | [`p0`](p0/README.md) / [`p1`](p1/README.md) / [`p2`](p2/README.md) / [`p3a`](p3a/README.md) / [`p4a`](p4a/README.md) 的"实测"节 |
|
||||
| `MEASUREMENTS.md` §6 / §7 / §8 / §9 | [`p5`](p5/README.md) / [`p5b`](p5b/README.md) / [`p5c`](p5c/README.md) / [`p5d`](p5d/README.md) |
|
||||
| `MEASUREMENTS.md` §10 / §11 / §12 / §13 | [`p5e`](p5e/README.md)(§10、§11)/ [`p6`](p6/README.md) / [`p34b`](p34b/README.md) |
|
||||
| `ARCHITECTURE.md` §17.1–§17.4 / §17.5 / §17.6 / §17.7 | [`p5`](p5/README.md) / [`p5b`](p5b/README.md) / [`p5c`](p5c/README.md) / [`p5e`](p5e/README.md) 的"落地形状"节(上层 §17 留一张索引表) |
|
||||
| `ROADMAP.md` 阶段表某行、"P5c 计划与审计"、"立即的下一项:P6.5 第一波"、债务表、开放问题 | 对应阶段 `README.md` 的"阶段表行"节;[`p5c`](p5c/README.md);[`p65`](p65/README.md);[`p5b`](p5b/README.md) 末节;[`OPEN-QUESTIONS.md`](OPEN-QUESTIONS.md) |
|
||||
| `ARCHITECTURE.md` §17.1–§17.4 / §17.5 / §17.6 / §17.7 | [`p5`](p5/README.md) / [`p5b`](p5b/README.md) / [`p5c`](p5c/README.md) / [`p5e`](p5e/README.md) 的"落地形状"节([`../design/10-phase-shapes.md`](../design/10-phase-shapes.md) 留一张索引表) |
|
||||
| `ROADMAP.md` 阶段表某行、"P5c 计划与审计"、"立即的下一项:P6.5 第一波"、债务表、开放问题 | 对应阶段 `README.md` 的"阶段表行"节;[`p5c`](p5c/README.md);[`p65`](p65/README.md);[`DEBTS.md`](DEBTS.md)(历史全表在 [`p5b`](p5b/README.md) 末节);[`OPEN-QUESTIONS.md`](OPEN-QUESTIONS.md) |
|
||||
| `ARCHITECTURE.md:NNN`、`MEASUREMENTS.md:NNN` 这类**行号**引用 | 指向写下它时的那个版本,今天已不对应;按上下文所说的节去找,或 `git show <当时的头>:docs/Disaggregated/<文件>` |
|
||||
| `~/w7/notes/<phase>/<file>.md` | `docs/Disaggregated/notes/<phase>/<file>.md`(这些笔记原先只存在于一台机器的 `~/w7/notes/`) |
|
||||
|
||||
@@ -59,11 +63,11 @@
|
||||
| **收录** | 全部 `.md` 笔记,以及阶段自己决定入库的小脚本、判读工装与证据摘要(如 `p7/device-window-*/`、`p12/device/`、`p12/gate/`) |
|
||||
| **不收录** | 大体积、随每次运行重新产生、只在产生它的机器上有意义的证据数据:完整运行日志、APK 与 `.idsig`、`nm` 转储、设备 logcat。上层文档引用它们时给的是路径,不是内容 |
|
||||
|
||||
新阶段开工时建 `notes/<阶段>/`,把计划、裁定与报告都放进去;阶段收官时补齐该目录的 `README.md` 汇总,上层索引只加一行摘要。
|
||||
新阶段开工时建 `notes/<阶段>/`,把计划、裁定与报告都放进去;阶段收官时补齐该目录的 `README.md` 汇总,顶层索引只改一行。
|
||||
|
||||
## 不受 citation lint 管
|
||||
|
||||
CI 的文档引用 lint 只检查 `docs/Disaggregated/*.md`(非递归,`.github/workflows/test.yml`),**本目录不在其中,这是有意的**:笔记里的 `file:line` 写在各自当时的代码头上,拿今天的 HEAD 解析必然大面积失败——那是它们作为历史记录的正确状态。要核对某份笔记的引用,用它自己声明的那个 revision:
|
||||
CI 的文档引用 lint 只检查顶层、`design/` 与 `guide/`(`.github/workflows/test.yml`),**本目录不在其中,这是有意的**:笔记里的 `file:line` 写在各自当时的代码头上,拿今天的 HEAD 解析必然大面积失败——那是它们作为历史记录的正确状态。要核对某份笔记的引用,用它自己声明的那个 revision:
|
||||
|
||||
```bash
|
||||
python3 scripts/check_doc_citations.py --rev <当时的头> docs/Disaggregated/notes/<phase>/<file>.md
|
||||
|
||||
@@ -22,6 +22,56 @@
|
||||
|
||||
- **P12 子集**:APK `26.09.29b7b28-trace`(**控制协议修订 3**,与 p7w8 的 server 不兼容),证据 `notes/p12/device/`(九格拼图 `evidence-strip.png`)。七个编号检查在三个 head 上跑过,`trial-*/` 是按 head 归档的旧轮次。
|
||||
|
||||
## 当前状态详情(2026-09-24,原 `CURRENT_STAGE_PROGRESS.md` 正文)
|
||||
|
||||
> 顶层 [`CURRENT_STAGE_PROGRESS.md`](../../CURRENT_STAGE_PROGRESS.md) 只留摘要;带裁定编号的落地表、门表、余项清单与证据位置在这里。
|
||||
|
||||
### 1. 一句话
|
||||
|
||||
Android 上的 server 自建窗口(自己的 SurfaceView)把 IPC 渲染流**上屏**,client 以 `WindowKind::ServerOwned` **完全无头**接入。子集已实现并入,**未收官**:审查轮修复之后欠一次真机复测,两个出口门都还没打。
|
||||
|
||||
### 2. 已落地
|
||||
|
||||
| 面 | 内容 | 出处 |
|
||||
|---|---|---|
|
||||
| wire | `WindowKind::ServerOwned`、控制修订 2 → 3、`SurfaceReply` 带回窗口真实尺寸或具名拒绝 | PLAN-P12 §1 |
|
||||
| server 窗口 | `ServerDisplay`(钩子 + 租约 + 有界 3 s `Detach`);会话表面模式首次创建即闩定(`SurfaceModeMismatch`);无显示具名拒绝(`NoServerDisplay`);失窗 → `Fatal{ServerWindowLost}` → client 读干净 device-lost | ID-P12-2、ID-P12-3 |
|
||||
| client | `MOBILEGL_IPC_SURFACE=server` → 无头窗口 surface,不发 `SetWindowHandle`;resize 变成对 server 窗口的几何请求,采用 server 回的真实尺寸 | ID-P12-10 |
|
||||
| 进程内 server | `MobileGLDisplayActivity`(`:mglwin`)里一个线程跑 TCP server:会话串行、会话间重置闩、后端按进程钉住;与离屏 `MobileGLServerService` 互斥,一台设备同一时刻一个 server | ID-P12-4 |
|
||||
| 审查轮 | 10 个问题已修;两个大项:批内失窗检查(`DrainRing` 每次 pop 前)、会话间 Espryt 单元影子悬空;另有推流中停机、会话结束清理、陈旧尺寸、日志转发 | ID-P12-5 … 11 |
|
||||
|
||||
### 3. 门
|
||||
|
||||
| 门 | 状态 | 证据 |
|
||||
|---|---|---|
|
||||
| G1 | ✅ pull 构建同机前后 `.text` 一致、符号 0 增 0 减 | ID-P12-15 |
|
||||
| 主机门 | ✅ unit 2605/2605,spawn / tcp 与三条 Magma 双进程车道全绿;剩余红全部归容器的 CMake/CTest 版本 | ID-P12-14 |
|
||||
| 真机七项检查(Redmi `2f7cbe2e`) | ✅ 审查轮之前(`29b7b284`):双后端上屏 ssim 1.0、串行 + `Refuse{Busy}`、失窗 device-lost、离屏不回归、无显示具名拒绝、一台设备一个 server;⏳ **审查轮之后未复测** | PLAN-P12 §2、ID-P12-13 |
|
||||
| 出口门 (a) | ❌ FCL 同机 spawn、双后端入世界、**杀 server** 产生干净 device-lost(本轮的驱动是 trace_replay,device-lost 由按 HOME 触发) | PLAN-P12 §3 |
|
||||
| 出口门 (b) | ❌ **另一台机器**的 client 经 TCP 在 Redmi 入世界,并记录 P6.5 必测数(本轮 client 是 `adb forward` 环路) | PLAN-P12 §3 |
|
||||
|
||||
### 4. 在跑
|
||||
|
||||
无。
|
||||
|
||||
### 5. 下一步
|
||||
|
||||
1. **真机复测**(ID-P12-13):重建 APK(控制修订 3),重跑七项检查,外加"推流中按 HOME:回调返回前 surface 已释放,并记耗时";在与原门同版本的 CMake/CTest 上复跑一次主机门。
|
||||
2. **出口门**:先 (b) 跨机 TCP 入世界 + P6.5 必测数,再 (a) FCL + 杀 server。
|
||||
3. **收尾**:写 `MG_Remote/CONTRACT-P12.md`;修 `notes/p12/gate.sh` 的 G1 符号比较(与基准不同源,会印出误导的 `added=N removed=N`);按惯例做一次收官审查。
|
||||
4. **阶段表行里的余项**(交接时在树上核过,均未做):in-process server 的 `unix:` 监听、DirectGLES `g_Display` / `g_Surface` / `g_Context` 去全局、cached-app freezer、多 context、FCL env 与 plugin 开关表接线、D8 窗口种类白名单。
|
||||
5. 其后按 [`ROADMAP.md`](../../ROADMAP.md):monolith 跑道 P8;IPC 跑道 P9 → P10 → P11;P6.5 残余(39 例 device 矩阵 + 必测数)与 P3b/P4b 余项并行;`CONTRACT-P7.md` §12 的记录债按阶段认领。
|
||||
|
||||
### 6. 阻塞 / 需要人
|
||||
|
||||
- 没有需要用户决策的阻塞。真机复测需要一台装有 NDK、连着 Redmi 的机器(审查轮所在的云容器没有)。
|
||||
- 已知环境敏感项:`IterationRPProgram203Scenario`(`integration-magma-full-split`)偶发红,按 ID-P7-22 / ID-P12-12 读作"既有、环境敏感"(钉 lavapipe ICD 即绿),不阻塞 P12。
|
||||
|
||||
### 7. 真机与证据
|
||||
|
||||
- 当前 APK `26.09.29b7b28-trace`(控制修订 3,与 P7 终局窗口的 p7w8 server 不兼容);设备 Redmi `2f7cbe2e`(Adreno 830),定频协议 [`devices/pin-verification-2026-09-07.md`](../../guide/pin-verification-2026-09-07.md)。
|
||||
- 真机证据 `notes/p12/device/`(`evidence-strip.png` 九格拼图,`trial-*/` 是按 head 归档的旧轮次);主机门日志 `notes/p12/gate/`;设备脚本 `notes/p12/android/`。
|
||||
|
||||
## 本目录
|
||||
|
||||
| 文件 | 内容 |
|
||||
|
||||
@@ -91,4 +91,4 @@ interfaces for `/proc/gpufreqv2/`)**. For those two devices run `bench.sh --no-p
|
||||
`tools/device_bench/pin_device.sh <serial> pin|unpin|check` (pure adb + su; exit 0 PINNED, 1 DRIFT,
|
||||
2 UNPINNED, so `check && measure` cannot measure unpinned). The profiles under `devices/` carry
|
||||
`PROFILE_VERIFIED=1` for the nodes and pins named in the file, not for `bench.sh`'s ability to drive
|
||||
them; the verification evidence is `docs/Disaggregated/devices/pin-verification-2026-09-07.md`.
|
||||
them; the verification evidence is `docs/Disaggregated/guide/pin-verification-2026-09-07.md`.
|
||||
|
||||
Reference in New Issue
Block a user