一个实时相机滤镜,在30 FPS下每帧只有约33毫秒的预算。演示时流畅的滤镜,一到真机上就卡顿,问题几乎总是出在五个环节之一:格式转换、帧背压、推理实际执行位置、同步回读、持续热负载。这篇文章讲的就是怎么定位到底是哪一个。
先说清楚:这里没有对任何SDK做基准测试,也没有列出任何手机的具体数据。一个没有标注设备型号、系统版本和热状态的数字,对读者没有任何参考价值。下面要分享的,是我在相机管线超预算时排查的顺序,以及一些能让你在翻trace之前,先在纸上算清楚某个环节是否可能超时的算术。
先算两笔账,预算上限就出来了
30 FPS下,你有1000/30=33.3毫秒来完成接收帧、转换、跑模型、渲染、合成这一整套流程。60 FPS则减半到16.7毫秒。每增加一个环节,就要从这33毫秒里分走一部分,而预览的流畅度取决于最慢的那一帧。
分辨率是另一个乘数,值得先在纸上算清楚。1080p帧是1920×1080=2,073,600像素,720p帧是1280×720=921,600像素,两者相差2.25倍。这意味着任何逐像素处理的环节,在1080p下的成本是720p的2.25倍——一行代码都不用改。
我习惯先算这笔账,因为很多时候算术本身就能结束排查。如果一个环节需要处理1080p帧的每一个像素,而你有四个这样的环节,那预算在模型加载之前就已经用完了。
30 FPS还是60 FPS?先别急着选
对于用户对着镜头移动脸部这种场景,30 FPS通常够用,60 FPS往往不值得那个代价——把预算砍半到16.7毫秒,通常意味着要降分辨率,而分辨率下降比流畅度提升更显眼。
例外是用户用手或头追踪的物体,这种情况下额外的采样确实能感知到。我的建议是先以30 FPS上线并做好埋点,再根据数据决定,因为30 FPS下的95分位延迟比目标帧率更能预测用户感知到的画质。
第一个排查点:格式转换
Android的相机硬件输出的是YUV_420_888,一种8位采样、4:2:0色度子采样的平面YCbCr格式。而大多数图像处理代码和教程代码用的是RGBA。于是转换环节就被插了进来。
热门跟贴