车机显示多窗口编程:实现导航、音乐、仪表信息的同屏分层显示
车机多窗口不是把导航Activity、音乐Activity、仪表View叠一起就完事。座舱屏最怕两件事:音乐悬浮窗盖住车速,导航刷新把仪表帧率拖垮。正确做法是把显示拆成硬件图层(Plane/Layer),按信息优先级定Z-order,由DPU/Weston/SurfaceFlinger做合成,应用只提交各自Surface,不互相覆盖。
一、信息优先级决定图层归属
先定层级,再谈布局:
P0 仪表层:车速、转速、ADAS告警、转向提示 → 独占底层硬件plane,禁止被覆盖
P1 导航层:中控地图、路线卡片、AR箭头
P2 媒体层:音乐卡片、专辑封面、歌词
P3 通知层:来电、消息Toast、空调弹窗
Overlay层:ADAS碰撞预警、确认弹窗,临时置顶
仪表屏和HUD归安全域,媒体窗口可以“飞屏”过去,但只能占预留信息区,不能压车速数字。
二、窗口对象与Z-order管理
Wayland/ivi-shell思路抽象成结构体:
typedef enum {
LAYER_CLUSTER = 0, // P0
LAYER_NAV, // P1
LAYER_MEDIA, // P2
LAYER_NOTIFY, // P3
LAYER_OVERLAY // 告警置顶
} win_layer_t;
typedef struct {
int id;
win_layer_t layer;
int z; // 同层内顺序
rect_t rect;
rect_t dirty; // 仅刷新变化区
uint8_t alpha;
uint8_t visible;
uint32_t surface_id;
} win_t;
win_t wins[MAX_WIN];
合成前先过仲裁:
int compose_priority(win_t*a, win_t*b){
if(a->layer != b->layer) return a->layer - b->layer;
return a->z - b->z;
}
音乐窗口layer=MEDIA,仪表底图layer=CLUSTER,Z-order天然隔开;就算音乐飞到仪表区,也只允许进“副信息槽”,主车速区在合成阶段裁剪掉。
三、DPU硬件合成优先
别把所有图层丢给GPU合成,车机平台应该走overlay plane:
仪表底图 → DPU base layer
导航地图 → overlay plane 0
音乐卡片 → overlay plane 1
告警条 → overlay plane 2(最高)
超出plane数量才GPU fallback合成
/* Weston/DRM思路 */
drm_plane_assign(PLANE_BASE, cluster_surf);
drm_plane_assign(PLANE_OVL0, nav_surf);
drm_plane_assign(PLANE_OVL1, media_surf);
drm_plane_set_zorder(PLANE_OVL1, PLANE_OVL0);
规则:
视频/地图走YUYV/UBWC,别用线性RGBA硬搬,带宽炸
静态壁纸合成一次底图缓存,不每帧重画
歌词滚动只更新文字脏区,不动地图
音乐封面切换才重传surface,平时不动
四、VSync+Fence防撕裂
多窗口同屏必须绑同一个垂直同步:
所有surface按VSync周期提交
写完成用fence/acquire fence,别CPU等GPU
present()一次出整屏,避免中控显示“左转”、仪表还停“直行”
drm_wait_vblank(fd, &vbl);
compositor_commit(layers); // 一次atomic commit
跨屏同步(中控+仪表+HUD)用统一VSync源或主屏时钟分发,延迟要求一般≤50ms。
五、状态同源:别各屏各算一份
多屏最容易出“仪表歌名旧、中控已切歌”。正确是单一数据源:
typedef struct {
int speed_kmh;
uint8_t gear;
nav_cmd_t nav;
media_state_t media;
} vehicle_state_t;
vehicle_state_t g_state; // 服务层更新
/* 各窗口订阅同一份 */
cluster_update(&g_state);
nav_update(&g_state);
media_update(&g_state);
CAN/以太网来的车速、方控切歌、导航指令都先写g_state,渲染线程按当前帧读。
六、行车场景动态降级
驾驶模式切换时重构层级:
行车中:媒体图层降透明度、歌词关、动画关
倒挡:后视摄像头层强插P0之上
ADAS告警:Overlay层置顶,音乐让位
停车:音乐卡片可全屏、帧率恢复
if(drive_mode == DRIVE){
set_layer_visible(LAYER_MEDIA_LYRIC, 0);
lower_fps(LAYER_MEDIA, 10);
}
七、落坑清单
音乐View直接盖仪表Speed→安全信息被挡
所有窗口GPU合成→DPU闲置、发热掉帧
每帧整屏重绘→地图卡、歌词抖
中控仪表各维护一份状态→显示不同步
不绑VSync→跨屏撕裂
歌词动画行车常开→分散注意力
overlay plane超数量不fallback→合成失败黑屏
悬浮窗z-index写魔法数→层级冲突
倒车摄像头不抢顶层→被导航盖住
媒体飞屏占满仪表→车速区被挤
八、落地顺序
定层级:Cluster/Nav/Media/Notify/Overlay
仪表底图走base plane,媒体走overlay
窗口对象带layer+z+dirty,合成前仲裁
音乐飞屏只进副信息槽,不压车速
各层surface按VSync提交,fence同步
车速/歌名/导航走统一状态服务
行车模式降媒体帧率、关歌词动画
ADAS/倒车触发Overlay强置顶
车机多窗口的本质是“安全信息永远在最底层且不被覆盖,娱乐信息永远在附加层且只刷脏区”。Z-order不是UI美观问题,是功能安全边界;硬件图层合成省GPU也省延迟;状态同源解决多屏不一致。写应用层时别去管像素混合,只管“我在哪一层、占哪块脏区、优先级多少”——剩下交给合成器。真在仪表上把车速数字被音乐卡片盖掉,不是体验问题,是HMI架构事故。





