diff --git a/debian/control b/debian/control
index a7bdd339c3458805ae3849cf6b86131ef9fc6bc1..75d0b1adc8894a2a7f4e7ef5c3e707824ffd5b6d 100644
--- a/debian/control
+++ b/debian/control
@@ -14,6 +14,7 @@ Build-Depends:
  libglib2.0-dev,
  libjson-glib-dev (>= 1.0),
  libtheora0 <!nocheck>,
+ libva-dev,
  libvdpau-dev,
  libvulkan-dev,
  libx11-dev,
diff --git a/debian/gitlab-ci.yml b/debian/gitlab-ci.yml
index 57e38cf01788710ff47a91daee5eeb2e6c7e9479..b1220f45fa4b5ac60f1aa8c9e3bdeb9234843eb1 100644
--- a/debian/gitlab-ci.yml
+++ b/debian/gitlab-ci.yml
@@ -19,6 +19,7 @@ variables:
         libglib2.0-dev
         libjson-glib-dev
         libtheora-dev
+        libva-dev
         libvdpau-dev
         libvulkan-dev
         libx11-dev
diff --git a/helpers/check-va-api.c b/helpers/check-va-api.c
new file mode 100644
index 0000000000000000000000000000000000000000..172e1c98a6f09951d6362cd9aa4ea54b123ca47e
--- /dev/null
+++ b/helpers/check-va-api.c
@@ -0,0 +1,298 @@
+/*
+ * Functional test for VA-API
+ *
+ * Copyright © 2020 Collabora Ltd.
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include <assert.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+
+#include <va/va.h>
+#include <va/va_x11.h>
+
+enum
+{
+  OPTION_HELP = 1,
+  OPTION_VERSION,
+};
+
+struct option long_options[] =
+{
+  { "help", no_argument, NULL, OPTION_HELP },
+  { "version", no_argument, NULL, OPTION_VERSION },
+  { NULL, 0, NULL, 0 }
+};
+
+static void usage (int code) __attribute__((__noreturn__));
+
+/*
+ * Print usage information and exit with status @code.
+ */
+static void
+usage (int code)
+{
+  FILE *fp;
+
+  if (code == 0)
+    fp = stdout;
+  else
+    fp = stderr;
+
+  fprintf (fp, "Usage: %s [OPTIONS]\n",
+           program_invocation_short_name);
+  exit (code);
+}
+
+static bool
+_do_vaapi (const char *description,
+           VAStatus va_status)
+{
+  if (va_status != VA_STATUS_SUCCESS)
+    {
+      fprintf (stderr, "%s failed: %s (%d)\n", description, vaErrorStr (va_status), va_status);
+      return False;
+    }
+  return True;
+}
+
+int
+main (int argc,
+      char **argv)
+{
+
+#define do_vaapi_or_exit(expr) if (! _do_vaapi (#expr, expr)) goto out;
+
+  int opt;
+  int surfaces_count = 2;
+  int ret = 1;
+  int max_profiles;
+  int num_profiles;
+  int major_version;
+  int minor_version;
+  unsigned int width = 1280;
+  unsigned int height = 720;
+  Display *display = NULL;
+  VADisplay va_display = NULL;
+  VASurfaceAttrib attr;
+  VAImage img;
+  VASurfaceID *surfaces = NULL;
+  VAImageFormat image_format;
+  VAProfile *profiles = NULL;
+  VAConfigID config;
+  VAContextID context = VA_INVALID_ID;
+  VABufferID misc_buf_id = VA_INVALID_ID;
+  VABufferID pipeline_param_buf_id = VA_INVALID_ID;
+  VAEncMiscParameterBuffer *misc_param_buffer;
+  VAEncMiscParameterBufferQualityLevel *buffer_quality_level;
+  VAProcPipelineParameterBuffer pipeline_param_buf;
+  VARectangle input_region;
+  VARectangle output_region;
+
+  while ((opt = getopt_long (argc, argv, "", long_options, NULL)) != -1)
+    {
+      switch (opt)
+        {
+          case OPTION_HELP:
+            usage (0);
+            break;
+
+          case OPTION_VERSION:
+            /* Output version number as YAML for machine-readability,
+             * inspired by `ostree --version` and `docker version` */
+            printf (
+                "%s:\n"
+                " Package: steam-runtime-tools\n"
+                " Version: %s\n",
+                argv[0], VERSION);
+            return 0;
+
+          case '?':
+          default:
+            usage (1);
+            break;  /* not reached */
+        }
+    }
+
+  img.image_id = VA_INVALID_ID;
+
+  attr.type = VASurfaceAttribPixelFormat;
+  attr.flags = VA_SURFACE_ATTRIB_SETTABLE;
+  attr.value.type = VAGenericValueTypeInteger;
+  /* Arbitrarily use the 8-bit RGBA, assuming to be widely supported.
+   * In case the current system doesn't support it, vaCreateSurfaces will fail
+   * and this test will exit returning 1 */
+  attr.value.value.i = VA_FOURCC_RGBA;
+  image_format.fourcc = VA_FOURCC_RGBA;
+  image_format.byte_order = VA_LSB_FIRST;
+  image_format.bits_per_pixel = 32;
+
+  /* Use the whole input surface */
+  input_region.x = 0;
+  input_region.y = 0;
+  input_region.width = width;
+  input_region.height = height;
+  /* Crop the output a few pixels from every corner */
+  output_region.x = 10;
+  output_region.y = 20;
+  output_region.width = width - 30;
+  output_region.height = height - 30;
+
+  display = XOpenDisplay (NULL);
+  if (!display)
+    {
+      fprintf (stderr, "An error occurred trying to open a connection to the X server\n");
+      return 1;
+    }
+
+  va_display = vaGetDisplay (display);
+  if (!va_display)
+    {
+      fprintf (stderr, "An error occurred trying to get a suitable VADisplay for VA-API\n");
+      goto out;
+    }
+
+  do_vaapi_or_exit (vaInitialize (va_display, &major_version, &minor_version));
+
+  /* Test the ability to get the supported profiles and that they are not more than
+   * the maximum number from the implementation */
+  max_profiles = vaMaxNumProfiles (va_display);
+  if (max_profiles < 1)
+    {
+      fprintf (stderr,
+               "vaMaxNumProfiles failed: unexpected number of maximum profiles (%i)\n", max_profiles);
+      goto out;
+    }
+  profiles = calloc (max_profiles, sizeof (VAProfile));
+
+  if (profiles == NULL)
+    {
+      fprintf (stderr, "Out of memory\n");
+      goto out;
+    }
+
+  do_vaapi_or_exit (vaQueryConfigProfiles (va_display, profiles, &num_profiles));
+  if (num_profiles > max_profiles)
+    {
+      fprintf (stderr,
+               "vaQueryConfigProfiles failed: the number of profiles (%i) exceed the maximum (%i)\n",
+               num_profiles, max_profiles);
+      goto out;
+    }
+
+  surfaces = calloc (surfaces_count, sizeof (VASurfaceID));
+
+  if (surfaces == NULL)
+    {
+      fprintf (stderr, "Out of memory\n");
+      goto out;
+    }
+
+  /* Test the creation of two surfaces and an image */
+  do_vaapi_or_exit (vaCreateSurfaces (va_display, VA_RT_FORMAT_RGB32, width, height,
+                                      surfaces, surfaces_count, &attr, 1));
+  do_vaapi_or_exit (vaCreateImage (va_display, &image_format, width, height, &img));
+
+  /* Get an image from the first surface */
+  do_vaapi_or_exit (vaGetImage (va_display, surfaces[0], 0, 0, width, height, img.image_id));
+
+  /* Render the image back to the second surface */
+  do_vaapi_or_exit (vaPutImage (va_display, surfaces[1], img.image_id,
+                                0, 0, width, height,
+                                0, 0, width, height));
+
+  /* Wait for all operations to complete */
+  do_vaapi_or_exit (vaSyncSurface (va_display, surfaces[1]));
+
+
+  /* Assume VAProfileNone to be available. If it isn't vaCreateConfig will not return
+   * "VA_STATUS_SUCCESS" and this test will exit returning 1 */
+  do_vaapi_or_exit (vaCreateConfig (va_display, VAProfileNone, VAEntrypointVideoProc,
+                                    NULL, 0, &config));
+
+  do_vaapi_or_exit (vaCreateContext (va_display, config, width, height, 0, surfaces,
+                                     surfaces_count, &context));
+
+
+  /* Try to render a picture, tuning its encode quality */
+  do_vaapi_or_exit (vaCreateBuffer (va_display, context, VAEncMiscParameterBufferType,
+                                    sizeof (VAEncMiscParameterBuffer) + sizeof (VAEncMiscParameterBufferQualityLevel),
+                                    1, NULL, &misc_buf_id));
+  do_vaapi_or_exit (vaMapBuffer (va_display, misc_buf_id, (void **)&misc_param_buffer));
+  misc_param_buffer->type = VAEncMiscParameterTypeQualityLevel;
+  buffer_quality_level = (VAEncMiscParameterBufferQualityLevel *)misc_param_buffer->data;
+  /* 1 is always the highest possible quality level, we don't need to check VAConfigAttribEncQualityRange */
+  buffer_quality_level->quality_level = 1;
+  do_vaapi_or_exit (vaUnmapBuffer (va_display, misc_buf_id));
+  do_vaapi_or_exit (vaBeginPicture (va_display, context, surfaces[1]));
+  do_vaapi_or_exit (vaRenderPicture (va_display, context, &misc_buf_id, 1));
+  do_vaapi_or_exit (vaSyncSurface (va_display, surfaces[1]));
+
+
+  /* Try to render a picture from the first surface to the second, applying a crop to it */
+  memset (&pipeline_param_buf, 0, sizeof (pipeline_param_buf));
+  pipeline_param_buf.surface = surfaces[0];
+  pipeline_param_buf.surface_region = &input_region;
+  pipeline_param_buf.output_region = &output_region;
+  /* Set a green background */
+  pipeline_param_buf.output_background_color = 0xff00ff00;
+  pipeline_param_buf.output_color_standard = VAProcColorStandardNone;
+  do_vaapi_or_exit (vaCreateBuffer (va_display, context, VAProcPipelineParameterBufferType,
+                                    sizeof (pipeline_param_buf), 1, &pipeline_param_buf, &pipeline_param_buf_id));
+  do_vaapi_or_exit (vaBeginPicture (va_display, context, surfaces[1]));
+  do_vaapi_or_exit (vaRenderPicture (va_display, context, &pipeline_param_buf_id, 1));
+  do_vaapi_or_exit (vaEndPicture (va_display, context));
+  do_vaapi_or_exit (vaSyncSurface (va_display, surfaces[1]));
+
+  ret = 0;
+
+out:
+  if (va_display)
+    {
+      if (pipeline_param_buf_id != VA_INVALID_ID)
+        vaDestroyBuffer (va_display, pipeline_param_buf_id);
+      if (misc_buf_id != VA_INVALID_ID)
+        vaDestroyBuffer (va_display, misc_buf_id);
+      if (context != VA_INVALID_ID)
+        vaDestroyContext (va_display, context);
+      if (img.image_id != VA_INVALID_ID)
+        vaDestroyImage (va_display, img.image_id);
+      if (surfaces)
+        vaDestroySurfaces (va_display, surfaces, surfaces_count);
+
+      vaTerminate (va_display);
+    }
+  if (display)
+    XCloseDisplay (display);
+
+  free (profiles);
+  free (surfaces);
+
+  return ret;
+}
diff --git a/helpers/meson.build b/helpers/meson.build
index 06436e5b20d0acd2749f4bd025c879f552379c9e..ea906175cef8a5b7130c03f9030d26c0e1823000 100644
--- a/helpers/meson.build
+++ b/helpers/meson.build
@@ -54,6 +54,17 @@ executable(
   # a game with no special linking would be able to load a library.
 )
 
+executable(
+  multiarch + '-check-va-api',
+  'check-va-api.c',
+  dependencies : [libva, libva_x11, xlib],
+  include_directories : project_include_dirs,
+  install : true,
+  install_dir : pkglibexecdir,
+  # Deliberately no RPATH/RUNPATH here: we want to determine whether
+  # a game with no special linking would be able to use VA-API.
+)
+
 executable(
   multiarch + '-check-vdpau',
   'check-vdpau.c',
diff --git a/meson.build b/meson.build
index 24323a80cb65ced6785db92d5ea2ec0b709dc4e9..8338e753b087939ab89d0f542b20d69305776730 100644
--- a/meson.build
+++ b/meson.build
@@ -196,6 +196,14 @@ if not libelf.found()
   libelf = c_compiler.find_library('elf', required: true)
 endif
 
+libva = dependency(
+  'libva',
+)
+
+libva_x11 = dependency(
+  'libva-x11',
+)
+
 vdpau = dependency(
   'vdpau',
 )