3、帧率控制策略:动态帧率调整、基于内容的帧率自适应、LFR与HFR切换

帧率控制,说白了就是「屏幕每秒该刷多少次」。

很多人觉得帧率越高越好。其实不是。高帧率带来的功耗增长,是肉眼可见的。我见过一个项目,为了追求120Hz的流畅度,整机续航直接掉了15%。用户反馈倒是没夸流畅,反而骂续航崩了。

所以,帧率控制的核心思路是:该快的时候快,该慢的时候慢。下面我拆开来讲。

3.1 动态帧率调整

动态帧率调整,就是让屏幕根据场景自动切换刷新率。比如你刷微博,手指滑动时60Hz就够了;停下来看文章,降到30Hz甚至1Hz都行。

我习惯把动态帧率分成三个层级:

  • 高帧率区(60Hz~120Hz):游戏、视频、滑动列表
  • 中帧率区(30Hz~60Hz):静态页面、阅读、图片浏览
  • 低帧率区(1Hz~30Hz):息屏显示、待机、AOD

这里有个坑。我曾经在某个平板上做过动态帧率,结果发现从120Hz降到30Hz时,屏幕会闪一下。为什么?因为面板的VCOM电压没跟上。后来我加了一个「帧率切换缓冲」,每次切换前先调整电压,再切帧率,问题就解决了。

核心原则:帧率切换不能太频繁。我建议至少保持200ms以上的稳定期,否则人眼会感知到「卡顿感」。

3.2 基于内容的帧率自适应

动态帧率是「看场景」,而基于内容的帧率自适应是「看画面」。说白了,就是分析当前显示的内容,来决定帧率。

举个例子。你播放一个24fps的电影,屏幕却跑在60Hz。每帧画面要重复显示2.5次,这其实浪费了功耗。更好的做法是:检测到视频源是24fps,屏幕就切到48Hz或24Hz。

我做过一个方案,流程是这样的:

1. 从GPU/显示驱动获取当前帧的渲染时间戳
2. 计算帧间隔(Frame Interval)
3. 如果帧间隔稳定在41.6ms(24fps),切到24Hz
4. 如果帧间隔稳定在16.6ms(60fps),保持60Hz
5. 如果帧间隔波动大(比如30~50ms),用中间值48Hz

嗯,这里要注意。不是所有面板都支持24Hz。有些LTPS面板最低只能到30Hz。所以我在代码里加了一个「面板能力查询」的接口,先查支持哪些帧率,再选最接近的那个。

我的经验:基于内容的帧率自适应,最好配合「帧率预测」来做。比如检测到用户正在快速滑动,提前把帧率拉高,而不是等卡了再升。我管这叫「预判式帧率控制」。

3.3 LFR与HFR切换

LFR(Low Frame Rate)和HFR(High Frame Rate)的切换,是帧率控制里最头疼的部分。为什么?因为面板的驱动方式完全不同。

LFR模式下,面板通常用「自刷新」或「DSI命令模式」,功耗极低。HFR模式下,面板用「视频模式」,带宽大、功耗高。

切换时,我遇到过三个典型问题:

  1. 切换延迟:从LFR切到HFR,面板需要重新初始化,耗时可能达到100ms以上。用户会感觉到「黑一下」。
  2. 画面撕裂:切换过程中,如果显示控制器还在送数据,面板却已经切了模式,就会出现撕裂。
  3. 功耗尖峰:切换瞬间,驱动IC的电流会突然飙升,可能触发电池保护。

怎么解决?我分享一个我自己的方案:

// 伪代码:LFR到HFR的平滑切换
1. 停止送数据(VBlank期间操作)
2. 发送切换命令到面板(TE信号同步)
3. 等待面板ACK(超时10ms)
4. 重新配置DSI时钟(从低频切到高频)
5. 恢复送数据(从下一帧开始)

警告:千万不要在画面更新过程中切换帧率。我曾经在某个项目里踩过这个坑,结果屏幕直接花屏了。后来我强制要求:所有帧率切换必须在VBlank期间完成。

另外,LFR和HFR的切换阈值也很关键。我习惯用「帧间隔标准差」来判断:

  • 标准差 < 2ms:稳定场景,可以切到LFR
  • 标准差 > 5ms:波动场景,保持HFR
  • 2ms ~ 5ms:中间态,用中档帧率

你想想看,如果帧间隔忽大忽小,你切到LFR,画面就会一卡一卡的。用户体验反而更差。

3.4 总结一下

帧率控制不是简单的「高帧率好还是低帧率好」。它是一个平衡艺术。我个人习惯把帧率控制做成一个「状态机」,根据场景、内容、面板能力三个维度来决策。

最后说一句:帧率切换的平滑度,比帧率本身更重要。用户能接受30Hz的静态画面,但接受不了从60Hz掉到30Hz时的「顿挫感」。这一点,我在多个项目里都验证过。