L'article a été rédigé en utilisant un Raspberry Pi 3 avec Buildroot 2021.05-514-g74adec4f3a qui contient Qt5 5.15.2 et un noyau 5.10. Le Raspberry est connecté à un télévieur via HDMI. L'objectif est d'obtenir un système minimaliste Linux sans serveur X permettant d'exécuter une application graphique tout en bénéficiant de l'accélération matérielle.
Utiliser eglfs_brcm comme backend
Le backend eglfs_brcm repose sur les pilotes graphiques
propriétaires Broadcom afin de fournir un support pour OpenGLES/EGL avec
les fichiers /usr/lib/libbrcmEGL.so et
/usr/lib/libbrcmGLESv2.so. Ceux-ci sont fournis par le
package Rpi-userland et les "Firmware Driver" dans le
noyau.
Configuration Buildroot
make raspberrypi3_defconfig
make menuconfig
System configuration
├─ System hostname (BR2_TARGET_GENERIC_HOSTNAME="buildrootqt5")
├─ Root password (BR2_TARGET_GENERIC_ROOT_PASSWD="root")
├─ remount root filesystem read-write during boot (BR2_TARGET_GENERIC_REMOUNT_ROOTFS_RW=n)
└─ Root filesystem overlay directories (BR2_ROOTFS_OVERLAY=output/rootfs_overlay)
Toolchain
└─ Enable WCHAR support (BR2_TOOLCHAIN_BUILDROOT_WCHAR=y)
Target packages
├─ Hardware handling
| └─ rpi-userland (BR2_PACKAGE_RPI_USERLAND=y)
|
└─ Graphic libraries and applications (graphic/text)
└─ Qt5 (BR2_PACKAGE_QT5=y)
├─ Custom configuration options (BR2_PACKAGE_QT5BASE_CUSTOM_CONF_OPTS
| [=-skip qtconnectivity -skip qtnetwork -skip qtgamepad -no-feature-vnc -no-feature-accessibility -nomake tests])
├─ eglfs support (BR2_PACKAGE_QT5BASE_EGLFS=y)
└─ Default graphical platform (BR2_PACKAGE_QT5BASE_DEFAULT_QPA="eglfs")
Peuvent être désactivés:
Target packages
├─ Graphic libraries and applications (graphic/text) → mesa3d (BR2_PACKAGE_MESA3D=n)
└─ Libraries
└─ Graphics
└─ libdrm (BR2_PACKAGE_LIBDRM=n)
Les librairies C uClibc-ng et glibc
fonctionnent avec ces modules Qt5 (musl n'a pas été testée)
cependant certains packages comme qt5webengine nécessitent
glibc. D'autres packages comme
qt5quickcontrols2, qt5graphicaleffects et
qt5wayland peuvent être ajoutés selon les besoins
applicatifs (ce qui n'est pas le cas pour notre application de
test).
Les options de compilation pour Qt peuvent être modifiées selon les besoins. Exemple: -skip qtconnectivity -skip qtdoc -skip qtandroidextras -skip qtnetworkauth -skip qtpurchasing -skip qtgamepad -no-feature-vnc -no-feature-accessibility -no-feature udpsocket -no-feature-networkproxy -no-feature-socks5 -no-feature-networkdiskcache -no-feature-testlib -nomake tests.
Sauvegarde de la configuration Buildroot:
make savedefconfig BR2_DEFCONFIG=./buildrootqt5.configConfiguration du noyau Linux
Pour la configuration du noyau, aucun pilote graphique particulier (DRM, VC4) n'est nécessaire, il faut veiller à la présence des "Firmware Driver" (présents par défaut):
make linux-menuconfigFirmware Drivers
└─ Raspberry Pi Firmware Driver (RASPBERRYPI_FIRMWARE=y)
Device Drivers
└─ Graphics support
└─ Direct Rendering Manager (XFree86 4.1.0 and higher DRI support) (DRM=n)
Sauvegarde de la configuration du noyau:
cp output/build/linux-custom/.config linux.config
Overlay
Pour inclure nos fichiers nous allons utiliser un overlay, tous les
fichiers contenu dans output/rootfs_overlay (considéré par
Buildroot comme référence à la racine) seront présents dans l'image
finale.
output/rootfs_overlay/usr/share/fonts/dejavu-sans-webfont.ttf:
Qt5 ne fournit plus de police de caractères, il faut au minimum une
police TTF sur le système. Buildroot peut également inclure des polices
avec:
Target packages
└─ Fonts, cursors, icons, sounds and themes
└─ DejaVu fonts (BR2_PACKAGE_DEJAVU)
output/rootfs_overlay/root/test.sh: script de lancement
de l'application QML box.qml.
#!/bin/sh
export QT_QPA_PLATFORM=eglfs
export QT_QPA_EGLFS_INTEGRATION=eglfs_brcm
export QT_QPA_EGLFS_ALWAYS_SET_MODE=1
export QT_XKB_CONFIG_ROOT=/tmp
#Backends Qt5 disponibles, à titre indicatif
export QT_QPA_PLATFORM_PLUGIN_PATH=/usr/lib/qt/plugins/platforms
#Dossier des polices TTF
export QT_QPA_FONTDIR=/usr/share/fonts
#Active le clavier dans le terminal en arrière plan (permet notamment le CTRL+C)
export QT_QPA_ENABLE_TERMINAL_KEYBOARD=1
#Cache le curseur de la souris
export QT_QPA_EGLFS_HIDECURSOR=1
qmlscene box.qml -platform eglfsIl n'est pas necéssaire d'utiliser
export LD_LIBRARY_PATH=/opt/vc/lib/:$LD_LIBRARY_PATHcar le package Rpi-userland place les librairies dans/usr/libau lieu de/opt/vc/lib.
output/rootfs_overlay/root/box.qml: l'application QML de
test qui sera exécutée avec qmlscene. Il s'agit d'une boîte
rouge déplaçable avec les flềches du clavier et les cliques d'une
souris.
import QtQuick 2.0
Item {
width: 800; height: 600
focus: true
Keys.onPressed: {
if (event.key == Qt.Key_Enter) {
rect.x = 600
rect.y = 400
}
if (event.key == Qt.Key_Space) {
rect.x = 50
rect.y = 50
}
if (event.key == Qt.Key_Left) {
rect.x = rect.x - 50
}
if (event.key == Qt.Key_Right) {
rect.x = rect.x + 50
}
if (event.key == Qt.Key_Down) {
rect.y = rect.y + 50
}
if (event.key == Qt.Key_Up) {
rect.y = rect.y - 50
}
}
Rectangle {
anchors.fill: parent
color: "black"
}
Rectangle {
id: rect
width: 100; height: 100
color: "red"
Behavior on x { PropertyAnimation { duration: 500 } }
Behavior on y { PropertyAnimation { duration: 500 } }
Text {
text: "Hello\nworld!"
font.pointSize: 24; font.bold: true
}
}
MouseArea {
anchors.fill: parent
onClicked: { rect.x = mouse.x; rect.y = mouse.y; }
}
}Si la résolution est supérieur à 800x600 pixels, l'application utilisera l'ensemble de l'espace disponible sans tenir compte des dimmensions
width: 800; height: 600.
Compilation
Lancer la compilation et la création de l'image:
make
Pour vérifier les options de compilation du package
qt5base, notamment les backends disponibles:
cat ./output/build/qt5base-5.15.2/config.summary
QPA backends:
DirectFB ............................... no
EGLFS .................................. yes
EGLFS details:
EGLFS OpenWFD ........................ no
EGLFS i.Mx6 .......................... no
EGLFS i.Mx6 Wayland .................. no
EGLFS RCAR ........................... no
EGLFS EGLDevice ...................... no
EGLFS GBM ............................ no
EGLFS VSP2 ........................... no
EGLFS Mali ........................... no
EGLFS Raspberry Pi ................... yes
EGLFS X11 ............................ no
LinuxFB ................................ no
VNC .................................... no
Copie de l'image générée vers la carte SD:
sudo dd if=output/images/sdcard.img of=/dev/mmcblk0 status=progress
Après démarrer le système sur le Raspberry, exécuter
test.sh:
sh test.sh
Utiliser eglfs_kms comme backend au lieu de eglfs_brcm
Au lieu d'utiliser le pilote propriétaire Broadcom, nous allons utiliser le backend OpenGLES libre fourni avec Mesa3d ainsi que le pilote VC4.
Configuration Buildroot
make raspberrypi3_defconfig
make menuconfigSystem configuration
├─ System hostname (BR2_TARGET_GENERIC_HOSTNAME="buildrootqt5")
├─ Root password (BR2_TARGET_GENERIC_ROOT_PASSWD="root")
├─ remount root filesystem read-write during boot (BR2_TARGET_GENERIC_REMOUNT_ROOTFS_RW=n)
├─ Root filesystem overlay directories (BR2_ROOTFS_OVERLAY=output/rootfs_overlay)
├─ Custom scripts to run before creating filesystem images
| (BR2_ROOTFS_POST_BUILD_SCRIPT=board/raspberrypi3/post-build.sh output/post-build-config.sh)
└─ /dev management (Dynamic using devtmpfs + eudev) (BR2_ROOTFS_DEVICE_CREATION_DYNAMIC_EUDEV=y)
Toolchain
└─ Enable WCHAR support (BR2_TOOLCHAIN_BUILDROOT_WCHAR=y)
Target packages
├─ Libraries
| └─ Graphics
| └─ libdrm (BR2_PACKAGE_LIBDRM=y)
| └─ vc4 (BR2_PACKAGE_LIBDRM_VC4=y)
└─ Graphic libraries and applications (graphic/text)
├─ mesa3d (BR2_PACKAGE_MESA3D=y)
| ├─ Gallium vc4 driver (BR2_PACKAGE_MESA3D_GALLIUM_DRIVER_VC4=y)
| └─ OpenGL ES (BR2_PACKAGE_MESA3D_OPENGL_ES=y)
└─ Qt5 (BR2_PACKAGE_QT5=y)
├─ Custom configuration options (BR2_PACKAGE_QT5BASE_CUSTOM_CONF_OPTS
| [=-skip qtconnectivity -skip qtnetwork -skip qtgamepad -no-feature-vnc -no-feature-accessibility -nomake tests])
├─ eglfs support (BR2_PACKAGE_QT5BASE_EGLFS=y)
└─ Default graphical platform (BR2_PACKAGE_QT5BASE_DEFAULT_QPA="eglfs")
Les packages mesa3d → OpenGL ES (BR2_PACKAGE_MESA3D_OPENGL_ES=y) et rpi-userland (BR2_PACKAGE_RPI_USERLAND=y) ne peuvent cohabiter pour fournir un support OpenGLES.
Nous utiliserons un script post-build
output/post-build-config.sh en plus de celui par défaut
board/raspberrypi3/post-build.sh. Ils doivent être séparés
par un espace.
L'utilisation de udev (eudev) est util pour la détection automatique
des périphériques et charger les modules requis ce qui permettra de
rendre disponible le GPU dans /dev/dri/card0. Il est
possible de se passer de udev en chargeant les modules requis
manuellement (cas exposé plus tard).
Pas besoin d'inclure mesa3d → Gallium v3d driver.
Sauvegarde de la configuration Buildroot:
make savedefconfig BR2_DEFCONFIG=./buildrootqt5_vc4.config
Configuration du noyau Linux
Pour la configuration du noyau, il faut inclure le pilote VC4:
make linux-menuconfig
Device Drivers
└─ Graphics support
├─ Direct Rendering Manager (XFree86 4.1.0 and higher DRI support) (CONFIG_DRM=y)
└─ Broadcom VC4 Graphics (CONFIG_DRM_VC4=m)
Pas besoin d'inclure Device Drivers → Graphics support → Broadcom V3D 3.x and newer (CONFIG_DRM_V3D)
Sauvegarde de la configuration du noyau:
cp output/build/linux-custom/.config linux_vc4.config
Overlay
Le contenu de l'overlay output/rootfs_overlay reste le
même sauf pour le script output/rootfs_overlay/root/test.sh
qui intègre une valeur différente pour
QT_QPA_EGLFS_INTEGRATION et une nouvelle variable
QT_QPA_EGLFS_NO_LIBINPUT:
#!/bin/sh
export QT_QPA_PLATFORM=eglfs
export QT_QPA_EGLFS_INTEGRATION=eglfs_kms
export QT_QPA_EGLFS_ALWAYS_SET_MODE=1
export QT_XKB_CONFIG_ROOT=/tmp
#Backends Qt5 disponibles, à titre indicatif
export QT_QPA_PLATFORM_PLUGIN_PATH=/usr/lib/qt/plugins/platforms
#Dossier des polices TTF
export QT_QPA_FONTDIR=/usr/share/fonts
#Active le clavier dans le terminal en arrière plan (permet notamment le CTRL+C)
export QT_QPA_ENABLE_TERMINAL_KEYBOARD=1
#Désactive Libinput, corrige le problème de clavier non fonctionnel
export QT_QPA_EGLFS_NO_LIBINPUT=1
#Cache le curseur de la souris
export QT_QPA_EGLFS_HIDECURSOR=1
qmlscene box.qml -platform eglfsAvec l'ajout de udev, Qt5 utilise son composant evdev qui nécessite la désactivation de Libinput avec
QT_QPA_EGLFS_NO_LIBINPUT=1.
Script post-build
Le chargement du pilote VC4 au démarrage nécessite l'ajout des
directives gpu_mem=128 et
dtoverlay=vc4-kms-v3d dans le fichier
config.txt de la partition de boot. Pour cela nous
utiliserons un script post-build post-build-config.sh dans
le dossier output/ (déjà configuré dans Buildroot).
post-build-config.sh:
#!/bin/sh
set -u
set -e
if ! grep -qE '^gpu_mem=128' "${BINARIES_DIR}/rpi-firmware/config.txt" && ! grep -qE '^dtoverlay=vc4-kms-v3d' "${BINARIES_DIR}/rpi-firmware/config.txt"; then
echo "Adding 'gpu_mem=128' and 'dtoverlay=vc4-kms-v3d' to config.txt (load VC4)."
echo "gpu_mem=128" >> "${BINARIES_DIR}/rpi-firmware/config.txt"
echo "dtoverlay=vc4-kms-v3d" >> "${BINARIES_DIR}/rpi-firmware/config.txt"
fiNe pas oublier de donner les droits d'exécution avec
chmod +x output/post-build-config.sh.
Compilation
Lancer la compilation et la création de l'image:
make
Pour vérifier les options de compilation du package
qt5base, notamment les backends disponibles:
cat ./output/build/qt5base-5.15.2/config.summary
QPA backends:
DirectFB ............................... no
EGLFS .................................. yes
EGLFS details:
EGLFS OpenWFD ........................ no
EGLFS i.Mx6 .......................... no
EGLFS i.Mx6 Wayland .................. no
EGLFS RCAR ........................... no
EGLFS EGLDevice ...................... yes
EGLFS GBM ............................ yes
EGLFS VSP2 ........................... no
EGLFS Mali ........................... no
EGLFS Raspberry Pi ................... no
EGLFS X11 ............................ no
LinuxFB ................................ no
VNC .................................... no
Copie de l'image générée vers la carte SD:
sudo dd if=output/images/sdcard.img of=/dev/mmcblk0 status=progress
Après démarrer le système sur le Raspberry, exécuter
test.sh pour lancer l'application QML:
sh test.sh
Se passer de udev (eudev)
Il est possible de ne pas inclure udev (dans Buildroot: System
configuration → /dev management). Les modules chargés
automatiquement par udev (liste récupérée avec lsmod):
Module Size Used by Tainted: G
snd_soc_hdmi_codec 20480 1
brcmfmac 327680 0
brcmutil 24576 1 brcmfmac
sha256_generic 16384 0
libsha256 20480 1 sha256_generic
vc4 249856 2
snd_soc_core 229376 2 snd_soc_hdmi_codec,vc4
snd_compress 20480 1 snd_soc_core
cfg80211 782336 1 brcmfmac
snd_pcm_dmaengine 16384 1 snd_soc_core
snd_pcm 114688 4 snd_soc_hdmi_codec,snd_soc_core,snd_compress,snd_pcm_dmaengine
snd_timer 36864 1 snd_pcm
snd 77824 5 snd_soc_hdmi_codec,snd_soc_core,snd_compress,snd_pcm,snd_timer
raspberrypi_hwmon 16384 0
i2c_bcm2835 16384 0
vc_sm_cma 32768 0
uio_pdrv_genirq 16384 0
uio 20480 1 uio_pdrv_genirq
fixed 16384 0
Pour se passer de udev et obtenir /dev/dri/card0,
certains modules doivent être chargés (via un script ou manuellement)
avec modprobe:
#!/bin/sh
modprobe snd_soc_hdmi_codec
modprobe i2c_bcm2835
modprobe vc4
modprobe vc_sm_cmaRésultat dans dmesg:
[ 40.318528] fb0: switching to vc4drmfb from simple
[ 40.325374] Console: switching to colour dummy device 80x30
[ 40.662845] vc4-drm soc:gpu: bound 3f400000.hvs (ops vc4_hvs_ops [vc4])
[ 40.677294] vc4-drm soc:gpu: bound 3f902000.hdmi (ops vc4_hdmi_ops [vc4])
[ 40.684485] vc4-drm soc:gpu: bound 3f806000.vec (ops vc4_vec_ops [vc4])
[ 40.691473] vc4-drm soc:gpu: bound 3f004000.txp (ops vc4_txp_ops [vc4])
[ 40.698433] vc4-drm soc:gpu: bound 3f206000.pixelvalve (ops vc4_crtc_ops [vc4])
[ 40.706101] vc4-drm soc:gpu: bound 3f207000.pixelvalve (ops vc4_crtc_ops [vc4])
[ 40.713734] vc4-drm soc:gpu: bound 3f807000.pixelvalve (ops vc4_crtc_ops [vc4])
[ 40.721306] vc4-drm soc:gpu: bound 3fc00000.v3d (ops vc4_v3d_ops [vc4])
[ 40.731228] [drm] Initialized vc4 0.0.0 20140616 for soc:gpu on minor 0
[ 40.881762] Console: switching to colour frame buffer device 240x67
[ 40.921477] vc4-drm soc:gpu: [drm] fb0: vc4drmfb frame buffer device
[ 41.002400] vc_sm_cma: module is from the staging directory, the quality is unknown, you have been warned.
[ 41.014768] bcm2835_vc_sm_cma_probe: Videocore shared memory driver
[ 41.021175] [vc_sm_connected_init]: start
[ 41.026489] [vc_sm_connected_init]: installed successfully