How to enable VirtIO-GPU Venus on QEMU explained

/ Linux Qemu

This article explains how to enable VirtIO-GPU Venus on QEMU, which is now having venus=on option support from v9.2.0 (released Nov. 2024). On top of it, I will show some benchmark results and compare it with VirGL solution.

vkcube on ubuntu and vkcube on qemu

Introduction

Official documentation of Virtio-GPU Venus is here. As explained on this article, Venus, as the code name of virtual Vulkan driver, is a relatively new solution to use host GPU through Virtual machine. Though this Venus articles was written in 2021 and development on QEMU seemed to be conducted on QEMU, its support on QEMU hasn't been upstreamed for long time. At the end of last year (2024), QEMU finally merged venus support at v9.2.0. Now everyone can try this solution without any patch-set to QEMU. By using v9.2.0+, just adding -device virtio-vga-gl,blob=on,hostmem=4G,venus=on will enable us to use virtual Vulkan driver. More detailed procedure will be shown on this paper.

Prerequisite

  • Ubuntu with docker installed.
  • YoctoLinux build environment.
  • x86_64 machine (if using my prebuilt core-image-weston-rootfs.ext4)

Everything necessary below will be built from source code.

  • Qemu v9.2.0
  • Mesa 24.0.1
  • Virglrenderer 1.1.0
  • bzImage(Linux kernel) 5.16+

Essentially only what we need to have is docker. Yocto Linux environment is nice to have if you want to modify guest machine component: Mesa version and so on.

The version combination of Mesa 24.0.1 / Virglrenderer 1.1.0 doesn't mean starting version of Venus support. It's just an version I verified. The exact support status can essentially varies for each combination of mesa/virglrenderer. We can't say simply which version of mesa/virglrenderer will gracefully support venus.

Build instructions

It's often the case that OSS software has many compile time options. Some of these are automatically detected at configure script depending on installed libraries. Qemu, VirGL, Mesa, all of these have this mechanism. Only way to precisely select desired option for each component is to make your own binary from source code. Here, I show how to make these component by using Docker Ubuntu environment.

1. QEMU with VirGL renderer

The key option is '-Dvenus=true' for virglrenderer. Qemu configure script will automatically detect platform installed virglrenderer without any option. DO NOT install virglrenderer from apt install command to make sure that qemu can detect locally built virglrenderer. I prepared just-use-this Dockerfile as below:

FROM ubuntu:22.04

RUN apt update && apt install -y tzdata

RUN apt update && apt install -y \
    bison clang cmake device-tree-compiler flex git iasl libaio-dev libbluetooth-dev libbpfcc-dev \
    libbpf-dev libbrlapi-dev libbz2-dev libcacard-dev libcap-ng-dev libcapstone-dev libcbor-dev \
    libcurl4-gnutls-dev libdaxctl-dev libdwarf-dev libdw-dev libfdt-dev libfuse3-dev libglib2.0-dev \
    libgstreamer1.0-dev libgstreamer-plugins-bad1.0-dev libgstreamer-plugins-base1.0-dev \
    libgstreamer-plugins-good1.0-dev libgtk-3-dev libgvnc-1.0-dev libibverbs-dev libiscsi-dev \
    libjack-dev libjpeg8-dev libkeyutils-dev liblzo2-dev libncurses5-dev libnfs-dev libnuma-dev \
    libpixman-1-dev libpmem2-dev libpmemblk-dev libpmem-dev libpmemkv-dev libpmemlog-dev libpmemobj-cpp-dev \
    libpmemobj-dev libpmempool-dev librbd-dev librdmacm-dev libsasl2-dev libsdl2-dev libsdl-image-gst \
    libseccomp-dev libslirp-dev libsnappy-dev libspice-protocol-dev libspice-server-dev libssh-dev \
    liburing-dev libusb-1.0-0-dev libusbredirhost-dev libusbredirparser-dev libvde-dev libvdeplug-dev \
    libvdeslirp-dev libvte-2.91-dev libxen-dev libzstd-dev mesa-vulkan-drivers meson ninja-build \
    pkg-config python3 python3-tomli python3-venv valgrind vulkan-validationlayers xfslibs-dev

RUN cd /tmp && \
    git clone -b sdk-1.3.261.1 https://github.com/KhronosGroup/Vulkan-Headers.git && \
    cd Vulkan-Headers && mkdir build && cd build && \
    cmake .. -DCMAKE_INSTALL_PREFIX=/usr && make install && \
    cd /tmp && \
    git clone -b sdk-1.3.261.1 https://github.com/KhronosGroup/Vulkan-Loader.git && \
    cd Vulkan-Loader && mkdir build && cd build && \
    cmake .. -DCMAKE_INSTALL_PREFIX=/usr && make install && \
    cd /tmp && \
    git clone -b 1.1.0 --depth 1 https://gitlab.freedesktop.org/virgl/virglrenderer.git && \
    cd virglrenderer && mkdir build && cd build && \
    meson .. --prefix=/usr -Dvenus=true && \
    ninja -j6 && ninja install

