Ultra96/Ultra96-V2 向け Ubuntu20.04で Lima を動かしてみた(共有バッファ編)







# 注意(2022年10月12日追記)


この記事は2021年4月に投稿したものであり、古い内容が含まれています。2022年10月に以下の記事を投稿しましたので参照してください。

・[『ZynqMP 向け Ubuntu22.04で Lima を動かしてみた(共有バッファ編)』@Qiita]


# はじめに


[Lima] とは Mali-400/450 用のオープンソースなグラフィックドライバです。筆者は Lima を Ultra96/Ultra96-V2 向け Ubuntu 20.04 で試験的に動かしてみました。動かすのに少々苦労したので、その方法を何回かに分けて説明します。



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



この記事では、DRI コンポーネント間でバッファを共有する仕組みである DRI2 と DRI3 について説明し、Lima を動かすために DDX Lima(Xorg Device Dependent X Driver for Lima) を DRI3 に対応した点を説明します。



  __なお、現時点(2021年4月)では、Ubuntu 20.04 の gnome-shell が動作するところまで確認していますが、すべてのアプリケーションで動作を確認したわけでは無いことに注意してください。__  




# 共有バッファの仕組み



## GEM について


[DRI(Direct Rendering Infrastructure)] は単一のソフトウェアではなく、いくつもの DRI コンポーネントで構成されています。これらの DRI コンポーネント間で画像データをやりとりするための手段として、共有バッファが用いられます。しかもディスプレイドライバやレンダリングエンジンのようなハードウェアでも使うため、DRM(irect Rendering Manager - Linux カーネル部分の DRI コンポーネント) でも使える共有バッファが必要です。

GEM(Graphics Excution Manager) は DRM の一部であり、複数のアプリケーションの間でバッファを共有するための API を提供しています。


![Fig.1 GEM(Graphics Excution Manager)](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24981/0eea0337-5093-b877-c22a-816da7d9e928.jpeg "Fig.1 GEM(Graphics Excution Manager)")

Fig.1 GEM(Graphics Excution Manager)

<br />




## DRI2 について


DRI2 は複数のアプリケーションの間でバッファを共有するための X Server の拡張機能です。DRI2 では、バッファの割り当ては X Server 自体によって行われ、DRI クライアントは DRI2 拡張機能で使用可能な DRI2GetBuffers などの操作を呼び出すことにより、これらのバッファを取得してウィンドウへのレンダリングを行います。内部的には、DRM 内の GEM が Linux カーネル内にバッファの実体を保持し、GEM 名(DRM にアクセスする複数のプロセスが同じバッファを参照できるようにするために GEM API によって提供されるグローバルハンドルの一種) を介して DRI クライアントにバッファの参照を渡します。DRI クライアントは渡された GEM 名を DRM に渡すことでバッファへのアクセス(open/mmap/closeなど)を行います。



残念ながら、DRI2 では Lima を動作させることが出来ませんでした。これはあくまでも筆者の推測ですが、どうやら DRI2 は、単一の DRM が提供するバッファを複数の DRI コンポーネントで共有することを想定しているようで、Lima のように 3D レンダリング専用の DRM とディスプレイ用の DRM にわかれているような、複数の DRM の間でバッファを共有することを想定していないようです(要検証)。

その結果、DRI2 の場合、Ubuntu20.04 の gnome-shell を動かそうとすると、libGL 内の DRI Lima Driver が X Server から渡された GEM 名でのバッファのオープンに失敗して、動きませんでした。




## DRI3 について


DRI3 は DRI2 で生じたいくつかの問題を修正するために 2013年に開発されました。DRI3 は DRI2 と比較して次の点が異なります。

* DRI3 クライアントは、バッファの割り当てを X Server に依存するのではなく、自分自身が行います。
* DRI3 は、 DRI2 が採用していた(古く安全で無い)GEM 名に基づくバッファオブジェクトの受け渡しをやめて、(安全で用途の広い)ファイル拡張子を使ったPRIME DMA-BUFs というバッファオブジェクトの受け渡しを採用しています。



ここで言う DMA-BUF というのは Linux カーネル内部の DMA バッファの共有 API の略称のことです。これは、異なる種類のデバイスドライバが管理している複数のデバイス間で DMA バッファを共有するための一般的なメカニズムを提供します。

DMA-BUF の特に重要な点は、異なるデバイスドライバ間で DMA バッファを共有できることです。DRI3 は この DMA-BUF という DMA バッファ共有 API を使うことによって、複数の DRM 間でバッファオブジェクトの共有を可能にしています。

バッファオブジェクトの受け渡しには DMA-BUF が提供するファイル記述子が使われます。



この DMA-BUF という機能は DRM Core にすでに実装されています。また、DRI Lima は DRI3 に対応しています。そして X Server が DRI3 拡張に対応するためには DDX(Device Dependent X)ドライバが DRI3 に対応している必要がありますが、残念ながら xlnx が提供している DDX ドライバは DRI3 は対応していません。


# DDX Xlnx を DRI3 対応にする


