DRI2
DRI2 to allocate memory
DRI (Direct Rendering Infrastructure) 是各家 open source 驅動程式
以 Intel 的 945GM
為例,在 kernel 有 drm.ko 與 i915.ko 兩個模組,對映到 userspace
- 有 drm 提供的libdrm.so 與 libdrm_intel.so
- 在 xserver 有 libglx.so 與 libdri2.so 這兩個 extension
- xf86-video-intel 提供的 intel_drv.so
- mesa 也有 libGL.so 以及 i915_dri.so。
intel_drv.so 被稱作 2D driver 或 DDX driver,i915_dri.so 則是 3D driver 或 DRI driver。 從一個 3D 程式被執行時到顯示在螢幕上,參與這個看似普通的過程背後就是上面這些檔案。
DRI 最重要的內涵在於,一個 X client 可以向 X server 取得視窗的 buffer(s),並利用硬體加速在上面直接繪圖, 不再透過 X server。Kernel 在 DRI 扮演的主要角色是讓多個 process 可以共享 (GEM) buffer, 還有接受與執行 process 傳來的硬體指令。這邊困難的地方之一在於 CPU 跟 GPU 都有各自的 MMU 與 cache,所以有許多同步的問題要處理。
libGL.so 在 DRI 架構裡扮演了 loader 的角色,它跑在 client 這邊。就像 OpenGL 與平台無關一樣, DRI driver 也與平台無關。loader 一方面載入 DRI driver,一方面又提供適當的 hook 讓 DRI driver 呼叫, 並實作了平台相關的部份。所以在 DRI driver 真的要用到 buffer 的時候,它會呼叫 loader 的 getBuffers 函數取得視窗的 buffer 與大小。 libglx.so 跟 libdri2.so 實作了 GLX 與 DRI2 這兩個 X extensions。在 X 的平台上, 對於 DRI driver 的要求,libGL.so 會利用 GLX 與 DRI2 協定取得所需的資訊,並傳回給 DRI driver。
一個快速驗證上面說法的方法是直接檢視系統的 /usr/lib/libGL.so
跟 /usr/lib/dri/i915_dri.so
。libGL.so
只是 loader,
檔案大小應該要比 i915_dri.so
小不少;而 i915_dri.so
與平台無關,
所以 ldd 應該不會看到 X11 的函式庫。如果再進一步想,那是不是只要提供適當的 loader,
DRI driver 也能用在 X 以外的平台?答案是肯定的。
在 compiz 剛出來的時候,有很多名詞跟著它一起冒出來。像是 XGL、GLX_EXT_texture_from_pixmap、還有 AIGLX 等。在 libGL.so 無法成功跟 X server 溝通使用 DRI 時,client 的 3D 繪圖指令會改經由 GLX 傳給 server 處理,這被稱作 indirect rendering。在早先,libglx.so 會利用純軟體的方式執行這些 3D 指令,所以 indirect rendering 跟 software rendering 是等價的。這個情形在 AIGLX 引入後有很大的改變。在 AIGLX 下,libglx.so 除了提供 GLX extension 實作外,它同時也是一個 DRI loader,可以利用 DRI driver 進行 3D 加速。因為 AIGLX,indirect rendering 不再直接被視為慢、無法接受,雖然它還是多了 client 到 server 的 overhead。AIGLX 即是 Accelerated Indirect GLX 的縮寫,非常直接。
DRI2 with GEM
目前 linux graphic 架構下統一由 DRM 來做管理, CRTC
, GEM
, 還有一些其他
而 DRM master 一般是由 display server (常用的是 Xorg) 掌握著, 以控制 CRTC, gpu memory allocate 狀況 然而 user 也需要 gpu memory 時, 但又不能使用 DRM master 這時, 透過 DRI2 + magic token 的方式 allocate GPU memory
假如要讓 Xorg 幫你畫, export flink name, 並 put image 至 x server 就可, 可參考 xvimage
#include <unistd.h>
// DRM
#include <drm/drm.h>
#include <drm/i915_drm.h>
#include <xf86drm.h>
#include <xf86drmMode.h>
#include <cstring>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
static Bool DRI2Authenticate(Display *dpy, XID window, unsigned int magic)
{
XExtDisplayInfo *info = DRI2FindDisplay(dpy);
xDRI2AuthenticateReq *req;
xDRI2AuthenticateReply rep;
XextCheckExtension(dpy, info, dri2ExtensionName, False);
LockDisplay(dpy);
GetReq(DRI2Authenticate, req);
req->reqType = info->codes->major_opcode;
req->dri2ReqType = X_DRI2Authenticate;
req->window = window;
req->magic = magic;
if (!_XReply(dpy, (xReply *)&rep, 0, xFalse)) {
UnlockDisplay(dpy);
SyncHandle();
return False;
}
UnlockDisplay(dpy);
SyncHandle();
return rep.authenticated;
}
int dri2_open(Display *dpy)
{
drm_auth_t auth;
char *driver, *device;
int fd;
// connect to Xserver
if (!DRI2Connect(dpy, DefaultRootWindow(dpy), &driver, &device))
return -1;
fd = open(device, O_RDWR);
if (fd < 0)
return -1;
// get magic token from kernel
if (drmGetMagic(fd, &auth.magic)) {
goto err_fd;
}
if (ioctl(fd, DRM_IOCTL_GET_MAGIC, &auth))
printf("Failed on drm ioctl DRM(get_magic) test\n");
else
printf("Success on drm ioctl DRM(get_magic) test\n");
drm_stats_t stats;
if (ioctl(fd, DRM_IOCTL_GET_STATS, &stats))
printf("Failed on drm ioctl DRM(get_status) test\n");
else
printf("Success on drm ioctl DRM(get_status) test\n");
// send magic token to xserver
if (!DRI2Authenticate(dpy, DefaultRootWindow(dpy), auth.magic))
goto err_fd;
return fd;
err_fd:
fprintf(stderr, "could not open right fd\n");
close(fd);
return -1;
}
#define WIDTH 300
#define STRIDE (WIDTH * 3)
#define HEIGHT 300
#define SIZE (HEIGHT * STRIDE)
#define DEPTH 24
int main(int argc, char const *argv[])
{
Display *dpy;
Screen *scr;
struct drm_i915_gem_create create;
struct drm_gem_flink flink;
struct drm_i915_gem_mmap_gtt map;
struct x11_overlay *priv;
unsigned int count, i, j;
int fd, x, y, w, h;
w = WIDTH;
h = HEIGHT;
XvAdaptorInfo *info;
XvImage *image;
XvPortID port = -1;
void *ptr, *mem;
GC gc;
dpy = XOpenDisplay(NULL);
if (dpy == NULL)
return -1;
scr = ScreenOfDisplay(dpy, DefaultScreen(dpy));
gc = XCreateGC(dpy, DefaultRootWindow(dpy), 0, NULL);
fd = dri2_open(dpy);
if (fd < 0)
goto err_dpy;
create.handle = 0;
create.size = ALIGN(create.size, 4096);
drmIoctl(fd, DRM_IOCTL_I915_GEM_CREATE, &create);
if (create.handle == 0) {
fprintf(stderr, "could not create i915 gem buffer\n");
goto err_image;
}
// create GEM flink, export GEM name, let anyone access this memory by name
flink.handle = create.handle;
if (drmIoctl(fd, DRM_IOCTL_GEM_FLINK, &flink))
goto err_create;
map.handle = create.handle;
// map memory
if (drmIoctl(fd, DRM_IOCTL_I915_GEM_MMAP_GTT, &map))
goto err_create;
ptr = mmap(0, create.size, PROT_READ | PROT_WRITE, MAP_SHARED, fd,
map.offset);
if (ptr == (void *)-1)
goto err_create;
// do something
// close gem memory
drmIoctl(fd, DRM_IOCTL_GEM_CLOSE, &create.handle);
err_fd:
close(fd);
err_dpy:
XCloseDisplay(dpy);
return -1;
}