- CtWireScenario reads `ar` / `od` off the server's PipeStats line on every arm - the serial == resets assertion could not fail and is dropped - ServerVerbSink's unread BindContexts, Presents and LastPresentSerial go too
14 KiB
构建布局、开关与计数器(原 ARCHITECTURE §16、附 A、附 B)
设计细节。设计要点与全部章节的索引见
../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(P13 起):默认(单进程记录臂,不编
MG_Remote,出货形态)、verify、split;Android 两份 APK flavour(单进程 / split)。pull 构建与MOBILEGL_PIPE_PUSHCMake 选项在 P13 删除。 - 模块(P13 W7):
SOURCE_FILES按 link ratchet 的 PARTITION 拆成七个 OBJECT 库,MobileGL/MobileGL_s由它们拼成;split 构建的MobileGL_server_linkcheck只链 SERVER 与 SHARED 模块并--no-undefined,基线外的前端引用在构建时就失败(scripts/ci/server_link_check.py)。记录臂的 server 端代码在MG_Backend/Record/(RecordVerbSink、StagedShadow、StagedTextureStore),客户端 verb 发射在MG_Impl/Pipe/Verb/,不再依赖MG_Remote。 - 测试接线: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前移除。
16.1 集成测试车道的接线规则
MG_IntegrationTest/CMakeLists.txt 的每条车道都要能因它存在的理由变红(§13.2),接线规则:
- 读库日志的用例必须独占日志:库以
fopen(path, "w")打开日志,同一路径上的每个进程启动时都截断它,ctest -j下读者会读到邻居截断后的文件,症状与「计数从未发出」相同。条目因此带私有MOBILEGL_LOG_FILE_PATH,过滤器只选这一个用例;车道必须跑整个 scenario(重新过滤会改测试名)时,加以日志命名的RESOURCE_LOCK,ctest 不会同时运行持同一把锁的条目。 - 选臂的每个变量都写成条目自己的 ctest
ENVIRONMENT属性:属性只覆盖它写出的变量,其余从 job 环境漏进来(job 级MOBILEGL_TRANSPORT=inproc会把没钉monolith的 monolith 条目悄悄变成又一条 split)。 - ctest 不把 SKIP 算失败,跳光的车道和全过的车道一样绿:必须真跑的车道钉
MOBILEGL_ITEST_REQUIRE_GPU=1,让不可用的 harness 变成失败;功能 opt-in(如MOBILEGL_ESPRYT_ENABLE_TEXTURE_VIEW=1)必须到达每个臂里读它的进程,tcp 臂的 server 由TcpServer.Startfixture 预先启动、不继承 client 条目的环境,所以也要设在 fixture 上。 ctest -L按正则子串匹配:新标签不得包含任何被不加锚点选择的已有标签,所以限定词放在中间(integration-magma-all-spawn不含integration-magma-spawn,integration-magma-spawn-buffers不含integration-magma-buffers);只选一个标签时写'^label$'。- 门控车道带一个私有的第二标签并以
--no-tests=error运行:构建缺少该选项时匹配为空即失败,而不是零条目的绿。 - 三条传输臂:
Split.为 inproc,server 是测试进程里的 apply 线程;Spawn.的 server 子进程继承::environ(ServerSpawn.cpp只去掉MOBILEGL_TRANSPORT与非 server 自有的MOBILEGL_IPC_*);Tcp.的 server 是整条车道共享的TcpServer.Startfixture,由scripts/ci/tcp_server_fixture.py以它自己的环境启动一次,每个 Hello fork 一个 session 子进程。 - 条目 ctest
ENVIRONMENT里由 server 读取的旋钮只到达 inproc 与 spawn 的 server;tcp 副本会以默认臂运行却顶着声称旋钮的名字,永远不会红。这类条目只注册 split + spawn(Magma 的列入scripts/ci/spawn_lane_parity.py的MAGMA_SERVER_ENV_KNOB_NO_TCP),旋钮取默认值的孪生条目保留三条臂;要 tcp 臂就得让 fixture 带上旋钮:对整条车道无害的追加到TcpServer.Start的ENVIRONMENT,否则另起一个带旋钮的 supervisor(P8Coverage.cmake)。只能在本进程读取的探针(描述符池普查、server loop 挂钩、server 缓存窥视、验证层输出)只注册 inproc(MAGMA_INPROC_ONLY)。 - Magma 分三层:门控的
integration-magma-{split,spawn,tcp}是手工布置、各带 flavour(strict + 双块、adopt 档、run-ahead credit、cache 钉定)的车道,测绿后才进 CI 门;integration-magma-all-*重放每次mgl_itest_register_split_arms调用;integration-magma-full-*跑整个集成二进制减去MGL_ITEST_MONOLITH_MECHANISM_FILTER与DualBlockScenario。后两层不进任何 CI 门,用例随修复其阻塞点的提交升入门控层。 MGITEST_SPLIT_LANE=1武装ScenarioFixture的析构断言:case 必须推进 client 编码器的记录序号,tcp 臂还要证明没有本地 server 子进程。它只适合每条都绘制的精选车道;整二进制集合(integration-magma-full-*、integration-verify-split)不设它,因为纯查询 case 由CapsMirror回答、不产生记录,代价是这些集合不证明记录过了线。
附 A:开关
CMake:MOBILEGL_BUILD_DISAGGREGATED(OFF)、MOBILEGL_BUILD_DISAGGREGATED_INPROC(OFF,隐含前者)、MOBILEGL_PIPE_VERIFY(OFF,永不出货)、MOBILEGL_BUILD_SERVER_SPIKE(仅 Android spike)、MOBILEGL_FLATC_EXECUTABLE(只服务 flatc-check)。MOBILEGL_PIPE_PUSH 与 MOBILEGL_PIPE_LEGACY_MEMOS 在 P13 删除(旧缓存里的 OFF 只告警并忽略)。
运行时,MGPipe(MobileGL/Config.h、ConfigLoader.cpp):
| 变量 | 默认 | 说明 |
|---|---|---|
MOBILEGL_PIPE_PUSH |
0x3fff |
子系统位图(MG_Pipe/MGPipe.h,位永不复用);P13 起位 0–13 固定开,清任何一位都在启动时具名拒绝;只剩位 63("关 CSO 内容寻址"的行为对照)可设 |
MOBILEGL_PIPE_VERIFY / _VERIFY_FATAL / _VERIFY_CORRUPT |
0 / 1 / 空 | 影子比对与它的阴性对照。比对器只编进 verify 构建,其他构建里 MOBILEGL_PIPE_VERIFY=1 只告警;_VERIFY_CORRUPT 不进 Config.h,只有 verify 构建在比对器武装时读它(PipeFill.cpp 的 ArmVerify,同样经环境与系统配置文件)。poison 的阴性对照没有运行时开关:集成测试的 harness 标记 MGITEST_POISON_OMIT 由重新 exec 的子进程经 verify 构建才有的 MGPipeSetPoisonOmission 武装(Harness/PoisonOmissionArm.h) |
MOBILEGL_PIPE_STATS / _STATS_PERIOD / _STATS_FILE |
0 / 120 / 空 | 边界计数器(附 B);dump 按角色写 <base>.client.json / <base>.server.json |
MOBILEGL_PIPE_INDEX_MIRROR_MB |
64 | 索引镜像预算(P8);_LEGACY_MEMOS、_TEXEL_RETAIN_MB、_HANDLE_ABA_CONTROL 随各自的臂在 P13 删除 |
运行时,传输与 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_FRAMES_IN_FLIGHT |
3 | 1..64,两个后端共用(旧名 MOBILEGL_MAGMA_FRAMESINFLIGHT 为弃用别名):Magma 的帧上下文数;Espryt 在 present S 后等待帧 S+1-N 完成;split 的 present credit 由它导出为 max(1, N-2)(P15) |
MOBILEGL_IPC_PRESENT_CREDIT |
由 MOBILEGL_FRAMES_IN_FLIGHT 导出(默认 1) |
1..8,§14;显式设置覆盖导出值,仅供实验 |
MOBILEGL_IPC_POLL_ESCALATE |
64 | 未报告 fence 的零超时轮询本地答"未完成",同一 fence 连续第 N 次才往返(P10,CONTRACT-P10.md §1);0 = 每次轮询都往返,是 A/B 对照 |
MOBILEGL_IPC_BATCH_WAITS |
1 | lockstep 下值类记录发布即返回;verify 构建里 MOBILEGL_PIPE_VERIFY=1 强制 0(RUN_AHEAD 同样强制 0,ROLE_SPLIT_STATE 强制关;其他构建不改它们) |
MOBILEGL_IPC_EVENT_WAIT_MS |
2000 | 反向事件遇满环时 server 等 client 排空的整笔预算(§11.7) |
MOBILEGL_IPC_WIRE_DEFERRED_MB |
64 | Magma wire 臂延迟回收的水位线;0 是 M2 阴性对照 |
MOBILEGL_IPC_PERSISTENT_HASH_SUPPRESS |
1 | persistent-map 推送只推变化的块(粒度固定 64 KiB,PersistentMapTracker::BlockBytes());0 是整段推送的 A/B 对照 |
MOBILEGL_IPC_ADOPT_TIER |
未设 = T0 | 未设(用户 2026-09-29 定为缺省)= 能用就 T0,不能用(stream、无 AHB、POST 失败、server 不允许)安静回退 T2:只 MGLOG_D、照样计数;0 = 点名要 T0,回退有具名行(A1 的 Refuse{AdoptTierOnStream} 等);1 = T1 已关,具名拒绝后 T2;2 = T2(P11 前的缺省)。CONTRACT-P11.md B2 |
MOBILEGL_IPC_ALLOW_ADOPT_T0 |
1 | server 自有:0 = 永不批准 T0(会话走 T2),经 ServerSpawn 保留 |
MOBILEGL_IPC_STRICT_ERRORS / MOBILEGL_IPC_AUDIT / MOBILEGL_IPC_ROLE_SPLIT_STATE |
0 / 0 / 1 | 残余输入读升级为 Fatal / 退休 staging 填 0xDD / 双块(P5f;P15 起默认开,0 关闭,阴性对照显式设 0;双块下 barriered fill 写 client 块,不再 quiesce applier) |
MOBILEGL_IPC_SPIN_US / MOBILEGL_IPC_SERVER_AFFINITY |
50 / auto |
park 前自旋预算 / apply 线程亲和(Redmi 内核忽略) |
MOBILEGL_IPC_RECOVER |
1 | session 因 device loss 结束后,应用下一次 eglCreateContext / eglCreate*Surface / eglInitialize 重新拨号得到新 session(旧 context 保持丢失,surface 在新 session 重建);0 = 丢失对整个进程永久 |
MOBILEGL_IPC_RESPAWN |
— | 具名拒绝:没有阶段实现 server 重启后的全量重推 |
显式不设立:MOBILEGL_IPC_PROGRAM(没有 relink 档)、MOBILEGL_IPC_VALIDATE_SERVER。计划中未接线: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、server 应用的控制记录 ar(applier_reset)/ od(object_death)、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 仍有效(docs/archive/notes/p6/README.md §12.1)。站点清单(哪些路径没有接线)写在 PipeStats.cpp 头部,那份清单是契约。