合成器(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图展示了从图层输入到最终显示的全过程:

合成器核心流程 图层1 图层2 图层3 图层N 合成策略判断 图层数 ≤ 硬件限制? 格式都支持? 无动态变换? 低功耗模式? 是 → Device合成 否 → Client合成 Device合成 Client合成 最终 画面 输出

从这张图可以看出,合成器的核心就是「先判断,再执行」。判断的依据就是图层属性、硬件能力和系统状态。执行时要么走硬件快速通道,要么走GPU慢速但灵活的通道。

实际项目中的合成优化

我在做开源鸿蒙图形子系统时,遇到过不少合成相关的性能问题。这里分享几个优化思路:

  • 减少图层数量:能合并的图层尽量合并,比如把静态背景和固定UI元素合成一张图。这样硬件合成就能覆盖更多场景。
  • 避免频繁切换策略:每次策略切换都有开销。如果动画只有几帧,不如直接走Client合成,省得来回折腾。
  • 利用硬件Overlay:很多芯片支持多个Overlay平面,每个平面可以独立设置缩放和位置。合理利用这些平面,能大幅提升合成效率。
  • 注意图层格式:硬件通常只支持RGB888、RGBA8888等常见格式。如果用了YUV或P010,大概率会触发Client合成。

调试小技巧:在开源鸿蒙里,可以通过hidumper命令查看当前合成策略。运行hidumper -s composer -a dump,就能看到每个图层的合成方式。我经常用这个命令来验证优化效果。

合成器这块,说白了就是「硬件能干的让硬件干,硬件干不了的GPU兜底」。理解了这个原则,你就能在设计图形系统时做出合理的选择。嗯,今天就先聊到这里,下次咱们再深入讲讲合成器的缓冲区管理和帧同步机制。

专注资料整理