Linuxグラフィックスタック覚書

なんだかややこしいLinuxグラフィックススタック周りの調査メモ

# Mesaについて
 ![mesa_arch.png](https://qiita-image-store.s3.amazonaws.com/0/39647/3f0d750f-7ab9-c151-edd3-3fff764e14cb.png)
https://keyj.emphy.de/files/linuxgraphics_en.pdf より

LinuxのOpenGLドライバスタックには上図のとおり色々なタイプがある。
例えばNVidiaのプロプライエタリドライバは一番左のタイプ。Mesaを使用せず完全に独自ドライバにて実装される。
ベンダー提供でもオープンソースのOpenGLドライバは現状全てMesaを使用しており、スタックは右３つのうちのどれかとなっている。
VMWareは右２つのどちらかのタイプである（どっちだろう？）。

Gallium3D State TrackerはMesaリポジトリのsrc/gallium/state_trackers以下にある
gallivmはsrc/gallium/auxiliary以下に
llvm backendはsrc/gallium/drivers以下に

# DRMについて
## DRMとは
DRMはMesaではなくカーネルの機能であり、Mesaから使われる立場にある。
もともとはXWindowSystemのために開発された。
XWindowSystemはサーバクライアントモデルで、クライアントがサーバに描画指示を送っていたが、それでは遅いということでクライアントが直接フレームバッファオブジェクトに描画できる仕組みとしてDRMが開発された。
サーバクライアント間でバッファオブジェクトを共有することで、従来あったオーバーヘッドを減らし高速に描画できるようになった。

## DRIとの違い？
用語的に紛らわしいのだが、Direct Rendering Infrastructure(DRI) はMesa(ユーザーランドライブラリ)やDRM(カーネルサブモジュール)によって提供される描画のためのフレームワークの名称であり、DRMはDRIを実現するための実装という位置付けでありその一部であると言える。

## DRM Authenticate(認証)について
DRMに対しては「/dev/dri/card0」のようなデバイスファイルでアクセス可能であるが、バッファオブジェクトの割当などはDRMマスタに認証されて初めて可能となる。
DRMマスタはdrmSetMaster()によって設定され、一つのデバイスに対して１アプリケーションのみがマスタとなれる。

認証は以下の手順で行われる

1. DRMを使用したいアプリケーションがdrmGetMagic()でmagic(実体はunsigned int)を取得
2. DRMマスタアプリケーションにmagicを送信
3. DRMマスタはdrmAuthMagic()にてmagicを認証

補足：drmGetMagic()/drmAuthMagic()はセキュリティ周りの問題ですでに時代遅れとされ、Render Nodeの使用が推奨されているが、Mesa10.4.7ではまだ現役である。

## WaylandでのDRM
WaylandクライアントがEGL(Mesa)を用いて描画を行う際、wl_drmプロトコルという形でDRMを利用する。
クライアントは「src/egl/drivers/dri2/platform_wayland.c」、サーバは「src/egl/wayland/wayland-drm/wayland-drm.c」あたりを参考に 

### DRM認証（クライアント起動時）
1. クライアントがregistry_handle_globalでwl_drmにバインドする
2. サーバからdeviceイベントが飛んでくる。引数にデバイス名(例: /dev/dri/card0)が含まれている
3. デバイスをオープンし、drmGetMagic()しAuthenticateリクエストをおこなう
4. サーバ側でmagicを認証する<br>
   DRMバックエンドであればdrmAuthMagic()、X11バックエンド（この場合DRMマスタはwaylandサーバではなくXサーバ）であればxcb_driの認証関数を実行（おそらくXサーバ内でdrmAuthMagic()される）

### バッファ共有(クライアントのeglSwapBuffers実行時)
PRIME（バッファオブジェクトに対応するFDを取得する。[参考](https://01.org/linuxgraphics/gfx-docs/drm/gpu/drm-mm.html#prime-buffer-sharing)）機能使用を想定
1. クライアントがPRIME機能でバッファオブジェクトのFDを取得
2. クライアントがwl_drmのcreate_prime_bufferリクエストでFDをサーバに送信
3. サーバはFDを元にバッファオブジェクトを取得し、wl_buffer作成

補足：このPRIMEは[これ](https://wiki.archlinuxjp.org/index.php/PRIME)と関係ある？

# GEMについて
参考：https://lwn.net/Articles/283798/ (APIは現状と異なるので注意)

Graphics Execution Managerは**DRMの一部**であり、基本的なアイデアは複数アプリケーション間のバッファの共有APIの提供である。
DRMの一部であるので基本的にAPIはDRIデバイスに対するioctlで提供される。
Mesaのコードを調査した結果の注意点としては、**全てのドライバがGEMを実装しているわけではない**という点である。ドライバによっては別の独自の共有APIを提供しており、Mesaの上位ドライバによって差異は吸収されている（例：VMWareのドライバ）

## GEM FLINK
バッファにシステムに一意な名前を付ける機能である。ここで作成した名前を使えば別プロセスであってもGEM OPENでバッファを開くことが可能となる。
以下参考にあるコード

```c
struct drm_gem_flink {
			/** Handle for the object being named */
			uint32_t handle;
		
			/** Returned global name */
			uint32_t name;
		};
		
		/* usage */
		flink.handle = <handle>;
		ret = ioctl (fd, DRM_IOCTL_GEM_FLINK, &flink);
		if (ret == 0)
			return flink.name;
```

## GEM OPEN
GEM FLINKによりつけられた名前からバッファを開きローカルなハンドルを得る機能。

```c
struct drm_gem_open {
			/** Name of object being opened */
			uint32_t name;
		
			/** Returned handle for the object */
			uint32_t handle;
			
			/** Returned size of the object */
			uint64_t size;
		};
		
		/* usage */
		open.name = <name>;
		ret = ioctl (fd, DRM_IOCTL_GEM_OPEN, &open);
		if (ret == 0) {
			*sizep = open.size;
			return open.handle;
		}
```

#GBMについて
Generic Buffer Manager はプラットフォームにより異なるDRM APIを隠蔽してバッファ取得のAPIを提供するモジュール。
これにより、移植性の高いコードが生成できると謳っている。
Mesaの中ではたとえばEGLのDRMバックエンドのコードで使われている。（というかここ以外で使ってます？）
Mesa以外ではWestonのcompositor-drm.c内で使用されている。

要するに「DRM版」（例えばWestonコンポジタの）と言った場合に、多くの場合で実際には直接DRM APIを呼び出すのではなくGBM APIを利用しているのである。