diff --git a/docs/Disaggregated/notes/p13/PLAN-P13.md b/docs/Disaggregated/notes/p13/PLAN-P13.md new file mode 100644 index 00000000..d8cc2b3e --- /dev/null +++ b/docs/Disaggregated/notes/p13/PLAN-P13.md @@ -0,0 +1,223 @@ +# P13 计划:退役 pull 路径,monolith 换到记录臂 + +> 2026-10-06,只读核查(树 `p13` = `origin/feat/disaggregated` `aee0632a`)。范围与出口门见 [`README.md`](README.md);P8 留下的缺陷见 [`../DEBTS.md`](../DEBTS.md) ID-P8-13 / ID-P8-14 两行与 [`B`](../p8/B.md)、[`C`](../p8/C.md)、[`D`](../p8/D.md)、[`E`](../p8/E.md)、[`SV`](../p8/SV.md)。本页只写计划,代码一行未动;实施等集成方点头。行号是本树当日的,实施时以符号为准。 + +## 0. 结论 + +| # | 结论 | 依据 | +|---|---|---| +| 1 | **monolith 的「pull」已经不在编译期,而在运行期。** feat 上 CI 的主构建本来就是 push + split(`MOBILEGL_CI_DISAGGREGATED=ON`),pull 只剩 `build-linux-monolith-control` 一个库给 retrace-split 当阴性对照;`SnapshotFromGLContext` 早已只在 verify 下编译。真正要做的是:后端约 260 处 `Transport != Monolith` 分支里,monolith 走的都是读前端对象 / 前端影子的臂 | Espryt 113 处、Magma 63 处运行期分支;push 下 `MGB_CTX` = `&gPipeInputs`,但 `kFatal` 类字段(`GetTextureObject`、`GetBoundVertexArray`、`GetProgramForDraw`……)装的仍是前端 `SharedPtr`,后端照样解引用 | +| 2 | **记录臂只在 `MOBILEGL_BUILD_DISAGGREGATED` 下编译。** Espryt 113 处运行期分支有 108 处在该宏内;记录臂读 CPU 字节靠 `MG_Remote::Server::StagedShadowStore` / `StagedTextureStore`;Magma 整个 wire 臂(14 个 `Wire*` 文件、`VulkanRenderer.h:634-918` 的 `m_wire*`、按句柄的纹理臂 `VkTextureManager.cpp:1869-3265`)整体在该宏内。不带 MG_Remote 的 push 库(FCL 内嵌形态)今天**不可能**走记录臂 | `Config.h:749`(无该宏时 `Transport` 是 `constexpr Monolith`);`WireDraw.inc:2` | +| 3 | **ID-P8-13 九条、ID-P8-14 两条缺陷全部在记录臂上已修好**,monolith 换臂即消失;回归用例已经存在,是 monolith 臂上按名跳过的那批(§3),取消跳过就是证明 | P8 B / C / D / E / SV 各包的 red-once「M」行 | +| 4 | **monolith 不能整套照搬 split 的存储语义。** split 下 server 暂存 store 复制字节(`CopiesIntoServerStorage()`),monolith 下 store 别名前端影子(`StagedShadow.h` 的 `m_copies=false`),不复制正是 monolith 不翻倍内存的原因(MC 26.3 有 441.5 MiB 存储)。记录臂里「GPU 写后刷新」在别名 store 上是空操作(`Managers.cpp:3755`),所以换臂要补一条别名 store 的刷新路径(§1.4),不能直接把 monolith 改成复制 | `Managers.cpp:1287`、`:3755`、`:3806` | +| 5 | **`static_assert(sizeof(ResidualValueBlock) == 0)` 在 C++ 里写不出来**(完整类型 sizeof ≥ 1)。出口门按「类型删掉」兑现:删结构、删 op 46 的发射与应用(线上行只追加,op 46 改为退役行、解码拒收,同 op 50 先例)、bit 4 退役 | `MGPipeTypes.h:1246-1273`;`PipeCalls.def:237-239` | +| 6 | **G1 不在 CI 里**,只在树外 `gate.sh`;P13 删 pull 后 G1 失去对象,需要一条裁定与接班门(§4.3) | `scripts/symbol_report.py` 在 CI 只跑 `--self-test`(`test.yml:3488`) | +| 7 | 删宏有**静默跳过陷阱**:24 个单元测试源用 `#if !MOBILEGL_PIPE_PUSH GTEST_SKIP()`、12 个套件用 `MGL_DECLARE_PULL_SKIP`、集成 CMake 三处 `if (MOBILEGL_PIPE_PUSH)`;宏一旦先于守卫消失,这些全部「绿着跳过」。另 `runtime_mode_proof.py --mode monolith` 把 monolith 定义成「没有 push」,push monolith 会让 `build-linux-monolith-control` 失败,进而让 retrace-split 整个矩阵**被跳过而不是变红**(`test.yml:3065`)。这两处必须在任何删除之前先改(W1) | `scripts/ci/runtime_mode_proof.py:33-42` | + +## 1. 清点 + +计数方法:`grep -E '^\s*#\s*(if|elif|ifdef|ifndef).*<宏>'` 数行,一行同时提两个宏两边都计。全树(含测试):`MOBILEGL_PIPE_PUSH` 613 行 / 99 文件,`MOBILEGL_PIPE_LEGACY_MEMOS` 49 行;`MGB_CTX` 409 处 / 28 文件;`MOBILEGL_BUILD_DISAGGREGATED`(MG_Remote 之外)约 700 行。 + +### 1.1 编译期:`MOBILEGL_PIPE_PUSH` / `MOBILEGL_PIPE_LEGACY_MEMOS`(删 `#else`,留 push 体) + +| 子系统 | 位置(PUSH / LEGACY 行数) | 删掉意味着什么 | 注意 | +|---|---|---|---| +| 前端:状态对象身份与死亡通知、mutation 记账 | `MG_State/**` 约 70;`PipeMutation.h:500`;`GL_Framebuffer.cpp` 18、`GL_Texture.cpp` 4、`GL_Buffer.cpp` 2 | 只去 `#if`,体保留;139 处里只有 6 处有 `#else` | 唯一有行为的 `#else` 是 `PipeInputsSwitch.h:22-26` 的 `MGB_CTX = pGLContext` | +| 前端 `MG_Impl/Pipe/*` | 14 个头整文件守卫(`Tracker.h`、`CsoCache.h`、`*Emit.h`……) | 去守卫 | `OwnedDrawInputs.h` 是 `DISAGG && PUSH`,W5 要放开 | +| CMake 源表 | `CMakeLists.txt:579-604` 七个 push-only 源 | 并入主表 | `:548-577` 三条强制规则删 | +| Espryt 缓冲 | `Managers.cpp:1690-1938`(pull 的 `BufferBackendOps`,`SetBackendResource`);`Mh:1089-1332` | 删 `#else` | 遗留 `EnsureBufferResource`(`M:4434+`,`PipeResource::m_backend`)**不在 `#if` 里**,是运行期 bit 7 关时的臂,W6 才能删 | +| Espryt VAO / 顶点 | `DG:1101-1307`、`M:6013-6250`(遗留 `SyncToBackend` 226 行)、`M:6716-6836`(遗留 fp64) | 删 | 遗留 fp64 臂是**唯一**调 `SyncGpuWrites` 的那条(缺陷 c 的「正确」版本),删它之前 W4a 先修句柄臂 | +| Espryt 纹理 / mip | `DG:3164-3490`(`#else`@3313 pull 单元快照)、`M:7073-11398` 多段、`Mh:2052-2475` | 删 `#else` 与 `!LEGACY` 不可达桩(`M:10794`、`:11065`) | — | +| Espryt 帧缓冲 | `DG:200-332`(`!PUSH \|\| LEGACY` 槽缓存)、`DG:12171-12224`(pull 遍历)、遗留 `g_fboTwinLookupMemo`(`DG:6619`) | 删 | — | +| Espryt sampler / program | `DG:7203-7416`、`DG:6240-6522`、`Mh:3167`(`ProgramBuildSource = ProgramObject`)、`M:15984`(`#if !PIPE_PUSH`) | 删 | push-monolith 下 `ProgramArchiveSource` 仍由前端 `ProgramObject` 现造(`Mh:3523-3528`),这是运行期问题(W4c) | +| Espryt 注册表 / 遗留 memo | `DG:109-187`(`OwnerEquals` + 三个 `TwinLookupMemo`)、`Mh:664-671`、`M:472-481` 等 | 删 | `espryt_memo_purity.py` 的锚点会全部失配(§4) | +| Magma | PUSH 44 / LEGACY 13:`ComputePipelineStateHash`(`VR:5317-5408`)、遗留 VAO memo(`VertexInputStateFactory.cpp:127-215`)、`MagmaPipeArms.h:96-130`;`#if !DISAGG` 的隐藏 GL 程序(`VR:1489-1578`、`:4849-4908`)、`g_programResourceCaches`(`DV:143-379`) | 删 | `g_programResourceCaches` 只在 `!DISAGG` 编译——它在 push 无 MG_Remote 构建里**活着**,W5 换成 `LinkArtifacts::storageBlocks` 后删 | +| `MGB_CTX` / `_LIVE` / `_IDENTITY` | 后端 11 个文件:`VulkanRenderer.cpp` 135、`DirectGLES.cpp` 114、`DirectVulkan.cpp` 49、`Managers.cpp` 24、`UniformManager.cpp` 23…… | 机械改写为 `MG_Pipe::gPipeInputs` 的访问器(W3) | `scripts/gen_pipe.py:466` 的正则 `MGB_CTX\|pGLContext` 同步改 | +| 配置 | `ConfigLoader.cpp:396`(pull 默认位图 0);`Config.h:419` `Features.PipeLegacyMemos`、`:420-423` `MOBILEGL_PIPE_TEXEL_RETAIN_MB` | 删;`TEXEL_RETAIN_MB` 零消费者(另删 `tools/trace_replay/trace_env_overrides_test.cpp:83-84` 的字符串) | — | +| 统计 | `PipeStats.cpp/.h` 17 处,无 `#else` | 去守卫 | `ByteClass::ResidualValueBlock` 随 W3b 删 | + +### 1.2 运行期:monolith 臂与记录臂(P13 的主体) + +分三类。**K 保留**:同地址空间的合法捷径,不是读前端状态;**F 换臂**:monolith 读前端对象或前端影子,改走记录臂;**D 删除**:换臂后无人用。 + +| 子系统 | 运行期分支(代表行) | monolith 今天做什么 | 记录臂 | 类 | +|---|---|---|---|---| +| 缓冲 ensure / flush | `M:2476`、`:2644`、`:3510`、`:4095`(无宏守卫)、`:4166`、`:4203`、`:4311`;Magma `VkBufferManager.cpp:239`、`:1215`、`:1374` | `liveHostBase` 取前端 `MappedData()`、`SyncPersistentMappedRange`、问前端 `IsMapped`;Magma 把 `VkBufferResource` 挂在前端 `PipeResource::m_backend` 上 | `StagedShadowStore`、`hostBytes` 来自 store、句柄表 | F | +| persistent map 捐赠 | `BufferObject.cpp:308`、`:891`、`:976`;`PipeRoute` 的 `MapPersistent` escape | 后端返回 GPU 映射指针,前端收养为 store 基址(设计 §12 D-B4:monolith 改造期不动) | split 是 T0 / T2 | **K**(进程内的 T0;保留,W4a 里只把入口统一到 `map_persistent` 记录) | +| 别名 store | `Managers.cpp:1287`(`StagedShadowStore(false)`);`TextureEmit.h:1568-1600`(纹理记录 blob 是前端层存储的裸地址) | 不复制 | 复制 | **K**,但要补刷新(§1.4) | +| indirect 命令 / count | `DG:496`、`:8661-8784`、`:9637`、`:9678`、`:9806`、`:9844`、`:10082`;`DV:652-953`;`VR:13416+`、`:13504-13511` | 前端 `GL_DRAW_INDIRECT_BUFFER` 槽、只 `SyncPersistentMappedRange`;Magma CPU 读 count、无 compute→indirect 屏障 | `VerbIndirectBuffer` + `SplitHostBytesForCpuRead`;Magma 原生 `DrawWireIndirectNative` + `TakeWireIndirectReadBarrier` | F | +| client 数组 / 索引 | `DG:2653`、`:2714`、`:2746`(三处无宏守卫)、`:9235`、`:9338`;`MultiDraw.cpp:183`;`DV:1254`、`VR:13635` | 后端读 `MGB_CTX` 的 VAO、前端 IBO `SyncGpuWrites` + `MappedData` | 早返回(client 在 GL 线程做 `OwnedDrawInputs` 快照);`IndexBuffer.Res` + `SplitHostBytesForCpuRead` | F | +| SSBO / image GPU 写标记 | `DG:951`、`:4487`;Magma `UniformManager.cpp:1632`、`:1885`、`VR:12636` | 走前端绑定、`MarkBufferGpuWritten(前端对象)`、`MGPipeAnnounceBufferGpuWritten` | applier 视图 + `serverGpuWritten` / `MarkWireBufferGpuWritten` | F | +| fp64 收窄 | `M:6893` | `hostBytes` 不同步 GPU 写(缺陷 c) | `SplitHostBytesForCpuRead` | F | +| 有中途同步的 draw(Magma) | `VR:3730`(最大索引扫描)、`:4223`(GL_DOUBLE)、`:4557`(restart 重写) | 录制中途 `SyncGpuWrites` → 提交并结束当前 CB → SIGSEGV | 每个 `ReadWireBuffer` 都在 `BeginCommandRecording` 之前(`WireDraw.inc:355`、`:430`、`:468`) | F | +| XFB | `DG:1731-2427` 十二处 | 独立角色状态、前端捕获程序与目标、`WritebackFromBackend` 写前端对象 | applier span + `OnBufferWriteback`(切片) | F | +| 纹理存储 / view / 重铸 | `M:7081`、`:8402`、`:9068-9133`、`:12001`、宏 `M:8695` | 前端 `IsTextureView`、前端 `RequireImageBindableStorage`(`M:7399`,读回驱动层失败时退回前端影子)、读前端层纹素 | `SyncTextureViewToBackendByRecord`、`RequireImageBindableStorageByHandle` | F | +| mip | `DG:13354`;`VR:12204`、`:12212-12420` | 前端单元、CPU 滤波前端影子(缺陷 d)、从 level 0 生长(e);Magma 无 BLIT 颜色格式直接跳过(M3)、深度 mip 不传 view 类型 | `GenerateMipmapByRecord` / `GenerateWireMipmap` | F | +| copy-tex / copy-image / readback | `DG:11530`、`:12788-13831`、`:14127-16191`;`VR:9663`、`:10305`、`:10624-10944`、`:11224`、`:11926` | 前端端点、影子镜像只做 RGB9_E5(缺陷 f)、PBO 经 `WritebackFromBackend` 写前端 | 按句柄端点、`FollowCopyImageInStagedStore`、`ReadWirePixels` | F | +| 单元 / sampler / image 单元 | `DG:367`、`:3424`、`:3566`、`:4597-4696`、`:7230`、`:7354`;`VkSamplerManager.cpp:476-482` | 遍历前端 `GetTextureUnitObject`、从前端 `SamplerObject` 铸造 | sampler-view / state 窗口、POD sampler | F | +| 帧缓冲 / render pass | `DG:325`、`:4850`、`:11365`;`M:12090-12515`;`VR:5760`、`:5994`、`:8120`、`:8524`;`VkRenderPassManager` | `HandleOf(前端 FBO)`、以前端 FBO 为键的 render pass | `m_pushedSyncHandle`、`DrawFramebuffer()` 记录、`m_wireDrawPass*` | F | +| program / uniform | `Mh:832` → `DG:6925-8862`(push-only);Magma `UniformManager` 十几处 `IsWire()` 分叉 | 前端 `ProgramObject`;Magma 占位纹理是前端 `TextureObject`(ratchet 里 63 个 `# P13` 符号) | `DrawProgram` 等记录;`WirePlaceholderImages.inc` | F | +| swapchain | `SwapchainObject.cpp:352-388` | 直接写前端默认 FBO 的 `TextureObject2D` | `OnSurfaceChanged` | F | +| query | `DV:1663`、`:1714` | 暂停期间 `GL_PRIMITIVES_GENERATED` 的 CPU 增量 | 无 | F(换臂前先核对 wire 臂是否丢了这个增量,可能是 split 侧缺陷) | +| residual 填充 | `PipeFill.cpp:3609-3611`(`contextValuesWireLive` monolith 为 false)、`:2198-2289`(7 个转发访问器,`InvalidateCompileEnv` / `RecordError` 还会写前端) | 每 verb 把未被记录覆盖的字段从活的 GLContext 拷进 `gPipeInputs`,含前端 `SharedPtr` 字段 | split 下值类字段由记录携带 | F(W4d:值类字段改由记录供给;`kFatal` 前端指针字段在后端不再读后从 `PipeInputs` 删) | +| 设备特性 | `VR:15505-15580`、`:16128`、`:16381` | monolith 不开 wire 用的可选扩展(renderpass2 深度解析、stencil export、AHB 导入、robustness2 null descriptor);`descriptorIndexingCore` 在 monolith 下把 1.2 设备当核心(实例只请求 1.1,疑为既有 monolith 小缺陷) | 都是可选、缺失时具名拒绝 | F(换臂后 monolith 也开这些可选扩展:**改动设备创建,需设备冒烟**) | +| 会话 / 生命周期 | `BackendObject_DirectGLES.cpp:901`、`:1189`;`ContextEpoch.h:19`;`DG:16737`、`:18343-18465`;`Utils.cpp:55`;`M:245`;Magma `BackendObject_DirectVulkan.cpp:347-756`、`FrameContext.cpp:231-247` | 无设备丢失闩、无 server 生命周期、总是原生 make-current | server 生命周期 | **K**(不是数据臂,留着 `Transport` 判断;但 `FrameContext.cpp:227-251` 的 present 屏障与 `VR:7954-7960` 的 HOST 阶段遗漏是 monolith 侧的 Vulkan 同步错误,换臂时一并对齐) | +| 槽 / 角色守卫 | `SlotAllocator.cpp:25-56`;`PipeFill.cpp:362`、`:1276-1334`;`PipeApply.cpp:1785-4006` | 空操作 | split 下守卫 | K | + +### 1.3 换臂后可以删的 monolith 粘合(D 类,W6) + +- Espryt:`g_fboTextureSyncList*`(`DG:3637-3657`、`:4101-4143`,无宏守卫);`SlotTables.h` 以前端对象铸句柄的四个入口(`GetOrCreate(StatePtr)` / `HandleOf` / `NoteStateForHandle` / `StateForHandle`,`:85-104`、`:538-590`);`MGPipeFrontendKeyedRegistryScope`(DG 14 处、M 11 处);`ResolveBufferBindingSubsystemArm` 的 monolith `false`(`DG:723`);`MGPipeUnmigratedEmulation` 的三个 monolith 名(`generate-mipmap-storage` `DG:11760`、`generate-mipmap-cpu-fallback` `DG:13009`、`get-tex-image-shadow` `DG:15417`,`PipeCatalogueTest.cpp:1451` 名单同步清空);XFB 角色状态 `g_roleXfbState`。 +- Magma:`MagmaPipeIdentityTables` / `m_pipeIdentity`(`MagmaPipeArms.h:319-610`);VAO memo 表与 fast-path 快照(`TrySetupDrawFastPath` 只在 monolith 跑,`VR:6702`——**换臂前先量**,它可能是 monolith 每 draw 成本的大头);`VkTextureManager::m_aliveObjects`;`VkRenderPassManager` 以前端 FBO / RBO 为键的条目;`UniformManager` 的三组前端占位纹理;`g_vulkanBufferBackendOps`;`RejectWireLegacyBuffer` 等守卫。 +- 前端:`PipeResource::m_backend`(`PipeResource.h:201-211`)、`BufferObject::Get/SetBackendResource`、`g_bufferBackendOps` 遗留表(bit 7 关时的臂);`ProgramObject::m_backendStateVersion` 的 `g_programResourceCaches` 读者;`PipeInputs` 里的前端指针访问器(`PipeInputs.h` 不再需要 `MG_State/GLState/Core.h`,verify 臂另起头文件)。 + +### 1.4 别名 store 的 GPU 写刷新(换臂里唯一的新设计) + +记录臂读者(restart 重写、multi-draw 重定基、`*IndirectCount`、`gl_BaseVertex`、fp64 收窄、XFB scatter 的捕获前字节)在 split 下经 `SplitHostBytesForCpuRead` 从 server 自己的 GL buffer 回读进复制型 store。monolith 不复制,这一步今天直接返回旧指针。方案: + +- **首选**:别名 store 上的刷新走具名反向回调——回读整 store(或读者需要的范围),以 `OnBufferWriteback` 交给前端落进它自己的影子(monolith 下回调直调、同步),清前端 `m_gpuWritePending`;store 的别名指针随之看到新字节。这正是 split 下 client 收到写回后做的事,monolith 只是同步完成;后端不写前端内存,前端也不读后端。 +- **备选**(首选在某个读者上不成立时):按条目「首次刷新即提升为复制」——只有被 GPU 写过、又被 CPU 读者消费的 store 付一份复制(实际是 indirect / count / 计算写的 EBO,量小)。 +- 不选:monolith 整体改复制型 store(内存翻倍,P11 刚把峰值内存压下 18–42%)。 +- 纹理侧不需要新机制:记录臂的 mip / 重铸 / 回读都先读驱动层,store 只作退路,别名下退路即前端影子(与 dev 的 `568090f0` 同路)。 + +## 2. 出口门 + +| 门(README) | 要做什么 | 主机可验 | 备注 | +|---|---|---|---| +| `static_assert(sizeof(ResidualValueBlock) == 0)` | 字面写不出(C++ 完整类型 sizeof ≥ 1)。按「类型删除」兑现:删结构、`PipeFields.def:179-183`、`EmitResidualValueState`(`PipeFill.cpp:3227`)、`Mono_`/`Wire_`/编解码臂、applier 的 `Residual` 与 `PipeResidualDiverged` 比对;op 46 改退役行(解码拒收),协议修订 +1(`protocol_revision_pins.json`);bit 4 退役不复用;`gen_pipe.py:1053-1065` 不再发 size 断言。它守的意图(「哪个 capability 没被记录带上」)由 verify 比对器接管:加一条 verify 车道用例,用 `MOBILEGL_PIPE_VERIFY_CORRUPT` 打一个 capability 字段,必须红 | 是 | 单元:`PipeCatalogueTest.cpp:317-361`、`RenderStateSpansTest.cpp:1000-1169`、`PipeWireCodecTest.cpp:1760`、`:2263-2281`、`PipeStatsTest.cpp:62`、`:538`、`TrackerTest.cpp:1233` 改为断言退役行被拒 | +| 三道纯度门在非 verify 构建转绿 | A 包含闭包:`check_include_closure.py` 的 clang 模式不带 `-D`(`:152-159`),今天按 pull 配置读头文件——删 `#if` 后自然量到 push 头;`PipeInputs.h` 去掉 `MG_State/GLState/Core.h`(verify 另头)。B server 镜像不引用 `MG_State::GLState` / glslang:link ratchet 172 → 0(94 条 `# P13` 随 W6 的 monolith 臂删除落下,其余 78 条按 bucket 逐条归属,`MG_Remote::Client::*` 那几条要看分区表是否该算 SHARED);W8 把它变成真链接。C `MG_Backend` 零 `pGLContext`:今天已只剩 `PipeInputs.h` 一处注释 | 是 | ratchet 的注解不被检查——W6 起在脚本里加「`# P13` 注解行必须为 0」的断言,防止回潮 | +| MGPipe recorder 金标 | MG_Test 今天没有 recorder,只有 `ResourceEmitTest.cpp` 的 Spy ops、`BufferTest.cpp:1567` 的 ZeroCopyMockBackend、`TextureUploadShapeScenario.cpp:190` 一个形状金标。新建:在 `MGPipeContext` / `MGPipeScreen` 表上装记录器(monolith 路由表本来就可替换,`PipeRoute.cpp:330-390`),每 verb 记下记录流与 `gPipeInputs` 的规范化哈希;fixture 用现有单元场景 + 2–3 条短 trace;金标入库;阴性对照:改一个 emitter 的字段必须红 | 是 | 开放问题 12 已写明它只覆盖推送内容 | +| monolith 逐线程 CPU 不差于 P0 基线 | 红米 `2f7cbe2e` 定频配对 A/B,rd12 / openra / MC 26.3。注意:用户 2026-09-08 定「性能对 pull 臂只记录、不设门」,P2 / P3a 已实测 push 比 pull 多 6–12%(VAO 密集 +27–30%),本门按字面今天大概率不过 | **否,延后** | 见开放问题 2;主机只做同机 `perf stat` 粗看,不当门 | +| 链接棘轮 `# P13` 符号 | 94 条(P7 记 86,后续 `d49df3549` 多标了 7 条裸 `# P13`、1 条双注):63 条 UniformManager 占位纹理、14 条 sampler 读前端 `SamplerObject`、6 条读前端 FBO / RBO / VAO / buffer、4 条 `MGB_CTX` 访问器 / 回读打包器、7 条槽分配器与 apply 线程拒绝函数。前五组随 W6 删;`HasOpenTransformFeedbackSpan`、`InvalidateCompileEnv` 两个 wire 臂也用,要 pipe 侧替身 | 是 | 每次落下都在同一提交里重写基线 | +| D12 模块 target | `SOURCE_FILES` 一张平表喂 `MobileGL` 与 `MobileGL_s`。拆成 OBJECT 库:`mg_util`、`mg_pipe`、`mg_frontend`(MG_State + MG_Impl)、`mg_backend`(两后端 + 从 MG_Remote 挪出的暂存 store)、`mg_remote_transport`、`mg_remote_client`、`mg_remote_server`;加一个 server-only 链接检查目标(不链 `mg_frontend`,必须链得过)。link ratchet 的 PARTITION 表就是边界的书面形式 | 是 | 放在 W6 之后,否则 monolith 臂还把前后端缠在一起 | +| 幸存缓存容量重调 | CSO LRU 64(`CsoCache.h:54`)、sampler CSO 256(`SamplerEmit.h:210`)、六个 memo 门、`sve`≈draw 数的线索(开放问题 14)。主机语料上用 `MOBILEGL_PIPE_STATS` 量 `csom` / `csob` 与各门命中率,定容量;设备复核延后 | 主机可定初值;设备延后 | 放在换臂之后(monolith 的命中率换臂后才有意义) | + +另加两条 P13 自己的门:**skip 普查**(§4.2,每波不增加跳过)与 **无 MG_Remote 符号**(§4.3,G1 的接班之一)。 + +## 3. 缺陷(ID-P8-13 / ID-P8-14) + +全部在记录臂上已修;「换臂即修」指 W4 换臂后自动消失。主机能红的都已有 red-once 记录;Magma 只能靠 CI(本机不跑 DirectVulkan 集成)。 + +| # | 后端 | 缺陷 | 根因 | 换臂即修? | 回归用例(今天在 monolith 按名跳过) | 波 | +|---|---|---|---|---|---|---| +| a | Espryt | `*IndirectCount` 读未同步影子 | `DG:9731-9768` 只 `SyncPersistentMappedRange` 后读前端 `MappedData()` | 是(`DG:9690` `SplitHostBytesForCpuRead`)+ §1.4 刷新 | `GpuWrittenDrawInputScenario` 的 `ACountAComputeShaderWrote…ElementsIndirectCount`(`:484`)、`…ArraysIndirectCount`(`:522`)、`ACountAnAtomicCounterProduced…`(`:700`);`WireIndirectDrawScenario.ComputeWrittenCountWordIsTheOneTheGpuReads`(`:307`) | W4a | +| b | Espryt | 原生 indirect 的 `gl_BaseVertex` 读旧影子 | `ResolveIndirectCommandBytes`(`DG:533-541`)只同步 persistent 映射 | 是(`CurrentIndirectCommandBytes` `DG:8609-8623`) | `ABaseVertexAComputeShaderWroteReachesGlBaseVertex`(`:562`) | W4a | +| c | Espryt | fp64 收窄读未同步影子 | push 句柄臂 `M:6839-6907` 读 `hostBytes`(P3a M-3 声明的偏差),正确的遗留臂只在 LEGACY 下 | 是(`M:6891-6896`)+ §1.4 | `DoubleVerticesAComputeShaderWrote…`(`:758`,Espryt 半) | W4a(W3 删遗留臂不改变 push-monolith 行为:push 本来就走有缺陷的句柄臂) | +| a′ | Magma | `glMultiDrawArraysIndirectCount` CPU 臂读未同步 count | `DV:752-767`;`MultiDrawElementsIndirectCount` 的 CPU 退路 `VR:13504-13511` 同病,且是在 `SetupDraw` 开始录制之后读 | 是(`DrawWireIndirect` 原生 count) | `…ArraysIndirectCount`(`:522`,Magma 半);`WireIndirectDrawScenario` 的 Magma 半(`:319-323`,今天是**静默早返回**,后半段从不跑) | W4a | +| b′ | Magma | 录制中途 `SyncGpuWrites` → SIGSEGV | `VR:3730`、`:4223`、`:4557` 在 `SetupDraw` 录制中回读,`Ops_ReadbackFromGpu` 冲刷并结束了仍持有的 CB;`DV:1254-1270` 的预同步只覆盖普通 `DrawElements` | 是(wire 臂所有 `ReadWireBuffer` 先于录制) | `RestartIndices…` 六例(`:431`、`:446`、`:464`、`:630`、`:651`、`:671`)、`DoubleVertices…`(`:758`,Magma 半);另 `BuildClosedLineLoopIndices`(`DV:1183`)同模式,无用例——W4a 加一例 | W4a | +| d | Espryt | RGB16F / RGB32F CPU 滤波读陈旧前端影子 | `GenerateThreeChannelFloatMipmapOnCpu`(`DG:12994-13065`)在 `DG:13387` 无条件先走,连驱动都不试 | 是(`GenerateMipmapByRecord` 先原生,再 server 读驱动层) | `GenerateMipmapServerScenario` 的 `Rgb16f/Rgb32fRenderTargetBaseGeneratesFromTheGpuWrittenLevel`(M1) | W4b | +| e | Espryt | R11F / 深度链无视 BASE / MAX,从 level 0 生长 | `EnsureGenerateMipmapStorageAllocated`(`DG:11747-11787`)、`GenerateDepthTexture2DMipmap`(`:12626`)、`GenerateColorTexture2DMipmap`(`:12657`) | 是(`GenerateMipmapWindowByRecord` `DG:11826`) | `PackedFloat` / `Depth` × `MutableMaxLevelBelowTheChain`、`MutableBaseLevelOne…`(M2) | W4b | +| g′ | Magma | 无 BLIT 的颜色格式没有着色器 mip 臂 | `VR:12291-12296` 打日志返回 | 是(`WireFramebuffer.inc:1506-1574`) | `PackedFloat*` 三例(M3) | W4b | +| f | Espryt | 拷贝后 `glGetTexImage` 读拷贝前影子 | 镜像只做 RGB9_E5(`DG:13840-13843`);`GetTexImage` 在 `DG:16074`、`:16133`、`:16147` 退回影子 | 是(`FollowCopyImageInStagedStore` 或驱动写标记后具名拒绝) | `CopyImageStoreReadbackScenario` 今天 monolith 不跳过、但主机驱动能 attach,走不到影子;monolith 加 `StoreRead.` 等价登记(旋钮 `MGITEST_ESPRYT_REFUSE_TEXTURE_READBACK_EXTENT=13x7` 今天只在 split 臂由 server 读,W4b 让 monolith 后端也读) | W4b | +| 14a | Espryt | 每次 indirect draw 在 CB 有 GPU 写时整 buffer 同步回读(`62bfe461`) | `SyncClientSideVertexArraysForIndirectFetch`(`DG:2711-2735`,无宏守卫)在判断「有没有 client 数组」**之前**就 `SyncGpuWrites`,每条命令、每个视口遍一次 | 是(记录臂 `DG:2714` 早返回,client 用 `OwnedDrawInputs`;**不需要 MG_Remote**,可最早落) | 无现成用例:W4a 加白盒计数(monolith 下一次带 GPU 写的 multi-draw indirect,回读次数 = 0);红米上原症状是挂死 > 10 分钟(P8 S),设备复核延后 | W4a | +| 14b | Magma | split 构建 monolith 深度 mip 不传 view 类型 | `GenerateDepthMipmapWithShader`(`VR:4918-4962`)的 `WireImage` 默认 2D;调用方断言在 INFO 下编译掉 | 是(`WireFramebuffer.inc:1539-1540` + `WireDepthMipmap.inc:70-72`) | `WireMipShapeScenario.Depth1DChainWithoutANativeBlitDeclinesByName`:今天要 `MGITEST_MAGMA_FORCE_SHADER_MIPMAP`(server 读),只登在 split / spawn;W4b 加 monolith 登记 | W4b | +| e′ | Magma | compute 写 → indirect 读无依赖(P8-D §6 推断) | dispatch 后与 indirect 前都不记屏障;render pass 外部依赖不含 `DRAW_INDIRECT` | 是(`TakeWireIndirectReadBarrier`) | `WireIndirectDrawScenario.ShaderWrittenCommandsWithoutAnApplicationBarrierAreOrdered`(`:347`)、`WireIndirectDispatchScenario.ShaderWrittenGroupCounts…`(`:269`):monolith 跳过;计数断言只在 inproc 可读——W4a 让 `WireIndirectPeek` 在 monolith 也可读 | W4a | +| h | Espryt | 重铸保留路径重放前端影子(设计 §8.4 记的已知 dev 缺陷) | `RequireImageBindableStorage`(`M:7399-7565`)读回驱动层失败时退回影子,并改写前端影子与脏标 | 是(`RequireImageBindableStorageByHandle`);注意它在 monolith 下今天会跳过主体只清 `m_isInitialized`(`M:7233-7241`),换臂时必须去掉这个分支 | `TextureRemintPullScenario` / `ImageBindableRemintScenario` 已不跳过;后者为 ID-P9-11 钉了 `glMemoryBarrier`,保持 | W4b | + +另有 monolith 侧的非 P8 跳过,换臂后一并取消(不是本阶段承诺修的缺陷,但它们同属「monolith 臂与记录臂不一致」,换臂后若仍红就各开一行债):`DepthStencilReadbackMatrixScenario.AFlippedMultisampleResolveMirrorsTheBandsAndAScaleDeclines`(Magma,`:567-574`)、`InvalidImageUnitScenario` 全部(Magma monolith,`:168-176`)、`TriangleScenario` 的 verb 戳用例(`:327-333`)。 + +## 4. CI 影响与不降级的替代 + +### 4.1 逐项 + +| 项(`test.yml` 行号) | 今天守什么 | P13 动了什么 | 替代(同一意图) | 波 | +|---|---|---|---|---| +| `build-linux` `-DMOBILEGL_PIPE_PUSH=${MOBILEGL_CI_DISAGGREGATED}`(`:123-126`)与 `runtime_mode_proof --mode monolith`(`:137-141`) | 主运行库的形态;feat 上已是 split | 选项删 | 去掉该 `-D`;`runtime_mode_proof` 的 monolith 改定义为「无 MG_Remote 符号」(`:33-42`、自测 `:126-132` 同改) | W1 / W3 | +| `build-linux-monolith-control`「build explicit pull runtime control」(`:200-274`)→ retrace-split「Negative control (pull library)」(`:3265-3277`,`retrace_pull_library_control.sh`) | 证明 `run_trace_case.cmake` 的传输解析断言能拦下一个没有 split 的库 | pull 库消失 | 同一 job 改造 **push、无 MG_Remote、非 verify**(即 FCL 内嵌形态)库;脚本逻辑不变(它查的就是 MG_Remote 缺席与证据句),改名为「transport-negative control」;W1 先两库并跑一轮,确认新库同样因同一句话变红,再在 W2 撤 pull 库。**retrace-split 的 `if: needs.build-linux-monolith-control.result == 'success'`(`:3065`)是跳过陷阱**:W1 同时把该条件改成「上游失败即本 job 失败」 | W1 → W2 | +| `prepare_test_runtime` 十二处 `pull-runtime/build-monolith-control`、`control_smoke_test.sh`、`testdata/stub_ctest.sh` | 路径与措辞 | 改名 | 改名;smoke test 用例数不变 | W2 | +| pipe-gates「Espryt memo-key purity」(`:3469-3485`,`espryt_memo_purity.py`) | 新代码不得用前端指针作 memo 键;遗留臂豁免 | 遗留臂删,锚点与 ALLOW 全失配 → 红 | 同一提交里删豁免与锚点,规则变成「全树无前端指针 memo 键」(更严);自测的阴性对照保留 | W3 | +| `build-linux-split`「Server link-closure ratchet」(`:1288-1317`) | server 分区不得新增对前端符号的引用 | `REQUIRED_FLAGS` 要求 `MOBILEGL_PIPE_PUSH=ON` 出现在 CMakeCache(`link_ratchet.py:184-188`),选项删后 exit 2 | 改为只要求 DISAGG + INPROC;`# P13` 落下时同提交重写基线;加「注解 `# P13` 行数必须为 0」断言(W6 之后) | W3 / W6 | +| `include-graph-check`(`:2284-2314`)与 `MobileGLPurity.IncludeClosure` | 门 A | 删 `#if` 后探针量到 push 头 | 不改脚本;若因此变红,是真红,按包含关系修(**不**加 `-DMOBILEGL_PIPE_PUSH=1` 让它量死代码) | W3 | +| integration-split-strict(`:1700-1716`)grep `kMGPipeP5eRunAheadReady = (true\|false)`(`MG_Backend/Init.cpp:102`) | 常量决定严格车道期望 | 不动 | 保证 P13 不改名、不挪该常量;改动它的提交必须同改 grep | 全程 | +| integration-split「Split coverage」「lane parity」与豁免表 `split_coverage_exemptions.txt:20`、`:27`(`PushMonolithArm`) | 每个 monolith 用例要么上 split 臂要么有原因 | `DirectGLES.PushMonolithArm.`(集成 CMake `:2763-2800`)原为 pull 是默认 monolith 时补的 push-monolith 车道;P13 后环境车道就是它 | 保留登记(只增不删,G14),豁免行原因改写;不删 | W3 | +| integration-verify「Push-build controls (G8, G8b, G9, G10, G12)」(`:788-800`)与 Transport A/B(`:1504-1543`)里的位图车道 | 见 4.1 下表 | 遗留臂删 | 见下表 | W3 | +| fatal_census(`:2544`) | abort 点棘轮 | 删 `PipeLegacyMemosDisabled` 等站点 → 变少 | 同提交 `--write-baseline` | W3 / W6 | +| wire_declines_audit(`:2551`) | 每行 decline 有站点 | 只有删到某行唯一站点才红 | 换臂若让某站点变成两臂共用,不影响;删 monolith 臂时逐行核对 | W6 | +| `symbol_report.py --self-test`(`:3488`) | G1 工具自测 | 不动 | 工具留给接班门复用(§4.3) | — | +| apk.yml `-Pmobilegl.pipePush`(`:109`、`:158`) | APK 形态跟分支 | 属性删 | 删参数(Gradle 不认识的 `-P` 无害,但删干净) | W2 | +| 树外 G1(`notes/p12/gate.sh`)、G5 脚本(`p3a/p4a_untouched_regions.sh`,CI 已于 `c1e957cb` 退役) | pull `.text` 不变 | 失去对象 | §4.3 | W2 | + +位图 A/B 车道(集成 CMake,标签 `integration-gpu;integration-monolith-control`,都钉 `MOBILEGL_TRANSPORT=monolith`): + +| 车道 | 掩码 | 意图 | P13 后 | 替代 | +|---|---|---|---|---| +| `{DirectGLES,DirectVulkan}.HandleRecycle.Legacy.` | `PUSH=0` | 遗留臂对照 | 遗留臂不存在 | 结构性删除(需裁定,开放问题 3);句柄臂的 `Handles.` 车道保留 | +| `DirectVulkan.HandleRecycle.AbaControl.` | `PUSH=0` + ABA | ABA 控制能打穿遗留臂的守卫 | 同上 | 同上;`AbaControlHandles.`(打穿句柄臂的身份)保留,它才是 ship 臂的阴性对照 | +| `HandleRecycle.Handles.` / `AbaControlHandles.` | `LEGACY_MEMOS=0`、`0x1fff` | 句柄臂 | 保留 | 去掉 `LEGACY_MEMOS=0`;掩码改为默认 `0x3fff` 或干脆不设 | +| `CsoContentAddressing.On.` / `Off.` | `0x1fff` / bit 63 | 内容寻址的行为对照 | bit 63 与 pull 无关 | 保留 | +| `ResourceSubsystemControl.On.` / `Off.`;`ResourceSubsystemOn.` / `Off.`(LargeArenaAdoption) | `0x1fff` / `0x7f` | 资源族开 / 关的 A/B | 关 = 遗留 `g_bufferBackendOps` 臂,W6 删 | W3 时 Off 仍可跑(遗留缓冲 ops 是运行期臂、不在宏下);W6 删该臂时 Off 车道改为「清该位 → 启动期具名拒绝」的阴性对照(`WILL_FAIL` + 具名正则),On 车道的计数断言不变 | +| `ObjectSubsystemControl.{On,Off,Refused,RefusedTexture,Consumer}`、`DirectVulkan…NoConsumer` | `0x1fff` / `0x1ff` / `0x9ff` / `0x5ff` | 对象族 A/B 与依赖拒绝 | Off 落进 D14 `Fatal{PipeLegacyMemosDisabled}` | `Refused*`(依赖两侧拒绝)保留——依赖表还在;Off 同上改具名拒绝对照 | +| `DirectGLES.MapPersistentRoundtrips.`、`TextureUploadShape.` | `0x1fff` | 资源 / 纹理上传形状 | 保留 | 掩码随默认 | + +单元测试里对应:`SanityTest.cpp:75-121`(`EsprytSlotArmEnvironment`)、`:4673-4831` 三个「无臂组合」用例、`SubsystemDepsTest.cpp:198-203`、`:250`,以及 `PipeCatalogueTest.cpp:216-220`、`:307-311` 两个只在 pull 下有断言的用例(`UninstalledTablesAreAllNull`、`ExactlyTheRoutedRowsAreInstalled`)。前者随遗留臂结构性删除;后两个的 pull 半没有对象,push 半已在。 + +### 4.2 静默跳过的防线(W1 先落) + +- **skip 普查门**:`scripts/ci/skip_census.py` 读各车道 JUnit(`census_junit.py` / `junit_tally.py` 已有解析),按「标签 × 名」记 pass / skip / fail,对着入库的 `scripts/data/skip_census_baseline.json` 断言:skip 集合只能变小;名集合只增不删(G14);任何一条从 pass 变 skip 即红。每波若有意退役用例,必须在同一提交改基线并在提交信息里点名。阴性对照:自测里塞一个「pass → skip」的假 JUnit 必须红。 +- **删宏顺序**:W3a 先让 CMake 无条件 `-DMOBILEGL_PIPE_PUSH=1`(选项删、宏仍定义为 1),全部 `#if` 按 push 求值;skip 普查必须与 W2 末完全相同。W3b 再用 `unifdef -DMOBILEGL_PIPE_PUSH=1 -UMOBILEGL_PIPE_LEGACY_MEMOS` 机械去宏,并在同一提交删 `#if !MOBILEGL_PIPE_PUSH GTEST_SKIP` 守卫与 `MGL_DECLARE_PULL_SKIP` 桩、集成 CMake 的 `if (MOBILEGL_PIPE_PUSH)`(`:411`、`:511`、`:1246`)只留 push 分支。最后加一条 hygiene grep:树里出现 `MOBILEGL_PIPE_PUSH` / `MOBILEGL_PIPE_LEGACY_MEMOS` / `MGB_CTX` 即红。 + +### 4.3 G1 的接班 + +G1 的意图是「拆分期间出货的单进程库不被悄悄改动」。P13 起出货库就是 push monolith,而且有意改动,G1 无法平移。接班(需裁定,开放问题 4): + +1. **无 MG_Remote 门**(CI):W1 新增的 push 无 MG_Remote 库 `nm --defined-only | grep -c MG_Remote` 必须为 0(设计 §13.2 那条「两条幸存字节级等式」之一,今天其实没人在 CI 里查)。W5 把暂存 store 挪出 `MG_Remote` 命名空间后它依然成立,且能因此变红。 +2. **skip 普查**(§4.2)接管 G2「pull / push 测试名同集」与 G14。 +3. **recorder 金标**(§2)接管「推送内容不悄悄变」。 +4. W2 落地前在树外 `gate.sh` 跑最后一次 G1,读数记进 P13 README 作为退役读数。 + +## 5. 构建形态 + +| 项 | 今天 | P13 后 | +|---|---|---| +| `MOBILEGL_PIPE_PUSH` | 选项,默认 OFF | 删(W3a 先固定为 1) | +| `MOBILEGL_PIPE_LEGACY_MEMOS` | 选项,默认 ON | 删;运行期 `Features.PipeLegacyMemos` 与 `Fatal{PipeLegacyMemosDisabled}`(`FatalFamilies.def:86`,只追加 → 改退役行)同删 | +| `MOBILEGL_PIPE_VERIFY` | 选项,隐含 push | 保留;车道全保留 | +| `MOBILEGL_BUILD_DISAGGREGATED` / `_INPROC` | 选项,默认 OFF | 保留,但含义收窄为「传输层」:MG_Remote 的 Transport / Wire / Client / Server 会话与 spawn。后端记录臂、暂存 store、applier 纹理 ops 不再受它门控(W5) | +| 运行期位图 `MOBILEGL_PIPE_PUSH` | 默认 `0x3fff`(P5e),可清 | bits 0–13 固定开,清任一位在启动期具名拒绝;bit 4 退役;bit 63(关 CSO 内容寻址)保留(开放问题 3) | +| `MOBILEGL_PIPE_TEXEL_RETAIN_MB` | 零消费者 | 删 | +| Gradle `mobilegl.pipePush`(`build.gradle:45-47`、`:79`) | 默认 OFF | 删属性与 `-D` | +| Gradle `mobilegl.buildDisaggregated` | standalone plugin 默认 ON,FCL 默认 OFF | 不变 | +| FCL 内嵌(`FoldCraftLauncher/settings.gradle.kts:29`,`FCLauncher/build.gradle.kts:63`) | pull | push monolith、**不编 MG_Remote**(推荐,开放问题 1) | +| `android-plugin/*.kts` | 只传 DISAGG / INPROC | 不变 | +| anland 脚本(`anland-mobilegl-plasma/scripts/build-android.sh:17-20` 传 `-DMOBILEGL_PIPE_PUSH=ON`;`xbuild.sh`、`wsl-test.sh`、`anland-build-client.sh` 只传 DISAGG) | push + split | 删 `-DMOBILEGL_PIPE_PUSH=ON`(留着只是 CMake 未用变量警告);worktree `anland-unified` 不归本阶段动,只在合并时提醒 | + +**FCL 要不要编 MG_Remote**(集成方要求给出裁定理由): + +| | A1:所有出货构建都编 MG_Remote(FCL 打开 DISAGG,运行期默认 monolith) | A2(推荐):把记录臂需要的东西从 MG_Remote 里拆出来,FCL 不编 MG_Remote | +|---|---|---| +| 工作量 | 小:记录臂已经在 DISAGG 下编译,FCL 换个开关即可 | 中:W5 要把 `StagedShadowStore` / `StagedTextureStore`(含其对 `FatalFunnel` 的依赖)、applier 纹理 ops、`ServerLoop::OnApplyThread` / `SessionLatch` / `ServerLoopInstance().Backend()` 的几处 caps 读、`OwnedDrawInputs.h` 的 `DISAGG` 守卫挪到角色中立的位置;约 700 处 DISAGG 站点要逐个分「数据臂」与「传输」 | +| 体积 / 启动 | MG_Remote 约 4.6 万行(非 MG_Remote 源约 26.6 万行,≈15%),外加 flatbuffers 运行时头与 glvnd 头;FCL 进程里多出 socket / spawn / 令牌代码(不监听,但进了地址空间);启动多一次传输解析,量级可忽略。具体字节数 W5 前量一次(Release aarch64 `.text` 差) | 不变 | +| 门 | 「无 MG_Remote」这条字节级等式失去出货对象,只能在 CI 里另造一个不出货的库当阴性对照,而那个库还得能渲染——又回到要保留 monolith 臂 | 无 MG_Remote 的库就是出货库,传输阴性对照与它同一个东西;与 D12 的模块边界一致(`mg_backend` 不依赖 `mg_remote_*`) | +| 风险 | 低 | W5 是一次大挪动,但纯结构性,主机全门可验 | + +推荐 A2,理由是第三行:A1 下要么出货库与对照库不同(对照库的 monolith 臂得一直留着,P13 的「删 monolith 臂」做不完),要么丢掉对照。A2 的工作与 D12 本来就要做的模块拆分重合。若集成方偏好先求快,可以 **A1 过渡、A2 收尾**:W4 在 DISAGG 构建里换臂时 FCL 暂按 A1 出货,W5 完成后改回不编 MG_Remote——但这意味着 W4 期间 FCL 形态与 CI 的无 MG_Remote 库不一致,不推荐。 + +## 6. 分波 + +每波:WSL `~/mgl-p13-*` 构建(clang;split 树 `-DMOBILEGL_BUILD_DISAGGREGATED=ON -DMOBILEGL_BUILD_DISAGGREGATED_INPROC=ON -DMOBILEGL_BUILD_INTEGRATION_TEST=ON`,另有无 MG_Remote 的 push 树与 verify 树按需),本机跑 `ctest -L unit`、`ctest -L integration-gpu -E DirectVulkan`、`-L integration-split` / `-spawn` / `-tcp`(`-E DirectVulkan`)、`retrace`(Espryt),**一次只跑一个套件**;skip 普查对着上一波;推到 `origin/feat/disaggregated` 后看 CI(Magma 集成只在 CI)。设备相关一律记「延后」。 + +| 波 | 内容 | 本机验证 | 风险 | +|---|---|---|---| +| **W1** CI 地基(不动产品代码) | `runtime_mode_proof` 改定义;retrace-split 的 `if` 跳过陷阱改成失败;`build-linux-monolith-control` 增产 push 无 MG_Remote 库,两库各跑一次阴性对照;新增「无 MG_Remote 符号」检查;新增 skip 普查脚本 + 基线 + 自测 | `control_smoke_test.sh`、各脚本 `--self-test`;本地造一个 push 无 MG_Remote 库跑 `retrace_pull_library_control.sh` 对照 | 低 | +| **W2** 默认翻转 | CMake `MOBILEGL_PIPE_PUSH` 默认 ON;Gradle 删 `pipePush`(FCL 变 push monolith);阴性对照只用新库,撤 pull 库;改名;树外 G1 最后一次读数入档 | unit + integration-gpu(push 无 MG_Remote 树);FCL 设备冒烟延后 | 低;FCL 第一次出货 push,设备上未验 | +| **W3a** 宏固定为 1 | 选项删,CMake 无条件定义 `MOBILEGL_PIPE_PUSH=1`;`link_ratchet` 的 `REQUIRED_FLAGS`;workflow / 脚本里的 `-D` | skip 普查与 W2 末逐名相同 | 低 | +| **W3b** 机械去宏(按目录拆提交:MG_State / MG_Impl / MG_Pipe / DirectGLES / DirectVulkan / 测试) | `unifdef`;删 pull 测试桩与守卫;`MGB_CTX*` 改写;删 `LEGACY_MEMOS`、`Features.PipeLegacyMemos`、D14 Fatal 行、`TEXEL_RETAIN_MB`;`espryt_memo_purity` 收紧;位图语义改为固定开(Off 车道暂保留,它们跑的是运行期遗留缓冲臂);`HandleRecycle.Legacy.` / `AbaControl.` 按裁定处理;`fatal_census` 重写基线;hygiene grep | 全套本机门;skip 普查只允许裁定过的退役 | **中高**:diff 大、与并行分支冲突多;`unifdef` 后要人工看注释里「pull build」的陈旧说法(只改明显错误的,不做大段重写) | +| **W3c** 残余块删除 | §2 第一行;op 46 退役行;协议修订 +1;verify 车道加 capability 腐蚀对照 | unit(codec / catalogue / spans)、integration-verify | 低;协议修订要同步 `protocol_revision_pins.json` 与 PeerLatch | +| **W4a** 换臂:缓冲、indirect、client 数组、GPU 写标记、fp64、XFB(在 DISAGG 构建里) | 引入单一谓词(如 `MG_Backend::DataArmIsRecord()`,恒 true),只替换 §1.2 的 F 类分支,K 类保留 `Transport`;§1.4 别名刷新;14a 先落(不需要 MG_Remote);Magma 读回移出录制;monolith 下 `WireIndirectPeek` 可读;取消 §3 W4a 行的全部跳过 | build-split 的 integration-gpu[monolith] / [inproc];split 各臂不变;retrace 230/236(已知 6 条 iterationrp);Magma 靠 CI | **高**:monolith 每 draw 路径整体换;`TrySetupDrawFastPath` 等 monolith 专属快路随之失效,主机先 `perf stat` 粗量 | +| **W4b** 换臂:纹理存储 / view / 重铸 / mip / copy / 回读 | 同上;`M:7233-7241` 的 monolith 早退删;monolith 后端读 `MGITEST_ESPRYT_REFUSE_TEXTURE_READBACK_EXTENT` / `MGITEST_MAGMA_FORCE_SHADER_MIPMAP`;取消 M1–M3、14b 跳过并补 monolith 登记 | 同上 | 高 | +| **W4c** 换臂:帧缓冲、单元 / sampler / image、program / uniform、swapchain、query、设备可选扩展 | Magma 占位纹理改 `WirePlaceholderImages`;sampler POD;render pass 走记录;monolith 开 wire 用的可选扩展;先核对 query 的 CPU 增量 | 同上 | 高;设备创建改动只能 CI + 设备冒烟(延后) | +| **W4d** residual 填充与 `PipeInputs` 前端字段 | monolith 的值类字段由记录携带;7 个转发访问器换 pipe 侧替身;`kFatal` 前端指针字段删;`PipeInputs.h` 去 `MG_State` 头 | 同上 + verify 车道 | 中 | +| **W5** 记录臂去 DISAGG 门控(A2) | 暂存 store / applier 纹理 ops / 选择器挪出 MG_Remote;`OwnedDrawInputs.h` 去守卫;`g_programResourceCaches` 换 `storageBlocks`;无 MG_Remote 库从此走记录臂 | push 无 MG_Remote 树全套;verify 树;无 MG_Remote 符号检查仍为 0 | 中(结构性大挪动) | +| **W6** 删 monolith 臂与粘合 | §1.3 全部;ratchet `# P13` → 0 并加断言;Off 车道改具名拒绝对照;wire_declines 逐行核 | 全套;ratchet 基线同提交重写 | 中 | +| **W7** D12 模块 target | OBJECT 库拆分 + server-only 链接检查目标;ratchet 余量清到 0 或逐条具名豁免 | 全套 + 新链接目标 | 中 | +| **W8** recorder 金标 | §2 | unit | 低 | +| **W9** 缓存容量重调 | 主机语料定初值,设备复核延后 | `MOBILEGL_PIPE_STATS` 读数 | 低 | +| **W10** 收官 | 全套门;文档(ROADMAP、CURRENT_STAGE、DEBTS 的 ID-P8-13 / -14 / `TEXEL_RETAIN_MB` 三行关闭、设计 05 / 07 / 09 / 附 A 改写、`guide/build-and-run.md` 四 flavour 表、`guide/discipline.md` 的 G1 / G2 行);设备延后清单 | — | — | + +W4 的内部顺序可以按 Espryt / Magma 拆成并行包(两后端互不碰文件),集成方合并。W3b 与 W4 都动 `DirectGLES.cpp` / `VulkanRenderer.cpp` 的大片代码,**不并行**。 + +## 7. 需要用户定的事 + +1. **FCL 内嵌库编不编 MG_Remote**:推荐 A2(不编,记录臂从 MG_Remote 拆出,§5);A1 更快但要么丢掉传输阴性对照,要么永远留着 monolith 臂。 +2. **「monolith 逐线程 CPU 不差于 P0 基线」是门还是记录**:2026-09-08 定过「对 pull 只记录、不设门」,而 push 已实测比 pull 多 6–12%(VAO 密集 +27–30%);P13 换臂会再动 monolith 每 draw 的路径。建议按 09-08 只记录(设备 A/B 延后),否则 P13 后要加一个优化包。 +3. **运行期位图**:bits 0–13 固定开、清位即启动期具名拒绝,bit 63 保留;`HandleRecycle.Legacy.` / `.AbaControl.` 两组遗留臂车道随遗留臂结构性删除(句柄臂的 `Handles.` / `AbaControlHandles.` 保留),各 Off 车道改成具名拒绝的阴性对照。这算不算「让 CI 变松」需要你点头。 +4. **G1 退役**:W2 记最后一次读数后退役,由「无 MG_Remote 符号」+ skip 普查 + recorder 金标接班(§4.3)。 diff --git a/docs/Disaggregated/notes/p13/README.md b/docs/Disaggregated/notes/p13/README.md index f1efd128..2d493881 100644 --- a/docs/Disaggregated/notes/p13/README.md +++ b/docs/Disaggregated/notes/p13/README.md @@ -1,6 +1,6 @@ # P13 — 退役 pull 路径(待排) -> 尚未开工。本页保存该阶段在路线图上的完整范围与出口门(2026-09-24 从 [`ROADMAP.md`](../../ROADMAP.md) 阶段表移入,原文照录);阶段开工后,计划、裁定与报告都放在本目录。 +> 只读核查与计划已完成:[`PLAN-P13.md`](PLAN-P13.md)(2026-10-06,待集成方裁定后开工)。本页保存该阶段在路线图上的完整范围与出口门(2026-09-24 从 [`ROADMAP.md`](../../ROADMAP.md) 阶段表移入,原文照录);阶段开工后,计划、裁定与报告都放在本目录。 ## 摘要