1. 课程导入
做嵌入式GUI开发,迟早会被图片卡住。我入行那会儿,第一个量产项目就是栽在图片上——界面切换像放幻灯片,用户按一下按钮,得等半秒才反应。后来一查,问题全出在图片这条链路上。
说白了,一张图片从文件变成屏幕上像素,要经过三道关卡:解码、传输、渲染。任何一环出问题,你的FPS就保不住。
1.1 图片加载的完整链路
先看看一张图片到底是怎么“走”到屏幕上的。我习惯把它分成三个阶段:
核心链路: 存储介质 → 解码器 → 内存缓冲区 → 显示驱动 → 屏幕
每个阶段都有各自的瓶颈。咱们一个一个拆。
1.2 解码瓶颈:CPU的噩梦
解码是图片加载的第一关,也是最容易被低估的一关。你想想看,一张1920x1080的JPEG图片,压缩后可能只有200KB,但解码成RGB565原始数据,直接膨胀到 1920×1080×2 ≈ 4MB。如果是ARGB8888,那就是8MB。
我在一个STM32H7的项目上遇到过:用软件JPEG解码一张全屏背景图,耗时接近300ms。用户滑动页面时,每翻一页就要卡300ms,这谁受得了?
我的经验: 解码器选型很关键。硬件JPEG解码器(比如NXP i.MX系列、STM32MP1系列)能把解码时间从几百毫秒压到十几毫秒。如果芯片不带硬件解码器,那就得在图片尺寸和压缩格式上做取舍。
常见的解码问题:
- 软件解码太慢——尤其是JPEG,DCT变换和霍夫曼解码都是CPU杀手
- 解码缓冲区过大——临时内存不够,导致反复申请释放
- 格式选错——PNG虽然无损,但解码比JPEG还慢,尤其是带alpha通道的
1.3 传输瓶颈:总线上的堵车
解码完的原始像素数据,得从内存搬到显示缓冲区。这一步看起来简单,但坑不少。
我记得有个项目用的是低端Cortex-M4,内部SRAM只有256KB。解码一张800x480的图片,RGB565格式,光原始数据就占800×480×2 ≈ 768KB。根本放不下!只能分块传输,每次传一小块到外部SDRAM,再刷到LCD。这一来一回,传输时间翻了好几倍。
| 传输方式 | 典型带宽 | 适用场景 | 注意事项 |
|---|---|---|---|
| CPU逐像素拷贝 | 10~50 MB/s | 小尺寸图片、低分辨率 | CPU占用100%,卡顿明显 |
| DMA传输 | 50~200 MB/s | 中等尺寸、批量刷新 | 需要DMA通道,注意对齐 |
| LTDC/DSI直接读取 | 200~500 MB/s | 全屏刷新、视频播放 | 需要硬件支持,内存带宽要够 |
注意: 很多开发板标称的LCD接口速度是理论值。实际跑起来,总线仲裁、SDRAM刷新周期、DMA描述符开销都会吃掉带宽。我曾经测过一个方案,理论带宽400MB/s,实际传输大块数据时只有120MB/s——因为SDRAM的页命中率太低。
1.4 渲染瓶颈:最后一公里的坑
数据到了显示缓冲区,不等于就能流畅显示。渲染阶段的问题往往最隐蔽。
举个例子:你的GUI框架可能用了多层合成。背景一层、控件一层、弹窗一层。每次刷新都要把这三层叠在一起。如果每层都是全屏大小,那合成一次就要处理好几MB的数据。FPS能高才怪。
我见过最夸张的一个案例:某方案用软件做alpha混合,每帧要混合4层全屏ARGB8888图片。算下来,每帧要处理 800×480×4×4 ≈ 6MB 的数据。CPU主频才400MHz,FPS直接掉到15帧以下。
1.5 三个核心性能指标
衡量图片性能好不好,我一般只看三个数:
- FPS(帧率)——低于30帧就能感觉到卡,低于20帧基本没法用
- 内存占用——解码缓冲区、帧缓冲区、图层缓冲区,加起来不能超过系统可用内存的60%,否则GC或内存碎片会搞死你
- 加载时间——从点击到图片显示,超过500ms用户就会觉得慢
我的经验阈值:
- FPS ≥ 45:流畅
- FPS 30~45:可接受
- FPS < 30:必须优化
- 加载时间 < 200ms:优秀
- 加载时间 200~500ms:一般
- 加载时间 > 500ms:不可接受
1.6 常见瓶颈速查表
做项目时,我习惯用这张表快速定位问题:
| 现象 | 可能瓶颈 | 快速验证方法 |
|---|---|---|
| 首次加载慢,后续快 | 解码瓶颈 | 测解码耗时,看CPU占用 |
| 滚动时卡顿 | 传输或渲染瓶颈 | 检查DMA配置,测帧合成时间 |
| 内存暴涨后卡死 | 内存泄漏或碎片 | 监控堆使用量,检查缓冲区释放 |
| 特定图片卡,其他不卡 | 图片格式或尺寸问题 | 对比不同格式、不同分辨率的解码时间 |
嗯,这一章先把全景搭起来。后面的章节,我会一个一个瓶颈拆开讲,包括怎么用硬件加速、怎么选图片格式、怎么优化内存布局。你先把这张图刻在脑子里——解码、传输、渲染,三个环节缺一不可。