一 上一篇介绍了 linux 的显示驱动drm 的架构,在这里按一定顺序回顾一下:
1 我把显示器连到显卡的DVI输出口, 这个连接抽象成 Connector
2 在 DVI 的 Connector 上驱动会分配 DVI 信号的 Encoder , 如果没分配, connector 资源上会找到 所有可用的 encoders
3 encoder 是为图像扫描现场 crtc 服务的, 驱动可能会给encoder 分配crtc , 或者从 encoder 的 possible_crtc 上能找到可用的
4 crtc 扫描现场要配置显示图像的物理内存区 fb
5 fb -> crtc -> encoder - > connector 这种关系绑定之后,绘图工作已经开始, 你可以在fb 上任意写画,然后立马得到显示!
6 然而为了避免图像撕裂,可以建立多 fb (缓冲) 通过 pageFlip 操作来刷新画图。
7 当然还有专为video 刷新用的plane , plane 也要绑定到 crtc 才能工作。
二 总结 + drm api 的使用:
api 使用参考 David Herrmann 的 drm-howto 以及 weston 还有 drm自带的 modetest 程序
drm api 核心配置就是要绑定一个 crtc 的关系 fb -> crtc -> encoder - > connector
我们来看“一”的回顾,咋是按照逆向的顺序?哈哈其实这样才顺理成章, api 如何用,往下看:
1 首先要打开drm 驱动模块,然后获取所有的资源
fd = open("/dev/dri/card0", O_RDWR | O_CLOEXEC);
drmModeRes res = drmModeGetResources(fd);
res 里有啥, res 里告诉了有几个connector , 几个 encoder , 几个 crtc 等以及他们的id , 完全不成套!
2 从 connector 开始, 顺藤摸瓜
先获取 connecotr 的具体资源
drmModeConnector * conn = drmModeGetConnector(fd, res->connectors[i]);
conn资源里重要的有两部分,一部分是通过线缆读出来显示器的 "modes " 如 1920x1080@60 等, 当然你要选一个最喜欢的
另一部分是encoder 按照一章节的2顺序开始找 encoder (看 drmModeConnector 的定义) :p
3 给encoder 寻找合适的 crtc
按照一章节的 3 顺序找 crtc
4 为 crtc 创建fb
drm 的例子 modetest 写的很详细, ARGB 可以直接 drmModeAddFB , 多平面的fb 可以用 drmModeAddFB2
5 绑定
核心四元组 < fb , crtc , conn , mode >
int drmModeSetCrtc(int fd, uint32_t crtcId, uint32_t bufferId,
uint32_t x, uint32_t y, uint32_t *connectors, int count,
drmModeModeInfoPtr mode);
6 pageFlip
extern int drmModePageFlip(int fd, uint32_t crtc_id, uint32_t fb_id,
uint32_t flags, void *user_data);
这个的玩法得好好记下,
首先 poll (drm_fd ) 可以收到 drm 的 POLLIN 消息, 消息里面无非两种一种是 VBLANK , 一种是 pageFlip complete
其次 收到 消息后必须要 调用drmHandleEvent(drm_fd , &evctx); 来处理消息 ,记得必须把你的 pageFlip 处理函数填到evctx里,
pageFlip 处理函数里干啥呢, pageFlip 一帧结束了,当然要 pageFlip 下一帧!
7 plane
plane 的玩法没搞明白,尝试了下,使用多缓冲 调 drmModeSetPlane 来换页可以实现视频的播放,但遇到两个问题:
其一是 SetPlane 的时机, 如何等待一个显示器的场同步? 看了 vblank 的code 感觉 vblank 是针对一个显卡的同步,但我有多个显示器呢?
其二是SetPlane 的运行耗时,我的 i3 cpu 执行一次用了 22 ~ 23 ms , 莫名其妙。
本想着plane 的yuv 通道可以节省资源,有这两大问题在就没法办了, pageFlip 的 fb 只能跟显示器相同颜 {MOD}空间,并且通常是 ARGB !!