4、图层合成原理:硬件图层(UI/G2D/VE)、alpha混合、Z-order配置

好,咱们今天聊点硬核的。图层合成,说白了就是让屏幕上那些乱七八糟的图层,最后能规规矩矩地叠在一起,变成你看到的那张完整画面。

我刚开始做显示驱动那会儿,总觉得这玩意儿不就是把几层图叠起来嘛,有啥难的?后来被坑了几次才明白——这里面的门道,比你想象的多得多。

4.1 硬件图层:UI、G2D、VE 各司其职

全志A133的显示子系统里,硬件图层主要分三类。我习惯把它们比作三个不同性格的工人:

  • UI图层:专门处理用户界面。说白了就是你的状态栏、桌面图标、弹窗这些。它走的是Display Engine的硬件通道,延迟最低,效率最高。
  • G2D图层:2D图形加速引擎。你想想看,如果UI图层要做一个旋转动画,或者缩放一个图片,CPU去算的话,帧率直接崩。G2D就是干这个的——硬件加速,不占CPU。
  • VE图层:视频引擎图层。专门伺候视频流。比如你播放一个1080p的H.264视频,VE直接解码后扔到自己的图层上,不跟UI抢资源。

关键点:这三个图层是独立的硬件通道,可以并行处理。但它们的数量是有限的——A133最多支持4个UI图层 + 1个G2D图层 + 1个VE图层。别超了,超了就只能软件合成,性能直接腰斩。

我记得有个项目,客户非要在界面上同时显示4个视频窗口。我说不行,VE图层只有一个。最后只能把3个视频用G2D先合成到一个图层里,再跟VE图层叠。嗯,这就是实战中的妥协。

4.2 Alpha混合:透明度的艺术

Alpha混合,说白了就是算透明度。你看到一个半透明的弹窗,底下还能隐约看到桌面——这就是Alpha混合的功劳。

公式其实很简单:

输出颜色 = 前景色 × α + 背景色 × (1 - α)

但这里有个坑——预乘Alpha。我刚开始做的时候没注意这个,结果半透明边缘总是有一圈白边。后来查了半天才发现,有些图层用的是预乘Alpha格式,有些不是,混在一起就出问题了。

避坑指南:我曾经在一个项目中,UI图层用的是非预乘Alpha,G2D图层用的是预乘Alpha。结果合成出来的画面,半透明区域颜色完全不对。排查了两天才找到原因——混合模式没统一。后来我定了个规矩:整个系统统一使用预乘Alpha格式,省心。

全志A133的硬件混合器支持两种Alpha模式:

模式 说明 适用场景
全局Alpha 整个图层用一个透明度值 淡入淡出动画、整体遮罩
逐像素Alpha 每个像素有自己的透明度 带透明区域的PNG图片、字体渲染

你想想看,如果做一个渐隐动画,用全局Alpha就够用了,硬件直接改一个寄存器值,效率极高。但如果你要显示一个带阴影的按钮,那就得用逐像素Alpha,每个像素的透明度都不一样。

4.3 Z-order配置:谁在上面?

Z-order,就是图层的堆叠顺序。数值越大,越靠上。这个好理解——状态栏永远在最上面,桌面在中间,壁纸在最底下。

但实际配置的时候,有几个细节要注意:

  • Z-order是硬件寄存器控制的。A133的Display Engine里有个寄存器叫LAYER_ZORDER,每个图层对应一个值。范围一般是0~7,0在最底下,7在最上面。
  • 同Z-order的处理:如果两个图层设了相同的Z-order值,硬件会按照图层ID的顺序来叠。这个顺序是固定的,别指望它能自动调整。
  • VE图层的特殊性:VE图层通常固定在最底层或最顶层,取决于你的配置。我一般把它放在UI图层下面,这样视频播放时,字幕和控件可以浮在上面。

我的习惯:我会在系统初始化时,把Z-order分配表写死。比如:

Z-order 0: 壁纸图层
Z-order 1: VE视频图层
Z-order 2: G2D图层(用于特效)
Z-order 3: UI图层(桌面)
Z-order 4: UI图层(弹窗)
Z-order 5: UI图层(状态栏)

这样每个图层的位置是确定的,调试起来一目了然。别动态改Z-order,除非你特别清楚自己在干什么——我曾经试过动态调整,结果图层闪得跟迪斯科似的。

4.4 合成流程:从图层到屏幕

整个合成流程,我画个简单的流水线给你看:

  1. 图层准备:每个图层准备好自己的帧缓冲区(FrameBuffer),里面存着像素数据。
  2. 硬件读取:Display Engine的DMA控制器,按Z-order顺序,从低到高依次读取每个图层的像素。
  3. Alpha混合:每读一个图层,就跟当前合成结果做一次Alpha混合。注意,这里是逐像素进行的,硬件流水线处理,速度极快。
  4. 输出到屏幕:所有图层合成完毕后,最终结果送到显示接口(比如MIPI DSI或HDMI),刷新到屏幕上。

这里有个性能关键点——图层数量直接影响带宽。每增加一个图层,Display Engine就要多读一次内存。如果图层太多,内存带宽吃紧,帧率就会掉。A133的Display Engine支持4个UI图层,但如果你只用了2个,就别开满4个,省点带宽给其他模块用。

实战经验:我做过一个测试,4个UI图层全开,1080p@60fps的情况下,内存带宽占用大约1.2GB/s。如果降到2个图层,带宽直接砍半到600MB/s。所以,能用2个图层解决的问题,别用4个。

4.5 避坑总结

嗯,最后总结几个我踩过的坑,你记一下:

  • 图层格式不匹配:不同图层的像素格式(ARGB8888、RGB565、NV12等)混用,会导致混合结果异常。统一格式,或者确保硬件支持格式转换。
  • Z-order冲突:两个图层设了相同Z-order,结果一个盖住了另一个。调试时先检查寄存器值。
  • Alpha混合溢出:如果Alpha值超过255(8位),硬件会截断,导致透明度不对。确保Alpha值在0~255范围内。
  • VE图层闪烁:VE图层和UI图层的帧率不同步,会导致画面撕裂。解决办法是开启VSync同步,或者用双缓冲。

图层合成这块,说白了就是硬件资源的管理。你只要把每个图层的角色、透明度、堆叠顺序搞清楚,剩下的交给硬件去算。但千万别想当然——我每次改Z-order配置,都会先拿示波器量一下VSync信号,确认没问题再提交代码。小心驶得万年船嘛。