Lima を動かすためには DRI2 ではダメで DRI3 が必要なようです。しかし、X Server が DRI3 拡張に対応するためには DDXドライバが DRI3 に対応している必要がありますが、残念ながら xlnx が提供している DDX ドライバは DRI3 は対応していませんでした。この章では DDX Xlnx を DRI3 に対応するための変更点を説明します。


## リポジトリ


この章で説明する内容は以下の github リポジトリに用意しています。



* https://github.com/ikwzm/xf86-video-armsoc-xilinx
* https://github.com/ikwzm/xf86-video-armsoc-xilinx/tree/master
* https://github.com/ikwzm/xf86-video-armsoc-xilinx/tree/xilinx-dri3-develop



2021年4月現在、まだ開発中のため DRI3 に対応したソースコードは xilinx-dri3-develop ブランチにあります。master ブランチにはまだ反映されていないことに注意してください。master ブランチには修正前のソースコードがあります。


## armsoc_dri3.c


DRI3 に対応するために armsoc_dri3.c を新しく作ります。armsoc_dri3.c には次の関数および変数を定義します。



  * ARMSOCDRI3ScreenInit()
  * armsoc_sync_init()
  * armsoc_dri3_open()
  * armsoc_dri3_pixmap_from_fd()
  * armsoc_dri3_fd_from_pixmap()


### 　ARMSOCDRI3ScreenInit


DDX ドライバを DRI3 に対応するためには、初期化時に次の関数を用意して dri3_scrren_init() を呼び出す必要があります。

  * open() - DRM をオープンする関数
  * pixmap_from_fd - ファイル記述子で示されたバッファオブジェクトから Pixmap を作る関数
  * fd_from_pixmap - Pixmap からバッファオブジェクトのファイル記述子を得る関数

また、初期化時にバッファ共有のために miSyncShmScreenInit() を呼び出す必要があります。そこで次のような ARMSOCDRI3ScreenInit() を定義します。


```C:src/armsoc_dri3.c
static dri3_screen_info_rec armsoc_dri3_screen_info = {
	.version        = DRI3_SCREEN_INFO_VERSION  ,
	.open           = armsoc_dri3_open          ,
	.pixmap_from_fd = armsoc_dri3_pixmap_from_fd,
	.fd_from_pixmap = armsoc_dri3_fd_from_pixmap,
};
Bool
ARMSOCDRI3ScreenInit(ScreenPtr pScreen)
{
	ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
	if (!armsoc_sync_init(pScreen))
		return FALSE;
	if (!dri3_screen_init(pScreen, &armsoc_dri3_screen_info)) {
		WARNING_MSG("dri3_screen_init failed\\n");
		return FALSE;
	}
	return TRUE;
}
```



### 　armsoc_sync_init


armsoc_sync_init() は 前節のARMSOCDRI3ScreenInit() から呼び出され、miSyncShmScreenInit() を呼び出します。


```C:src/armsoc_dri3.c
static Bool
armsoc_sync_init(ScreenPtr pScreen)
{
	ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
	if (!xf86LoaderCheckSymbol("miSyncShmScreenInit")) {
		WARNING_MSG("SYNC extension fences disabled because "
					"miSyncShmScreenInit symbol unresolved\\n");
		return FALSE;
	}
	if (!miSyncShmScreenInit(pScreen)) {
		WARNING_MSG("miSyncShmScreenInit failed\\n");
		return FALSE;
	}
	return TRUE;
}

```



### 　armsoc_dri3_open


armsoc_dri3_open() は DRM ドライバをオープンして認証を行います。


```C:src/armsoc_dri3.c
static int
armsoc_dri3_open(ScreenPtr pScreen, RRProviderPtr provider, int* out)
{
	ScrnInfoPtr       pScrn   = xf86ScreenToScrn(pScreen);
	struct ARMSOCRec* pARMSOC = ARMSOCPTR(pScrn);
	int               fd;
	drm_magic_t       magic;
	if (!pARMSOC->deviceName) {
		DEBUG_MSG("pARMSOC->deviceName failed");
		goto badalloc;
	}
	fd = open(pARMSOC->deviceName, O_RDWR | O_CLOEXEC);
	if (fd < 0) {
		DEBUG_MSG("open(%s) failed", pARMSOC->deviceName);
		goto badalloc;
	}

	if (drmGetMagic(fd, &magic) < 0) {
		if (errno == EACCES) {
			goto success;
		} else {
			DEBUG_MSG("drmGetMagic(%s) failed", pARMSOC->deviceName);
			goto badmatch;
		}
	}
	DEBUG_MSG("drmGetMagic(%s) magic=%d", pARMSOC->deviceName, magic);
	if (drmAuthMagic(pARMSOC->drmFD, magic) < 0) {
		DEBUG_MSG("drmAuthMagic(%d,%d) failed", pARMSOC->drmFD, magic);
		goto badmatch;
	}
  success:
	*out = fd;
	DEBUG_MSG("DRI3 Open success");
	return Success;
  badmatch:
	close(fd);
	ERROR_MSG("DRI3 Open failed error=BadMatch");
	return BadMatch;
  badalloc:
	ERROR_MSG("DRI3 Open failed error=BadAlloc");
	return BadAlloc;
}	

```



