diff --git a/MobileGL/MG_Remote/CONTRACT-P5.md b/MobileGL/MG_Remote/CONTRACT-P5.md index 277a6c07..54635c20 100644 --- a/MobileGL/MG_Remote/CONTRACT-P5.md +++ b/MobileGL/MG_Remote/CONTRACT-P5.md @@ -383,8 +383,8 @@ is a pull-build symbol and G1 admits **no resize**, which is the same reason the |---|---|---| | `MOBILEGL_TRANSPORT` | `monolith` | `monolith\|inproc\|spawn\|unix:\|pipe:`. The three P6 forms **parse and are then refused by name**, staying on monolith: a P6 lane that set `spawn`, fell back silently and went green on the wrong arm is the failure this wording avoids. | | `MOBILEGL_IPC_SERVER_PATH` | `""` | P6 consumes it; P5 parses it because t1's ctest `ENVIRONMENT` blocks and `add_trace_replay_test`'s SPLIT variant already carry it, and an unparsed variable is indistinguishable from a parsed-and-ignored one. | -| `MOBILEGL_IPC_RING_MB` | 8 | SEG_CMD. **One record may be at most half of this** (`RingProducer::MaxRecordBytes`), so 8 MiB caps a record at 4 MiB. R-10 makes the codec publish a max-record-bytes counter rather than assume that is enough. | -| `MOBILEGL_IPC_STAGE_MB` | 32 | SEG_STAGE. Every blob and every var-tail's bytes. | +| `MOBILEGL_IPC_RING_MB` | 8 | SEG_CMD. **One record may be at most half of this** (`RingProducer::MaxRecordBytes`), so 8 MiB caps a record at 4 MiB. R-10 makes the codec publish a max-record-bytes counter rather than assume that is enough. The stage chunk budget below cuts a record's BLOB and never the record's own bytes, so this bound is the one thing no emitter cuts. | +| `MOBILEGL_IPC_STAGE_MB` | 32 | SEG_STAGE. Every blob and every var-tail's bytes, **one record's blob at a time**, and **content chunking is landed**: the rows that can outgrow the segment cut themselves at `MGPipeStageChunkBytes()` = `clamp(segment/4, 4096, segment)` — the buffer content walks (`PipeFill.cpp`'s two `MGPipeContentChunkCap` sites, `1e7c372e`) and one texture level's whole-width slabs (`TextureEmit.h`'s slab split, assembled server-side by `StagedTextureStore::AdoptRun`, `9469d48e`). A record type with no such cut, or a single piece still larger than the arena, stays `Fatal{RingOverrun, "SEG_STAGE"}` at the encoder — the backstop, not the normal path. | | `MOBILEGL_IPC_SPIN_US` | 50 | spin before parking, either direction. | | `MOBILEGL_IPC_PERSISTENT_BLOCK_KB` | 64 | **0 is the E3(a) negative control, not "unlimited"**: it turns the push off and `PersistentCoherentMapScenario` must go red. | | `MOBILEGL_IPC_PERSISTENT_HASH_SUPPRESS` | 1 | 1 = the push ships only blocks whose hash changed since the last push (tracked buffers: the mprotect fault bitmap; untracked: the content scan). 0 restores the whole-range push (A/B control). | diff --git a/docs/Disaggregated/ARCHITECTURE.md b/docs/Disaggregated/ARCHITECTURE.md index 5eae7f4b..2f8e4ac7 100644 --- a/docs/Disaggregated/ARCHITECTURE.md +++ b/docs/Disaggregated/ARCHITECTURE.md @@ -121,7 +121,7 @@ Flags:`kNeedsAck`、`kHasBlob`(B,payload 拥有一个 `MGPBlobRef` 成员, - `MGHostSpan`(32 B)是唯一形状随传输而变的东西:monolith 下 `Ptr` 指向 shadow 或应用内存;split 下 `Ptr == nullptr`、字节在 `SEG_STAGE`,或 `Seg == kMGHostSpanSegFromServerIndexMirror`(P8)。它只进变长尾(`DrawVbo` 的用户索引、`SetShaderBuffers` 的具名 UBO 字节),永不内联进定长 payload——VBO 路径不为它付字节。 - 变长记录(`kVarTail`)= 定长前缀 + 自描述长度的内联尾巴;`kHasBlob` 记录额外校验 `BlobRef` 落在其声明的段内。`SEG_CMD` 是对端并发写入的区域,运行期违反一律 `Fatal{ProtocolCorruption}`。 - wire 记录头 `MGPWireRecHeader{Op:u16, Flags:u16, Size:u32}`(8 B),Size 含头、8 字节倍数;**没有逐记录序号字段**——seq 就是记录序数(producer `m_emitSeq++` / consumer `m_applySeq++`)。`Flags` 是 ring 封帧,永不是 call flags(R-17)。 -- 单条记录上界 = ring 容量的一半(`RingProducer::MaxRecordBytes()`),超过的记录被具名拒绝(`Fatal{RingOverrun}`),不分块(R-10;实测 `maxrec` 见 `MEASUREMENTS.md` §6.3)。 +- 单条记录上界 = ring 容量的一半(`RingProducer::MaxRecordBytes()`),超过的记录被具名拒绝(`Fatal{RingOverrun}`)——**记录本身永不分块**(R-10;实测 `maxrec` 见 `MEASUREMENTS.md` §6.3)。**内容侧已分块**:会超出 `SEG_STAGE` 的 blob 由发射侧按 stage chunk 预算 `MGPipeStageChunkBytes()`(`clamp(segment/4, 4096, segment)`,默认 8 MiB)切成多条记录——buffer 范围走查(`PipeFill.cpp`)与纹理一级的整宽 slab(`TextureEmit.h`,服务端 `StagedTextureStore::AdoptRun` 拼回整级);单片仍超容量或该 record 类型未接入分片时仍是 `Fatal{RingOverrun, "SEG_STAGE"}` 兜底。 - `MGPSubData` 的 buffer 半边:`Target == Buffer` 时目的字节范围搭在 `UnionBox.X/W` 上,`MGPipeSetSubDataBufferRange()` 是唯一拼写;单条记录上限 offset 2³¹−1 / size 2³²−1。 ### 4.1 关键 payload @@ -369,7 +369,7 @@ P13:删 `SnapshotFromGLContext()` 的非 verify 分支、`MGB_CTX`、`MOBILEGL | `SEG_SHADOW[n]` | 5 | client | 每对象 ≥256 KiB(Phase 2) | 零拷贝 buffer/texture shadow | | `SEG_ADOPT[n]` | 6 | server(client RW) | 每 buffer ≥16 MiB(P11) | 应用直写 GPU 内存 | -`SEG_STAGE` 默认 32 MiB 装不下 P5b 目标负载的单次 128 MiB 上传;**默认不改**,普查与设备用显式 `MOBILEGL_IPC_STAGE_MB=256` profile,分块 / 专用 carrier 是 P8 的设计(`ROADMAP.md` 开放问题 11)。 +`SEG_STAGE` 默认 32 MiB 曾装不下 P5b 目标负载的单次 128 MiB 上传;**内容侧分块已落地(2026-09-20)**——buffer 范围走查(`1e7c372e`)与纹理整宽 slab(`9469d48e`)按 `MGPipeStageChunkBytes()`(默认 segment/4 = 8 MiB)切成多条记录,默认 32 MiB 即可容纳,历史普查 / 设备的显式 `MOBILEGL_IPC_STAGE_MB=256` profile 不再是这两条路径的必需;未接入分片的 record 类型与专用 carrier 仍是 P8 的设计(`ROADMAP.md` 开放问题 11)。 创建(`ShmSegment`):Android `ASharedMemory_create`(API 26);桌面 Linux `memfd_create`;其他 POSIX `shm_open`;Windows `CreateFileMappingW`。传递:POSIX `SCM_RIGHTS`(`FdPassing`,专用 `AF_UNIX SOCK_DGRAM` socketpair);Windows 段名走 `SegmentRef`。不进 `SEG_STAGE` 的:restart 重写的整 EBO 与 multi-draw 展平的索引流(走索引镜像)。`SEG_SHADOW` 块的退休:释放的块进 pending 链表,`appliedSeq`(借入 GPU 时间线的 slot 用 `retiredSeq`)越过最后一条引用它的记录后才归还。 diff --git a/docs/Disaggregated/CURRENT_STAGE_PROGRESS.md b/docs/Disaggregated/CURRENT_STAGE_PROGRESS.md index 9ad91b54..2d4faef6 100644 --- a/docs/Disaggregated/CURRENT_STAGE_PROGRESS.md +++ b/docs/Disaggregated/CURRENT_STAGE_PROGRESS.md @@ -275,7 +275,7 @@ p50 181(约 1.10 倍)——那一次不可配对,只能作为方向性提 | 项 | 证据 / 去向 | |---|---| -| `SEG_STAGE` 默认 32 MiB 装不下目标负载的单次 128 MiB 上传;**决定 = 默认不改**,普查与 Redmi 显式 `MOBILEGL_IPC_STAGE_MB=256`;分块 / 专用 carrier 留 P8(`ROADMAP.md` 开放问题 11) | `p5b-results/blit-codex-v1.md`;`MEASUREMENTS.md` §7.2 | +| ~~`SEG_STAGE` 默认 32 MiB 装不下目标负载的单次 128 MiB 上传~~ | **内容侧分块已落地(2026-09-20)**:buffer 范围走查 `1e7c372e`、纹理整宽 slab `9469d48e`,预算 `MGPipeStageChunkBytes()`(默认 segment/4 = 8 MiB),默认 32 MiB 即可容纳;未接入分片的 record 类型与专用 carrier 仍归 P8(`ROADMAP.md` 开放问题 11)。历史证据 `p5b-results/blit-codex-v1.md`;`MEASUREMENTS.md` §7.2 | | Magma P7 剩余功能与性能 | 应用 VBO/EBO/UBO、已对齐 SSBO/atomic/texel、persistent 和 indirect 消费已随 `38919d45` 实际游戏修复落地;XFB buffer capture、部分不对齐 range/UBO byte-tail、placeholder/native-format 及批处理性能仍待做。P5b “82个错答”只属历史计数;见 [修复报告](notes/p5f/magma-inproc-fix.md) | | rd12 GLES `InitialBytesNotCarried/resource_respecify`、rd12 VK `BarrierTimeout/Present`、`iris-bsl-esc-menu-854` GLES、三条 `texture-remint-pull` 仿真槽、`create-indirect` VK 内存膨胀 | 79 trace 普查 `counts.json` / `trace-transitions.json` | | RGB 三通道 CPU mip 回退仍是具名 Fatal | `p5b-results/mip-codex-v1.md` | diff --git a/docs/Disaggregated/MEASUREMENTS.md b/docs/Disaggregated/MEASUREMENTS.md index 4d29071d..6cc5f2e1 100644 --- a/docs/Disaggregated/MEASUREMENTS.md +++ b/docs/Disaggregated/MEASUREMENTS.md @@ -339,6 +339,8 @@ Espryt T1(`0x1fff` − pull)= +1076.1,T2(`0x1ff` − pull)= +1064.7, max record bytes(x2,`37fc4fdb`):Triangle / persistent map / OpenRA 25 帧 / SmallRing 都是 **`maxrec=784 B` / `SetVertexAttribDefaults`**,默认 cap 4 MiB(占 0.019%)——这些负载不需要 chunking;oversized `DrawVbo` 尾的 unit 具名拒绝 `Fatal{RingOverrun, "DrawVbo"}`。 +**内容分块落地(2026-09-20,`1e7c372e` buffer / `9469d48e` texture)**:`SEG_STAGE` 仍是"一条记录一个整 blob"的 arena,但会超出它的内容由发射侧按 stage chunk 预算 `MGPipeStageChunkBytes()` = `clamp(segment/4, 4096, segment)`(默认 32 MiB → 8 MiB)切成多条 `resource_subdata`——buffer 范围走查与纹理一级的整宽 slab(服务端 `StagedTextureStore::AdoptRun` 拼回整级)。§7.2 普查为容纳单次 128 MiB 上传而显式设的 `MOBILEGL_IPC_STAGE_MB=256` 因此不再是这两条路径的必需;未接入分片的 record 类型仍由 `Fatal{RingOverrun, "SEG_STAGE"}` 具名拒绝。上段的 `maxrec` 数字是分块前的测量,分块后的逐 blob 字节分布尚无新测量。 + ### 6.4 Redmi 四臂 A/B(split 未测) 会话 2026-09-16,runner `eec0e836`,三份 APK 源码头 joint `e61d0012`;臂 = pull APK / push APK / split APK + `MOBILEGL_TRANSPORT=inproc` / splitctl(split APK 不设 transport)。40 组前后 pin check 全 P/P,37.2–39.9 °C。**27 组完成 / 13 组失败:全部 10 组 split 在 benchmark 前中止**(improved-transparency 双后端 `DrawElementsInstancedBaseVertex`,其余 `DrawElements`),另 3 组是 rd12/DirectVulkan 的既有 `scudo` 崩溃。帧 p50 / p99(ms): diff --git a/docs/Disaggregated/README.md b/docs/Disaggregated/README.md index d55002be..280bee3c 100644 --- a/docs/Disaggregated/README.md +++ b/docs/Disaggregated/README.md @@ -38,7 +38,7 @@ MOBILEGL_TRANSPORT=inproc ctest --test-dir build-split -L integration-gpu --outp | 变量 | 默认 | 用途 | |---|---:|---| -| `MOBILEGL_IPC_STAGE_MB` | `32` | `SEG_STAGE` 容量;目标负载 profile 显式 `256`(单次 128 MiB 上传),默认不改 | +| `MOBILEGL_IPC_STAGE_MB` | `32` | `SEG_STAGE` 容量(一条记录一个整 blob);**内容分块预算 = 本值的 1/4**(`MGPipeStageChunkBytes()`,默认 8 MiB),超预算的 buffer 范围 / 纹理级切成多条记录,故历史 profile 的显式 `256` 不再是这两条路径的必需 | | `MOBILEGL_IPC_VERB_BARRIER` | `1` | 每个 verb 等 `appliedSeq == emitSeq`;`0` 只作 E1 阴性控制 | | `MOBILEGL_IPC_AUDIT` | `0` | apply 返回后以 `0xDD` 填退休 staging,查跨返回持针 | | `MOBILEGL_IPC_STRICT_ERRORS` | `0` | BARRIER-PULLED residual input 提升为具名 Fatal | diff --git a/docs/Disaggregated/ROADMAP.md b/docs/Disaggregated/ROADMAP.md index 338f5206..c143d325 100644 --- a/docs/Disaggregated/ROADMAP.md +++ b/docs/Disaggregated/ROADMAP.md @@ -125,7 +125,7 @@ P5c 当时移交的对象类 BARRIER-PULLED、transport registry 前端键、EGL | `GetCaps` 的两个 blobref | 目前不骑 record;一旦运输,必须有 server→client carrier rule,不能套 `SEG_STAGE` | | PACK-PBO readback | P5 具名拒绝;真实形状是 server 写 buffer resource、client `MarkGpuWritten`,P6(ID-57) | | ABI fingerprint | 不混 segment sizes;改变 8/32/16 MiB + 256 KiB ledger 时仍欠这项 | -| 默认 32 MiB staging | 目标负载单次 128 MiB 上传装不进默认 stage;普查 / Redmi 显式 256 MiB profile,默认不改;分块 / 专用 carrier 归 P8(开放问题 11) | +| 默认 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 核验并装配已有载体,不再重做这些迁移 | | 未迁移与仿真路径 | 15 个 class-C 槽(query / sync 尾 / `GetTexImage` / `SetSwapInterval`)→ P9 / P10;client vertex arrays、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 已完成([报告](notes/p5f/magma-runahead.md));XFB、部分不对齐范围、placeholder/native-format 及进一步性能工作仍归 P7([报告](notes/p5f/magma-inproc-fix.md)) | **P5e 补**:client vertex arrays 在 run-ahead 下是**具名拒绝**(ID-82,`Fatal{UnmigratedVerb, "DrawArrays+CLIENT_ARRAYS"}`),并有自己的预期红车道 `integration-clientarrays-split`(ID-134);staging 仍归 P8 | E2 wire 内容控制 | draw-drop 是 OpenRA 的有效控制(758 draws,SSIM 0.000036);clear-drop 被全屏 overdraw 掩盖,不能作控制 | @@ -147,7 +147,7 @@ P0 已回答的不再列出(spike A 的域、spike B 的分档、`posix_spawn` 8. **一份反射归档能否服务三个消费者。** Espryt 那一半已答(P4a):能且不需要复制。Magma 的两个消费者(`DirectVulkan.cpp` 为 `glGetProgramResource*` 重跑反射)未答。P7。 9. **viewport-array 回放能否塞进一次 `draw_vbo`**:各遍之间观察到的状态是否与今天一致未验证。P8。 10. **`ResidentSubData` 的不对称。** P3a 原样保留:`SubDataResident` 是 `kOptional`,Magma 未补实现、`kCapResidentSubData` 未接线。P7 / 独立 `dev` PR。 -11. **`SEG_STAGE` 的上限。** 目标负载已实测单次 128 MiB 上传,超过默认 32 MiB;普查与 Redmi 统一显式 256 MiB,默认不变,分块路径未实现。P8 需要 MC in-world / Create 的占用分布与更大 blob 的 carrier 设计。 +11. **`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"}` 兜底。**仍未答**:未接入分片的 record 类型(宿主索引 span、`SetGlobalConstants` 的整个 UBO 等)的更大 blob carrier 设计,以及 MC in-world / Create 的占用分布。P8。 12. **P13 之后 split-only 渲染 bug 的 server 侧第二意见。** verify 构建 + recorder 只覆盖推送内容,不覆盖后端对它的解释。 13. **烘焙后的内部 shader 能否在没有活 `ProgramObject` 的情况下表达 uniform location 与 UBO 布局。** 未做原型。P7。 14. **推送模型改变哪些按拉取模式调过的缓存命中率。** 幸存者容量在 P13 重调。一条线索:设备上 26.3 Espryt 的 `sve` ≈ draw 数(每 draw 重发一次 sampler-view 集合),桌面只有 ~0.07/draw;列入 P3b/P4b 优化清单。 diff --git a/docs/Disaggregated/notes/p5/BRIEF-P5.md b/docs/Disaggregated/notes/p5/BRIEF-P5.md index 7edfee5c..cd0f7bf3 100644 --- a/docs/Disaggregated/notes/p5/BRIEF-P5.md +++ b/docs/Disaggregated/notes/p5/BRIEF-P5.md @@ -213,16 +213,24 @@ OpenRA 再加 blit/纹理/程序若干;总计 6–15 个。**其余 ~25 条目 通用规则:**批处理只允许让水位线变晚,绝不允许让等待者看到"比实际做的更多"的值。** **pad 记录(`kRecPad`)不推进 seq**——两侧都必须跳过它再计数,否则每一次回绕都会让 seq 永久错位且无校验和。 -### R-10 — P5 不做分块,但必须证明不需要 +### R-10 — blob 走 `SEG_STAGE`,记录上界,以及内容侧的分块 规则:**所有 blob 走 `SEG_STAGE`,记录本身只带 `{Seg, Offset, Size}`**,于是 `CreateShaderState` 的记录是 192+8 字节而不是整个 archive。var-tail 的长度由 GL 上限或发射器自己的 切分(`MGPipeForEachSubDataRecordRange`,`PipeFill.cpp:694-713`)界定。因此**没有一条记录会超过 -`RingProducer::MaxRecordBytes() == Capacity()/2`**,超过即 `Fatal{RingOverrun}`,分块留 P8 -(`ROADMAP.md:25`)。 -**证明义务**:wire 包加一个 `PipeStats` 最大记录字节的 max 计数器,在缩减路径 + OpenRA 上出数, +`RingProducer::MaxRecordBytes() == Capacity()/2`**,超过即 `Fatal{RingOverrun}`——**记录本身永不分块**。 +**内容侧分块已落地(本节原写"P5 不做分块…分块留 P8")**:`SEG_STAGE` 仍是"一条记录一个整 blob"的 +线性 arena,但会超出它的内容由发射侧按 stage chunk 预算切开——预算 `MGPipeStageChunkBytes()` = +`clamp(segment/4, 4096, segment)`,默认 `MOBILEGL_IPC_STAGE_MB=32` → 8 MiB +(`MG_Remote/Client/GpuWritePending.h:169`,无 session / monolith / server 角色自身返回 0 = 不切)。 +已接入的两条内容路径:**buffer 内容走查**(`PipeFill.cpp` 的 `MGPipeContentChunkCap`,`1e7c372e`) +与**纹理一级的整宽 slab**(`TextureEmit.h` 的 slab 切分,服务端由 `StagedTextureStore::AdoptRun` +按 run 拼回整级、覆盖不全即 `Fatal{StageSnapshotTooNarrow}`,`9469d48e`)。**未接入分片的 record +类型、以及单片仍大于 arena 的情形,仍由编码器的 `Fatal{RingOverrun, "SEG_STAGE"}` 兜底**;编码器 +上界(一条记录的 header + payload + 内联尾)与这条 Fatal 都不因分块而放松。 +**证明义务(不变)**:wire 包加一个 `PipeStats` 最大记录字节的 max 计数器,在缩减路径 + OpenRA 上出数, 写进 MEASUREMENTS。若实测有记录逼近 `Capacity()/2`(默认 `MOBILEGL_IPC_RING_MB=8` → 4 MiB), -立刻上报集成者,由集成者决定是提前做分块还是调大默认 ring。 +立刻上报集成者,由集成者决定是为该 record 类型加分片还是调大默认 ring。 ### R-11 — 暂存字节的生命期:**应用侧不得跨返回持有指针**