みつきんのメモ

組み込みエンジニアです。Interface誌で「Yocto Projectではじめる 組み込みLinux開発入門」連載中

Yocto Honister ラズパイ4でVulkanデモプログラムを動かしてみる

はじめに

なんとなく、Raspberry Pi 4 で Vulkan を試すと同様のことをPokyでもやってみた。

使用するのはラズベリーパイ4

動かすデモプログラムはSaschaWillems/Vulkan。これはVulkanのデモとしては有名なものらしい。

run_crops.sh(余談)

gistに置いた。

実行権限をつけてPATHの通ったところに置いておく。

$ chmod +x ./run_crops.sh

Pokyの作成

作業場所は~/work/yocto/rpi_honister_vulkanとする。

$ mkdir -p ~/work/yocto/rpi_honister_vulkan && cd ~/work/yocto/rpi_honister_vulkan

Pokyの取得

まずはPoky一式を取得する。

$ git clone git://git.yoctoproject.org/poky.git -b honister

meta-raspberrypiの取得

meta-raspberrypiをbitbake-layers layerindex-fetchで取得する。

$ run_crops.sh bitbake-layers layerindex-fetch meta-raspberrypi

local.confの修正

build/conf/local.confに下記の内容を追加する。

MACHINE = "raspberrypi4-64"
DL_DIR ?= "${TOPDIR}/../downloads"

# enable uart
ENABLE_UART = "1"

# systemd
DISTRO_FEATURES:append = " systemd pam"
VIRTUAL-RUNTIME_init_manager = "systemd"
DISTRO_FEATURES_BACKFILL_CONSIDERED = "sysvinit"
VIRTUAL-RUNTIME_initscripts = ""

core-image-x11をビルド

今回はGUIを使用するのでcore-image-x11をビルドする。

$ run_crops.sh bitbake core-image-x11

これが作業の土台となる。

ラズパイ向けのVulkanドライバ

下記コマンドでmesaのdevshellに入る。

$ run_crops.sh bitbake mesa -c devshell

Vulkanドライバがビルドされているかを確認する。

sh-4.4# ls -lha ../packages-split/mesa-vulkan-drivers
total 8.0K
drwxrwxrwx  2 root root 4.0K Oct 14 19:59 .
drwxr-xr-x 30 root root 4.0K Oct 14 19:59 ..

この時点でなにも生成されてないことがわかる。

devshellはexitコマンドで抜けられる。

mesaのレシピ

poky/meta/recipes-graphics/mesa/mesa.incの以下の部分を確認する。

# Vulkan drivers need dri3 enabled
# amd could be enabled as well but requires gallium-llvm with llvm >= 3.9
VULKAN_DRIVERS = ""
VULKAN_DRIVERS:append:x86:class-target = ",intel"
VULKAN_DRIVERS:append:x86-64:class-target = ",intel"
VULKAN_DRIVERS:append ="${@bb.utils.contains('PACKAGECONFIG', 'freedreno', ',freedreno', '', d)}"
PACKAGECONFIG[vulkan] = "-Dvulkan-drivers=${@strip_comma('${VULKAN_DRIVERS}')}, -Dvulkan-drivers='',"

ラズパイ向けのVulkanドライバをビルドするには、mesaのビルド時点で-Dvulkan-drivers=broadcomという設定が必要になるらしい。 この定義を見ていると、VULKAN_DRIVERSに",broadcom"が設定されていれば良さそう。

手早く確認するためにbuild/conf/local.confに下記の1行を追加してみる。

VULKAN_DRIVERS:append:rpi:pn-mesa = ",broadcom"

Override Syntaxが_から:に変わっているので、この辺りの表現はわかりやすくなっている。

この状態でmesaのレシピをビルドしてみる。

$ run_crops.sh bitbake mesa -f

再度devshellを起動してmeta-vulkan-driversの内容を確認する。

sh-4.4# ls -lha ../packages-split/mesa-vulkan-drivers
total 12K
drwxrwxrwx  3 root root 4.0K Oct 14 19:59 .
drwxr-xr-x 30 root root 4.0K Oct 14 19:59 ..
drwxr-xr-x  4 root root 4.0K Oct 14 19:59 usr

usrディレクトリが生成されている。

