はじめに
Lima とは Mali-400/450 用のオープンソースなグラフィックドライバです。筆者は Lima を ZynqMP 向け Ubuntu 22.04 で試験的に動かしてみました。動かすのに少々苦労したので、その方法を何回かに分けて説明します。
- 概要編
- DRM Lima 編
- DRI Lima 編 (この記事)
- 共有バッファ編
- ストライド問題編
- GEM キャッシュ編
- インストール編
- [glmrak2編]
この記事では Mesa が提供している DRI Lima ドライバを xlnx に対応するための修正点や、Debian パッケージのビルド方法を説明します。
DRI Lima とは
DRI Lima はMesa(オープンソースな OpenGL 実装)の DRI(Direct Rendering Infrastructure)ドライバです。DRI Lima は、OpenGL コマンドシーケンスを Lima のコマンドに変換し、DRM Lima を使用してコマンドをハードウェアに送信します。DRI Lima は、DRM Lima がカーネル空間で必要なことをすべて実行する間、ユーザー空間で可能な限り多くの処理を行います。
Fig.1 DRI Lima
DRI Lima を xlnx 対応にする
Ubuntu 22.04 に使われている libGL の DRI ドライバは libgl1-mesa-dri という Debian パッケージで提供されています。そして、このパッケージには Lima に対応した DRI ドライバが含まれています。しかし、残念ながらこのパッケージに含まれている DRI Limaは DDX Xlnx および DRM Xlnx に対応していません。そのため、xlnx に対応した DRI Lima を別途ビルドする必要があります。この章ではその変更点を説明します。
ソースコード
Ubuntu 22.04 に使われている libgl1-mesa-dri のバージョンは 22.0.5-0ubuntu0.1 です。ソースコードは apt-get の source コマンドによってダウンロードします。
shell$ apt-get source mesa=22.0.5-0ubuntu0.1
shell$ cd mesa-22.0.5
ライブラリの追加
DRI Lima が xlnx に対応するためには、/usr/lib/aarch64-linux-gnu/dri/xlnx_dri.so という動的ライブラリが必要です。
実は libgl1-mesa-dri が提供している DRI ドライバは、ファイルこそ多数ありますが、一つのライブラリファイルをハードリンクして別名を与えているにすぎません。したがって、xlnx_dri.so という名前の動的ライブラリを作るだけならば、src/gallium/targets/dri/meson.build に次のように 'xlnx_dri.so' を追加するだけで、ビルド時に xlnx_dri.so が生成されます。ただしこのままだと xlnx に対応したエントリポイントが無いので動的ライブラリとのリンクに失敗します。
index d066ce6..d1b0824 100644
--- a/src/gallium/targets/dri/meson.build
+++ b/src/gallium/targets/dri/meson.build
@@ -91,6 +91,7 @@ foreach d : [[with_gallium_kmsro, [
'st7735r_dri.so',
'stm_dri.so',
'sun4i-drm_dri.so',
+ 'xlnx_dri.so',
]],
[with_gallium_radeonsi, 'radeonsi_dri.so'],
[with_gallium_nouveau, 'nouveau_dri.so'],
エントリポイントの追加
DRI Lima が xlnx に対応するためには、xlnx_dri.so という名前の 動的ライブラリがあるだけでは不十分です。それに加えて、動的ライブラリに __driDriverGetExtensions_xlnx() という関数がエントリポイントとして定義されていなければなりません。そこで、src/gallium/targes/dri/target.c に xlnx のエントリポイントを追加します。
index 2ac9ef4..ae5a7b3 100644
--- a/src/gallium/targets/dri/target.c
+++ b/src/gallium/targets/dri/target.c
@@ -120,6 +120,7 @@ DEFINE_LOADER_DRM_ENTRYPOINT(st7586)
DEFINE_LOADER_DRM_ENTRYPOINT(st7735r)
DEFINE_LOADER_DRM_ENTRYPOINT(stm)
DEFINE_LOADER_DRM_ENTRYPOINT(sun4i_drm)
+DEFINE_LOADER_DRM_ENTRYPOINT(xlnx)
#endif
DRI Lima のビルド
この章では、xlnx に対応した DRI Lima の Debian パッケージをビルドする方法について説明します。
ビルド環境
Mesa の Debian パッケージをビルドするためには、arm64 用のコンパイラなどの様々なパッケージが必要になります。そしてそれらのパッケージのバージョンが整合していなければなりません。これらの環境を PC (x86 アーキテクチャ) 上でクロスコンパイルしたり、QEMU 等の仮想環境をつかってビルドするとうまくいきませんでした。そこで筆者は、Ultra96-V2 に Ubuntu22.04 の CUI 版をインストールして、セルフコンパイルしました。セルフコンパイルだとバージョンの不整合に悩む必要がありません。
Ultra96-V2 上の Ubuntu 22.04 で次のようにしてビルド環境を構築します。
shell$ sudo apt-get build-dep mesa
shell$ sudo apt-get install cmake valgrind libunwind-dev libconfig-dev
ソースコードのダウンロード
ソースコードは apt-get の source コマンドによってダウンロードします。
shell$ apt-get source mesa=22.0.5-0ubuntu0.1
shell$ cd mesa-22.0.5
ソースコードの修正
前章で説明した DRI Lima を xlnx に対応するための修正をソースコードに行います。ここでは省略します。
追加パッケージの追加
前章までの修正で xlnx に対応した DRI Lima (xlnx_dri.so) がビルドできます。しかし、このままだと、libgl1-mesa-dri パッケージに xlnx_dri.so が含まれることになり、すでに libgl1-mesa-dri パッケージがインストールされたシステムに xlnx_dri.so を追加するためには libgl1-mesa-dri パッケージそのものを上書きしなければなりません。xlnx_dri.so を含むパッケージのバージョン番号を変えるにしても変えないにしても管理上の問題が起こりそうです。
そこで、別途 xlnx_dri.so だけを含んだ libgl1-mesa-xlnx-dri パッケージを作っておきます。すでに libgl1-mesa-dri パッケージがインストールされたシステムには、このパッケージを後から追加インストールすることで xlnx_dri.so だけをインストールできるようにしておきます。
具体的には debian/control と debian/rules を次のように修正します。
index 061ee58..4cf2442 100644
--- a/debian/control
+++ b/debian/control
@@ -429,4 +429,23 @@ Description: free implementation of the OpenCL API -- ICD runtime
provides a standardized interface for computational analysis on graphical
processing units.
+Package: libgl1-mesa-xlnx-dri
+Section: libs
+Architecture: any
+Pre-Depends: ${misc:Pre-Depends}
+Depends:
+ ${shlibs:Depends},
+ ${misc:Depends}
+Multi-Arch: same
+Description: free implementation of the OpenGL API -- xlnx dri module
+ This version of Mesa provides GLX and DRI capabilities: it is capable of
+ both direct and indirect rendering. For direct rendering, it can use DRI
+ modules from the libgl1-mesa-dri package to accelerate drawing.
+ .
+ This package does not include the OpenGL library itself, only the DRI
+ modules for accelerating direct rendering.
+ .
+ For a complete description of Mesa, please look at the
+ libglx-mesa0 package.
+
# vim: tw=0
index 9dc0560..91cc60a 100755
--- a/debian/rules
+++ b/debian/rules
@@ -216,6 +216,11 @@ override_dh_install:
rm debian/tmp/usr/lib/*/libEGL_mesa.so
rm debian/tmp/usr/lib/*/libGLX_mesa.so
+ # Copy the hardlinked xlnx_dri.so correctly.
+ install -m755 -d debian/libgl1-mesa-xlnx-dri/usr/lib/${DEB_HOST_MULTIARCH}/dri/
+ cp debian/tmp/usr/lib/${DEB_HOST_MULTIARCH}/dri/xlnx_dri.so \
+ debian/libgl1-mesa-xlnx-dri/usr/lib/${DEB_HOST_MULTIARCH}/dri/
+
# Copy the hardlinked *_dri.so correctly.
install -m755 -d debian/libgl1-mesa-dri/usr/lib/${DEB_HOST_MULTIARCH}/dri/
mv debian/tmp/usr/lib/${DEB_HOST_MULTIARCH}/dri/*_dri.so \';
CntSty={
ParSty='diff
パッケージのビルド
次のようにしてパッケージ全体をビルドします。
shell$ sudo debian/rules binary
Ultra96-V2 だと I/O が遅いせいか、多少時間がかかります。我慢して待っているとlibgl1-mesa-xlnx-dri パッケージが含まれた Mesa のすべての Debian パッケージが出来ます。
shell$ dpkg --info ./libgl1-mesa-dri_22.0.5-0ubuntu0.1_arm64.deb
new Debian package, version 2.0.
size 6920362 bytes: control archive=1390 bytes.
1115 bytes, 22 lines control
3665 bytes, 47 lines md5sums
168 bytes, 5 lines * postinst #!/bin/sh
168 bytes, 5 lines * postrm #!/bin/sh
168 bytes, 5 lines * preinst #!/bin/sh
168 bytes, 5 lines * prerm #!/bin/sh
Package: libgl1-mesa-dri
Source: mesa
Version: 22.0.5-0ubuntu0.1
Architecture: arm64
Maintainer: Debian X Strike Force <debian-x@lists.debian.org>
Installed-Size: 21569
Depends: libc6 (>= 2.34), libdrm-amdgpu1 (>= 2.4.105), libdrm-nouveau2 (>= 2.4\
.66), libdrm-radeon1 (>= 2.4.31), libdrm2 (>= 2.4.89), libelf1 (>= 0.142), libe\
xpat1 (>= 2.0.1), libgcc-s1 (>= 3.0), libglapi-mesa (= 22.0.5-0ubuntu0.1), libl\
lvm13, libsensors5 (>= 1:3.5.0), libstdc++6 (>= 11), libvulkan1 (>= 1.2.131.2),\
libzstd1 (>= 1.4.0), zlib1g (>= 1:1.1.4)
Recommends: libgl1-amber-dri
Section: libs
Priority: optional
Multi-Arch: same
Homepage: https://mesa3d.org/
Description: free implementation of the OpenGL API -- DRI modules
This version of Mesa provides GLX and DRI capabilities: it is capable of
both direct and indirect rendering. For direct rendering, it can use DRI
modules from the libgl1-mesa-dri package to accelerate drawing.
.
This package does not include the OpenGL library itself, only the DRI
modules for accelerating direct rendering.
.
For a complete description of Mesa, please look at the
libglx-mesa0 package.
リポジトリ
この章で説明した内容は以下の github リポジトリにあります。
Debian パッケージの提供
前章で説明したセルフビルド方法は、ビルド環境に Ultra96 + Ubuntu 22.04 が必要なこともあって、とっても面倒です。そこですでに Ubuntu 22.04 用にビルドした Debian パッケージを以下の github リポジトリに用意しています。自分でビルドするのが面倒な人はダウンロードしてください。
-
https://github.com/ikwzm/mesa-xlnx/tree/mesa-xlnx-dri_22.0.5-0ubuntu0.1
- libgl1-mesa-xlnx-dri_22.0.5-0ubuntu0.1_arm64.deb
補足 libGL が DRI ドライバをロードする仕組み
この章では、libGL が DRI ドライバをロードする仕組みを説明します。
dri3_create_screen
DRI3 の場合は、dri3_create_screen() を呼び出してスクリーンを生成します。このスクリーン生成関数は次のような手順で DRI ドライバをロードします。
- glx_scrren_init() を呼び出して初期化します。
- loader_dri3_open() を呼び出して X-Server が現在使っている DRM ドライバのファイルディスクリプタ(psc->fd)を得ます。
例) X-Server が /dev/dri/card0 を使っていたならばこのデバイスファイルのファイルディスクリプタ - loader_get_driver_for_fd() を呼び出して 2で得たファイルディスクリプタのDRM ドライバ名(driverName)を得ます。
例) /dev/dri/card0 のDRMドライバ名が xlnx ならば "xlnx" - driOpenDriver() を呼び出して3で得たDRI ドライバ名の DRI ドライバをロードします。driOpenDriver() は次節で説明します。
static struct glx_screen *
dri3_create_screen(int screen, struct glx_display * priv)
{
xcb_connection_t *c = XGetXCBConnection(priv->dpy);
const __DRIconfig **driver_configs;
const __DRIextension **extensions;
const struct dri3_display *const pdp = (struct dri3_display *)priv->dri3Display;
struct dri3_screen *psc;
__GLXDRIscreen *psp;
struct glx_config *configs = NULL, *visuals = NULL;
char *driverName, *tmp;
int i;
unsigned char disable;
psc = calloc(1, sizeof *psc);
if (psc == NULL)
return NULL;
psc->fd = -1;
if (!glx_screen_init(&psc->base, screen, priv)) {
free(psc);
return NULL;
}
psc->fd = loader_dri3_open(c, RootWindow(priv->dpy, screen), None);
if (psc->fd < 0) {
int conn_error = xcb_connection_has_error(c);
glx_screen_cleanup(&psc->base);
free(psc);
InfoMessageF("screen %d does not appear to be DRI3 capable\n", screen);
if (conn_error)
ErrorMessageF("Connection closed during DRI3 initialization failure");
return NULL;
}
psc->fd = loader_get_user_preferred_fd(psc->fd, &psc->is_different_gpu);
driverName = loader_get_driver_for_fd(psc->fd);
if (!driverName) {
ErrorMessageF("No driver found\n");
goto handle_error;
}
extensions = driOpenDriver(driverName, &psc->driver);
if (extensions == NULL)
goto handle_error;
:
(後略)
:
}
driOpenDriver
driOpenDriver() は loader_open_driver() を呼び出します。その際、DRI ドライバをサーチするディレクトリの環境変数名を渡します。loader_open_driver() は次節で説明します。
_X_HIDDEN const __DRIextension **
driOpenDriver(const char *driverName, void **out_driver_handle)
{
void *glhandle;
/* Attempt to make sure libGL symbols will be visible to the driver */
glhandle = dlopen(GL_LIB_NAME, RTLD_NOW | RTLD_GLOBAL);
static const char *search_path_vars[] = {
"LIBGL_DRIVERS_PATH",
"LIBGL_DRIVERS_DIR", /* deprecated */
NULL
};
const __DRIextension **extensions =
loader_open_driver(driverName, out_driver_handle, search_path_vars);
if (glhandle)
dlclose(glhandle);
return extensions;
}
loader_open_driver
loader_open_driver() は次の手順で指定された DRI ドライバ名に対応した動的ライブラリをメモリにロードします。
- サーチするパス名の配列(search_paths) を作ります。
- sprintf("%.*s/%s_dri.so",p, driver_name) という名前の動的ライブラリを dlopen() によりメモリにロードします。p はサーチするパス名です。
例) driver_name が "xlnx" ならば、サーチするディレクトリから "xlnx_dri.so" を探します。 - loader_get_extensions_name(driver_name) を呼んでget_extensions_name を得ます。
例) driver_name が "xlnx" ならば、"__driDriverGetExtensions_xlnx" になります。 - 動的ライブラリの get_extensions_name で指定されたシンボルのアドレス(get_extensions) を得ます。
- get_extensions で指定されたアドレスを関数呼び出しして extensions を得ます。
const struct __DRIextensionRec **
loader_open_driver(const char *driver_name,
void **out_driver_handle,
const char **search_path_vars)
{
char path[PATH_MAX], *search_paths, *next, *end;
char *get_extensions_name;
const struct __DRIextensionRec **extensions = NULL;
const struct __DRIextensionRec **(*get_extensions)(void);
search_paths = NULL;
if (geteuid() == getuid() && search_path_vars) {
for (int i = 0; search_path_vars[i] != NULL; i++) {
search_paths = getenv(search_path_vars[i]);
if (search_paths)
break;
}
}
if (search_paths == NULL)
search_paths = DEFAULT_DRIVER_DIR;
void *driver = NULL;
end = search_paths + strlen(search_paths);
for (char *p = search_paths; p < end; p = next + 1) {
int len;
next = strchr(p, ':');
if (next == NULL)
next = end;
len = next - p;
#if USE_ELF_TLS
snprintf(path, sizeof(path), "%.*s/tls/%s_dri.so", len, p, driver_name);
driver = dlopen(path, RTLD_NOW | RTLD_GLOBAL);
#endif
if (driver == NULL) {
snprintf(path, sizeof(path), "%.*s/%s_dri.so", len, p, driver_name);
driver = dlopen(path, RTLD_NOW | RTLD_GLOBAL);
if (driver == NULL)
log_(_LOADER_DEBUG, "MESA-LOADER: failed to open %s: %s\n",
path, dlerror());
}
/* not need continue to loop all paths once the driver is found */
if (driver != NULL)
break;
}
if (driver == NULL) {
log_(_LOADER_WARNING, "MESA-LOADER: failed to open %s (search paths %s)\n",
driver_name, search_paths);
*out_driver_handle = NULL;
return NULL;
}
log_(_LOADER_DEBUG, "MESA-LOADER: dlopen(%s)\n", path);
get_extensions_name = loader_get_extensions_name(driver_name);
if (get_extensions_name) {
get_extensions = dlsym(driver, get_extensions_name);
if (get_extensions) {
extensions = get_extensions();
} else {
log_(_LOADER_DEBUG, "MESA-LOADER: driver does not expose %s(): %s\n",
get_extensions_name, dlerror());
}
free(get_extensions_name);
}
if (!extensions)
extensions = dlsym(driver, __DRI_DRIVER_EXTENSIONS);
if (extensions == NULL) {
log_(_LOADER_WARNING,
"MESA-LOADER: driver exports no extensions (%s)\n", dlerror());
dlclose(driver);
}
*out_driver_handle = driver;
return extensions;
}
loader_get_extensions_name
loader_get_extensions_name() は driver_name で指定された DRI ドライバ名に対応するエントリポイント名を返します。例えば、driver_name が "xlnx" だった場合、"__driDriverGetExtensions_xlnx" になります。
char *
loader_get_extensions_name(const char *driver_name)
{
char *name = NULL;
if (asprintf(&name, "%s_%s", __DRI_DRIVER_GET_EXTENSIONS, driver_name) < 0)
return NULL;
const size_t len = strlen(name);
for (size_t i = 0; i < len; i++) {
if (name[i] == '-')
name[i] = '_';
}
return name;
}
__driDriverGetExtensions_xlnx
エントリポイント(__driDriverGetExtensions_xlnx) は動的ライブラリ(xlnx_dri.so) に定義されています。このエントリポイントは前章で説明した DRI Lima のビルドの際に追加されたものです。
#define DEFINE_LOADER_DRM_ENTRYPOINT(drivername) \
const __DRIextension **__driDriverGetExtensions_##drivername(void); \
PUBLIC const __DRIextension **__driDriverGetExtensions_##drivername(void) \
{ \
globalDriverAPI = &galliumdrm_driver_api; \
return galliumdrm_driver_extensions; \
}
:
:
DEFINE_LOADER_DRM_ENTRYPOINT(xlnx)
:
参考
Debian Package
以下の URL に、この記事で紹介した DRI Lima の修正を施した Debian Package を提供しています。また、修正用のパッチファイルも提供しているので参考にしてください。
旧記事
この記事は次の記事を加筆修正したものです。
その他
- Lima web (https://gitlab.freedesktop.org/lima/web)
- Mesa 3D and Direct Rendering Infrastructure wiki (https://dri.freedesktop.org/wiki)