### 　armsoc_dri3_pixmap_from_fd


armsoc_dri3_pixmap_from_fd() は他の DRM が管理しているバッファオブジェクト(を示す DMA-BUF API が生成したファイル記述子)から Pixmap を作る関数です。


```C:src/armsoc_dri3.c
static PixmapPtr
armsoc_dri3_pixmap_from_fd(ScreenPtr pScreen,
						   int       fd     ,
						   CARD16    width  ,
						   CARD16    height ,
						   CARD16    stride ,
						   CARD8     depth  ,
						   CARD8     bpp    )
{
	ScrnInfoPtr       pScrn   = xf86ScreenToScrn(pScreen);
	struct ARMSOCRec* pARMSOC = ARMSOCPTR(pScrn);
	PixmapPtr         pixmap  = NULL;
	struct ARMSOCPixmapPrivRec* priv;

```


depth と bpp(bit per pixel) の値をチェックします。DDX Xlnx では depth は８以上、bppは 8、16、32 のみサポートしています。


```C:src/armsoc_dri3.c
	if (depth < 8) {
		DEBUG_MSG("(fd=%d,width=%d,height=%d,stride=%d,depth=%d,bpp=%d) depth < 8 failed",
				  fd, width, height, stride, depth, bpp);
		goto failed;
	}
	switch (bpp) {
	case 8:
	case 16:
	case 32:
		break;
	default:
		DEBUG_MSG("(fd=%d,width=%d,height=%d,stride=%d,depth=%d,bpp=%d) bpp failed",
				  fd, width, height, stride, depth, bpp);
		goto failed;
	}

```


まずは Pixmap を生成します。その際、user_hint に ARMSOC_CREATE_PIXMAP_IMPORT を指定します。Pixmap を生成するとき、通常は GEM にバッファを確保してそのバッファオブジェクトとPixmapと結びつけるのですが、user_hint にこの値をセットしているとバッファオブジェクトを生成しません。

その代わりに、armsoc_bo_import_with_dim() が生成したバッファオブジェクトを Pixmap に結びつけます。armsoc_bo_import_with_dim() は、他の DRM  が管理しているバッファオブジェクト(を示す DMA-BUF API が生成したファイル記述子)をとりこんで、自身が管理するバッファオブジェクトを生成します。


```C:src/armsoc_dri3.c
	pixmap = pScreen->CreatePixmap(pScreen, width, height, depth, ARMSOC_CREATE_PIXMAP_IMPORT);
	if (!pixmap) {
		DEBUG_MSG("(fd=%d,width=%d,height=%d,stride=%d,depth=%d,bpp=%d) CreatePixmap() failed",
				  fd, width, height, stride, depth, bpp);
		goto failed;
	}
	priv = exaGetPixmapDriverPrivate(pixmap);
	if (!priv) {
		goto failed;
	}
	priv->bo = armsoc_bo_import_with_dim(pARMSOC->dev, fd, width, height, stride, depth, bpp);
	if (!priv->bo) {
		DEBUG_MSG("(fd=%d,width=%d,height=%d,stride=%d,depth=%d,bpp=%d) armsoc_bo_import_with_dim() failed",
				  fd, width, height, stride, depth, bpp);
		goto failed;
	}

```


Pixmap 生成時には設定できなかった stride を後付けで Pixmap に設定します。


```C:src/armsoc_dri3.c
	if (!pScreen->ModifyPixmapHeader(pixmap, 0, 0, 0, 0, stride, NULL)) {
		DEBUG_MSG("(fd=%d,width=%d,height=%d,stride=%d,depth=%d,bpp=%d) ModifyPixmapHeader(pixmap=%p,stride=%d) failed",
				  fd, width, height, stride, depth, bpp, pixmap, stride);
		goto failed;
	}

```


成功時には pixmap のアドレスを、失敗時には NULL を返します。


```C:src/armsoc_dri3.c
	return pixmap;
	
 failed:
	if (pixmap)
		fbDestroyPixmap(pixmap);
	ERROR_MSG("DRI3 Pixmap from FD(%d) failed", fd);
	return NULL;
}

```



### 　armsoc_dri3_fd_from_pixmap


armsoc_dri3_fd_from_pixmap() は、Pixmap が管理しているバッファオブジェクトを他の DRM と共有するためのファイル記述子を生成します。ファイル記述子とともに、バッファの横幅のバイト数を示す stride とバッファのバイト数を示す size も返します。


