mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-11-07 10:03:23 +09:00
7.0 KiB
7.0 KiB
P12:server 自有屏幕窗口
2026-09-29。依据 docs/Disaggregated/notes/p12/(PLAN-P12.md、裁定 INTEGRATOR-DECISIONS-P12.md ID-P12-1..15、验收 CROSSHOST-ACCEPTANCE.md / FCL-ACCEPTANCE.md)。本文只记实现所需的协议与行为;验证结果以那些文件里的运行证据为准。
范围
server 应用自己拥有一个窗口,把 client 渲染的帧直接上屏;client 不需要窗口。离屏(pbuffer)路径不变,两条路径每会话只有一条活跃。不在本阶段:X11 / Win32 / None 窗口种类的白名单(D8)、cached-app freezer 的完整处理、多 context、TLS、把触摸转发给 client。
配置
- client:
MOBILEGL_IPC_SURFACE=offscreen|server(默认offscreen)。server让eglCreateWindowSurface向远端 server 要它自己的窗口;没有远端 server 时忽略并说一次。 - server:进程是否拥有显示由宿主决定(
ServerDisplay钩子已安装 = 拥有)。fork 出的离屏 supervisor 不拥有。
线上格式(控制面修订 3)
WindowKind.ServerOwned = 7,只在CreateWindowSurface上合法;nativeToken恒为 0,没有任何指针过线(Rule G / H)。SetWindowHandle不得指名它(SurfaceRefusal::ServerOwnedOnSetWindowHandle)。SurfaceReply追加width、height(server 窗口实际尺寸)与refusal。SurfaceRefusal:NoServerDisplay(server 没有显示)、NoServerWindow(有显示但等不到窗口)、SurfaceModeMismatch(会话已被另一种表面模式闩定)、ServerOwnedOnSetWindowHandle。拒绝是具名的ok=false应答,不闩会话。- 修订号 2 → 3,
wireFingerprint随之移动:client 与 server 一起重装。 - client 的
SetWindowHandle不再无条件发送(server-owned 模式下不发)。
表面模式
会话的表面模式在第一次成功的表面创建时闩定:ServerOwned 窗口 → 上屏,pbuffer → 离屏;之后另一种具名拒绝(SurfaceModeMismatch)。client 自己命名的窗口(X11 / Win32 / None)既不闩也不拒(D8 未关)。
窗口租约与失窗
ServerDisplay持有窗口引用与租约;创建表面时 apply 线程按需取租约。窗口没有时等(有界),同时按SurfaceProgress心跳,client 读作"还在跑"而不是"活着但沉默"。- 失窗的顺序即契约(
surfaceDestroyed→Detach):apply 线程先让后端放掉窗口(Espryt 销毁 EGL surface / context,Magma 销毁 swapchain / VkSurface),再以Fatal{ServerWindowLost}闩会话,最后才结束租约、让回调返回。Detach有界 3 s,超时不抽走窗口。失窗请求在DrainRing每次 pop 之前也检查。 - 窗口属于显示进程,不属于会话:会话结束后窗口还在,下一个会话接着用;Espryt 的孪生对象与单元影子随会话清空(
DropEveryTwinForEndedServerSession),下一会话从空表开始。
几何
- client 请求
W×H:W,H > 0→ server 显示SurfaceHolder.setFixedSize(W,H),视图等比缩放、留黑边放进屏幕;0/0→setSizeFromLayout(窗口自己的大小)。请求是异步应答,server 等布局回报(ID-P12-9)。 - 请求尺寸的来源:
eglCreateWindowSurface的EGL_WIDTH/EGL_HEIGHT;两者缺省(SDL 就是这样)且传入了 native window 时,取ANativeWindow_getWidth/Height(Android、split 构建、server-owned)。缺这一步时游戏按自己的窗口渲染、server 窗口取屏幕尺寸,横屏游戏在竖屏窗口里被裁成左下角。 ResizeEGLWindowSurface走同一条几何请求,后端表面随窗口真实尺寸,reply 带回该尺寸,client 采用它(ID-P12-10)。kEventSurfaceChanged携带宽高(Espryt 以前只发格式,client 的默认帧缓冲会停在 512×512 占位值)。
死亡与 device-lost
- device-lost 只由对端挂断闩住,从不由超时闩住(CONTRACT-P6 §5.4 不变)。杀 server 进程后 client 在毫秒级闩住,随后每个 verb 被 DECLINED,
glGetGraphicsResetStatus报GL_UNKNOWN_CONTEXT_RESET。 - 闩锁的
DEVICE LOST说一次;闩住之后 DECLINED 的 verb 不再逐条打 ERROR(一个 FCL 游戏几千次/秒)。
server 的形态
- 进程内显示 server(
MobileGLDisplayActivity,进程:mglwin):TCP / unix 监听线程 + 会话线程(TcpSupervisor::Handoff::Thread),预认证 / Busy / DataBind 路由与 fork 形状是同一份代码;会话串行;后端按进程钉住(无MOBILEGL_BACKEND_TYPE时取第一个会话的);会话间ResetSessionLatch。以崩溃隔离换窗口可达:会话里一个未武装的SessionFail会带走整个显示进程。 - 离屏 supervisor(
MobileGLServerService):每会话 fork 一个子进程,子进程PR_SET_PDEATHSIG随 supervisor 去。 - 同一设备一个 server:显示 Activity 与离屏 service 互相替换(谁后启谁拥有端口)。Render Server 屏(
ServerControlActivity)是两种形态的入口,字段与 extras:listen/token/env(KEY=VALUE;KEY)/onscreen/start/stop。 - 停止推流中的进程内 server 时,会话在
loop.Stop()前放弃队列里剩余记录(AbandonQueuedRecords);正常的 client EOF 仍 drain 完。 - 转发日志:进程内 server 只有会话线程与选择加入的线程转发,且在释放日志互斥之后、在独立转发互斥下发送。
FCL 集成(FCL fork,不在本仓库)
- FCL 的界面一退后台就把游戏暂停;server 窗口要在屏幕上,FCL 必然在后台。版本设置
keepRunningInBackground("游戏退到后台时不暂停",默认关):强制 TextureView 并保留它的 SurfaceTexture,界面离开前台时不通知 SDL 暂停、不撤销 GLFW focused / visible。 - FCL 默认的 spawn(游戏进程派生 server 子进程)把自己的窗口指针发给 server,按 Rule H 被拒(
UnmigratedSurface):只能接外部 render server,MOBILEGL_IPC_CONTROL=tcp://127.0.0.1:<port>、MOBILEGL_IPC_DATA=stream、MOBILEGL_IPC_SURFACE=server;后端由mg_env.txt里的MOBILEGL_BACKEND_TYPE选。 - 时序:FCL 的游戏界面出现后要再等约 10 s 才能把 server 窗口切到前台。
不变量
- G1:pull 构建符号 0/0/0/0,
.text不变(全部新代码在MOBILEGL_BUILD_DISAGGREGATED下;EGLImpl的新分支还要__ANDROID__)。 - 离屏路径仍是门(gate 3 跑
--use-pbuffer)。RefuseCode只追加。 - 会话里 server-owned 表面之外的表面(
SurfaceModeMismatch)具名拒绝,会话继续。
仍开放
D8 窗口种类白名单(X11 / Win32 / None 仍被接受并把 token 强转为指针);freezer 的完整处理;多 context;TLS;server 窗口不转发输入;DirectGLES 的 g_Display/g_Surface/g_Context 仍是全局(每进程一个会话时不需要);修后的横屏 server 窗口没有重测。