Files
MobileGL/docs/Disaggregated
BZLZHH d2efd1d79d [Perf] (MG_Remote/Client): the create window lands OFF - the deferral cannot be collected, 8 reply slots against 59,673 replies in one frame - measured on the device at 4,536 blocking creates either way
The window was the handoff's remaining item: let up to N resource_create answers be
outstanding, read a provisional accept, collect the answers in order, and un-latch an
object whose late answer is DECLINED. It is a sound mechanism and the gates below pin
it - and on the device it does nothing at all, which is why the default is 1.

MEASURED, same device, same frame, same 101,421,396 B staged, same binary:

    MOBILEGL_IPC_CREATE_WINDOW=4   by_op 2 = 4,536   wall 28.64 s
    MOBILEGL_IPC_CREATE_WINDOW=1   by_op 2 = 4,536   wall 28.64 s
    (the handoff's pre-window baseline: 31.3 s)

by_op 2 counts the creates that took the BLOCKING path (LinkMetricsBeginReply is
called with ownsReplySlot = rowCarriesReplySlot && wantReply), so it not moving means
no create deferred. A temporary counter in the row said why: pushed=24, blocked=4,476,
took=20, readfail=8,951, pending pinned at 4 - the drain READ the answers back and
failed every time.

WHY, IN THE PROTOCOL'S OWN ARITHMETIC. An answer is readable only until its pool slot
is reused, and the pool is kDefaultReplySlotCount = 8. The server answers EVERY
reply-owning record, not only the rows that wait, because it cannot know which those
are (ClientSession.cpp:1897-1900: "The server posts either way"). This frame posts
59,673 of them: create 4,536 + respecify 20,633 + params 18,056 + sub-data 12,671 +
the rest. 8 slots against 59,673 replies churns 7,459 times per frame, i.e. 13.2
replies between two creates, so an answer is overwritten after 8 replies - 0.61 of a
create. The deferral is not slow, it is UNREADABLE, and no window depth fixes that.

AND IT IS NOT NEUTRAL: with the window on, the provisional accept latches an object
the applier may have refused, and the un-latch that corrects it lives in that same
unreadable drain. A refusal would become an object the client believes is published -
the silent divergence the suspect list exists to prevent. So the machinery is kept
(one experiment away from usable: a deeper pool, or a "do not answer me" bit, both
wire changes) and the default is off.

Worktree note: the window implementation itself was uncommitted in the tree at
82ed1df9 (MOBILEGL-CS-HANDOFF-2.md section 2, "can compile, gates mostly green, never
run on a device"). This commit lands it in the state the measurement supports. The
row also gains the heal the suspect list was missing - a BLOCKING create that lands
takes its object back out of it, or kCreateSuspectMax recoveries would retire the
window for the rest of the process - and the clamp that ties the environment's value
to the array, with a static_assert tying the array to half the reply pool so "someone
raises kCreateWindowMax to 8 and steps on the pool" is a compile error.

Gates (RemoteClientControls.inc), all green, 171/171 RemoteClientTest:
  ACreateWindowDefersTheAnswersAndTheWindowOffDoesNot - window on = 12 records 0 waits;
    the SAME CHILD turns the knob to 1 and it is 12 records 12 waits (the negative
    control); pending <= effective after every create; 99 clamps to the array; the
    array is <= half the pool
  ARefusedCreateUnlatchesItsObjectAndItsNextCreateBlocks - a real refusal (the
    server's consumed mask narrowed) clears the latch, +1 suspect, the object's next
    create BLOCKS (2 records 2 waits), and restoring the consumer HEALS it (latch
    back, suspect -1)

Docs: CONTRACT-P5E.md section 2.5 rewritten (it still said all three rows keep
kWaitReply), plus
docs/Disaggregated/notes/p12/CREATE-WINDOW-MEASURED.md (the numbers, the arithmetic,
the picture evidence: both arms SSIM 0.977) and
docs/Disaggregated/notes/p12/UNPUBLISH-GUARD-SIGNALS.md (handoff section 3: neither
caps generation nor FreshlyPrimed is a signal for "the applier dropped its records",
and the residual gap is one line - the dedup at TextureEmit.h:838 does not read the
latch, so an UNCHANGED descriptor after a drop produces no record and therefore no
refusal to self-heal from).
2026-09-26 00:47:19 +08:00
..

MGPipe:MobileGL 前后端拆分

做什么:在 MobileGL 的前端(处理应用的 GL 调用)和后端(调用 GLES / Vulkan 驱动)之间立一份显式接口——前端把状态变化推给后端,后端不再直接读前端的内存。有了这份接口,前后端可以放在两个线程、两个进程,甚至两台机器上运行。

做到哪了(2026-09-25):接口与拆分已完成到"两个进程、可经 TCP 跨机"。当前在做 P12:让 Android 上的 server 自己开窗口,把画面直接显示到屏幕上——审查后的主机定向测试和 Redmi 真机七项复测已通过;FCL 与跨机 TCP 两项验收仍未完成。→ CURRENT_STAGE_PROGRESS.md

一张图

应用的 GL 调用
  → 前端 MG_Impl:处理 GL 语义;在每条"做事的命令"(draw、clear…)之前,把状态变化整理成记录
  → MGPipe 接口(两张函数指针表,由调用目录 PipeCalls.def 生成)
  → 三选一:直接调用(单线程)│ 队列 → 另一个线程 │ 队列 → 另一个进程 / 另一台机器
  → 后端 MG_Backend:Espryt(GLES)/ Magma(Vulkan),按记录更新自己的状态并调用驱动
  ← 反向消息(错误、GPU 写回、窗口尺寸)按顺序回到前端

文档导航

想知道 看
现在做到哪、下一步 CURRENT_STAGE_PROGRESS.md
设计要点与各部分设计 ARCHITECTURE.md → design/
分几个阶段、每个阶段做了什么 ROADMAP.md → notes/<阶段>/
性能与验证的关键数字 MEASUREMENTS.md
怎么构建、运行、测试 guide/build-and-run.md
代码在哪 / 术语 / 工程纪律 guide/code-map.md、guide/glossary.md、guide/discipline.md
各阶段的原始计划、裁定、报告、证据 notes/

整理记录:2026-09-24 两次整理——顶层只留索引,设计正文拆到 design/,上手与参考放 guide/,阶段细节放 notes/<阶段>/;整理前的版本见提交 1fb18d9。更早的设计竞赛与评审稿在 git 历史(8b31de2f、1794ac94、8349babe、87ee17c6)。