RUN cd /tmp && \
    git clone --depth 1 -b v9.2.0 https://gitlab.com/qemu-project/qemu.git && \
    cd qemu && mkdir build && cd build && \
    ../configure \
    --prefix=/usr \
    --target-list=x86_64-softmmu && \
    ninja -j6 && \
    ninja install && \
    ninja clean

Saving this as Dockerfile, run following command to build docker image.

docker build -t qemu .

Please make sure that following output be seen

docker run -it --rm qemu-release qemu-system-x86_64 --version
QEMU emulator version 9.2.0 (v9.2.0)
Copyright (c) 2003-2024 Fabrice Bellard and the QEMU Project developers

Now, qemu-system-x86_64 with necessary libraries is successfully built as docker image. It means, the image tagged as qemu contains /usr/bin/qemu-system-x86_64 and runtime libraries needed by qemu.

2. Linux Kernel

Venus depends on VirtIO-GPU shared memory feature on guest Linux Kernel. Linux kernel supported this feature from 5.16 upstream. Linux kernel has also many many compile time options, known as Kconfig or simply kernel configuration. Although Yocto Linux procedure will produce bzImage, it's more clear for us to build Kernel separately from YoctoLinux, because -kernel option of qemu enable us to directly run the kernel as if kernel is just an certain kind of executable.

To build Linux kernel, docker container is also useful to setup prerequisite environment.

FROM ubuntu:24.04

# https://wiki.qemu.org/Hosts/Linux
RUN apt update && apt install -y git build-essential flex bison bc libssl-dev libelf-dev libncurses-dev

It can be done on Ubuntu PC without docker. For now I build it with docker build -t linux . and run following steps.

docker run -it --rm -v $(pwd):$(pwd) -w $(pwd) linux /bin/bash
git clone --depth 1 -b v6.1.122  https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-6.1
cd linux-6.1
make O=build defconfig
./scripts/kconfig/merge_config.sh -O build arch/x86/configs/x86_64_defconfig ../virtual_device.cfg
make O=build bzImage -j $(nproc)
exit

As you see, first line will run docker container and commands until exit should run inside container. Here virtual_device.cfg is a fragment file which have following contents

cat virtual_device.cfg
CONFIG_VIRTIO=y
CONFIG_VIRTIO_PCI=y
CONFIG_VIRTIO_PCI_LEGACY=y
CONFIG_VIRTIO_BLK=y
CONFIG_BLK_MQ_VIRTIO=y
CONFIG_DRM_VIRTIO_GPU=y

Save it to $(pwd) directory before the above steps. You can add another Kconfig options to try qemu another feature. For this article I avoid going this another direction of trial. Finally you can find bzImage, kernel file excutable by Qemu.

  LD      arch/x86/boot/compressed/vmlinux
  ZOFFSET arch/x86/boot/zoffset.h
  OBJCOPY arch/x86/boot/vmlinux.bin
  AS      arch/x86/boot/header.o
  LD      arch/x86/boot/setup.elf
  OBJCOPY arch/x86/boot/setup.bin
  BUILD   arch/x86/boot/bzImage
