3. 多屏渲染概述

多屏渲染并不是“把同一画面复制到多个显示器”这么简单。它本质上是在一个渲染循环中,为多个物理或逻辑显示目标生成各自正确的图像,并保证它们之间的时间同步、空间关系和内容一致性。理解多屏渲染,首先要理解不同应用场景对“屏”的定义、对“同步”的要求、对“性能”的约束各不相同。本节从拼接屏、扩展屏、VR/AR双屏和车载多屏四类典型场景出发,分析它们的渲染需求与工程差异。

一、核心原理:多屏渲染的三个基本维度

无论哪种多屏形态,都可以从三个维度拆解:显示目标、空间关系和时间关系。显示目标决定有几个帧缓冲、几个视口、几个 EGLSurface;空间关系决定每个屏幕显示的是同一画面的不同区域,还是不同视角,还是完全独立的内容;时间关系决定多屏之间是否需要严格同帧、是否允许一帧延迟、是否要求垂直同步对齐。

多屏渲染的核心公式可以概括为:
总渲染负载 = 屏幕数量 × 单屏分辨率 × 单屏像素复杂度 × 同步开销系数
其中同步开销系数在拼接屏和 VR 场景中显著大于 1,在扩展屏和车载多屏中则取决于是否共享同一 GPU 上下文。

在 OpenGL ES 中,多屏通常通过多 EGLSurface、多视口(glViewport)、多帧缓冲对象(FBO)以及共享上下文(EGL_CONTEXT_CLIENT_VERSION + share_context)来实现。关键问题是:哪些屏幕可以共享同一上下文,哪些必须独立上下文,以及如何避免每帧重复上传纹理和重复计算。

多屏渲染四类典型场景与需求对比 拼接屏 同一画面分块 · 严格同帧 扩展屏 独立内容 · 可异步 桌面/窗口系统管理 VR/AR 双屏 左右眼视差 · 极低延迟 畸变校正 · 预测追踪 车载多屏 仪表/中控/HUD/后排 安全等级 · 功能隔离 同步要求 独立要求 帧级硬同步 眼级同步 + 预测 安全域隔离 窗口级异步 关键工程指标 拼接屏:几何对齐误差 < 1px,帧差 < 1 帧,边缘融合带宽 VR/AR:MTP 延迟 < 20ms,双眼渲染差异 < 1ms,畸变网格精度 车载:功能安全 ASIL 等级,启动时间 < 2s,多屏独立刷新率

二、拼接屏:空间连续性与帧同步

拼接屏由多块物理屏幕按矩阵排列组成一个逻辑大屏。它的核心需求是空间连续性:相邻屏幕之间的画面必须无缝衔接,不能出现错位、撕裂或亮度突变。在渲染层面,通常有两种实现方式:

  • 单帧缓冲分块输出:渲染到一个大尺寸 FBO,然后按屏幕位置用 glViewport 或 glBlitFramebuffer 分块拷贝到各 EGLSurface。优点是几何关系天然正确,缺点是显存占用大。
  • 多视口独立渲染:每个屏幕独立设置投影矩阵和视口,分别渲染。优点是灵活,缺点是需要精确计算每个屏幕的视锥体,否则拼接处会出现透视错位。
拼接屏最常见的误区是只对齐了分辨率,没有对齐投影矩阵。如果每块屏幕使用相同的透视投影,边缘屏幕的画面会被拉伸。正确做法是根据屏幕在拼接墙中的物理位置,计算各自的离轴投影矩阵(off-axis projection)。

三、扩展屏:独立内容与上下文共享

扩展屏场景更接近桌面多显示器:每个屏幕显示不同窗口、不同应用或不同视图。它的渲染需求与拼接屏相反——内容独立、刷新可异步。在 OpenGL ES 工程中,扩展屏通常由窗口系统管理多个 EGLSurface,应用层需要决定是否共享 EGLContext。