sh-4.4# find ../packages-split/mesa-vulkan-drivers -type f
../packages-split/mesa-vulkan-drivers/usr/share/vulkan/icd.d/broadcom_icd.aarch64.json
../packages-split/mesa-vulkan-drivers/usr/lib/libvulkan_broadcom.so

ドライバとjsonファイルが生成されていることがわかる。

ここまででラズベリーパイ向けのVulkanドライバがビルドできた。

Vulkanデモプログラム

デモプログラムはSaschaWillems/Vulkanを使用する。

いつもの三角形や歯車が実装されている。

レイヤの作成

デモプログラムをPokyに組み込むためにレシピを作成することになるが、そのレシピを組み込むためのレイヤを作成する。

$ run_crops.sh bitbake-layers create-layer meta-vulkan-demo
$ rm -rf ./build/meta-vulkan-demo/recipes-example
$ rm -rf ./build/meta-vulkan-demo/recipes-recipetool
$ mkdir -p ./build/meta-vulkan-demo/recipes-graphics/vulkan
$ mv ./build/meta-vulkan-demo ./poky
$ run_crops.sh bitbake-layers add-layer $(pwd)/poky/meta-vulkan-demo

レシピの作成

recipetoolを使用してデモプログラムのレシピを作成する。

$ run_crops.sh recipetool create https://github.com/SaschaWillems/Vulkan.git -o $(pwd)/poky/meta-vulkan-demo/recipes-graphics/vulkan/vulkan-demo_git.bb

do_configureでエラー

試しにビルドすると下記のようなエラーが発生した。

(...snip...)
| CMake Error at CMakeLists.txt:46 (find_library):
|   Could not find Vulkan_LIBRARY using the following names: vulkan
(...snip...)
ERROR: Task (/home/mickey/work/yocto/rpi_honister_vulkan/poky/meta-vulkan-demo/recipes-graphics/vulkan/vulkan-demo_git.bb:do_configure) failed with exit code '1'
NOTE: Tasks Summary: Attempted 773 tasks of which 771 didn't need to be rerun and 1 failed.

Summary: 1 task failed:
  /home/mickey/work/yocto/rpi_honister_vulkan/poky/meta-vulkan-demo/recipes-graphics/vulkan/vulkan-demo_git.bb:do_configure
Summary: There was 1 ERROR message shown, returning a non-zero exit code.

vulkan関連のモジュールが見つからないのでdo_configureタスクに失敗している。

diff --git a/recipes-graphics/vulkan/vulkan-demo_git.bb b/recipes-graphics/vulkan/vulkan-demo_git.bb
index c173b4f..a837a5d 100644
--- a/recipes-graphics/vulkan/vulkan-demo_git.bb
+++ b/recipes-graphics/vulkan/vulkan-demo_git.bb
@@ -35,7 +35,7 @@ S = "${WORKDIR}/git"
 # NOTE: unable to map the following CMake package dependencies: Wayland Vulkan DirectFB
 # NOTE: the following library dependencies are unknown, ignoring: vulkan-1 vulkan
 #       (this is based on recipes that have previously been built and packaged)
-DEPENDS = "libxcb wayland-protocols"
+DEPENDS = "libxcb wayland-protocols vulkan-loader"

 inherit cmake pkgconfig

ビルド時の依存関係にvulkan-loaderを追加すると、do_configureは成功し、do_compile以降の処理が実行されるようになる。

do_installのエラー

do_installタスクで下記のエラーが発生した。cmakeでのビルドプロセスにinstallターゲットが存在しないとなっている。

... (snip) ...
| ninja: error: unknown target 'install'
... (snip) ...

SaschaWillems/Vulkanの作業例ではmake install的なコマンドは見当たらなかった。 インストールプロセスをまともに実装していないのかもしれない。

その場合は自分でdo_installタスクを作成する。

devshellでビルド結果を確認してみる。

sh-4.4# find ../build -type f | less
(...snip...)
../build/bin/indirectdraw
../build/bin/texturearray
../build/bin/computenbody
../build/bin/gltfscenerendering
../build/bin/deferredshadows
../build/.ninja_lo

必要そうなものはbuild/bin以下に固まっているようだ。なのでとりあえず下記のようなdo_installタスクを実装する。

