4. 硬件与系统约束
图层合成这事儿,说白了就是「怎么把好几层画面又快又好地拼成一张图」。很多做嵌入式 GUI 的朋友一开始觉得这有啥难的,不就是把图片叠在一起吗?等你真在 60 帧的刷新率下跑起来,就知道坑有多深了。
我早年做过一个智能手表项目,屏幕分辨率不高,但动画效果要求很花哨。那时候我天真地以为软件逐像素合成就行,结果帧率直接掉到 15 帧,卡得用户想砸表。后来我才意识到——硬件图层叠加才是嵌入式 GUI 的命门。
4.1 硬件图层叠加:让硬件替你干活
现代嵌入式 SoC 里,显示控制器通常都支持多个硬件图层。每个图层有自己的显存区域,可以独立配置颜色格式、透明度、位置和大小。合成工作由硬件完成,CPU 和 GPU 几乎不用参与。
核心思想:能交给硬件做的事,绝不自己动手。硬件图层叠加就是典型的「用硬件资源换性能」。
我习惯把硬件图层分为三类:
- 背景层:通常是纯色或静态图片,几乎不更新
- 内容层:显示主要界面内容,比如菜单、列表
- 覆盖层:弹窗、光标、通知等临时元素
举个例子,一个典型的智能家居控制面板:
| 图层 | 内容 | 更新频率 | Alpha |
|---|---|---|---|
| Layer 0 | 壁纸背景 | 几乎不变 | 255 |
| Layer 1 | 设备列表 | 中等 | 255 |
| Layer 2 | 弹出菜单 | 高 | 200 |
| Layer 3 | 触摸光标 | 极高 | 128 |
你看,光标层单独放一层,更新时只需要刷新这一小块显存,不用重绘整个屏幕。这就是硬件图层的威力。
我的习惯:把最常变化的元素放在最高编号的图层。因为很多硬件合成器是从高到低逐层合成的,高图层变化时,低图层完全不用动。
4.2 Alpha 混合优化:别让透明度拖垮性能
Alpha 混合是性能杀手,这一点我深有体会。曾经有个项目,界面上有大量半透明叠加效果,帧率直接腰斩。后来我仔细分析了 Alpha 混合的计算开销,发现很多地方其实可以优化。
标准 Alpha 混合公式:
// 标准 Alpha 混合
out = src * alpha + dst * (1 - alpha)
// 预乘 Alpha 优化
out = src_pre + dst * (1 - alpha)
// 其中 src_pre = src * alpha 已经提前算好
预乘 Alpha 能省掉一次乘法,别小看这一下,在每帧几百万像素的合成中,省下来的就是帧率。
我总结了几条 Alpha 混合的优化原则:
- 能不透明就不透明:alpha = 255 时直接拷贝,跳过混合计算
- 能全透明就跳过:alpha = 0 时直接忽略该图层
- 预乘 Alpha 是好朋友:提前把颜色乘好透明度
- 用查表法代替实时计算:对于固定透明度,提前算好混合系数
我曾经踩过的坑:在 STM32 上用软件做 Alpha 混合,每像素用了浮点运算。后来改成定点数 + 查表,速度提升了 4 倍。嵌入式平台上,浮点能不用就不用。
4.3 合成器设计模式:架构决定性能
合成器是 GUI 系统的核心组件,负责管理所有图层的合成策略。我见过太多项目把合成逻辑散落在各处,最后改都改不动。
我个人推荐三种合成器设计模式:
4.3.1 直接合成模式
最简单,也最常用。从底层到顶层逐层合成,每层依次混合。适合图层数量少(2-4层)的场景。
// 直接合成伪代码
void direct_composite(layer_t layers[], int count) {
framebuffer_t fb = layers[0].buffer; // 底层直接拷贝
for (int i = 1; i < count; i++) {
if (layers[i].alpha == 0) continue;
if (layers[i].alpha == 255) {
copy_layer(fb, layers[i]); // 直接覆盖
} else {
blend_layer(fb, layers[i]); // Alpha 混合
}
}
}
4.3.2 区域脏矩形模式
只更新变化区域。适合局部更新频繁的场景,比如触摸反馈、光标移动。
- 维护一个脏矩形列表
- 只合成脏矩形覆盖的区域
- 硬件图层支持部分更新时效果最佳
4.3.3 硬件卸载模式
把合成工作完全交给显示控制器。CPU 只负责更新图层内容,不参与合成。
| 模式 | CPU 负载 | 内存占用 | 适用场景 |
|---|---|---|---|
| 直接合成 | 高 | 低 | 图层少、硬件弱 |
| 脏矩形 | 中 | 中 | 局部更新多 |
| 硬件卸载 | 极低 | 高(多缓冲区) | 硬件支持图层叠加 |
我建议:先搞清楚你的硬件支持几个图层,再选模式。很多 MCU 只有 1-2 个硬件图层,那就老老实实用直接合成 + 脏矩形优化。
4.4 合成器架构图
下面这张图是我自己项目里用过的合成器架构,你可以参考一下:
4.5 实战建议:从硬件出发做设计
说了这么多,最后给几条实在的建议:
- 先看芯片手册:搞清楚你的 SoC 支持几个硬件图层、每个图层的颜色格式限制、是否支持独立 Alpha
- 图层数量宁少勿多:硬件图层是稀缺资源,能用 2 层解决的别用 3 层
- Alpha 混合能省则省:半透明效果看着漂亮,但性能代价很大。我一般只在弹窗和光标上用半透明
- 脏矩形一定要做:不管用哪种合成模式,只更新变化区域都是性价比最高的优化
- 预乘 Alpha 是标配:所有图片资源在加载时就预乘好,运行时直接混合
记住一句话:图层合成的终极目标,是让 CPU 在每一帧里什么都不用做。所有合成工作,要么由硬件完成,要么提前算好。
嗯,图层与合成这块,说到底就是「怎么把画面拼起来最省力」。你想想看,硬件图层叠加就像搭积木,每块积木都是独立的,拼的时候只动需要动的那块。Alpha 混合优化就是让每块积木的拼接缝更顺滑,但别让顺滑变成负担。合成器设计模式则是决定谁来搭积木——是 CPU 亲自上阵,还是让硬件流水线自动完成。
我在实际项目中,最常用的组合是「硬件图层叠加 + 脏矩形更新 + 预乘 Alpha」。这套组合拳打下来,60 帧基本稳如老狗。当然,具体怎么选,还得看你的硬件平台和业务场景。