mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-11-07 10:03:23 +09:00
5.9 KiB
5.9 KiB
P10:sync / query / present 节奏
2026-09-29。依据 docs/Disaggregated/notes/p10/(计划与范围重划 PLAN-P10.md、裁定 INTEGRATOR-DECISIONS-P10.md)。本文只记实现所需的协议与行为;验证结果以 notes 里的运行证据为准。
范围
fence 轮询不再是往返(§1);最后一个 class-C 与 split 覆盖(§2);credit 与往返的测量记录(§3)。路线图原文里已做完或前提已变的项(client 铸造 query handle、present 1:1、credit 默认 1、无 present 负载的 SEG_STAGE 饥饿)见计划 §1。
1. fence 轮询从 server 的报告作答(A)
线上格式
EventKind追加kEventFenceSignaled = 5,载荷EventFenceSignaledHead { EventHandle Fence; }(8 字节):server 看到该 client fence 已 signaled,每个 fence 至多一条。opcode 与控制协议修订号不变。
server
ServerVerbSink按创建顺序记下尚未报告的 fence(FenceCreate入队;被删的出队时跳过)。ReportSignaledFences(flush)从最老的一个开始查,查到第一个未完成的就停(同一 context 的 GPU 按提交顺序完成),每个已完成的发一条kEventFenceSignaled并立即发布。OnPresent在归还 present credit 之前调用一次(状态查询,不提交):credit 放行的 client 会立刻去等一两帧前的 fence,报告必须先于放行。- apply 线程在每个 drain 批次之后调用(后端表
GL.GetSyncStatus,不提交);将要空闲且还有未报告的 fence 时先调一次flush=true——最老的 pending fence 改用GL.ClientWaitSync(GL_SYNC_FLUSH_COMMANDS_BIT, 0),这是两个后端都会提交所持命令的唯一后端无关方式——仍有未报告的就限时停车 1 ms(kFenceIdlePollMs)后再查,否则照旧无限期停车。SEG_EVENT满时不报告(与既有"环满不 apply"同一规矩)。 - 与后端无关:两个后端都实现
GL.FenceSync / GetSyncStatus / ClientWaitSync。没有原生 fence 的(FenceSync为空或返回空)视为已 signaled。
client
- 事件排空时记下已报告的 fence(
{slot → gen},独立互斥锁:排空发生在持有g_fenceMutex的发射函数内部);往返答"已 signaled"的也记下;glDeleteSync时忘掉。 glClientWaitSync/glGetSynciv(GL_SYNC_STATUS)先做一次轮询入口(ClientSession::PollEntry:刷出已发布的记录、排空反向通道),然后:- 已知 signaled → 本地答
GL_ALREADY_SIGNALED/GL_SIGNALED; - 未知且调用不能等(timeout = 0、
glGetSynciv)→ 本地答GL_TIMEOUT_EXPIRED/GL_UNSIGNALED;同一 fence 连续第 N 次(MOBILEGL_IPC_POLL_ESCALATE,默认 64)改为一次真往返,计数归零; - 未知且调用可以等(timeout > 0)→ 真往返,语义不变。
- 已知 signaled → 本地答
MOBILEGL_IPC_POLL_ESCALATE=0:每次轮询都往返,即 P10 之前的行为(A/B 臂)。设备丢失后一律走往返臂(其 DECLINED 答复是既有的 no-op)。- 计数:
ReadFencePollCounters()(本地作答、升级、往返、server 报告),经SplitRuntimePeek给车道。
门
FencePollScenario(两后端 × 单进程 / Split / Spawn / Tcp):零超时轮询循环有界结束(带 / 不带 flush 位,后者仅 split);已 signaled 的 fence 轮询不产生回复槽记录;只轮询不发记录时,空闲 server 仍会报告后来完成的 fence(无升级)。单元控制RemoteClientControls.AnUnreportedFencePollCrossesOnlyEveryPollEscalateTimes钉住"第 N 次才过线"。- red-once:
POLL_ESCALATE=0→ 200 条回复槽记录 / 200 次往返;去掉空闲复查 → Espryt 需 1 次升级、Magma 升级 15 次仍等不到(Magma 的 fence 不会自己提交);client 关掉本地作答 → 单元控制 exit 163。
测量
PacedFrameFenceWaitsBench(手动开启,不设门)的配对结果在 A-FENCE-POLL.md:Magma 走 tcp 时每帧耗时降 37%、fence 往返为 0;Espryt 在主机 llvmpipe 上没有收益,因为只有阻塞等待才能让 fence 可见。在 Adreno 830 上两后端都是 0 次往返、598 / 598 次本地作答(C-MEASUREMENTS.md §1)。
2. 最后一个 class-C 与 split 覆盖(B)
- 发射表的
SetSwapInterval槽 = server 转发器(与eglSwapInterval经SurfaceControlOp::SetSwapInterval的同一路);SetSwapInterval_Unmigrated与BackendObject_Remote的覆盖删除。class C 为空:69 已实现 / 0 未迁移。单元控制:ServerLoopTest.TheEmitTableSwapIntervalSlotCrossesAsOneDispatchedFrame(经表调用,恰好一个控制帧到达)。 - caps mirror 采纳 server 快照时,
GL_KHR_parallel_shader_compile按 client 的MOBILEGL_ASYNC_SHADER_COMPILE重算:编译池与GL_MAX_SHADER_COMPILER_THREADS_KHR都在 client。red-once:去掉重算 → 两后端 Tcp 的AsyncOff扩展串用例红。 - 登记(全部进门):
AsyncCompileScenario(AsyncOn./AsyncOff./OptimisticShaderStatus.)与XfbPrimitiveQueryScenario× 两后端 × Split / Spawn / Tcp;PrimitivesGeneratedNoXfbScenario只在 Magma(PrimGen.三臂,PrimGenReroute.仅 Split / Spawn:tcp 的共享 server 读不到改道开关)。red-once:client 查询结果加一 → 101 条里 49 条红(全部 XfbQuery / PrimGen / PrimGenReroute),同名 monolith 条目仍绿。
3. 测量(只记录)
见 C-MEASUREMENTS.md,包括真机 fence bench、设备上 TCP loopback 的 PRESENT_CREDIT 1/2/3、以及主机 retrace 往返普查。在 loopback 上 credit 没有噪声以外的影响(rd12 51.4 / 51.6 / 52.4 fps),默认值保持 1。
不变量
- G1:新代码全在
MOBILEGL_BUILD_DISAGGREGATED下(MG_Remote、Config的 IPC 段)。 EventKind只追加;控制协议修订号不变(3)。