```C:src/armsoc_dri3.c
static int
armsoc_dri3_fd_from_pixmap(ScreenPtr pScreen,
						   PixmapPtr pixmap ,
						   CARD16*   stride ,
						   CARD32*   size   )
{
	ScrnInfoPtr       pScrn = xf86ScreenToScrn(pScreen);
	struct armsoc_bo* bo;
	int               fd;
	bo = ARMSOCPixmapBo(pixmap);
	if (!bo) {
		exaMoveInPixmap(pixmap);
		bo = ARMSOCPixmapBo(pixmap);
		if (!bo) {
			DEBUG_MSG("(pixmap=%p) bo==NULL failed", pixmap);
			goto failed;
		}
	}
	fd = armsoc_bo_export(bo);
	if (fd < 0) {
		DEBUG_MSG("(pixmap=%p) armsoc_bo_export(%p) failed", pixmap, bo);
		goto failed;
	}
	*stride = armsoc_bo_pitch(bo);
	*size   = armsoc_bo_size(bo);
	DEBUG_MSG("(pixmap=%p,width=%d,height=%d,depth=%d,bpp=%d,*stride=%d,*size=%d) success Fd(%d)", pixmap, armsoc_bo_width(bo), armsoc_bo_height(bo), armsoc_bo_depth(bo), armsoc_bo_bpp(bo), *stride, *size, fd);
	return fd;
  failed:
	ERROR_MSG("DRI3 Fd From Pixmap(%p) failed", pixmap);
	return -1;
}

```



## armsoc_dumb.c


DRI3 に対応するために バッファオブジェクトを管理するソースコードのarmsoc_dumb.c に何点か修正が必要です。


### 　struct armsoc_bo


バッファオブジェクト管理のための構造体 struct armsoc_bo に、armsoc_dri3_pixmap_from_fd() で取り込まれたバッファオブジェクトかどうかを識別するためのメンバー変数 import を追加します。


```diff:src/armsoc_dumb.c
@@ -62,6 +63,7 @@ struct armsoc_bo {
 	uint32_t pitch;
 	int refcnt;
 	int dmabuf;
+	int import;
 	/* initial size of backing memory. Used on resize to
 	 * check if the new size will fit
 	 */

```



### 　armsoc_bo_new_with_dim


armsoc_bo_new_with_dim() で自分自身でバッファオブジェクトを生成た場合は struct armsoc_bo のメンバー変数 import に０をセットします。


```diff:src/armsoc_dumb.c
@@ -170,14 +172,77 @@ struct armsoc_bo *armsoc_bo_new_with_dim(struct armsoc_device *dev,
 	new_buf->refcnt = 1;
 	new_buf->dmabuf = -1;
 	new_buf->name = 0;
+	new_buf->import = 0;
 
 	return new_buf;
 }

```



### 　armsoc_bo_import_with_dim


armsoc_dri3_pixmap_from_fd() から呼び出されるarmsoc_bo_import_with_dim() を追加します。この関数は、他の DRM  が管理しているバッファオブジェクト(を示す DMA-BUF API が生成したファイル記述子)をとりこんで、自身が管理するバッファオブジェクトを生成します。

まずは、DRM に対して DRM_IOCTL_PRIME_FD_TO_HANDLE を ioctl で呼び出し、ファイル記述子からバッファオブジェクトのハンドルを得ます。


```diff:src/armsoc_dumb.c
+struct armsoc_bo *armsoc_bo_import_with_dim(struct armsoc_device *dev, int fd,
+			uint32_t width, uint32_t height, uint32_t stride, uint8_t depth,
+			uint8_t bpp)
+{
+#ifdef DRM_IOCTL_PRIME_FD_TO_HANDLE
+	struct drm_prime_handle import_handle = { 0 };
+	struct armsoc_bo *new_bo;
+	off_t seek;
+	int res;
+
+	new_bo = malloc(sizeof(*new_bo));
+	if (!new_bo)
+		return NULL;
+
+	import_handle.fd    = fd;
+	import_handle.flags = 0;
+	
+	res  = drmIoctl(dev->fd, DRM_IOCTL_PRIME_FD_TO_HANDLE, &import_handle);
+	if (res) {
+		xf86DrvMsg(-1, X_ERROR,
+			" %s: DRM_IOCTL_PRIME_FD_TO_HANDLE({fd: %d}) failed. errno: %d - %s\\n",
+			__func__, import_handle.fd, errno, strerror(errno));
+		free(new_bo);
+		return NULL;
+	}
+

```


DRM_IOCTL_PRIME_FD_TO_HANDLE で得たハンドル import_handle.handle を バッファオブジェクトのメンバー変数 handle にセットします。その他のバッファオブジェクトのメンバー変数を初期化します。また、 lseek() でバッファの最後のポインタを得ることでバッファオブジェクトの正味のサイズ original_size を得ます。

バッファオブジェクトの生成に成功した時は、そのポインタを返します。失敗した時は NULL を返します。


