在多窗口模式下保持渲染上下文
解读
国内主流 Unity 项目(MMO、SLG、XR 仿真、数字孪生)为了**“一屏主控、多屏输出”(例如驾驶舱+大屏+副屏、主播端+弹幕窗、VR 头显+监控窗口),普遍采用“多 Camera 多窗口”**而非多进程。
面试官真正想确认的是:
- 你是否理解 Unity 的单线程渲染循环与**图形上下文(Graphics Context)**绑定规则;
- 能否在 Windows Standalone、UWP、Android 分屏、WebGL 多 Canvas 等国内常见发布平台下,保证上下文不丢失、资源不重复、性能不爆炸;
- 是否具备工程级落地经验:热更资源、后处理、SRP、HDR、RT 句柄、抗锯齿、深度缓冲在多窗口之间的共享与隔离策略。
一句话:让 N 个窗口“看起来各玩各的”,底层却共享同一份渲染上下文,不闪不炸不掉帧。
知识点
- Unity 渲染线程模型
- 主线程:Scene 更新、C# 脚本;
- 渲染线程:向图形 API 提交命令;
- 图形上下文(GL context / D3D Device Context)每进程唯一,但每个窗口需要** SwapChain / Surface**。
- Camera.targetDisplay 与 Display.Activate
- Standalone 下最多 8 个 Display,索引 0~7;
- 调用
Display.Activate(width,height,refreshRate)激活第二块屏,必须在启动后第一帧之前完成,否则 Windows 会强制新建 DXGI 输出,导致上下文重建。
- RenderTexture 中转方案
- 主 Camera 输出到 RT,Blit 到各窗口的额外 Camera(只负责全屏 Quad),避免多个 Camera 同时向不同 SwapChain 提交,减少上下文切换。
- CommandBuffer & ScriptableRenderContext
- 在 URP/HDRP 下,用
ScriptableRenderContext.EmitGeometryForCamera把同一帧数据复用到多个窗口,节省 Culling。
- 在 URP/HDRP 下,用
- 平台差异
- Windows:Direct3D11 支持多 SwapChain 共享同 Device;OpenGL 需要
wglShareLists手动共享,Unity 已封装,但线程锁开销大; - Android:7.0+ 支持分屏多窗口,但SurfaceView/TextureView 生命周期与 Activity 同步,onPause 会强制释放上下文,需在
OnApplicationPause(false)里重新调用 Display.Activate; - WebGL:多 Canvas 对应多 WebGL Context,无法共享(WebGL 1.0 规范限制),必须主线程一次性渲染到多个 RT 后,用
texSubImage2D分块上传到不同 Canvas,内存翻倍; - iPadOS 多任务:Unity 默认只认一个
UIWindow,需原生插件新建UIWindow并绑定CAMetalLayer,Unity 2022.1+ 官方示例MultiWindowUIKit已给出** Metal 共享 Texture** 方案。
- Windows:Direct3D11 支持多 SwapChain 共享同 Device;OpenGL 需要
- 资源与性能
- 后处理(Bloom、AO) 默认随 Camera 实例化,多窗口会N 份全屏 RT,需自定义
VolumeManager只计算一次,再 Blit 到各窗口; - 抗锯齿 只在主 Camera 做 MSAA,其余窗口用 FXAA 或 SMAA 作为后处理全屏;
- 热更资源(Addressables) 的贴图、Mesh 在显存中全局唯一,但 Shader 变体收集需把多窗口的 Camera 都加入
ShaderVariantCollection录制列表,否则首次切换窗口会卡顿。
- 后处理(Bloom、AO) 默认随 Camera 实例化,多窗口会N 份全屏 RT,需自定义
答案
“我们在《XXX 数字孪生驾驶舱》项目中需要 1 个主控 4K 窗口+3 个 1080P 监控窗口。
- 启动阶段:
- 在第一条 Awake 里用
Screen.SetResolution(7680,1080,FullScreenMode.Windowed)把 Windows 横向拼成 7680×1080,再用 Win32 APISetWindowPos把 4 个窗口虚屏拆分到 3 块显卡输出口,保证 Display 索引 0~3 在第一帧前激活,避免 DXGI 重建上下文。
- 在第一条 Awake 里用
- 渲染阶段:
- 主 Camera 开启
allowHDR=1、MSAA=4,输出到 4K RT(R16G16B16A16_SFloat); - 三个副窗口各挂一个仅全屏 Quad 的 Camera,Layer 设为单独
UIWindow,用CommandBuffer.Blit把主 RT 拷贝到各自 RT,分辨率 1080P、FXAA 抗锯齿; - 在 URP RendererFeature 里插入
CustomBlitPass,利用ScriptableRenderContext复用 Culling 结果,节省 30% CPU; - 后处理体积(Bloom、ColorAdjust)只在主 Camera 计算一次,通过
GlobalVolume共享,显存 RT 从 8 张降到 2 张。
- 主 Camera 开启
- 生命周期:
- Windows 下监听
WmDeviceChange,显卡热插拔时调用QualitySettings.SetQualityLevel强制重建 SwapChain,但不重启进程; - Android 分屏场景在
OnApplicationPause里把副窗口 Camera 的targetTexture设为null,暂停渲染,避免 GL 上下文丢失崩溃; - WebGL 端 fallback 到单线程双 Canvas,主线程交替
gl.bindFramebuffer,帧率锁 30 FPS,保证不掉帧。
- Windows 下监听
- 结果:
- 4 窗口同时跑 60 FPS,GPU 占用 RTX3060 仅 58%,内存比 naive 方案下降 42%,通过信通院 72 小时稳定性测试。”
拓展思考
- Unity 2023 的 GPU Resident Drawer 把 RenderState 常驻显存,多窗口能否共享?
- 目前每个
Camera仍独立DrawShadows,需要 SRP 团队后续暴露SharedCullingData接口,可提前关注官方 Roadmap。
- 目前每个
- HDRP 的 DLSS/FSR 只支持主视图,副窗口若强行开启会黑屏,需自定义
DynamicResolutionHandler把主 Camera 的深度+运动矢量 RT 拷贝到副窗口,手动调用 DLSS Plugin 的Execute方法,显存再省 20%。 - 国内信创环境(统信UOS+麒麟+国产显卡) 仅支持 OpenGL 3.x 无多 SwapChain 扩展,必须回退到单窗口多 RT+本地 framebuffer blit,帧率上限 30 FPS,面试时可主动提及**“国产显卡驱动白名单”**适配经验,体现落地能力。