mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-11-07 01:53:24 +09:00
The three things that made the deferral inert are all fixed, in the order they were
found: the reply ring could not hold an answer across the ~1,000 records between two
creates; a residency budget could not help because what displaces an answer is what
the SERVER sends, not what the client emits; and so the mechanism had to be retention.
WHAT LANDED
* ILink::DeclareReplyRead + LinkCapabilities::RetainsReplies. The wire declares a
seq will be read BEFORE it publishes, so the declaration cannot race its own
answer; a link with the capability false ignores it, and the window does not
defer on one (CreateWindowEffective() is gated on it).
* StreamLink's ring of the last 8 answers becomes a declared-answer store: a
monotone whitelist plus a cursor, answers kept until their reader TAKES them
(take-and-delete under one lock, bytes swapped into the scratch), and a capacity
of 24 that is FATAL to exceed rather than evicting. An evicted wanted answer is
unrecoverable - re-latching by sending another create wipes the server's record
(PipeApply.cpp:2033) and its PendingUploads (PipeApply.h:332) while the client
cleared its dirty flags at emission (PipeApply.h:298-304) - so the capacity is
sized so it cannot be reached and reaching it says its own name.
* An answer nobody declared is DROPPED, which is what keeps the store bounded by the
client's deferral depth instead of by the server's answer rate. Retain-on-arrival
grows ~11.3 entries per create against a server that ignores the no-reply bit.
* A read that fails after WaitForApplied has returned Reached is now Fatal{ReplyMissing}
naming the seq: on the stream arm the reply frame strictly precedes the progress
frame that publishes appliedSeq, so that combination is a broken invariant, and
retrying it (what the row used to do) wedges the window for the rest of the
process while leaving every deferred object latched on a provisional accept.
* The residency budget is deleted (WireTables.cpp keeps its history), the window
retirement branch drains once before falling back - it could otherwise strand the
answers it was holding - and Detach clears the store.
MEASURED ON THE DEVICE, load frame, both ends rebuilt, frame-sent identical per op
(create 4,536, records 103,097) throughout:
window 1 (R-5's shape) 4,536 takes 28.66 s
window 2 explicit 2,500 17.27 s
window 2 AS THE SHIPPED DEFAULT 2,563 16.52 s
window 4 1,644 11.02 s
window 2 against an OLD SERVER 2,663 16.38 s (zero Fatals)
The last row is the version-skew case the design review named as a must: a server
built before the no-reply bit answers ~55,903 records a frame, the whitelist drops
every answer nobody declared, and the deferral still reads its own back.
GATES (172 + 15 green), and the important one is proved to go red:
* ADeclaredAnswerSurvivesFarMoreInterveningAnswersThanAnyFixedBuffer - reverting the
store to "keep the last 8 arrivals" fails it immediately with "the declared answer
did not survive 64 intervening ones". That is the line between the ring-1024
stand-in and a bounded mechanism.
* AnAnswerNobodyDeclaredIsNotKeptAtAll - red if everything is retained.
* TheWindowRefusesToDeferOnALinkThatCannotRetainAnswers - on the in-process (ShmLink)
arm the knob at 4 must still block every create, red if the capability gate goes.
* The refused-create case now reads the repair that exists on THAT arm (the latch),
and asserts the window's own books are empty there.
NOT DONE, and named: the window has no unit gate on the STREAM arm yet - only the
device runs. Three rounds of this feature were green in the lanes while inert on the
device because the lanes and the device run different transports; the transport-level
retention gates close that for the store, and the window-level one should be built on
StreamClientPublicationTest's real stream session next.
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)。