```diff:src/armsoc_dumb.c
+	new_bo->dev      = dev;
+	new_bo->handle   = import_handle.handle;
+	new_bo->size     = stride * height;
+	new_bo->map_addr = NULL;
+	new_bo->fb_id    = 0;
+	new_bo->pitch    = stride;
+	new_bo->width    = width;
+	new_bo->height   = height;
+	new_bo->depth    = depth;
+	new_bo->bpp      = bpp;
+	new_bo->refcnt   = 1;
+	new_bo->dmabuf   = -1;
+	new_bo->name     = 0;
+	new_bo->import   = 1;
+
+	seek = lseek(import_handle.fd, 0, SEEK_END);
+	if (seek < 0) {
+		xf86DrvMsg(-1, X_ERROR,
+			" %s: lseek({fd: %d}) failed. errno: %d - %s\\n", 
+			__func__, import_handle.fd, errno, strerror(errno));
+		armsoc_bo_unreference(new_bo);
+		return NULL;
+	} else if (new_bo->size > seek) {
+		xf86DrvMsg(-1, X_ERROR,
+			" %s: requerd size(%d) is lager than actual (%ld)\\n",
+			__func__, new_bo->size, (long)seek);
+		armsoc_bo_unreference(new_bo);
+		return NULL;
+	} else {
+		new_bo->original_size = seek;
+		return new_bo;
+	}
+#else
+	return NULL;
+#endif
+}
+

```



### 　armsoc_bo_export


バッファオブジェクトを他の DRM と共有するためのファイル記述子を生成する関数 armsoc_bo_export() を追加します。具体的にはDRM に対して DRM_IOCTL_PRIME_HANDLE_TO_FD を ioctl で呼び出して得たバッファオブジェクトを示すファイル記述子を返します。


```diff:src/armsoc_dumb.c
+int  armsoc_bo_export(struct armsoc_bo *bo)
+{
+	int res;
+	struct drm_prime_handle export_handle = { 0 };
+
+	export_handle.handle = bo->handle;
+	export_handle.flags  = DRM_CLOEXEC;
+
+	res = drmIoctl(bo->dev->fd, DRM_IOCTL_PRIME_HANDLE_TO_FD, &export_handle);
+	if (res) {
+		xf86DrvMsg(-1, X_ERROR,
+			"DRM_IOCTL_PRIME_HANDLE_TO_FD(handle:0x%X) failed. errno: %d - %s\\n",
+				export_handle.handle, errno, strerror(errno));
+		return -1;
+	}
+	xf86DrvMsg(-1, X_INFO, "DRM_IOCTL_PRIME_HANDLE_TO_FD(handle:0x%X) return: %d\\n",
+			   export_handle.handle, export_handle.fd);
+	return export_handle.fd;
+}
+

```



### 　armsoc_bo_imported


バッファオブジェクトが armsoc_dri3_pixmap_from_fd() で取り込まれたかどうかを識別するための関数を追加します。


```diff:src/armsoc_dumb.c
+int armsoc_bo_imported(struct armsoc_bo *bo)
+{
+	return bo->import;
+}
+

```



## armsoc_dumb.h


バッファオブジェクト用のヘッダファイル armsoc_dumb.h に新しく追加した関数のarmsoc_bo_import_with_dim()、armsoc_bo_export()、armsoc_bo_imported() のプロトタイプを追加します。


```diff:src/armsoc_dumb.h
@@ -74,6 +74,11 @@ struct armsoc_bo *armsoc_bo_new_with_dim(struct armsoc_device *dev,
 			uint32_t width,
 			uint32_t height, uint8_t depth, uint8_t bpp,
 			enum armsoc_buf_type buf_type);
+struct armsoc_bo *armsoc_bo_import_with_dim(struct armsoc_device *dev, int fd,
+			uint32_t width,
+			uint32_t height, uint32_t stride, uint8_t depth, uint8_t bpp);
+int  armsoc_bo_export(struct armsoc_bo *bo);
+int	 armsoc_bo_imported(struct armsoc_bo *bo);
 uint32_t armsoc_bo_width(struct armsoc_bo *bo);
 uint32_t armsoc_bo_height(struct armsoc_bo *bo);
 uint8_t armsoc_bo_depth(struct armsoc_bo *bo);

```



## armsoc_exa.h


user_hint に設定するための定数 ARMSOC_CREATE_PIXMAP_IMPORT を追加します。


```diff:src/armsoc_exe.h
@@ -108,7 +108,7 @@ struct ARMSOCPixmapPrivRec {
 
 
 #define ARMSOC_CREATE_PIXMAP_SCANOUT 0x80000000
-
+#define ARMSOC_CREATE_PIXMAP_IMPORT  0x40000000
 
 void *ARMSOCCreatePixmap2(ScreenPtr pScreen, int width, int height,
 		int depth, int usage_hint, int bitsPerPixel,

```



## armsoc_exa.c


Pixmap を生成する際に user_hint に ARMSOC_CREATE_PIXMAP_IMPORT が設定されたときの処理を追加します。


### 　ARMSOCCreatePixmap2


ARMSOCCreatePixmap2() は X Server が Pixmap を生成する際に、DDX ドライバに対して呼び出される関数です。DDX Xlnx ではバッファオブジェクトを生成して Pixmap と関連づけます。