do_install() {
    install -d ${D}${bindir}
    install -m 0755 ${B}/bin/* ${D}${bindir}
}

よく使用される変数

do_installタスクを実装する際にだいたい必要になることが多い変数を下記にまとめる。

変数 概要 ディレクト
WORKDIR bitbake中のレシピの作業ディレクト (..snip..)/vulkan-demo/1.0+gitAUTOINC+821a0659a7-r0
S bitbake中のソースディレクト ${WORKDIR}/git
D bitbake中のinstall先 ${WORKDIR}//build
B bitbake中のビルドディレクト ${WORKDIR}//image
bindir ルートFS中の実行ファイルインストール先 /usr/bin
datadir ルートFS中のデータインストール先 /usr/share

WORKDIRは通常、bitbake時のbuild/tmp/work/CPUアーキ/レシピ/バージョンとなる。

これらの変数の実際の値は下記のように確認することができる。

$ run_crops.sh bitbake "レシピ名" -e | grep '^変数名='

# vulkan-demoでDを確認したい場合は下記のようにする。
$ run_crops.sh bitbake vulkan-demo -e | grep '^D='
D="/home/mickey/work/yocto/rpi_honister_vulkan/build/tmp/work/cortexa72-poky-linux/vulkan-demo/1.0+gitAUTOINC+821a0659a7-r0/image"

デモプログラムの実行

bitbakeが通るようになったので、vulkan-demoを組み込んだイメージを作成する。

local.confに下記の一行を追加する。

IMAGE_INSTALL:append = " vulkan-demo"

core-image-x11を作成し、ラズパイ上でデモプログラムであるgearsを実行する。 すると下記のようなエラーが発生する。

root@raspberrypi4-64:~# gears
Error: Could not find asset path in /home/mickey/work/yocto/rpi_honister_vulkan/build/tmp/work/cortexa72-poky-linux/vulkan-demo/1.0+gitAUTOINC+821a0659a7-r0/git/data/

エラー自体はデモプログラムの実行に必要なアセットと呼ばれるデータファイルが見つからないというもの。 このエラーの問題点は下記の2つ

  1. ルートFSにアセットが含まれていない
  2. デモプログラムがアセットを読み出すパスがプログラムのビルド時のホストPC上のディレクトリを指している

アセットの取得

SaschaWillems/VulkanAssets の項にアセットを取得する方法が記載してある。

python download_assets.py

vulkan-demoのレシピにこの処理を追加してやれば良さそう。

do_compile:append() {
    cd ${S}
    ./download_assets.py
}

do_install() {
    install -d ${D}${bindir}
    install -m 0755 ${B}/bin/* ${D}${bindir}

    install -d ${D}${datadir}/Vulkan/data
    cp -r ${S}/data ${D}${datadir}/Vulkan
}

FILES:${PN} += "${datadir}/Vulkan/data"

do_compileタスクに処理を追加し、download_assets.pyを実行する。 次に、取得したアセットがビルドしたパッケージに含まれるようにインストールする。 最後に、インストールされたデータがきちんとパッケージに反映されるようにFILESに追加する。

この修正後にbitbakeしてdevshellでpackages-split/vulkan-demoディレクトリを確認してみる。

$ run_crops.sh bitake vulkan-demo
$ run_crops.sh bitake vulkan-demo -c devshell
sh-4.4# cd ../packages-split/vulkan-demo/usr/share
sh-4.4# ls
Vulkan

packages-split/vulkan-demo/usr/share以下にVulkanディレクトリがあれば、 アセットはルートFSに含まれるようになっている。

アセットのロードパスの変更

アセットがルートFSに含まれるようになったが、問題はデモプログラムがアセットを読み込むパスを変更する必要があるということ。 このパスはどのように決定されているのだろうか。

ソースコードにエラーメッセージの一部の文字列でgrepしてみる。

$ run_crops.sh bitbake vulkan-demo -c devshell
$ grep -r 'Could not find asset path in' .
base/vulkanexamplebase.cpp:             std::cerr << "Error: Could not find asset path in " << getAssetPath() << "\n";

getAssetPath()の実装を確認する。外部関数として実装されているのはbase/VulkanTools.cppのみのようだ。

const std::string getAssetPath()
{
#if defined(VK_USE_PLATFORM_ANDROID_KHR)
    return "";
#elif defined(VK_EXAMPLE_DATA_DIR)
    return VK_EXAMPLE_DATA_DIR;
#else
    return "./../data/";
#endif
}

VK_EXAMPLE_DATA_DIRが定義されていればその内容が展開されreturnされるようだ。 VK_EXAMPLE_DATA_DIRがどのように定義されているかを確認する。

$ grep -r VK_EXAMPLE_DATA_DIR .
./CMakeLists.txt:   add_definitions(-DVK_EXAMPLE_DATA_DIR=\"${RESOURCE_INSTALL_DIR}/\")
./CMakeLists.txt:   add_definitions(-DVK_EXAMPLE_DATA_DIR=\"${CMAKE_SOURCE_DIR}/data/\")

CMakeLists.txtで定義されているようだ。

if(RESOURCE_INSTALL_DIR)
    add_definitions(-DVK_EXAMPLE_DATA_DIR=\"${RESOURCE_INSTALL_DIR}/\")
    install(DIRECTORY data/ DESTINATION ${RESOURCE_INSTALL_DIR}/)
else()
    add_definitions(-DVK_EXAMPLE_DATA_DIR=\"${CMAKE_SOURCE_DIR}/data/\")
endif()

内容を確認すると、RESOURCE_INSTALL_DIRが定義されている場合はその内容がVK_EXAMPLE_DATA_DIRに代入されるように見える。 RESOURCE_INSTALL_DIRはレシピ中のEXTRA_OECMAKEで定義することができる。

レシピに下記の行を変更する。

EXTRA_OECMAKE = "-DRESOURCE_INSTALL_DIR=${datadir}/Vulkan/data"

vulkan-demo_git.bb完成形

vulkan-demo_git.bbの完成形を以下に示す。

LICENSE = "MIT & Unknown"
LIC_FILES_CHKSUM = "file://LICENSE.md;md5=dcf473723faabf17baa9b5f2207599d0 \
                    file://external/glm/util/conan-package/lib_licenses/LICENSE1.txt;md5=37ffc29490b4cf57fd7e5ffba4c1b185 \
                    file://external/glm/util/conan-package/lib_licenses/LICENSE2.txt;md5=f7f57caa96a29810aa62259f274b374b \
                    file://external/tinygltf/LICENSE;md5=82f6e601fc2fd27cbf55d6f5f176d017 \
                    file://external/imgui/LICENSE.txt;md5=f3c4ae64ab2a23f1b8734609e1a2d48a \
                    file://external/ktx/LICENSE.md;md5=654c86a2bade5bb7fbcde45788f98a61"

SRC_URI = "gitsm://github.com/SaschaWillems/Vulkan.git;protocol=https;branch=master"

PV = "1.0+git${SRCPV}"
SRCREV = "821a0659a76131662b1fc4a77c5a1ee6a9a330d8"

S = "${WORKDIR}/git"

DEPENDS = "libxcb wayland-protocols vulkan-loader"

inherit cmake pkgconfig

EXTRA_OECMAKE = "-DRESOURCE_INSTALL_DIR=${datadir}/Vulkan/data"

do_compile:append() {
    cd ${S}
    ./download_assets.py
}

do_install() {
    install -d ${D}${bindir}
    install -m 0755 ${B}/bin/* ${D}${bindir}

    install -d ${D}${datadir}/Vulkan/data
    cp -r ${S}/data ${D}${datadir}/Vulkan
}

FILES:${PN} += "${datadir}/Vulkan/data"

gitリポジトリにサブモジュールが含まれている場合、 recipetool createによってSRC_URIのfetcherがgitsm://になっている。 このようにできる範囲で最適なfetcherを選択してくれる。

動作画面

ラズパイ4でgearsが動いた。

f:id:mickey_happygolucky:20220305123817j:plain
Vulkanのgears

まとめ

ラズパイ4でVulkanのデモプログラムを動かしてみた。 デフォルト設定ではvulkanドライバが作成されないので、VULKAN_DRIVERS",broadcom"を追加する必要がある。

recipetoolを使って新規にレシピを作成する方法もざっと紹介した。

ラズパイ4のVulkanは結構ヌルヌル動いた。