一、什么是DRM

DRM( Direct Rendering Manager)即直接渲染管理器。它是为了解决多个程序对 Video Card 资源的协同使用问题而产生的。它向用户空间提供了一组 API,用以访问操纵 GPU。

简单理解,DRM是Linux下的图形渲染架构,用来管理显示输出和分配buffer。应用程序可以直接操纵 DRM的 ioctl 或者是用 framebuffer 提供的接口进行显示相关操作。后来封装成了 libdrm 库,让用户可以更加方便的进行显示控制。

总体框架如下
在这里插入图片描述

二、DRM包含的基本概念

要弄明白 DRM 是怎么把用户的绘图输出到显示屏上,绕不开以下几个概念,具体关系如下图所示:
在这里插入图片描述

2.1 DRM Framebuffer

它是一块内存区域,可以理解为一块画布,驱动和应用层都能访问它。绘制前需要将它格式化,设定绘制的色彩模式(例如RGB24,YUV 等)和画布的大小(分辨率)。

2.2 CRTC

阴极摄像管上下文。这个看名字很很难懂,但简单的来说他就是显示输出的上下文,可以理解为扫描仪。CRTC对内连接 Framebuffer 地址,对外连接 Encoder,会扫描 Framebuffer 上的内容,叠加上 Planes 的内容,最后传给Encoder。

2.3 Planes

平面。它和 Framebuffer 一样是内存地址。它的作用是干什么呢?打个比方,在电脑上,一边打字聊微信一边看电影,这里对立出来两个概念,打字是文字交互,是小范围更新的 Graphics 模式;看电影是全幅高速更新的 Video 模式,这两种模式将显卡的使用拉上了两个极端。

这时Planes就发挥了很好的作用,它给 Video 刷新提供了高速通道,使 Video 单独为一个图层,可以叠加在 Graphic 上或之下,并具有缩放等功能。

Planes 是可以有多个的,相当于图层叠加,因此扫描仪(CRTC)扫描的图像实际上往往是 Framebuffer 和 Planes 的组合(Blending)。

2.4 Encoder

编码器。它的作用就是将内存的 pixel 像素编码(转换)为显示器所需要的信号。简单理解就是,如果需要将画面显示到不同的设备(Display Device)上,需要将画面转化为不同的电信号,例如 DVID、VGA、YPbPr、CVBS、Mipi、eDP 等。

Encoder 和 CRTC 之间的交互就是我们所说的 ModeSetting,其中包含了前面提到的色彩模式、还有时序(Timing)等。

2.5 Connector

连接器。它常常对应于物理连接器 (例如 VGA, DVI, FPD-Link, HDMI, DisplayPort, S-Video等) ,它不是指物理线,在 DRM中,Connector 是一个抽象的数据结构,代表连接的显示设备,从Connector中可以得到当前物理连接的输出设备相关的信息 ,例如,连接状态,EDID数据,DPMS状态、支持的视频模式等。

三、drm demo用例分析

以rk的image_process.cc为例:

mpp解码->缓存drmdescript buff yuv420sp到缓存链表->rga 缩放合成格式转换->drm dumpbuffer list(通过handle操作)->drm送显(handler操作)

在这里插入图片描述

如上buffer操作都是通过硬件操作。

3.1 rga对图像镜像缩放合成格式转换过程

rga会先创建drm buffer来存放转换后的图像数据

AllocJoinBO(drm_fd, rga, jb, w, h)
	RkRgaAllocBuffer(drmfd, &bo, w, h, 32)
		drmIoctl(drm_fd, DRM_IOCTL_MODE_CREATE_DUMB, &arg);
		bo_info->handle = arg.handle;
	RkRgaGetBufferFd(&bo, &fd)	
		//将handler转换成fd以便后面drmModeSetPlane直接使用
		drmPrimeHandleToFD(bo_info->fd, bo_info->handle, 0, fd);

接着将多路AVFrame 通过rag进行缩放合成及格式转换

std::shared_ptr<JoinBO> jb = get_available_buffer(frames);//获取空闲的drm framebuffer
scale_frame_to(av_frame, jb.get(), i)		
	AVDRMFrameDescriptor *desc = (AVDRMFrameDescriptor *)av_frame->data[0];//data应该也是指向drm的某种buffer
	AVDRMLayerDescriptor *layer = &desc->layers[0];
	rga_info_t src, dst;
	src.fd = desc->objects[0].fd;
    rga_set_rect(&src.rect, 0, 0, av_frame->width, av_frame->height,
                 layer->planes[0].pitch,
                 layer->planes[1].offset / layer->planes[0].pitch,
                 RK_FORMAT_YCrCb_420_SP);	//设置源rect
    dst.fd = jb->fd;
    Rect &dts_rect = slice_rect[index];
    rga_set_rect(&dst.rect, dts_rect.x, dts_rect.y,
                 dts_rect.w, dts_rect.h,
                 jb->width, jb->height,
                 dst_format);         //设置目的rect及转换格式dst_format NV12/RGB888...
    rga.RkRgaBlit(&src, &dst, NULL); //进行数据转换及拷贝                    

转换完成后放入h->PushBO(jb);进行零拷贝显示

3.2、drm显示初始化

创建drm framebuffer并初始化drm显示组件

AllocJoinBO(drm_fd, rga, &crtc_jb, cur_mode.hdisplay, cur_mode.vdisplay)
add_fb(drm_fd, &crtc_jb, &crtc_fb_id)
	//将drm framebuffer添加到framebuffer从而在drmModeSetCrtc时显示,在这里可以设置显示格式nv12 rgb等
    drmModeAddFB2(fd, jb->width, jb->height, fourcc_fmt,
                            handles, pitches, offsets, fb_id, 0);
int ret = drmModeSetCrtc(dev.fd, crtc_id, crtc_fb_id, 0, 0, &conn_id, 1,
                         &cur_mode);

从函数参数可以得知需要crtc设备的id,connecter id及drm设备/dev/dri/card0 节点文件描述符。获取方式参考代码实现。

3.3、drm图像帧刷新

std::shared_ptr<JoinBO> sjb = PopBO(); //取得rga转换后的drm framebuffer
render_jb = sjb;
std::pair<uint32_t, bool> &render_fb = get_fb_id(dev.fd, jb_fb_map, render_jb.get());
	add_fb(dmafd, jb, &fb_id)
		drmModeAddFB2(fd, jb->width, jb->height, fourcc_fmt,
                            handles, pitches, offsets, fb_id, 0);
render_fb_id = render_fb.first;//将handler传入                            
drmModeSetPlane(dev.fd, plane_id, crtc_id, render_fb_id, 0,
                                  0, 0, render_jb->width, render_jb->height,
                                  0, 0, render_jb->width << 16, render_jb->height << 16);   
//根据vblank刷新                                  
drmModePageFlip(dev.fd, crtc_id, render_fb_id,
                              DRM_MODE_PAGE_FLIP_EVENT, &waiting_for_flip);                                            
Logo

更多推荐