我在使用适用于ZynqMP的Ubuntu22.04上尝试运行Lima(DRI Lima篇)

首先

Lima是Mali-400/450的开源图形驱动程序。本文作者尝试在ZynqMP的Ubuntu 22.04上试验性地运行了Lima。由于遇到了一些困难,因此将其方法分为几个部分进行解释。

    • 概要編

 

    • DRM Lima 編

 

    • DRI Lima 編 (この記事)

 

    • 共有バッファ編

 

    • ストライド問題編

 

    • GEM キャッシュ編

 

    • インストール編(近日公開)

 

    [glmrak2編]

在这篇文章中,将解释Mesa提供的DRI Lima驱动程序在xlnx上的修正点以及Debian软件包的构建方法。

DRI Lima是什么意思?

DRI Lima 是 Mesa(一种开源的 OpenGL 实现)的 DRI(直接渲染基础设施)驱动程序。DRI Lima 将 OpenGL 命令序列转换为 Lima 命令,并使用 DRM Lima 将命令发送到硬件上。DRI Lima 在用户空间尽可能多地进行处理,以便 DRM Lima 在内核空间中完成其所需的全部工作。

Fig.1 DRI Lima

图1 利马 DRI

 

将DRI Lima适应xlnx技术。

Ubuntu 22.04 使用的 libGL 的 DRI 驱动程序以 Debian 包 libgl1-mesa-dri 的形式提供。该包中包含了适用于 Lima 的 DRI 驱动程序。然而,遗憾的是,该包中包含的 Lima DRI 不支持 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的建筑

在这一章中,我们将介绍如何构建适用于 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)。然而,按照目前的方式,xlnx_dri.so 将被包含在 libgl1-mesa-dri 包中,如果要将 xlnx_dri.so 添加到已安装 libgl1-mesa-dri 包的系统中,就必须覆盖 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 儲存庫找到。

    https://github.com/ikwzm/mesa-xlnx

提供Debian软件包

前一章中提到的自建方法非常麻煩,因為需要Ultra96 + Ubuntu 22.04的建置環境。因此,我們在以下的github儲存庫中準備了已經針對Ubuntu 22.04建置的Debian套件。如果覺得自己建置太麻煩的話,請下載這些套件。

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屏幕

在DRI3的情况下,调用dri3_create_screen()来生成屏幕。此屏幕生成函数按以下步骤加载DRI驱动。

    1. 调用glx_screen_init()进行初始化。

 

    1. 调用loader_dri3_open()获取当前X-Server正在使用的DRM驱动程序的文件描述符(psc->fd)。

 

    1. 例如,如果X-Server使用/dev/dri/card0,则是该设备文件的文件描述符。

 

    1. 调用loader_get_driver_for_fd()获取通过2得到的文件描述符的DRM驱动程序名称(driverName)。

 

    1. 例如,如果/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() 函数会调用 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() 函数会按照以下步骤来加载与指定的 DRI 驱动名称对应的动态库到内存中。

    1. 创建一个名为search_paths的路径名数组。

 

    1. 通过sprintf(“%.*s/%s_dri.so”, p, driver_name)将名为driver_name的动态库加载到内存中,加载方法为dlopen()。其中p是要搜索的路径名。

 

    1. 例如,如果driver_name为”xlnx”,则从搜索目录中查找”xlnx_dri.so”。

 

    1. 调用loader_get_extensions_name(driver_name)来获取get_extensions_name。

 

    1. 例如,如果driver_name为”xlnx”,那么它将变成”__driDriverGetExtensions_xlnx”。

 

    1. 通过动态库中指定的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()函数返回与指定的DRI驱动程序名称driver_name对应的入口点名称。例如,如果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 取得驱动程式扩展_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软件包

我們在以下網址上提供了修復了DRI Lima的Debian軟件包。同時,我們也提供了修復用的補丁檔案作為參考。

    https://github.com/ikwzm/mesa-xlnx/tree/mesa-xlnx-dri_22.0.5-0ubuntu0.1

这是一篇过时的文章。

这篇文章是对下一篇文章进行了修改和补充。

    『Ultra96/Ultra96-V2 向け Ubuntu20.04で Lima を動かしてみた(DRI Lima 編)』@Qiita

其他

    • Lima web (https://gitlab.freedesktop.org/lima/web)

 

    Mesa 3D and Direct Rendering Infrastructure wiki (https://dri.freedesktop.org/wiki)
广告
将在 10 秒后关闭
bannerAds