Kernel: arch/x86/boot/bzImage is ready  (#1)
make[1]: Leaving directory '/home/user/work/linux-release/build'
exit
ls linux-6.1/build/arch/x86/boot/bzImage

Final text should be like this. linux-6.1/build/arch/x86/boot/bzImage can be seen from outside of docker environment. Pick it to your suitable location. If build tree is no longer needed, you can remove whole linux-6.1 directory by rm -rf.

3. Yocto Linux rootfs

This step will be a little bit difficult and time consuming for some people. If you are not interested in customizing and engineering guest operating system, you can use my prebuilt rootfs to quickly try VirtIO-GPU Venus. In this case you can skip this step and move on to "Run Qemu & Try vkcube".

Official documentation of Yocto/bitbake is here. I delegate environment setup to this documentation for using bitbake command and concentrate on explaining customization point to enable VirtIO-GPU Venus from guest operating system. At first, please make sure you can build core-image-weston as below steps:

git clone git://git.yoctoproject.org/poky 
cd poky
git checkout yocto-5.1.1
. ./oe-init-build-env build-styhead
bitbake core-image-weston

This will produce out-of-box rootfs at build-styhead/tmp/deploy/images/qemux86-64/core-image-weston-qemux86-64.rootfs.ext4. When using Yocto-5.1.1, Mesa 24.0.7 is used by default. The version 24.0.7 is recent enough to use -Dvulkan-drivers=virtio from do_configure. However, this -Dvulkan-drivers=virtio configuration is not put by default so that we need to add following bbappend files to mesa recipe.

FILESEXTRAPATHS:prepend := "${THISDIR}/files:"
VULKAN_DRIVERS = "virtio"
GALLIUMDRIVERS = "zink,virgl"
EXTRA_OEMESON += " \
    -Dvulkan-layers=device-select \
"
FILES:mesa-vulkan-drivers += " \
    /usr/lib/libVkLayer_MESA_device_select.so \
"
SRC_URI += " \
    file://0001-venus-make-cross-device-optional.patch \
"

Note that, I added this commit to let vkCreateInstance successful on Qemu v9.2.0, ahead of Yocto mesa move on to more recent version of Mesa. Unless this fix, vkCreateInstance will fail due to missing VIRTGPU_PARAM_CROSS_DEVICE. Only this fix is mandatory to use VirtIO-GPU Venus for Yocto-5.1.1. For this point, there is no vulkan application to call Vulkan API at all. "vkcube" can be added by

DEPENDS += "wayland wayland-native wayland-protocols"

EXTRA_OECMAKE = " \
    -DBUILD_CUBE=ON \
    -DCUBE_WSI_SELECTION=WAYLAND \
"

My prebuilt rootfs contains another modification which is not explained here.

Run Qemu & Try vkcube

Now you have all things to try VirtIO-GPU Venus on your laptop: qemu docker image, bzImage, core-image-weston-qemux86-64.ext4. Put it into your current directly and run following script:

#!/usr/bin/env bash

docker run -it --rm \
    -w $(pwd) \
    -v $(pwd):$(pwd) \
    -v /run:/run \
    -e XDG_RUNTIME_DIR=/run/user/1000 \
    --privileged \
    qemu \
    qemu-system-x86_64 \
    -m 4G -smp 4 --enable-kvm \
    -kernel bzImage-release \
    -append "console=ttyS0 root=/dev/vda nokaslr" \
    -serial mon:stdio \
    -display gtk,gl=on -vga none \
    -device virtio-vga-gl,blob=on,hostmem=4G,venus=on \
    -object memory-backend-memfd,id=mem1,size=4G \
    -machine memory-backend=mem1 \
    -drive file=core-image-weston-qemux86-64.rootfs.ext4,format=raw,if=virtio

Those who knows well about virtualization technologies may wonder why we need to doubly virtualize guest by docker + qemu, but it actually works. By using terms from computer science and virtualization technologies, Type2 + Type3 virtualization can co-exist. Note that, KVM sometimes regardeed as Type-1, but here I don't deep dive into this direction. Now, you can see the console like

Poky (Yocto Project Reference Distro) 5.1.1 qemux86-64 ttyS0

qemux86-64 login:

Just enter "root" here. Run vkcube by

root@qemux86-64:~# export XDG_RUNTIME_DIR=/run/user/1000/
root@qemux86-64:~# export WAYLAND_DISPLAY=wayland-1
root@qemux86-64:~# export VK_DRIVER_FILES=/usr/share/vulkan/icd.d/virtio_icd.x86_64.json
root@qemux86-64:~# 
root@qemux86-64:~# vkcube
Selected GPU 0: Virtio-GPU Venus (Intel(R) HD Graphics 520 (SKL GT2)), type: IntegratedGpu

vkcube using VirtIO-GPU Venus

Success!!

Benckmark result

There are two well-known GPU benckmarks: glmark2, vkmark. I added these two from Yocto recipe. I omit whole procedure to do it, but again my prebuilt rootfs contains both these two benchmarks. Still you can do the same with me quickly.

vkmark

  1. Ubuntu
  2. VirtIO-GPU Venus
  3. VirtIO-GPU Venus (offscreen)

Result (Ubuntu)

=======================================================
    vkmark 2017.08
=======================================================
    Vendor ID:      0x8086
    Device ID:      0x1916
    Device Name:    Intel(R) HD Graphics 520 (SKL GT2)
    Driver Version: 100663305
    Device UUID:    50ffa5a61895bba5200df0064824bd39
=======================================================
[vertex] device-local=true: FPS: 3968 FrameTime: 0.252 ms
[vertex] device-local=false: FPS: 4062 FrameTime: 0.246 ms
[texture] anisotropy=0: FPS: 3767 FrameTime: 0.265 ms
[texture] anisotropy=16: FPS: 3770 FrameTime: 0.265 ms
[shading] shading=gouraud: FPS: 3190 FrameTime: 0.313 ms
[shading] shading=blinn-phong-inf: FPS: 3239 FrameTime: 0.309 ms
[shading] shading=phong: FPS: 3365 FrameTime: 0.297 ms
[shading] shading=cel: FPS: 3319 FrameTime: 0.301 ms
[effect2d] kernel=edge: FPS: 3381 FrameTime: 0.296 ms
[effect2d] kernel=blur: FPS: 1273 FrameTime: 0.786 ms
[desktop] <default>: FPS: 876 FrameTime: 1.142 ms
[cube] <default>: FPS: 5195 FrameTime: 0.192 ms
[clear] <default>: FPS: 4682 FrameTime: 0.214 ms
=======================================================
                                   vkmark Score: 3391
=======================================================

vkmark run on native Ubuntu destop scene

vkmark run on native Ubuntu cube scene

This is expected as highest result without any GPU virtualization. For this picture, I used -display gtk,gl=on,full-screen=on to see guest screen as full-screen mode. Gtk+3 display default size was too small to see the content of vkmark.

Result (VirtIO-GPU Venus)

root@qemux86-64:~# vkmark
=======================================================
    vkmark 2017.08
=======================================================
    Vendor ID:      0x8086
    Device ID:      0x1916
    Device Name:    Virtio-GPU Venus (Intel(R) HD Graphics 520 (SKL GT2))
    Driver Version: 100663303
    Device UUID:    b07efb5ab969942c4d4245e8c29beaf1
=======================================================
[vertex] device-local=true: FPS: 747 FrameTime: 1.339 ms
[vertex] device-local=false: FPS: 737 FrameTime: 1.357 ms
[texture] anisotropy=0: FPS: 710 FrameTime: 1.408 ms
[texture] anisotropy=16: FPS: 707 FrameTime: 1.414 ms
[shading] shading=gouraud: FPS: 674 FrameTime: 1.484 ms
[shading] shading=blinn-phong-inf: FPS: 684 FrameTime: 1.462 ms
[shading] shading=phong: FPS: 692 FrameTime: 1.445 ms
[shading] shading=cel: FPS: 695 FrameTime: 1.439 ms
[effect2d] kernel=edge: FPS: 870 FrameTime: 1.149 ms
[effect2d] kernel=blur: FPS: 708 FrameTime: 1.412 ms
[desktop] <default>: FPS: 486 FrameTime: 2.058 ms
[cube] <default>: FPS: 758 FrameTime: 1.319 ms
[clear] <default>: FPS: 800 FrameTime: 1.250 ms
=======================================================
                                   vkmark Score: 712
=======================================================

vkmark on qemu VirtIO-GPU venus desktop scene

vkmark on qemu VirtIO-GPU venus cube scene

This is VirtIO-GPU Venus solution performance, compared to native GPU.

Result (VirtIO-GPU Venus (offscreen))

root@qemux86-64:~# vkmark
=======================================================
    vkmark 2017.08
=======================================================
    Vendor ID:      0x8086
    Device ID:      0x1916
    Device Name:    Virtio-GPU Venus (Intel(R) HD Graphics 520 (SKL GT2))
    Driver Version: 100663303
    Device UUID:    b07efb5ab969942c4d4245e8c29beaf1
=======================================================
[vertex] device-local=true: FPS: 1521 FrameTime: 0.657 ms
[vertex] device-local=false: FPS: 1536 FrameTime: 0.651 ms
[texture] anisotropy=0: FPS: 1344 FrameTime: 0.744 ms
[texture] anisotropy=16: FPS: 1341 FrameTime: 0.746 ms
[shading] shading=gouraud: FPS: 1336 FrameTime: 0.749 ms
[shading] shading=blinn-phong-inf: FPS: 1331 FrameTime: 0.751 ms
[shading] shading=phong: FPS: 1382 FrameTime: 0.724 ms
[shading] shading=cel: FPS: 1403 FrameTime: 0.713 ms
[effect2d] kernel=edge: FPS: 1863 FrameTime: 0.537 ms
[effect2d] kernel=blur: FPS: 1499 FrameTime: 0.667 ms
[desktop] <default>: FPS: 818 FrameTime: 1.222 ms
[cube] <default>: FPS: 1675 FrameTime: 0.597 ms
[clear] <default>: FPS: 1885 FrameTime: 0.531 ms
=======================================================
                                   vkmark Score: 1456
=======================================================

display not active by weston

Final result require some explanation about off-screen. After 300 seconds from booting qemu, weston, the gray desktop, goes into inactive mode and don't output any screen content at all. This corresponds to sleep mode of usual laptop."Display output is not active" is drawn by Qemu and this indicates that guest virtual machine doesn't put any output. Due to some reason, vkmark hit higher score on this inactive status.

Summary is here.


scene Ubuntu VirtIO-GPU Venus
(offscreen)
VirtIO-GPU Venus
(onscreen)
vertex device-local=true 3968 1521 747
vertex device-local=false 4062 1536 737
texture anisotropy=0 3767 1344 710
texture anisotropy=16 3770 1341 707
shading shading=gouraud 3190 1336 674
shading shading=blinn-phong-inf 3239 1331 684
shading shading=phong 3365 1382 692
shading shading=cel 3319 1403 695
effect2d kernel=edge 3381 1863 870
effect2d kernel=blur 1273 1499 708
desktop default 876 818 486
cube default 5195 1675 758
clear default 4682 1885 800
vkmark score 3391 1456 712

Off-screen result is not affected by Window System Integration, which depends on Qemu implementation arround Gtk+, and in this case guest system also has windowing system: weston. Precisely speaking, drawing contents by GPU and display its output into physical display relay on different mechanism. By using virtio-vga-gl (compared to virtio-gpu-gl), latter part also relay on VirtIO-GPU scan-out feature. While GPU performance only determined by first part (means drawing content by GPU), off-screen result represents potential performance upper bounds of Virtio-GPU Venus solution.

glmark2: Zink vs VirGL

Zink is also relatively new solution to convert OpenGL commands into VirtIO-GPU Venus. By using Zink, OpenGL commands will become Vulkan commands on Host side via VirtIO-GPU Venus transport. We've already done the preparation to use zink for any OpenGL application. Remember that mesa configure contains -Dgallium-drivers=zink,virgl. This was preparation. To use zink for glmark2, run

MESA_LOADER_DRIVER_OVERRIDE=zink glmark2-es2-wayland

is enough. export MESA_LOADER_DRIVER_OVERRIDE=zink will set zink by default.

  1. Ubuntu
  2. VirGL offscreen
  3. Zink offscreen

Ubuntu

user@user:~$ glmark2-es2-wayland
=======================================================
    glmark2 2023.01
=======================================================
    OpenGL Information
    GL_VENDOR:      Intel
    GL_RENDERER:    Mesa Intel(R) HD Graphics 520 (SKL GT2)
    GL_VERSION:     OpenGL ES 3.2 Mesa 24.0.9-0ubuntu0.3
    Surface Config: buf=32 r=8 g=8 b=8 a=8 depth=24 stencil=0 samples=0
    Surface Size:   800x600 windowed
=======================================================
[build] use-vbo=false: FPS: 2006 FrameTime: 0.499 ms
[build] use-vbo=true: FPS: 4201 FrameTime: 0.238 ms
[texture] texture-filter=nearest: FPS: 3674 FrameTime: 0.272 ms
[texture] texture-filter=linear: FPS: 3568 FrameTime: 0.280 ms
[texture] texture-filter=mipmap: FPS: 3530 FrameTime: 0.283 ms
[shading] shading=gouraud: FPS: 2999 FrameTime: 0.333 ms
[shading] shading=blinn-phong-inf: FPS: 2528 FrameTime: 0.396 ms
[shading] shading=phong: FPS: 2934 FrameTime: 0.341 ms
[shading] shading=cel: FPS: 2802 FrameTime: 0.357 ms
[bump] bump-render=high-poly: FPS: 1158 FrameTime: 0.864 ms
[bump] bump-render=normals: FPS: 4161 FrameTime: 0.240 ms
[bump] bump-render=height: FPS: 3880 FrameTime: 0.258 ms
[effect2d] kernel=0,1,0;1,-4,1;0,1,0;: FPS: 3533 FrameTime: 0.283 ms
[effect2d] kernel=1,1,1,1,1;1,1,1,1,1;1,1,1,1,1;: FPS: 1245 FrameTime: 0.804 ms
[pulsar] light=false:quads=5:texture=false: FPS: 4180 FrameTime: 0.239 ms
[desktop] blur-radius=5:effect=blur:passes=1:separable=true:windows=4: FPS: 1141 FrameTime: 0.876 ms
[desktop] effect=shadow:windows=4: FPS: 1853 FrameTime: 0.540 ms
[buffer] columns=200:interleave=false:update-dispersion=0.9:update-fraction=0.5:update-method=map: FPS: 467 FrameTime: 2.144 ms
[buffer] columns=200:interleave=false:update-dispersion=0.9:update-fraction=0.5:update-method=subdata: FPS: 539 FrameTime: 1.856 ms
[buffer] columns=200:interleave=true:update-dispersion=0.9:update-fraction=0.5:update-method=map: FPS: 492 FrameTime: 2.036 ms
[ideas] speed=duration: FPS: 2174 FrameTime: 0.460 ms
[jellyfish] <default>: FPS: 2946 FrameTime: 0.340 ms
[terrain] <default>: FPS: 175 FrameTime: 5.741 ms
[shadow] <default>: FPS: 1132 FrameTime: 0.884 ms
[refract] <default>: FPS: 282 FrameTime: 3.549 ms
[conditionals] fragment-steps=0:vertex-steps=0: FPS: 2445 FrameTime: 0.409 ms
[conditionals] fragment-steps=5:vertex-steps=0: FPS: 2722 FrameTime: 0.367 ms
[conditionals] fragment-steps=0:vertex-steps=5: FPS: 2440 FrameTime: 0.410 ms
[function] fragment-complexity=low:fragment-steps=5: FPS: 2544 FrameTime: 0.393 ms
[function] fragment-complexity=medium:fragment-steps=5: FPS: 3001 FrameTime: 0.333 ms
[loop] fragment-loop=false:fragment-steps=5:vertex-steps=5: FPS: 2598 FrameTime: 0.385 ms
[loop] fragment-steps=5:fragment-uniform=false:vertex-steps=5: FPS: 2615 FrameTime: 0.383 ms
[loop] fragment-steps=5:fragment-uniform=true:vertex-steps=5: FPS: 2827 FrameTime: 0.354 ms
=======================================================
                                  glmark2 Score: 2386
=======================================================

Result (VirGL offscreen)

root@qemux86-64:~# glmark2-es2-wayland
=======================================================
    glmark2 2023.01
=======================================================
    OpenGL Information
    GL_VENDOR:      Mesa
    GL_RENDERER:    virgl (Mesa Intel(R) HD Graphics 520 (SKL GT2))
    GL_VERSION:     OpenGL ES 3.2 Mesa 24.0.7
    Surface Config: buf=32 r=8 g=8 b=8 a=8 depth=32 stencil=0 samples=0
    Surface Size:   800x600 windowed
=======================================================
[build] use-vbo=false: FPS: 1193 FrameTime: 0.838 ms
[build] use-vbo=true: FPS: 1637 FrameTime: 0.611 ms
[texture] texture-filter=nearest: FPS: 1276 FrameTime: 0.784 ms
[texture] texture-filter=linear: FPS: 782 FrameTime: 1.279 ms
[texture] texture-filter=mipmap: FPS: 1216 FrameTime: 0.823 ms
[shading] shading=gouraud: FPS: 786 FrameTime: 1.273 ms
[shading] shading=blinn-phong-inf: FPS: 1311 FrameTime: 0.763 ms
[shading] shading=phong: FPS: 791 FrameTime: 1.265 ms
[shading] shading=cel: FPS: 532 FrameTime: 1.881 ms
[bump] bump-render=high-poly: FPS: 182 FrameTime: 5.507 ms
[bump] bump-render=normals: FPS: 1624 FrameTime: 0.616 ms
[bump] bump-render=height: FPS: 1353 FrameTime: 0.739 ms
[effect2d] kernel=0,1,0;1,-4,1;0,1,0;: FPS: 182 FrameTime: 5.511 ms
[effect2d] kernel=1,1,1,1,1;1,1,1,1,1;1,1,1,1,1;: FPS: 182 FrameTime: 5.517 ms
[pulsar] light=false:quads=5:texture=false: FPS: 1030 FrameTime: 0.971 ms
[desktop] blur-radius=5:effect=blur:passes=1:separable=true:windows=4: FPS: 180 FrameTime: 5.564 ms
[desktop] effect=shadow:windows=4: FPS: 181 FrameTime: 5.538 ms
[buffer] columns=200:interleave=false:update-dispersion=0.9:update-fraction=0.5:update-method=map: FPS: 83 FrameTime: 12.126 ms
[buffer] columns=200:interleave=false:update-dispersion=0.9:update-fraction=0.5:update-method=subdata: FPS: 404 FrameTime: 2.479 ms
[buffer] columns=200:interleave=true:update-dispersion=0.9:update-fraction=0.5:update-method=map: FPS: 84 FrameTime: 11.985 ms
[ideas] speed=duration: FPS: 787 FrameTime: 1.271 ms
[jellyfish] <default>: FPS: 181 FrameTime: 5.528 ms
[terrain] <default>: FPS: 141 FrameTime: 7.126 ms
[shadow] <default>: FPS: 799 FrameTime: 1.252 ms
[refract] <default>: FPS: 166 FrameTime: 6.037 ms
[conditionals] fragment-steps=0:vertex-steps=0: FPS: 338 FrameTime: 2.964 ms
[conditionals] fragment-steps=5:vertex-steps=0: FPS: 1054 FrameTime: 0.949 ms
[conditionals] fragment-steps=0:vertex-steps=5: FPS: 999 FrameTime: 1.001 ms
[function] fragment-complexity=low:fragment-steps=5: FPS: 911 FrameTime: 1.098 ms
[function] fragment-complexity=medium:fragment-steps=5: FPS: 182 FrameTime: 5.503 ms
[loop] fragment-loop=false:fragment-steps=5:vertex-steps=5: FPS: 394 FrameTime: 2.544 ms
[loop] fragment-steps=5:fragment-uniform=false:vertex-steps=5: FPS: 686 FrameTime: 1.459 ms
[loop] fragment-steps=5:fragment-uniform=true:vertex-steps=5: FPS: 732 FrameTime: 1.367 ms
=======================================================
                                  glmark2 Score: 677
=======================================================
root@qemux86-64:~#

Result (Zink offscreen)

root@qemux86-64:~# MESA_LOADER_DRIVER_OVERRIDE=zink glmark2-es2-wayland
=======================================================
    glmark2 2023.01
=======================================================
    OpenGL Information
    GL_VENDOR:      Mesa
    GL_RENDERER:    zink Vulkan 1.3(Virtio-GPU Venus (Intel(R) HD Graphics 520 (SKL GT2)) (MESA_VENUS))
    GL_VERSION:     OpenGL ES 3.2 Mesa 24.0.7
    Surface Config: buf=32 r=8 g=8 b=8 a=8 depth=24 stencil=0 samples=0
    Surface Size:   800x600 windowed
=======================================================
[build] use-vbo=false: FPS: 97 FrameTime: 10.417 ms
[build] use-vbo=true: FPS: 1430 FrameTime: 0.699 ms
[texture] texture-filter=nearest: FPS: 1342 FrameTime: 0.745 ms
[texture] texture-filter=linear: FPS: 1330 FrameTime: 0.752 ms
[texture] texture-filter=mipmap: FPS: 1317 FrameTime: 0.760 ms
[shading] shading=gouraud: FPS: 1264 FrameTime: 0.792 ms
[shading] shading=blinn-phong-inf: FPS: 1262 FrameTime: 0.793 ms
[shading] shading=phong: FPS: 1252 FrameTime: 0.799 ms
[shading] shading=cel: FPS: 1251 FrameTime: 0.799 ms
[bump] bump-render=high-poly: FPS: 1029 FrameTime: 0.972 ms
[bump] bump-render=normals: FPS: 1371 FrameTime: 0.730 ms
[bump] bump-render=height: FPS: 1353 FrameTime: 0.740 ms
[effect2d] kernel=0,1,0;1,-4,1;0,1,0;: FPS: 934 FrameTime: 1.072 ms
[effect2d] kernel=1,1,1,1,1;1,1,1,1,1;1,1,1,1,1;: FPS: 819 FrameTime: 1.222 ms
[pulsar] light=false:quads=5:texture=false: FPS: 1016 FrameTime: 0.985 ms
[desktop] blur-radius=5:effect=blur:passes=1:separable=true:windows=4: FPS: 529 FrameTime: 1.891 ms
[desktop] effect=shadow:windows=4: FPS: 673 FrameTime: 1.487 ms
[buffer] columns=200:interleave=false:update-dispersion=0.9:update-fraction=0.5:update-method=map: FPS: 183 FrameTime: 5.472 ms
[buffer] columns=200:interleave=false:update-dispersion=0.9:update-fraction=0.5:update-method=subdata: FPS: 99 FrameTime: 10.160 ms
[buffer] columns=200:interleave=true:update-dispersion=0.9:update-fraction=0.5:update-method=map: FPS: 182 FrameTime: 5.517 ms
[ideas] speed=duration: FPS: 846 FrameTime: 1.182 ms
[jellyfish] <default>: FPS: 572 FrameTime: 1.750 ms
[terrain] <default>: FPS: 142 FrameTime: 7.044 ms
[shadow] <default>: FPS: 766 FrameTime: 1.305 ms
[refract] <default>: FPS: 176 FrameTime: 5.683 ms
[conditionals] fragment-steps=0:vertex-steps=0: FPS: 1066 FrameTime: 0.938 ms
[conditionals] fragment-steps=5:vertex-steps=0: FPS: 1062 FrameTime: 0.942 ms
[conditionals] fragment-steps=0:vertex-steps=5: FPS: 1058 FrameTime: 0.946 ms
[function] fragment-complexity=low:fragment-steps=5: FPS: 1055 FrameTime: 0.949 ms
[function] fragment-complexity=medium:fragment-steps=5: FPS: 1058 FrameTime: 0.946 ms
[loop] fragment-loop=false:fragment-steps=5:vertex-steps=5: FPS: 1052 FrameTime: 0.951 ms
[loop] fragment-steps=5:fragment-uniform=false:vertex-steps=5: FPS: 1058 FrameTime: 0.946 ms
[loop] fragment-steps=5:fragment-uniform=true:vertex-steps=5: FPS: 1048 FrameTime: 0.955 ms
=======================================================
                                  glmark2 Score: 898
=======================================================
root@qemux86-64:~#

Summary is here.


scene Ubuntu VirGL Zink
build use-vbo=false 2006 1193 97
build use-vbo=true 4201 1637 1430
texture texture-filter=nearest: 3674 1276 1342
texture texture-filter=linear: 3568 782 1330
texture texture-filter=mipmap: 3530 1216 1317
shading shading=gouraud: 2999 786 1264
shading shading=blinn-phong-inf: 2528 1311 1262
shading shading=phong: 2934 791 1252
shading shading=cel: 2802 532 1251
bump bump-render=high-poly: 1158 182 1029
bump bump-render=normals: 4161 1624 1371
bump bump-render=height: 3880 1353 1353
effect2d kernel=0,1,0;1,-4,1;0,1,0;: 3533 182 934
effect2d kernel=1,1,1,1,1;1,1,1,1,1;1,1,1,1,1;: 1245 182 819
pulsar light=false:quads=5:texture=false: 4180 1030 1016
desktop blur-radius=5:effect=blur:passes=1:separable=true:windows=4: 1141 180 529
desktop effect=shadow:windows=4: 1853 181 673
buffer
columns=200:interleave=false:update-dispersion=0.9:update-fraction=0.5:update-method=map:
467 83 183
buffer
columns=200:interleave=false:update-dispersion=0.9:update-fraction=0.5:update-method=subdata:
539 404 99
buffer
columns=200:interleave=true:update-dispersion=0.9:update-fraction=0.5:update-method=map:
492 84 182
ideas speed=duration: 2174 787 846
jellyfish default: 2946 181 572
terrain default: 175 141 142
shadow default: 1132 799 766
refract default: 282 166 176
conditionals fragment-steps=0:vertex-steps=0: 2445 338 1066
conditionals fragment-steps=5:vertex-steps=0: 2722 1054 1062
conditionals fragment-steps=0:vertex-steps=5: 2440 999 1058
function fragment-complexity=low:fragment-steps=5: 2544 911 1055
function fragment-complexity=medium:fragment-steps=5: 3001 182 1058
loop fragment-loop=false:fragment-steps=5:vertex-steps=5: 2598 394 1052
loop fragment-steps=5:fragment-uniform=false:vertex-steps=5: 2615 686 1058
loop fragment-steps=5:fragment-uniform=true:vertex-steps=5: 2827 732 1048
glmark2 Score 2386 677 898

Zink is superior to classic VirGL, based on this result. Expert can analyze this result further by considering characteristics of each scene. I will omit this in this article, though there are many interesting point even from my view.

Troubleshoot

Starting from VN_DEBUG environment variable is the best way.

VN_DEBUG=all vkcube

90% of VirtIO-GPU Venus trouble will be handled by this debugging feature. For example, if I missed venus=on toggle from -device virtio-vga-gl,blob=on,hostmem=4G,venus=on, VN_DEBUG will show

root@qemux86-64:~# VN_DEBUG=all vkcube
MESA-VIRTIO: debug: vn_env is as below:
        debug = 0x1ff
        perf = 0x0
        draw_cmd_batch_limit = 4294967295
        relax_base_sleep_us = 160
MESA-VIRTIO: debug: failed to connect to /tmp/.virgl_test: No such file or directory
MESA-VIRTIO: debug: using DRM device /dev/dri/renderD128
MESA-VIRTIO: debug: failed to get venus v0 capset: Invalid argument
MESA-[   13.593327] audit: type=1701 audit(1735660479.644:5): auid=4294967295 uid=0 gid=0 ses=4294967295 subj=kernel pid=285 comm="vkcube" exe="/usr/bin/vkcube" sig=6 res=1
VIRTIO: debug: vn_CreateInstance: VK_ERROR_INITIALIZATION_FAILED
vkcube: /usr/src/debug/vulkan-tools/1.3.290.0/cube/cube.c:3622: demo_init_vk: Assertion `!err' failed.
Aborted (core dumped)
root@qemux86-64:~#

Then I could realize that venus capset (capability set) is not notified from Qemu.

Conclusion & Remarks

This article explains how to enable VirtIO-GPU Venus on your Qemu machine. Remark is that, while I am trying venus, there are some hang, crashing guest machine. This solution seems to have some points to be improved. The mesa "24.0.7" which is used for this article is in real not the latest version, so that some of them will be fixed on up-to-date version of Mesa.

About benchmarking, I didn't fully expose precondition and provide just one trial result. It also contains intentional sample drop when I encounter some crash, hangs on VirtIO-GPU Venus side experiment. This is also important to notice. For this moment, Venus is not simply super solution compared to VirGL. VirGL is stable enough while Venus not. I believe just doing it on your laptop may answer every unknown point. Please see the real.

References