diff --git a/MobileGL/Config.h b/MobileGL/Config.h index b8689639..9bd5e4b9 100644 --- a/MobileGL/Config.h +++ b/MobileGL/Config.h @@ -202,9 +202,12 @@ namespace MobileGL::MG_Config { // persistently+coherently mapped storage at definition time (negative control / // escape hatch). Frontend-scoped: it engages only where the active backend // provides AcquirePersistentMap. With adoption on, an app SubData into a busy - // 128MB arena is a plain memcpy into GPU-visible memory; every driver-mediated - // route for the same write stalls the thread or ghost-copies the whole arena on - // this class of Mali driver, and the arena stops costing its size again in RAM. + // 128MB arena lands GPU-ordered through the backend's resident-subdata op + // (BufferObject::LandBytesIntoResidentStore, since 0ee3384b: an in-place write + // tore the frames still reading the old bytes) - still no driver-mediated write + // into the busy mutable store, which on this class of Mali driver stalls the + // thread or ghost-copies the whole arena, and the arena stops costing its size + // again in RAM. Bool DisableLargeBufferAdoption = false; // MOBILEGL_ESPRYT_FORCE_DS_READBACK_EMULATION: make DirectGLES skip the native ES // depth/stencil reads and always go through the shader-sampling emulation. Core GL @@ -610,10 +613,11 @@ namespace MobileGL::MG_Config { // poll loop cannot outlive a late report. 0 = every poll crosses, as before P10: the A/B // arm and the red-once of the no-reply gate (FencePollScenario). Uint32 PollEscalate = 64; - // MOBILEGL_IPC_ADOPT_TIER: 2 = emulate (client keeps the shadow and pushes), which - // is the only tier P5 implements and the reason persistent-map-push can be non-zero - // at all (R-6). 0 and 1 parse and are Fatal at use with "P11"; they exist now so the - // negative control has a spelling the day P11 writes it. + // MOBILEGL_IPC_ADOPT_TIER: 2 = emulate (T2: client keeps the shadow and pushes), the + // only implemented tier and the reason persistent-map-push can be non-zero at all + // (R-6). 0 (T0) and 1 (T1) parse and are settled at the handshake (P11 A1, + // MG_Remote/Transport/AdoptTier.h): a stream data plane refuses them by name and runs + // T2; over shared segments the client dies by name before any record. Uint32 AdoptTier = 2; // MOBILEGL_IPC_VERB_BARRIER: 1 = the client blocks at every verb boundary until // appliedSeq reaches its emitSeq (R-1). 0 is the negative control: it is EXPECTED to diff --git a/MobileGL/MG_Backend/Init.cpp b/MobileGL/MG_Backend/Init.cpp index 5c226f4d..9d057b6a 100644 --- a/MobileGL/MG_Backend/Init.cpp +++ b/MobileGL/MG_Backend/Init.cpp @@ -81,9 +81,10 @@ namespace MobileGL::MG_Backend { // DirectGLES consumes all thirteen migrated families (P2's 0..6, P3a's 7..8, P4a's // 9..12): it registers the resource op table in Initialize() // (BackendObject_DirectGLES.cpp:849) and reads every other family out of gPipeInputs. - // DirectVulkan registers NO resource ops - MGPipeSetResourceOps has exactly one caller - // in the whole tree and it is Managers.cpp:2594 - so bit 7 is CLEAR for it, which is - // the same fact ObjectSubsystemControlScenario already pins from the client side. + // DirectVulkan registers its wire resource op table off monolith only + // (VkBufferManager::RegisterWireResourceOps, VkBufferManager.cpp): under a transport it + // owns buffer stores and claims bit 7 (the P4a mask, see ConsumedSubsystemsFor below); + // under monolith it registers none and its buffers keep the legacy BufferBackendOps path. // P5e (MG_Remote/CONTRACT-P5E.md §6.2). THE ONE CONSTANT THE WHOLE PHASE HANGS ON. // // kCapRunAheadApply says "this server applies an unbarriered record without reading diff --git a/MobileGL/MG_Remote/CONTRACT-P11.md b/MobileGL/MG_Remote/CONTRACT-P11.md new file mode 100644 index 00000000..467d9c77 --- /dev/null +++ b/MobileGL/MG_Remote/CONTRACT-P11.md @@ -0,0 +1,54 @@ +# P11:persistent map 采纳档位诚实化(A 包主机半) + +2026-09-29。依据 [`PLAN-P11.md`](../../docs/Disaggregated/notes/p11/PLAN-P11.md) §2 的 A1、A2 与 C 的文档修正。档位名按 `design/07`:T0 = client 分配 `AHardwareBuffer`、server 导入;T1 = server 导出 opaque fd;T2 = 拒绝,client 保留 shadow 并推送。本文只记实现所需的行为与门;A3 / A4(设备)与 B(T0)不在本文。 + +## 范围 + +档位在握手时定下(§1);断言 arena 落在哪一档、三臂登记(§2);文档漂移(§3)。 + +## 1. 档位由知道数据面的一侧在握手时定下(A1) + +### 线上格式 + +- 无变化。LinkTerms、`RefuseCode`、控制协议修订号(3)都不动:每一侧只判自己的旋钮,不需要把档位带过线。 + +### 规则(`Transport/AdoptTier.cpp` `SettleAdoptTierAtHandshake`) + +| 数据面 | 侧 | `MOBILEGL_IPC_ADOPT_TIER=0/1` | +|---|---|---| +| Stream(tcp) | client / server | 一行 W `Refuse{AdoptTierOnStream, "T"}`,本会话 T2;从不 Fatal | +| SharedSegments(inproc、fork / unix spawn) | client | `Fatal{UnimplementedAdoptTier, "T"}`,握手期、任何记录之前 | +| SharedSegments | server | 一行 W(档位由 client 定),T2 | + +- 调用点:client `StartOverTransportPair`(Welcome 校验后)与 `StartOverSocket`(link terms 定下 `stream` 后、开数据连接前);server `ServerSession::Accept`(后端检查后、Welcome 前)。 +- 使用处 `Client::AdoptTierIsEmulate`(`PipeApply.cpp`、`PipeWireCodec.cpp` 仍问它):旋钮 2 → true;本进程已有握手定档 → true,不再打印;没有握手定过档的调用者 → 仍 `Fatal{UnimplementedAdoptTier}`("no handshake settled")。旋钮 > 2(解析器不收)→ Fatal。 +- `ServerSpawn` 从它拉起的子进程环境剔除 `MOBILEGL_IPC_*`(五个 server 自有旋钮除外),所以 fork / unix spawn 的 server 看不到 client 的旋钮;tcp 的 server 读自己的环境。 + +### 普查 + +新的死亡走 `SessionFail`:abort 站点仍 79,family 词仍 45(复用 `UnimplementedAdoptTier`);refusal 词 3 → 4,`AdoptTierOnStream` 进 `fatal_census.py` 的 `LOCAL_REFUSAL_WORDS`(本地拒绝,不成为 Refuse 帧)。 + +### 门 + +- 单元:`AdoptTierStream.*`(真 tcp 会话:client 0/1 → 会话起、一行拒绝、`map_persistent` 得 DECLINED、之后仍 apply;进程内显示 server 0 → 每会话一行、不死);`RemoteClientControls.AdoptTier{Zero,One}OverSharedSegmentsDiesAtTheHandshakeBeforeAnyMapPersistent`(inproc 真握手,子进程在 `Start` 返回前死、具名);`SplitBufferSet.OnlyAdoptTierTwoIsImplemented`(函数级矩阵);`PipeWireCodecTest.AdoptTier{Zero,One}AfterAStreamHandshakeIsADeclineOnTheWirePath` 与 `AnUnsettledAdoptTierStillDiesByNameOnTheWirePath`。 +- 集成:`{DirectGLES,DirectVulkan}.Tcp.AdoptTier0.LargeArenaAdoptionScenario.*`(`MOBILEGL_IPC_ADOPT_TIER=0`;`integration-tcp` / `integration-magma-tcp`),`AStreamSessionRefusesTheAdoptTierOnceByName` 读 client 日志恰一行,其余用例绿。tcp 专属:共享段上同一旋钮在握手期死(`spawn_lane_parity.py` 的 `MAGMA_TCP_ONLY`)。 +- red-once:删 Stream 分支 → 两后端 tcp 条目 client 握手期 `Fatal{UnimplementedAdoptTier}`、无拒绝行,单元 `AdoptTierStream` 两例、codec 两例、`SplitBufferSet` 一例红;把检查挪回使用处 → inproc 会话起来、`map_persistent` 发出后才在 apply 线程死(子进程写出 `S`),spawn 臂则静默跑 T2(server 环境被剔除)。 + +## 2. arena 落在哪一档(A2) + +- `LargeArenaAdoptionScenario.TheArenaLandsInTheTierItsLaneDeclares`:`PersistentMapPeek` 读 arena 的 `IsBackendPersistentMapped`,与车道的 `MGITEST_PERSISTENT_MAP_ARM` 比;问两次(NULL 定义后;一帧绘制 + SSBO 绑定 + dispatch 后)。未声明的车道跳过。 +- 声明:monolith = adopted(`DirectGLES.ResourceSubsystemOn/Off.`、新 `DirectVulkan.ArenaArm.`,都钉 monolith);split = emulated(Espryt 三臂经 `SplitLogPaths` 按条目声明;Magma `Arena.` 块与 Buffers 车道)。 +- 登记:`mgl_itest_register_split_arms("LargeArenaAdoptionScenario.*")`(Espryt 进门、Magma 进信息层);Magma 进门:`DirectVulkan..Arena.`(band 口味,六例)与 Buffers 车道(加六例,`test.yml` 的 require-green 名单同步)。 +- red-once:monolith 车道加 `MOBILEGL_DISABLE_LARGE_BUFFER_ADOPTION=1` → 三条红;split 车道改声明 adopted → 两后端 8 条红。删 `PipeApply.cpp` 的 R-6 拒绝**不会**让 Espryt inproc 采纳(实测仍 emulated):split 下 client 的采纳门经线路走,codec 不调 applier 就答 DECLINED;applier 只在 server 角色自己发射时被问到(只有 Magma 的 `EnsureGpuResidentStorage`),而 Magma 的线路表 `MapPersistent` 恒返回空。 + +## 3. 文档漂移(C) + +- `Config.h`:大 buffer 采纳下的 SubData 自 `0ee3384b` 起经 resident-subdata GPU 有序落地,不是"plain memcpy"。 +- `MG_Backend/Init.cpp`:DirectVulkan 在非 monolith 下注册线路资源表(`VkBufferManager::RegisterWireResourceOps`)并声明 bit 7。 +- 档位命名按 `design/07`:`PersistentMapTracker.h` 注释已改。`protocol.fbs` 的 `Adopt = 6 // server-owned adopted store` 注释**没改**:`protocol_revision_pin.py` 对整文件取 sha256,改注释要 bump 修订号。 +- `CONTRACT-P6.md`、`design/06`、`design/07` 的"stream 上具名拒绝"现为真,已指向本节代码。 + +## 不变量 + +- G1:新代码全在 `MOBILEGL_BUILD_DISAGGREGATED` 下(`MG_Remote`)。 +- 线上格式与控制协议修订号不变(3)。 diff --git a/MobileGL/MG_Remote/CONTRACT-P6.md b/MobileGL/MG_Remote/CONTRACT-P6.md index eea4aeda..e4b6c417 100644 --- a/MobileGL/MG_Remote/CONTRACT-P6.md +++ b/MobileGL/MG_Remote/CONTRACT-P6.md @@ -673,7 +673,8 @@ readback and the reply-slot pool (P9); `DynamicBackendParameters`' fixed-width r **The two-axis transport stack — control × data, TCP-capable, cross-build — is P6.5** and is the IPC track's next stage (§10.3). P9 builds its reply semantics on P6.5's message replies rather than -on a slot pool, and P11's T0/T1 are refused by name on a stream data plane. +on a slot pool, and P11's T0/T1 are refused by name on a stream data plane (landed by P11 A1 at +the handshake: `Transport/AdoptTier.cpp`, `SettleAdoptTierAtHandshake`; CONTRACT-P11 §1). **Untrusted-guest hardening is `Ph`**, and it must land before any shipping server app accepts a connection from a guest it did not produce — including the `Fatal`-policy flip (§5.2), the handle-slot budget, and the caps in §12. diff --git a/docs/Disaggregated/design/06-transport.md b/docs/Disaggregated/design/06-transport.md index c2e923c2..7a4651d2 100644 --- a/docs/Disaggregated/design/06-transport.md +++ b/docs/Disaggregated/design/06-transport.md @@ -12,7 +12,7 @@ | `SEG_STAGE` | client | 32 MiB | bulk 字节:buffer / 纹理 subdata、UBO、client 数组、persistent-map 脏块 | | `SEG_REPLY` | server | 16 MiB,8 × 2 MiB slot | readback 像素、acceptance 答案(ID-47) | | `SEG_EVENT` | server | 256 KiB SPSC ring | 回调事件 | -| `SEG_SHADOW` / `SEG_ADOPT` | client / server | — | 零拷贝 shadow(Phase 2)/ ≥16 MiB 采纳(P11);stream 数据面上具名拒绝 | +| `SEG_SHADOW` / `SEG_ADOPT` | client / server | — | 零拷贝 shadow(Phase 2)/ ≥16 MiB 采纳(P11);stream 数据面上具名拒绝(`MG_Remote/Transport/AdoptTier.cpp`,握手期,P11 A1) | 段在同机上由 `ShmSegment` 创建(Android `ASharedMemory`、Linux `memfd`、Windows `CreateFileMappingW`),描述符经 `SCM_RIGHTS` 传递;stream 数据面上两端各持一份同尺寸的私有段(§11.9)。 diff --git a/docs/Disaggregated/design/07-memory-readback-verification.md b/docs/Disaggregated/design/07-memory-readback-verification.md index 0ae86975..043a8ba4 100644 --- a/docs/Disaggregated/design/07-memory-readback-verification.md +++ b/docs/Disaggregated/design/07-memory-readback-verification.md @@ -10,7 +10,7 @@ |---|---|---| | T0 | client 分配 `AHardwareBuffer` BLOB,server 导入(Vulkan / GLES) | 唯一在两台设备、两个后端上都完整读写的档(P11 主攻) | | T1 | server 导出 opaque fd | 仅 Adreno 的 Vulkan 路径 | -| T2 | 拒绝,client 侧推送 | 永久正确回退;**今天 split 用的档**(`MOBILEGL_IPC_ADOPT_TIER=2`),stream 数据面上强制 | +| T2 | 拒绝,client 侧推送 | 永久正确回退;**今天 split 用的档**(`MOBILEGL_IPC_ADOPT_TIER=2`),stream 数据面上强制(P11 A1:两端握手时 `Transport::SettleAdoptTierAtHandshake` 对 0/1 打一行 `Refuse{AdoptTierOnStream}` 并走 T2;共享段上 client 握手期 `Fatal{UnimplementedAdoptTier}`,[`CONTRACT-P11.md`](../../../MobileGL/MG_Remote/CONTRACT-P11.md) §1) | T2 下 client 侧推送三件套:不做 map/unmap 命令对(payload 带 `hasLiveHostWrites`);在每个 validate 点按块(`MOBILEGL_IPC_PERSISTENT_BLOCK_KB`,默认 64)推送可达的已映射 buffer,脏页追踪 + 哈希抑制只发变化的块(P5d);门 `PersistentCoherentMapScenario`。`mpr` 数的是每一次 `MapPersistent` 发射(铸成或拒绝都算),所以在 monolith 下也可断言。