mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-11-15 22:13:24 +09:00
Statics, reverse channel, Magma cross-role sites, EGL control plane, registry rekey, build/gate mechanics, the 15 BARRIER_PULLED fields' carrier rulings, and the dual-block mechanism's design recon.
229 lines
19 KiB
Markdown
229 lines
19 KiB
Markdown
# f0-egl — P5f §2.3 EGL / surface 控制面普查(只读)
|
||
|
||
> 基线 `feat/disaggregated @ 8b68b92c`(代码头 `25fba0d5`)。路径在 `MobileGL/` 下。
|
||
> 本文件只读普查,不改任何代码。对应 P5c 审计 G4 行(`docs/Disaggregated/ROADMAP.md:71`),
|
||
> 原计划记给 P6(`MobileGL/MG_Remote/CONTRACT-P5C.md:537`),P5f 计划 §2.3
|
||
> (`docs/Disaggregated/P5F-WIRE-COMPLETENESS.md:181-186`)将其收回 P5f 的包 `fc`。
|
||
|
||
---
|
||
|
||
## 1. 单槽邮箱机制(现状)
|
||
|
||
**机制本体。** 十二个 forwarder 共享同一条过线通道:
|
||
|
||
- `ServerLoop.h:220`:`using ControlWork = MobileGLResult (*)(void* user);` —— 一个**裸函数指针**
|
||
加一个 `void*`。注释(`ServerLoop.h:217-219`、`ServerLoop.cpp:784-787`)说明为何不是
|
||
`std::function`:析构路径不得分配(ID-8)。
|
||
- `ServerLoop.cpp:788-793`:每个 forwarder 用同一个无捕获 trampoline
|
||
`+[](void* user) { return static_cast<Args*>(user)->Run(); }` 和**指向调用者栈上 `Args` 的
|
||
指针**调用 `RunOnApplyThread`。
|
||
- `ServerLoop.cpp:645-707`(`RunOnApplyThread`):`m_callerMutex` 串行化多个投递者(:653);
|
||
在 `m_controlMutex` 临界区内发布 `{m_controlWork, m_controlUser, m_controlPending}` 四元组,
|
||
影子位 `m_controlPosted` 最后发(:685-694);发布后再敲门铃(:702-704,先发布后敲门是
|
||
Doorbell 的硬规则);然后阻塞在 `m_controlDone.wait`(:705),返回 `m_controlResult`(:706)。
|
||
- `ServerLoop.cpp:533-568`(`PumpControlRequest`):apply 线程在 drain 批之间取走请求、
|
||
执行 `work(user)`(:560)、回填结果并 notify(:561-567)。
|
||
- 泵点:`ApplyThreadMain` 主循环 :397-398 与退出前 :453;park 谓词 `ready`(:391-395)把
|
||
`ControlIsPending()` 列为唤醒条件之一。
|
||
- 重入臂:`RunOnApplyThread:651`——apply 线程自己投递就内联执行(拆构路径
|
||
`~BackendObject_DirectGLES` → `ReleaseEGLResources` 走这里)。
|
||
- 无线程时的行为:backend 活着而线程不在 → `Fatal{ApplyThreadNotRunning}`(:670-676);
|
||
backend 已不在 → 返回 `MOBILEGL_ERR_NOT_INITIALIZED`(:678-683)。
|
||
|
||
**邮箱字段**在 `ServerLoop.h:367-389`:`m_callerMutex` / `m_controlMutex` /
|
||
`m_controlPosted`(一位影子)/ `m_controlDone` / `m_controlWork` / `m_controlUser` /
|
||
`m_controlResult` / `m_controlPending` / `m_controlFinished`。
|
||
|
||
**今天为何能工作**:两个角色同进程、同地址空间。函数指针是双方都能执行的代码地址;
|
||
`&args`、`major`/`minor`、`&handle` 指向的栈帧由阻塞握手(:705 的 wait)保证在整个执行期间存活;
|
||
单槽够用是因为"verb barrier 同一时刻只留一个 client 线程可运行"(`ServerLoop.h:367-368`
|
||
注释),加上 `m_callerMutex` 兜底。跨进程后这三样全部失效:代码地址、栈地址、以及"阻塞等待
|
||
同进程另一个线程"本身。
|
||
|
||
**P5f 处置建议**:邮箱机制在 inproc 下整体保留(它是 apply 线程归属语义的一部分),但在其上
|
||
叠一层"帧化"接缝:每个 forwarder 的参数先收敛成一个**纯值结构**(见 §4 草案),inproc 臂把该
|
||
结构放在栈上传指针(现状等价),spawn 臂把同一结构编码进 `SurfaceOp` 帧。这样 fc 包不动
|
||
线程模型,只动载荷的形状。
|
||
|
||
## 2. 十二个 forwarder 逐个点名
|
||
|
||
声明在 `ServerLoop.h:459-475`,定义在 `ServerLoop.cpp:802-1033`。下表"载荷"一列标出
|
||
**值**(可直接序列化)/ **栈上指针**(指向调用者存储)/ **句柄值**(client 进程的 EGL 句柄,
|
||
跨进程无意义)。
|
||
|
||
| # | forwarder(定义行) | 签名 | 语义 | 载荷分类 |
|
||
|---|---|---|---|---|
|
||
| 1 | `ServerInitializeEGLDisplay`(`ServerLoop.cpp:802-816`) | `Bool (EGLDisplay dpy, EGLint* major, EGLint* minor)` | 初始化 server 后端的 EGL display,回写主/次版本 | dpy 句柄值;**major/minor 是指向 client 栈的 out 指针**(:805-806) |
|
||
| 2 | `ServerCreateEGLWindowSurface`(:818-834) | `Bool (EGLSurface surface, const WindowHandle& handle)` | 建窗口 surface;成功后 `ForgetCurrentTuple()`(:829,N-3) | surface 句柄值;**`&handle` 是指向 client 存储的指针**(:821/:832),且 `WindowHandle::Handle` 本身是 `void*`(`BackendObject.h:561-566`) |
|
||
| 3 | `ServerResizeEGLWindowSurface`(:836-850) | `Bool (EGLSurface, Uint32 w, Uint32 h)` | 重建 swapchain | 全为值。**最干净的一个** |
|
||
| 4 | `ServerCreateEGLPbufferSurface`(:852-870) | `Bool (EGLSurface, EGLint w, EGLint h)` | 建 pbuffer;成功后 `ForgetCurrentTuple()`(:865) | 全为值 |
|
||
| 5 | `ServerMakeEGLCurrent`(:872-918) | `Bool (EGLDisplay, EGLSurface draw, EGLSurface read, EGLContext ctx)` | 经 `ApplyMakeCurrent`(:269-316)三分支:NativeBind / RepeatNoOp / ClientRelease(ID-54);真绑定后重发布 caps 快照(:899-913,R-12) | 四个全是句柄值 |
|
||
| 6 | `ServerSwapEGLBuffers`(:920-933) | `Bool (EGLDisplay, EGLSurface draw)` | 直接在 server 上 present | 句柄值。**无生产调用者**(见 §5 发现 F8) |
|
||
| 7 | `ServerSetEGLSwapInterval`(:935-946) | `void (Int interval)` | 设置交换间隔;返回值被丢弃(:945) | 值 |
|
||
| 8 | `ServerReleaseEGLSurface`(:948-965) | `void (EGLSurface)` | 释放 surface;`ForgetCurrentTupleIfItNames`(:960,N-3) | 句柄值 |
|
||
| 9 | `ServerReleaseEGLResources`(:967-989) | `void ()` | **按契约阻塞**(:967-973 注释:之后 `MobileGL::Destroy()` 立即走);`ForgetCurrentTuple()`(:984) | 无参数 |
|
||
| 10 | `ServerInitCapabilities`(:991-1007) | `Bool ()` | 跑 server 端 `InitCapabilities` 并 `PublishCapsSnapshot()`(:999-1002) | 无参数 |
|
||
| 11 | `ServerInitWindowSurface`(:1009-1020) | `Bool ()` | server 端 `InitWindowSurface` | 无参数。**无任何调用者**(见 §5 发现 F8) |
|
||
| 12 | `ServerSetWindowHandle`(:1022-1033) | `void (const WindowHandle& handle)` | 把窗口句柄交给 server 后端 | **`&handle` 是指向 client 存储的指针**(:1024/:1031),内含 `void*` |
|
||
|
||
参数里的**函数指针**:每一条都是同一个 trampoline(:790-791),它承载的唯一信息是"调哪个
|
||
`Args::Run`",即**操作的身份**。参数里的**栈上 `void*`**:`&args` 永远存在(:792),其内部再
|
||
含 client 栈指针的是 #1(major/minor)、#2 与 #12(handle)。
|
||
|
||
**今天为何能工作**:同进程;阻塞握手(`RunOnApplyThread:705`)使调用者栈帧在整个 RPC 期间
|
||
存活,所以 out 指针与引用参数安全。EGL 句柄值两边通用是因为同一个驱动在同一进程里认它们。
|
||
|
||
**P5f 处置建议(共性)**:
|
||
- 函数指针 → `SurfaceOp.kind` 枚举(操作身份显式化)。
|
||
- out 指针 → 帧的 reply 字段(#1 已被 `SurfaceReply.eglMajor/eglMinor` 覆盖)。
|
||
- `WindowHandle` → `windowKind` + `nativeToken` + `width`/`height` 值字段;`Handle` 指针在
|
||
Android 上是 `ANativeWindow*`(`P6-SPAWN-PLAN.md:38`),**子进程无意义,真窗口到达是 P12**,
|
||
P5f 只在 server 侧落具名拒绝 `Fatal{UnmigratedSurface, "AndroidNativeWindow@P12"}`
|
||
(`P6-CONTRACT-DRAFT.md:43` 已预写这个 Fatal 名)。
|
||
- EGL 句柄值 → client 铸造的 token(`DisplayToken/SurfaceToken/ContextToken`,
|
||
`P6-CONTRACT-DRAFT.md:41` 已裁定:client 铸造、从 1 起稠密、session 内不复用,server 持
|
||
token → native 映射,永远不见 client 的 EGL 值)。`protocol.fbs:189-190` 的
|
||
`display/surface: ulong` 已经就是 token 的宽度。
|
||
|
||
## 3. client 侧调用面(`BackendObject_Remote.cpp`)
|
||
|
||
九个 EGL 虚函数的路由(:196-310),核心纪律是"先转发,再跑基类"(:198-201 注释):
|
||
|
||
- `InitializeEGLDisplay`(:203-207)→ forwarder #1。
|
||
- `CreateEGLWindowSurface`(:209-225)→ 先 #12(:214,"唯一告诉 server 窗口是哪扇的途径"),
|
||
再 #2(:215),随后 `DrainPublishedEvents()`(:223)——surface-changed 事件走 SEG_EVENT。
|
||
- `ResizeEGLWindowSurface`(:227-234)→ #3 + drain(:232)。
|
||
- `CreateEGLPbufferSurface`(:236-243)→ #4 + drain(:241)。
|
||
- `MakeEGLCurrent`(:245-276)→ #5,成功后 `PumpControlPlane()` + drain + 刷新格式能力
|
||
(:265-274,codex 12:把 R-12 的"第二次到达即失效"闭合在造成它的那次 make-current 上)。
|
||
- `SwapEGLBuffers`(:278-287)→ **不转发**:present 走记录(class-B `Present` emitter),
|
||
注释明确 #6 "在 P5 没有调用者,这是刻意的"(:284-285)。
|
||
- `SetEGLSwapInterval`(:289-297)→ #7。覆写的理由:基类会调 class-C 的 `SetSwapInterval`
|
||
槽而 Fatal(:291-295 注释)。
|
||
- `ReleaseEGLSurface`(:299-302)→ #8。
|
||
- `ReleaseEGLResources`(:304-310)→ #9,按契约阻塞。
|
||
- 另有 `InitCapabilities`(:106-142)调 #10(:125);`InitWindowSurface`(:144-149)是
|
||
**纯 client no-op,不调 #11**。
|
||
|
||
**每个 forwarder 调用前的 fence**:`WaitForApplyBeforeEglForwarder`(:189-193),对除
|
||
#6 外的每一次转发先 `WaitForApplyToCatchUp`。理由(:176-187 注释、CONTRACT-P5E §2.5
|
||
`CONTRACT-P5E.md:177-180`):邮箱在 drain 批之间被泵,run-ahead 下控制请求会插进两条
|
||
在飞记录之间。client 入口侧:九个虚函数由 `MG_Impl/EGLImpl/EGLImpl.cpp` 的 EGL 入口调
|
||
(如 eglMakeCurrent → :284、eglSwapBuffers → :178、eglSwapInterval → :448、eglTerminate 路径
|
||
→ :328)。
|
||
|
||
**P5f 处置建议**:这个 fence 在 spawn 下**仍然是必要的**——socket 控制通道与 SEG_CMD 是两条
|
||
通道,控制帧一样可以超过在飞记录。fc 包的帧化必须保留"发帧前先等 `appliedSeq` 追上
|
||
`LastPublishedSeq`"这一步;它不属于 wire 格式而属于发送纪律,应写进 fc 的契约行。
|
||
|
||
## 4. schema 对照与 wire 帧设计草案
|
||
|
||
### 4.1 现有 schema(`Protocol/protocol.fbs`,已定但无人使用)
|
||
|
||
- `SurfaceOpKind`(:166-175):`None / InitializeDisplay / CreateWindowSurface /
|
||
CreatePbufferSurface / ResizeWindowSurface / ReleaseSurface / MakeCurrent / ReleaseCurrent`,
|
||
共 7 个有效枚举。
|
||
- `WindowKind`(:177-184):`None / AndroidNativeWindow / X11 / Win32Hwnd / Surfaceless /
|
||
Pbuffer`。
|
||
- `SurfaceOp`(:186-196):`seq, kind, display, surface, windowKind, nativeToken, width,
|
||
height, swapInterval`。`nativeToken` 注释自述"Android transfers the window out of band"。
|
||
- `SurfaceReply`(:198-204):`seq, ok, eglMajor, eglMinor, defaultFb`。
|
||
- 信封:`CtrlMsg` union(:273-284)已含 `SurfaceOp/SurfaceReply`;`CtrlEnvelope`(:286-288)。
|
||
|
||
### 4.2 映射表:12 forwarder → op
|
||
|
||
| forwarder | 映射 | 缺的枚举 / 字段 |
|
||
|---|---|---|
|
||
| #1 InitializeEGLDisplay | `InitializeDisplay`;reply 的 `eglMajor/eglMinor` 正是 major/minor out 参数的线形 | 无 |
|
||
| #2 CreateEGLWindowSurface | `CreateWindowSurface` + `windowKind`/`nativeToken`/`width`/`height` | 无(P12 前 server 具名拒绝 `AndroidNativeWindow`) |
|
||
| #3 ResizeEGLWindowSurface | `ResizeWindowSurface` + `width`/`height` | 无 |
|
||
| #4 CreateEGLPbufferSurface | `CreatePbufferSurface` + `width`/`height` | 无 |
|
||
| #5 MakeEGLCurrent | `MakeCurrent`;其 ClientRelease 臂(`ClassifyEglMakeCurrent`,:257-267)落 `ReleaseCurrent` | **`SurfaceOp` 缺 `readSurface` 与 `context` 两个字段**:MakeCurrent 的语义单元是 (dpy, draw, read, ctx) 四元组,schema 只有一个 `surface` 且没有 context 字段 |
|
||
| #6 SwapEGLBuffers | **不配帧**:present 走记录(`BackendObject_Remote.cpp:278-287`) | — |
|
||
| #7 SetEGLSwapInterval | 用 `swapInterval` 字段 | **缺 `SetSwapInterval` 枚举** |
|
||
| #8 ReleaseEGLSurface | `ReleaseSurface` | 无 |
|
||
| #9 ReleaseEGLResources | 无参数,阻塞 | **缺 `ReleaseResources` 枚举** |
|
||
| #10 InitCapabilities | **不配 SurfaceOp**:应答物是 `CapsSnapshot` 帧(server 已在 :999-1002 发布;`P6-CONTRACT-DRAFT.md:88-89` 同此裁定) | —(但 client 需要一个"等下一帧 CapsSnapshot"的同步点,见 4.4) |
|
||
| #11 InitWindowSurface | **不配帧**:client 侧 no-op(:144-149),无调用者 | — |
|
||
| #12 SetWindowHandle | 用 `windowKind`/`nativeToken`/`width`/`height` | **缺 `SetWindowHandle` 枚举** |
|
||
|
||
缺的三个枚举与 `P6-SPAWN-PLAN.md:76` 的预测("预期缺 `SetSwapInterval`/`ReleaseResources`/
|
||
`SetWindowHandle`")**逐字吻合,本普查确认**。9 个需映射的 forwarder = 7 个现有枚举 + 3 个新增
|
||
枚举 - 1 个复用(ReleaseCurrent 由 #5 的释放臂复用),与 `P6-CONTRACT-DRAFT.md:87-90` 的
|
||
"the remaining nine map onto the seven plus §1's additions" 一致。追加枚举必须 append-only
|
||
(schema 自述纪律在 :268-272 的 union 注释与 `CapsSnapshot` 的 deprecated 先例 :115-127)。
|
||
|
||
另外发现 **schema 缺的字段**(P6 计划未预写):
|
||
1. `SurfaceOp` 缺 `readSurface`(MakeCurrent 需要 draw≠read 的场景)。
|
||
2. `SurfaceOp` 缺 `context` token 字段(MakeCurrent/ReleaseCurrent 都要指名 ctx)。
|
||
3. `WindowKind` 与 `WindowBackend`(`BackendObject.h:551-559`:Android/X11/MetalLayer/Win32)
|
||
**不互洽**:`WindowKind` 缺 `MetalLayer`,多 `Surfaceless`/`Pbuffer`(那两个不是窗口后端)。
|
||
需要一张显式映射表,不能按整数值直译。
|
||
|
||
### 4.3 帧设计草案(逐 forwarder)
|
||
|
||
公共规则:`seq` 由 client 单调铸造,`SurfaceReply.seq` 回声;携带 `ok=false` 等价今天的
|
||
`Bool false` 或 void 丢弃结果;server 侧答不出来等价 `ServerBackendOrNull()` 为 null
|
||
(:798)时的 `MOBILEGL_ERR_NOT_INITIALIZED`。token 化按 `P6-CONTRACT-DRAFT.md:41`。
|
||
|
||
| op 帧 | 载荷 | reply | 阻塞性 |
|
||
|---|---|---|---|
|
||
| `InitializeDisplay` | `display`(token) | `ok` + `eglMajor/eglMinor` | 阻塞(今天返回 Bool) |
|
||
| `CreateWindowSurface` | `surface`(token) + `windowKind` + `nativeToken` + `width/height` | `ok` + `defaultFb`(见 4.4 的路线冲突) | 阻塞。可并入 #12(见下) |
|
||
| `ResizeWindowSurface` | `surface` + `width/height` | `ok` + `defaultFb` | 阻塞 |
|
||
| `CreatePbufferSurface` | `surface` + `width/height`(`windowKind=Pbuffer`) | `ok` | 阻塞 |
|
||
| `MakeCurrent` | `display` + `surface`(draw) + **新字段 `readSurface`** + **新字段 `context`** | `ok` | 阻塞 |
|
||
| `ReleaseCurrent` | `display`(其余 token 为 0 合法,`P6-CONTRACT-DRAFT.md:41` 已许) | `ok` | 阻塞(今天经同一个 Bool 通道) |
|
||
| `SetSwapInterval`(新枚举) | `surface` + `swapInterval` | 可无 reply | **可改 fire-and-forget**:今天虽经阻塞邮箱但结果被丢弃(:945) |
|
||
| `ReleaseSurface` | `surface` | 可无 reply | 可 fire-and-forget(:964 丢结果);但须保序 |
|
||
| `ReleaseResources`(新枚举) | 无 | `ok` | **必须阻塞**:`ServerLoop.cpp:967-973` 的契约,`Destroy()` 在它返回后立即走 |
|
||
| `SetWindowHandle`(新枚举) | `windowKind` + `nativeToken` + `width/height` | 可无 reply | 可 fire-and-forget,**但必须在后续 `CreateWindowSurface` 之前生效**——同一有序通道即保证 |
|
||
|
||
两个可合并项(供 fc 包定夺,本普查只摆出):① `CreateWindowSurface` 的 schema 字段已自带
|
||
`windowKind/nativeToken/width/height`,#12 可以并进 #2,`SetWindowHandle` 枚举只在"先给窗口
|
||
后建 surface"的时序必须拆开时才需要——今天的代码就是拆开的(:214-215),保守做法是两者都
|
||
落;② `ReleaseCurrent` 也可用 `MakeCurrent` + 全 0 token 表达,现有枚举分开更好,保留。
|
||
|
||
### 4.4 三个与 schema 相邻的副作用,必须在 fc 的契约里点名
|
||
|
||
1. **caps 重发布**:#5 的真绑定臂(:899-913)和 #10(:999-1002)都会让 server 主动推一帧
|
||
`CapsSnapshot`。跨进程后这是 server 自发帧;client 今天的吸收点(RPC 返回后
|
||
`PumpControlPlane()`,:266-272 与 :130)天然对应"reply 之后泵控制通道"。schema 无需加
|
||
字段,但 fc 应写明"MakeCurrent reply 到达后、下一帧 CapsSnapshot 到达前"的语义窗口。
|
||
2. **`SurfaceReply.defaultFb` 与 SEG_EVENT 是重复路线**:P5c 已把默认 FBO 形状做成
|
||
surface-changed 事件,client 在 create/resize reply 后 `DrainPublishedEvents()`
|
||
(:223/:232/:241)。`defaultFb` 字段(:154-160, :203)若启用是第二载体。建议 fc 二选一
|
||
并写明,不要两个都活(`CapsSnapshot` 的 deprecated 字段教训,:101-127,就是两种拼法
|
||
并存的代价)。
|
||
3. **server 死亡的具名化**:今天的 `Fatal{ApplyThreadNotRunning}`(:670-676)在 spawn 下
|
||
对应 `FatalCode::ServerCrashed`(:240 已分配,`P6-CONTRACT-DRAFT.md:44` 已点名 P6 是
|
||
第一个能产生它的阶段)。fc 的 client 侧发送路径要把"发不出去/等不到 reply"接到这个码上。
|
||
|
||
## 5. 其他发现
|
||
|
||
- **F8:两个 forwarder 没有生产调用者。** `ServerSwapEGLBuffers` 只有单测调用
|
||
(`MG_Test/Wire/ServerLoopTest.cpp:1549`、`:1568`);`ServerInitWindowSurface` 全树无调用者
|
||
(client 的 `InitWindowSurface` 是纯 no-op,`BackendObject_Remote.cpp:144-149`)。
|
||
处置建议:fc 不为它们配帧(§4.2 已按此映射);`ServerLoop.h:469`/`:474` 的声明与
|
||
实现在 spawn 世界只剩 inproc 测试价值,fc 可具名删除或标注 inproc-only。
|
||
- **F9:文档行号漂移。** `ROADMAP.md:71`(P5c 审计 G4 行)引 `ServerLoop.cpp:543-598`,
|
||
当前 HEAD 的邮箱是 `:533-568`(PumpControlRequest)+ `:645-707`(RunOnApplyThread);
|
||
`CONTRACT-P5C.md:537` 把控制面记给 P6,P5f 计划 §2.3(`P5F-WIRE-COMPLETENESS.md:181-186`)
|
||
已改记 P5f 的包 `fc`(`P5F-WIRE-COMPLETENESS.md:252`)。处置建议:fc 落地时顺手刷新
|
||
ROADMAP G4 行的行号与去向。
|
||
- **F10:`ServerLoop.h:436` 与 `ServerLoop.cpp:968` 引的 `EGLImpl.cpp` 行号**(:284/:326)
|
||
与当前 `MG_Impl/EGLImpl/EGLImpl.cpp` 基本相符(:284 是 eglMakeCurrent;ReleaseEGLResources
|
||
实际在 :328)。仅为记录,不构成缺陷。
|
||
|
||
## 6. 结论汇总
|
||
|
||
1. 十二个 forwarder 的过线载荷里,真正"不可序列化"的只有两类:**trampoline 函数指针**
|
||
(操作身份 → `SurfaceOp.kind`)和**三处 client 栈/堆指针**(#1 的 major/minor → reply 字段;
|
||
#2/#12 的 `WindowHandle*` → windowKind+nativeToken 值字段,Android 的 `ANativeWindow*`
|
||
归 P12,P5f 只落具名拒绝)。其余参数全是值或 client 句柄值(→ token)。
|
||
2. schema 缺口清点确认:**缺 3 个枚举**(SetSwapInterval / ReleaseResources /
|
||
SetWindowHandle,与 P6-SPAWN-PLAN 的预测逐字一致)+ **缺 2 个字段**(MakeCurrent 的
|
||
readSurface 与 context token)+ WindowKind/WindowBackend 需要显式映射表。
|
||
3. #6、#10、#11 不配 SurfaceOp 帧:present 走记录、InitCapabilities 走 CapsSnapshot、
|
||
InitWindowSurface 无调用者。需要帧化的是 9 个 forwarder、10 种 op(含 ReleaseCurrent)。
|