user_hint に ARMSOC_CREATE_PIXMAP_IMPORT が設定されたときは、バッファオブジェクトを生成しないように処理を追加します。


```diff:src/armsoc_exe.c
@@ -88,10 +88,12 @@ ARMSOCCreatePixmap2(ScreenPtr pScreen, int width, int height,
 	if (!priv)
 		return NULL;
 
-	if (usage_hint & ARMSOC_CREATE_PIXMAP_SCANOUT)
-		buf_type = ARMSOC_BO_SCANOUT;
+	if (!(usage_hint & ARMSOC_CREATE_PIXMAP_IMPORT) && 
+		(width > 0 && height > 0 && depth > 0 && bitsPerPixel > 0)) {
+
+		if (usage_hint & ARMSOC_CREATE_PIXMAP_SCANOUT)
+			buf_type = ARMSOC_BO_SCANOUT;
 
-	if (width > 0 && height > 0 && depth > 0 && bitsPerPixel > 0) {
 		/* Pixmap creates and takes a ref on its bo */
 		priv->bo = armsoc_bo_new_with_dim(pARMSOC->dev,
 				width,

```



### 　ARMSOCModifyPixmapHeader


ARMSOCModifyPixmapHeader() は X Server が Pixmap の各種情報(Header) を書き換える場合に DDX ドライバに対して呼び出される関数です。

user_hint に ARMSOC_CREATE_PIXMAP_IMPORT が設定されたときは、バッファオブジェクトがまだ関連づけられていなかった時は何もせず正常終了する処理を追加しています。また、armsoc_dri3_pixmap_from_fd() で取り込まれたバッファオブジェクトの場合は画像の幅 width、高さ height、bitPerPixel は変更出来ないようにしています。


```diff:src/armsoc_exe.c
@@ -225,10 +227,20 @@ ARMSOCModifyPixmapHeader(PixmapPtr pPixmap, int width, int height,
 	if (!pPixmap->drawable.width || !pPixmap->drawable.height)
 		return TRUE;
 
+	if ((priv->usage_hint & ARMSOC_CREATE_PIXMAP_IMPORT) && !priv->bo)
+		return TRUE;
+
 	assert(priv->bo);
 	if (armsoc_bo_width(priv->bo) != pPixmap->drawable.width ||
 	    armsoc_bo_height(priv->bo) != pPixmap->drawable.height ||
 	    armsoc_bo_bpp(priv->bo) != pPixmap->drawable.bitsPerPixel) {
+		if (armsoc_bo_imported(priv->bo)) {
+			ERROR_MSG("failed to resize %dx%d%d imported bo",
+					pPixmap->drawable.width,
+					pPixmap->drawable.height,
+					pPixmap->drawable.bitsPerPixel);
+			return FALSE;
+		}
 		/* pixmap drops ref on its old bo */
 		armsoc_bo_unreference(priv->bo);
 		/* pixmap creates new bo and takes ref on it */

```



## armsoc_driver.h



### 　struct ARMSOCRec


DDX 全体を管理するための構造体 struct ARMSOCRec に DRI2と DRI3 を管理するためのメンバー変数を追加します。


```diff:src/armsoc_driver.h
@@ -128,8 +128,22 @@ struct ARMSOCRec {
 	 */
 	struct ARMSOCEXARec	*pARMSOCEXA;
 
+	/** record if success xf86LoadSubModule("dri2")  */
+	Bool				dri2_available;
+	/** record if DRI >= 2 and success xf86LoadSubModule("dri2") */
+	Bool				dri2_enable;
 	/** record if ARMSOCDRI2ScreenInit() was successful */
-	Bool				dri;
+	Bool				dri2;
+	const char  		*dri2DriverName;
+
+	/** record if success xf86LoadSubModule("dri3")  */
+	Bool				dri3_available;
+	Bool				dri3_override;
+	/** record if DRI >= 3 and success xf86LoadSubModule("dri3")  */
+	Bool				dri3_enable;
+
+	/** record if ARMSOCDRI3ScreenInit() was successful */
+	Bool				dri3;
 
 	/** user-configurable option: */
 	Bool				NoFlip;

```



### 　ARMSOCDRI3ScreenInit


ARMSOCDRI3ScreenInit() のプロトタイプ宣言を追加します。


```diff:src/armsoc_driver.h
+/**
+ * DRI3 functions..
+ */
+Bool ARMSOCDRI3ScreenInit(ScreenPtr pScreen);
+
 
```



## armsoc_driver.c


DDX ドライバの初期化時に、オプション "DRI" に3 が設定されていたと場合は ARMSOCDRI3ScreenInit() を呼び出すように変更します。


### 　ARMSOCOptions


xorg.conf 等で設定するオプション変数に "DRI" を追加します。


