4、MDP流水线架构:Layer Mixer、Pipe(VIG/RGB/DMA)、Destination Scaler、CSC(色彩空间转换)

好,咱们今天聊点硬核的。MDP(Mobile Display Processor)流水线,说白了就是高通显示子系统的“心脏”。你屏幕上每一帧画面,从GPU或视频解码器出来之后,都得经过这条流水线“加工”一遍,才能最终点亮屏幕。

我个人习惯把MDP流水线想象成一条汽车装配线。每个工位干不同的活,有的负责搬运原料(Pipe),有的负责调色(CSC),有的负责把多个零件拼在一起(Layer Mixer),最后还有个质检员负责缩放尺寸(Destination Scaler)。任何一个环节出问题,你看到的画面就会花屏、撕裂或者颜色不对。

我在项目中遇到过好几次,客户反馈说“屏幕颜色偏绿”,查了半天发现是CSC矩阵配置错了。嗯,这种坑踩过一次就忘不了。咱们今天就把这条流水线拆开,一个一个讲清楚。

4.1 Pipe:数据搬运工的三兄弟

MDP里的Pipe,就是负责从内存里把图像数据“搬”到处理单元的家伙。高通把Pipe分成了三种类型:VIG、RGB、DMA。你想想看,为什么要有三种?

说白了,是为了应对不同的数据源和性能需求。

Pipe类型 全称 主要用途 典型场景
VIG Video Input Gate 视频流输入,支持缩放和格式转换 摄像头预览、视频播放
RGB RGB Pipe 图形/UI图层,支持Alpha混合 桌面图标、游戏UI
DMA Direct Memory Access 纯数据搬运,不做任何处理 帧缓冲拷贝、离线渲染

VIG Pipe 是最“全能”的。它自带硬件缩放器,可以把1080p的视频直接缩放到4K输出。我记得有一次调试视频通话,画面总是卡顿,后来发现是VIG Pipe的缩放参数没对齐,导致带宽浪费。

RGB Pipe 则专注于UI图层。它支持每像素alpha通道,可以实现半透明效果。你手机桌面上的天气小部件,背景半透明,就是RGB Pipe的功劳。

DMA Pipe 最简单,也最“暴力”。它不干任何额外的事,就是一股脑把数据从A搬到B。适合做屏幕截图或者帧缓冲备份。我曾经用它来做“画中画”功能,直接把视频帧拷贝到另一个图层,效率很高。

我的小技巧: 在配置Pipe时,尽量让VIG Pipe处理视频流,RGB Pipe处理UI层。别把视频流塞给RGB Pipe,它没有硬件缩放器,会占用CPU资源,得不偿失。

4.2 Layer Mixer:图层“搅拌机”

多个Pipe的数据都准备好了,怎么合成一个完整的画面?这就轮到Layer Mixer登场了。

Layer Mixer,你可以把它理解成一个“硬件搅拌机”。它把多个图层(Layer)按照Z-order顺序叠在一起,处理透明度、混合模式,最终输出一帧完整的图像。

高通8155的MDP通常有多个Layer Mixer,每个Mixer可以处理最多4个或8个图层。为什么需要多个?因为现在的车载系统,仪表盘、中控、HUD,每个屏幕都需要独立的Mixer。

我在项目中遇到过一个问题:中控屏幕上的导航箭头总是被背景图片挡住。查了半天,发现是Layer Mixer的Z-order配置反了。嗯,这里要注意,Z-order值越大,图层越靠上。

// 伪代码示例:配置Layer Mixer的Z-order
layer_mixer_config_t mixer_cfg;
mixer_cfg.layer_count = 3;

// 图层0:背景(最底层)
mixer_cfg.layers[0].pipe_id = RGB_PIPE_0;
mixer_cfg.layers[0].z_order = 0;

// 图层1:导航箭头(中间层)
mixer_cfg.layers[1].pipe_id = VIG_PIPE_1;
mixer_cfg.layers[1].z_order = 1;

// 图层2:触摸反馈(最顶层)
mixer_cfg.layers[2].pipe_id = DMA_PIPE_2;
mixer_cfg.layers[2].z_order = 2;

// 应用配置
mdp_apply_layer_mixer_config(&mixer_cfg);
避坑指南: 我曾经因为图层数量超过Mixer上限,导致系统直接崩溃。每个Mixer支持的图层数是固定的,别超了。如果图层太多,可以考虑合并图层或者使用多个Mixer级联。

