# P8-B:Espryt `glGenerateMipmap` 在 split 下(B1 生成窗口、B2 RGB16F / RGB32F) > 2026-09-29,分支 `p8/b`(基线 `8d3e2317`),代码提交 `8dac1fd8`。计划:[`PLAN-P8.md`](PLAN-P8.md) 包 B。 ## 1. 结论 | 名 | 基线(split 臂) | 本包之后 | 位置 | |---|---|---|---| | `generate-mipmap-storage`(split 检查) | 描述符层数少于「从 level 0 起的整链」即 `Fatal{UnmigratedEmulation}`:MAX_LEVEL 偏小、`glTexStorage2D` 层数不足(合法 GL) | 删;生成窗口 = 记录的计划 | `MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp:11759` | | `generate-mipmap-cpu-filter` | RGB16F / RGB32F 生成一律 Fatal | 删;先原生 `glGenerateMipmap`,驱动拒绝或旋钮强制时 server 从自己的层做 CPU 滤波 | `DirectGLES.cpp:13091`、`:13209` | - split 臂的 `UnmigratedEmulation` 可达名清零;树里剩 4 名(`copy-image-shadow-mirror`、`generate-mipmap-storage` 的 monolith 生长、`generate-mipmap-cpu-fallback`、`get-tex-image-shadow`),都只在 monolith 臂上。 - `PipeCatalogueTest.cpp:1451` 名单保持 4 名,注释补记漂移(它从未列过 `cpu-filter`)与本包的两处退役。 - 主机(llvmpipe)上 RGB16F(原生存储)与 RGB32F(加宽为 RGBA32F)都由原生 `glGenerateMipmap` 完成;CPU 臂只在旋钮下走到(日志核实:非强制条目 0 行 `generate-mipmap-server-cpu`)。 ## 2. 设计 | 项 | 做法 | 位置 | |---|---|---| | B1 窗口 | `Base = VerbMipBaseLevel`;`End = min(VerbMipLevelCount, Desc.Levels, MaxLevel+1, Base + 基准层的整链)`,与 Magma 的裁剪同式(`WireFramebuffer.inc:1358`);client 已按同一窗口定义层(`EmitTables.cpp:384-390`) | `DirectGLES.cpp:11759` | | B1 两条 blit 链 | 深度 / R11F 的记录臂改为只跑 `[Base+1, End)`,不再从 level 1 跑到描述符末尾(BASE_LEVEL=1 时会用 level 0 覆盖 base) | `DirectGLES.cpp:12523`、`:12540` | | B1 死臂 | `EnsureGenerateMipmapStorageAllocated(texture)` 的 transport 臂删除:split 下 `GenerateMipmap` 在它之前已经返回 | `DirectGLES.cpp:11781` | | B2 原生 | 三通道浮点也先走原生 `glGenerateMipmap`;错误不转给应用,改走 CPU 臂 | `DirectGLES.cpp:13258` | | B2 CPU 臂 | 源 = 驱动上的 base 层(`ReadTextureLevelTight`,RGB/FLOAT);驱动读不回(不可附着的存储)才用 staged store,且 store 须整层覆盖;都没有则具名拒绝(`MGLOG_E_ONCE`,不 Fatal);2×2 盒滤波(monolith 同式);按驱动存储的通道数 `glTexSubImage2D`(RGB 或 RGBA,FLOAT);支持 2D 与立方体,其余目标具名拒绝 | `DirectGLES.cpp:13036`、`:13091` | | B2 旋钮 | `MGITEST_ESPRYT_FORCE_CPU_MIPMAP=1`,server 读(`MGITEST_MAGMA_FORCE_SHADER_MIPMAP` 先例);命中打一行 `generate-mipmap-server-cpu`(INFO,每进程一次) | `DirectGLES.cpp:13017` | | B2 client 影子 | 任何臂都不读 client 影子 | — | 未改:`PipeCalls.def:260` 的 `GenerateMipmap` 仍是 `kWaitApplied`。P5e 裁定 14 留着它的唯一理由(CPU 滤波读 client 影子)已消失;翻成 `kWaitNone` 是协议改动,交集成方。 ## 3. 覆盖(`GenerateMipmapServerScenario`) 用例:基准层分上下两半(A / B),逐层 `texelFetch` 到 RGBA8 回读;窗口外的层检查未变或仍未定义。 | 用例 | 形状 | 行 | |---|---|---| | `PackedFloat` / `Depth` × `MutableMaxLevelBelowTheChain` | 可变,定义 0..2,MAX_LEVEL=2;level 3 须未定义 | `GenerateMipmapServerScenario.cpp:307` | | `PackedFloat` / `Depth` × `StorageWithFewerLevelsThanTheChain` | `glTexStorage2D` 3 层(整链 5) | `GenerateMipmapServerScenario.cpp:326` | | `PackedFloat` / `Depth` × `MutableBaseLevelOneKeepsTheLevelsOutsideItsWindow` | 定义 0..3,BASE=1、MAX=2;level 0/3 不变,level 4 未定义 | `GenerateMipmapServerScenario.cpp:341` | | `Rgb16f` / `Rgb32f` × `UploadedBaseGeneratesItsChain` | 仅上传 level 0 | `GenerateMipmapServerScenario.cpp:366` | | `Rgb16f` / `Rgb32f` × `RenderTargetBaseGeneratesFromTheGpuWrittenLevel` | 上传哨兵色后把 level 0 当渲染目标绘制(Complementary colortex2 形状) | `GenerateMipmapServerScenario.cpp:382` | | 臂 | 登记 | Espryt | Magma(对照) | |---|---|---|---| | monolith | 环境发现 `DirectGLES.` / `DirectVulkan.` | 4 绿 + 6 按名跳过(M1、M2) | 7 绿 + 3 按名跳过(M3) | | Split / Spawn / Tcp | `MobileGL/MG_IntegrationTest/CMakeLists.txt:3442`;Magma 门控块 `MobileGL/MG_IntegrationTest/CMakeLists.txt:4174` | 10 × 3 绿 | 10 × 3 绿 | | 强制 CPU(Split / Spawn) | `MobileGL/MG_IntegrationTest/CMakeLists.txt:3443-3459`,标签 `integration-cpu-mip-{split,spawn}` | 4 × 2 绿,server 日志须有 `generate-mipmap-server-cpu`(`GenerateMipmapServerScenario.cpp:295`) | — | | A/B inproc(门的 `integration-gpu[inproc]` 环境) | 同环境发现 | 10 绿 | 10 绿 | - 强制 CPU 臂不上 tcp:tcp 的 server 是共享的 `TcpServer.Start` 夹具(`MobileGL/MG_IntegrationTest/CMakeLists.txt:2168`),旋钮放那里会让所有 tcp 条目都走 CPU 臂、原生臂在 tcp 上失去覆盖;CPU 臂本身与传输无关(spawn 已证跨进程)。与 P9 `RemintFallback.` 同形,不需要 parity 例外行。 ## 4. red-once | # | 变异 | 载体 | 观察到的红 | |---|---|---|---| | 1 | 基线 `8d3e2317`(只加用例) | `DirectGLES.{Split,Spawn,Tcp}.GenerateMipmapServerScenario.*` | 30/30 abort:B1 六例 `Fatal{UnmigratedEmulation, "generate-mipmap-storage"}`,B2 四例 `Fatal{UnmigratedEmulation, "generate-mipmap-cpu-filter"}`;`CpuMip.` 8/8 abort | | 2 | CPU 臂入口放回 `cpu-filter` 闩(去掉新臂) | `DirectGLES.{Split,Spawn}.CpuMip.*` | 8/8 Subprocess aborted | | 3 | CPU 臂跳过驱动回读、改读 store | 同上 | 4/8 Failed:两个渲染目标用例 × 两臂读到哨兵色(陈旧) | | 4 | server 不再读旋钮 | 同上 | 8/8 Failed:像素对(原生臂),server 日志无 `generate-mipmap-server-cpu` | | 5 | 两条 blit 链改回 `1 .. Desc.Levels` | `DirectGLES.{Split,Spawn,Tcp}.GenerateMipmapServerScenario.{PackedFloat,Depth}*` | 6/18 Failed(另 2 条为 tcp 夹具):`MutableBaseLevelOne` 两格式 × 三臂 | | 6 | 窗口不按计划 / MAX_LEVEL 裁剪(`End = Desc.Levels`) | 同上 | 6/18 Failed:同 5 | | 7 | `MGITEST_RUN_MONOLITH_MIP_DEFECTS=1` | `Direct{GLES,Vulkan}.GenerateMipmapServerScenario.*`,`MOBILEGL_TRANSPORT=monolith` | 9/20 Failed:M1 两例、M2 四例、M3 三例(§5) | 恢复一律复制比对 + `touch`(`cmp` 一致),每次恢复后重建。 ## 5. monolith 缺陷(记给 dev,ID-P8-3;用例在该臂按名跳过,旋钮 `MGITEST_RUN_MONOLITH_MIP_DEFECTS=1` 复现) | # | 后端 | 缺陷 | 位置 | red-once 读数 | |---|---|---|---|---| | M1 | Espryt | RGB16F / RGB32F CPU 滤波读前端层影子;level 0 被 GPU 写过后影子陈旧 | `DirectGLES.cpp:12927`(调用 `:13320`) | level 1..3 = 哨兵色 `rgba(255,0,255,255)`,两格式 | | M2 | Espryt | R11F / 深度:存储生长从 level 0 算整链,定义 MAX_LEVEL 之外的层;两条 blit 链从 level 1 跑到末尾,无视 BASE_LEVEL / MAX_LEVEL | `DirectGLES.cpp:11707`、`:12580`、`:12610` | MAX_LEVEL=2 时 level 3 宽 2(应 0);BASE=1 时 level 1..3 全被 level 0 覆盖,level 4 被定义 | | M3 | Magma | 无 BLIT 的颜色格式(lavapipe 上 `B10G11R11_UFLOAT`,VkFormat 122)直接跳过生成,只打 `MGLOG_W_ONCE`;wire 臂自 P7 有着色器臂,monolith 没有 | `MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp:12229` | 三个 PackedFloat 用例生成层读到 `rgba(0,0,0,255)` | - M1 修法指向:monolith 也改为回读驱动层(P9 dev `568090f0` 的 `AdoptDriverLevelIntoShadow` 同路)。M2:monolith 两条链与生长改用 `ComputeMipmapGenerationRange` 窗口。M3:monolith 复用 wire 臂的着色器 mip。都动 pull 构建的 `.text`,按 G1 在 dev 修。 ## 6. 门 `gate.sh ~/w7/p8-b p8b`,头 `1bb77890`,dirty 0: | 项 | 读数 | |---|---| | G1 | `added=0 removed=0`(clang 22.1.6);`.text` a5ba13,与建树时相同 | | census / ratchet | 79 个 abort 点、45 个族词,与基线同:`MGPipeUnmigratedEmulation` 的调用点不是 abort 点(abort 在 `PipeApply.cpp` 的漏斗里),删两个名不改计数,基线不动;ratchet 172 不变 | | parity / protocol pin / wire-declines | 0 错;revision 5;53 行 0 未记 | | hygiene | 12/12 OK | | unit | 2714/2714 | | integration-split / spawn / tcp | 437 / 356 / 358,全绿 | | integration-magma-split / spawn / tcp / full-split | 261 / 238 / 207 / 646,全绿 | | integration-gpu[monolith] / [inproc] | 2746 / 2746,全绿 | | retrace(`MOBILEGL_BUILD_TRACE_REPLAY=ON`,236 条) | 230 过;6 条 `iris-iterationrp-in-world`(两后端 × monolith / SPLIT / SPAWN)为已知主机 llvmpipe JIT 崩溃;全部重放日志 0 行 `generate-mipmap-*` | ## 7. 发现未修 | 项 | 说明 | |---|---| | `PipeCatalogue` 名单无门 | 名单与树里的调用点没有任何脚本比对,漂移只能靠人发现;`fatal_census` 数的是 abort 点,不数 `MGPipeUnmigratedEmulation` 调用 | | CPU 臂的目标 | 2D 数组 / 3D / 立方体数组的 RGB16F / RGB32F 若驱动拒绝原生生成,CPU 臂具名拒绝(monolith 的 CPU 滤波对这些目标同样只按 2D 取址) | | 层间精度 | CPU 臂层间以 float 传递,不在每层回量化到 half;GL 不规定滤波,与 monolith(每层从 half 影子读)可能差 1 ulp |