```diff:src/armsoc_driver.c
@@ -102,6 +102,7 @@ _X_EXPORT DriverRec ARMSOC = {
 /** Supported options, as enum values. */
 enum {
 	OPTION_DEBUG,
+	OPTION_DRI_LEVEL,
 	OPTION_NO_FLIP,
 	OPTION_CARD_NUM,
 	OPTION_BUSID,
@@ -114,6 +115,7 @@ enum {
 /** Supported options. */
 static const OptionInfoRec ARMSOCOptions[] = {
 	{ OPTION_DEBUG,      "Debug",      OPTV_BOOLEAN, {0}, FALSE },
+	{ OPTION_DRI_LEVEL,  "DRI",        OPTV_INTEGER, {0}, FALSE },
 	{ OPTION_NO_FLIP,    "NoFlip",     OPTV_BOOLEAN, {0}, FALSE },
 	{ OPTION_CARD_NUM,   "DRICard",    OPTV_INTEGER, {0}, FALSE },
 	{ OPTION_BUSID,      "BusID",      OPTV_STRING,  {0}, FALSE },

```



### 　ARMSOCPreInit


オプション "DRI" の値に基づいてstruct ARMSOCRec の DRI2と DRI3 を管理するためのメンバー変数を設定します。


```diff:src/armsoc_driver.c
@@ -771,6 +773,7 @@ ARMSOCPreInit(ScrnInfoPtr pScrn, int flags)
 	rgb defaultMask = { 0, 0, 0 };
 	Gamma defaultGamma = { 0.0, 0.0, 0.0 };
 	int driNumBufs;
+	int driLevel;
 
 	TRACE_ENTER();
 

@@ -932,12 +935,45 @@ ARMSOCPreInit(ScrnInfoPtr pScrn, int flags)
 	}
 
 	/* Load external sub-modules now: */
-	if (!(xf86LoadSubModule(pScrn, "dri2") &&
-			xf86LoadSubModule(pScrn, "exa") &&
-			xf86LoadSubModule(pScrn, "fb"))) {
+	if (!(xf86LoadSubModule(pScrn, "exa") &&
+		  xf86LoadSubModule(pScrn, "fb"))) {
 		goto fail2;
 	}
 
+	/* Load dri2 and dri3 sub-modules */
+	if (!xf86GetOptValInteger(pARMSOC->pOptionInfo, OPTION_DRI_LEVEL, &driLevel)) {
+		driLevel = 2;
+	}
+	pARMSOC->dri3_available = FALSE;
+	pARMSOC->dri3_override  = FALSE;
+	pARMSOC->dri3_enable    = FALSE;
+	pARMSOC->dri3           = FALSE;
+	pARMSOC->dri2_available = FALSE;
+	pARMSOC->dri2_enable    = FALSE;
+	pARMSOC->dri2           = FALSE;
+	pARMSOC->dri2DriverName = pARMSOC->drmmode_interface->driver_name;
+
+
+	pARMSOC->dri3_available = !!xf86LoadSubModule(pScrn, "dri3");
+	if (pARMSOC->dri3_available)
+		INFO_MSG("LoadModule: dri3 success");
+	else
+		WARNING_MSG("LoadModule: dri3 error");
+	pARMSOC->dri3_override  =
+		!pARMSOC->dri3_available ||
+		xf86IsOptionSet(pARMSOC->pOptionInfo, OPTION_DRI_LEVEL);
+
+	if (driLevel >= 3)
+		pARMSOC->dri3_enable = pARMSOC->dri3_available;
+
+	pARMSOC->dri2_available = !!xf86LoadSubModule(pScrn, "dri2");
+	if (pARMSOC->dri2_available)
+		INFO_MSG("LoadModule: dri2 success");
+	else
+		WARNING_MSG("LoadModule: dri2 error");
+	if (driLevel >= 2)
+		pARMSOC->dri2_enable = pARMSOC->dri2_available;
+
+

```



### 　ARMSOCAccelInit


struct ARMSOCRec の DRI2と DRI3 を管理するためのメンバー変数の値に基づいて DRI3 および DRI2 の初期化関数を呼び出します。


```diff:src/armsoc_driver.c
 /**
- * Initialize EXA and DRI2
+ * Initialize EXA and DRI2 and DRI3
  */
 static void
 ARMSOCAccelInit(ScreenPtr pScreen)
 {
 	ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
 	struct ARMSOCRec *pARMSOC = ARMSOCPTR(pScrn);
+	char str[128] = "";
 
 	if (!pARMSOC->pARMSOCEXA)
 		pARMSOC->pARMSOCEXA = InitNullEXA(pScreen, pScrn,
 								pARMSOC->drmFD);
+	if (!pARMSOC->pARMSOCEXA)
+		return;
+	
+	if (pARMSOC->dri2_enable)
+		pARMSOC->dri2 = ARMSOCDRI2ScreenInit(pScreen);
+
+	if (pARMSOC->dri2)
+		strcat(str, "DRI2 ");
+	
+	if (pARMSOC->dri3_enable || (!pARMSOC->dri2 && !pARMSOC->dri3_override))
+		pARMSOC->dri3 = ARMSOCDRI3ScreenInit(pScreen);
 
-	if (pARMSOC->pARMSOCEXA)
-		pARMSOC->dri = ARMSOCDRI2ScreenInit(pScreen);
+	if (pARMSOC->dri3)
+		strcat(str, "DRI3 ");
+
+	if (*str)
+		INFO_MSG("direct rendering: %senabled", str);
 	else
-		pARMSOC->dri = FALSE;
+		INFO_MSG("direct rendering: disabled");
 }

```



