合成器(Composer)原理:硬件合成与GPU合成
合成器,说白了就是决定「谁把画面画到屏幕上」的那个家伙。我刚开始接触图形子系统时,以为合成就是把几层图叠在一起,简单得很。后来才发现,这里面的门道,够你琢磨好一阵子。
在开源鸿蒙里,合成器负责把多个图层(Layer)合并成一张最终画面,再送给显示硬件。这个过程中,有两个关键角色:硬件合成和GPU合成。它们各有各的脾气,也各有各的用武之地。
硬件合成 vs GPU合成
先说说硬件合成。它依赖显示控制器(Display Controller)里的专用模块,比如Overlay Engine或Composition Engine。这些模块是硬连线逻辑,专门干叠图这件事。我曾在某款低端芯片上调试过,硬件合成几乎不占CPU和GPU资源,功耗也低得感人。
但硬件合成有个致命弱点——灵活性差。它支持的图层数量有限,比如最多4层或8层。而且图层格式、旋转、缩放等操作,都得看硬件脸色。你想想看,如果某个图层格式硬件不支持,那就只能交给GPU处理了。
GPU合成呢?它用着色器(Shader)程序来叠图,想怎么叠就怎么叠。旋转、缩放、模糊、色彩变换,统统不在话下。我在项目中遇到过需要做圆角裁剪的场景,硬件合成直接罢工,最后还是靠GPU搞定的。
不过GPU合成的代价也明显:功耗高、延迟大。每次合成都要把图层数据从内存搬到GPU显存,算完再搬回来。这一来一回,带宽和电量就哗哗地流走了。
核心原则:能用硬件合成,就别用GPU合成。这是所有图形系统优化的第一铁律。
图层叠加(Layer Blending)
图层叠加,就是多个图层按照透明度、混合模式叠在一起的过程。嗯,这里要注意,不是简单地把像素叠上去就完事了。
每个图层都有一个alpha通道,表示透明度。叠加时,合成器会按照从下到上的顺序,逐层计算最终颜色。公式大概是这样的:
// 伪代码:图层叠加计算
result_color = src_color * src_alpha + dst_color * (1 - src_alpha)
这个公式看着简单,但实际项目中坑不少。我曾经遇到过一个bug:某个图层的alpha值被设成了0.5,结果它下面的所有图层都透出来了,整个界面像鬼影一样。排查了半天,才发现是应用层把alpha值传错了。
常见的混合模式有:
- Normal(正常):直接覆盖,不透明部分完全遮挡下层
- Multiply(正片叠底):颜色相乘,常用于阴影效果
- Screen(滤色):颜色反相相乘,用于发光效果
- Overlay(叠加):根据下层亮度决定使用Multiply还是Screen
在开源鸿蒙里,图层叠加是由合成器统一管理的。每个图层可以独立设置混合模式,合成器会按照正确的顺序和公式进行计算。
个人经验:调试图层叠加问题时,先把所有图层的alpha设为1.0,看看是不是混合模式本身的问题。如果正常了,再逐个调整alpha值。这招帮我省了不少时间。
合成策略:Client/Device合成
合成策略,就是决定「谁来干活」的问题。开源鸿蒙里主要有两种策略:Client合成和Device合成。
Device合成,就是让显示硬件(Device)来做合成。合成器把多个图层直接传给显示控制器,硬件自己把它们叠好再输出。这种方式效率最高,但受限于硬件能力。
Client合成,则是让合成器进程(Client)自己来做。合成器用GPU或CPU把图层先合成一张图,再把这张图传给显示硬件。灵活性高,但性能开销大。
实际系统中,合成器会动态选择策略。我参与过一个项目,系统会根据图层数量和格式自动切换:
| 条件 | 合成策略 | 原因 |
|---|---|---|
| 图层数 ≤ 4,且格式都支持 | Device合成 | 硬件能搞定,效率最高 |
| 图层数 > 4,或有不支持的格式 | Client合成 | 硬件能力不足,交给GPU |
| 有图层做动画(旋转/缩放) | Client合成 | 硬件不支持动态变换 |
| 系统处于低功耗模式 | Device合成 | 省电优先 |
这个策略切换的逻辑,我建议你仔细看看源码。开源鸿蒙里有一个CompositionStrategy的枚举,里面定义了各种策略的优先级和切换条件。
避坑指南:我曾经在某个芯片上发现,Device合成时如果图层有半透明区域,硬件会错误地处理alpha值,导致边缘出现黑边。后来我强制让半透明图层走Client合成,问题才解决。所以,别完全信任硬件,该出手时就出手。
合成流程的核心逻辑
说了这么多,咱们用一张图来总结合成器的核心逻辑。这张SVG图展示了从图层输入到最终显示的全过程:
从这张图可以看出,合成器的核心就是「先判断,再执行」。判断的依据就是图层属性、硬件能力和系统状态。执行时要么走硬件快速通道,要么走GPU慢速但灵活的通道。
实际项目中的合成优化
我在做开源鸿蒙图形子系统时,遇到过不少合成相关的性能问题。这里分享几个优化思路:
- 减少图层数量:能合并的图层尽量合并,比如把静态背景和固定UI元素合成一张图。这样硬件合成就能覆盖更多场景。
- 避免频繁切换策略:每次策略切换都有开销。如果动画只有几帧,不如直接走Client合成,省得来回折腾。
- 利用硬件Overlay:很多芯片支持多个Overlay平面,每个平面可以独立设置缩放和位置。合理利用这些平面,能大幅提升合成效率。
- 注意图层格式:硬件通常只支持RGB888、RGBA8888等常见格式。如果用了YUV或P010,大概率会触发Client合成。
调试小技巧:在开源鸿蒙里,可以通过hidumper命令查看当前合成策略。运行hidumper -s composer -a dump,就能看到每个图层的合成方式。我经常用这个命令来验证优化效果。
合成器这块,说白了就是「硬件能干的让硬件干,硬件干不了的GPU兜底」。理解了这个原则,你就能在设计图形系统时做出合理的选择。嗯,今天就先聊到这里,下次咱们再深入讲讲合成器的缓冲区管理和帧同步机制。