libdrm samples
本文转载自:libdrm samples。
1. Background
DRM(Direct Rendering Manager) is the kernel part of DRI(Direct Rendering Infrastructure) originally designed for Linux X display server. In this artical, we focus on how to use libdrm.
DRM mainly has two parts:
- KMS: kernel mode setting
- GEM: Graphics Execution Manager, buffer management
DRM major concepts:
- Framebuffer: Memory infomation such as width, height, depth, bpp, pixel format, etc.
- CRTC: Mode information, resolution, depth, polarity, porch, refresh rate, etc
- Encoder: convert digital signal from CRTC to appropriate analog level, eDP, MIPI, …
- Connector: Physical connector like HDMI,DVI-D,VGA,S-Video
2. librdrm
DRM exports API through ioctl, libdrm is a user mode library to wrap these ioctls. The general steps to use the libdrm are:
open
/drmOpen
/dev/dri/cardN
device node- call
drmModeGetResources
, get all thedrmModeRes
resources. Resources include all the fb, crtc, encoder, connector, etc. - loop through
drmModeRes
structure, calldrmModeGetConnector
, get the first connected connector(DRM_MODE_CONNECTED) drmModeConnector
stores all the supporting mode, choose one from them.- loop through
drmModeRes
,calldrmModeGetEncoder
. If the encoder matches with the selected mode, save thedrmModeModeInfo
for later use. - create
kms_driver
, create buffer object(BO),get the pitch of the BO,and map the BO to user space. - draw on the BO using cairo or whatever graphic toolbox you like.
- get original display mode by calling
drmModeGetCrtc
, this will be used after program exit to restore the original mode. - get frame buffer ID by calling
drmModeAddFB
, whose argument is BO handle. - call
drmModeSetCrtc
with frame buffer ID, the BO attached with the FB is outputed to display.
3. a basic sample
The code snippet that follows these steps:
1 | fd = open("/dev/dri/card0", O_RDWR); |
Open drm device, get drmModeRes
based on fd. drmModeRes
is the central point for following operations, incuding searching connectors and encoders. This picture shows some of the structures in libdrm. The arrows are data flow.
1 | for(i=0; i < resources->count_connectors; ++i){ |
Search resource->connector
array,get the first connected( DRM_MODE_CONNECTED
) connector. Get drmModeInfo
by connector->modes[0]
.
1 | for(i=0; i < resources->count_encoders; ++i){ |
In all encoders (resource->encoders
array), search the matched encoder with connected connector.
1 | kms_create(fd, &kms_driver); |
create kms_driver
, create BO(buffer object), get the pitch of the BO.
1 | kms_bo_map(kms_bo, &map_buf); |
map BO to user space, map_buf
stores the memory address.
1 | orig_crtc = drmModeGetCrtc(fd, encoder->crtc_id); |
get original display mode by calling drmModeGetCrtc
, this is used to restore display mode after program exits.
1 | kms_bo_get_prop(kms_bo, KMS_HANDLE, &bo_handle); |
get BO handle, call drmModeAddFB
, get Frame Buffer id (fb_id
). set display mode by calling drmModeSetCrtc
,output the FB content(map_buf
) associated with the BO.
1 | getchar() |
wait for user input, restore the original display mode and quit the program.
4. page flip
Above sample draws on the same BO when it is displayed, thus it will probably make display flicker. Page flip use double buffer mechanisam, can be used to avoid flicker. Two frame buffers are created, each associated a BO. The BO being drawn is not the same BO being displayed. The application maintains two frame buffers. Picture is drawn on current frame buffer, and drmModePageFlip
is called to send another frame buffer to crtc when vblank comes. Two frame buffers are switched in this way.
In this example, select
is used to monitor multiple fds.
1 | struct termios old_tio, new_tio; |
close line buffer of stdin in order for the program to receive any key without input enter.
1 | while(1){ |
The main loop of the program. stdin is put into monitor list of select
. Whenever terminal has input, select
return ready. ESC or ‘q’ is used to quit the program. We use drmHandleEvent
to handle event on the drm device. evctx
is an instance of drmEventContext
structure, evctx.page_flip_handler
points to the page flip handler.
1 | void page_flip_handler(int fd, unsigned int frame, |
page_flip_handler
get current fb id, get next fb id, call drmModePageFlip
. The next fb is set to crtc, frame buffer is swtiched.
1 | ret = drmModeSetCrtc(fd, orig_crtc->crtc_id, orig_crtc->buffer_id, |
cleanup, restore work after program exists. tcsetattr is used to restore original terminal setting.