### 　ARMSOCScreenInit


失敗時の処理を修正します。


```diff:src/armsoc_driver.c
@@ -1190,12 +1241,20 @@ fail6:
 	drmmode_cursor_fini(pScreen);
 
 fail5:
-	if (pARMSOC->dri)
+	if (pARMSOC->dri3) {
+		pARMSOC->dri3 = FALSE;
+	}
+
+	if (pARMSOC->dri2) {
 		ARMSOCDRI2CloseScreen(pScreen);
+		pARMSOC->dri2 = FALSE;
+	}
 
-	if (pARMSOC->pARMSOCEXA)
+	if (pARMSOC->pARMSOCEXA) {
 		if (pARMSOC->pARMSOCEXA->CloseScreen)
 			pARMSOC->pARMSOCEXA->CloseScreen(CLOSE_SCREEN_ARGS);
+		pARMSOC->pARMSOCEXA = FALSE;
+	}
 fail4:
 	/* Call the CloseScreen functions for fbInitScreen,  miDCInitialize,
 	 * exaDriverInit & xf86CrtcScreenInit as appropriate via their

```



### 　ARMSOCCloseScreen


終了時の処理を修正します。


```diff:src/armsoc_driver.c
@@ -1270,12 +1329,19 @@ ARMSOCCloseScreen(CLOSE_SCREEN_ARGS_DECL)
 
 	ret = (*pScreen->CloseScreen)(CLOSE_SCREEN_ARGS);
 
-	if (pARMSOC->dri)
+	if (pARMSOC->dri3)
+		pARMSOC->dri3 = FALSE;
+
+	if (pARMSOC->dri2) {
 		ARMSOCDRI2CloseScreen(pScreen);
+		pARMSOC->dri2 = FALSE;
+	}
 
-	if (pARMSOC->pARMSOCEXA)
+	if (pARMSOC->pARMSOCEXA) {
 		if (pARMSOC->pARMSOCEXA->CloseScreen)
 			pARMSOC->pARMSOCEXA->CloseScreen(CLOSE_SCREEN_ARGS);
+		pARMSOC->pARMSOCEXA = FALSE;
+	}
 
 	assert(pARMSOC->scanout);
 	/* Screen drops its ref on the scanout buffer */

```



# 参考


* 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/xf86-video-armsoc-xilinx


[概要編]: https://qiita.com/ikwzm/items/642df8c14fd3a9e782b6 "『Ultra96/Ultra96-V2 向け Ubuntu20.04で Lima を動かしてみた(概要編)』@Qiita"
[DRM Lima 編]: https://qiita.com/ikwzm/items/91adfcf05e767d33be70 "『Ultra96/Ultra96-V2 向け Ubuntu20.04で Lima を動かしてみた(DRM Lima 編)』@Qiita"
[DRI Lima 編]: https://qiita.com/ikwzm/items/ec29ae456ead67438733 "『Ultra96/Ultra96-V2 向け Ubuntu20.04で Lima を動かしてみた(DRI Lima 編)』@Qiita"
[共有バッファ編]: https://qiita.com/ikwzm/items/ed2ce2d8bb90391d7c65 "『Ultra96/Ultra96-V2 向け Ubuntu20.04で Lima を動かしてみた(共有バッファ編)』@Qiita"
[ストライド問題編]: https://qiita.com/ikwzm/items/5c4c0114c15cfba8e95b "『Ultra96/Ultra96-V2 向け Ubuntu20.04で Lima を動かしてみた(ストライド問題編)』@Qiita"
[インストール編]: https://qiita.com/ikwzm/items/4dfb8f06ddf9d59e310c "『Ultra96/Ultra96-V2 向け Ubuntu20.04で Lima を動かしてみた(インストール編)』@Qiita"
[Ubuntu Desktop ビルド編]: https://qiita.com/ikwzm/items/21a59e68d30ad9faad6a "『Ultra96/Ultra96-V2 向け Ubuntu20.04で Lima を動かしてみた(Ubuntu Desktop ビルド編)』@Qiita"
[『ZynqMP 向け Ubuntu22.04で Lima を動かしてみた(共有バッファ編)』@Qiita]: https://qiita.com/ikwzm/items/aaa7c7a321cfc29518df "『ZynqMP 向け Ubuntu22.04で Lima を動かしてみた(共有バッファ編)』@Qiita"
[Lima]: https://gitlab.freedesktop.org/lima/web "Lima Website"
[DRI]: https://dri.freedesktop.org/wiki "Mesa 3D and Direct Rendering Infrastructure wiki"
[DRI(Direct Rendering Infrastructure)]: https://dri.freedesktop.org/wiki "Mesa 3D and Direct Rendering Infrastructure wiki"