我在Ultra96/Ultra96-V2上尝试在Ubuntu20.04上运行Lima (DRI Lima版本)

请注意(2022年10月12日补充)

请参考我于2022年10月发布的以下文章,这是一篇在2021年4月发布的旧文章,其中包含了过时的内容。

在ZynqMP上尝试在Ubuntu22.04上运行Lima(DRI Lima编)的文章。

首先

Lima是Mali-400/450的开源图形驱动程序。我在Ultra96/Ultra96-V2上的Ubuntu 20.04上试验性地运行了Lima。由于运行起来有点困难,所以我会分几次说明运行的方法。

    概要編
    DRM Lima 編
    DRI Lima 編 (この記事)
    共有バッファ編
    ストライド問題編
    インストール編
    Ubuntu Desktop ビルド編

本篇文章将讲解有关Mesa提供的DRI Lima驱动适配到xlnx所需要的修正点以及Debian软件包的构建方法。

需要注意的是,截至到目前(2021年4月),我们确认Ubuntu 20.04的gnome-shell可以运行,但请注意,并不是所有应用程序都进行了确认。

DRI Lima 是什麼?

DRI Lima是Mesa的DRI(Direct Rendering Infrastructure)驱动程序,它将OpenGL命令序列转换为Lima命令,并使用DRM Lima将命令发送到硬件。DRI Lima在用户空间尽可能多地处理,而DRM Lima在内核空间执行必要的操作。

Fig.1 DRI Lima

图1 利马DRI

将DRI Lima转换为xlnx兼容的形式。

在 Ubuntu 20.04 中使用的 libGL DRI 驱动程序在 Debian 软件包中提供的 libgl1-mesa-dri 中。而且,该软件包中包含了适用于 Lima 的 DRI 驱动程序。然而,令人遗憾的是,该软件包中包含的 Lima DRI 不支持 DDX Xlnx 和 DRM Xlnx。因此,您需要单独构建支持 xlnx 的 DRI Lima。本章将解释这些更改的内容。

源代码

Ubuntu 20.04 使用的 libgl1-mesa-dri 版本为 20.2.6-0ubuntu0.20.04.1。源代码可通过 apt-get 的 source 命令进行下载。

shell$ apt-get source mesa=20.2.6-0ubuntu0.20.04.1
shell$ cd mesa-20.2.6

添加图书馆

为了使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 7cd8666..c85dc74 100644
--- a/src/gallium/targets/dri/meson.build
+++ b/src/gallium/targets/dri/meson.build
@@ -84,6 +84,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/targets/dri/target.c中添加xlnx的入口点。

index f71f690..e8f4340 100644
--- a/src/gallium/targets/dri/target.c
+++ b/src/gallium/targets/dri/target.c
@@ -110,6 +110,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
 

地理環境研究所利馬的建築

在本章中,将介绍如何构建适用于xlnx的DRI Lima的Debian软件包。

构建环境

为了构建Mesa的Debian软件包,需要许多不同的包,如适用于arm64的编译器,而且这些软件包的版本必须相互兼容。在PC(x86架构)上交叉编译这些环境,或者使用QEMU等虚拟环境进行构建并不顺利。因此,我安装了Ubuntu20.04的CUI版本在Ultra96-V2上,并进行了自主编译。使用自主编译就无需担心版本不一致的问题。

在Ultra96-V2上的Ubuntu 20.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=20.2.6-0ubuntu0.20.04.1
shell$ cd mesa-20.2.6

修改源代码

在源代码中进行了修正以使其与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 c1f316b..d1b0bcf 100644
--- a/debian/control
+++ b/debian/control
@@ -427,4 +427,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 cbbf298..b457d91 100755
--- a/debian/rules
+++ b/debian/rules
@@ -202,6 +202,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 \\

构建软件包

下面是构建整个软件包的方法。

shell$ sudo debian/rules binary

由于Ultra96-V2的I/O速度较慢,可能需要一些时间。请耐心等待,然后您将获得包含libgl1-mesa-xlnx-dri软件包在内的所有Debian软件包。

shell$ dpkg --info ../libgl1-mesa-xlnx-dri_20.2.6-0ubuntu0.20.04.1_arm64.deb 
 new Debian package, version 2.0.
 size 5167796 bytes: control archive=1048 bytes.
    1095 bytes,    21 lines      control              
     397 bytes,     5 lines      md5sums              
 Package: libgl1-mesa-xlnx-dri
 Source: mesa
 Version: 20.2.6-0ubuntu0.20.04.1
 Architecture: arm64
 Maintainer: Debian X Strike Force <debian-x@lists.debian.org>
 Installed-Size: 18260
 Depends: libc6 (>= 2.29), libdrm-amdgpu1 (>= 2.4.100), libdrm-nouveau2 (>= 2.4.66), libdrm-radeon1 (>= 2.4.31), libdrm2 (>= 2.4.89), libelf1 (>= 0.142), libexpat1 (>= 2.0.1), libglapi-mesa (= 20.2.6-0ubuntu0.20.04.1), libllvm11 (>= 1:9~svn298832-1~), libsensors5 (>= 1:3.5.0), libstdc++6 (>= 5.2), libunwind8, libzstd1 (>= 1.3.2), zlib1g (>= 1:1.1.4)
 Section: libs
 Priority: optional
 Multi-Arch: same
 Homepage: https://mesa3d.org/
 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.

仓库

本章所介绍的内容已经在以下的 GitHub 代码仓库中准备好了。

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

提供Debian软件包

在前一章中详细说明了自建方法,但由于需要 Ultra96 + Ubuntu 20.04 的构建环境,所以非常繁琐。因此,我已经在以下的 github 仓库中准备了适用于 Ubuntu 20.04 的 Debian 软件包,如果你觉得自己构建太麻烦,可以直接下载使用。

    https://github.com/ikwzm/ZynqMP-FPGA-Ubuntu20.04-Lima-Ultra96

    libgl1-mesa-xlnx-dri_20.2.6-0ubuntu0~20.04.1_arm64.deb

補足 libGL 在加载 DRI 驱动程序的机制。

在本章中,将会解释libGL加载DRI驱动的机制。

创建dri3屏幕

对于DRI3情况,可以调用dri3_create_screen()来生成屏幕。该屏幕生成函数通过以下步骤加载DRI驱动程序。

    调用glx_screen_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驱动程序。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 驱动名称对应的动态库到内存中。

    创建一个搜索路径的数组(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() 函数将返回与 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;
}

获取扩展驱动_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)
  :

请提供下列资料作为参考。

    Lima web (https://gitlab.freedesktop.org/lima/web)
    Mesa 3D and Direct Rendering Infrastructure wiki (https://dri.freedesktop.org/wiki)
    https://github.com/ikwzm/ZynqMP-FPGA-Ubuntu20.04-Lima-Ultra96
    https://github.com/ikwzm/mesa-xlnx

bannerAds