The graphics-core20 Snap interface

The problem with snapping userspace graphics drivers

Over the past few years developing graphical snaps to run on Ubuntu Core we’ve encountered various issues. One of these is the need to make userspace graphics drivers available inside snap confinement. There’s a partial solution in snapd that, on desktop systems can make these available from the host system but among the cases where this doesn’t work is on Ubuntu Core.

Until recently, we have been including the mesa drivers inside the snap as these have the least licensing issues, and cover a wide range of hardware. But it is not an ideal solution:

  • Both server (e.g. mir-kiosk) and client (e.g. mir-kiosk-kodi) snaps contain these drivers;
  • Security updates to mesa require all the server and client snaps to be rebuilt and distributed; and
  • There’s no way to introduce other drivers, even if Mir supports them.

This led to some frustration as there are a number of graphics stacks that Mir supports that are not supported by mir-kiosk.

Introducing graphics-core20

Fundamentally, userspace graphics drivers are just files. And snapd has a way to share files between snaps: content interfaces. Care is needed to ensure that the binaries are ABI compatible, but as the binaries come from a stable archive (20.04LTS) that isn’t a problem here.

The Mir team are now maintaining a mesa-core20 content snap with the current mesa drivers from the archive:

  • lib contains the mesa shared libraries (add to LD_LIBRARY_PATH)
  • drv contains the mesa drivers (set LIBGL_DRIVERS_PATH/LIBVA_DRIVERS_PATH to this)
  • glvnd/egl_vendor.d contains the mesa ICD (set __EGL_VENDOR_LIBRARY_DIRS to this)
  • libdrm contains mesa configuration for driver support (layout to /usr/share/libdrm)
  • drirc.d contains mesa app-specific workarounds (layout to /usr/share/drirc.d)
  • etc/mir-quirks contains any Mir configuration for driver support (none for mesa)

We’ve also built and tested a number of snaps to prove this approach. As well as setting the above environment variables, they need the following plug declaration:

  graphics-core20:
    interface: content
    target: $SNAP/graphics
    default-provider: mesa-core20

There’s also two mesa specific directories that needs to be bind mounted:

layout:
  /usr/share/libdrm:  # Needed by mesa-core20 on AMD GPUs
    bind: $SNAP/graphics/libdrm
  /usr/share/drirc.d:  # Used by mesa-core20 for app specific workarounds
    bind: $SNAP/graphics/drirc.d

Finally, it is desirable to avoid shipping anything from mesa that gets pulled into the snap with a “cleanup” part similar to this:

  cleanup:
    after: [kodi, mir-kiosk-snap-launch]
    plugin: nil
    build-snaps: [ mesa-core20 ]
    override-prime: |
      set -eux
      cd /snap/mesa-core20/current/egl/lib
      find . -type f,l -exec rm -f $SNAPCRAFT_PRIME/usr/lib/${SNAPCRAFT_ARCH_TRIPLET}/{} \;
      rm -fr "$SNAPCRAFT_PRIME/usr/lib/${SNAPCRAFT_ARCH_TRIPLET}/dri"
      for CRUFT in bug drirc.d glvnd libdrm lintian man; do
        rm -rf "$SNAPCRAFT_PRIME/usr/share/$CRUFT"
      done

(This example is taken from a mir-kiosk-kodi PR).

The graphics-core20 libraries

As we’ve gained experience with this interface we’ve added components to the point we need to document the libraries that consumers can depend upon in .../lib and which providers must supply.
(As opposed to additional libraries that are here to support these).

Actual graphics interface

  • libEGL.so.1
  • libva.so.2
  • libvulkan.so.1
  • libGLESv2.so.2

LIbraries we need for gbm/kms

  • Libdrm.so.2
  • libgbm.so.1

Wayland support

  • libwayland-client.so.0
  • libwayland-server.so.0

X support (optional)

  • libX11-xcb.so.1
  • libXau.so.6
  • libxcb-dri2.so.0
  • libxcb-dri3.so.0
  • libxcb-present.so.0
  • libxcb.so.1
  • libxcb-sync.so.1
  • libxcb-xfixes.so.0
  • libXdmcp.so.6
  • libxshmfence.so.1

The result

With this approach:

  • There’s only one copy of the drivers in one snap;
  • Mesa updates only require one snap to be rebuilt and distributed; and,
  • It is possible to replace mesa with other (compatible) drivers

Now and the future

We have been testing this approach with a new Mir based server [Ubuntu Frame] both with mesa-core20 and (experimentally, for now) other drivers providing KMS, libgbm and an EGL supporting EGL_WL_bind_wayland_display.

Ubuntu Frame has yet to be released to stable, but we are already confident that we can use this approach to enable gbm-kms support for drivers other than mesa and, with additional work, expand it to support some of the other Mir “graphics platforms”.

Longer term, we’re hopeful that the experience gained will feed into improvements in graphics support in snapd that make this unnecessary.

6 Likes

libGLU: A note

Some graphics applications use libGLU.so.1 which is supplied by the libglu1-mesa apt package. We considered adding this to mesa-core20 and to the requirements for graphics-core20.

We decided not to add it as, although it is provided by mesa, it doesn’t appear to be driver specific and would impose additional requirements on implementing alternative implementations of the interface.

Application snaps that need libGLU should continuing staging libglu1-mesa themselves.

Motivating example: https://github.com/MirServer/mir-kiosk-scummvm/pull/9

AMD GPUs: A note

/usr/share/libdrm/amdgpu.ids is a file referenced via a hard coded path in libdrm_amdgpu.so.1.0.0 which is included in the mesa-core20 snap providing this interface. Failing to find this file causes problems on AMD GPUs. (See, https://github.com/snapcrafters/scummvm/issues/31 for this problem in a different context.)

The file is accessed in Mesa’s libdrm (amdgpu/amdgpu_asic_id.c), where there’s no customization point (e.g. environment variable) enabling the path to be updated point a path provided by graphics-core20.

When /usr/share/libdrm/amdgpu.ids is included in a confined snap, it can be bind mounted using “layout” (see https://github.com/snapcrafters/scummvm/pull/32), but that gets messy with the graphics-core20 interface supplying either Mesa, or another provider that may not include this file.


After some experimentation this didn’t prove as messy as anticipated. Layouts don’t complain if the directory doesn’t exist. So, I’m proposing a solution: expose the directory via graphics-core20 and use layouts to put it where amdgpu_asic_id.c expects it:

https://github.com/MirServer/mesa-core20/pull/11

https://github.com/MirServer/mir-kiosk-kodi/pull/24