共享上下文的好处是纹理、Shader、VBO 等资源只需创建一次,多个屏幕的渲染线程可以直接复用;坏处是上下文切换和资源竞争会引入额外开销。对于两个屏幕显示同一 3D 场景的不同视角,推荐共享上下文 + 多视口;对于两个屏幕显示完全无关的 UI,推荐独立上下文,避免相互阻塞。

// 共享上下文创建示例(伪代码)
EGLContext sharedCtx = eglCreateContext(display, config, EGL_NO_CONTEXT, ctxAttribs);
EGLContext screen1Ctx = eglCreateContext(display, config, sharedCtx, ctxAttribs);
EGLContext screen2Ctx = eglCreateContext(display, config, sharedCtx, ctxAttribs);

// 每个屏幕绑定自己的 Surface
eglMakeCurrent(display, surface1, surface1, screen1Ctx);
glViewport(0, 0, width1, height1);
renderScene(view1);

eglMakeCurrent(display, surface2, surface2, screen2Ctx);
glViewport(0, 0, width2, height2);
renderScene(view2);

四、VR/AR 双屏:视差、延迟与畸变

VR/AR 头显的左右眼屏幕是最苛刻的多屏场景。它要求:左右眼图像必须基于正确的瞳距和视差分别渲染;从头部运动到光子显示的总延迟(MTP)必须低于 20ms;镜片带来的桶形畸变必须通过预畸变网格校正。这意味着 VR 多屏不是“渲染两次”,而是“以双眼为一个渲染单元,在单帧内完成两次视图变换 + 畸变后处理”。

工程上常用单通道立体渲染:使用纹理数组(GL_TEXTURE_2D_ARRAY)或实例化渲染,一次提交绘制两个眼睛的视图,减少 CPU 提交开销。随后在畸变阶段,用一张预计算的畸变网格对左右眼纹理分别采样,输出到最终帧缓冲。

五、车载多屏:安全隔离与多域共存

车载多屏包括仪表盘、中控屏、HUD、副驾屏和后排娱乐屏。它的特殊之处在于功能安全:仪表盘属于安全关键显示,不能因为中控屏的应用崩溃而黑屏。因此车载多屏通常采用多域隔离架构:仪表域和中控域运行在不同进程甚至不同 SoC 上,通过 Hypervisor 或容器隔离 GPU 资源。

场景屏幕数量同步要求典型分辨率关键约束
拼接屏4~16帧级硬同步1920×1080 每屏几何对齐、边缘融合
扩展屏2~4可异步2560×1440 每屏上下文共享、窗口管理
VR/AR 双屏2眼级同步 + 预测2160×2160 每眼MTP < 20ms、畸变校正
车载多屏3~6安全域隔离1920×720 仪表ASIL 等级、启动时间

六、案例复盘:从“能显示”到“显示对”

某拼接屏项目初期采用每屏独立渲染,结果在拼接缝处出现明显错位。排查发现每块屏幕使用了相同的透视投影矩阵,但屏幕物理位置不同,导致视锥体不匹配。改为根据屏幕中心坐标计算离轴投影矩阵后,错位消失。这个案例说明:多屏渲染的第一原则是先建立统一的空间坐标系,再分配每个屏幕的视图变换。

另一个车载项目在仪表和中控之间共享了同一个 EGLContext,结果中控应用的一次长耗时纹理上传导致仪表帧率抖动。复盘结论是:安全关键屏幕必须拥有独立的上下文和独立的渲染线程,宁可牺牲部分资源共享,也要保证确定性。

多屏渲染的选型口诀:
拼接屏看几何,扩展屏看上下文,VR 看延迟,车载看隔离。
先确定同步边界,再决定资源是否共享,最后才是优化渲染性能。

总结来看,多屏渲染的需求分析不是简单统计屏幕数量,而是回答四个问题:屏幕之间是空间连续还是内容独立?是否要求同帧?是否允许共享 GPU 资源?是否有安全或实时性等级要求?回答完这四个问题,才能选择正确的 EGL 配置、上下文策略和渲染架构。