diff --git a/bin/system-info.c b/bin/system-info.c
index 69667a6de7c4869c96ab26503fcac226bb01f2fc..83b0059f63688149051b2986cda575278f9a09b3 100644
--- a/bin/system-info.c
+++ b/bin/system-info.c
@@ -386,6 +386,53 @@ print_graphics_details(JsonBuilder *builder,
             }
 
         }
+
+      if (rendering_interface == SRT_RENDERING_INTERFACE_VULKAN)
+        {
+          g_autoptr(SrtObjectList) devices = srt_graphics_get_devices (g->data);
+          const GList *iter;
+          json_builder_set_member_name (builder, "devices");
+          json_builder_begin_array (builder);
+            {
+              for (iter = devices; iter != NULL; iter = iter->next)
+                {
+                  json_builder_begin_object (builder);
+                  json_builder_set_member_name (builder, "name");
+                  json_builder_add_string_value (builder,
+                                                 srt_graphics_device_get_name (iter->data));
+                  json_builder_set_member_name (builder, "api-version");
+                  json_builder_add_string_value (builder,
+                                                 srt_graphics_device_get_api_version (iter->data));
+                  json_builder_set_member_name (builder, "driver-version");
+                  json_builder_add_string_value (builder,
+                                                 srt_graphics_device_get_driver_version (iter->data));
+                  json_builder_set_member_name (builder, "vendor-id");
+                  json_builder_add_string_value (builder,
+                                                 srt_graphics_device_get_vendor_id (iter->data));
+                  json_builder_set_member_name (builder, "device-id");
+                  json_builder_add_string_value (builder,
+                                                 srt_graphics_device_get_device_id (iter->data));
+                  json_builder_set_member_name (builder, "type");
+                  jsonify_enum (builder, SRT_TYPE_VK_PHYSICAL_DEVICE_TYPE,
+                                srt_graphics_device_get_device_type (iter->data));
+
+                  messages = srt_graphics_device_get_messages (iter->data);
+                  if (messages != NULL)
+                    _srt_json_builder_add_array_of_lines (builder, "messages", messages);
+
+                  if (srt_graphics_device_get_issues (iter->data) != SRT_GRAPHICS_ISSUES_NONE)
+                    {
+                      json_builder_set_member_name (builder, "issues");
+                      json_builder_begin_array (builder);
+                      jsonify_graphics_issues (builder, srt_graphics_device_get_issues (iter->data));
+                      json_builder_end_array (builder);
+                    }
+                  json_builder_end_object (builder);
+                }
+            }
+          json_builder_end_array (builder);
+        }
+
       json_builder_end_object (builder); // End object for parameters
       g_free (parameters);
     }
diff --git a/bin/system-info.md b/bin/system-info.md
index 88bdeea84726361f4d7fce62bbe3798c3928ab84..117fd6dca29df2e696e0227831410682b30b37d9 100644
--- a/bin/system-info.md
+++ b/bin/system-info.md
@@ -504,6 +504,46 @@ keys:
         **messages**
         :   Human-readable array of diagnostic messages.
 
+        **devices**
+        :   An array of objects describing the available graphics devices.
+            It is currently printed only when the rendering interface is
+            **vulkan**. Every object has the following keys:
+
+            **name**
+            :   The name of this graphics device, or **null** if it could
+                not be determined.
+
+            **api-version**
+            :   The API version used by this graphics device, or **null**
+                if it could not be determined.
+
+            **driver-version**
+            :   The driver version used by this graphics device, or **null**
+                if it could not be determined.
+
+            **vendor-id**
+            :   The vendor ID of this graphics device, or **null** if it
+                could not be determined.
+
+            **device-id**
+            :   The device ID of this graphics device, or **null** if it
+                could not be determined.
+
+            **type**
+            :   The type of this graphics device, typically one of
+                `integrated-gpu`, `discrete-gpu`, `virtual-gpu`,
+                or `cpu` (denoting software rendering such as llvmpipe).
+
+
+            **messages**
+            :   Human-readable array of diagnostic messages.
+
+            **issues**
+            :   Problems with this graphics device, represented as an array
+                of strings. The array is empty if no problems were detected.
+                The possible values are the same as the ones in the **issues**
+                of **graphics-details**.
+
     **dri_drivers**
     :   An array of objects describing the Mesa DRI driver modules that have been
         found. These drivers are used by Mesa and by older versions of Xorg.
