ROADMAP gains the section "the immediate next item": a design, not an implementation, for running the frontend on an x86_64 WSL process and the backend on the Redmi over TCP, with the integration lane and the CI retrace subset as the acceptance. The P6.5 row now points at it. What the design rests on, verified in the tree: the control plane already treats the rendezvous as a NAME with two ordered connections (SocketTransport), ClientSession::StartOverSocket already separates "how the socket came" from the handshake, MGPBlobRef addresses kSegStage only through one ResolveOrFatal, the reply header and the event heads are already wire-shaped, LogLine exists in CtrlMsg with no producer, goldens are shared between desktop and device, and the trace APK can already exec libMobileGLServer.so from nativeLibraryDir. What it does not rest on, stated rather than assumed: ILink is declared and unused beyond LinkProgress, ShmLink does not exist, both sessions hold SessionSegments directly - the lk seam body from the end-state review was never built and is this wave's first package (lk2). The manifest has no INTERNET permission. The mechanism: StreamLink as mirrored private segments replicated by messages - same sizes both ends, same offsets, so the codec, the watermark rules, the apply thread and the encoder do not change; RingControl becomes Progress frames sent on flush-on-idle (the recorded amendment to "appliedSeq is never lazy": late by at most one flush, never early); replies become seq-stamped messages; the event ring is mirrored with the P5e blocking rule intact. Cross-architecture makes the PipeFields.def layout digest mandatory (wf). Server logs on another machine make LogLine forwarding mandatory (lf) so the 34 server-log readers stay unchanged. A --serve supervisor forks one child per session on the device, started by a foreground Service in the trace APK. Packages lk2 -> wf -> ct -> sl -> lf -> sv -> t65, each with red-once; the gate list, the mandatory numbers (kWaitReply per frame x RTT, SEG_STAGE bytes/frame against the link, PRESENT_CREDIT 1..3), the two-sided configuration runbook and the risks (RTT per reply row, single large uploads, AP isolation) are in the section. check_doc_citations: 74 citations, 0 problems.
81 KiB
MGPipe 路线图
状态:P0、P0.5、P1、P2、P3a、P4a、P5、P5b、P5c、P5d、P5e、P5f 已收官。P5f 于 2026-09-20 完成主机门、Redmi 六个 clean-boot 臂、跨模型族审查及修复验证;最终验收代码为 WSL
cfca93c7885fd8db1e881f91189ba59090ae9c52(Windowsc42577a4等价)。证据见notes/p5f/close-report.md、close-review.md、device-report.md。P6 全部包(a6 / c6 / lk / so / sm / cp / hs / dl / st / t6)已落地于feat/disaggregated(头16bfab10,2026-09-21);出口门第 1–6 项达成,第 7 项(真机配对 A/B 热窗口)与第 8 项(三个强制性能数)尚未正式采集,故 P6 记为「实现完成、出口性能/设备数待补」,未宣布收官。逐包状态、出口门逐项、证据见CURRENT_STAGE_PROGRESS.md;设计见ARCHITECTURE.md。P5e 的历史性能与 E1 对照债保留在阶段行、P5E-RUNAHEAD.md和MEASUREMENTS.md,不由 P5f 的新对照代为关闭。CI / inproc 功能补齐已收官(2026-09-21):拆分主产物 + inproc 主验收、完整 retrace 双 transport、18 项 CI 门、双 ABI APK / AVD 全绿并合入feat/disaggregated(e1bf3677),见CURRENT_STAGE_PROGRESS.md§2.8。
P5f 后续 Magma run-ahead 已完成(
194382c9,2026-09-20):主机真实排队、credit、负控及 Vulkan 同步验证通过,Redmi 同包 Magma 开/关与 GLES 三臂 FCL 世界实测通过。见 报告。P6 / P7 全阶段状态不因此改变。
终局(2026-09-22 改判):client 与 server 可以在不同机器、不同 OS / 架构上,经 TCP 连接;同机的
spawn(AF_UNIX + 共享段)保留为本机形态。传输栈拆成两根独立可选的轴——控制面(ITransport:fork继承 fd /unix:<path>/tcp://host:port)与数据面(ILink:共享段ShmLink/ 字节流StreamLink)——由握手协商、可以混搭(同机可以控制面走 TCP、数据面走共享段以降开销),上层只见ITransport+ILink,不得按链路种类分支。此前P6-ENDSTATE-REVIEW.md假设的 AVF pVM /AF_VSOCK终局撤回(该文顶部有更正)。这一改判把 P6.5 从条件项变成 IPC 跑道的关键路径,把 wire 定宽 + 布局摘要从 P7 / hs 的"以后"变成 P6.5 的第一个包,把 Ph(含配对 / 认证)变成 P12 非 loopback 监听的前提;设计见ARCHITECTURE.md§11.9。
通用纪律(每个 commit)
- 默认 ALL target 必须完整构建;禁止提交热路径插桩(CI grep 门)。
- 每个门必须能因它存在的理由变红:每个门带阴性对照并真跑过一次红(R-16);公共 GL 看不见的改白盒断言。
- 正确性门是硬门;性能对着 pull 臂只记录、不作阻塞门(用户 2026-09-08:push 比 pull 多约 10% 逐线程 CPU 已被接受;专门的优化阶段排在路线图推完之后)。唯一不可谈判的是数字必须真的采到。
- 每阶段出口跑一次五部分门(
ARCHITECTURE.md§13.2);性能判据是逐线程 CPU 时间;设备对比走 reboot-clean + 同热窗口配对 A/B,只用 Redmi2f7cbe2e(devices/pin-verification-2026-09-07.md);Windows 机器不是正确性门。 - G1:pull 构建的符号集与
.text字节对基线恒等(每阶段 0/0/0/0);G2:pull 与 push 的 ctest 名集合相同;G14:测试名只增不删;G5:保护区字节一致。 - 拆分不借机顺手修
dev的 bug:句柄化过程中发现的dev侧缺陷记成独立条目、独立 PR(开放问题 15、17)。 - 过程(用户 2026-09-16,ID-66):不过度验证;轮次间不做对抗性审查,每个 P 阶段收官由 Codex 审一次,发现进入下一阶段首轮;只读 / 非核心任务派给 Codex;实现方与审查方错开模型族。
两条跑道分开:monolith 跑道 P0 → P0.5 → P1 → P2 → P3a → P4a → P3b/P4b → P7 → P8 → P13,每段可独立交付、可随时中止;IPC 跑道 P5 → P5b → P5c → P5d → P5e → P5f → P6 → P6.5 → Ph → P12 → P9 → P10 → P11(同机臂)。目标顺序:先完整的 separate-thread rendering(P5b,已达成),再让 inproc 变成只经 wire 交换的诚实两角色(P5c),再把它的 CPU 代价压到可接受(P5d),然后退役 draw path 上的 lockstep(P5e:client 发了就走,server 只从记录带来的句柄解析对象),然后是 separate-process transport(P6,此时只是传输替换)。P6 之后(2026-09-22 定):传输栈两轴化并能跨机(P6.5),server 能安全地接受不是它自己产生的连接(Ph),server 成为拥有显示的常驻应用(P12),异步反向通道与节奏(P9、P10)建立在 P6.5 的消息式 reply 之上,同机的 ≥16 MiB 采纳(P11)最后。
阶段
| 阶段 | 状态 | 落地什么 | 验收门 / 证据 |
|---|---|---|---|
| P0 卫生、度量、门、骨架 | ✅ | 边界计数器;PipeCalls.def 完整目录 + payload POD + 生成器 G1–G7 + CI pipe-gates;gen_pipe_dirty_surface.py;check_doc_citations.py;MOBILEGL_PIPE_* 开关;MG_Remote/{Protocol,Transport} 骨架 + protocol.fbs + flatc-check + MG_Test/Wire;三个严格 no-op 收益;spike A / B;retrace --env 透传 |
单元 / 集成 / trace 逐名不变;wire 层测试绿;两台设备字节 / 调用基线在案;spike 结论。MEASUREMENTS.md §1 |
| P0.5 值头与制品头抽取 | ✅ 5d99ee43 |
MG_Pipe/MGPipeValueTypes.h;ProgramArtifacts.h;Visit() 归档 + sizeof 绊线;CI -H include 闭包断言(scripts/check_include_closure.py)+ scripts/symbol_report.py |
测试名零删除、.text 不变、符号 0 增 / 0 删 / 42 重命名 |
P1 PipeInputs 替换与 verify harness |
✅ | MG_Backend/MGPipe/PipeInputs.h(63 字段);277 处箭头 + 58 行非箭头逐条转换;逐 verb 类填充点;逐 verb 世代 poison;G4 影子比对器 + verify CI 模式;push-on-mutation 三字段 |
pull nm 不变;单元 1485×3;integration-gpu 878;integration-verify 818 零 Fatal;79 retrace verify 79/79 armed 零分歧。§2 |
| P2 渲染状态 CSO + 第一片 Track H + 残余值块 | ✅ 738b289d |
Tracker(dirty 位、5 个聚合世代、抑制器);dirty-surface 成门;MGPipeRenderStateSpans chunk 表 + G7;CsoCache(64);create/bind_render_state + set_dynamic_state(SyncRenderState 一行不动);pixel pack / patch / attrib defaults;ResidualValueBlock 棘轮 1248 → 8;Espryt slot 表 + Magma vertex input 重键(11 条 memo 删除 + 2 条重键);MOBILEGL_PIPE_LEGACY_MEMOS;三个 capability 真存储 |
G1 4 处认定 resize、.text +160 B;G2 名差 0;单元 1566×3;integration-gpu 916 三臂;retrace 79/79 push + verify;对照 44/44。GO/NO-GO(2026-09-08):继续。§3 |
| P3a handle wave 1(Espryt):buffer、VAO | ✅ fde5fda3 |
九条 resource_* + 五条 vertex-input 调用接线到句柄形 MGPipeResourceOps;第七张 Espryt slot 表;ResourceRespecify 的 kNeedsAck 谓词;VAO twin 六条身份 memo 在句柄臂退役、四条重键;mpr 定义;位 7/8,push 默认 0x1ff |
五部分门全绿:G1 0/0/0/0 .text +0;G5 十一函数;单元 1619×3;integration-gpu 958 五臂;verify 842;retrace 79/79 ×2。实际 1 天。§4 |
| P4a handle wave 2(Espryt):FBO / 纹理 / sampler / program | ✅ 8c458cd5 |
十四条族调用接线(framebuffer state、sampler state / view、texture params、shader images、shader state、draw / dispatch program、global constants);纹理 / renderbuffer 复用 resource_*(目录不加行);六种 kind 按 {slot, gen} 重键;CompositeResolver;Named = 3 framebuffer 记录;消费者门 + 客户端依赖表;位 9–12,push 默认 0x1fff;emulation 在 split 下具名 Fatal |
全门:G1 0/0/0/0;G5 17 区;G2 2902;单元 1785×3;integration-gpu 1117 七臂;verify 920;retrace 79/79;八族拒绝普查 0;PipeApplyPeek 白盒对照。实际 1 天。§5 |
| P5 传输 + inproc applier + 发射表 | ✅ ff2994d9..37fc4fdb |
同一 codec 上的 InProcessTransport;四种 build flavour;发射表三类 / caps mirror / reply mailbox;apply-thread context 终身持有;lockstep verb barrier;tight ReadPixels;persistent-map 块推送;G8 字段归属生成器;split 测试 / CI / APK 车道;八包(c0 契约、w1 codec、s1 session、p1 归属、b1 persistent map、t1 车道、c1 路由、v1 server)+ j0 / x2 / v1-r3 收尾 |
joint:OpenRA inproc 2/2 SSIM 1.0;integration-split 21/21;E1 14/14 红;G1 0/0/0/0。收尾头全门在 E3(a) 停止(设计性 skip 被判红),Part 2/4 未到达;收官审查 1 blocker / 10 major / 2 minor → P5b r1/r2。§6 |
P5b inproc verb migration |
✅ 37fc4fdb..82683d4a |
class-C 普查决定顺序;d1 19 / i1 7 / t2 6 / f1 11 槽迁移;sync 五槽;具名 blit;GLES mip descriptor;r1 / r2;收官审查三项修复。发射表 A=2 / B=54 / C=15 | 主机全门 348d22a4 complete;integration-split 107/107;合并普查零回退、79 trace 72/6/1;Redmi 正确性 8/8 + 四臂 A/B、barrier tax 首测。§7 |
P5c inproc 共享内存读点归零 |
✅ 11ac3de6..b88e8487 |
server 端纹理 staged shadow(StagedTextureStore,句柄为键、整段覆盖、defined-ness 追踪);SEG_EVENT 三个 producer + kEventGlError(反向通道零裸指针);sink / twin 按记录句柄解析(blit 具名臂经 verb 句柄工作区,Magma 走 G6 消费臂);applier_reset / object_death 控制记录(opcode 77/78,framebuffer 首个 wire delete);set_context_values(79)退役全部值类 BARRIER-PULLED 行,三 shutter 自答;双层 Fatal{RoleViolation} 守卫 + InBarrierWait 接线;两个具名豁免 scope(通告家族 P4b/P7、G6 registry 家族 P3b/P4b)承接未到期的债 |
角色守卫开启下 unit 2187/2187(strict 下同绿)、integration-split 111/111、每层守卫 red-once 按名变红;纹理 0xDD audit 在 bsl / complementary 两条 in-world trace 零 Fatal;rsp 按帧实测(bsl 948.5/帧,残留全为钉住的对象类 15 行);G1 三个认定 resize(SwapchainObject::Create / CopyTexSubImage2D / ScopedRestartIndexSubstitution,均已具名)+ .text −16 B、pull 构建零 MG_Remote 符号;G5 保护区字节一致;双生成器 --check/--self-test 绿 |
P5d inproc 性能专项 |
✅ cb06538c、56a77348、1f8de61b |
一二轮:persistent-map 推送三级臂(mprotect 脏页追踪 + 哈希抑制 + 绑定过滤 PushDrawConsumers)、barrier 批处理 MOBILEGL_IPC_BATCH_WAITS + 延期销毁队列、自旋时钟 64 合 1、零页注册 + shadow 页对齐分配。三轮(按符号化 profile 逐项切,四包 + 一份 lockstep 可行性研究):apply 空转轮询零锁(控制邮箱原子影子位)、Doorbell::Wait 校准自旋稳态不读时钟、wait/park 计数进 stats;OnApplyThread() 内联为线程指针比较、三个 scope 深度计数器退掉 thread_local;shadow 分配尺寸补页、追踪范围向外对齐、边块哈希消失、脏页排空修复;image unit 高水位、残余填充 memo、server stamp 常量表;generate_mipmap 不再免等(apply 读残余输入)、death 测试围栏 |
三轮设备(Redmi,CPU 定频):inproc 64.3 → 103-106 fps(p50),client 线程 CPU 15.0 → 9.2 ms/帧、apply 12.4 → 7.0-7.4;monolith 同场景 115(120 Hz vsync 封顶的运行)/ 206(一次未封顶,渲染线程 5.0 ms/帧 大核);配对比较以逐线程 CPU/帧为主,见报告"数字"节;unit 2203/2203、integration-split 111/111(两条曾 flaky 的用例复跑 6/6);双生成器、include 闭包、dirty-surface、doc 引用绿。未完成:R-1 序列化(研究结论:留给 P3b/P4b 后的 P11 gPipeInputs 版本化)、线程放置(内核忽略亲和)、SetHashSuppressor 哈希等小项。报告:P5D-INPROC-PERFORMANCE.md |
| P5e 退役 Espryt draw path 的 lockstep | ✅ 已收官(2026-09-19),附一条具名未决:E1 对照需按 ID-114 重新定义 | 契约 MG_Remote/CONTRACT-P5E.md(规则 F:未设障记录的 apply 不得读任何 client 内存;ID-84 限定它只约束未设障记录);裁定 ID-80..136(~/w7/notes/p5e/INTEGRATOR-DECISIONS-P5E.md)。十二个包,三波:wave 1 c0e(契约 + 线上行:PipeCalls.def 第五列 WaitClass、set_program_bindings opcode 80、kCapRunAheadApply 位 10 仅 DirectGLES 臂、kDrawClientArrays、子系统位 13、两个旋钮)→ id(registry 按 {slot, gen} 重键、by-handle resolver、分配器守卫);wave 2 vi / sb / pg / tx2 / fb 五个 per-draw 家族 ∥ ra(等待规则、present credit、gPipeInputs 变 server 角色内存),加 fix1(设备发现的 monolith 臂回归,ID-107);wave 3 pa(program 家族离开前端)、mv(multi-draw + 五个档位的门禁条目)、gl(门禁机制本身 + 两处潜伏崩溃)、ra2(翻开关后暴露的竞态)。kMGPipeP5eRunAheadReady 与 kMGPipeP5eClientWaitRuleLanded 均已翻。最大的那次迁移(逐 draw 的 GetProgramForDraw)没有改动任何线上结构:记录早就带着逐 link 的 ProgramArchive,缺的是仍去问前端对象的读者。报告 P5E-RUNAHEAD.md |
出口门已逐条跑过(BRIEF-P5E §3,2026-09-19 在集成树上,run-ahead 已武装):item 1 unit 在 strict 两臂均 2256/2256;item 2/3 integration-split 179/179、逐条普查零 Fatal{、Admitted{ 恰为八行;item 4(a) RUN_AHEAD=0 作为配对对照 179/179 绿;item 4(b) VERB_BARRIER=0 变红(车道 62/179);item 4(c) Magma 逐字记下 run-ahead requested, server does not publish kCapRunAheadApply - running lockstep,而 Espryt 对照记下 run-ahead ARMED;item 5 在集成树上重跑 red-once(ID-121 重排后的形式):倒掉 pa 的臂得 GetProgramForDraw@DrawArrays、倒掉 mv 的臂得 GetBoundVertexArray@DrawArrays,恰好各一个具名对,还原后各自转绿;item 7 MOBILEGL_IPC_AUDIT=1 179/179。唯一未过的是 E1 对照脚本本身(ID-122):已修掉它两个遮蔽性缺陷(选中了一个在本车道永远跑不起来的阳性对照用例;ctest 与日志助手对“谁被选中”意见不一致),但它索要的 Fatal{BarrierViolation} 在整次运行里 出现 0 次:BATCH_WAITS 默认值 1 下那个重叠不发生,而 run-ahead 武装后 VERB_BARRIER=0 还会顺手关掉 run-ahead(ID-114),它产生的那条臂既不是 lockstep 也不是 run-ahead。E1 需要重新定义它要证伪什么,不是再调阈值;在想清楚之前不动它的计数规则,否则只是换一种伪绿。G1 仍由 CI 断言(ID-123) |
| P5f 一切状态上 wire | ✅ 已收官(2026-09-20);验收代码 Windows c42577a4 / WSL cfca93c7885f |
f0 普查、f1 双块、fc 控制帧、fe Espryt 字段、fm Magma record consumers、fs 静态世代/角色及 liveness、fr registry、fv 反向回调均已合并。字段归属 41 RECORD_SUPPLIED / 6 APPLIER_DERIVED / 0 BARRIER_PULLED / 16 FATAL;strict / dualblock 预期 marker 集合均空。控制帧与静态量工作已完成,不再列为 P6 待实现项 | unit 2310 = 2300 PASS + 10 skip;GPU 1377 零失败;ordinary / strict split 180 = 176 PASS + 4 skip;双块 212 = 206 PASS + 6 精确具名 skip;双后端 RSP 门 2 PASS / 0 skip、逐帧 rsp=0。G1 .text 10806051、symbols 27815 恒等且 0/0/0/0;G2 3016 同名;G14 3646→3744 无删名。逐包与收官修复均有 red-once;Claude 异族终审 P1/P2 已修;Redmi 六 clean-boot 臂 60 PASS / 6 skip / 0 failed、36 个窗口 rsp=0。详见 close-report |
| P6 spawn transport | ⏳ 全部包已落地(2026-09-21,头 16bfab10);出口门 1–6 达成,7/8 待采(真机配对 A/B + 三个性能数)。收官前另欠(2026-09-22 核出):server 进程侧 PipeStats 从未初始化(srv=0 srvpark=0 是结构性零,门 8 无 server 半边;修复进行中)、184 符号棘轮 CI 门(契约 §12.1);契约 §2 分给 hs 的 LinkTerms / Refuse / 指纹拆分未落地,移交 P6.5(负控 S6 随之) |
十包全落地:a6 进程装配/链接边界审计 → c6 规范契约 + 数据面缝惰声明 → lk RingControl 按写者重组 → so SocketTransport(AF_UNIX SOCK_STREAM + SCM_RIGHTS,12 条契约用例)→ sm 两个独立启动的进程(非 fork,name 汇合、共享匿名段,四守卫接线)→ cp EGL 控制帧跨进程并应答(InitCapabilities wire kind 11)→ hs build stamp 显式 present 入指纹 + abiMajor/Minor 补检 + 真实 pid → dl device-lost 闩(取自描述符挂断绝不取自超时)+ Session::Fail 漏斗(FatalFamilies.def 词表 + 90 站点归一 + SessionFault 帧命名家族)+ Fatal 普查门 → st fb-slot memo 具名 Fatal → t6 integration-spawn 车道(102/102)+ 名集合结构性一致门 + per-role 日志(<base>.client.log / <base>.server.log)。Android CI 新增 spawn-acceptance 臂,App 从 nativeLibraryDir 解析 server 路径。真机 Redmi 2f7cbe2e 上 OpenRA.DirectGLES spawn ssim=1.0 连跑三次 + 6 case 绿;DirectVulkan 真机分离路径不确定分歧(inproc 与 spawn 同表现→ Magma × 真实 GPU,非 P6,桌面 lavapipe 为 1.0)。边界仍为 pbuffer / surfaceless / 离屏;真窗口归 P12。契约 MG_Remote/CONTRACT-P6.md;a6 产出 a6-audit-v1.md、a6-link-experiment.md(server 不能不链 MG_Impl:184 符号,P7 101 / P3b-P4b 14 / 共有 60 / P6 自己 6);终局重审 P6-ENDSTATE-REVIEW.md |
出口门(契约 §9)1–6 ✅:G1 pull 0/0/0/0、.text 恒 0xa52203;integration-spawn 与 split 名集合一致(可比 102==102)且同数绿;进程树运行时恰多一子进程、结束为零;每条 spawn 条目记子进程 pid 与 transport=spawn(--require-spawn 门);每包 red-once。7/8 待采:真机 reboot-clean 同热窗口配对 A/B、三个强制性能数(socket 门铃 vs condvar、SEG_STAGE 字节/帧、chunking 后记录/帧)尚未正式记录 |
| P6.5 传输栈两轴化 + 跨机 | 立即的下一项(第一波:全 TCP,WSL client ↔ adb 设备 server,设计见本表下方「立即的下一项」节;2026-09-22 定) | 包:wf wire 定宽与布局摘要——wireFingerprint = PipeFields.def 派生的逐成员 name / offsetof / sizeof 摘要 + 有序目录摘要 + 记录头布局 + 两个 blob codec 版本 + kOpCount + 字节序 / 指针宽度,保留 sizeof(MGPCaps) / sizeof(DynamicBackendParameters)(wire 事实)、删 sizeof(GLFunctionsTable);buildFingerprint 只在 Dial == Fork 下比较;DynamicBackendParameters / MGPCaps / RenderStateParameters 整块 blob 的定宽化(两端是不同编译器产的不同二进制,long / padding / 位域差异都是静默读错状态);Refuse{code, detail, 对端值} 控制消息,握手路径零 abort → ct 控制面端点种类(fork 继承 fd / unix:<path> / tcp://host:port;DialMode{No, Fork, Connect} 与 MGPipeShouldDial() 按契约 §3 补齐;TCP keepalive 失败作为传输层报告的挂断进 dl 的闩,不是 apply 超时)→ nd 数据面协商(LinkTerms{dataPlane, delivery, wireForm, maxReplyBytes, cmdWindowBytes, stageWindowBytes} 入 Hello / Welcome,四个尺寸由 server 陈述;dataPlane ∈ {SharedSegments, Stream};auto 的判据是段实际交付成功而不是地址判断,回落必须具名入日志与 stats;链路工厂在会话建立时选定一对,之后不变——缝是 setup-time 的,契约 §8.2)→ sd 共享段交付与控制面解耦(ShmLink 自带 AF_UNIX aux 汇合名、在 Welcome 里公布,SCM_RIGHTS 只走它;于是控制面 TCP + 数据面共享段在同机是合法配对;ITransport 收窄为纯控制帧,描述符传递归数据面)→ sl StreamLink(分块封帧;watermark 变消息且规则逐字保留——late-never-early、>= 不是 ==、倒退是 Fatal;appliedSeq 修正案 + flush-on-idle;reply 变自带 seq 的消息,本链路上删 slot 池 / 取模 / stamp 自检;SEG_STAGE 变发送窗口,三个 cursor 要么完整复活要么不用、不留中间态;program archive 与 draw_vbo range 尾分片;GetCaps 两个 blobref 变消息;每方向每进程一条专用读线程)→ t65 车道。上层纯度:SessionProducer / SessionConsumer / ServerLoop / PipeWireEncoder、ClientSession.cpp 的 kSegEvent 挂载与 ResourceTracker.h 的 writeback 解析都只经 ILink。设计 ARCHITECTURE.md §11.9 |
门:inproc / spawn 车道结果数与逐线程 CPU 逐字不变(ILink::ReserveRecord 虚调用代价实测,RingProducer::Reserve 失去内联是具体风险);纯度 grep——ShmLink / StreamLink / RingControl / ReplySlotPool / EventRingConsumer / m_shm. 只出现在 Transport/ 与其测试,上层零链路种类分支;2×2 矩阵车道(控制面 {unix, tcp} × 数据面 {shm, stream},loopback)名集合与 spawn 一致且同数绿,每条目记 control=… data=… 的 arm 证明(ID-124 的形状);跨机臂一条真 trace 逐像素;wf 三条 red-once(临时树里同宽成员互换 → 摘要变而 stamp 不变;wireMajor = 99 → 两端具名拒绝、server exit 0、client 不回落 monolith;仅 stamp 不同的对端在 Dial != Fork 下连接成功——正控制);必测数:逐帧 kWaitReply 次数(ResourceCreate / SetTextureParams / 纹理半边 ResourceSubData 都在此列,TCP 上每次即一个 RTT)、SEG_STAGE 字节/帧对链路带宽、PRESENT_CREDIT 1..3 配对、混搭(tcp + shm)对(unix + shm)的逐线程 CPU 差 |
| Ph 不可信对端加固 + 配对 | 待排,P12 非 loopback 监听之前 | Session::Fail 的策略翻成每会话闩(每个依赖 [[noreturn]] 的站点造真实返回路径,不是一个函数翻一下;P6 dl 已把站点收口到一处);handle slot 预算(BackendSlotTable 的两条拒绝从 MOBILEGL_ASSERT 变 release 下的具名拒绝,杜绝静默 null twin);readback 尺寸门挪 server 侧且在 resize 之前设绝对上限;StagedTextureStore 按该 level 宣告范围设界;ArchiveVector 按 count * sizeof(element) 设上限;SEG_EVENT 溢出不再能让一个仅仅不排空的对端 60 秒杀掉宿主渲染器;配对 / 认证(Hello 带配对令牌,无令牌只允许 loopback;令牌够不够、要不要 TLS 见开放问题 21);段 / 窗口尺寸由 server 钳制(P6.5 nd 已陈述) |
非 loopback 监听在无令牌时具名拒绝;畸形帧 / 超界计数 / 不排空对端的模糊臂下 server 不 abort、会话闩住、下一连接照常服务;Session::Fail 站点普查门继续零新增;契约 §12.2 五处无界分配各一条 red-once |
| P3b / P4b 深化(Espryt) | 待排 | 按存储属主键控的发射游标与 view 索引重映射;g_fboTextureSyncList;ResolvedTextureBindingMemo / SamplerPassMemo / image sweep / program registry 重键;XFB scatter 搬到 client;删 fragColor 重推导 workaround 与 g_broadcastMemo*;raw-depth-fetch sampler 的 monolith 清理(transport 原生臂已由 P5f fs 落地);回读 / pack state;P4a 记下的 R-3 / R-4 / R-5 / R-7 / R-10 / R-11;ProgramArtifacts.h 的 NDK 尺寸钉;链接实验分给它的 14 个符号(D12) |
纹理 / program 场景;CTS texture_* / shader_image_* / packed_pixels 在 0.5 pp 内;每一个 Iris trace;TextureUploadShapeScenario 升级成门 |
| P7 DirectVulkan(Magma)全量迁移 | 部分已提前落地,阶段未收官 | 38919d45 已补实际游戏的 server buffer consumers 和 Android 旋转 blit(报告);194382c9 又完成 Magma run-ahead、GPU 延迟退休与 VAO 身份碰撞修复(报告);其余工作:其余子系统:SetupDrawSnapshot 探测字段塌成 dirty mask;占位纹理原生化;具名 UBO host payload(D-B8);内部 shader 烘焙;VertexInputStateFactory 内容寻址 CSO;D18 容器纪律保留。P6 真机发现(2026-09-22 归此):DirectVulkan 分离路径在 Redmi 上分歧且不确定(0.976–0.989,inproc 与 spawn 同表现,pbuffer monolith 1.0)——Magma × 真实 GPU;树上 @P7 具名拒绝清单(multisample-blit、depth-stencil-mipmap、vertex-format-conversion、texel-buffer-native-format、unaligned-*-range、uniform-buffer-byte-tail / dynamic-offset、copy-image-in-place、Magma 无 StateObjectDeathOps);链接实验分给 P7 的 101 个符号(D12)。DynamicBackendParameters 定宽重写不再是 P7 的——它是跨平台 wire 的前提,归 P6.5 wf |
集成 + trace 在 Magma 的 push 与 split 下全绿;verify 零分歧;真机 DirectVulkan split ssim 回到 1.0;184 符号棘轮(契约 §12.1)下降 P7 的 101 个——原 "nm -D libMobileGLServer.so | grep glslang 为空" 不可达,D12 裁定 server 镜像就是整个 libMobileGL.so;CTS 0.5 pp 内。再基线检查点:中点完成子系统 < 40% 立即重定基线 |
| P8 emulation 下放 + 索引宿主镜像 + 协议广度 | 待排 | MG_Impl/Pipe/HostResolve.cpp(beba0256 落地为 owned buffer;最大索引扫描、*IndirectCount 解析,逐站点 reconcile);Server/IndexHostMirror 待重裁(树里 0 处;a6 §6 实测宿主索引 span 无 producer、client 索引已是 owned buffer——按 kCapNeedsHostIndexBytes 的真实消费者重新裁定,不照抄 ARCHITECTURE.md §10.3);multi-draw client indices(MultiDrawElements+CLIENT_INDICES 仍具名拒绝);CopyImage 镜像搬到 client;viewport-array 回放验证;generate_mipmap 计划 + CPU 回退纹素;texture-remint-pull 仿真下放(15 处具名拒绝);大 blob carrier 只剩 program archive 与 draw_vbo range 尾(a6 §6;与 P6.5 sl 共用同一分片);kCapDriverOrderedXfbCapture(0 处); |
'^DirectGLES\.Split\.' 与 '^DirectGLES\.' 逐名相同;trace split 双后端 SSIM ≥ 0.99 含两个 coherent_as_flush fixture;ClientArrayAfterComputeWriteScenario;create-indirect 上 roundtrips-per-frame 读零;index-mirror-bytes 逐用例发布 |
| P9 反向通道 | 待排,P6.5 之后(2026-09-22 重定范围) | 异步 reply 语义,机制建立在 P6.5 的消息式 reply 之上(不再造 SEG_REPLY slot 池——池今天就有,stream 链路会删它);2 MiB reply cap 与 chunked readback(契约 §10.2.3 从 P6 改判至此,ID-47);PACK-PBO 回读 fire-and-forget + client 侧 MarkGpuWritten(ID-57,P6 未做);十个回调里未武装的五个:OnXfbScatterReady、OnTextureWriteback、OnTexturePullRequest、OnMipLevelsGenerated、OnLog 分级(ClientSession.cpp:429;OnBufferWriteback / OnGpuWritten / OnSurfaceChanged / OnGlError / OnCapsInvalidated 已武装);OnGpuWritten 收窄;OnBufferWriteback 批处理 + epoch 排序;OnGlError 有序(CONTRACT-P5C §4.2);纹理拉取四条缓解 + 终止符;XFB 命名空间(DeleteTransformFeedback 的 server 侧泄漏——按裁定无行、最后两个 class-C 之一)。SEG_EVENT 溢出策略ARCHITECTURE.md §11.7),剩余的"不排空对端杀宿主"归 Ph |
回读 / XFB 场景在 split 下绿;TextureRemintPullScenario(含无解用例);拉取计数逐 trace 发布;两条故障注入 |
| P10 sync / query / present 节奏 | 待排 | QueryCreate 为 kWaitNone);轮询入口成门铃点 + MOBILEGL_IPC_POLL_ESCALATE(0 处;TCP 上是首要杠杆之一);DirectGLES 的 fence 完成度来自逐 fence 退休(g_completedFrameSerial 仍只在 Present 前进;Magma 那半已由 run-ahead 落地);非 present fence tick(唯一记载处);present 1:1(已是);ConfigLoader.cpp:414);PRESENT_CREDIT > 1 从未量过(重审 §8.7)——带延迟链路上的首要杠杆,inproc 臂上即可取;roundtrip 计数器与输入延迟直方图(今天只有 MapPersistentRoundtrips);SetSwapInterval(最后一个手写 class-C,EmitTables.cpp:1665) |
query / XFB / AsyncCompile 场景在 split 下绿;trace 上 draw / state / upload 路径 roundtrip 读零;零 timeout 轮询有界退出;credit 1..3 在 TCP loopback 与跨机臂上的配对记录;配对 A/B 记录 |
| P11 persistent map 与 ≥16 MiB 采纳 | 待排,同机臂专属、已让位(2026-09-22) | T0 / T1 只对共享映射成立:dataPlane = Stream 上点名 T0 / T1 是具名拒绝并强制 T2(重审 §6 第 5 项),今天 MOBILEGL_IPC_ADOPT_TIER 0/1 已是 Fatal-at-use、kSegAdopt 只有枚举;前置开放问题 3。POST 探针档位选择(T0 主攻,Adreno 可选 T1,T2 回退);SEG_ADOPT 生命周期绑 completedFrameSerial;gPipeInputs 版本化 / 双缓冲(P5d 研究:draw barrier 延后一个 verb 的前提,须在 P3b/P4b 的句柄键控 twin 表之后)。P5e 的更正:P5e 退役 draw lockstep 没有需要它——ra2 实测证明字段值已由 SuppliedFieldMask 分区,当时那次竞态在元数据标量上,修法是让守卫测事实而非断言未来(ID-135)。本行保留采纳存储的一般版本/时序问题;契约 §3.2 的 per-role stamp 已由 P5f 落地,不再是待办 |
LargeArenaAdoptionScenario 在所选档下绿;26.3 与两个 Create fixture SSIM ≥ 0.99;Adreno 830 上 p99 帧时与峰值 RSS 对采纳基线(163→21 ms / 40→115 fps / ~400 MB)回归不超过 10% |
| P12 Android 生产窗口路径 | 待排,Ph 之后 | 按重审 §6 改小:SurfaceANativeWindow 的 NDK API 这条约束不再成立;WindowKind::ServerOwned;两个后端各一条 CreateEGLWindowSurface 臂;m_windowHandle 四处写点 + sameHandle 去重键(a6 §6);DirectGLES g_Display / g_Surface / g_Context 去全局;kEventSurfaceChanged 的 Width / Height 缺口(Espryt 只发布格式,server 拥有显示时 client 默认 FBO 会停在 512×512 占位值);client 侧不再无条件发 SetWindowHandle(BackendObject_Remote.cpp:215——今天 FCL 里开 spawn,第一次 eglCreateWindowSurface 就在 server 进程 abort);server app 监听 unix: 与 tcp://(后者 Ph 之后);server 生命周期绑 Activity;cached-app freezer(SPAWN-PLAN §8.2);多 context(契约 §11);FCL env 与 plugin APK 开关表接线 |
两个门:(a) Minecraft 经 FCL 同机 spawn 在 Adreno 830 双后端入世界,杀 server 产生干净 device-lost latch;(b) 另一台机器上的 client 经 TCP 在 Redmi 上进世界(数据面 stream),并记录 P6.5 的必测数 |
| P13 退役 pull 路径 | 待排 | 删 SnapshotFromGLContext() 非 verify 分支、MGB_CTX、MOBILEGL_PIPE_PUSH、MOBILEGL_PIPE_LEGACY_MEMOS;保留 MOBILEGL_PIPE_VERIFY;MGPipe recorder;删 set_residual_value_state;在计数器活着的情况下重调幸存缓存容量;建立模块 target 与边界(D12:SOURCE_FILES 是一张平表喂两个库 target,无模块可链) |
static_assert(sizeof(ResidualValueBlock) == 0);三道纯度门在非 verify 构建上转绿;recorder 金标建立;monolith 逐线程 CPU 不差于 P0 基线 |
CTS 周转单独计价(gl44to46 约 56,271 例):逐阶段只跑该阶段可能影响的具名块;完整 caselist 只在架构边界与合并 dev 之前跑,放 CI 不放关键路径。
立即的下一项:P6.5 第一波 —— 全 TCP 传输(client 在 WSL,server 在 adb 设备)
状态:设计已定(2026-09-22),未实现。 目标形态:x86_64 的 WSL 进程作为 client,Redmi
2f7cbe2e(aarch64,Adreno)上的 server 进程作为 server,两者在同一局域网内经 TCP 连接;integration全部用例与 CI retrace 的 split 子集在这条链路上跑通并对上 golden。本节是 P6.5 行的第一波,只做「控制面 TCP + 数据面 stream」这一对;同机的「TCP 控制 + 共享段数据」混搭(ndauto/ sd)是第二波,本节不做。
1 树上已有的(不重写)与必须先补的(不能绕)
| 已有 | 出处 | 对本波的意义 |
|---|---|---|
控制面 SocketTransport:Listen / AcceptPair / ConnectTo,汇合点是一个名字,两条连接按序接受(第一条控制、第二条 aux);auxFd = -1 已是「这条链不传 fd」的诚实答案 |
SocketTransport.h:70-91 |
TCP 只是第三种名字(tcp://host:port);两条连接的规矩原样用:第一条控制,第二条从 aux 变成数据流 |
帧:[u32 'MGLF'][u32 len][payload],FrameReader 是真正的字节流重组器 |
Framing.h |
数据连接复用同一重组器,只换 magic |
server 主程序:endpoint 来自 argv[1] / MOBILEGL_IPC_ENDPOINT;Listen → AcceptPair → 建 backend → Accept(Welcome + 七个描述符)→ 控制泵,对端 EOF 即退出;MOBILEGL_IPC_DIAL=no 反递归 |
ServerMain.cpp(正在被门 8 的工作修改,不引行号) |
单会话进程形状保留为 child;设备侧要加一个 supervisor 循环 |
client:StartOverSocket(transport) 已把「socket 怎么来的」与握手分开;spawn 路径 = 造 @mgl-<pid>-<this> 名 → LaunchServer → ConnectTo |
ClientSession.cpp:693-724 |
Dial = Connect 只是跳过 LaunchServer、把名字换成 tcp:// |
握手后 client 收 7 个 fd(4 段 + 3 个门铃)再 AdoptFromDescriptors |
ClientSession.cpp:832-897 |
stream 上这一段整体不存在:段是本地私有的,门铃是本地条件变量 |
数据面没有缝:ILink 只有声明,树里用到它的只有 LinkProgress(Ring.h:97);ShmLink 不存在;两端 session 直接持有 SessionSegments(SessionRings.h:130);ServerSession.h:93 自己写着「lk 把门铃挪到 ILink 后面并删掉这些访问器」——没做 |
Ring.h:108(RingControl 重组已做)、ILink.h、StreamLink.h |
重审 §5.1 那「约一周」的 lk 主体仍然欠着,是本波第一个包,没有它 StreamLink 无处可插 |
wire 寻址:MGPBlobRef{Offset, Size, Seg} 只允许 kSegStage,解析集中在 ResolveOrFatal |
PipeWireCodec.cpp:1421(拒绝其它段::496、:1401) |
两端各建一个同尺寸的本地 stage 窗口、偏移一致,codec 一行不改 |
reply:ReplySlotHeader{Seq, Status, Size} 16 字节、seq % slotCount(8 槽) |
ReplySlot.h |
头已经是消息形状;stream 上删掉取模与 stamp 自检 |
| event:四种 kind、定宽头 | EventRing.h |
已是 wire 形状,原样装帧 |
指纹:CapsAbiFingerprint() = 三个 sizeof + 协议版本 + git stamp |
protocol.fbs table Hello 的注释 |
x86_64 ↔ aarch64 两份二进制:必须换成布局摘要,否则要么永远拒绝(stamp 不同)要么静默读错 |
日志按角色分文件;server 角色由启动方设的 MOBILEGL_IPC_ROLE=server 决定;LogLine 已在 CtrlMsg 里、零生产者 |
ServerSpawn.cpp:116、ClientSession.cpp:1948 |
34 个读 <base>.server.log 的消费方在跨机时读不到 → 日志前送(§3.5) |
车道:mgl_itest_register_split_arms 一个宏两条臂 {Split, Spawn},环境差只有 MOBILEGL_TRANSPORT=spawn + MOBILEGL_IPC_SERVER_PATH |
MG_IntegrationTest/CMakeLists.txt:1985-2010 |
加第三条臂 Tcp;名集合门(scripts/ci/spawn_lane_parity.py)与 arm 证明(scripts/ci/junit_tally.py:27)各加一臂 |
retrace:桌面经 MOBILEGL_TRANSPORT / MOBILEGL_IPC_SERVER_PATH 环境(run_trace_case.cmake:47-52);设备经 --env K=V;… intent extra,server 路径从 nativeLibraryDir 解析(TraceReplayActivity.java:374-386);桌面与设备共用同一套 golden(trace_cases.json),驱动差异由 SSIM 阈值吸收 |
— | 跨机 retrace 的 golden 就是现有的,不另造 |
设备已能从 app 进程 exec libMobileGLServer.so(spike A、spawn-acceptance 臂) |
apk.yml:477-487 |
supervisor 就是这个可执行文件加一个 --serve |
manifest 没有 INTERNET 权限 |
android-plugin/app/src/main/AndroidManifest.xml |
server 要监听 TCP,trace flavour 必须加 |
2 拓扑:谁监听、谁拨号、为什么
server 在手机上监听,client 从 WSL 拨出。理由不是偏好:WSL2 默认 NAT,出站到局域网直通、入站要 portproxy;手机侧无防火墙。Role × Dial 两轴(契约 §3)因此在本波是 Role=Client, Dial=Connect / Role=Server, Dial=No。备用形态:adb forward tcp:40613 tcp:40613 把 server 绑在手机 loopback、client 连 WSL 内 adb 的 loopback——要求 adb 跑在 WSL 里(adb-over-Wi-Fi:adb connect <phone>:5555),因为 Windows 侧 adb 绑的 127.0.0.1 WSL2 够不着;路由器开了 AP 隔离时用它。
3 机制
3.1 端点与握手(包 ct)。SocketTransport::Listen/ConnectTo 识别 tcp://host:port(其余仍是 AF_UNIX 路径);两条 TCP 连接按序:控制、数据。socket 选项:TCP_NODELAY(批处理由我们的 publish 点决定,不交给 Nagle)、SO_KEEPALIVE + TCP_KEEPIDLE=2s / KEEPINTVL=1s / KEEPCNT=3 + TCP_USER_TIMEOUT=5000ms——Wi-Fi 掉线不产生 FIN,这是唯一能把「半开」变成挂断的机制;它以 POLLHUP/错误进入 Doorbell::Dead(),走 dl 的闩,是传输层报告的挂断,不是 apply 超时(开放问题 23)。Hello 追加 linkTerms{dataPlane=Stream, wireForm, maxReplyBytes, cmdWindowBytes, stageWindowBytes} 与 token;Welcome 回填 server 陈述的四个尺寸;新增 Refuse{code, detail, 对端值}(CtrlMsg 只追加)。buildFingerprint(git stamp)比较策略:Dial=Fork 不匹配 → Refuse(抓陈旧 MOBILEGL_IPC_SERVER_PATH,不变);Dial=Connect 不匹配 → 警告,MOBILEGL_IPC_REQUIRE_SAME_BUILD=1 时 → Refuse(车道设它:APK 与 WSL 构建必须同一 commit)。令牌:MOBILEGL_IPC_TOKEN 两端一致;server 无令牌时只允许绑 loopback,错令牌 → Refuse 并记日志。
3.2 wire 定宽与布局摘要(包 wf)。wireFingerprint = gen_pipe.py 从 PipeFields.def 生成的逐成员 {name, offsetof, sizeof} 表的摘要 + 有序目录摘要(opcode / payload 名 / flags / WaitClass)+ 记录头布局 + MGPipeRenderStateSpans 的 offsetof 表 + 两个 blob codec 版本 + kOpCount + MOBILEGL_ABI_VERSION + 字节序 + 指针宽度 + sizeof(MGPCaps) / sizeof(DynamicBackendParameters);删 sizeof(GLFunctionsTable)。x86_64 ↔ aarch64 都是 LP64、小端、Itanium ABI,布局大概率一致,但由摘要断言、不由这句话断言;已知的真实差异是 char 的符号性(aarch64 默认无符号)——wf 普查 payload 与 blob 里裸 char 当数用的站点。DynamicBackendParameters 定宽重写(从 P7 挪来)与 RenderStateParameters 1168 字节整块的成员表在此包。三条 red-once 见 P6.5 行。
3.3 缝的主体(包 lk2,零行为变化)。SessionSegments + RingProducer/Consumer + ReplySlotPool + EventRingProducer/Consumer + 两个门铃搬到 ShmLink : ILink 后面;SessionProducer / SessionConsumer / ServerLoop / PipeWireEncoder / ClientSession.cpp 的 kSegEvent 挂载 / ResourceTracker.h 的 writeback 解析都改收 ILink*;ServerSession.h:93 说要删的访问器删掉。门:inproc / spawn 车道结果数与逐线程 CPU 逐字不变(ILink::ReserveRecord 的虚调用在 ~850–3550 次/帧上实测;若不在噪声里,答案是把 reserve/commit 做成 link 自有 arena 上的非虚函数);纯度 grep 门上线。
3.4 StreamLink(包 sl)——被消息复制的镜像段。设计原则一句话:两端各自持有一份同尺寸的私有段,链路只负责把字节搬到对面同一偏移;codec、watermark 规则、apply 线程、编码器全部不变。
- 段:握手协商后两端各
malloc四块:cmd 环(cmdWindowBytes)、stage 窗口(stageWindowBytes)、event 环、reply 邮箱。尺寸相同 ⇒ 偏移相同 ⇒MGPBlobRef与ResolveOrFatal原样工作。 - 数据连接帧:
[u32 'MGLD'][u32 len][u8 kind][u8×3][u64 a][u64 b][payload]。C→S:Cmd{ringOffset, bytes}(记录字节逐字、写到对面环的同一偏移)、Stage{offset, bytes}、ClientProgress{cmdHead, submittedSeq, eventTail}。S→C:Progress{appliedSeq, retiredSeq, completedFrameSerial, presentAckSerial, cmdAppliedTail, eventRingFull}、Reply{seq, status, bytes}(头即ReplySlotHeader)、Event{ringOffset, bytes}。 - client 侧:编码器照旧写本地 cmd 环与 stage 窗口;
Flush()把[上次已送, cmdHead)作为一帧Cmd发出(环回绕用两段 iov 一次sendmsg),之前先发本次新 stage 的Stage帧——顺序由编码器「先 stage 后 reserve」保证;LinkCapabilities.PublishIsDelivery=false,所以每个等待点先Flush(这个位就是为此设的)。C→S 背压:WaitForCmdSpace不变(读Progress里的cmdAppliedTail),send()阻塞是第二层,没有无界缓冲。 - server 侧:
mgl-srv-io读线程把Cmd/Stage拷到镜像段同偏移,再推进本地cmdHead/submittedSeq并敲 apply 的本地门铃;apply 线程一行不改。 ProgressS→C 的发出时机(对Ring.h「appliedSeq不得懒发布」的修正案,规则改成「最多晚一次 flush,绝不早」):(a) apply 线程即将 park 前(flush-on-idle,解决「帧内最后一条kWaitReply记录两端互等」);(b) 每次PostReply之后;(c) present ack /completedFrameSerial变化时;(d) 每 64 条已 apply 记录或 1 ms 取先到者。接收侧对每个 watermark 做AdvanceMonotonic,倒退是Fatal{ProtocolCorruption}。- reply:
kWaitReply下同一时刻最多一条未决,client 侧是「单槽 + 条件变量 + seq 核对」;本链路上 slot 池、取模、stamp 自检删除(ShmLink保留)。 - event:server 的
EventRingProducer写本地环,io 线程把新增字节装Event帧;client 读线程写进 client 本地环同偏移,EventRingConsumer不变;Drained()发ClientProgress{eventTail};server producer 在head − 对端 tail > capacity时阻塞(P5e 的规则原样,eventRingFull经Progress回传)。 - 门铃:
ConsumerBell()/ProducerBell()是本地CondVarDoorbell,由各自的读线程敲;读线程见 EOF / 错误置Dead()。每进程数据连接一条专用读线程(写在调用线程上),控制连接的读法不变。 - 帧上限:
Cmd/Stage单帧 ≤kMaxFramePayloadSize(64 MiB);一条记录本来 ≤ 4 MiB。未分片的 program archive /draw_vborange 尾在 stream 上和今天一样受stageWindowBytes约束,超出仍是Fatal{RingOverrun}——不是本波阻塞项,记在开放问题 11。 - 不做的:
kSegShadow/kSegAdopt(P8 / P11 的段)在 stream 上具名拒绝;GetCaps的 blobref 若仍走 stage 之外 → 具名拒绝,本波不发明第三条载体。
3.5 日志前送(包 lf)。server 进程的日志 sink 在 MOBILEGL_IPC_ROLE=server 且有控制传输且 MOBILEGL_IPC_LOG_FORWARD=1(Dial=Connect 默认开)时,每行同时写本地 <base>.server.log 并作为一帧 LogLine(已在 CtrlMsg 里)经控制连接发出;client 收到后写入自己的 <base>.server.log。于是 34 个消费方(PipeStatsWindow 的计数器窗口、四个 arming 场景、拒绝普查)一字不改。顺序:读者在 MarkLaneLog / ReadLaneLogSince 之前调一个导出的 MGPipeSyncPeerLog()(一次控制往返:LogFlush → server 冲刷并 ack),远端不存在时它是 no-op;导出符号一个,-fvisibility=hidden 下显式 default。若 lf 滑期,退路是这 34 处在 tcp 臂上按名 SKIP(清单文件,像 dualblock-expected-fatals.txt),不许静默通过。stats 行一帧一条,前送代价可忽略。
3.6 设备侧 supervisor 与启动(包 sv)。mobilegl_server_main tcp://0.0.0.0:40613 --serve [--max-sessions 1]:listen → 循环 { AcceptPair(控制 + 数据)→ fork() → child 关掉 listen fd、用 SocketTransport(streamFd, dataFd) 走今天的单会话路径到 _exit;parent 关掉已接受的 fd、非阻塞 waitpid、继续 accept }。每会话一进程 ⇒ eglTerminate 的进程级拆机被隔离(D9 原样);同时只服务 1 个会话,第二个连接 → Refuse{Busy}。启动:trace flavour 新增前台 MobileGLServerService(android:process=":mglsrv"),am start-foreground-service 带 --es listen、--es token、--es env "K=V;…"(复用 --env 的 K=V; 语法),它从 nativeLibraryDir exec libMobileGLServer.so(spike A 的路径)、给 child 设 MOBILEGL_IPC_ROLE=server、持 partial wake lock;am force-stop 整包停掉。后端由 client 的 Hello.backendType 请求、supervisor 的 env 可钉死,不一致 → Refuse。EGL 在 child 里是 pbuffer / surfaceless(P6 边界;真窗口归 P12)。manifest 加 INTERNET。
3.7 谓词与测试的拓扑感知。client 进程:Transport=Spawn(拓扑:两进程)、Dial=Connect、不 fork;MGPipeBackendIsLocal()=false。CountOwnChildren() 一类断言改为按 Dial 分支:Connect 下断言本地零子进程 + Welcome::serverPid 非零 + 日志 control=tcp。S2(kill server)在跨机臂上经 MGITEST_PEER_KILL_CMD 模板(adb -s <serial> shell run-as top.mobilegl.plugin.trace kill -9 {pid})真杀——不允许用协作式「请你退出」代替,那测的不是挂断。
4 包与顺序
lk2(缝主体,零行为)→ wf(定宽 + 摘要 + Refuse)→ ct(tcp 端点 + keepalive + 令牌 + Connect 拨号)→ sl(StreamLink,先在 WSL loopback 上对着 spawn 车道打平)→ lf(日志前送)→ sv(supervisor + Service + adb 脚本)→ t65(tcp 臂、跨机臂、门、数)。每包 red-once(R-16)。第二波(不在本节):nd 的 auto 与 sd 的 aux 交付(同机 TCP 控制 + 共享段数据)、P11 的 stream 具名拒绝、Windows client。
5 出口门(本波)
- G1 pull 0/0/0/0、
.text不变(全部新代码在MOBILEGL_BUILD_DISAGGREGATED下)。 - lk2 后 inproc / spawn 车道结果数与逐线程 CPU 逐字不变(实测,不论证)。
integration-tcp(WSL loopback,server 以--serve tcp://127.0.0.1:40613本地起):名集合 == spawn(parity 脚本加臂)、同数绿;每条目 arm 证明control=tcp data=stream server=<host:port> pid=N(junit_tally --require-tcp-ran)。integration-tcp-device(server 在 Redmi):同一名集合;首轮记录、不设阻塞门,每一条与 loopback 臂的差异必须具名(GPU 能力 / P7 的 Magma 分歧 / 时限);第二轮起转硬门。- retrace:CI split 子集(
trace_cases.json里未"split": false的)从 WSL 对着手机跑,DirectGLES 的 SSIM ≥ 现有阈值、OpenRA 为 1.0(E2 的锚);DirectVulkan 记录不设门(真机分歧是 P7 的)。 - red-once:wf 三条(P6.5 行);ct——错令牌 → Refuse、
wireMajor不匹配 → 两端具名拒绝、adb shell svc wifi disable→ 10 s 内闩住(而不是 120 sBarrierTimeout);sl——Progress倒退 →Fatal{ProtocolCorruption}、越窗的Stage帧 → 拒绝、去掉 flush-on-idle 后「帧内最后一条kWaitReply」用例必须挂住并被超时抓到;lf——关掉前送后四个 arming 场景必须按名红;sv——第二个并发连接 →Refuse{Busy}。 - 必测数(记录):逐帧
kWaitReply次数与 RTT 分布(OpenRA、MC in-world 各一);SEG_STAGE字节/帧对链路实测吞吐(车道自己量一次 64 MiBStage突发);PRESENT_CREDIT1 / 2 / 3 的帧率;loopback tcp+stream 对 spawn(unix+shm)的逐线程 CPU 差。
6 两边怎么配(落地后的操作手册;今天还跑不了)
手机(server)
# 1. 同一 commit 构建 trace APK(分离构建)并安装
./gradlew :app:assembleTraceDebug -Pmobilegl.buildDisaggregated=ON && adb install -r app/build/outputs/apk/trace/debug/*.apk
# 2. 手机 IP
adb shell ip -f inet addr show wlan0
# 3. 起 server(前台 Service → supervisor 监听)
adb shell am start-foreground-service -n top.mobilegl.plugin.trace/.MobileGLServerService \
--es listen tcp://0.0.0.0:40613 --es token devtoken \
--es env "MOBILEGL_BACKEND_TYPE=DirectGLES;MOBILEGL_PIPE_STATS=1;MOBILEGL_PIPE_STATS_PERIOD=1;MOBILEGL_LOG_FILE_PATH=/data/data/top.mobilegl.plugin.trace/files/mgl.log"
adb logcat -s MobileGL | grep -m1 'listening on tcp://'
# 停:adb shell am force-stop top.mobilegl.plugin.trace
WSL(client),在分离构建目录(今天是 ~/lk-split):
export MOBILEGL_TRANSPORT=spawn # 拓扑:两进程;不 fork,因为下面给了远端
export MOBILEGL_IPC_CONTROL=tcp://<phone-ip>:40613
export MOBILEGL_IPC_DATA=stream
export MOBILEGL_IPC_TOKEN=devtoken
export MOBILEGL_IPC_REQUIRE_SAME_BUILD=1 # APK 与本地构建必须同 commit
ctest -L integration-tcp-device -j 1 --output-on-failure # -j 1:设备同时只服务一个会话
# retrace(同一环境):
(cd build-retrace/tools/trace_replay && ctest -V -R '^MobileGLTraceReplay\.OpenRA\.DirectGLES$')
日志:client 侧 <base>.client.log 与前送来的 <base>.server.log 都在 WSL;设备本地副本用 adb shell run-as top.mobilegl.plugin.trace cat files/mgl.server.log。
备用(AP 隔离 / 没有局域网直通):adb 跑在 WSL 里——Windows 上一次 adb tcpip 5555,WSL 里 adb connect <phone-ip>:5555 && adb forward tcp:40613 tcp:40613;手机侧 --es listen tcp://127.0.0.1:40613(loopback,不需令牌),WSL 侧 MOBILEGL_IPC_CONTROL=tcp://127.0.0.1:40613。
7 风险与未决(本波)
kWaitReply× RTT:ResourceCreate/SetTextureParams/ 纹理ResourceSubData每次一个 RTT(Wi-Fi 2–5 ms);MC 加载区块的创建突发会可感知地慢。本波只量(门 7),减少这一列是 P9 / P10 的活。- 带宽:
SEG_STAGE字节/帧对 Wi-Fi;单次 128 MiB 上传 = 秒级停顿(开放问题 19、20)。 - Wi-Fi AP 隔离:用 §6 的 adb forward 备用。
- DirectVulkan 真机分歧(P7)会在门 5 出现:记录,不当本波的红。
MOBILEGL_IPC_ROLE:supervisor 必须替 child 设它,否则 child 的日志会当 client 写(Log.cpp从这个 env 解析角色)。- ctest 并行 vs 一设备一会话:
-j 1或 supervisor--max-sessions N(每会话一进程,N 个 EGL 上下文同时存在,Adreno 上未验证)。 - 同 commit:两端二进制不同是常态,指纹只保证 wire 布局;
REQUIRE_SAME_BUILD是车道纪律,不是协议要求。
历史:P5c 计划与审计(inproc 共享内存读点;审计头 a79a0af6,2026-09-17)
为什么插在 P6 之前:P5 / P5b 的 inproc 只把 verb 记录、SEG_STAGE blob、reply 与 caps 快照做成了 wire。对 a79a0af6 的只读静态审计(~/w7/notes/p5c/p5c-audit-v1.md,59 行清单,逐条在该头上重新解析)证明两个角色之间仍有一批不经任何载体的直接内存读写,靠 verb barrier 与同一地址空间才正确。它们不是传输问题:spawn 换掉的只是 ring 的传递方式,而这些读写在另一个进程里根本没有对应内存。P5c 的目标是让 inproc 诚实——两线程之间除 SEG_CMD / SEG_STAGE / SEG_REPLY / SEG_EVENT / caps 快照 / 控制帧之外零直接访问——从而 P6 只是传输替换。本节保留 P5c 启动时的计划与审计语境;P5c 已收官,后续对象类、控制帧、静态量与反向通道的收口结果以 P5f 报告为准。
审计清单(按家族;file:line 以 a79a0af6 为准,完整 59 行见报告)
| # | 方向 | 直接访问的是什么 | 站点 | 今天为何能工作 | 现有守卫 | P5c 载体 |
|---|---|---|---|---|---|---|
| T1 | S→C 读 | 纹理纹素:resource_subdata 已把 level 字节放进 SEG_STAGE,但 ApplyTextureUpload 只做门与累积、把指针丢掉,Espryt 同步时从 client 的 MipmapStorage 重读纹素;纹理对象的形状与脏区也直接读前端对象 |
丢指针 MobileGL/MG_Pipe/PipeApply.cpp:989-1008;重读 DirectGLES/Managers.cpp:7315(MapMipmapData)、:6940、:7129、:7198;形状 :7314、:7332、:6878-6880、DirectGLES/DirectGLES.cpp:8502-8507、:8528-8529;脏区 DirectGLES/Managers.cpp:7360、:7371 |
barrier + 同地址空间 | 无:整个纹理家族不在 FieldOwnership.def,rsp / MOBILEGL_IPC_STRICT_ERRORS / MOBILEGL_IPC_AUDIT 都看不见 |
server 端纹理 staged shadow(buffer StagedShadowStore 的纹理半边);SyncMipmapsToBackend 改读它,形状与脏区读描述符 |
| T2 | S→C 读写 | client 的 slot 分配器 MGPipeSlots():mip / CopyTex / twin 创建时按前端 GetLifetimeId() 在 client 分配器里查找甚至铸造 |
DirectGLES/DirectGLES.cpp:8087 → DirectGLES/SlotTables.h:392-400(HandleOf → FindByLifetimeId);铸造 DirectGLES/SlotTables.h:232(GetOrCreate)、:418、:428;DirectGLES/Managers.cpp:2708、:4293、:5627;DirectGLES/DirectGLES.cpp:6706、:8185、:8564、:8659 |
barrier;mip 只 Find 不 mint | 无(MOBILEGL_ASSERT 在 INFO 构建失效) |
记录已带句柄(MGPMipPlan::Res、MGPCopyFromFramebuffer::Dst、MGPBlit::ReadFbo/DrawFbo)→ GetOrCreate(MGPipeHandle)(DirectGLES/SlotTables.h:278) |
| T3 | S→C 读 | 具名 blit:client 临时改绑自己的 read / draw 绑定槽,server 经 MGB_CTX 读绑定槽 |
client MobileGL/MG_Remote/Client/EmitTables.cpp:741-786;server DirectGLES/DirectGLES.cpp:7782-7785 |
barrier + RAII 恢复 | rsp(GetFramebufferBindingSlot) |
记录里的 ReadFbo / DrawFbo,sink 侧按句柄解析(MobileGL/MG_Remote/Server/PipeApplier.cpp:228-244) |
| T4 | S→C 读 | CopyTexImage2D / CopyTexSubImage2D 经纹理单元绑定槽取目的纹理 |
DirectGLES/DirectGLES.cpp:8561-8563、:8656-8658 |
barrier | rsp(GetTextureUnitObject) |
记录里的 Dst 句柄 |
| T5 | S→C 写 | Magma 生成 mip 直接写 client 的 level 存储(Espryt 的重推导臂没有 Magma 对应) | DirectVulkan/Renderer/VulkanRenderer.cpp:1562-1590(调用 :11317) |
barrier | 无 | 与 GLES 同形:只按描述符验证 |
| B1 | S→C 读 | BufferObject::HasDefinedContent(),每个 ensure 的 draw 都读 |
DirectGLES/Managers.cpp:3147-3148(EnsureBufferResourceForHandle;:3141-3146 自述为债) |
barrier | 无 | 描述符旁的 live-content 位,client 发布 |
| B2 | S→C 读 | HandleOfBuffer 每次 ensure 读前端 GetLifetimeId() |
DirectGLES/Managers.cpp:2705-2716 |
barrier + memo | 无 | 记录里的句柄 |
| B3 | S→C 读 | 旧 buffer 臂(MappedData() / IsMapped() / GetChangeSerial())在 split 构建里位 7 清零时可达,arm resolver 只打一行 MGLOG_D |
DirectGLES/Managers.cpp:2586-2620;臂体 :1012-1022、:1119-1121、:2944-2949、:3281-3327 |
同进程 | 无 | 有传输时具名拒绝(同 Fatal{PipeLegacyMemosDisabled} 的形状) |
| B4 | S→C 读写 | XFB:SharedPtr<BufferObject> 跨整个 span 持有,结束时 WritebackFromBackend,scatter 对 client shadow 读改写 |
DirectGLES/DirectGLES.cpp:950、:1060、:1077、:1081、:1172、:1185 |
barrier | 无 | OnBufferWriteback / OnXfbScatterReady 事件——P9 的题材,P5c 只记账 |
| R1 | S→C 写 | 反向通道是 apply 线程直接调进 client 的 MG_State:OnBufferWriteback 把裸指针塞进 MGPBlobRef.Offset,client 侧再转回指针;consumer 会拒绝任何真实段 |
生产 DirectGLES/Managers.cpp:2339-2343(Ops_H_Readback);消费 MobileGL/MG_Impl/Pipe/ResourceTracker.h:553-572(拒绝真实段 :560-564) |
同地址空间 + barrier | 无 | SEG_EVENT kEventBufferWriteback:consumer 已在(MobileGL/MG_Remote/Client/ClientSession.cpp:176-267),全仓零 producer(MobileGL/MG_Remote/Server/ServerSession.cpp:484-492 无调用者) |
| R2 | S→C 写 | OnGpuWritten 直接调进 client tracker;Magma 更是绕过回调直接 MarkGpuWritten() |
Espryt DirectGLES/Managers.cpp:2729-2752,喂入 DirectGLES/DirectGLES.cpp:568、:616、:2601;Magma DirectVulkan/Renderer/UniformManager.cpp:1075、:1231、DirectVulkan/Renderer/VulkanRenderer.cpp:11618 |
同地址空间 | MGLOG_E_ONCE |
SEG_EVENT kEventGpuWritten(consumer MobileGL/MG_Remote/Client/ClientSession.cpp:227-236) |
| R3 | S→C 写 | 默认 framebuffer 附件(pDefaultFramebufferInfo)由 server 在 surface 创建时写 |
DirectGLES/DirectGLES.cpp:11519-11620;Magma DirectVulkan/Renderer/SwapchainObject.cpp:276-335 |
barrier;罕见 | 无 | SEG_EVENT kEventSurfaceChanged(consumer stub MobileGL/MG_Remote/Client/ClientSession.cpp:242-253) |
| R4 | S→C 写 | sticky forward:RecordError 写 client 错误队列;InvalidateCompileEnv 写 client 编译环境(在 stamped verb 之外静默 no-op) |
MobileGL/MG_Impl/Pipe/PipeFill.cpp:1753-1756、:1764-1772 |
barrier | rsp;strict 下 Fatal |
RecordError → OnGlError 事件(顺序保证是 P9 的);InvalidateCompileEnv 的活已由 R-12 caps 重发布完成(MobileGL/MG_Remote/Client/CapsMirror.cpp:78-80),删 forward |
| R5 | — | MGPipeCallbacks 十个回调只装了两个,且都是 apply 线程内联调用;SEG_EVENT 段已创建、映射、握手公告、每个 reply verb 后排空,但没有任何生产者 |
安装 MobileGL/MG_Impl/Pipe/ResourceTracker.h:606-613;段 MobileGL/MG_Remote/Server/ServerSession.cpp:285-301;排空 MobileGL/MG_Remote/Client/ClientSession.cpp:836 |
— | 溢出 latch 与 eventDropped 已建未用 |
本身就是载体 |
| C1 | S→C 读 | Magma 四处直接读 client 镜像 pActiveBackendObject->GetDynamicParameters()(Espryt 已改走 server 自己的 backend);Espryt 的格式表回落臂在 server backend 为空时读 client 镜像 |
DirectVulkan/DirectVulkan.cpp:713-715;DirectVulkan/Renderer/VulkanRenderer.cpp:667-676;DirectVulkan/Renderer/VertexInputStateFactory.cpp:249-250、:502-503;DirectGLES/Utils.cpp:50 |
镜像值等于快照 | 无 | server 自己的 Backend()->GetDynamicParameters();回落臂拒绝 |
| G1 | shared | gPipeInputs:client 残余填充写、applier 写、server 的 36 行 BARRIER-PULLED 读 |
MobileGL/MG_Backend/MGPipe/PipeInputs.h:806;填充 MobileGL/MG_Impl/Pipe/PipeFill.cpp:2798;applier MobileGL/MG_Pipe/PipeApply.cpp:1415-1560;stamp MobileGL/MG_Backend/MGPipe/PipeInputs.cpp:103-132;rsp MobileGL/MG_Backend/MGPipe/PipeInputs.cpp:72 |
只靠 verb barrier 保证单写者 | rsp + strict Fatal + poison |
值类行(GetActiveTextureUnit、GetMaxTouchedTextureUnit、GetTouchedBufferBindingPointCount、GetCurrentVertexAttribute、XFB 六项、三个世代)→ 每 verb 一条残余值记录或按 Coverage.def 由 server 自答;对象类行(GetBoundVertexArray、GetProgramForDraw/Dispatch、GetTextureUnitObject、GetImageTextureBinding、GetFramebufferBindingSlot、GetBufferBindingSlot/Point、sticky GetTextureObject / GetProgramObject)返回前端对象指针、无法序列化,留 P3b/P4b/P7/P8 的 twin 表 |
| G2 | C→S 写 | GL 线程在 FreshlyPrimed 时整体 MGPipeApplierReset() server 的 g_applier |
MobileGL/MG_Impl/Pipe/PipeFill.cpp:2654-2656 → MobileGL/MG_Pipe/PipeApply.cpp:1202-1299 |
barrier 的后置条件(validate 时无在飞记录) | 无 | make-current 边上的 applier_reset 控制记录 |
| G3 | C→S 写 | 六种前端对象死亡:栈上结构体指针经 mailbox 送到 apply 线程(framebuffer 没有 wire delete opcode) | DirectGLES/Managers.cpp:206-228 |
阻塞 + 同地址空间 | 无 | object_death wire 记录 |
| G4 | C→S 控制 | 十二个 Server* EGL forwarder 经 one-slot mailbox 传函数指针 + void* 栈局部 |
MobileGL/MG_Remote/Client/BackendObject_Remote.cpp:125-264;邮箱本体在 fc 前的 HEAD 上是 ServerLoop.cpp:533-568(PumpControlRequest)+ :645-707(RunOnApplyThread)——本行原引 :543-598,f0-egl F9 修正 |
阻塞 | Fatal{ApplyThreadNotRunning} |
已由 P5f 包 fc 帧化(原记 P6):SurfaceControlFrame 纯值帧走同一阻塞通道(Server/SurfaceControlFrame.h、分发 ApplySurfaceControlFrame),wire 编解码与具名拒绝在 Protocol/SurfaceOpCodec.cpp;报告 notes/p5f/fc-report.md |
| G5 | C→S 读 | ServerLoop::OnApplyThread() 每条 routed 记录读;bring-up 直接读 loop.Backend() / MGPipeGetResourceOps() |
MobileGL/MG_Remote/Client/WireTables.cpp:71;MobileGL/MG_Backend/Init.cpp:100、:145、:164-170、:247 |
同进程 | — | client 本地角色标志;caps 快照的 cap 位(P6 顺手) |
| G6 | shared | 前端地址 / lifetime-id 键控的 Espryt twin registry;持前端裸指针的 memo(ResolvedDrawBuffers::Entry::frontend、三张 texture sync list);server 读 pDefaultFramebufferInfo;g_rawDepthFetchSamplerState;ScopedDefaultUnpackState::s_synced 从不按 context 世代重置 |
DirectGLES/Managers.h:309-377、:1185;DirectGLES/DirectGLES.cpp:61-62、:1958-1987、:2846;DirectGLES/Managers.cpp:5806-5812 |
barrier / server 私有 | — | 历史移交给 P3b/P4b / P6;transport registry 与 s_synced 世代问题已由 P5f fr / fs 收口 |
| A1 | — | barrier 的 client 半边断言从未接线:InBarrierWait() 零调用点,只有 ApplyThreadIsInsideApplier 在 emit 时检查 |
MobileGL/MG_Remote/Client/ClientSession.cpp:880、:748-753 |
— | — | 接上,或删掉契约里的这句声明 |
已确认 wire-clean、不进 P5c:buffer 全家族(Ops_H_* 只拿句柄 + 记录 + SEG_STAGE 字节,StagedShadowStore::Adopt 复制,Fatal{StageSnapshotTooNarrow} 六处);resource_respecify 初始字节双向拒绝;persistent map 仿真档按块过 SEG_STAGE,Fatal{RoleViolation} 挡住 apply 线程;CreateShaderState 归档是一个 blob;caps 的格式表与 renderer 字符串走 CapsSnapshot 控制帧(GetCaps 记录本身已死,只是卫生项);tight ReadPixels;DrawVbo 用户索引;fence 家族;gMGPipeSegmentResolver 与 gBackendFunctionsTable 的角色划分。
包与顺序
| 包 | 内容 | 关闭的行 |
|---|---|---|
| c0c 契约 | MG_Remote/CONTRACT-P5C.md:纹理 staged shadow 的所有权与覆盖规则;SEG_EVENT 记录与 blobref 约定(Seg = kSegEvent);sink / twin 按句柄解析的规则;applier_reset / object_death 记录;角色守卫的语义(有传输时 apply 线程触前端对象表面、GL 线程触 server 状态表面都是 Fatal{RoleViolation});残余值记录的字段表;把纹理家族补进 FieldOwnership.def |
— |
| tx 纹理 | StagedTextureStore:ApplyTextureUpload 采纳 SEG_STAGE 字节到 server 侧 level shadow;SyncMipmapsToBackend、形状读、脏区改读描述符与 shadow;Magma mip 改按描述符;MOBILEGL_IPC_AUDIT 的 0xDD 覆盖纹理 |
T1、T5(T2 的 mip 半边随之) |
| ev 反向通道 | ServerSession::PublishEvents 接三个 producer(writeback / gpu-written / surface-changed),consumer 的段臂改为解析 kSegEvent;Magma MarkGpuWritten 改走回调;RecordError 进事件(顺序仍 P9);删 InvalidateCompileEnv forward;溢出策略沿用 ARCHITECTURE.md §11.7 |
R1–R5 |
| hd 句柄解析 | blit / mip / CopyTex / HandleOfBuffer 的 sink 与 twin 按记录里的句柄解析(GetOrCreate(MGPipeHandle)),server 不再触 MGPipeSlots();HasDefinedContent 改读描述符的 live-content 位;Magma 四处 caps 读改 server backend,Utils.cpp 回落臂拒绝 |
T2、T3、T4、B1、B2、C1 |
| ct 控制记录 | applier_reset 与 object_death 两条 wire 记录(framebuffer 首次有 delete opcode);mailbox 只剩 EGL forwarder 给 P6 |
G2、G3 |
| rv 残余值 | 值类 BARRIER-PULLED 行改为每 verb 的残余值记录(或 server 自答),gen_pipe_field_ownership.py 把它们改成 RECORD-SUPPLIED;对象类行保持 BARRIER-PULLED 并逐行标注归属阶段 |
G1 的值类半边 |
| gt 门 | 角色守卫(两层 Fatal{RoleViolation},仅 split 构建、有传输时生效);InBarrierWait 接线;旧 buffer 臂传输拒绝;rsp 按帧进 stats 行并在四条 A/B trace 上测得;CI 加一条 MOBILEGL_IPC_STRICT_ERRORS=1 + 角色守卫的 integration-split 车道 |
A1、B3,以及所有行的红一次证据 |
顺序:c0c → tx / ev / hd 并行 → ct / rv → gt 收口。每包自带 red-once(R-16),阶段末一次 Codex / Kimi 审查(ID-66)。
出口门(E-P5c)
- 角色守卫下全绿:守卫开启时
integration-split(107)、broad inproc 车道对348d22a4普查零回退、79 trace 无新增首阻塞;关掉任一层守卫必须能在一条具名用例上变红。 - 纹理字节不再回读 client:
MOBILEGL_IPC_AUDIT=1对纹理 staged 字节的0xDD填充在四条 A/B trace 上无失败;把采纳改回丢指针必须变红。 SEG_EVENT有生产者:三条事件的往返单元与集成用例;排空点上eventDropped == 0;把 producer 改回裸指针必须变红。rsp分类完成:四条 A/B trace 每帧rsp已测;剩余读全部是对象类行,名单由FieldOwnershipTest钉住,值类行为 0。- Redmi 四臂复测(记录,不设门):writeback / gpu-written 改走事件后 barrier tax 重新记录。
- G1 0/0/0/0、G2 / G14、G5 照旧。
历史移交项(P5c 当时不碰)
P5c 当时移交的对象类 BARRIER-PULLED、transport registry 前端键、EGL 控制面、s_synced / g_syncedRenderStateParameters 世代与反向通道跨角色访问,已在 P5f 收口。P9 的异步化/排序、P8/P9 的协议广度与仿真功能仍按各阶段推进;不要把这份历史清单重新当成 P6 的帧化或静态量施工单。
里程碑
- P2 出口(2026-09-08):GO/NO-GO 判定继续——五部分门全绿,逐线程 CPU 代价约 +10% 被接受;tracker 绝对 ns 上限降为记录项。
- P3a / P4a 出口(2026-09-08):各 1 天,远低于 27 / 39 天的再基线绊线,未触发重定基线。
- P5 出口(2026-09-16):缩减路径首个 IPC 帧;最终全门在 E3(a) 停止;红米四臂的 split 均停在索引 draw,barrier tax 未测(归 P5b)。
- P5b 出口(2026-09-16,已达成):主机全门
348d22a4complete;79 trace 72/6/1;设备源头82683d4a(APKp5bcodex2)Redmi 正确性 8/8——四条 A/B trace 双后端首次在设备上 inproc 渲染;barrier tax 首测 split−push 逐线程 CPU p50 +5.9% – +18.2%。 - P5f 出口(2026-09-20):双块和 strict 空棘轮、逐帧 rsp=0、零 BARRIER_PULLED、主机全门、异模型族审查修复和 Redmi 六 clean-boot 臂全部完成;P6 前提解除,实施未开始。
- CI / inproc 收尾出口(2026-09-21):
feat/disaggregated主产物拆分编译 + inproc 主验收全绿——unit 2328 零失败、integration-gpu双 transport 各 1455 零失败、retrace 77 例 monolith 77/77 且 inproc 的唯一间歇项(bsl-esc-menu-854DirectVulkan,lavapipe 固有 texture-handle 注册停顿越过旧 30 s barrier 预算)在预算修正为 120 s 后隔离 10/10 绿、18 项 CI 门全绿、双 ABI APK 与 AVD 生命周期验证通过;合入头e1bf3677,最终矩阵以远端 CI 为准。 - P6 全部包落地(2026-09-21,头
16bfab10):a6/c6/lk/so/sm/cp/hs/dl/st/t6 十包合入feat/disaggregated;三种传输(monolith/inproc/spawn)桌面与真机两进程 retrace 逐像素对上 golden;device-lost 闩把杀 server 从 123 s 错误BarrierTimeout变 5.7 s 正确DEVICE LOST;Session::Fail漏斗把裸 abort 93→4。出口门 1–6 ✅,第 7 项(真机配对 A/B 热窗口)与第 8 项(三个强制性能数)待采,未宣布收官。逐项见CURRENT_STAGE_PROGRESS.md。 - 终局改判(2026-09-22):AVF pVM /
AF_VSOCK终局撤回,改为 TCP 跨机跨平台 + 控制面 × 数据面两轴传输栈;P6.5 上关键路径,Ph 前置于 P12 的非 loopback 监听;P6 核出三项漏项(server 进程PipeStats结构性零、184 棘轮门、hs 的三行契约未落地)与CURRENT_STAGE_PROGRESS.md里一条错误的 S6 陈述(已改);class-C 已归零到两个(SetSwapInterval、DeleteTransformFeedback)。 - 仍是方向、不据此伪造新日历:全功能 split(P8 之后)、纯度门在非 verify 构建上转绿(P13)。
再基线检查点仅剩一条:P7 中点完成子系统 < 40% 立即重定基线(P3a 的检查点发现不了 Magma 特有的超期)。
P5 / P5b 出口记录的债务
| 债务 | 去向 / 当前口径 |
|---|---|
| P5 的 27 个普通 inproc wrong-answer(历史计数) | 22(14 layered + 3 packed depth/stencil + 5 framebuffer recycle)→ P4b/P7 texture readback;3 → P7 query;1 inspection(TextureParamsWithoutASamplerView)→ 原记 P6,场景代码里已无 P6 引用,疑由 P5f fc / P6 cp 顺手关闭、待核;1 FBO/RBO delete 用例在 r1 定向验证中通过,完整像素因果未隔离。最终计数以 MEASUREMENTS.md §7.2 的同名比对为准 |
rsp residual inputs |
P5f 已关闭残余读取:字段归属 41 record / 6 derived / 0 barrier / 16 fatal;strict 与双块 marker 表均为空,双后端逐帧 RSP 门 2 PASS / 0 skip,Redmi 36 个统计窗口 rsp=0。P5e 的“rsp 只计被采纳设障 pull”是历史计数规则,八对 admitted 的历史观察不再是当前允许集合 |
SEG_REPLY 2 MiB 单槽 payload cap |
→ P9(契约 §10.2.3 改判,ID-47);stream 链路上 reply 本就是消息,cap 变成 P6.5 nd 的 maxReplyBytes 窗口 |
GetCaps 的两个 blobref |
目前不骑 record;一旦运输,必须有 server→client carrier rule,不能套 SEG_STAGE |
| PACK-PBO readback | P5 具名拒绝;真实形状是 server 写 buffer resource、client MarkGpuWritten;P6 未做,→ P9(ID-57) |
| ABI fingerprint | → P6.5 wf / nd:尺寸不再进指纹而进 LinkTerms 由 server 陈述;指纹改成 wire 布局摘要(hs 只落了 build stamp 显式化与 abiMajor/Minor 补检,wireFingerprint / buildFingerprint 拆分与 PipeFields.def 摘要这一半没做) |
| 默认 32 MiB staging | 目标负载单次 128 MiB 上传装不进默认 stage;内容侧分块已落地(buffer 范围走查 1e7c372e、纹理整宽 slab 9469d48e,预算 MGPipeStageChunkBytes() = 默认 segment/4 = 8 MiB),故普查 / Redmi 的显式 256 MiB profile 不再是这两条路径的必需;未接入分片的 record 类型与专用 carrier 仍归 P8(开放问题 11) |
| P5b 的 inproc 依赖 | 历史的 scoped client binding、mip registry 查询与 FBO death mailbox 已经 P5c/P5f 改为记录、句柄和控制/事件路径;P6 核验并装配已有载体,不再重做这些迁移 |
| 未迁移与仿真路径 | class-C 已归零到两个(2026-09-22 核):EmitTables.cpp 的 MGR_UNMIGRATED_* 列表全空,只剩手写的 SetSwapInterval(P10)与按裁定无行的 DeleteTransformFeedback(P9);契约 §11 / SPAWN-PLAN §8 的"511 class-C entries"是 P5 joint 树上的旧普查(notes/p6/census-classC.md),不是当前数;beba0256 起 wire 复制到 owned buffer 并保留 baseVertex/baseInstance/drawID,本轮补齐 monolith 臂 62bfe461..e258a822)、multi-draw client indices、RGB 三通道 CPU mip、renderbuffer copy endpoint、未绑定 named clear、texture-remint-pull 仿真保留具名拒绝 → P8 / P9;Magma split compute / image 的“82 个错答”是 P5b 历史普查计数,不是当前失败清单;P5f 后续已补应用 buffer consumers 与 Android 旋转 blit,Magma inproc 已能运行所测 MC 世界;后续 run-ahead 已完成(报告);XFB、部分不对齐范围、placeholder/native-format 及进一步性能工作仍归 P7(报告) |
| E2 wire 内容控制 | draw-drop 是 OpenRA 的有效控制(758 draws,SSIM 0.000036);clear-drop 被全屏 overdraw 掩盖,不能作控制 |
| max record bytes | 实测 reduced / OpenRA maxrec=784 B,默认 cap 4 MiB;只代表所测负载,后续索引 / indirect 尾仍须记录 |
| 树外脚本 | 普查跑器、wsl_p5_gate.sh、Redmi 定频行都在 ~/w7/notes/,git merge 不会传播 |
| 临时 CI trigger | 合入 dev 前删除 test.yml / apk.yml 的 feat/disaggregated TEMPORARY trigger |
开放问题
P0 已回答的不再列出(spike A 的域、spike B 的分档、posix_spawn 不可用、OOM 探测惯用法、GetInteger64i_v/GetProgramiv 退役、D21 与 RenderbufferObject lifetime id、动态 accessor 基线)。
- client 侧 dirty 走查的真实每 draw CPU 代价。 已答(P2–P4a):推送没有在拉取基线之下净减少;Release 下 P2 边界 +6–12%,P3a 在 VAO 切换密的 rd12 上再加 +17–19 pt,P4a 在 Espryt 上 +3.4–5.7 pt(
MEASUREMENTS.md§3–§5)。用户 2026-09-08 接受,性能自此只记录。 - 真实语料上纹理重铸拉取的发生率。 已答(P4a):可忽略,保留 LRU 维持默认 0——79 例 × 两后端 780 个统计窗口里共 2 次(
iris-photon、iris-derivative各 1,只在 DirectGLES)。但 P5b 下这条路径是具名 Fatal,正是这两条 trace 加create-indirectGLES 的首阻塞(P9)。 - spike B 的
untrusted_app域复核。 两台设备的分档在shell域测得;从应用进程再跑一次extmem_probe(spike A 的 exec 钩子已可用)。P11 前做。2026-09-22 降级:只对同机臂的 T0 / T1 有意义,stream 数据面不需要。 - 渲染状态的 wire 粒度。 已答(P2):16 个边界 / 15 个 chunk,7 pipeline 396 B + 8 dynamic 772 B;CSO LRU 64 暂定,P13 重调。
- 无存储的 capability。 已答(P2):
FramebufferSrgb、DepthClamp、TextureCubeMapSeamless三个都补了真存储,sizeof(RenderStateParameters)仍 1168。 - 具名 UBO host payload 的形状(D-B8)。 Magma 在 26.3 世界每帧重打包 331 KB 具名 UBO 字节,Espryt 为 0。要么冻结第二变长尾形状,要么走备选(Magma 直接描述符绑定常驻
VkBufferrange,独立devPR)。P7。 MG_Util的切割缝。 server 需要 SPIRV-Cross / ESSL 转译缓存 / 格式处理器 / POST 探针,client 需要 glslang 与反射层;MG_Util内部是否有干净的 Transpile-vs-Reflect 缝未审计。P7。- 一份反射归档能否服务三个消费者。 Espryt 那一半已答(P4a):能且不需要复制。Magma 的两个消费者(
DirectVulkan.cpp为glGetProgramResource*重跑反射)未答。P7。 - viewport-array 回放能否塞进一次
draw_vbo:各遍之间观察到的状态是否与今天一致未验证。P8。 ResidentSubData的不对称。 P3a 原样保留:SubDataResident是kOptional,Magma 未补实现、kCapResidentSubData未接线。P7 / 独立devPR。SEG_STAGE的上限。 内容侧已答(2026-09-20):分块已实现——buffer 范围走查(1e7c372e)与纹理整宽 slab(9469d48e)按MGPipeStageChunkBytes()(默认 32 MiB 的 1/4 = 8 MiB)切成多条resource_subdata,所以单次 128 MiB 上传不再需要显式MOBILEGL_IPC_STAGE_MB=256;单片仍超 arena 或该 record 类型未接入分片时仍是Fatal{RingOverrun, "SEG_STAGE"}兜底。仍未答(2026-09-22 收窄):a6 §6 实测没走查的只剩 program archive(唯一无界 blob)与draw_vbo的NumDraws*12range 尾;宿主索引 span 无 producer(划掉),set_global_constants有走查。二者的分片是 P6.5 sl 与 P8 共用的同一件事,stream 发送窗口对着它们定尺。MC in-world / Create 的占用分布仍未量(门 8 的树上工作正在补)。- P13 之后 split-only 渲染 bug 的 server 侧第二意见。 verify 构建 + recorder 只覆盖推送内容,不覆盖后端对它的解释。
- 烘焙后的内部 shader 能否在没有活
ProgramObject的情况下表达 uniform location 与 UBO 布局。 未做原型。P7。 - 推送模型改变哪些按拉取模式调过的缓存命中率。 幸存者容量在 P13 重调。一条线索:设备上 26.3 Espryt 的
sve≈ draw 数(每 draw 重发一次 sampler-view 集合),桌面只有 ~0.07/draw;列入 P3b/P4b 优化清单。 - monolith 的
*IndirectCount不调SyncGpuWrites()是不是潜在缺口。 独立dev问题,拆分不得借机顺手修。 - 索引宿主镜像的实际内存占用。 MC / Sodium / Iris 语料里 element-array buffer 总量未测;若显著超 64 MiB,退化路径的频率与代价必须实测。P8。
create-indirectfixture 在 Adreno 830 上的失败是dev@81b17c0b就有的(基线 APK 复现),不是本分支造成;rd12+ Magma 在两台 Adreno 830 上的scudo::reportMapError崩溃同样是dev侧。两者排除在设备 A/B 之外、留在桌面语料里;P8 要在create-indirect上断言roundtrips-per-frame == 0,所以dev的修复在别人的关键路径上。- split 两侧的负载平衡。 P5e 解锁出来的新问题:VD32(~3550 draws/帧)下 apply 线程 11-12 ms、client 15.7 ms,client 是瓶颈且打满 ~94%。把工作从 client 挪到 apply 会直接降瓶颈——这在 lockstep 下毫无意义(两侧串行相加),run-ahead 才使它成为一个杠杆。供选:编码侧的 emitter、tracker 走查、持久映射的脏页追踪(
change_protection在侧写里占 1.3%)。需先重新侧写:等待消失后尾部各项的分母都变了。排在 P5e 之后;无主,2026-09-22 建议挂在路线图推完后的优化阶段,P6.5 的 2×2 矩阵车道顺带重新侧写 - 链路带宽预算与压缩。 TCP 终局下
SEG_STAGE字节/帧对的是 Wi-Fi / USB(量级 30–100 MB/s),不是内存;ARCHITECTURE.md§14 的 ~1 MB/帧预算在 60 fps 是 60 MB/s,已贴着 Wi-Fi 上限。是否对 stage 载荷做 LZ4 类压缩、纹理上传是否按内容哈希在 server 侧去重,要等门 8 的分布出来再定。P6.5 契约写下第一行之前必答。 - 大单次上传的停顿。 128 MiB 一次上传在 50 MB/s 上是 2.5 s;内容侧分块解决的是"装不装得下",不是"要等多久"。跨机臂上要么接受并记录,要么给上传做异步化(与 P9 的语义一起想)。不假装解决。
- 配对 / 认证的形式。 令牌足够还是要 TLS?局域网上的 GL 命令流不含凭据,但一个能被随便连的渲染 server 是一条远程代码路径。Ph 定。
- client 平台范围。 默认先 Linux + Android(现有 CI 能验),Windows client 第二批(
ShmSegmentWin32在;pipe:具名拒绝作废、TCP 替代),macOS 仍不拆分。这决定ARCHITECTURE.md§15.3 "Windows 机器不是正确性门"要不要改。 - 半开连接的检测。 Wi-Fi 掉线不产生挂断事件;dl 的闩取自描述符挂断,TCP 上需要 keepalive / 心跳超时并由传输层报告为挂断——这与"绝不取自 apply 超时"不矛盾,写清楚免得重吵。P6.5 ct。
- E1 对照的重定义(ID-122)无主。 建议并入 P6.5 的门设计:S8 的好路径断言(
SessionFaultCount() == 0)已是它想表达的形状,缺的是一个能被证伪的负控。