Two things land together: the plan and contract draft P6 needs before it starts, and the
working notes that until now existed on exactly one machine.
1. P6's plan and contract draft (neither started; not one line of code)
docs/Disaggregated/P6-SPAWN-PLAN.md the package plan
docs/Disaggregated/P6-CONTRACT-DRAFT.md the contract draft (moves to
MG_Remote/CONTRACT-P6.md when c6 lands)
The three findings that mattered, all written into the documents:
a. P6 is SMALLER than its roadmap cell suggests. The transport primitives are already in
the tree and already tested: SocketDoorbell including hangup detection and the two
lost-wakeup fences (Doorbell.cpp:211-360, four cases in FdPassingTest), SCM_RIGHTS fd
passing, ShmSegment::Adopt, and the SurfaceOp / WindowKind schema already defined in
protocol.fbs. inproc uses a real second shm mapping rather than an alias, and
SessionRings.h:22-28 says that was done so today is not the first time it runs. P6 is
assembly plus a control plane plus a process, not "write a socket transport".
b. The evidence for "P6 is only a transport swap" has EXPIRED. It was bought by P5c, whose
audit found 59 direct memory accesses in a tree that already looked clean. P5e changed
the tree again. So the first package, a6, is a read-only audit that writes no code, and
every contract row that depends on it is marked PENDING a6 rather than decided in
advance.
c. P6 does not get a game running on the phone. ANativeWindow* is meaningless in the child,
and protocol.fbs:186-196's own comment says Android transfers the window out of band -
that band is P12. P6 lands pbuffer / surfaceless / offscreen, which is what HeadlessGL,
the split-equivalent lane and trace replay need, and which is exactly what P6's own exit
gate measures. A real window reaching the server is a named refusal. This gets its own
section because "P6 is done" would otherwise read as "the game runs now".
The contract adds rule G: a role may not name a process-local handle. It extends P5c's
rule E (a role may not name the other role's memory) from memory to identity - EGLDisplay /
EGLSurface / EGLContext / ANativeWindow* / fd number / pid / mapped address either cross as
a token this contract defines or they do not cross, and a descriptor crosses only through
SCM_RIGHTS, where the kernel renumbers it.
What P5e left for P6 gets its own section. The latch semantics are already designed
(ARCHITECTURE.md:504) and are copied unchanged. What is new is that after run-ahead the
client no longer waits while the peer process can DIE: the client holds records that were
published and will never be applied, and Android's freezer suspends the child without
closing anything. So slow and dead must be distinguishable WITHOUT a timeout threshold.
That is P6's only new design; everything else is carriage. It shares a predicate with E1's
outstanding re-specification (ID-122), and the two should be designed together.
Two gates carried over from P5e's lessons: G2 requires integration-spawn's test-name set to
match integration-split's name for name, because a differing set means the lane is quietly
running less; and the arm-proof gate requires every entry to record the child pid and
transport=spawn in its OWN private log - ID-124 established that this kind of false green
really happens, and a spawn lane that actually ran monolith would pass every other gate.
S2's shape must be settled before it is written, and the document says why: E1 is the
cautionary case.
2. The working notes move into the repository (docs/Disaggregated/notes/, 415 .md files)
ROADMAP / CURRENT_STAGE_PROGRESS / MEASUREMENTS cite ruling numbers, audit line numbers and
the provenance of gate figures from ~/w7/notes/, a directory that exists on one machine and
is under no version control. A record cited hundreds of times that lives only locally is not
a record, so it moves in whole, with its directory structure intact.
Only .md moves. Scripts, run logs, census and gate result JSON, patches, APKs and nm dumps
stay where they are: they are evidence DATA rather than documents - large, regenerated per
run, and mostly carrying absolute paths and device serials that mean something only on the
machine that produced them. The mapping and this split are written in
docs/Disaggregated/notes/README.md.
Two exceptions are recorded there too: P6's BRIEF has been promoted to a first-class
document and keeps no copy under notes/, so the two cannot drift apart; and recovered/ holds
material salvaged from failed workflow runs, which may duplicate the like-named phase
directories and is kept because a few of those were the only complete version at the time.
CI's citation lint takes docs/Disaggregated/*.md only (non-recursive), and notes/ is
deliberately outside it: those notes' file:line citations were written against their own
code heads, and resolving them against today's HEAD would fail wholesale - which is not an
error, it is their correct state as a historical record. To check one, use the revision it
declares (--rev).
3. Inconsistencies fixed in passing
- README.md's status line still said P5e was in progress while the stage table already said
closed.
- CURRENT_STAGE_PROGRESS's next-step item 1 still listed the exit gate as pending, though
(a) and (b) were run in the previous commit; rewritten to closed plus the one item that
actually remains (E1).
- ROADMAP's P6 row still had the pre-phase plan in three of its cells; it now carries the
package order, the scope boundary and the two new gates.
Checked with check_doc_citations.py: 125 citations across docs/Disaggregated/*.md, 0 problems.
48 KiB
11. 分阶段实施计划
通用纪律(每个 commit 都适用):默认 ALL target 必须能完整构建;禁止提交热路径插桩;每个门必须能因它存在的理由变红;Windows 机器不是正确性门(其 Vulkan 缺
vkCreateHeadlessSurfaceEXT,占该机 567 个基线集成失败中的 423 个);设备对比走 reboot-clean + 同热窗口配对 A/B,CPU 定频按项目协议(大核 1.96 / 小核 1.55GHz,GPU 拉满,40°C 门槛);每个阶段的出口都跑一次 §10.3 的五部分门(不只是 P0);每个阶段的性能判据都是逐线程 CPU 时间,不是墙钟帧时。 两条跑道:P0-P4a、P3b/P4b、P7、P8、P13 是 monolith 跑道,每一段都可独立交付、可随时中止且 monolith 严格好于起点;P5、P6、P9-P12 是 IPC 跑道,整段继承PLAN.md§6-§13。
P0 — 卫生、度量、门与骨架(8 天)
交付物
- 清工作树 per-draw
fprintf:DirectGLES.cpp:290-303、:640-663、Managers.cpp:875-877(后者在pendingMutex临界区内)。CI 加 grep 门禁止MG_Backend/与MG_State/下出现fprintf(stderr/printf(。 TracyPlot逐帧字节/调用计数器,装在边界两侧,按类分:cmd-records、stage-buffer、stage-texture、stage-ubo、persistent-map-push、server-ring、server-staging、residual-value-block、texture-pull。树里今天完全没有 per-frame 字节度量(MG_Util/Metrics只是格式算术,Tracy 只有 zone 无 plot),所以之后每一个 ring 尺寸、批阈值、wire 粒度决策否则都是猜测。两台设备取基线。MG_Pipe/PipeCalls.def+MGPipeTypes.h+MGPipeHandles.h+MGPipeCallbacks.h:完整调用目录,即使暂未实现的条目也占位(记录编号绝不能churn)。scripts/gen_pipe.py与六个生成器 G1-G6 的骨架 + CIpipe-gen-check(重生成 +git diff --exit-code)。MOBILEGL_PIPE_PUSH/_VERIFY/_STATS在ConfigLoader.cpp与既有开关并列解析。- 三个严格 no-op 的免费收益:
GetIntegeri_v/GetInteger64i_v/GetProgramiv的纯前端 case 移回MG_Impl(Espryt 14 / Magma ~10 个读点);RenderbufferObject::GetLifetimeId()(不加GetVersion());D21 重键(m_xfbCounterSlotByObject改用 XFB 对象 lifetimeId,VulkanRenderer.cpp:11136-11146)——这一条是潜伏 bug 修复,先独立落dev。 - 回答
FramebufferSrgb/DepthClamp无存储是潜伏 bug 还是有意为之(§10.4-6)。必须在渲染状态 blob 冻结之前。 MG_Remote/{Protocol,Transport}骨架与PLAN.mdP0 完全一致(ITransport、InProcessTransport、校验型Framing、Ring+RingControl双游标三元组双向 doorbell、Doorbell、ShmSegment、SCM_RIGHTS第一优先);protocol.fbs+ 提交的protocol_generated.h+flatc-check;MG_Test/Wire/。PLAN.mdP0 的两个 spike 原样跑:spike A(Android 交付链:改名成lib*.so的add_executable能否被 AGP 打包、能否在untrusted_app域内 exec);spike B(external memory 导出,两台设备)。
验收:AdvertisedLimitsScenario(6 个测试)绿;367 集成 × 2 backend + 428 单元逐名不变;40 个 trace 全绿;nm --defined-only 与剥调试信息 .text 除三处刻意移动的符号外一致;两台设备的基线字节/调用/逐线程 CPU 数字记录在案;spike A/B 出结论并写进开放问题(spike B 的结论直接决定 P11 的规模)。
P1 — PipeInputs 替换与 verify harness(8 天)
交付物
MG_Backend/MGPipe/PipeInputs.h:每个 backend 真正用到的GLContext方法一个访问器(Espryt 32 / Magma 55),字段类型与今天读到的完全一致,按 memo 键组织。- 机械
sed:MG_State::pGLContext->→MGB_CTX->(293 处);外加逐条手工转换 58 行非箭头用法(§2.4:~40 处MOBILEGL_ASSERT真值判定删除、~10 处空守卫改直读、3 处 patch 三元、DirectGLES.cpp:146的.get()裸指针捕获、2 处!= nullptr条件、1 处注释)。这份 58 行清单是本阶段的显式交付物,不是"顺手处理"。 SnapshotFromGLContext()在PrepareForDraw(DirectGLES.cpp:2916)与SetupDraw(VulkanRenderer.cpp:6371)顶端。- G5 的
m_filledMaskpoison:debug 与 disaggregated 构建里读未填字段 =Fatal{UnmigratedPipeInput, "<field>"}。 - G4 的
MOBILEGL_PIPE_VERIFY=1逐字段影子比对器 + 第三种 CI 模式接线。
验收:pull 构建里 nm --defined-only + 剥调试信息 .text size 与替换前完全一致——本阶段可证明是一次替换(这是最后一次这条等式成立);全部 40 个 trace 与 367 个集成测试在 MOBILEGL_PIPE_VERIFY=1 下零分歧;故意损坏一个快照字段能让 verify 门变红;故意留一个字段不填能在第一个 draw 上触发 poison Fatal。
**★ 第 16 天 — 最早可见里程碑:**零产品风险地证明"推送等价于拉取",逐 draw 逐字段。
P2 — 值推送:渲染状态 CSO(双后端)+ 残余值块(8 天)
交付物
MG_Impl/Pipe/Tracker.{h,cpp}:dirty 位来自既有计数器(§5.2,三个回绕Uint16在 tracker 边界加宽)+ 固定 validate 顺序(§5.3)。MG_Impl/Pipe/CsoCache:64 项 LRU,键是三段(head/blend/tail,与DirectGLES.cpp:2038-2047完全一致的划分)的 xxHash。create_render_state/bind_render_state(D-B1):Espryt 侧RenderStateImpl的 693 行函数体、三段 memcmp、g_syncedColorMaskAlphaWidenMask、dual-source decline 一行不动(消除 4 个读点::2007, 2021, 2050, 2133);Magma 侧ComputePipelineStateHash/GetOrCreatePipeline/ApplyDynamicDrawStateTail改从 CSO 取(消除 ~55 个读点)。两个版本号都过线。set_pixel_pack_state(PACK only)、set_patch_state、set_vertex_attrib_defaults。set_residual_value_state+ResidualValueBlock(§6.3):static_assert(sizeof == MGL_RESIDUAL_BLOCK_SIZE)(逐阶段下调)+ 逐成员offsetof断言 + split 下逐字段序列化。
验收:367 集成 × 2 backend × 2 模式(pull / push)逐名相同;40 个 trace 在 monolith-push 下 SSIM ≥ 0.99,双后端;ClipDistance、SampleMaskScope、SampleVariables、DualSourceBlend、ViewportArray、PrimitiveRestart 场景绿;verify 模式零分歧;两台设备 reboot-clean 配对:monolith-push 在 p50 与 p99 逐线程 CPU 上落在 monolith-pull 噪声内或更好。
★ 第 24 天 — GO/NO-GO 决策点。 此刻手上有:verify harness、双后端已推送的渲染状态、真实 CPU 增量、Track H 在两个最便宜子系统上的单位成本。若 CPU 数字为负、或 Track H 单位成本超估计 50%,退回方案 A 只损失这 24 天中的 16 天(P0 是 PLAN.md 共有的)。
P3a — handle wave 1(Espryt):slot 基建、buffer、VAO(12 天)
交付物:SlotAllocator(按 kind 稠密 + gen + 保留段);PipeObjectTables(POD slot 数组);删 TwinLookupMemo×3、OwnerEquals、g_fbSlotCache、6 个 registry GC 扫描、VkTextureManager::PruneDeadTextures、m_convertedVertexStreams 的 sourcePin;7 个 BufferBackendOps → resource_create/respecify/destroy、resource_subdata、buffer_subdata_resident(可 null,保住 Magma 的差异)、resource_flush_range(带应用真实 access flags)、resource_readback、map_persistent(不碰实现);pool 与延迟释放机制原样搬;create/bind/delete_vertex_elements_state(两个视图都带:解析后的 VertexAttribute[32] 与 VertexBufferBindingPoint;IsLong 与 Type 分开);set_vertex_buffers(baseInstance 是显式字段,不再是调用方武装的 ScopedFetchBaseInstance 作用域);set_index_buffer(带 restart index 与模式);Adreno 禁用属性 SIGSEGV workaround 原样保留。
验收:全套门(monolith-push,DirectGLES);LargeArenaAdoption、ResidentIndex、StorageBufferRegrow、AtomicCounter、BufferTexture、CrossFrameBuffer、SsboArrayLength、SsboArrayDynamicIndex、VertexArrayEnableDisable、VertexAttribBinding、DoublePrecision、DrawParameters、MultiDraw、PrimitiveRestart 场景;create-indirect、create-instancing、rd12-odinlite、improved-transparency-26.3、fabric-sodium trace SSIM ≥ 0.99;新增 HandleRecycleScenario 绿,且它在 slot 重键之前必须是红的;MC 26.3 在 Adreno 上 p99 不变(16MiB 采纳结果不得回归)。
⚠ 再基线检查点 1:若 P3a 超期 >50%(>18 天),"窄 handle 化"的前提就是错的,必须在 P4a 开始之前重定基线。
P4a — handle wave 2(Espryt):FBO / 纹理 / sampler / program 的身份与描述符(16 天)
刻意推迟到首帧之后的部分:memo 重键、dirty 归属反转、program 陈旧性重构(→ P3b/P4b)。本阶段只做 wire 需要的身份与描述符。
交付物:set_framebuffer_state(8 个 MGPSurface + client 解析后的 readSurface + 内联 internalFormat + contentHash + isDefault 保留 handle,退役 4 处 pDefaultFramebufferInfo 读);四个跨对象 mask 在推送时刻推出;create/bind/delete_sampler_state(SamplerParameters 逐字节含 borderColorForm);create/delete_sampler_view(glTextureView 变普通 view CSO);set_sampler_views(client 侧解析)+ bind_sampler_states;set_shader_images;create/bind/delete_shader_state(逐 stage SPIR-V + Visit() 全结构体反射归档 + sizeof 绊线);set_draw_program / set_dispatch_program;set_global_constants;CompositeResolver.cpp(保留 slot 段 + 生命周期);纹理与 renderbuffer 的 resource_create/respecify/subdata。emulation 路径在 split 模式下显式 Fatal 直到 P8。
验收:全套门;CrossFrameBuffer、LayeredAttachmentShape/Barrier、SnormAttachment、RenderbufferBlendFormat、FragmentOutputArrayIndex、Orientation、ClearThenReadPixels、FragCoordOrigin、TextureView、ProgramPipeline、PostLinkAttach、RelinkStageSet、SpirvShaderBinary、AsyncCompile(6 个)场景;KHR-GL46.direct_state_access.framebuffers* 与整个 packed_pixels 块在两台设备上绿(~3300 个 framebuffer/用例,是 handle 复用的压力测试)。
P5 — 传输 + inproc applier + 发射表(12 天)
交付物:MG_Remote/Client 的发射表实现 MGPipeScreen/MGPipeContext;Server/PipeApplier.cpp 从记录填 PipeInputs 与 PipeObjectTables;ServerLoop(mgl-srv-io + mgl-srv-apply,后者终身持有原生 context);单一 hook 点 MG_Backend/Init.cpp:48-70 装 BackendObject_Remote(下游零 #ifdef);MGPCaps 快照;一条阻塞 read_pixels;client 侧保守 MarkGpuWritten 与 emitSeq;client 侧块粒度 persistent-map 推送(T2 档下强制,PLAN.md §5.10 第 2 条);InProcessTransport。
验收:ctest -R 'DirectGLES\.Split\..*(ClearThenReadPixels|Triangle)' 在 MOBILEGL_TRANSPORT=inproc 下绿;OpenRA trace 在 split 模式下 SSIM ≥ 0.99;PersistentCoherentMapScenario 绿(map PERSISTENT|WRITE|COHERENT、写、不做任何其它 GL 调用、draw、回读校验——这是唯一一个为一个致命缺陷而存在的门);两个角色的峰值 RSS 记录在案,作为对 PLAN.md R14 的基线;persistent-map-push 字节量出数;任何未迁移的 PipeInputs 字段读产生 Fatal{UnmigratedPipeInput} 而不是静默垃圾。
★ 第 64 天 — 首个 IPC 帧(inproc)。诚实标注:这是缩减路径——client 数组、restart 重写、indirect-count 解析在 split 下仍是 Fatal,全功能要等 P8。
P6 — spawn transport(5 天)
交付物:SocketTransport(socketpair + fork/execve,显式 envp 剔除 MOBILEGL_TRANSPORT/MOBILEGL_IPC_* 加 mobilegl_server_main 内强制 Monolith 的双保险);ServerMain;MOBILEGL_IPC_SERVER_PATH 为主 + dladdr 兜底(两个桌面门都会挫败 dladdr);就绪握手有界重试;client EOF 即时退出;server 死亡的 device-lost latch;trace-replay 的 SPLIT 后缀与 -DTRACE_TRANSPORT= 接线。
验收:P5 全部测试在 MOBILEGL_TRANSPORT=spawn 下绿;fork 链测试断言进程树只多一个子进程;HeadlessGL 的 fork 预检交互测试无孤儿 server(pgrep 计数在 100ms 内归零);run_android_retrace_local.py --case OpenRA --backend DirectGLES 在 35d0befa 上 SSIM ≥ 0.99。
★ 第 69 天 — 首个跨进程帧(缩减路径)。
P3b / P4b — 深化(Espryt):memo 重键、dirty 反转、program 陈旧性(28 天)
交付物:重键 ResolvedDrawBuffers、PendingAttribValueMask、ConvertedFloat64Stream、SyncCurrentFBO 四元组戳、ResolvedTextureBindingMemo、SamplerPassMemo、image sweep、program registry 到 {slot, gen};删 g_unitTextureSyncList、g_fboTextureSyncList、g_unitSamplerLookupMemos、g_imageSweep*、DirectGLES.cpp:1372-1489 的 ~115 行 unit-bindings epoch 推导;dirty 归属反转(§7.3,client 保 rect 模型与发射游标、发射后自清;resource_subdata 带 box 与 rect 列表、server 选形状);删 fragColor 重推导 workaround(:2712-2732)与 g_broadcastMemo*;用推送的 FBO 状态、image 格式、set_patch_state 退役 9 条陈旧性判定里的第 4-6、8-9 条;Espryt 的 raw-depth-fetch SamplerObject 原生化。
验收:~25 个纹理场景(TextureView、LayeredTextureReadback、ImageSizeAfterRespec、FormatlessImageBake、NonCoreImageFormat、ImageFormatQualifier、ImageTargetKind、ImageLoadStoreSso、UnboundImageDescriptor、SwizzleAccessRoutine、IntegerBorderColor、PixelStoreSweep、SampledSetStaleness、ThreeChannelAttachment、BufferTexture、CopyImage*×3、ClearTexImageUndefinedLevelZero、DepthStencilReadback×3、PackedWordReadback);21 个 program 场景 + 整个 MG_Test/ShaderTranspiler 目录;两台设备上完整 KHR-GL46.texture_* / internalformat.texture2d.* / shader_image_* / packed_pixels 块,conformance 在 pull 基线 0.5pp 内;每一个 Iris trace;Mali 上 box-vs-rects 上传形状不得回归(+6ms/frame 悬崖);verify 模式全程零分歧。
P7 — DirectVulkan(Magma)全量迁移(48-85 天,可与 P5/P6/P8 并行)
交付物:§6.5 的 11 个子系统,重点三项:SetupDrawSnapshot 的 ~14 个探测字段(含两个有损的版本求和)塌成 dirty mask 比较;UniformManager 的 8 类占位 TextureObject 换成原生 VkImage+view+descriptor(~120 行删除,43 个 MOBILEGL_ASSERT(pGLContext) 里的 9 个消失);blit / depth-mipmap 内部 shader 烘焙成签进树的 SPIR-V + uniform location + UBO 布局,由一个 MG_Test 重跑树内 glslang 逐字节比对的用例守新鲜度(不用 host 工具构建步骤);VertexInputStateFactory 的后端堆裸指针写回直接删除;VkRenderPassManager / VkTextureManager 的节点式容器纪律原样保留(D18,postmortem 注释逐字带进 review checklist)。
验收:367 集成 + 40 trace 在 DirectVulkan 的 monolith-push 与 split 下全绿;verify 零分歧;nm -D libMobileGLServer.so | grep glslang 为空——这是整个论点的强制执行点;UnboundImageDescriptor、SampleMaskScope、ImageLoadStoreSso、AtomicCounter、SsboArrayDynamicIndex、NonCoreImageFormat、Orientation、DepthStencilReadback* 场景;两台设备 CTS 在 0.5pp 内。
⚠ 再基线检查点 2:P7 中点(第 24 个工作日)若已完成子系统 < 40%,立即重定基线——P3a 的检查点只能发现 Espryt 侧的偏差,发现不了 Magma 特有的超期,而 P7 在单跑道下位于关键路径。
P8 — emulation 下放 + 协议广度(14 天)
交付物:MG_Impl/Pipe/HostResolve.cpp——client 数组范围计算、最大索引扫描(TryComputeMaxIndexFromHostBytes 移到 client,唯一的无界应用指针读)、primitive-restart 重写、indirect-count 解析,每一条前面都有与 monolith 的 SyncPersistentMappedRange()+SyncGpuWrites() 完全相同位置的 publish/wait/drain(§5.8.1);MGHostSpan 的 split 填法;CopyImage shadow 镜像搬到 client(删掉一整条 server→client 字节通道);draw_vbo(info, indirect, ranges[], numDraws) 收编 multi-draw 族;viewport-array 回放验证在一次 pipe 调用驱动下各遍之间观察到的状态与今天一致(EndViewportRoutingPasses 会调 InvalidateSyncedRenderState,DirectGLES.cpp:3841);generate_mipmap 返回 level 计划与 CPU 回退的纹素。
验收:ctest -L integration-gpu -R '^DirectGLES\.Split\.' 与 '^DirectGLES\.' 逐名相同,DirectVulkan 同;40 个 trace 在 split 下双后端 SSIM ≥ 0.99,含两个 coherent_as_flush: true 的 Create fixture(两种模式都开着该开关跑);新增 ClientArrayAfterComputeWriteScenario 绿,且去掉那次等待必须能看到几何缺失;MultiDraw、PrimitiveRestart、ViewportArray、DrawParameters、CopyImage*×3、GuiBatch 场景。
P9 — 反向通道(10 天)
交付物:SEG_REPLY 4KiB slot 池;阻塞 read_pixels;PBO 回读 fire-and-forget;on_gpu_written{res, ranges} 收窄(配 set_shader_buffers 的 writableMask);on_buffer_writeback 按操作级批处理(今天两处逐行循环:Utils.cpp:2342、DirectGLES.cpp:7633)配 epoch bump 的排序规则(§7.4);on_texture_writeback(一个生产者);on_mip_levels_generated;on_texture_pull_request 三条缓解全上(§7.5);on_gl_error 有序 + 分配类 kNeedsAck;on_caps_invalidated;on_surface_changed;on_log 按严重级分级(≤WARN 有损 / ≥ERROR 无损 + 每秒速率限制器 + "N errors suppressed");SEG_EVENT 溢出策略 + 等待循环内排空。
验收:DepthStencilReadback×3、PackedWordReadback、LayeredTextureReadback、ClearThenReadPixels、XfbAfterClipDistance、XfbCaptureBufferReuse、XfbRepeatedCapture、TessellationXfbCapture 在 split 下绿;新增 TextureRemintPullScenario(同时强制 RequireImageBindableStorage 与帧中格式再生)绿,且逐 trace 用例发布拉取计数;故障注入:client 被 credit 阻塞时灌满 SEG_EVENT,两侧都必须恢复;日志洪泛下注入一次 backend link 失败,那行 ERROR 必须出现。
P10 — sync / query / present 节奏(6 天)
交付物:client 铸造 sync 与 query handle;轮询入口成为门铃点 + MOBILEGL_IPC_POLL_ESCALATE 饥饿升级;fence 完成度来自真的逐 fence 退休(不是 present 水位——那正是 MC 1.21.5 native-heap OOM 的成因);DirectGLES 的非 present fence tick;Present 严格 1:1;MOBILEGL_IPC_PRESENT_CREDIT 默认 1 + 叠加公式写进文档;逐帧 roundtrip 计数器与输入延迟直方图(记录发射时刻 → present 完成时刻);PLAN.md §8 末尾的三个独立 dev monolith 修复。
验收:XfbPrimitiveQuery、PrimitivesGeneratedNoXfb、AsyncCompile 在 split 下绿;40 个用例上 draw/state/upload 路径的 roundtrip 计数器读零,条件渲染与阻塞 query 次数逐用例发布而非声称为零;零 timeout 轮询循环测试在有界时间退出(不加 publish 规则它会永远挂住);bench.sh 在 35d0befa 上配对 A/B:两侧都关采纳时 split 帧时在 monolith 10% 内,输入延迟直方图 p50/p99 记录在案。
P11 — persistent map 与 ≥16MiB 采纳(8 天;spike B 全否则缩为 2 天)
交付物:由 P0 spike B 驱动的 POST 探针档位选择(T2 拒绝 / T1 server 导出 / T0 server 导入);SEG_ADOPT 生命周期绑 completedFrameSerial;MOBILEGL_IPC_ADOPT_TIER 覆盖开关做负面对照。
验收:LargeArenaAdoptionScenario 在所选档位下绿;improved-transparency-minecraft-26.3 与两个 Create fixture SSIM ≥ 0.99;35d0befa 上配对 reboot-clean 的 p99 帧时与峰值 RSS 对 monolith 采纳基线(p99 163→21ms、40→115fps、~400MB)——split 在所选档位下 p99 不得回归超过 10%;若 T2 成为永久答案,其实测代价必须写进文档。
P12 — Android 生产窗口路径(10 天)
交付物:android:process=":mgl" 的 Service 收 Java Surface(Binder)后 ANativeWindow_fromSurface(minSdk 26 无公开 ANativeWindow 扁平化,这条路径是强制的;树内先例是 android:process=":bench" 的 BenchService);server 生命周期绑 Activity;FCL 用户 env 与 plugin APK V2 开关表接线(零新增管线,两条通道都已存在)。
验收:Minecraft 通过 FCL 在 spawn 模式下在 35d0befa 上双后端入世界;配对 reboot-clean bench + 输入延迟直方图;杀 server 产生干净的 device-lost latch 而不是挂起或崩溃;SIGKILL 故障注入。
P13 — 退役 pull 路径(6 天)
交付物:删 SnapshotFromGLContext()、MGB_CTX 宏、MOBILEGL_PIPE_PUSH(推送成为唯一路径),保留 MOBILEGL_PIPE_VERIFY 工装;删 set_residual_value_state 与 ResidualValueBlock;MG_Backend 的 MG_State include 收缩到共享值头白名单;最终符号/尺寸/CPU 报告。
验收:static_assert(sizeof(ResidualValueBlock) == 0) 编译通过(临时物可证明地消失);grep -c 'pGLContext' MG_Backend/ == 0;nm --undefined-only libMobileGLServer.so 无 MG_State::GLState::、无 glslang 符号;全套门(367 × 2 backend × {monolith, split}、428 单元、40 trace SSIM ≥ 0.99、两台设备 CTS 在 81b17c0b 基线 0.5pp 内);monolith 逐线程 CPU 在两台设备的 p50 与 p99 上不差于 P0 基线——本设计的性能主张在这里成立或倒下。
11.5 总估时、里程碑与 CTS 周转
累计(单跑道):P0 8 → 8;P1 8 → 16;P2 8 → 24;P3a 12 → 36;P4a 16 → 52;P5 12 → 64;P6 5 → 69;P3b/P4b 28 → 97;P8 14 → 111;P9 10 → 121;P10 6 → 127;P11 8 → 135;P12 10 → 145;P13 6 → 151;P7(Magma)48-85 → 199-236。
报作 200-260 人天(上界含缓冲)。两个工程师、P7 与 P5/P6/P8 并行 → 约 6-7 个月,真正的约束是两台设备的争用而不是人头。
里程碑:第 16 天 verify harness 全绿(零产品风险);第 24 天 GO/NO-GO;第 64 天首个 inproc IPC 帧(缩减路径);第 69 天首个跨进程帧(缩减路径);第 111 天全功能 split(P8 出口);第 151/199-236 天接口纯度门转绿。
CTS 周转必须单独计价,不折进阶段估时。 gl44to46 caselist 约 56,271 例。分层门控:逐阶段只跑该阶段改动可能影响的具名 CTS 块(P4a 的 packed_pixels、P3b/P4b 的 texture_*/shader_image_*、P9 的 transform_feedback*),完整 caselist 只在四个架构边界跑(P3a handle、P4a framebuffer、P3b/P4b 纹理、P13 纯度)以及每次合并 dev 之前,且放在 CI 而不是关键路径上。设备锁协议照旧。若实测周转仍主导排期,诚实做法是加宽估时而不是削弱门。
12. 风险与对策
| # | 风险 | 对策 |
|---|---|---|
| B-R1 | 效率是方案 A 的 3 倍、首帧晚 4-5 倍(200-260 天 vs 77;第 69 天 vs 第 15 天)。排期驱动的评审可以只凭这一条否掉本方案 | 把价值排在承诺之前:P0-P2(~24 天,其中 16 天是方案 B 独有)交付 handle 化 twin 与内容寻址的渲染状态 CSO——零 IPC 风险的可测量 monolith 性能工作——并产出字节计数器与第一个逐线程 CPU 数字。第 24 天显式 GO/NO-GO。 P13 是一个完全自洽、不含任何 IPC 的 monolith 交付物;P5 的 inproc 在 P13 之后是本项目手上最大的单一 CPU 杠杆,只要 12 天 |
| B-R2 | 中心性能主张未经测量。 可达性遍历是搬走而不是消失;若 client 侧 dirty 走查比它替换掉的拉取更贵,整个论证反转。而树里今天没有任何 per-frame 字节或调用度量 | 字节/调用计数器是 P0 交付物,不是后续性能阶段的事(这是历次评审对四个候选设计的共同发现)。每阶段的验收都用逐线程 CPU 时间,两台设备、reboot-clean、配对。P2 刻意选最便宜的面,好让机制在花掉 200 天之前就被证明。先清工作树 per-draw fprintf——它污染每一个基线 |
| B-R3 | monolith 字节一致门按构造死亡,P2 的逐名集成基线也随之移动(参考构建变了) | 五部分替代门,全部在 P0/P1 落地(§10.3),其中第 ② 部分(逐 draw 逐字段影子比对)在语义上严格强于任何符号 diff。两条字节等式仍作断言保留。在设计文档里明写这条损失,不藏。 P2 的参考构建明确定义为"重构后的 monolith",并要求 P1 出口先证明重构本身是等价的 |
| B-R4 | server 发起的纹理拉取是新停顿类,且触发路径之一(整格式再生 Managers.cpp:3950-4195)在普通 glTexImage 格式变更上就会触发、无法被 hint 预防 |
三条缓解同时上:imageBindableHint 预防主因;异步park-and-re-emit 让停顿落在 mgl-srv-apply 而不是应用线程;有上限的 ≤32MiB 保留 LRU 让一张纹理不会停顿两次。TextureRemintPullScenario 强制两个成因,拉取计数逐 trace 用例发布而不是声称为零。若实测率非平凡,保留缓存从可选升为强制并拿真预算 |
| B-R5 | P3b/P4b(纹理,28 天)与 P7 中的 VkTextureManager 是最大最险的段,且正压在项目已实测 +6ms/frame 悬崖(rect 列表 vs union box)与 7 条 fallback-repack 路径上(其可行性判定要求 uploadData == mipData) |
resource_subdata 同时带 box 与 rect 列表、server 选形状——决策留在做过那次测量的那一侧。repack 族原地不动,只把输入从拉取的 shadow 指针换成 MGPBlobRef(monolith 里是同一个指针)。在 Mali 设备上专门设门并公布帧时增量。P3b/P4b 拆成两个可独立落地的半(先 sampler view + sampler,再 image unit + dirty 反转),让回归能二分到其中一半 |
| B-R6 | tracker 完整性:推送之后 server 不能再重读活状态来校验快路径命中,它必须信任上一次推送。任何 tracker 忘记发的 mutator 会静默漂移,直到某个场景恰好触及。历史上最危险的正是这个形状——DSA by-name 模拟曾在不移动任何计数器的情况下交换一个纹理单元绑定(DirectGLES.cpp:1441-1465) |
三层:(1) 构建期 G5 的 written-once 位图断言每个 kCtxState 组在第一个 verb 消费它之前被写过;(2) 运行期 poison mask 在读未填字段时 Fatal{UnmigratedPipeInput};(3) 语义 MOBILEGL_PIPE_VERIFY 逐 draw 逐字段比对,专抓"dirty 位触发得太少"这个危险方向。删掉 SnapshotFromGLContext()(P13)之后完整性成为构建期事实。迁移粒度是一个 accessor,所以一次遗漏被限制在一个阶段 B 步骤内。477 行 inventory 保留为 tracker 侧检查表 |
| B-R7 | AcquirePersistentMap 跨进程无解会葬送 MC 26.3 的结果(p99 163→21ms、40→115fps、省 400MB),而没有任何目标平台的支持被验证过 |
显式隔离:改造期完全不碰(它已经是一个返回指针的显式调用,原样穿过 P0-P13),只有 IPC 那一步会打破它。决策交给 PLAN.md §6.8 的三档 POST 探针与 P0 第一周的 spike B。T2(拒绝)前端已在三处容忍并让 client 侧块推送成为强制(P5 就交付)。若两台设备都否,P11 从 8 天缩为 2 天的文档与负面对照。不让一个平台未知数挡住 200 天的接口工作 |
| B-R8 | D18 的节点式容器纪律在重构中丢失:m_renderbufferResources / m_textureResources 是故意用 std::unordered_map 而非本项目开放寻址的 UnorderedMap,因为调用方跨后续查表缓存 Resource*;一次扩表搬迁曾让 BlitFramebuffer 静默停在 "source image layout is undefined"(postmortem VkRenderPassManager.h:375-397)。把它"优化"回去正是一次大重构最容易犯的错 |
D18 是全表唯一标为 UNCHANGED 的身份行,理由写进设计正文:接口对此零约束,ska 的 erase-shift 让 hazard 更糟而不是成为历史,postmortem 注释必须逐字带进 P7 的 review checklist。slot 数组在插入下稳定,实际改善了处境——但仍然点名,防止有人顺手改回去 |
| B-R9 | 逐 backend 的行为不对称被统一接口抹平。 Magma 故意不注册 ResidentSubData(VkBufferManager.cpp:104-111),差别是 glBufferSubData 在活 coherent map 上的排序语义;PrefersCpuXfbPrimitiveAccounting、DirectVulkan 留空的 8 个槽、不同的 multi-draw 分档同形。强制统一会静默改变 Magma 行为并发布一次 Minecraft chunk 撕裂 |
可选性是接口的一等属性:null 项在本代码库里已经表示"未实现,前端回退"(BackendObject.h:212-215, 265-269),MGPCaps 携带显式的 callMask 能力位集,取代今天 GL_Query.cpp:471, 545, 768 的隐式槽位空否探测。cap 位集同时决定每个 emulation 在哪一侧 lowering,所以不对称成了机制而不是疣 |
| B-R10 | 接口在未测量的形状上过早冻结:渲染状态 CSO 粒度、resource_subdata 的 box+rect 双载荷、sampler-view 解析边界、screen/context 划分全在 P0-P2 定死;而若干 server 侧缓存的容量是按拉取模式调的(Magma 的 2048 槽 VaoDrawMemo、4 个 SetupDrawSnapshot、8 个 pipeline memo、8 个 syncedTextureMemo;Espryt 的 4096/256/64 槽 TwinLookupMemo) |
payload 结构从第一天走 structSize-first 版本纪律(复用 Feat/CS-Delta-IPC 的 mg_protocol_base.h 词汇),可增长。字节计数器在 P0 落地,让 P2 的 CSO 粒度决策有数据。P13 在计数器活着的情况下重调所有幸存缓存的容量,并把它们当作带 env 覆盖的调优参数而不是常量。screen/context 划分在 P0 定进头文件但按 context 计数 == 1 实现——现在免费,事后是一次全面重编号 |
| B-R11 | 58 行非箭头 pGLContext 用法的迁移缺口:三份候选设计都提了"机械 sed 293 处",都没交代这 58 行;其中 DirectGLES.cpp:146 的 .get() 裸指针捕获 sed 完全抓不到 |
§2.4 已把它们逐形态分类。P1 的交付物包含这份 58 行清单的逐条转换,不是"顺手处理"。纯度门 grep 的是 pGLContext 而不是 pGLContext->,所以漏一条就红 |
| B-R12 | 残余值块是迁移期边界上的一个洞:poison mask 抓得到"未填字段",抓不到"两侧布局不同"——而这正是一个异质 POD 并集跨编译器/ABI 边界最容易出的问题,且 monolith 的 verify harness 看不见它(monolith 里两侧是同一个 TU) | 逐成员 offsetof 断言(不只是 sizeof)加上 split 模式下逐字段序列化(走 G3 生成的编解码器)而不是整块 memcpy。块的字节量单独计一类,让"它还有多大"可见。static_assert(sizeof == 0) 让退役是编译错误 |
| B-R13 | SEG_EVENT 的 ERROR 无损化重新引入 §7.4 要防的死锁:一个 shader 风暴产生的 MGLOG_E 洪泛可能停住 apply |
每秒 ERROR 速率限制器,超限发一条显式的 "N errors suppressed";MGLOG_E_ONCE 的 latch 变 per-server(这正是想要的);P9 的故障注入门要求"日志洪泛下注入一次 link 失败,那行 ERROR 必须出现"且"两侧都恢复" |
| B-R14 | 排期估计偏乐观:独立的改造成本分析给出 backend 工作单独 202-266 天;本文的 200-260(含 IPC)落在其乐观端,而 P7 的 48 天下界明显低于同口径的 85-111 | 两个再基线检查点(P3a 超期 >50%、P7 中点完成度 <40%),且 P7 的上界 85 已经写进总估时区间。CTS 周转单独计价不折进阶段。若两个检查点任一触发,先跑 inproc 的证伪数字再决定是否继续——这与 PLAN.md R15 的退火思路一致 |
| B-R15 | 在 GL setter 时刻推送(而不是 draw validate 时刻)会让整件事变慢,且这是最容易被后续实现者做错的一处 | 写成规范条款并给出理由与证据(DirectGLES.cpp:2044-2046 的 Blaze3D per-batch blend toggle);P2 的设备门直接暴露它(monolith-push 必须在 p50 与 p99 逐线程 CPU 上落在噪声内);两个高水位标记(TouchBindPoint、NoteUnitTouched)明确要求留在 tracker 走查里 |
13. 开放问题
- client 侧 dirty 走查的真实每 draw CPU 代价是多少? 中心性能主张是"遍历搬走而不是翻倍",树里没有任何度量。这是 P2 的头号数字,按逐线程 CPU、两台设备报。
- 真实语料上纹理重铸拉取的实际发生率?
imageBindableHint能预防主因,但整格式再生(Managers.cpp:3950-4195)在普通glTexImage格式变更上就触发。若 MC 或 Iris fixture 上的实测率非平凡,保留 LRU 从可选升为强制并需要真预算。 AcquirePersistentMap跨进程能不能成? P0 spike B 第一周回答。未验证:VK_KHR_external_memory_fd的 host-visible-coherent 支持在 Adreno 830 / Oppo Mali / lavapipe / Windows ANGLE lane 上的可用性;GLES 侧能否用GL_EXT_memory_object_fd+glBufferStorageMemEXT走同一条路(Espryt 今天的采纳走的是glBufferStorageEXT+glMapBufferRange(PERSISTENT|COHERENT),不是外部内存)。- 渲染状态的 wire 粒度定多少? ~1.2KB 整块 vs 三个现有段 vs 逐 dirty 位子集。P0 落计数器、P2 用数据定。CSO LRU 的容量(暂定 64)同理。
MG_Util的切割缝在哪里? server 需要 SPIRV-Cross pass 流水线、ESSL 转译缓存、像素/纹理格式处理器、POST 探针、loader;client 需要 glslang phase A/B 与反射层。ProgramObject.h传递性 includeShaderObject.h → ShaderCompileTask.h,所以任何链接真ProgramObject的 client 都会拉进编译机制。没有审计过是否存在一条干净的 Transpile-vs-Reflect 缝。- 一份反射归档能服务三个消费者吗? Espryt 读前端表,Magma 跑 SPIRV-Reflect,而
DirectVulkan.cpp:161为glGetProgramResource*又反射了第二遍。没有确认一份 payload 能同时满足三者。 - viewport-array 回放能塞进一次
draw_vbo吗? 今天它从 14 个 draw 入口经ForEachViewportRoutingPass重发应用的 draw N 次,而EndViewportRoutingPasses会调InvalidateSyncedRenderState(DirectGLES.cpp:3841)。没有验证单次 pipe 调用驱动下各遍之间观察到的状态是否与今天一致。 ResidentSubData的不对称该怎么收口? null 项保住今天的行为,但拆分工作可能正是给 Magma 补一个真实现的时机——那是行为变更而不是重构,应当作为独立的devPR。SEG_STAGE的上限定多少? 现在多了四类字节(client 顶点数组、client 索引数组、multi-draw 参数块、client 解析的 indirect 命令块)。体量不变但都走 ring slot。需要 P8 之后用 MC in-world 与 Create 两类 fixture 的stage-*计数器给 p99 占用。FramebufferSrgb/DepthClamp无存储是潜伏 bug 还是有意为之? 六个 backend 消费者今天读到恒定 false(RenderState.cpp:380, 428-429)。必须在渲染状态 blob 冻结之前回答,因为它决定接口里要不要这个字段。- P13 之后
MOBILEGL_IPC_VALIDATE_SERVER还有对应物吗? 方案 A 靠"server 侧保留MG_Impl校验器"把分歧变成 server GL error。方案 B 的 server 没有MG_Impl,所以 split-only 的渲染 bug 没有第二意见。什么替代那个诊断能力? - client 侧 restart 重写与 indirect-count 解析会不会改变可观察行为? 两者今天都以
m_valid=false加诊断的方式退出(DirectGLES.cpp:4405-4409、:4685-4688);把 decline 移到 client 改变了哪个线程打这条日志,也可能改变它相对 GL error 的顺序。 - Magma 的两个内部 shader 烘焙后,uniform location 与 UBO 布局能否在没有活
ProgramObject的情况下表达?(VulkanRenderer.cpp:4238-4241, 4319-4324, 8450-8452)未做原型。 - 推送模型会改变哪些按拉取模式调过的缓存命中率? Magma 的 2048 槽
VaoDrawMemo、4 个SetupDrawSnapshot、8 个 pipeline memo、8 个syncedTextureMemo;Espryt 的 4096/256/64 槽TwinLookupMemo(后者会消失)。幸存者的容量要在 P13 重调。
14. 对方案 A 文档与 Feat/CS-Delta-IPC 的复用清单
14.1 对 PLAN.md 的复用(本文的 §8.1 是权威列表,这里给的是"取/改/弃"总账)
| 判定 | PLAN.md 章节 |
|---|---|
原样取(不复述,以 PLAN.md 为准) |
§6.1(段布局、shm 矩阵、SCM_RIGHTS 第一优先、SEG_SHADOW 退休规则);§6.2/§6.2a(RingControl、双向 doorbell);§6.3(记录格式与运行期边界检查);§6.4(WAR 纪律与 P4.5 零拷贝);§6.5(ring 分配与背压);§6.6 前三条;§6.7 第 2、5 行;§6.8(三档采纳与 POST 探针);§7.1-§7.3;§8 末尾(真 fence 退休 + 三个独立 dev 修复);§9-§9.3;§10(线程模型、核心放置、拆机顺序);§11.1-§11.6;§12 第 1-3 层与 §12.4;§13(目录、一库两角色、submodule guard、三个 ctest 陷阱、SPLIT 接线、CI 门);§15 P0 的卫生与两个 spike |
| 取并改 | §7.4(事件通道:on_log 按严重级分级,§7.4);§12.2(隔离从四个进程全局降到两个);§5.10(第 2、3 条逐字取,第 1 条缩成一个 hasLiveHostWrites 位);§6.10(四类应用指针改由 cap 门控 lowering;陈旧索引纪律逐字保留并加门);§5.9a(READ 面编目生成器改为禁止门);§6.4 的拷贝账(删掉第 (3) 行,P1-4=3 / P4.5=2,PLAN.md 的"方案 B"目标结构性达成) |
| 弃 | §5.0、§5.1、§5.2、§5.4 的 replica 对象表规则与 Fatal{IdentityDivergence}、§5.6a、§5.7 的 Phase 1-4 分支与 SetReplicaResolvedDrawProgram 钩子、§5.9b 与全部 mutation 覆盖机制(gen_impl_mutation_surface.py、MutationCoverage.def、ImplMutationSurface.inc、MG_Remote::Shared::)、§6.9 的 relink 档与 MOBILEGL_IPC_PROGRAM、§12 第 4 层的字节一致断言、Server/ReplicaContext.*、阶段 P5、风险 R1 与 R6、开放问题 §17-5 |
新增(PLAN.md 没有的) |
MG_Pipe/ 全套与六个生成器;PipeInputs + poison mask;MOBILEGL_PIPE_VERIFY 影子比对;残余值块与它的编译错误退役绊线;on_texture_pull_request / on_texture_writeback / on_mip_levels_generated 三个事件;纹理拉取的三条缓解与计数器;HandleRecycleScenario / TextureRemintPullScenario 两个场景;RenderbufferObject::GetLifetimeId()(不含 GetVersion());D21 的潜伏 bug 修复 |
14.2 对 Feat/CS-Delta-IPC(worktree ../MobileGL-CS)的复用
PLAN.md §14 的 REUSE / CHANGE / DROP 判定整体继承。方案 B 的四处差异:
| 条目 | PLAN.md 判定 |
方案 B 的差异 |
|---|---|---|
docs/CS_Refactor/HandleSessionGeneration.md(546895aa) |
REUSE,三处修改,其中"handle 清单补 RenderbufferObject::GetLifetimeId() 与 GetVersion()" |
只补 GetLifetimeId()。GetVersion() 只是 replica 的 delta 触发器;推送模型里 glRenderbufferStorage* 本身就是一次 pipe 调用 |
docs/CS_Refactor/backend_read_inventory.md + extract_backend_read_inventory.py(546895aa) |
CHANGE 成 gen_backend_state_surface.py,未知 accessor 一律 UNMAPPED 并编译失败 |
同意其修正(删掉制造"0 UNMAPPED"的 GetBuffer*/GetTexture*/GetProgram*/GetVertex* 前缀兜底规则 :234-241,把真 pull point 与 signature handle 化分开统计),但用途改变:它变成 tracker 侧的覆盖检查表(G6),而真正的门是 §4.7.2 的纯度门——禁止那次读,比编目它强得多,且不会过期。另外:gen_impl_mutation_surface.py 在方案 B 里不存在(没有 replica 就没有 mutation 要 replay) |
MobileGL/RemoteClient/StateEmitter.h:39-307(仅 emit 半边,b50f3348+d96be9f3) |
CHANGE,各域字段遍历抬进 WireMirror |
更直接可用:在 MGPipe 下那些字段集就是 pipe 的状态对象 payload(MGPFramebufferState、MGPVertexElements、MGPSamplerView …),不再是一份 delta 目录。同样必须修的缺陷不变:GL name 换 lifetimeId(:48-49, 85, 166-168, 203, 230)、O(n²) 线性扫描换 handle map(:175-181, 244-249, 253-258, 293-298)、固定 6 attachment(:232-236)换 MaxColorAttachments、补上被跳过的 texture view(:70-74)。applier 半边(:312-501)仍然不取 |
MobileGL/Protocol/mg_protocol_base.h(546895aa) |
REUSE(干净无依赖的词汇) | 同意,且 structSize-first 版本纪律是 B-R10 的对策:payload 结构体从第一天就可增长 |
DROP 名单完全一致:bfa.h(480 行,"strict C ABI" 名不副实,且手抄的 60 字段 MobileGLDynamicParameters 是本项目已造成 481 例 CTS 失败簇的那类静默漂移炸弹)、mgruntime_api.h + UtilRuntime/*、LocalSocketTransport 的实现(每次 send 的 UAF、无上限分配、fd=-1 硬编码)、ServerHost/main.cpp(编译不过)、StateEquivalenceTest.cpp、c7c9e346+29d721ef 的 share-group sessioning、b50f3348 的 RenderState::InstallParameters + 裸 public:、d96be9f3 的 per-draw fprintf TRIAGE 指令。
附 A:接口调用目录速查表
完整 payload 定义见
MG_Pipe/MGPipeTypes.h;本表按PipeCalls.def的顺序。Flags:A=kNeedsAck、B=kHasBlob、V=kVarTail、H=kHostSpan、R=kReplySlot、O=kOptional(可为 null)。
MGPipeScreen(14)
| 调用 | payload | flags | 取代 |
|---|---|---|---|
get_caps |
MGPCaps |
R | 40 pActiveBackendObject-> + 89 caps 读点 |
resource_create |
MGPResourceDesc |
— | buffer/texture/renderbuffer 创建 |
resource_respecify |
MGPResourceDesc |
— | BufferBackendOps::Respecify 泛化 |
resource_destroy |
handle | — | OnDestroy + 两个 WeakPtr GC 扫描 |
map_persistent / unmap_persistent |
handle | R, O | AcquirePersistentMap(改造期不碰) |
fence_create / fence_status / fence_wait / fence_destroy |
handle (+timeout) | — / — / R / — | FenceSync…GetSyncStatus(两值契约保留) |
query_create / _begin / _end / _available / _result / _destroy |
handle + kind | — | BackendObject.h:230-256 |
MGPipeContext — CSO(15)
create/bind/delete × render_state / vertex_elements / sampler / sampler_view / shader。
create_render_state 带 B(整块或变化段);create_shader_state 带 B(SPIR-V + 反射归档)。
MGPipeContext — set_*(14 + 1 临时)
set_framebuffer_state · set_vertex_buffers · set_index_buffer · set_indirect_buffers · set_sampler_views(V) · bind_sampler_states(V) · set_shader_images(V) · set_shader_buffers(V) · set_stream_output_targets(V) · set_global_constants(B) · set_vertex_attrib_defaults · set_pixel_pack_state · set_patch_state · set_draw_program / set_dispatch_program
临时(P2..P13):set_residual_value_state(B),带 static_assert(sizeof(ResidualValueBlock)==0) 退役绊线。
MGPipeContext — transfer(12)
resource_subdata(B,V) · buffer_subdata_resident(B,O) · resource_flush_range · resource_readback(R) · resource_copy_region · blit · clear · generate_mipmap · read_pixels(R) · get_texture_image(R)
MGPipeContext — 命令(10)
draw_vbo(H,V) · launch_grid · memory_barrier · begin/end/pause/resume_stream_output · flush · present · set_swap_interval(O)
反向:MGPipeCallbacks(9)
on_gl_error · on_gpu_written · on_buffer_writeback · on_texture_writeback · on_texture_pull_request · on_mip_levels_generated · on_surface_changed · on_caps_invalidated · on_log(≤WARN 有损 / ≥ERROR 无损 + 速率限制)
显式删除
GetIntegeri_v · GetInteger64i_v · GetProgramiv · ShaderStorageBlockBinding(折进 MGPProgramDesc)· set_pixel_unpack_state(不存在)· 压缩格式概念(不存在)· pipe_transfer(不存在)
附 B:环境变量与 CMake 选项
CMake
| 选项 | 默认 | 说明 |
|---|---|---|
MOBILEGL_BUILD_DISAGGREGATED |
OFF | 出货形态。开启后 MG_Remote/** 进 SOURCE_FILES,支持 spawn/unix:/pipe:。两个进程全局保持普通全局,GL 热路径无 TLS |
MOBILEGL_BUILD_DISAGGREGATED_INPROC |
OFF | CI/调试形态,隐含开启上者,额外加角色隔离 shim(方案 B 下只需隔离 gPipeCtx 与 pActiveBackendObject) |
MOBILEGL_FLATC_EXECUTABLE |
空 | 只服务 CI 的 flatc-check;默认构建图里没有 flatc |
MOBILEGL_BAKED_INTERNAL_SHADERS |
ON(P7+) | DirectVulkan 的 blit/depth-mipmap shader 烘焙成签进树的 SPIR-V,由 MG_Test 重跑树内 glslang 逐字节比对守新鲜度。monolith 也受益(少一次启动期 glslang 编译) |
注:
MG_Pipe/**不在任何 option 之后——它是 monolith 的架构,永远进构建。
运行时(方案 B 新增)
| 变量 | 默认 | 说明 |
|---|---|---|
MOBILEGL_PIPE_PUSH |
迁移期按阶段推进;P13 后删除 | 子系统位图(0 = 全 pull)。任何提交都能在同一份二进制上按子系统 A/B |
MOBILEGL_PIPE_VERIFY |
0 | 逐 draw 逐字段影子比对(~5-10× 慢,永不出货,P13 后保留为工装) |
MOBILEGL_PIPE_STATS |
0 | 字节/调用/roundtrip/纹理拉取/残余块计数器转储 |
MOBILEGL_PIPE_TEXEL_RETAIN_MB |
32 | 纹理重铸拉取的保留 LRU 预算上限(§7.5c) |
运行时(继承 PLAN.md 附录)
MOBILEGL_TRANSPORT(monolith 默认 / inproc / spawn / unix:<path> / pipe:<name>)· MOBILEGL_IPC_SERVER_PATH · MOBILEGL_IPC_RING_MB(8) · MOBILEGL_IPC_STAGE_MB(32,上限由实测定) · MOBILEGL_IPC_PRESENT_CREDIT(1) · MOBILEGL_IPC_SPIN_US(50) · MOBILEGL_IPC_POLL_ESCALATE(64) · MOBILEGL_IPC_PERSISTENT_BLOCK_KB(64) · MOBILEGL_IPC_ADOPT_TIER(auto) · MOBILEGL_IPC_SHADOW_SHM(1,P4.5+) · MOBILEGL_IPC_INLINE_PAYLOADS(0,负面对照) · MOBILEGL_IPC_SERVER_AFFINITY(auto) · MOBILEGL_IPC_STRICT_ERRORS(0,诊断) · MOBILEGL_IPC_AUDIT(0) · MOBILEGL_IPC_TRACE(0) · MOBILEGL_IPC_ATTACH(空) · MOBILEGL_IPC_RESPAWN(0) · MOBILEGL_IPC_IDLE_EXIT_S(30)
删除:MOBILEGL_IPC_PROGRAM(没有 relink 档)· MOBILEGL_IPC_VALIDATE_SERVER(server 没有 MG_Impl 校验器——替代诊断手段见开放问题 11)
保留的既有负面对照开关:MOBILEGL_ESPRYT_DISABLE_UBO_RING · _UNPACK_RING · _UPLOAD_RING · _INVALIDATE_FLUSH · MOBILEGL_DISABLE_LARGE_BUFFER_ADOPTION · MOBILEGL_COHERENT_AS_FLUSH(在拆分模式下照常生效,这样两个 coherent_as_flush: true 的 Create fixture 在 split 与 monolith 下走同一条 buffer 路径,逐名对比才有意义)