diff --git a/helpers/check-vulkan.c b/helpers/check-vulkan.c
index e8303509c255e2b3c02885625ba19f25a0e2b70a..598148abbcbc935c63bea00a10e258082c716bea 100644
--- a/helpers/check-vulkan.c
+++ b/helpers/check-vulkan.c
@@ -1372,16 +1372,14 @@ int main (int argc,
     goto out;
 
   for (i = 0; i < physical_device_count; i++)
-    print_physical_device_info (physical_devices[i], stderr);
+    print_physical_device_info (physical_devices[i], original_stdout);
 
   for (i = 0; i < physical_device_count; i++)
     {
       result = draw_test_triangle (vk_instance, physical_devices[i], error);
-      print_draw_test_result (i, result, local_error, stderr);
-
-      if (local_error != NULL)
-        g_printerr ("%s", local_error->message);
+      print_draw_test_result (i, result, local_error, original_stdout);
 
+      /* The eventual error has already been included in the drawing test JSON */
       g_clear_error (error);
 
       /* Return exit success if we are able to draw with at least one device */
diff --git a/steam-runtime-tools/graphics-internal.h b/steam-runtime-tools/graphics-internal.h
index f8931abde06d096ef6b3ba3b3a2695d398008acb..55300797b890ece83d9b5def27fede5db9a9a76c 100644
--- a/steam-runtime-tools/graphics-internal.h
+++ b/steam-runtime-tools/graphics-internal.h
@@ -1,6 +1,6 @@
 /*< internal_header >*/
 /*
- * Copyright © 2019 Collabora Ltd.
+ * Copyright © 2019-2021 Collabora Ltd.
  *
  * SPDX-License-Identifier: MIT
  *
@@ -35,8 +35,9 @@
  * _srt_graphics_new:
  * @multiarch_tuple: A multiarch tuple like %SRT_ABI_I386,
  *  representing an ABI
- * @window_system: The window system to check,
- * @rendering_interface: The renderint interface to check,
+ * @window_system: The window system to check
+ * @rendering_interface: The rendering interface to check
+ * @graphics_devices: Array of #SrtGraphicsDevice
  * @issues: Problems found when checking @multiarch_tuple with
  *  the given @winsys and @renderer.
  * @messages: Any debug messages found when checking graphics.
@@ -54,11 +55,37 @@ static inline SrtGraphics *_srt_graphics_new (const char *multiarch_tuple,
                                               SrtGraphicsLibraryVendor library_vendor,
                                               const gchar *renderer_string,
                                               const gchar *version_string,
+                                              GPtrArray *graphics_devices,
                                               SrtGraphicsIssues issues,
                                               const gchar *messages,
                                               int exit_status,
                                               int termination_signal);
 
+/*
+ * _srt_graphics_device_new:
+ * @name: Device name, also referred as renderer name
+ * @api_version: API version used by this device
+ * @driver_version: Driver version used by this device
+ * @vendor_id: Device vendor ID
+ * @device_id: Device ID
+ * @type: The device type
+ * @issues: Problems found when checking the drawing capabilities of
+ *  the device
+ *
+ * Inline convenience function to create a new SrtGraphicsDevice.
+ * This is not part of the public API.
+ *
+ * Returns: (transfer full): A new #SrtGraphicsDevice
+ */
+static inline SrtGraphicsDevice *_srt_graphics_device_new (const gchar *name,
+                                                           const gchar *api_version,
+                                                           const gchar *driver_version,
+                                                           const gchar *vendor_id,
+                                                           const gchar *device_id,
+                                                           SrtVkPhysicalDeviceType type,
+                                                           SrtGraphicsIssues issues);
+
+
 /*
  * _srt_graphics_hash_key:
  * @window_system: The window system,
@@ -89,6 +116,7 @@ _srt_graphics_new (const char *multiarch_tuple,
                    SrtGraphicsLibraryVendor library_vendor,
                    const gchar *renderer_string,
                    const gchar *version_string,
+                   GPtrArray *graphics_devices,
                    SrtGraphicsIssues issues,
                    const gchar *messages,
                    int exit_status,
@@ -103,12 +131,33 @@ _srt_graphics_new (const char *multiarch_tuple,
                        "rendering-interface", rendering_interface,
                        "renderer-string", renderer_string,
                        "version-string", version_string,
+                       "graphics-devices", graphics_devices,
                        "messages", messages,
                        "exit-status", exit_status,
                        "terminating-signal", terminating_signal,
                        NULL);
 }
 
+static inline SrtGraphicsDevice *
+_srt_graphics_device_new (const gchar *name,
+                          const gchar *api_version,
+                          const gchar *driver_version,
+                          const gchar *vendor_id,
+                          const gchar *device_id,
+                          SrtVkPhysicalDeviceType type,
+                          SrtGraphicsIssues issues)
+{
+  return g_object_new (SRT_TYPE_GRAPHICS_DEVICE,
+                       "name", name,
+                       "api-version", api_version,
+                       "driver-version", driver_version,
+                       "vendor-id", vendor_id,
+                       "device-id", device_id,
+                       "type", type,
+                       "issues", issues,
+                       NULL);
+}
+
 static inline int _srt_graphics_hash_key(SrtWindowSystem window_system,
                                          SrtRenderingInterface rendering_interface)
 {
diff --git a/steam-runtime-tools/graphics-test-defines.h b/steam-runtime-tools/graphics-test-defines.h
index a53de0fd34e269a02f3f1d67d3e66cf903b12843..a31e73a0bdeb9250352b248799f906727585b3aa 100644
--- a/steam-runtime-tools/graphics-test-defines.h
+++ b/steam-runtime-tools/graphics-test-defines.h
@@ -1,6 +1,6 @@
 /*< internal_header >*/
 /*
- * Copyright © 2019 Collabora Ltd.
+ * Copyright © 2019-2021 Collabora Ltd.
  *
  * SPDX-License-Identifier: MIT
  *
@@ -27,12 +27,35 @@
 #pragma once
 
 // Test strings for use in mock and graphics test
-#define SRT_TEST_GOOD_GRAPHICS_RENDERER "Mesa DRI Intel(R) Haswell Desktop "
+#define SRT_TEST_GOOD_GRAPHICS_RENDERER "AMD RADV NAVI10 (ACO)"
+#define SRT_TEST_GOOD_GRAPHICS_API_VERSION "1.2.145"
+#define SRT_TEST_GOOD_GRAPHICS_DRIVER_VERSION "20.3.3"
+#define SRT_TEST_GOOD_GRAPHICS_VENDOR_ID "0x1002"
+#define SRT_TEST_GOOD_GRAPHICS_DEVICE_ID "0x731f"
+
 #define SRT_TEST_SOFTWARE_GRAPHICS_RENDERER "llvmpipe (LLVM 8.0, 256 bits)"
+#define SRT_TEST_SOFTWARE_GRAPHICS_API_VERSION "1.0.2"
+#define SRT_TEST_SOFTWARE_GRAPHICS_DRIVER_VERSION "0.0.1"
+#define SRT_TEST_SOFTWARE_GRAPHICS_VENDOR_ID "0x10005"
+#define SRT_TEST_SOFTWARE_GRAPHICS_DEVICE_ID "0"
+
 #define SRT_TEST_GOOD_GRAPHICS_VERSION "3.0 Mesa 19.1.3"
 #define SRT_TEST_SOFTWARE_GRAPHICS_VERSION "3.1 Mesa 19.1.3"
-#define SRT_TEST_GOOD_VULKAN_DRIVER_VERSION "79695877"
-#define SRT_TEST_GOOD_VULKAN_VERSION "1.1.102 (device 8086:0412) (driver 19.1.5)"
+#define SRT_TEST_GOOD_VULKAN_MESSAGES "WARNING: lavapipe is not a conformant vulkan implementation, testing use only."
+#define SRT_TEST_GOOD_VULKAN_VERSION \
+SRT_TEST_GOOD_GRAPHICS_API_VERSION \
+" (device " \
+SRT_TEST_GOOD_GRAPHICS_VENDOR_ID \
+":" \
+SRT_TEST_GOOD_GRAPHICS_DEVICE_ID \
+") (driver " \
+SRT_TEST_GOOD_GRAPHICS_DRIVER_VERSION \
+")"
+
+#define SRT_TEST_MIXED_VULKAN_MESSAGES_1 "Failed to open file “./_build/helpers/shaders/vert.spv”: No such file or directory"
+#define SRT_TEST_MIXED_VULKAN_MESSAGES_2 "vkWaitForFences (dev_props->device, 1, &dev_props->in_flight_fences[dev_props->current_frame], VK_TRUE, 1) failed: TIMEOUT (2)"
+#define SRT_TEST_BAD_VULKAN_MESSAGES "failed to create window surface!\n"
+
 #define SRT_TEST_GOOD_VDPAU_RENDERER "G3DVL VDPAU Driver Shared Library version 1.0\n"
 #define SRT_TEST_BAD_VDPAU_MESSAGES "Failed to open VDPAU backend libvdpau_nvidia.so: cannot open shared object file: No such file or directory\n\
 vdp_device_create_x11 (display, screen, &device, &vdp_get_proc_address) failed: 1\n"
diff --git a/steam-runtime-tools/graphics.c b/steam-runtime-tools/graphics.c
index 20d1a99937dbfcdfbbcf4efa700d9e94609609e3..075e6112732e8bd4d1bd34a6d07c7382e3f3be62 100644
--- a/steam-runtime-tools/graphics.c
+++ b/steam-runtime-tools/graphics.c
@@ -1,5 +1,5 @@
 /*
- * Copyright © 2019-2020 Collabora Ltd.
+ * Copyright © 2019-2021 Collabora Ltd.
  *
  * SPDX-License-Identifier: MIT
  *
@@ -61,6 +61,11 @@
  * This is a reference-counted object: use g_object_ref() and
  * g_object_unref() to manage its lifecycle.
  *
+ * #SrtGraphicsDevice is an opaque object representing a single, physical or
+ * virtual, GPU.
+ * This is a reference-counted object: use g_object_ref() and
+ * g_object_unref() to manage its lifecycle.
+ *
  * #SrtEglIcd is an opaque object representing the metadata describing
  * an EGL ICD.
  * This is a reference-counted object: use g_object_ref() and
@@ -72,6 +77,379 @@
  * g_object_unref() to manage its lifecycle.
  */
 
+struct _SrtGraphicsDevice
+{
+  /*< private >*/
+  GObject parent;
+  SrtGraphicsIssues issues;
+  gchar *name;
+  gchar *api_version;
+  gchar *driver_version;
+  gchar *vendor_id;
+  gchar *device_id;
+  gchar *messages;
+  SrtVkPhysicalDeviceType type;
+};
+
+struct _SrtGraphicsDeviceClass
+{
+  /*< private >*/
+  GObjectClass parent_class;
+};
+
+enum
+{
+  GRAPHICS_DEVICE_PROP_0,
+  GRAPHICS_DEVICE_PROP_ISSUES,
+  GRAPHICS_DEVICE_PROP_NAME,
+  GRAPHICS_DEVICE_PROP_API_VERSION,
+  GRAPHICS_DEVICE_PROP_DRIVER_VERSION,
+  GRAPHICS_DEVICE_PROP_VENDOR_ID,
+  GRAPHICS_DEVICE_PROP_DEVICE_ID,
+  GRAPHICS_DEVICE_PROP_TYPE,
+  N_GRAPHICS_DEVICE_PROPERTIES,
+};
+
+G_DEFINE_TYPE (SrtGraphicsDevice, srt_graphics_device, G_TYPE_OBJECT)
+
+static void
+srt_graphics_device_init (SrtGraphicsDevice *self)
+{
+}
+
+static void
+srt_graphics_device_get_property (GObject *object,
+                                  guint prop_id,
+                                  GValue *value,
+                                  GParamSpec *pspec)
+{
+  SrtGraphicsDevice *self = SRT_GRAPHICS_DEVICE (object);
+
+  switch (prop_id)
+    {
+      case GRAPHICS_DEVICE_PROP_ISSUES:
+        g_value_set_flags (value, self->issues);
+        break;
+
+      case GRAPHICS_DEVICE_PROP_NAME:
+        g_value_set_string (value, self->name);
+        break;
+
+      case GRAPHICS_DEVICE_PROP_API_VERSION:
+        g_value_set_string (value, self->api_version);
+        break;
+
+      case GRAPHICS_DEVICE_PROP_DRIVER_VERSION:
+        g_value_set_string (value, self->driver_version);
+        break;
+
+      case GRAPHICS_DEVICE_PROP_VENDOR_ID:
+        g_value_set_string (value, self->vendor_id);
+        break;
+
+      case GRAPHICS_DEVICE_PROP_DEVICE_ID:
+        g_value_set_string (value, self->device_id);
+        break;
+
+      case GRAPHICS_DEVICE_PROP_TYPE:
+        g_value_set_enum (value, self->type);
+        break;
+
+      default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+srt_graphics_device_set_property (GObject *object,
+                                  guint prop_id,
+                                  const GValue *value,
+                                  GParamSpec *pspec)
+{
+  SrtGraphicsDevice *self = SRT_GRAPHICS_DEVICE (object);
+
+  switch (prop_id)
+    {
+      case GRAPHICS_DEVICE_PROP_ISSUES:
+        /* Construct-only */
+        g_return_if_fail (self->issues == 0);
+        self->issues = g_value_get_flags (value);
+        break;
+
+      case GRAPHICS_DEVICE_PROP_NAME:
+        /* Construct only */
+        g_return_if_fail (self->name == NULL);
+        self->name = g_value_dup_string (value);
+        break;
+
+      case GRAPHICS_DEVICE_PROP_API_VERSION:
+        /* Construct only */
+        g_return_if_fail (self->api_version == NULL);
+        self->api_version = g_value_dup_string (value);
+        break;
+
+      case GRAPHICS_DEVICE_PROP_DRIVER_VERSION:
+        /* Construct only */
+        g_return_if_fail (self->driver_version == NULL);
+        self->driver_version = g_value_dup_string (value);
+        break;
+
+      case GRAPHICS_DEVICE_PROP_VENDOR_ID:
+        /* Construct only */
+        g_return_if_fail (self->vendor_id == NULL);
+        self->vendor_id = g_value_dup_string (value);
+        break;
+
+      case GRAPHICS_DEVICE_PROP_DEVICE_ID:
+        /* Construct only */
+        g_return_if_fail (self->device_id == NULL);
+        self->device_id = g_value_dup_string (value);
+        break;
+
+      case GRAPHICS_DEVICE_PROP_TYPE:
+        /* Construct-only */
+        g_return_if_fail (self->type == 0);
+        self->type = g_value_get_enum (value);
+        break;
+
+      default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+srt_graphics_device_finalize (GObject *object)
+{
+  SrtGraphicsDevice *self = SRT_GRAPHICS_DEVICE (object);
+
+  g_free (self->name);
+  g_free (self->api_version);
+  g_free (self->driver_version);
+  g_free (self->vendor_id);
+  g_free (self->device_id);
+  g_free (self->messages);
+
+  G_OBJECT_CLASS (srt_graphics_device_parent_class)->finalize (object);
+}
+
+static GParamSpec *graphics_device_properties[N_GRAPHICS_DEVICE_PROPERTIES] = { NULL };
+
+static void
+srt_graphics_device_class_init (SrtGraphicsDeviceClass *cls)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (cls);
+
+  object_class->get_property = srt_graphics_device_get_property;
+  object_class->set_property = srt_graphics_device_set_property;
+  object_class->finalize = srt_graphics_device_finalize;
+
+  graphics_device_properties[GRAPHICS_DEVICE_PROP_ISSUES] =
+    g_param_spec_flags ("issues", "Issues", "Problems with the graphics device card",
+                        SRT_TYPE_GRAPHICS_ISSUES, SRT_GRAPHICS_ISSUES_NONE,
+                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+                        G_PARAM_STATIC_STRINGS);
+
+  graphics_device_properties[GRAPHICS_DEVICE_PROP_NAME] =
+    g_param_spec_string ("name", "Device name",
+                         "Which name the device has.",
+                         NULL,
+                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+                         G_PARAM_STATIC_STRINGS);
+
+  graphics_device_properties[GRAPHICS_DEVICE_PROP_API_VERSION] =
+    g_param_spec_string ("api-version", "API version",
+                         "Which API version is in use.",
+                         NULL,
+                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+                         G_PARAM_STATIC_STRINGS);
+
+  graphics_device_properties[GRAPHICS_DEVICE_PROP_DRIVER_VERSION] =
+    g_param_spec_string ("driver-version", "Driver version",
+                         "Which driver version is in use.",
+                         NULL,
+                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+                         G_PARAM_STATIC_STRINGS);
+
+  graphics_device_properties[GRAPHICS_DEVICE_PROP_VENDOR_ID] =
+    g_param_spec_string ("vendor-id", "Vendor ID", "The vendor ID of the device.",
+                         NULL,
+                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+                         G_PARAM_STATIC_STRINGS);
+
+  graphics_device_properties[GRAPHICS_DEVICE_PROP_DEVICE_ID] =
+    g_param_spec_string ("device-id", "Device ID", "The device ID of the device.",
+                         NULL,
+                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+                         G_PARAM_STATIC_STRINGS);
+
+  graphics_device_properties[GRAPHICS_DEVICE_PROP_TYPE] =
+    g_param_spec_enum ("type", "Type", "Which type the device is.",
+                       SRT_TYPE_VK_PHYSICAL_DEVICE_TYPE, SRT_VK_PHYSICAL_DEVICE_TYPE_OTHER,
+                       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+                       G_PARAM_STATIC_STRINGS);
+
+  g_object_class_install_properties (object_class, N_GRAPHICS_DEVICE_PROPERTIES,
+                                     graphics_device_properties);
+}
+
+/**
+ * srt_graphics_device_get_issues:
+ * @self: a SrtGraphicsDevice object
+ *
+ * Return the problems found when testing @self.
+ *
+ * Returns: A bitfield containing problems, or %SRT_GRAPHICS_ISSUES_NONE
+ *  if no problems were found
+ */
+SrtGraphicsIssues
+srt_graphics_device_get_issues (SrtGraphicsDevice *self)
+{
+  g_return_val_if_fail (SRT_IS_GRAPHICS_DEVICE (self), SRT_GRAPHICS_ISSUES_UNKNOWN);
+  return self->issues;
+}
+
+/**
+ * srt_graphics_device_get_name:
+ * @self: a SrtGraphicsDevice object
+ *
+ * Return the device name of @self, or %NULL if it's not known.
+ *
+ * Returns (nullable): A string indicating the device name.
+ */
+const char *
+srt_graphics_device_get_name (SrtGraphicsDevice *self)
+{
+  g_return_val_if_fail (SRT_IS_GRAPHICS_DEVICE (self), NULL);
+  return self->name;
+}
+
+/**
+ * srt_graphics_device_get_api_version:
+ * @self: a SrtGraphicsDevice object
+ *
+ * Return the API version used by @self, or %NULL if it's not known.
+ *
+ * Returns (nullable): A string indicating the API version.
+ */
+const char *
+srt_graphics_device_get_api_version (SrtGraphicsDevice *self)
+{
+  g_return_val_if_fail (SRT_IS_GRAPHICS_DEVICE (self), NULL);
+  return self->api_version;
+}
+
+/**
+ * srt_graphics_device_get_driver_version:
+ * @self: a SrtGraphicsDevice object
+ *
+ * Return the driver version used by @self, or %NULL if it's not known.
+ *
+ * Returns (nullable): A string indicating the driver version.
+ */
+const char *
+srt_graphics_device_get_driver_version (SrtGraphicsDevice *self)
+{
+  g_return_val_if_fail (SRT_IS_GRAPHICS_DEVICE (self), NULL);
+  return self->driver_version;
+}
+
+/**
+ * srt_graphics_device_get_vendor_id:
+ * @self: a SrtGraphicsDevice object
+ *
+ * Return the vendor ID of @self, or %NULL if it's not known.
+ *
+ * Returns (nullable): A string indicating the vendor ID.
+ */
+const char *
+srt_graphics_device_get_vendor_id (SrtGraphicsDevice *self)
+{
+  g_return_val_if_fail (SRT_IS_GRAPHICS_DEVICE (self), NULL);
+  return self->vendor_id;
+}
+
+/**
+ * srt_graphics_device_get_device_id:
+ * @self: a SrtGraphicsDevice object
+ *
+ * Return the device ID of @self, or %NULL if it's not known.
+ *
+ * Returns (nullable): A string indicating the device ID.
+ */
+const char *
+srt_graphics_device_get_device_id (SrtGraphicsDevice *self)
+{
+  g_return_val_if_fail (SRT_IS_GRAPHICS_DEVICE (self), NULL);
+  return self->device_id;
+}
+
+/**
+ * srt_graphics_device_get_messages:
+ * @self: a SrtGraphicsDevice object
+ *
+ * Return the diagnostic messages produced while checking @self device
+ * drawing capabilities.
+ *
+ * Returns: (nullable) (transfer none): A string, which must not be freed,
+ *  or %NULL if there were no diagnostic messages.
+ */
+const char *
+srt_graphics_device_get_messages (SrtGraphicsDevice *self)
+{
+  g_return_val_if_fail (SRT_IS_GRAPHICS_DEVICE (self), NULL);
+  return self->messages;
+}
+
+/**
+ * srt_graphics_device_get_device_type:
+ * @self: a SrtGraphicsDevice object
+ *
+ * Return the @self device type
+ *
+ * Returns: An enumeration of #SrtVkPhysicalDeviceType indicating the type
+ *  of this device.
+ */
+SrtVkPhysicalDeviceType
+srt_graphics_device_get_device_type (SrtGraphicsDevice *self)
+{
+  g_return_val_if_fail (SRT_IS_GRAPHICS_DEVICE (self),
+                        SRT_VK_PHYSICAL_DEVICE_TYPE_OTHER);
+  return self->type;
+}
+
+/*
+ * @self: a SrtGraphicsDevice object
+ * @can_draw: if %TRUE, this device is able to successfully draw a triangle
+ *  test
+ *
+ * @self issues is adjusted accordingly to the @can_draw value.
+ */
+static void
+_srt_graphics_device_set_can_draw (SrtGraphicsDevice *self,
+                                   gboolean can_draw)
+{
+  g_return_if_fail (SRT_IS_GRAPHICS_DEVICE (self));
+  if (can_draw)
+    self->issues &= ~SRT_GRAPHICS_ISSUES_CANNOT_DRAW;
+  else
+    self->issues |= SRT_GRAPHICS_ISSUES_CANNOT_DRAW;
+}
+
+/*
+ * @self: a SrtGraphicsDevice object
+ * @messages (nullable): diagnostic messages
+ *
+ * Set @self diagnostic messages to the provided @messages.
+ */
+static void
+_srt_graphics_device_set_messages (SrtGraphicsDevice *self,
+                                   const gchar *messages)
+{
+  g_return_if_fail (SRT_IS_GRAPHICS_DEVICE (self));
+  g_free (self->messages);
+  self->messages = g_strdup (messages);
+}
+
 struct _SrtGraphics
 {
   /*< private >*/
@@ -84,6 +462,7 @@ struct _SrtGraphics
   gchar *messages;
   gchar *renderer_string;
   gchar *version_string;
+  GPtrArray *devices;
   int exit_status;
   int terminating_signal;
 };
@@ -104,6 +483,7 @@ enum {
   PROP_RENDERING_INTERFACE,
   PROP_RENDERER_STRING,
   PROP_VERSION_STRING,
+  PROP_GRAPHICS_DEVICES,
   PROP_EXIT_STATUS,
   PROP_TERMINATING_SIGNAL,
   N_PROPERTIES
@@ -158,6 +538,10 @@ srt_graphics_get_property (GObject *object,
         g_value_set_string (value, self->version_string);
         break;
 
+      case PROP_GRAPHICS_DEVICES:
+        g_value_set_boxed (value, self->devices);
+        break;
+
       case PROP_EXIT_STATUS:
         g_value_set_int (value, self->exit_status);
         break;
@@ -173,9 +557,9 @@ srt_graphics_get_property (GObject *object,
 
 static void
 srt_graphics_set_property (GObject *object,
-                          guint prop_id,
-                          const GValue *value,
-                          GParamSpec *pspec)
+                           guint prop_id,
+                           const GValue *value,
+                           GParamSpec *pspec)
 {
   SrtGraphics *self = SRT_GRAPHICS (object);
   const char *tmp;
@@ -239,6 +623,12 @@ srt_graphics_set_property (GObject *object,
         self->version_string = g_value_dup_string (value);
         break;
 
+      case PROP_GRAPHICS_DEVICES:
+        /* Construct only */
+        g_return_if_fail (self->devices == NULL);
+        self->devices = g_value_dup_boxed (value);
+        break;
+
       case PROP_EXIT_STATUS:
         self->exit_status = g_value_get_int (value);
         break;
@@ -252,6 +642,16 @@ srt_graphics_set_property (GObject *object,
     }
 }
 
+static void
+srt_graphics_dispose (GObject *object)
+{
+  SrtGraphics *self = SRT_GRAPHICS (object);
+
+  g_clear_pointer (&self->devices, g_ptr_array_unref);
+
+  G_OBJECT_CLASS (srt_graphics_parent_class)->dispose (object);
+}
+
 static void
 srt_graphics_finalize (GObject *object)
 {
@@ -273,6 +673,7 @@ srt_graphics_class_init (SrtGraphicsClass *cls)
 
   object_class->get_property = srt_graphics_get_property;
   object_class->set_property = srt_graphics_set_property;
+  object_class->dispose = srt_graphics_dispose;
   object_class->finalize = srt_graphics_finalize;
 
   properties[PROP_ISSUES] =
@@ -327,6 +728,12 @@ srt_graphics_class_init (SrtGraphicsClass *cls)
                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
                          G_PARAM_STATIC_STRINGS);
 
+  properties[PROP_GRAPHICS_DEVICES] =
+    g_param_spec_boxed ("graphics-devices", "List of graphics devices", "List of #SrtGraphicsDevice, representing the graphical cards.",
+                        G_TYPE_PTR_ARRAY,
+                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+                        G_PARAM_STATIC_STRINGS);
+
   properties[PROP_EXIT_STATUS] =
     g_param_spec_int ("exit-status", "Exit status", "Exit status of helper(s) executed. 0 on success, positive on unsuccessful exit(), -1 if killed by a signal or not run at all",
                       -1,
@@ -352,7 +759,9 @@ srt_graphics_class_init (SrtGraphicsClass *cls)
  * @renderer_string: (out) (transfer none) (not optional):
  */
 static SrtGraphicsIssues
-_srt_process_wflinfo (JsonNode *node, const gchar **version_string, const gchar **renderer_string)
+_srt_process_wflinfo (JsonNode *node,
+                      gchar **version_string,
+                      const gchar **renderer_string)
 {
   g_return_val_if_fail (version_string != NULL, SRT_GRAPHICS_ISSUES_UNKNOWN);
   g_return_val_if_fail (renderer_string != NULL, SRT_GRAPHICS_ISSUES_UNKNOWN);
@@ -388,7 +797,7 @@ _srt_process_wflinfo (JsonNode *node, const gchar **version_string, const gchar
       return issues;
     }
 
-  *version_string = json_object_get_string_member (sub_object, "version string");
+  *version_string = g_strdup (json_object_get_string_member (sub_object, "version string"));
   *renderer_string = json_object_get_string_member (sub_object, "renderer string");
 
   /* Check renderer to see if we are using software rendering */
@@ -402,73 +811,59 @@ _srt_process_wflinfo (JsonNode *node, const gchar **version_string, const gchar
 }
 
 /*
- * @node: The JsonNode to process the vulkaninfo results from.
- * @new_version_string: (out) (transfer full) (not optional):
- * @renderer_string: (out) (transfer none) (not optional):
+ * @node (not nullable):
+ * @version_string (not optional) (out):
+ * @device (not optional) (out):
  */
 static SrtGraphicsIssues
-_srt_process_vulkaninfo (JsonNode *node, gchar **new_version_string, const gchar **renderer_string)
+_srt_process_check_vulkan_info (JsonNode *node,
+                                gchar **version_string,
+                                SrtGraphicsDevice **device)
 {
-  g_return_val_if_fail (new_version_string != NULL, SRT_GRAPHICS_ISSUES_UNKNOWN);
-  g_return_val_if_fail (renderer_string != NULL, SRT_GRAPHICS_ISSUES_UNKNOWN);
-
-  SrtGraphicsIssues issues = SRT_GRAPHICS_ISSUES_NONE;
-
-  if (node == NULL)
-    {
-      g_debug ("The json output is empty");
-      issues |= SRT_GRAPHICS_ISSUES_CANNOT_LOAD;
-      return issues;
-    }
-
-  JsonObject *object = json_node_get_object (node);
-  JsonNode *sub_node = NULL;
+  JsonObject *object = NULL;
   JsonObject *sub_object = NULL;
+  JsonNode *sub_node = NULL;
+  const gchar *device_name = NULL;
+  const gchar *api_version = NULL;
+  const gchar *driver_version = NULL;
+  const gchar *vendor_id = NULL;
+  const gchar *device_id = NULL;
+  SrtVkPhysicalDeviceType device_type;
 
-  // Parse vulkaninfo output
-  unsigned int api_version = 0;
-  unsigned int hw_vendor = 0;
-  unsigned int hw_device = 0;
-  unsigned int driver_version = 0;
+  /* Until we parse the drawing test results, we assume that we are not
+   * able to draw with this device */
+  SrtGraphicsIssues issues = SRT_GRAPHICS_ISSUES_CANNOT_DRAW;
 
-  if (!json_object_has_member (object, "VkPhysicalDeviceProperties"))
-    {
-      g_debug ("The json output doesn't contain VkPhysicalDeviceProperties");
-      issues |= SRT_GRAPHICS_ISSUES_CANNOT_LOAD;
-      return issues;
-    }
+  object = json_node_get_object (node);
+
+  /* We only parse the device information, not the drawing test result */
+  if (!json_object_has_member (object, "device-info"))
+    return issues;
 
-  sub_node = json_object_get_member (object, "VkPhysicalDeviceProperties");
+  sub_node = json_object_get_member (object, "device-info");
   sub_object = json_node_get_object (sub_node);
 
-  if (!json_object_has_member (sub_object, "deviceName") ||
-      !json_object_has_member (sub_object, "driverVersion") ||
-      !json_object_has_member (sub_object, "apiVersion") ||
-      !json_object_has_member (sub_object, "deviceID") ||
-      !json_object_has_member (sub_object, "vendorID"))
-    {
-      g_debug ("Json output is missing deviceName or driverVersion");
-      issues |= SRT_GRAPHICS_ISSUES_CANNOT_LOAD;
-      return issues;
-    }
+  device_name = json_object_get_string_member_with_default (sub_object, "device-name",
+                                                            NULL);
+  device_type = json_object_get_int_member_with_default (sub_object, "device-type", 0);
+  api_version = json_object_get_string_member_with_default (sub_object, "api-version",
+                                                            NULL);
+  driver_version = json_object_get_string_member_with_default (sub_object, "driver-version",
+                                                               NULL);
+  vendor_id = json_object_get_string_member_with_default (sub_object, "vendor-id",
+                                                          NULL);
+  device_id = json_object_get_string_member_with_default (sub_object, "device-id",
+                                                          NULL);
+
+  *version_string = g_strdup_printf ("%s (device %s:%s) (driver %s)",
+                                     api_version, vendor_id, device_id, driver_version);
 
-  api_version = json_object_get_int_member (sub_object, "apiVersion");
-  hw_vendor = json_object_get_int_member (sub_object, "vendorID");
-  driver_version = json_object_get_int_member (sub_object, "driverVersion");
-  hw_device = json_object_get_int_member (sub_object, "deviceID");
+  if (device_type == SRT_VK_PHYSICAL_DEVICE_TYPE_CPU)
+    issues |= SRT_GRAPHICS_ISSUES_SOFTWARE_RENDERING;
 
-  *new_version_string = g_strdup_printf ("%u.%u.%u (device %04x:%04x) (driver %u.%u.%u)",
-                                        VK_VERSION_MAJOR (api_version),
-                                        VK_VERSION_MINOR (api_version),
-                                        VK_VERSION_PATCH (api_version),
-                                        hw_vendor,
-                                        hw_device,
-                                        VK_VERSION_MAJOR (driver_version),
-                                        VK_VERSION_MINOR (driver_version),
-                                        VK_VERSION_PATCH (driver_version));
-  *renderer_string = json_object_get_string_member (sub_object, "deviceName");
+  *device = _srt_graphics_device_new (device_name, api_version, driver_version,
+                                      vendor_id, device_id, device_type, issues);
 
-  /* NOTE: No need to check for software rendering with vulkan yet */
   return issues;
 }
 
@@ -586,13 +981,12 @@ _argv_for_graphics_test (const char *helpers_path,
         break;
 
       case SRT_RENDERING_INTERFACE_VULKAN:
-        argv = _srt_get_helper (helpers_path, multiarch_tuple, "vulkaninfo",
+        argv = _srt_get_helper (helpers_path, multiarch_tuple, "check-vulkan",
                                 flags, error);
 
         if (argv == NULL)
           goto out;
 
-        g_ptr_array_add (argv, g_strdup ("-j"));
         break;
 
       case SRT_RENDERING_INTERFACE_VDPAU:
@@ -627,28 +1021,6 @@ out:
   return argv;
 }
 
-static GPtrArray *
-_argv_for_check_vulkan (const char *helpers_path,
-                        SrtTestFlags test_flags,
-                        const char *multiarch_tuple,
-                        GError **error)
-{
-  GPtrArray *argv;
-  SrtHelperFlags flags = SRT_HELPER_FLAGS_TIME_OUT;
-
-  if (test_flags & SRT_TEST_FLAGS_TIME_OUT_SOONER)
-    flags |= SRT_HELPER_FLAGS_TIME_OUT_SOONER;
-
-  argv = _srt_get_helper (helpers_path, multiarch_tuple, "check-vulkan",
-                          flags, error);
-
-  if (argv == NULL)
-    return NULL;
-
-  g_ptr_array_add (argv, NULL);
-  return argv;
-}
-
 static GPtrArray *
 _argv_for_check_gl (const char *helpers_path,
                     SrtTestFlags test_flags,
@@ -998,8 +1370,7 @@ _srt_check_graphics (gchar **envp,
   int exit_status = -1;
   int terminating_signal = 0;
   g_autoptr(JsonNode) node = NULL;
-  const gchar *version_string = NULL;
-  gchar *new_version_string = NULL;
+  g_autofree gchar *version_string = NULL;
   const gchar *renderer_string = NULL;
   GError *error = NULL;
   SrtGraphicsIssues issues = SRT_GRAPHICS_ISSUES_NONE;
@@ -1008,6 +1379,9 @@ _srt_check_graphics (gchar **envp,
   const gchar *ld_preload;
   gchar *filtered_preload = NULL;
   SrtGraphicsLibraryVendor library_vendor = SRT_GRAPHICS_LIBRARY_VENDOR_UNKNOWN;
+  g_auto(GStrv) json_output = NULL;
+  g_autoptr(GPtrArray) graphics_device = g_ptr_array_new_with_free_func (g_object_unref);
+  gsize i;
 
   g_return_val_if_fail (details_out == NULL || *details_out == NULL, SRT_GRAPHICS_ISSUES_UNKNOWN);
   g_return_val_if_fail (((unsigned) window_system) < SRT_N_WINDOW_SYSTEMS, SRT_GRAPHICS_ISSUES_UNKNOWN);
@@ -1046,10 +1420,10 @@ _srt_check_graphics (gchar **envp,
     {
       case SRT_RENDERING_INTERFACE_GL:
       case SRT_RENDERING_INTERFACE_GLESV2:
-      case SRT_RENDERING_INTERFACE_VULKAN:
         non_zero_wait_status_issue = SRT_GRAPHICS_ISSUES_CANNOT_LOAD;
         break;
 
+      case SRT_RENDERING_INTERFACE_VULKAN:
       case SRT_RENDERING_INTERFACE_VDPAU:
       case SRT_RENDERING_INTERFACE_VAAPI:
         /* The test here tries to draw an offscreen X11 window */
@@ -1070,8 +1444,13 @@ _srt_check_graphics (gchar **envp,
                              FALSE,
                              non_zero_wait_status_issue);
 
-  if (issues != SRT_GRAPHICS_ISSUES_NONE)
+  if (rendering_interface != SRT_RENDERING_INTERFACE_VULKAN
+      && issues != SRT_GRAPHICS_ISSUES_NONE)
     {
+      /* For Vulkan `LIBGL_DEBUG` has no effect. Also we try to continue even
+       * if we faced some issues because we might still have a valid JSON in
+       * output */
+
       // Issues found, so run again with LIBGL_DEBUG=verbose set in environment
       issues |= _srt_run_helper (&my_environ,
                                  &output,
@@ -1090,8 +1469,6 @@ _srt_check_graphics (gchar **envp,
     {
       case SRT_RENDERING_INTERFACE_GL:
       case SRT_RENDERING_INTERFACE_GLESV2:
-      case SRT_RENDERING_INTERFACE_VULKAN:
-
         node = json_from_string (output, &error);
         if (node == NULL)
           {
@@ -1114,21 +1491,7 @@ _srt_check_graphics (gchar **envp,
 
             goto out;
           }
-        break;
-
-      case SRT_RENDERING_INTERFACE_VDPAU:
-      case SRT_RENDERING_INTERFACE_VAAPI:
-        /* The output is in plan text, nothing to do here */
-        break;
-
-      default:
-        g_return_val_if_reached (SRT_GRAPHICS_ISSUES_UNKNOWN);
-    }
 
-  switch (rendering_interface)
-    {
-      case SRT_RENDERING_INTERFACE_GL:
-      case SRT_RENDERING_INTERFACE_GLESV2:
         issues |= _srt_process_wflinfo (node, &version_string, &renderer_string);
 
         if (issues != SRT_GRAPHICS_ISSUES_NONE)
@@ -1193,39 +1556,109 @@ _srt_check_graphics (gchar **envp,
         break;
 
       case SRT_RENDERING_INTERFACE_VULKAN:
-        issues |= _srt_process_vulkaninfo (node, &new_version_string, &renderer_string);
-        if (new_version_string != NULL)
+        if (output == NULL || output[0] == '\0')
           {
-            version_string = new_version_string;
+            g_debug ("The helper output is empty");
+            issues |= SRT_GRAPHICS_ISSUES_CANNOT_LOAD;
+            goto out;
           }
 
-        /* Now perform *-check-vulkan drawing test */
-        g_ptr_array_unref (argv);
-        g_clear_pointer (&output, g_free);
+        json_output = g_strsplit (output, "\n", -1);
 
-        argv = _argv_for_check_vulkan (helpers_path,
-                                       test_flags,
-                                       multiarch_tuple,
-                                       &error);
-
-        if (argv == NULL)
+        for (i = 0; json_output[i] != NULL; i++)
           {
-            issues |= SRT_GRAPHICS_ISSUES_CANNOT_DRAW;
-            /* Put the error message in the 'messages' */
-            child_stderr2 = g_strdup (error->message);
-            goto out;
+            g_autofree gchar *new_version_string = NULL;
+            g_autoptr(JsonNode) this_node = NULL;
+            SrtGraphicsDevice *device = NULL;
+            SrtGraphicsIssues device_issues = SRT_GRAPHICS_ISSUES_NONE;
+            this_node = json_from_string (json_output[i], &error);
+            if (this_node == NULL)
+              {
+                if (error != NULL)
+                  {
+                    g_debug ("The Vulkan helper output is not a valid JSON: %s", error->message);
+                    g_clear_error (&error);
+                  }
+                break;
+              }
+
+            device_issues |= _srt_process_check_vulkan_info (this_node, &new_version_string,
+                                                             &device);
+
+            /* If we were unable to get the device info from this node, it
+             * probably means that we already checked all the "device-info"
+             * JSON objects. There is no need to continue because drawing
+             * tests always follow the device info. */
+            if (device == NULL)
+              break;
+
+            /* If the GPU 0 is a software rendering, we propagate this info to
+             * the general Vulkan issues too. */
+            if (i == 0 && device_issues & SRT_GRAPHICS_ISSUES_SOFTWARE_RENDERING)
+              issues |= SRT_GRAPHICS_ISSUES_SOFTWARE_RENDERING;
+
+            /* If this is the first device that we have, store its version and
+             * renderer for the SrtGraphics object */
+            if (version_string == NULL)
+              version_string = g_strdup (new_version_string);
+
+            if (renderer_string == NULL)
+              renderer_string = srt_graphics_device_get_name (device);
+
+            g_ptr_array_add (graphics_device, device);
           }
 
-        /* Now run and report exit code/messages if failure */
-        issues |= _srt_run_helper (&my_environ,
-                                   &output,
-                                   &child_stderr2,
-                                   argv,
-                                   &wait_status,
-                                   &exit_status,
-                                   &terminating_signal,
-                                   FALSE,
-                                   SRT_GRAPHICS_ISSUES_CANNOT_DRAW);
+        for (i = 0; json_output[i] != NULL; i++)
+          {
+            JsonObject *object = NULL;
+            JsonObject *sub_object = NULL;
+            JsonNode *sub_node = NULL;
+            gboolean can_draw;
+            gsize index;
+            const gchar *messages = NULL;
+            g_autoptr(JsonNode) this_node = NULL;
+            this_node = json_from_string (json_output[i], &error);
+            if (this_node == NULL)
+              {
+                if (error != NULL)
+                  {
+                    g_debug ("The Vulkan helper output is not a valid JSON: %s", error->message);
+                    g_clear_error (&error);
+                  }
+                /* Apparently we reached the final empty line */
+                break;
+              }
+
+            object = json_node_get_object (this_node);
+            /* We only parse the drawing test result */
+            if (!json_object_has_member (object, "test"))
+              continue;
+
+            sub_node = json_object_get_member (object, "test");
+            sub_object = json_node_get_object (sub_node);
+
+            if (!json_object_has_member (sub_object, "index"))
+              {
+                g_debug ("The Vulkan helper output seems to be malformed");
+                break;
+              }
+
+            index = json_object_get_int_member (sub_object, "index");
+            can_draw = json_object_get_boolean_member_with_default (sub_object, "can-draw",
+                                                                    FALSE);
+            messages = json_object_get_string_member_with_default (sub_object, "error-message",
+                                                                   NULL);
+
+            if (graphics_device->len < index + 1)
+              {
+                g_debug ("Apparently the Vulkan helper returned more test results than devices info");
+                break;
+              }
+            _srt_graphics_device_set_can_draw (g_ptr_array_index (graphics_device, index),
+                                               can_draw);
+            _srt_graphics_device_set_messages (g_ptr_array_index (graphics_device, index),
+                                               messages);
+          }
         break;
 
       case SRT_RENDERING_INTERFACE_VDPAU:
@@ -1240,8 +1673,8 @@ _srt_check_graphics (gchar **envp,
 
 out:
 
-  /* If we have stderr (or error messages) from both vulkaninfo and
-   * check-vulkan, combine them */
+  /* If we have stderr (or error messages) from both wflinfo and
+   * check-gl, combine them */
   if (child_stderr2 != NULL && child_stderr2[0] != '\0')
     {
       gchar *tmp = g_strconcat (child_stderr, child_stderr2, NULL);
@@ -1257,12 +1690,12 @@ out:
                                       library_vendor,
                                       renderer_string,
                                       version_string,
+                                      graphics_device,
                                       issues,
                                       child_stderr,
                                       exit_status,
                                       terminating_signal);
 
-  g_free (new_version_string);
   g_clear_pointer (&argv, g_ptr_array_unref);
   g_free (output);
   g_free (child_stderr);
@@ -1464,6 +1897,63 @@ srt_graphics_get_messages (SrtGraphics *self)
   return self->messages;
 }
 
+/**
+ * srt_graphics_get_devices:
+ * @self: a graphics object
+ *
+ * Return the list of graphics devices that have been found.
+ *
+ * Returns: (transfer full) (element-type SrtGraphicsDevice): The graphics
+ *  devices. Free with `g_list_free_full (list, g_object_unref)`.
+ */
+GList *
+srt_graphics_get_devices (SrtGraphics *self)
+{
+  GList *ret = NULL;
+  guint i;
+
+  g_return_val_if_fail (SRT_IS_GRAPHICS (self), NULL);
+
+  for (i = 0; self->devices != NULL && i < self->devices->len; i++)
+    ret = g_list_prepend (ret, g_object_ref (g_ptr_array_index (self->devices, i)));
+
+  return g_list_reverse (ret);
+}
+
+static SrtGraphicsIssues
+_srt_get_graphics_issues_from_json_object (JsonObject *json_obj)
+{
+  JsonArray *array;
+  SrtGraphicsIssues graphics_issues = SRT_GRAPHICS_ISSUES_NONE;
+  gsize j;
+
+  /* In graphics, a missing "issues" array means that no issues were found */
+  if (json_object_has_member (json_obj, "issues"))
+    {
+      array = json_object_get_array_member (json_obj, "issues");
+
+      /* We are expecting an array of issues here */
+      if (array == NULL)
+        {
+          g_debug ("'issues' in 'graphics-details' is not an array as expected");
+          graphics_issues |= SRT_GRAPHICS_ISSUES_UNKNOWN;
+        }
+      else
+        {
+          for (j = 0; j < json_array_get_length (array); j++)
+            {
+              const gchar *issue_string = json_array_get_string_element (array, j);
+              if (!srt_add_flag_from_nick (SRT_TYPE_GRAPHICS_ISSUES,
+                                            issue_string,
+                                            &graphics_issues,
+                                            NULL))
+                graphics_issues |= SRT_GRAPHICS_ISSUES_UNKNOWN;
+            }
+        }
+    }
+  return graphics_issues;
+}
+
 /**
  * _srt_graphics_get_from_report:
  * @json_obj: (not nullable): A JSON Object representing an ABI,
@@ -1508,6 +1998,8 @@ _srt_graphics_get_from_report (JsonObject *json_obj,
           SrtGraphicsIssues graphics_issues = SRT_GRAPHICS_ISSUES_NONE;
           SrtWindowSystem window_system = SRT_WINDOW_SYSTEM_X11;
           SrtRenderingInterface rendering_interface = SRT_RENDERING_INTERFACE_GL;
+          g_autoptr(GPtrArray) devices = g_ptr_array_new_with_free_func (g_object_unref);
+          gsize j;
 
           gchar **stack_parts = g_strsplit (l->data, "/", 2);
           if (stack_parts[1] == NULL)
@@ -1551,30 +2043,7 @@ _srt_graphics_get_from_report (JsonObject *json_obj,
           version = json_object_get_string_member_with_default (json_stack_obj, "version",
                                                                 NULL);
 
-          /* In graphics, a missing "issues" array means that no issues were found */
-          if (json_object_has_member (json_stack_obj, "issues"))
-            {
-              array = json_object_get_array_member (json_stack_obj, "issues");
-
-              /* We are expecting an array of issues here */
-              if (array == NULL)
-                {
-                  g_debug ("'issues' in 'graphics-details' is not an array as expected");
-                  graphics_issues |= SRT_GRAPHICS_ISSUES_UNKNOWN;
-                }
-              else
-                {
-                  for (guint j = 0; j < json_array_get_length (array); j++)
-                    {
-                      const gchar *issue_string = json_array_get_string_element (array, j);
-                      if (!srt_add_flag_from_nick (SRT_TYPE_GRAPHICS_ISSUES,
-                                                   issue_string,
-                                                   &graphics_issues,
-                                                   NULL))
-                        graphics_issues |= SRT_GRAPHICS_ISSUES_UNKNOWN;
-                    }
-                }
-            }
+          graphics_issues = _srt_get_graphics_issues_from_json_object (json_stack_obj);
 
           /* A missing exit_status means that its value is the default zero */
           exit_status = json_object_get_int_member_with_default (json_stack_obj, "exit-status", 0);
@@ -1593,12 +2062,75 @@ _srt_graphics_get_from_report (JsonObject *json_obj,
                 library_vendor = SRT_GRAPHICS_LIBRARY_VENDOR_UNKNOWN;
             }
 
+          if (json_object_has_member (json_stack_obj, "devices"))
+            {
+              array = json_object_get_array_member (json_stack_obj, "devices");
+              gsize array_size = 0;
+
+              /* We are expecting an array of devices here */
+              if (array == NULL)
+                g_debug ("'devices' in 'graphics-details' is not an array as expected");
+              else
+                array_size =  json_array_get_length (array);
+
+              for (j = 0; j < array_size; j++)
+                {
+                  SrtGraphicsDevice *device = NULL;
+                  const gchar *name;
+                  const gchar *api_version;
+                  const gchar *driver_version;
+                  const gchar *vendor_id;
+                  const gchar *device_id;
+                  g_autofree gchar *dev_messages = NULL;
+                  SrtVkPhysicalDeviceType type = SRT_VK_PHYSICAL_DEVICE_TYPE_OTHER;
+                  SrtGraphicsIssues issues = SRT_GRAPHICS_ISSUES_NONE;
+
+                  JsonObject *device_obj = json_array_get_object_element (array, j);
+                  name = json_object_get_string_member_with_default (device_obj, "name",
+                                                                     NULL);
+                  api_version = json_object_get_string_member_with_default (device_obj,
+                                                                            "api-version",
+                                                                            NULL);
+                  driver_version = json_object_get_string_member_with_default (device_obj,
+                                                                               "driver-version",
+                                                                               NULL);
+                  vendor_id = json_object_get_string_member_with_default (device_obj,
+                                                                          "vendor-id",
+                                                                          NULL);
+                  device_id = json_object_get_string_member_with_default (device_obj,
+                                                                          "device-id",
+                                                                          NULL);
+
+                  if (json_object_has_member (device_obj, "type"))
+                    {
+                      const gchar *type_str = json_object_get_string_member (device_obj, "type");
+                      G_STATIC_ASSERT (sizeof (SrtVkPhysicalDeviceType) == sizeof (gint));
+                      if (!srt_enum_from_nick (SRT_TYPE_VK_PHYSICAL_DEVICE_TYPE,
+                                              type_str,
+                                              (gint *) &type,
+                                              NULL))
+                        type = SRT_VK_PHYSICAL_DEVICE_TYPE_OTHER;
+                    }
+
+                  issues = _srt_get_graphics_issues_from_json_object (device_obj);
+                  dev_messages = _srt_json_object_dup_array_of_lines_member (device_obj,
+                                                                             "messages");
+
+                  device = _srt_graphics_device_new (name, api_version, driver_version,
+                                                     vendor_id, device_id, type,
+                                                     issues);
+                  _srt_graphics_device_set_messages (device, dev_messages);
+                  g_ptr_array_add (devices, device);
+                }
+            }
+
           graphics = _srt_graphics_new (multiarch_tuple,
                                         window_system,
                                         rendering_interface,
                                         library_vendor,
                                         renderer,
                                         version,
+                                        devices,
                                         graphics_issues,
                                         messages,
                                         exit_status,
diff --git a/steam-runtime-tools/graphics.h b/steam-runtime-tools/graphics.h
index a5ad1d0804a3b8ffef59749f60bfeba4b06d02ab..da2cf0e84da3b417769b6e37893e2d1e5eedc2f9 100644
--- a/steam-runtime-tools/graphics.h
+++ b/steam-runtime-tools/graphics.h
@@ -1,5 +1,5 @@
 /*
- * Copyright © 2019 Collabora Ltd.
+ * Copyright © 2019-2021 Collabora Ltd.
  *
  * SPDX-License-Identifier: MIT
  *
@@ -47,6 +47,18 @@ typedef struct _SrtGraphicsClass SrtGraphicsClass;
 _SRT_PUBLIC
 GType srt_graphics_get_type (void);
 
+typedef struct _SrtGraphicsDevice SrtGraphicsDevice;
+typedef struct _SrtGraphicsDeviceClass SrtGraphicsDeviceClass;
+
+#define SRT_TYPE_GRAPHICS_DEVICE (srt_graphics_device_get_type ())
+#define SRT_GRAPHICS_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SRT_TYPE_GRAPHICS_DEVICE, SrtGraphicsDevice))
+#define SRT_GRAPHICS_DEVICE_CLASS(cls) (G_TYPE_CHECK_CLASS_CAST ((cls), SRT_TYPE_GRAPHICS_DEVICE, SrtGraphicsDeviceClass))
+#define SRT_IS_GRAPHICS_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SRT_TYPE_GRAPHICS_DEVICE))
+#define SRT_IS_GRAPHICS_DEVICE_CLASS(cls) (G_TYPE_CHECK_CLASS_TYPE ((cls), SRT_TYPE_GRAPHICS_DEVICE))
+#define SRT_GRAPHICS_DEVICE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), SRT_TYPE_GRAPHICS_DEVICE, SrtGraphicsDeviceClass)
+_SRT_PUBLIC
+GType srt_graphics_device_get_type (void);
+
 /* Backward compatibility with previous steam-runtime-tools naming */
 #define SRT_GRAPHICS_ISSUES_INTERNAL_ERROR SRT_GRAPHICS_ISSUES_UNKNOWN
 
@@ -133,6 +145,48 @@ typedef enum
 
 #define SRT_N_RENDERING_INTERFACES (SRT_RENDERING_INTERFACE_VAAPI + 1)
 
+/**
+ * SrtVkPhysicalDeviceType:
+ * @SRT_VK_PHYSICAL_DEVICE_TYPE_OTHER: The GPU does not match any other available types
+ * @SRT_VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU: The GPU is typically one embedded in or
+ *  tightly coupled with the host
+ * @SRT_VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU: The GPU is typically a separate processor
+ *  connected to the host via an interlink.
+ * @SRT_VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU: The GPU is typically a virtual node in a
+ *  virtualization environment
+ * @SRT_VK_PHYSICAL_DEVICE_TYPE_CPU: The GPU is typically running on the same processors
+ *  as the host (software rendering such as llvmpipe)
+ *
+ * These enums have been taken from the VkPhysicalDeviceType Vulkan specs.
+ * Please keep them in sync.
+ * https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VkPhysicalDeviceType.html
+ */
+typedef enum
+{
+  SRT_VK_PHYSICAL_DEVICE_TYPE_OTHER = 0,
+  SRT_VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU = 1,
+  SRT_VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU = 2,
+  SRT_VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU = 3,
+  SRT_VK_PHYSICAL_DEVICE_TYPE_CPU = 4,
+} SrtVkPhysicalDeviceType;
+
+_SRT_PUBLIC
+SrtGraphicsIssues srt_graphics_device_get_issues (SrtGraphicsDevice *self);
+_SRT_PUBLIC
+const char *srt_graphics_device_get_name (SrtGraphicsDevice *self);
+_SRT_PUBLIC
+const char *srt_graphics_device_get_api_version (SrtGraphicsDevice *self);
+_SRT_PUBLIC
+const char *srt_graphics_device_get_driver_version (SrtGraphicsDevice *self);
+_SRT_PUBLIC
+const char *srt_graphics_device_get_vendor_id (SrtGraphicsDevice *self);
+_SRT_PUBLIC
+const char *srt_graphics_device_get_device_id (SrtGraphicsDevice *self);
+_SRT_PUBLIC
+const char *srt_graphics_device_get_messages (SrtGraphicsDevice *self);
+_SRT_PUBLIC
+SrtVkPhysicalDeviceType srt_graphics_device_get_device_type (SrtGraphicsDevice *self);
+
 _SRT_PUBLIC
 const char *srt_graphics_get_multiarch_tuple (SrtGraphics *self);
 _SRT_PUBLIC
@@ -151,6 +205,8 @@ const char *srt_graphics_get_renderer_string (SrtGraphics *self);
 _SRT_PUBLIC
 const char *srt_graphics_get_messages (SrtGraphics *self);
 _SRT_PUBLIC
+GList *srt_graphics_get_devices (SrtGraphics *self);
+_SRT_PUBLIC
 gchar *srt_graphics_dup_parameters_string (SrtGraphics *self);
 _SRT_PUBLIC
 int srt_graphics_get_exit_status (SrtGraphics *self);
@@ -340,6 +396,7 @@ const gchar *srt_glx_icd_get_library_path (SrtGlxIcd *self);
 
 #ifdef G_DEFINE_AUTOPTR_CLEANUP_FUNC
 G_DEFINE_AUTOPTR_CLEANUP_FUNC (SrtGraphics, g_object_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (SrtGraphicsDevice, g_object_unref)
 G_DEFINE_AUTOPTR_CLEANUP_FUNC (SrtEglIcd, g_object_unref)
 G_DEFINE_AUTOPTR_CLEANUP_FUNC (SrtDriDriver, g_object_unref)
 G_DEFINE_AUTOPTR_CLEANUP_FUNC (SrtVaApiDriver, g_object_unref)
diff --git a/tests/graphics.c b/tests/graphics.c
index b8e527bb4daedf9a844630540fabfdf3be37dcc6..53b30ecad420793ea548edd33d06bf7c5df250b3 100644
--- a/tests/graphics.c
+++ b/tests/graphics.c
@@ -1,5 +1,5 @@
 /*
- * Copyright © 2019 Collabora Ltd.
+ * Copyright © 2019-2021 Collabora Ltd.
  *
  * SPDX-License-Identifier: MIT
  *
@@ -205,14 +205,14 @@ static void
 test_object (Fixture *f,
              gconstpointer context)
 {
-  SrtGraphics *graphics;
+  g_autoptr(SrtGraphics) graphics = NULL;
   SrtGraphicsIssues issues;
   SrtGraphicsLibraryVendor library_vendor;
   gboolean vendor_neutral;
-  gchar *messages;
-  gchar *tuple;
-  gchar *renderer;
-  gchar *version;
+  g_autofree gchar *messages = NULL;
+  g_autofree gchar *tuple = NULL;
+  g_autofree gchar *renderer = NULL;
+  g_autofree gchar *version = NULL;
   int exit_status;
   int terminating_signal;
 
@@ -222,6 +222,7 @@ test_object (Fixture *f,
                                SRT_GRAPHICS_LIBRARY_VENDOR_GLVND,
                                SRT_TEST_GOOD_GRAPHICS_RENDERER,
                                SRT_TEST_GOOD_GRAPHICS_VERSION,
+                               NULL,
                                SRT_GRAPHICS_ISSUES_NONE,
                                "",
                                0,
@@ -258,11 +259,6 @@ test_object (Fixture *f,
   g_assert_cmpstr (version, ==, SRT_TEST_GOOD_GRAPHICS_VERSION);
   g_assert_cmpint (exit_status, ==, 0);
   g_assert_cmpint (terminating_signal, ==, 0);
-  g_free (messages);
-  g_free (tuple);
-  g_free (renderer);
-  g_free (version);
-  g_object_unref (graphics);
 }
 
 /*
@@ -331,7 +327,7 @@ test_normalize_window_system (Fixture *f,
      * are too old) */
   };
   gsize i;
-  SrtSystemInfo *info = srt_system_info_new (NULL);
+  g_autoptr(SrtSystemInfo) info = srt_system_info_new (NULL);
 
   srt_system_info_set_helpers_path (info, f->builddir);
 
@@ -387,8 +383,6 @@ test_normalize_window_system (Fixture *f,
       g_assert_cmpint (winsys, ==, test_vector->out.winsys);
       g_clear_object (&graphics);
     }
-
-  g_object_unref (info);
 }
 
 /*
@@ -398,13 +392,13 @@ static void
 test_sigusr (Fixture *f,
                gconstpointer context)
 {
-  SrtGraphics *graphics = NULL;
+  g_autoptr(SrtGraphics) graphics = NULL;
   SrtGraphicsIssues issues;
-  gchar *tuple;
+  g_autofree gchar *tuple = NULL;
   int exit_status;
   int terminating_signal;
 
-  SrtSystemInfo *info = srt_system_info_new (NULL);
+  g_autoptr(SrtSystemInfo) info = srt_system_info_new (NULL);
   srt_system_info_set_helpers_path (info, f->builddir);
 
   issues = srt_system_info_check_graphics (info,
@@ -428,10 +422,6 @@ test_sigusr (Fixture *f,
       g_assert_cmpint (exit_status, ==, 128 + SIGUSR1);
     }
   g_assert_cmpint (terminating_signal, ==, SIGUSR1);
-  g_free (tuple);
-
-  g_object_unref (graphics);
-  g_object_unref (info);
 }
 
 /*
@@ -440,12 +430,12 @@ test_sigusr (Fixture *f,
 static void
 assert_egl_icd (SrtEglIcd *icd)
 {
-  GError *error = NULL;
-  GError *error_property = NULL;
-  gchar *json_path = NULL;
-  gchar *library_path = NULL;
-  gchar *resolved = NULL;
-  gchar *resolved_property = NULL;
+  g_autoptr(GError) error = NULL;
+  g_autoptr(GError) error_property = NULL;
+  g_autofree gchar *json_path = NULL;
+  g_autofree gchar *library_path = NULL;
+  g_autofree gchar *resolved = NULL;
+  g_autofree gchar *resolved_property = NULL;
 
   g_assert_true (SRT_IS_EGL_ICD (icd));
 
@@ -497,13 +487,6 @@ assert_egl_icd (SrtEglIcd *icd)
       g_assert_cmpstr (resolved, ==, NULL);
       g_assert_cmpstr (resolved_property, ==, NULL);
     }
-
-  g_clear_error (&error);
-  g_clear_error (&error_property);
-  g_free (json_path);
-  g_free (library_path);
-  g_free (resolved);
-  g_free (resolved_property);
 }
 
 /*
@@ -555,8 +538,9 @@ test_icd_egl (Fixture *f,
               gconstpointer context)
 {
   const Config *config = context;
-  SrtSystemInfo *info = srt_system_info_new (NULL);
-  GList *icds;
+  g_autoptr(SrtSystemInfo) info = srt_system_info_new (NULL);
+  g_autoptr(SrtObjectList) icds = NULL;
+  g_autofree gchar *resolved = NULL;
   const GList *iter;
   const char * const multiarchs[] = { "mock-abi", NULL };
 
@@ -589,7 +573,6 @@ test_icd_egl (Fixture *f,
   if (config != NULL && config->icd_mode == ICD_MODE_EXPLICIT_DIRS)
     {
       SrtEglIcd *other;
-      gchar *resolved;
 
       iter = icds;
       g_assert_nonnull (iter);
@@ -637,7 +620,6 @@ test_icd_egl (Fixture *f,
                        "/opt/libEGL_myvendor.so");
       resolved = srt_egl_icd_resolve_library_path (iter->data);
       g_assert_cmpstr (resolved, ==, "/opt/libEGL_myvendor.so");
-      g_free (resolved);
 
       iter = iter->next;
       g_assert_null (iter);
@@ -675,7 +657,6 @@ test_icd_egl (Fixture *f,
   else if (config != NULL && config->icd_mode == ICD_MODE_RELATIVE_FILENAMES)
     {
       const char *path;
-      gchar *resolved;
 
       iter = icds;
       g_assert_nonnull (iter);
@@ -695,7 +676,6 @@ test_icd_egl (Fixture *f,
                        "libEGL_mesa.so.0");
       resolved = srt_egl_icd_resolve_library_path (iter->data);
       g_assert_cmpstr (resolved, ==, "libEGL_mesa.so.0");
-      g_free (resolved);
 
       iter = iter->next;
       g_assert_nonnull (iter);
@@ -735,7 +715,6 @@ test_icd_egl (Fixture *f,
   else if (config != NULL && config->icd_mode == ICD_MODE_FLATPAK)
     {
       SrtEglIcd *other;
-      gchar *resolved;
 
       iter = icds;
       g_assert_nonnull (iter);
@@ -747,7 +726,6 @@ test_icd_egl (Fixture *f,
       resolved = srt_egl_icd_resolve_library_path (iter->data);
       g_assert_cmpstr (resolved, ==,
                        "/usr/lib/mock-abi/GL/glvnd/egl_vendor.d/../libEGL_relative.so");
-      g_free (resolved);
 
       other = srt_egl_icd_new_replace_library_path (iter->data,
                                                     "/run/host/libEGL.so");
@@ -766,8 +744,6 @@ test_icd_egl (Fixture *f,
     }
   else
     {
-      gchar *resolved;
-
       /* EGL ICDs don't respect the XDG variables, so XDG_DIRS is the same
        * as NORMAL. */
       iter = icds;
@@ -786,13 +762,10 @@ test_icd_egl (Fixture *f,
                        "libEGL_mesa.so.0");
       resolved = srt_egl_icd_resolve_library_path (iter->data);
       g_assert_cmpstr (resolved, ==, "libEGL_mesa.so.0");
-      g_free (resolved);
 
       iter = iter->next;
       g_assert_null (iter);
     }
-
-  g_list_free_full (icds, g_object_unref);
 }
 
 /*
@@ -801,13 +774,13 @@ test_icd_egl (Fixture *f,
 static void
 assert_vulkan_icd (SrtVulkanIcd *icd)
 {
-  GError *error = NULL;
-  GError *error_property = NULL;
-  gchar *api_version = NULL;
-  gchar *json_path = NULL;
-  gchar *library_path = NULL;
-  gchar *resolved = NULL;
-  gchar *resolved_property = NULL;
+  g_autoptr(GError) error = NULL;
+  g_autoptr(GError) error_property = NULL;
+  g_autofree gchar *api_version = NULL;
+  g_autofree gchar *json_path = NULL;
+  g_autofree gchar *library_path = NULL;
+  g_autofree gchar *resolved = NULL;
+  g_autofree gchar *resolved_property = NULL;
 
   g_assert_true (SRT_IS_VULKAN_ICD (icd));
 
@@ -863,14 +836,6 @@ assert_vulkan_icd (SrtVulkanIcd *icd)
       g_assert_cmpstr (resolved, ==, NULL);
       g_assert_cmpstr (resolved_property, ==, NULL);
     }
-
-  g_clear_error (&error);
-  g_clear_error (&error_property);
-  g_free (api_version);
-  g_free (json_path);
-  g_free (library_path);
-  g_free (resolved);
-  g_free (resolved_property);
 }
 
 /*
@@ -901,9 +866,10 @@ test_icd_vulkan (Fixture *f,
                  gconstpointer context)
 {
   const Config *config = context;
-  SrtSystemInfo *info = srt_system_info_new (NULL);
-  GList *icds;
+  g_autoptr(SrtSystemInfo) info = srt_system_info_new (NULL);
+  g_autoptr(SrtObjectList) icds = NULL;
   const GList *iter;
+  g_autofree gchar *resolved = NULL;
   const char * const multiarchs[] = { "mock-abi", NULL };
 
   srt_system_info_set_environ (info, f->fake_icds_envp);
@@ -1042,7 +1008,6 @@ test_icd_vulkan (Fixture *f,
     }
   else if (config != NULL && config->icd_mode == ICD_MODE_FLATPAK)
     {
-      gchar *resolved;
       SrtVulkanIcd *other;
 
       iter = icds;
@@ -1108,7 +1073,6 @@ test_icd_vulkan (Fixture *f,
       g_assert_cmpstr (srt_vulkan_icd_get_api_version (iter->data), ==, "1.1.102");
       resolved = srt_vulkan_icd_resolve_library_path (iter->data);
       g_assert_cmpstr (resolved, ==, "/usr/lib/i386-linux-gnu/libvulkan_intel.so");
-      g_free (resolved);
 
       iter = iter->next;
       g_assert_nonnull (iter);
@@ -1131,8 +1095,6 @@ test_icd_vulkan (Fixture *f,
     }
   else if (config != NULL && config->icd_mode == ICD_MODE_XDG_DIRS)
     {
-      gchar *resolved;
-
       iter = icds;
       g_assert_nonnull (iter);
       /* We load $XDG_CONFIG_DIRS instead of /etc/xdg */
@@ -1153,7 +1115,6 @@ test_icd_vulkan (Fixture *f,
       g_assert_cmpstr (srt_vulkan_icd_get_api_version (iter->data), ==, "1.2.3");
       resolved = srt_vulkan_icd_resolve_library_path (iter->data);
       g_assert_cmpstr (resolved, ==, "libvulkan_basename.so");
-      g_free (resolved);
 
       iter = iter->next;
       g_assert_nonnull (iter);
@@ -1190,8 +1151,6 @@ test_icd_vulkan (Fixture *f,
     }
   else
     {
-      gchar *resolved;
-
       iter = icds;
       g_assert_nonnull (iter);
       g_assert_cmpstr (srt_vulkan_icd_get_json_path (iter->data), ==,
@@ -1221,7 +1180,6 @@ test_icd_vulkan (Fixture *f,
       g_assert_cmpstr (srt_vulkan_icd_get_api_version (iter->data), ==, "1.1.102");
       resolved = srt_vulkan_icd_resolve_library_path (iter->data);
       g_assert_cmpstr (resolved, ==, "/usr/lib/i386-linux-gnu/libvulkan_intel.so");
-      g_free (resolved);
 
       iter = iter->next;
       g_assert_nonnull (iter);
@@ -1242,8 +1200,6 @@ test_icd_vulkan (Fixture *f,
       iter = iter->next;
       g_assert_null (iter);
     }
-
-  g_list_free_full (icds, g_object_unref);
 }
 
 typedef struct
@@ -1677,9 +1633,9 @@ check_paths_are_absolute (const GList *list,
                           SrtGraphicsModule module)
 {
   const gchar *library_path = NULL;
-  gchar *absolute_path = NULL;
   for (const GList *iter = list; iter != NULL; iter = iter->next)
     {
+      g_autofree gchar *absolute_path = NULL;
       switch (module)
         {
           case SRT_GRAPHICS_DRI_MODULE:
@@ -1705,7 +1661,6 @@ check_paths_are_absolute (const GList *list,
       g_assert_cmpstr (library_path, ==, absolute_path);
       g_assert_nonnull (library_path);
       g_assert_cmpint (library_path[0], ==, '/');
-      g_free (absolute_path);
     }
 }
 
@@ -1714,10 +1669,10 @@ check_paths_are_relative (const GList *list,
                           SrtGraphicsModule module)
 {
   const gchar *library_path = NULL;
-  gchar *absolute_path = NULL;
   gsize i = 0;
   for (const GList *iter = list; iter != NULL; iter = iter->next, i++)
     {
+      g_autofree gchar *absolute_path = NULL;
       switch (module)
         {
           case SRT_GRAPHICS_DRI_MODULE:
@@ -1746,7 +1701,6 @@ check_paths_are_relative (const GList *list,
       g_assert_cmpint (library_path[0], !=, '/');
       g_assert_cmpint (absolute_path[0], ==, '/');
       assert_same_file (library_path, absolute_path);
-      g_free (absolute_path);
     }
 }
 
@@ -1754,9 +1708,9 @@ static void
 test_dri_debian10 (Fixture *f,
                    gconstpointer context)
 {
-  SrtSystemInfo *info;
-  gchar **envp;
-  gchar *sysroot;
+  g_autoptr(SrtSystemInfo) info = NULL;
+  g_auto(GStrv) envp = NULL;
+  g_autofree gchar *sysroot = NULL;
   GList *dri;
   GList *va_api;
   const gchar *multiarch_tuples[] = {"i386-mock-debian", "x86_64-mock-debian", NULL};
@@ -1830,19 +1784,15 @@ test_dri_debian10 (Fixture *f,
   va_api = srt_system_info_list_va_api_drivers (info, multiarch_tuples[1], SRT_DRIVER_FLAGS_INCLUDE_ALL);
   check_list_suffixes (va_api, va_api_suffixes_x86_64, SRT_GRAPHICS_VAAPI_MODULE);
   g_list_free_full (va_api, g_object_unref);
-
-  g_object_unref (info);
-  g_free (sysroot);
-  g_strfreev (envp);
 }
 
 static void
 test_dri_fedora (Fixture *f,
                  gconstpointer context)
 {
-  SrtSystemInfo *info;
-  gchar **envp;
-  gchar *sysroot;
+  g_autoptr(SrtSystemInfo) info = NULL;
+  g_auto(GStrv) envp = NULL;
+  g_autofree gchar *sysroot = NULL;
   GList *dri;
   GList *va_api;
   const gchar *multiarch_tuples[] = {"i386-mock-fedora", "x86_64-mock-fedora", NULL};
@@ -1886,19 +1836,15 @@ test_dri_fedora (Fixture *f,
   va_api = srt_system_info_list_va_api_drivers (info, multiarch_tuples[1], SRT_DRIVER_FLAGS_NONE);
   check_list_suffixes (va_api, va_api_suffixes_64, SRT_GRAPHICS_VAAPI_MODULE);
   g_list_free_full (va_api, g_object_unref);
-
-  g_object_unref (info);
-  g_free (sysroot);
-  g_strfreev (envp);
 }
 
 static void
 test_dri_ubuntu16 (Fixture *f,
                    gconstpointer context)
 {
-  SrtSystemInfo *info;
-  gchar **envp;
-  gchar *sysroot;
+  g_autoptr(SrtSystemInfo) info = NULL;
+  g_auto(GStrv) envp = NULL;
+  g_autofree gchar *sysroot = NULL;
   GList *dri;
   GList *va_api;
   const gchar *multiarch_tuples[] = {"mock-ubuntu-64-bit", NULL};
@@ -1937,27 +1883,23 @@ test_dri_ubuntu16 (Fixture *f,
   va_api = srt_system_info_list_va_api_drivers (info, multiarch_tuples[0], SRT_DRIVER_FLAGS_INCLUDE_ALL);
   check_list_suffixes (va_api, va_api_suffixes, SRT_GRAPHICS_VAAPI_MODULE);
   g_list_free_full (va_api, g_object_unref);
-
-  g_object_unref (info);
-  g_free (sysroot);
-  g_strfreev (envp);
 }
 
 static void
 test_dri_with_env (Fixture *f,
                    gconstpointer context)
 {
-  SrtSystemInfo *info;
-  gchar **envp;
-  gchar *sysroot;
-  gchar *libgl;
-  gchar *libgl2;
-  gchar *libgl3;
-  gchar *libva;
-  gchar *libva2;
-  gchar *libva3;
-  gchar *libgl_combined;
-  gchar *libva_combined;
+  g_autoptr(SrtSystemInfo) info = NULL;
+  g_auto(GStrv) envp = NULL;
+  g_autofree gchar *sysroot = NULL;
+  g_autofree gchar *libgl = NULL;
+  g_autofree gchar *libgl2 = NULL;
+  g_autofree gchar *libgl3 = NULL;
+  g_autofree gchar *libva = NULL;
+  g_autofree gchar *libva2 = NULL;
+  g_autofree gchar *libva3 = NULL;
+  g_autofree gchar *libgl_combined = NULL;
+  g_autofree gchar *libva_combined = NULL;
   GList *dri;
   GList *va_api;
   const gchar *multiarch_tuples[] = {"i386-mock-fedora", NULL};
@@ -2069,29 +2011,17 @@ test_dri_with_env (Fixture *f,
   check_list_suffixes (va_api, va_api_suffixes, SRT_GRAPHICS_VAAPI_MODULE);
   check_paths_are_relative (va_api, SRT_GRAPHICS_VAAPI_MODULE);
   g_list_free_full (va_api, g_object_unref);
-
-  g_object_unref (info);
-  g_free (sysroot);
-  g_free (libgl_combined);
-  g_free (libva_combined);
-  g_free (libgl);
-  g_free (libgl2);
-  g_free (libgl3);
-  g_free (libva);
-  g_free (libva2);
-  g_free (libva3);
-  g_strfreev (envp);
 }
 
 static void
 test_dri_flatpak (Fixture *f,
                   gconstpointer context)
 {
-  SrtSystemInfo *info;
-  gchar **envp;
-  gchar *sysroot;
-  GList *dri;
-  GList *va_api;
+  g_autoptr(SrtSystemInfo) info;
+  g_auto(GStrv) envp = NULL;
+  g_autofree gchar *sysroot = NULL;
+  g_autoptr(SrtObjectList) dri = NULL;
+  g_autoptr(SrtObjectList) va_api = NULL;
   const gchar *multiarch_tuples[] = { "mock-abi", NULL };
   const gchar *dri_suffixes[] = {"/usr/lib/mock-abi/GL/lib/dri/i965_dri.so",
                                   NULL};
@@ -2113,15 +2043,9 @@ test_dri_flatpak (Fixture *f,
 
   dri = srt_system_info_list_dri_drivers (info, multiarch_tuples[0], SRT_DRIVER_FLAGS_NONE);
   check_list_suffixes (dri, dri_suffixes, SRT_GRAPHICS_DRI_MODULE);
-  g_list_free_full (dri, g_object_unref);
 
   va_api = srt_system_info_list_va_api_drivers (info, multiarch_tuples[0], SRT_DRIVER_FLAGS_NONE);
   check_list_suffixes (va_api, va_api_suffixes, SRT_GRAPHICS_VAAPI_MODULE);
-  g_list_free_full (va_api, g_object_unref);
-
-  g_object_unref (info);
-  g_free (sysroot);
-  g_strfreev (envp);
 }
 
 typedef struct
@@ -2266,12 +2190,12 @@ test_vdpau (Fixture *f,
   for (gsize i = 0; i < G_N_ELEMENTS (vdpau_test); i++)
     {
       const VdpauTest *test = &vdpau_test[i];
-      SrtSystemInfo *info;
-      gchar **envp;
-      gchar *sysroot;
-      gchar *ld_library_path = NULL;
-      gchar *vdpau_path = NULL;
-      gchar *vdpau_relative_path = NULL;
+      g_autoptr(SrtSystemInfo) info = NULL;
+      g_auto(GStrv) envp = NULL;
+      g_autofree gchar *sysroot = NULL;
+      g_autofree gchar *ld_library_path = NULL;
+      g_autofree gchar *vdpau_path = NULL;
+      g_autofree gchar *vdpau_relative_path = NULL;
       GList *vdpau;
 
       g_test_message ("%s: %s", test->sysroot, test->description);
@@ -2345,16 +2269,21 @@ test_vdpau (Fixture *f,
           check_paths_are_relative (vdpau, SRT_GRAPHICS_VDPAU_MODULE);
           g_list_free_full (vdpau, g_object_unref);
         }
-
-      g_object_unref (info);
-      g_free (sysroot);
-      g_free (ld_library_path);
-      g_free (vdpau_path);
-      g_free (vdpau_relative_path);
-      g_strfreev (envp);
     }
 }
 
+typedef struct
+{
+  SrtGraphicsIssues issues;
+  const gchar *name;
+  const gchar *api_version;
+  const gchar *driver_version;
+  const gchar *vendor_id;
+  const gchar *device_id;
+  const gchar *messages;
+  SrtVkPhysicalDeviceType type;
+} GraphicsDeviceTest;
+
 typedef struct
 {
   const gchar *description;
@@ -2367,6 +2296,8 @@ typedef struct
   const gchar *renderer_string;
   const gchar *version_string;
   const gchar *messages;
+  /* Arbitrary size, increase it if necessary */
+  GraphicsDeviceTest devices[4];
   int exit_status;
   gboolean vendor_neutral;
 } GraphicsTest;
@@ -2465,6 +2396,27 @@ static const GraphicsTest graphics_test[] =
     .multiarch_tuple = "mock-good",
     .renderer_string = SRT_TEST_GOOD_GRAPHICS_RENDERER,
     .version_string = SRT_TEST_GOOD_VULKAN_VERSION,
+    .messages = SRT_TEST_GOOD_VULKAN_MESSAGES,
+    .devices =
+    {
+      {
+        .name = SRT_TEST_GOOD_GRAPHICS_RENDERER,
+        .api_version = SRT_TEST_GOOD_GRAPHICS_API_VERSION,
+        .driver_version = SRT_TEST_GOOD_GRAPHICS_DRIVER_VERSION,
+        .vendor_id = SRT_TEST_GOOD_GRAPHICS_VENDOR_ID,
+        .device_id = SRT_TEST_GOOD_GRAPHICS_DEVICE_ID,
+        .type = SRT_VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU,
+      },
+      {
+        .name = SRT_TEST_SOFTWARE_GRAPHICS_RENDERER,
+        .api_version = SRT_TEST_SOFTWARE_GRAPHICS_API_VERSION,
+        .driver_version = SRT_TEST_SOFTWARE_GRAPHICS_DRIVER_VERSION,
+        .vendor_id = SRT_TEST_SOFTWARE_GRAPHICS_VENDOR_ID,
+        .device_id = SRT_TEST_SOFTWARE_GRAPHICS_DEVICE_ID,
+        .type = SRT_VK_PHYSICAL_DEVICE_TYPE_CPU,
+        .issues = SRT_GRAPHICS_ISSUES_SOFTWARE_RENDERING,
+      },
+    },
     .vendor_neutral = TRUE,
   },
 
@@ -2472,23 +2424,46 @@ static const GraphicsTest graphics_test[] =
     .description = "bad vulkan",
     .window_system = SRT_WINDOW_SYSTEM_X11,
     .rendering_interface = SRT_RENDERING_INTERFACE_VULKAN,
-    .issues = SRT_GRAPHICS_ISSUES_CANNOT_LOAD,
+    .issues = SRT_GRAPHICS_ISSUES_CANNOT_LOAD | SRT_GRAPHICS_ISSUES_CANNOT_DRAW,
     .multiarch_tuple = "mock-bad",
-    .messages = "/build/vulkan-tools/src/Vulkan-Tools-1.1.114/vulkaninfo/vulkaninfo.c:5884: failed with VK_ERROR_INITIALIZATION_FAILED\n",
+    .messages = SRT_TEST_BAD_VULKAN_MESSAGES,
     .exit_status = 1,
     .vendor_neutral = TRUE,
   },
 
   {
-    .description = "good vulkan driver but check-vulkan failure",
+    .description = "good vulkan driver but drawing test fails",
     .window_system = SRT_WINDOW_SYSTEM_X11,
     .rendering_interface = SRT_RENDERING_INTERFACE_VULKAN,
     .issues = SRT_GRAPHICS_ISSUES_CANNOT_DRAW,
     .multiarch_tuple = "mock-mixed",
     .renderer_string = SRT_TEST_GOOD_GRAPHICS_RENDERER,
     .version_string = SRT_TEST_GOOD_VULKAN_VERSION,
-    .messages = "failed to create window surface!\n",
+    .messages = SRT_TEST_GOOD_VULKAN_MESSAGES,
     .exit_status = 1,
+    .devices =
+    {
+      {
+        .name = SRT_TEST_GOOD_GRAPHICS_RENDERER,
+        .api_version = SRT_TEST_GOOD_GRAPHICS_API_VERSION,
+        .driver_version = SRT_TEST_GOOD_GRAPHICS_DRIVER_VERSION,
+        .vendor_id = SRT_TEST_GOOD_GRAPHICS_VENDOR_ID,
+        .device_id = SRT_TEST_GOOD_GRAPHICS_DEVICE_ID,
+        .type = SRT_VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU,
+        .messages = SRT_TEST_MIXED_VULKAN_MESSAGES_1,
+        .issues = SRT_GRAPHICS_ISSUES_CANNOT_DRAW,
+      },
+      {
+        .name = SRT_TEST_SOFTWARE_GRAPHICS_RENDERER,
+        .api_version = SRT_TEST_SOFTWARE_GRAPHICS_API_VERSION,
+        .driver_version = SRT_TEST_SOFTWARE_GRAPHICS_DRIVER_VERSION,
+        .vendor_id = SRT_TEST_SOFTWARE_GRAPHICS_VENDOR_ID,
+        .device_id = SRT_TEST_SOFTWARE_GRAPHICS_DEVICE_ID,
+        .type = SRT_VK_PHYSICAL_DEVICE_TYPE_CPU,
+        .messages = SRT_TEST_MIXED_VULKAN_MESSAGES_2,
+        .issues = SRT_GRAPHICS_ISSUES_CANNOT_DRAW | SRT_GRAPHICS_ISSUES_SOFTWARE_RENDERING,
+      },
+    },
     .vendor_neutral = TRUE,
   },
 
@@ -2521,20 +2496,23 @@ test_check_graphics (Fixture *f,
   for (gsize i = 0; i < G_N_ELEMENTS (graphics_test); i++)
     {
       const GraphicsTest *test = &graphics_test[i];
-      SrtGraphics *graphics = NULL;
+      g_autoptr(SrtGraphics) graphics = NULL;
+      g_autoptr(SrtObjectList) devices = NULL;
       SrtGraphicsIssues issues;
-      gchar *tuple;
-      gchar *renderer;
-      gchar *version;
-      gchar *messages;
+      g_autofree gchar *tuple = NULL;
+      g_autofree gchar *renderer = NULL;
+      g_autofree gchar *version = NULL;
+      g_autofree gchar *messages = NULL;
       int exit_status;
       int terminating_signal;
       gboolean vendor_neutral;
       SrtGraphicsLibraryVendor library_vendor;
+      GList *iter;
+      gsize j;
 
       g_test_message ("%s", test->description);
 
-      SrtSystemInfo *info = srt_system_info_new (NULL);
+      g_autoptr(SrtSystemInfo) info = srt_system_info_new (NULL);
       srt_system_info_set_helpers_path (info, f->builddir);
       srt_system_info_set_test_flags (info, test->test_flags);
 
@@ -2550,6 +2528,50 @@ test_check_graphics (Fixture *f,
       g_assert_cmpint (srt_graphics_get_exit_status (graphics), ==, test->exit_status);
       g_assert_cmpint (srt_graphics_get_terminating_signal (graphics), ==, 0);
 
+      devices = srt_graphics_get_devices (graphics);
+      for (j = 0, iter = devices; iter != NULL; iter = iter->next, j++)
+        {
+          g_autofree gchar *name = NULL;
+          g_autofree gchar *api_version = NULL;
+          g_autofree gchar *driver_version = NULL;
+          g_autofree gchar *vendor_id = NULL;
+          g_autofree gchar *device_id = NULL;
+          SrtVkPhysicalDeviceType type;
+
+          g_assert_cmpstr (srt_graphics_device_get_name (iter->data), ==, test->devices[j].name);
+          g_assert_cmpstr (srt_graphics_device_get_api_version (iter->data), ==,
+                           test->devices[j].api_version);
+          g_assert_cmpstr (srt_graphics_device_get_driver_version (iter->data), ==,
+                           test->devices[j].driver_version);
+          g_assert_cmpstr (srt_graphics_device_get_vendor_id (iter->data), ==,
+                           test->devices[j].vendor_id);
+          g_assert_cmpstr (srt_graphics_device_get_device_id (iter->data), ==,
+                           test->devices[j].device_id);
+          g_assert_cmpint (srt_graphics_device_get_device_type (iter->data), ==,
+                           test->devices[j].type);
+          g_assert_cmpstr (srt_graphics_device_get_messages (iter->data), ==,
+                           test->devices[j].messages);
+          g_assert_cmpint (srt_graphics_device_get_issues (iter->data), ==,
+                           test->devices[j].issues);
+
+          g_object_get (iter->data,
+                        "name", &name,
+                        "api-version", &api_version,
+                        "driver-version", &driver_version,
+                        "vendor-id", &vendor_id,
+                        "device-id", &device_id,
+                        "type", &type,
+                        "issues", &issues,
+                        NULL);
+          g_assert_cmpstr (name, ==, test->devices[j].name);
+          g_assert_cmpstr (api_version, ==, test->devices[j].api_version);
+          g_assert_cmpstr (driver_version, ==, test->devices[j].driver_version);
+          g_assert_cmpstr (vendor_id, ==, test->devices[j].vendor_id);
+          g_assert_cmpstr (device_id, ==, test->devices[j].device_id);
+          g_assert_cmpint (type, ==, test->devices[j].type);
+          g_assert_cmpint (issues, ==, test->devices[j].issues);
+        }
+
       vendor_neutral = srt_graphics_library_is_vendor_neutral (graphics, &library_vendor);
       g_assert_cmpint (library_vendor, ==, test->library_vendor);
       g_assert_cmpint (vendor_neutral, ==, test->vendor_neutral);
@@ -2570,13 +2592,6 @@ test_check_graphics (Fixture *f,
       g_assert_cmpstr (messages, ==, test->messages);
       g_assert_cmpint (exit_status, ==, test->exit_status);
       g_assert_cmpint (terminating_signal, ==, 0);
-      g_free (tuple);
-      g_free (renderer);
-      g_free (version);
-      g_free (messages);
-
-      g_object_unref (graphics);
-      g_object_unref (info);
     }
 }
 
@@ -2590,9 +2605,9 @@ static void
 test_glx_debian (Fixture *f,
                  gconstpointer context)
 {
-  SrtSystemInfo *info;
-  gchar **envp;
-  gchar *sysroot;
+  g_autoptr(SrtSystemInfo) info = NULL;
+  g_auto(GStrv) envp = NULL;
+  g_autofree gchar *sysroot = NULL;
   GList *glx;
   const gchar *multiarch_tuples[] = {"i386-mock-debian", "x86_64-mock-debian", NULL};
   const gchar *glx_suffixes_i386[] = {"libGLX_mesa.so.0",
@@ -2630,19 +2645,15 @@ test_glx_debian (Fixture *f,
   check_list_suffixes (glx, glx_suffixes_x86_64, SRT_GRAPHICS_GLX_MODULE);
   check_list_links (glx, glx_paths_x86_64, SRT_GRAPHICS_GLX_MODULE);
   g_list_free_full (glx, g_object_unref);
-
-  g_object_unref (info);
-  g_free (sysroot);
-  g_strfreev (envp);
 }
 
 static void
 test_glx_container (Fixture *f,
                     gconstpointer context)
 {
-  SrtSystemInfo *info;
-  gchar **envp;
-  gchar *sysroot;
+  g_autoptr(SrtSystemInfo) info = NULL;
+  g_auto(GStrv) envp = NULL;
+  g_autofree gchar *sysroot = NULL;
   GList *glx;
   const gchar *multiarch_tuples[] = {"i386-mock-container", "x86_64-mock-container", NULL};
   const gchar *glx_suffixes_i386[] = {"libGLX_nvidia.so.0",
@@ -2680,10 +2691,6 @@ test_glx_container (Fixture *f,
   check_list_suffixes (glx, glx_suffixes_x86_64, SRT_GRAPHICS_GLX_MODULE);
   check_list_links (glx, glx_paths_x86_64, SRT_GRAPHICS_GLX_MODULE);
   g_list_free_full (glx, g_object_unref);
-
-  g_object_unref (info);
-  g_free (sysroot);
-  g_strfreev (envp);
 }
 
 static const Config dir_config = { ICD_MODE_EXPLICIT_DIRS };
diff --git a/tests/json-report/full-good-report.json b/tests/json-report/full-good-report.json
index 05b01afda1950c36f802cd68bc62394135c8e538..223105bec5fc0834c6b421a2ea6b856e0bfaefaf 100644
--- a/tests/json-report/full-good-report.json
+++ b/tests/json-report/full-good-report.json
@@ -71,7 +71,8 @@
             "cannot-load",
             "cannot-draw"
           ],
-          "exit-status" : 1
+          "exit-status" : 1,
+          "devices" : []
         },
         "x11/vdpau" : {
           "renderer" : "G3DVL VDPAU Driver Shared Library version 1.0\n",
@@ -137,8 +138,26 @@
       ],
       "graphics-details" : {
         "x11/vulkan" : {
-          "renderer" : "AMD Radeon RX 5700 XT",
-          "version" : "1.2.136 (device 1002:731f) (driver 2.0.140)"
+          "renderer" : "AMD RADV NAVI10 (ACO)",
+          "version" : "1.2.145 (device 0x1002:0x731f) (driver 20.3.3)",
+          "devices" : [
+            {
+              "name" : "AMD RADV NAVI10 (ACO)",
+              "api-version" : "1.2.145",
+              "driver-version" : "20.3.3",
+              "vendor-id" : "0x1002",
+              "device-id" : "0x731f",
+              "type" : "discrete-gpu"
+            },
+            {
+              "name" : "llvmpipe (LLVM 8.0, 256 bits)",
+              "api-version" : "1.0.2",
+              "driver-version" : "0.0.1",
+              "vendor-id" : "0x10005",
+              "device-id" : "0",
+              "type" : "cpu"
+            }
+          ]
         },
         "x11/vdpau" : {
           "renderer" : "G3DVL VDPAU Driver Shared Library version 1.0\n",
diff --git a/tests/json-report/partial-report.json b/tests/json-report/partial-report.json
index d843f8237787346bd7a777c93c926c8935d11604..a76188cefe1f6777f7d75fd3ec4971aa6fab6211 100644
--- a/tests/json-report/partial-report.json
+++ b/tests/json-report/partial-report.json
@@ -17,6 +17,24 @@
         "error": "We just don't know"
       },
       "graphics-details" : {
+        "x11/vulkan" : {
+          "renderer" : "AMD RADV NAVI10 (ACO)",
+          "version" : "1.2.145 (device 0x1002:0x731f) (driver 20.3.3)",
+          "devices" : [
+            {
+              "name" : "AMD RADV NAVI10 (ACO)",
+              "api-version" : "1.2.145",
+              "driver-version" : "20.3.3",
+              "type" : "discrete-gpu",
+              "messages" : [
+                "vkWaitForFences (dev_props->device, 1, &dev_props->in_flight_fences[dev_props->current_frame], VK_TRUE, 1) failed: TIMEOUT (2)"
+              ],
+              "issues" : [
+                "cannot-draw"
+              ]
+            }
+          ]
+        },
         "x11/vdpau" : {
           "renderer" : "G3DVL VDPAU Driver Shared Library version 1.0\n",
           "library-vendor" : "futuristic"
diff --git a/tests/meson.build b/tests/meson.build
index f395896ec7c83161a2ae6e1e18b6afffd733f7ba..a6b4f79dea839308be9cd30ceb09412129d5f9c6 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -125,7 +125,6 @@ install_data('generate-sysroots.py', install_dir : tests_dir)
 # These are all the same: they just exit 0.
 foreach helper : [
   'mock-good-check-gl',
-  'mock-good-check-vulkan',
   'mock-software-check-gl',
   'mock-true',
 ]
@@ -264,14 +263,14 @@ foreach helper : [
   'mock-bad-check-vdpau',
   'mock-bad-check-vulkan',
   'mock-bad-check-xdg-portal',
-  'mock-bad-vulkaninfo',
   'mock-good-check-va-api',
   'mock-good-check-vdpau',
+  'mock-good-check-vulkan',
   'mock-good-check-xdg-portal',
   'mock-good-flatpak-check-xdg-portal',
-  'mock-good-vulkaninfo',
   'mock-hanging-check-xdg-portal',
   'mock-hanging-wflinfo',
+  'mock-mixed-check-vulkan',
   'mock-sigusr-wflinfo',
 ]
   executable(
@@ -286,13 +285,6 @@ endforeach
 # These need to be the same as their -good- counterparts, to exercise
 # the case where the preliminary check succeeds, but actually rendering
 # a window fails (hence "mixed").
-executable(
-  'mock-mixed-vulkaninfo',
-  'mock-good-vulkaninfo.c',
-  include_directories : project_include_dirs,
-  install: true,
-  install_dir: tests_dir
-)
 executable(
   'mock-mixed-wflinfo',
   'mock-good-wflinfo.c',
@@ -302,20 +294,15 @@ executable(
   install_dir: tests_dir
 )
 
-# These are all essentially the same as mock-bad-check-vulkan: they
-# fail with a message on stderr.
-foreach helper : [
+# mock-bad-check-gl is essentially the same as mock-bad-check-vulkan:
+# it fails with a message on stderr.
+executable(
   'mock-bad-check-gl',
-  'mock-mixed-check-vulkan',
-]
-  executable(
-    helper,
-    'mock-bad-check-vulkan.c',
-    include_directories : project_include_dirs,
-    install: true,
-    install_dir: tests_dir
-  )
-endforeach
+  'mock-bad-check-vulkan.c',
+  include_directories : project_include_dirs,
+  install: true,
+  install_dir: tests_dir
+)
 
 # Helpers with a GLib dependency and one source file of the same name
 # as the helper itself.
diff --git a/tests/mock-bad-check-vulkan.c b/tests/mock-bad-check-vulkan.c
index 3e3fc6050282bc0680a9269f5e47c7dd6bc4283c..c0786bfd490b9a9490dd96f4e413cd03a28a3bab 100644
--- a/tests/mock-bad-check-vulkan.c
+++ b/tests/mock-bad-check-vulkan.c
@@ -1,5 +1,5 @@
 /*
- * Copyright © 2019 Collabora Ltd.
+ * Copyright © 2019-2021 Collabora Ltd.
  *
  * SPDX-License-Identifier: MIT
  *
@@ -25,12 +25,13 @@
 
 #include <stdio.h>
 
+#include "../steam-runtime-tools/graphics-test-defines.h"
+
 int
 main (int argc,
       char **argv)
 {
   // Give bad output
-  fprintf (stderr, "failed to create window surface!\n");
+  fprintf (stderr, SRT_TEST_BAD_VULKAN_MESSAGES);
   return 1;
 }
-
diff --git a/tests/mock-good-check-vulkan.c b/tests/mock-good-check-vulkan.c
new file mode 100644
index 0000000000000000000000000000000000000000..f002d89b5eb14cda36d6c237be2095bbfbf432d0
--- /dev/null
+++ b/tests/mock-good-check-vulkan.c
@@ -0,0 +1,60 @@
+/*
+ * Copyright © 2021 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 <stdio.h>
+
+#include "../steam-runtime-tools/graphics-test-defines.h"
+
+int
+main (int argc,
+      char **argv)
+{
+  // Give good output
+  fprintf (stderr, SRT_TEST_GOOD_VULKAN_MESSAGES);
+
+  printf ("{\"device-info\":{"
+          "\"device-name\":\"" SRT_TEST_GOOD_GRAPHICS_RENDERER "\","
+          "\"device-type\":2,"
+          "\"api-version\":\"" SRT_TEST_GOOD_GRAPHICS_API_VERSION "\","
+          "\"driver-version\":\"" SRT_TEST_GOOD_GRAPHICS_DRIVER_VERSION "\","
+          "\"vendor-id\":\"" SRT_TEST_GOOD_GRAPHICS_VENDOR_ID "\","
+          "\"device-id\":\"" SRT_TEST_GOOD_GRAPHICS_DEVICE_ID "\"}}\n"
+          "{\"device-info\":{"
+          "\"device-name\":\"" SRT_TEST_SOFTWARE_GRAPHICS_RENDERER "\","
+          "\"device-type\":4,"
+          "\"api-version\":\"" SRT_TEST_SOFTWARE_GRAPHICS_API_VERSION "\","
+          "\"driver-version\":\"" SRT_TEST_SOFTWARE_GRAPHICS_DRIVER_VERSION "\","
+          "\"vendor-id\":\"" SRT_TEST_SOFTWARE_GRAPHICS_VENDOR_ID "\","
+          "\"device-id\":\"" SRT_TEST_SOFTWARE_GRAPHICS_DEVICE_ID "\"}}\n"
+          "{\"test\":{"
+          "\"index\":0,"
+          "\"can-draw\":true}}\n"
+          "{\"test\":{"
+          "\"index\":1,"
+          "\"can-draw\":true}}\n");
+
+  return 0;
+}
+
diff --git a/tests/mock-good-vulkaninfo.c b/tests/mock-good-vulkaninfo.c
deleted file mode 100644
index 26c191bfda7d17cde928c617886b8ea27ff5e866..0000000000000000000000000000000000000000
--- a/tests/mock-good-vulkaninfo.c
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright © 2019 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 <stdio.h>
-
-#include "../steam-runtime-tools/graphics-test-defines.h"
-
-int
-main (int argc,
-      char **argv)
-{
-  // Give good output
-  printf ("{\n\t\"$schema\": \"https://schema.khronos.org/vulkan/devsim_1_0_0.json#\",\n"
-          "\t\"comments\": {\n\t\t\"desc\": \"JSON configuration file describing GPU 0. Generated using the vulkaninfo program.\","
-          "\"vulkanApiVersion\": \"1.1.121\"\n\t},\n\t\"ArrayOfVkLayerProperties\": [\n\t\t{\n"
-          "\t\t\t\"layerName\": \"VK_LAYER_LUNARG_standard_validation\",\n"
-          "\t\t\t\"specVersion\": 4194425,\n"
-          "\t\t\t\"implementationVersion\": 1,\n"
-          "\t\t\t\"description\": \"LunarG Standard Validation Layer\"\n"
-          "\t\t},\n"
-          "\t\t{\n"
-          "\t\t\t\"layerName\": \"VK_LAYER_VALVE_steam_fossilize_32\",\n"
-          "\t\t\t\"specVersion\": 4198473,\n"
-          "\t\t\t\"implementationVersion\": 1,\n"
-          "\t\t\t\"description\": \"Steam Pipeline Caching Layer\"\n"
-          "\t\t},\n"
-          "\t\t{\n"
-          "\t\t\t\"layerName\": \"VK_LAYER_VALVE_steam_fossilize_64\",\n"
-          "\t\t\t\"specVersion\": 4198473,\n"
-          "\t\t\t\"implementationVersion\": 1,\n"
-          "\t\t\t\"description\": \"Steam Pipeline Caching Layer\"\n"
-          "\t\t},\n"
-          "\t\t{\n"
-          "\t\t\t\"layerName\": \"VK_LAYER_VALVE_steam_overlay_32\",\n"
-          "\t\t\t\"specVersion\": 4198473,\n"
-          "\t\t\t\"implementationVersion\": 1,\n"
-          "\t\t\t\"description\": \"Steam Overlay Layer\"\n"
-          "\t\t},\n"
-          "\t\t{\n"
-          "\t\t\t\"layerName\": \"VK_LAYER_VALVE_steam_overlay_64\",\n"
-          "\t\t\t\"specVersion\": 4198473,\n"
-          "\t\t\t\"implementationVersion\": 1,\n"
-          "\t\t\t\"description\": \"Steam Overlay Layer\"\n"
-          "\t\t}\n"
-          "\t],\n"
-          "\t\"VkPhysicalDeviceProperties\": {\n"
-          "\t\t\"apiVersion\": 4198502,\n"
-          "\t\t\"driverVersion\": "
-          SRT_TEST_GOOD_VULKAN_DRIVER_VERSION
-          ",\n"
-          "\t\t\"vendorID\": 32902,\n"
-          "\t\t\"deviceID\": 1042,\n"
-          "\t\t\"deviceType\": 1,\n"
-          "\t\t\"deviceName\": \""
-          SRT_TEST_GOOD_GRAPHICS_RENDERER
-          "\"\n"
-          "\t}\n"
-          "}\n");
-  return 0;
-}
-
diff --git a/tests/mock-mixed-check-vulkan.c b/tests/mock-mixed-check-vulkan.c
new file mode 100644
index 0000000000000000000000000000000000000000..f932d6255eeb431f5d2667c6fe4f05f1597b206f
--- /dev/null
+++ b/tests/mock-mixed-check-vulkan.c
@@ -0,0 +1,61 @@
+/*
+ * Copyright © 2021 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 <stdio.h>
+
+#include "../steam-runtime-tools/graphics-test-defines.h"
+
+int
+main (int argc,
+      char **argv)
+{
+  fprintf (stderr, SRT_TEST_GOOD_VULKAN_MESSAGES);
+
+  printf ("{\"device-info\":{"
+          "\"device-name\":\"" SRT_TEST_GOOD_GRAPHICS_RENDERER "\","
+          "\"device-type\":2,"
+          "\"api-version\":\"" SRT_TEST_GOOD_GRAPHICS_API_VERSION "\","
+          "\"driver-version\":\"" SRT_TEST_GOOD_GRAPHICS_DRIVER_VERSION "\","
+          "\"vendor-id\":\"" SRT_TEST_GOOD_GRAPHICS_VENDOR_ID "\","
+          "\"device-id\":\"" SRT_TEST_GOOD_GRAPHICS_DEVICE_ID "\"}}\n"
+          "{\"device-info\":{"
+          "\"device-name\":\"" SRT_TEST_SOFTWARE_GRAPHICS_RENDERER "\","
+          "\"device-type\":4,"
+          "\"api-version\":\"" SRT_TEST_SOFTWARE_GRAPHICS_API_VERSION "\","
+          "\"driver-version\":\"" SRT_TEST_SOFTWARE_GRAPHICS_DRIVER_VERSION "\","
+          "\"vendor-id\":\"" SRT_TEST_SOFTWARE_GRAPHICS_VENDOR_ID "\","
+          "\"device-id\":\"" SRT_TEST_SOFTWARE_GRAPHICS_DEVICE_ID "\"}}\n"
+          "{\"test\":{"
+          "\"index\":0,"
+          "\"can-draw\":false,"
+          "\"error-message\":\"" SRT_TEST_MIXED_VULKAN_MESSAGES_1 "\"}}\n"
+          "{\"test\":{"
+          "\"index\":1,"
+          "\"can-draw\":false,"
+          "\"error-message\":\"" SRT_TEST_MIXED_VULKAN_MESSAGES_2 "\"}}\n");
+
+  return 1;
+}
+
diff --git a/tests/system-info.c b/tests/system-info.c
index 7dcd72b1a0742993737ba91441636d492463c499..4bcf23de815afa76fcfa7235e3b3b152316ab81e 100644
--- a/tests/system-info.c
+++ b/tests/system-info.c
@@ -25,6 +25,7 @@
 
 #include <libglnx.h>
 
+#include "steam-runtime-tools/graphics-test-defines.h"
 #include <steam-runtime-tools/steam-runtime-tools.h>
 #include "steam-runtime-tools/utils-internal.h"
 
@@ -2448,6 +2449,18 @@ typedef struct
   gboolean is_extra;
 } DriverTest;
 
+typedef struct
+{
+  SrtGraphicsIssues issues;
+  const gchar *name;
+  const gchar *api_version;
+  const gchar *driver_version;
+  const gchar *vendor_id;
+  const gchar *device_id;
+  const gchar *messages;
+  SrtVkPhysicalDeviceType type;
+} GraphicsDeviceTest;
+
 typedef struct
 {
   SrtWindowSystem window_system;
@@ -2457,6 +2470,7 @@ typedef struct
   SrtGraphicsLibraryVendor library_vendor;
   SrtGraphicsIssues issues;
   const gchar *messages;
+  GraphicsDeviceTest devices[4];
   int exit_status;
   int terminating_signal;
   gboolean is_available;
@@ -2722,9 +2736,28 @@ static const JsonTest json_test[] =
           {
             .window_system = SRT_WINDOW_SYSTEM_X11,
             .rendering_interface = SRT_RENDERING_INTERFACE_VULKAN,
-            .renderer = "AMD Radeon RX 5700 XT",
-            .version = "1.2.136 (device 1002:731f) (driver 2.0.140)",
+            .renderer = SRT_TEST_GOOD_GRAPHICS_RENDERER,
+            .version = SRT_TEST_GOOD_VULKAN_VERSION,
             .is_available = TRUE,
+            .devices =
+            {
+              {
+                .name = SRT_TEST_GOOD_GRAPHICS_RENDERER,
+                .api_version = SRT_TEST_GOOD_GRAPHICS_API_VERSION,
+                .driver_version = SRT_TEST_GOOD_GRAPHICS_DRIVER_VERSION,
+                .vendor_id = SRT_TEST_GOOD_GRAPHICS_VENDOR_ID,
+                .device_id = SRT_TEST_GOOD_GRAPHICS_DEVICE_ID,
+                .type = SRT_VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU,
+              },
+              {
+                .name = SRT_TEST_SOFTWARE_GRAPHICS_RENDERER,
+                .api_version = SRT_TEST_SOFTWARE_GRAPHICS_API_VERSION,
+                .driver_version = SRT_TEST_SOFTWARE_GRAPHICS_DRIVER_VERSION,
+                .vendor_id = SRT_TEST_SOFTWARE_GRAPHICS_VENDOR_ID,
+                .device_id = SRT_TEST_SOFTWARE_GRAPHICS_DEVICE_ID,
+                .type = SRT_VK_PHYSICAL_DEVICE_TYPE_CPU,
+              },
+            },
           },
           {
             .window_system = SRT_WINDOW_SYSTEM_X11,
@@ -2962,6 +2995,26 @@ static const JsonTest json_test[] =
         .issues = SRT_LIBRARY_ISSUES_UNKNOWN,
         .graphics =
         {
+          {
+            .window_system = SRT_WINDOW_SYSTEM_X11,
+            .rendering_interface = SRT_RENDERING_INTERFACE_VULKAN,
+            .renderer = SRT_TEST_GOOD_GRAPHICS_RENDERER,
+            .version = SRT_TEST_GOOD_VULKAN_VERSION,
+            .is_available = TRUE,
+            .devices =
+            {
+              {
+                .name = SRT_TEST_GOOD_GRAPHICS_RENDERER,
+                .api_version = SRT_TEST_GOOD_GRAPHICS_API_VERSION,
+                .driver_version = SRT_TEST_GOOD_GRAPHICS_DRIVER_VERSION,
+                .type = SRT_VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU,
+                /* A trailing newline is added by
+                 * _srt_json_object_dup_array_of_lines_member() */
+                .messages = SRT_TEST_MIXED_VULKAN_MESSAGES_2 "\n",
+                .issues = SRT_GRAPHICS_ISSUES_CANNOT_DRAW,
+              },
+            },
+          },
           {
             .window_system = SRT_WINDOW_SYSTEM_X11,
             .rendering_interface = SRT_RENDERING_INTERFACE_VDPAU,
@@ -3240,6 +3293,7 @@ json_parsing (Fixture *f,
       gchar **driver_environment_list;
       gsize j;
       gsize jj;
+      gsize k;
       GList *icds;
       GList *iter;
       GList *desktop_entries;
@@ -3343,6 +3397,7 @@ json_parsing (Fixture *f,
           for (jj = 0; this_arch.graphics[jj].is_available; jj++)
             {
               SrtGraphics *graphics = NULL;
+              g_autoptr(SrtObjectList) devices = NULL;
               SrtGraphicsLibraryVendor library_vendor;
               srt_system_info_check_graphics (info,
                                               multiarch_tuples[j],
@@ -3363,6 +3418,29 @@ json_parsing (Fixture *f,
                                srt_graphics_get_exit_status (graphics));
               g_assert_cmpint (this_arch.graphics[jj].terminating_signal, ==,
                                srt_graphics_get_terminating_signal (graphics));
+
+              devices = srt_graphics_get_devices (graphics);
+              for (k = 0, iter = devices; iter != NULL; iter = iter->next, k++)
+                {
+                  GraphicsDeviceTest this_device_test = this_arch.graphics[jj].devices[k];
+                  g_assert_cmpstr (srt_graphics_device_get_name (iter->data), ==,
+                                   this_device_test.name);
+                  g_assert_cmpstr (srt_graphics_device_get_api_version (iter->data), ==,
+                                   this_device_test.api_version);
+                  g_assert_cmpstr (srt_graphics_device_get_driver_version (iter->data), ==,
+                                   this_device_test.driver_version);
+                  g_assert_cmpstr (srt_graphics_device_get_vendor_id (iter->data), ==,
+                                   this_device_test.vendor_id);
+                  g_assert_cmpstr (srt_graphics_device_get_device_id (iter->data), ==,
+                                   this_device_test.device_id);
+                  g_assert_cmpint (srt_graphics_device_get_device_type (iter->data), ==,
+                                   this_device_test.type);
+                  g_assert_cmpstr (srt_graphics_device_get_messages (iter->data), ==,
+                                   this_device_test.messages);
+                  g_assert_cmpint (srt_graphics_device_get_issues (iter->data), ==,
+                                   this_device_test.issues);
+                }
+
               g_object_unref (graphics);
             }