diff --git a/debian/control b/debian/control
index b756aa547d5b6fa88b3acd6feb5d6e9fd3c5ae1f..a7bdd339c3458805ae3849cf6b86131ef9fc6bc1 100644
--- a/debian/control
+++ b/debian/control
@@ -14,6 +14,7 @@ Build-Depends:
  libglib2.0-dev,
  libjson-glib-dev (>= 1.0),
  libtheora0 <!nocheck>,
+ libvdpau-dev,
  libvulkan-dev,
  libx11-dev,
  libxcb1-dev,
diff --git a/debian/gitlab-ci.yml b/debian/gitlab-ci.yml
index f1bcb8c245dce75179b9e70b31773c49480b30f3..57e38cf01788710ff47a91daee5eeb2e6c7e9479 100644
--- a/debian/gitlab-ci.yml
+++ b/debian/gitlab-ci.yml
@@ -19,6 +19,7 @@ variables:
         libglib2.0-dev
         libjson-glib-dev
         libtheora-dev
+        libvdpau-dev
         libvulkan-dev
         libx11-dev
         libxcb1-dev
diff --git a/helpers/check-vdpau.c b/helpers/check-vdpau.c
new file mode 100644
index 0000000000000000000000000000000000000000..9480f943320603fb49e037f41f39b9125964b32c
--- /dev/null
+++ b/helpers/check-vdpau.c
@@ -0,0 +1,222 @@
+/*
+ * Functional test for VDPAU, loosely based on test code from libvdpau-va-git
+ *
+ * Copyright © 2013 Rinat Ibragimov
+ * 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 <errno.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+
+#include <vdpau/vdpau.h>
+#include <vdpau/vdpau_x11.h>
+
+static VdpGetProcAddress *vdp_get_proc_address;
+static VdpDeviceDestroy *vdp_device_destroy;
+static VdpGetErrorString *vdp_get_error_string;
+static VdpOutputSurfaceCreate *vdp_output_surface_create;
+static VdpOutputSurfaceGetBitsNative *vdp_output_surface_get_bits_native;
+static VdpOutputSurfacePutBitsNative *vdp_output_surface_put_bits_native;
+static VdpOutputSurfaceRenderOutputSurface *vdp_output_surface_render_output_surface;
+
+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 void
+_do_vdpau_or_exit (const char *description,
+                   VdpStatus status)
+{
+  if (status != VDP_STATUS_OK)
+    {
+      if (vdp_get_error_string != NULL)
+        fprintf (stderr, "%s failed: %s (%d)\n", description, vdp_get_error_string (status), status);
+      else
+        fprintf (stderr, "%s failed: %d\n", description, status);
+
+      if (getenv ("SRT_CHECK_VDPAU_CRASH") != NULL)
+        abort ();
+      else
+        exit (1);
+    }
+}
+
+#define do_vdpau_or_exit(expr) _do_vdpau_or_exit (#expr, expr)
+
+static void
+load_vdpau (VdpDevice dev)
+{
+#define GET_POINTER(fid, fn) do_vdpau_or_exit (vdp_get_proc_address (dev, fid, (void**)&fn))
+
+   GET_POINTER(VDP_FUNC_ID_DEVICE_DESTROY,                       vdp_device_destroy);
+   GET_POINTER(VDP_FUNC_ID_GET_ERROR_STRING,                     vdp_get_error_string);
+   GET_POINTER(VDP_FUNC_ID_OUTPUT_SURFACE_CREATE,                vdp_output_surface_create);
+   GET_POINTER(VDP_FUNC_ID_OUTPUT_SURFACE_GET_BITS_NATIVE,       vdp_output_surface_get_bits_native);
+   GET_POINTER(VDP_FUNC_ID_OUTPUT_SURFACE_PUT_BITS_NATIVE,       vdp_output_surface_put_bits_native);
+   GET_POINTER(VDP_FUNC_ID_OUTPUT_SURFACE_RENDER_OUTPUT_SURFACE, vdp_output_surface_render_output_surface);
+}
+
+int
+main (int argc,
+      char **argv)
+{
+  Display *display = NULL;
+  VdpDevice device;
+  VdpOutputSurface out_surface_1;
+  VdpOutputSurface out_surface_2;
+  int screen;
+  int opt;
+
+  /* Arbitrarily declare two 4x4 source images */
+  uint32_t black_box[] =
+  {
+    0xff000000, 0xff000000, 0xff000000, 0xff000000,
+    0xff000000, 0xff000000, 0xff000000, 0xff000000,
+    0xff000000, 0xff000000, 0xff000000, 0xff000000,
+    0xff000000, 0xff000000, 0xff000000, 0xff000000
+  };
+
+  uint32_t two_red_dots[] =
+  {
+    0xff000000, 0xff000000, 0xff000000, 0xff000000,
+    0xff000000, 0xffff0000, 0xff000000, 0xff000000,
+    0xff000000, 0xff000000, 0xff000000, 0xff000000,
+    0xff000000, 0xff000000, 0xff000000, 0xffff0000
+  };
+
+  const void * const source_data_1[] = {black_box};
+  const void * const source_data_2[] = {two_red_dots};
+  uint32_t source_pitches[] = { 4 * 4 };
+
+  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 */
+        }
+    }
+
+  display = XOpenDisplay (NULL);
+  if (display == NULL)
+    {
+      fprintf (stderr, "An error occurred trying to open a connection to the X server\n");
+      return 1;
+    }
+  screen = DefaultScreen (display);
+
+  do_vdpau_or_exit (vdp_device_create_x11 (display, screen, &device, &vdp_get_proc_address));
+
+  load_vdpau (device);
+
+  do_vdpau_or_exit (vdp_output_surface_create (device, VDP_RGBA_FORMAT_B8G8R8A8, 4, 4, &out_surface_1));
+  do_vdpau_or_exit (vdp_output_surface_create (device, VDP_RGBA_FORMAT_B8G8R8A8, 4, 4, &out_surface_2));
+
+  /* Upload the first black_box surface and then the two_red_dots one */
+  do_vdpau_or_exit (vdp_output_surface_put_bits_native (out_surface_1, source_data_1, source_pitches, NULL));
+  do_vdpau_or_exit (vdp_output_surface_put_bits_native (out_surface_2, source_data_2, source_pitches, NULL));
+
+  VdpOutputSurfaceRenderBlendState blend_state = {
+    .struct_version =                 VDP_OUTPUT_SURFACE_RENDER_BLEND_STATE_VERSION,
+    .blend_factor_source_color =      VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_ONE,
+    .blend_factor_source_alpha =      VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_ONE,
+    .blend_factor_destination_color = VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_ZERO,
+    .blend_factor_destination_alpha = VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_ZERO,
+    .blend_equation_color =           VDP_OUTPUT_SURFACE_RENDER_BLEND_EQUATION_ADD,
+    .blend_equation_alpha =           VDP_OUTPUT_SURFACE_RENDER_BLEND_EQUATION_ADD,
+    .blend_constant =                 {0, 0, 0, 0}
+  };
+
+  /* Do the rendering of the two surfaces */
+  do_vdpau_or_exit (vdp_output_surface_render_output_surface (out_surface_1, NULL, out_surface_2, NULL,
+                                                              NULL, &blend_state,
+                                                              VDP_OUTPUT_SURFACE_RENDER_ROTATE_0));
+
+  /* Retrieve the data back from the surface */
+  uint32_t receive_buf[16];
+  void * const dest_data[] = {receive_buf};
+  do_vdpau_or_exit (vdp_output_surface_get_bits_native (out_surface_1, NULL, dest_data, source_pitches));
+
+  /* We expect that the retrieved buffer is equal to two_red_dots because previously
+   * we blended the two surfaces together */
+  if (memcmp (receive_buf, two_red_dots, 4*4*4) != 0)
+    {
+      fprintf (stderr, "The rendered surface is not what we expected!\n");
+      return 1;
+    }
+
+  vdp_device_destroy (device);
+
+  return 0;
+}
diff --git a/helpers/meson.build b/helpers/meson.build
index c9fd9f63f92b933ea06ad3470e7eab8eff690351..8f54e62e14644cc4b43137ae5298e2028caeb785 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-vdpau',
+  'check-vdpau.c',
+  dependencies : [xlib, vdpau],
+  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 VDPAU.
+)
+
 executable(
   multiarch + '-check-vulkan',
   'check-vulkan.cpp',
diff --git a/meson.build b/meson.build
index fc853a03079ff3b1a15aec1b5f7b547464c5bc29..270e376df388bdcd2a643fea198e6a9c36a0854f 100644
--- a/meson.build
+++ b/meson.build
@@ -196,6 +196,10 @@ if not libelf.found()
   libelf = c_compiler.find_library('elf', required: true)
 endif
 
+vdpau = dependency(
+  'vdpau',
+)
+
 vulkan = dependency(
   'vulkan',
 )