4.3 Destination Scaler:最后的“整形师”

Layer Mixer合成完画面后,输出分辨率可能和屏幕物理分辨率不一致。比如你合成的是1920x1080的画面,但屏幕是4K的。这时候就需要Destination Scaler(目标缩放器)来“整形”。

Destination Scaler位于MDP流水线的末端,紧挨着显示接口(DSI/DP)。它负责把合成后的画面缩放到屏幕的实际分辨率。

为什么不在Pipe阶段就缩放?你想想看,如果每个Pipe都单独缩放,不仅浪费算力,还可能导致图层之间的对齐误差。不如等所有图层合成好了,统一缩放一次,精度更高,效率也更高。

我记得在调试一个双屏异显项目时,主屏是4K,副屏是1080p。两个屏幕共用一个MDP,但每个屏幕有自己的Destination Scaler。配置的时候一定要区分清楚,别把主屏的缩放参数写到副屏去了。

参数 说明 典型值
src_width/src_height 输入分辨率(合成后的分辨率) 1920 x 1080
dst_width/dst_height 输出分辨率(屏幕物理分辨率) 3840 x 2160
scaling_filter 缩放算法(双线性、双三次等) BILINEAR / BICUBIC
我的建议: 如果对画质要求高,比如播放4K HDR视频,建议使用BICUBIC(双三次)滤波。虽然性能开销大一点,但画面边缘更平滑。普通UI界面用BILINEAR就够了。

4.4 CSC:色彩空间的“翻译官”

最后,咱们聊聊CSC(Color Space Conversion,色彩空间转换)。

不同的数据源,色彩空间可能不一样。摄像头采集的是YCbCr,GPU渲染的是sRGB,视频解码出来的是BT.2020。如果不做转换,直接混在一起,颜色就全乱了。

CSC模块就是干这个的——它把不同色彩空间的像素数据,统一转换到目标色彩空间(通常是屏幕支持的色彩空间)。

CSC的核心是一个3x3的矩阵运算。每个像素的R、G、B值乘以这个矩阵,再加上偏移量,就完成了转换。

// CSC矩阵示例:YCbCr转RGB
// 假设输入是BT.601标准的YCbCr
csc_matrix_t ycbcr_to_rgb = {
    .coeff = {
        { 1.164,  0.000,  1.596 },
        { 1.164, -0.392, -0.813 },
        { 1.164,  2.017,  0.000 }
    },
    .offset = { -16, -128, -128 }
};

// 应用CSC配置
mdp_configure_csc(VIG_PIPE_0, &ycbcr_to_rgb);

我在项目中遇到过最头疼的问题,就是HDR视频播放时颜色发灰。查了三天,发现是CSC矩阵用的是SDR的标准,没有切换到BT.2020的转换矩阵。嗯,这里要注意,HDR和SDR的CSC矩阵是完全不同的。

避坑指南: 我曾经因为CSC矩阵配置错误,导致整个屏幕偏紫色。排查方法很简单:用示波器抓取RGB通道的波形,对比标准色卡。如果波形不对,十有八九是CSC矩阵算错了。

4.5 流水线整体流程回顾

好了,咱们把整个MDP流水线串起来捋一遍:

  1. Pipe阶段: VIG/RGB/DMA Pipe从内存读取数据,VIG做缩放,RGB做alpha混合,DMA纯搬运。
  2. CSC阶段: 每个Pipe的输出经过CSC模块,统一转换到目标色彩空间。
  3. Layer Mixer阶段: 多个图层按照Z-order合成一帧完整画面。
  4. Destination Scaler阶段: 合成后的画面缩放到屏幕物理分辨率。
  5. 输出: 最终数据送到DSI或DP接口,点亮屏幕。

你想想看,这一套流程走下来,每一帧画面都要经过几十次矩阵运算和像素处理。高通8155能流畅驱动三块4K屏幕,靠的就是这条精心设计的硬件流水线。

我个人习惯在调试时,先确认Pipe配置是否正确,再看CSC矩阵,最后检查Layer Mixer和Destination Scaler。按这个顺序排查,90%的问题都能快速定位。

好,今天就聊到这里。下一章咱们讲讲MDP的时钟和带宽管理,那才是真正考验BSP工程师功底的地方。