[Mesa-dev] [PATCH mesa 1/3] radv: Add VK_KHR_display, VK_EXT_direct_mode_display and VK_EXT_acquire_xlib_display [v3]

Keith Packard keithp at keithp.com
Fri Dec 22 03:19:13 UTC 2017


Implements VK_KHR_display, VK_EXT_direct_mode_display and
VK_EXT_acquire_xlib_display using DRM/KMS.

This uses X leases to provide the same API as nVidia, allowing an
application to discover available display resources and acquire one
from the X server using RandR leases.

Because Linux DRM does not allow access to KMS resources through a
render node, applications must use vkGetRandROutputDisplayEXT to
access a suitable VkDisplayKHR object as
vkGetPhysicalDeviceDisplayPropertiesKHR will not be able to see the
required kernel objects.

Contrarywise, nVidia masks off some RandR outputs from X (such as
head-mounted-displays), those are *only* visible through
vkGetPhysicalDeviceDisplayPropertiesKHR. Hence, an application wanting
to work with both systems may need to use both mechanisms to locate
the desired output device.

v2:
	Rework VK_KEITHP_kms_display extension to just use a data
	structure.

v3:
	Restructure to provide KMS access using the existing
	VK_EXT_acquire_xlib_display instead of creating a new
	extension.

	Rebase on more recent mesa master

Signed-off-by: Keith Packard <keithp at keithp.com>
---
 configure.ac                        |   12 +
 src/amd/vulkan/Makefile.am          |   17 +
 src/amd/vulkan/Makefile.sources     |    3 +
 src/amd/vulkan/radv_extensions.py   |    3 +
 src/amd/vulkan/radv_private.h       |    1 +
 src/amd/vulkan/radv_wsi.c           |    3 +-
 src/amd/vulkan/radv_wsi_display.c   |  185 ++++
 src/intel/Makefile.vulkan.am        |   17 +
 src/intel/vulkan/anv_wsi.c          |    3 +-
 src/vulkan/Makefile.am              |   15 +
 src/vulkan/Makefile.sources         |    4 +
 src/vulkan/wsi/wsi_common.c         |   19 +-
 src/vulkan/wsi/wsi_common.h         |    5 +-
 src/vulkan/wsi/wsi_common_display.c | 1782 +++++++++++++++++++++++++++++++++++
 src/vulkan/wsi/wsi_common_display.h |   92 ++
 src/vulkan/wsi/wsi_common_private.h |   10 +
 16 files changed, 2166 insertions(+), 5 deletions(-)
 create mode 100644 src/amd/vulkan/radv_wsi_display.c
 create mode 100644 src/vulkan/wsi/wsi_common_display.c
 create mode 100644 src/vulkan/wsi/wsi_common_display.h

diff --git a/configure.ac b/configure.ac
index 79f275d3914..0666b9d2021 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1494,6 +1494,10 @@ fi
 AC_SUBST([GL_LIB])
 AC_SUBST([OSMESA_LIB])
 
+# Check for RandR leases
+PKG_CHECK_MODULES([RANDRPROTO], [randrproto >= 1.6.0],
+                  [have_xlease=yes], [have_xlease=no])
+
 # Check for libdrm
 PKG_CHECK_MODULES([LIBDRM], [libdrm >= $LIBDRM_REQUIRED],
                   [have_libdrm=yes], [have_libdrm=no])
@@ -1533,6 +1537,7 @@ AM_CONDITIONAL(HAVE_APPLEDRI, test "x$enable_dri" = xyes -a "x$dri_platform" = x
 AM_CONDITIONAL(HAVE_LMSENSORS, test "x$enable_lmsensors" = xyes )
 AM_CONDITIONAL(HAVE_GALLIUM_EXTRA_HUD, test "x$enable_gallium_extra_hud" = xyes )
 AM_CONDITIONAL(HAVE_WINDOWSDRI, test "x$enable_dri" = xyes -a "x$dri_platform" = xwindows )
+AM_CONDITIONAL(HAVE_XLEASE, test "x$have_xlease" = xyes )
 
 AC_ARG_ENABLE([shared-glapi],
     [AS_HELP_STRING([--enable-shared-glapi],
@@ -1832,9 +1837,16 @@ if test x"$enable_dri3" = xyes; then
     PKG_CHECK_MODULES([XCB_DRI3], [$dri3_modules])
 fi
 
+if test x"$have_xlease" = xyes; then
+    DEFINES="$DEFINES -DHAVE_XLEASE"
+    randr_modules="x11-xcb xcb-randr"
+    PKG_CHECK_MODULES([XCB_RANDR], [$randr_modules])
+fi
+
 AM_CONDITIONAL(HAVE_PLATFORM_X11, echo "$platforms" | grep -q 'x11')
 AM_CONDITIONAL(HAVE_PLATFORM_WAYLAND, echo "$platforms" | grep -q 'wayland')
 AM_CONDITIONAL(HAVE_PLATFORM_DRM, echo "$platforms" | grep -q 'drm')
+AM_CONDITIONAL(HAVE_PLATFORM_DISPLAY, echo "$platforms" | grep -q 'drm')
 AM_CONDITIONAL(HAVE_PLATFORM_SURFACELESS, echo "$platforms" | grep -q 'surfaceless')
 AM_CONDITIONAL(HAVE_PLATFORM_ANDROID, echo "$platforms" | grep -q 'android')
 
diff --git a/src/amd/vulkan/Makefile.am b/src/amd/vulkan/Makefile.am
index 6b352aebf98..f80e041fbe7 100644
--- a/src/amd/vulkan/Makefile.am
+++ b/src/amd/vulkan/Makefile.am
@@ -76,6 +76,23 @@ VULKAN_LIB_DEPS = \
 	$(DLOPEN_LIBS) \
 	-lm
 
+if HAVE_PLATFORM_DISPLAY
+AM_CPPFLAGS += \
+	-DVK_USE_PLATFORM_DISPLAY_KHR \
+	-DVK_USE_PLATFORM_XLIB_XRANDR_EXT
+
+VULKAN_SOURCES += $(VULKAN_WSI_DISPLAY_FILES)
+
+if HAVE_PLATFORM_X11
+if HAVE_XLEASE
+AM_CPPFLAGS += \
+	$(XCB_RANDR_CFLAGS)
+VULKAN_LIB_DEPS += $(XCB_RANDR_LIBS)
+endif
+endif
+
+endif
+
 if HAVE_PLATFORM_X11
 AM_CPPFLAGS += \
 	$(XCB_DRI3_CFLAGS) \
diff --git a/src/amd/vulkan/Makefile.sources b/src/amd/vulkan/Makefile.sources
index c9d172c3b1b..4321f7ddea3 100644
--- a/src/amd/vulkan/Makefile.sources
+++ b/src/amd/vulkan/Makefile.sources
@@ -75,6 +75,9 @@ VULKAN_WSI_WAYLAND_FILES := \
 VULKAN_WSI_X11_FILES := \
 	radv_wsi_x11.c
 
+VULKAN_WSI_DISPLAY_FILES := \
+	radv_wsi_display.c
+
 VULKAN_GENERATED_FILES := \
 	radv_entrypoints.c \
 	radv_entrypoints.h \
diff --git a/src/amd/vulkan/radv_extensions.py b/src/amd/vulkan/radv_extensions.py
index 9af941fab35..fc27ff8734c 100644
--- a/src/amd/vulkan/radv_extensions.py
+++ b/src/amd/vulkan/radv_extensions.py
@@ -80,6 +80,9 @@ EXTENSIONS = [
     Extension('VK_KHR_wayland_surface',                   6, 'VK_USE_PLATFORM_WAYLAND_KHR'),
     Extension('VK_KHR_xcb_surface',                       6, 'VK_USE_PLATFORM_XCB_KHR'),
     Extension('VK_KHR_xlib_surface',                      6, 'VK_USE_PLATFORM_XLIB_KHR'),
+    Extension('VK_KHR_display',                           1, 'VK_USE_PLATFORM_DISPLAY_KHR'),
+    Extension('VK_EXT_direct_mode_display',               1, 'VK_USE_PLATFORM_DISPLAY_KHR'),
+    Extension('VK_EXT_acquire_xlib_display',              1, 'VK_USE_PLATFORM_XLIB_XRANDR_EXT'),
     Extension('VK_KHX_multiview',                         1, True),
     Extension('VK_EXT_external_memory_dma_buf',           1, True),
     Extension('VK_EXT_global_priority',                   1, 'device->rad_info.has_ctx_priority'),
diff --git a/src/amd/vulkan/radv_private.h b/src/amd/vulkan/radv_private.h
index 244ab8967b6..bf0643a6a5e 100644
--- a/src/amd/vulkan/radv_private.h
+++ b/src/amd/vulkan/radv_private.h
@@ -73,6 +73,7 @@ typedef uint32_t xcb_window_t;
 #include "radv_entrypoints.h"
 
 #include "wsi_common.h"
+#include "wsi_common_display.h"
 
 #define ATI_VENDOR_ID 0x1002
 
diff --git a/src/amd/vulkan/radv_wsi.c b/src/amd/vulkan/radv_wsi.c
index e016e837102..5ec872a63d0 100644
--- a/src/amd/vulkan/radv_wsi.c
+++ b/src/amd/vulkan/radv_wsi.c
@@ -41,7 +41,8 @@ radv_init_wsi(struct radv_physical_device *physical_device)
 	return wsi_device_init(&physical_device->wsi_device,
 			       radv_physical_device_to_handle(physical_device),
 			       radv_wsi_proc_addr,
-			       &physical_device->instance->alloc);
+			       &physical_device->instance->alloc,
+                               physical_device->local_fd);
 }
 
 void
diff --git a/src/amd/vulkan/radv_wsi_display.c b/src/amd/vulkan/radv_wsi_display.c
new file mode 100644
index 00000000000..6bfce5f37ed
--- /dev/null
+++ b/src/amd/vulkan/radv_wsi_display.c
@@ -0,0 +1,185 @@
+/*
+ * Copyright © 2017 Keith Packard
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission.  The copyright holders make no representations
+ * about the suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+#include <stdbool.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include "radv_private.h"
+#include "radv_cs.h"
+#include "util/disk_cache.h"
+#include "util/strtod.h"
+#include "vk_util.h"
+#include <xf86drm.h>
+#include <xf86drmMode.h>
+#include <amdgpu.h>
+#include <amdgpu_drm.h>
+#include "winsys/amdgpu/radv_amdgpu_winsys_public.h"
+#include "ac_llvm_util.h"
+#include "vk_format.h"
+#include "sid.h"
+#include "util/debug.h"
+#include "wsi_common_display.h"
+
+#define MM_PER_PIXEL     (1.0/96.0 * 25.4)
+
+VkResult
+radv_GetPhysicalDeviceDisplayPropertiesKHR(VkPhysicalDevice             physical_device,
+                                           uint32_t                     *property_count,
+                                           VkDisplayPropertiesKHR       *properties)
+{
+   RADV_FROM_HANDLE(radv_physical_device, pdevice, physical_device);
+
+   return wsi_display_get_physical_device_display_properties(physical_device,
+                                                             &pdevice->wsi_device,
+                                                             property_count,
+                                                             properties);
+}
+
+VkResult
+radv_GetPhysicalDeviceDisplayPlanePropertiesKHR(VkPhysicalDevice                physical_device,
+                                                uint32_t                        *property_count,
+                                                VkDisplayPlanePropertiesKHR     *properties)
+{
+   RADV_FROM_HANDLE(radv_physical_device, pdevice, physical_device);
+
+   return wsi_display_get_physical_device_display_plane_properties(physical_device,
+                                                                   &pdevice->wsi_device,
+                                                                   property_count,
+                                                                   properties);
+}
+
+VkResult
+radv_GetDisplayPlaneSupportedDisplaysKHR(VkPhysicalDevice               physical_device,
+                                         uint32_t                       plane_index,
+                                         uint32_t                       *display_count,
+                                         VkDisplayKHR                   *displays)
+{
+   RADV_FROM_HANDLE(radv_physical_device, pdevice, physical_device);
+
+   return wsi_display_get_display_plane_supported_displays(physical_device,
+                                                           &pdevice->wsi_device,
+                                                           plane_index,
+                                                           display_count,
+                                                           displays);
+}
+
+
+VkResult
+radv_GetDisplayModePropertiesKHR(VkPhysicalDevice               physical_device,
+                                 VkDisplayKHR                   display,
+                                 uint32_t                       *property_count,
+                                 VkDisplayModePropertiesKHR     *properties)
+{
+   RADV_FROM_HANDLE(radv_physical_device, pdevice, physical_device);
+
+   return wsi_display_get_display_mode_properties(physical_device,
+                                                  &pdevice->wsi_device,
+                                                  display,
+                                                  property_count,
+                                                  properties);
+}
+
+VkResult
+radv_CreateDisplayModeKHR(VkPhysicalDevice                      physical_device,
+                          VkDisplayKHR                          display,
+                          const VkDisplayModeCreateInfoKHR      *create_info,
+                          const VkAllocationCallbacks           *allocator,
+                          VkDisplayModeKHR                      *mode)
+{
+   return VK_ERROR_INITIALIZATION_FAILED;
+}
+
+
+VkResult
+radv_GetDisplayPlaneCapabilitiesKHR(VkPhysicalDevice                    physical_device,
+                                    VkDisplayModeKHR                    mode_khr,
+                                    uint32_t                            plane_index,
+                                    VkDisplayPlaneCapabilitiesKHR       *capabilities)
+{
+   RADV_FROM_HANDLE(radv_physical_device, pdevice, physical_device);
+
+   return wsi_get_display_plane_capabilities(physical_device,
+                                             &pdevice->wsi_device,
+                                             mode_khr,
+                                             plane_index,
+                                             capabilities);
+}
+
+VkResult
+radv_ReleaseDisplayEXT(VkPhysicalDevice physical_device,
+                       VkDisplayKHR     display)
+{
+   RADV_FROM_HANDLE(radv_physical_device, pdevice, physical_device);
+
+   return wsi_release_display(physical_device,
+                              &pdevice->wsi_device,
+                              display);
+}
+
+#ifdef VK_USE_PLATFORM_XLIB_XRANDR_EXT
+VkResult
+radv_AcquireXlibDisplayEXT(VkPhysicalDevice     physical_device,
+                           Display              *dpy,
+                           VkDisplayKHR         display)
+{
+   RADV_FROM_HANDLE(radv_physical_device, pdevice, physical_device);
+
+   return wsi_acquire_xlib_display(physical_device,
+                                   &pdevice->wsi_device,
+                                   dpy,
+                                   display);
+}
+
+VkResult
+radv_GetRandROutputDisplayEXT(VkPhysicalDevice  physical_device,
+                              Display           *dpy,
+                              RROutput          output,
+                              VkDisplayKHR      *display)
+{
+   RADV_FROM_HANDLE(radv_physical_device, pdevice, physical_device);
+
+   return wsi_get_randr_output_display(physical_device,
+                                       &pdevice->wsi_device,
+                                       dpy,
+                                       output,
+                                       display);
+}
+
+#endif /* VK_USE_PLATFORM_XLIB_XRANDR_EXT */
+
+VkResult
+radv_CreateDisplayPlaneSurfaceKHR(VkInstance                            _instance,
+                                  const VkDisplaySurfaceCreateInfoKHR   *create_info,
+                                  const VkAllocationCallbacks           *allocator,
+                                  VkSurfaceKHR                          *surface)
+{
+   RADV_FROM_HANDLE(radv_instance, instance, _instance);
+   const VkAllocationCallbacks *alloc;
+
+   if (allocator)
+     alloc = allocator;
+   else
+     alloc = &instance->alloc;
+
+   return wsi_create_display_surface(_instance, alloc, create_info, surface);
+}
diff --git a/src/intel/Makefile.vulkan.am b/src/intel/Makefile.vulkan.am
index 811faab556e..b2559327de2 100644
--- a/src/intel/Makefile.vulkan.am
+++ b/src/intel/Makefile.vulkan.am
@@ -175,6 +175,23 @@ VULKAN_SOURCES += $(VULKAN_WSI_WAYLAND_FILES)
 VULKAN_LIB_DEPS += $(WAYLAND_CLIENT_LIBS)
 endif
 
+if HAVE_PLATFORM_DISPLAY
+VULKAN_CPPFLAGS += \
+	-DVK_USE_PLATFORM_DISPLAY_KHR \
+	-DVK_USE_PLATFORM_XLIB_XRANDR_EXT
+
+VULKAN_SOURCES += $(VULKAN_WSI_DISPLAY_FILES)
+
+if HAVE_PLATFORM_X11
+if HAVE_XLEASE
+VULKAN_CPPFLAGS += \
+	$(XCB_RANDR_CFLAGS)
+VULKAN_LIB_DEPS += $(XCB_RANDR_LIBS)
+endif
+endif
+
+endif
+
 noinst_LTLIBRARIES += vulkan/libvulkan_common.la
 vulkan_libvulkan_common_la_SOURCES = $(VULKAN_SOURCES)
 vulkan_libvulkan_common_la_CFLAGS = $(VULKAN_CFLAGS)
diff --git a/src/intel/vulkan/anv_wsi.c b/src/intel/vulkan/anv_wsi.c
index 6082c3dd093..f86d83589ea 100644
--- a/src/intel/vulkan/anv_wsi.c
+++ b/src/intel/vulkan/anv_wsi.c
@@ -39,7 +39,8 @@ anv_init_wsi(struct anv_physical_device *physical_device)
    return wsi_device_init(&physical_device->wsi_device,
                           anv_physical_device_to_handle(physical_device),
                           anv_wsi_proc_addr,
-                          &physical_device->instance->alloc);
+                          &physical_device->instance->alloc,
+                          physical_device->local_fd);
 }
 
 void
diff --git a/src/vulkan/Makefile.am b/src/vulkan/Makefile.am
index 8766952eafb..4a18e997005 100644
--- a/src/vulkan/Makefile.am
+++ b/src/vulkan/Makefile.am
@@ -54,6 +54,21 @@ AM_CPPFLAGS += \
 VULKAN_WSI_SOURCES += $(VULKAN_WSI_X11_FILES)
 endif
 
+if HAVE_PLATFORM_DISPLAY
+AM_CPPFLAGS += \
+	-DVK_USE_PLATFORM_DISPLAY_KHR
+
+VULKAN_WSI_SOURCES += $(VULKAN_WSI_DISPLAY_FILES)
+
+if HAVE_PLATFORM_X11
+if HAVE_XLEASE
+AM_CPPFLAGS += \
+	-DVK_USE_PLATFORM_XLIB_XRANDR_EXT
+endif
+endif
+
+endif
+
 BUILT_SOURCES += $(VULKAN_WSI_WAYLAND_GENERATED_FILES)
 CLEANFILES = $(BUILT_SOURCES)
 
diff --git a/src/vulkan/Makefile.sources b/src/vulkan/Makefile.sources
index 6e9a09ce78b..2b509d811fd 100644
--- a/src/vulkan/Makefile.sources
+++ b/src/vulkan/Makefile.sources
@@ -17,6 +17,10 @@ VULKAN_WSI_X11_FILES := \
 	wsi/wsi_common_x11.c \
 	wsi/wsi_common_x11.h
 
+VULKAN_WSI_DISPLAY_FILES := \
+	wsi/wsi_common_display.c \
+	wsi/wsi_common_display.h
+
 VULKAN_UTIL_FILES := \
 	util/vk_alloc.h \
 	util/vk_util.c \
diff --git a/src/vulkan/wsi/wsi_common.c b/src/vulkan/wsi/wsi_common.c
index 2de5f154c8a..99f4c607666 100644
--- a/src/vulkan/wsi/wsi_common.c
+++ b/src/vulkan/wsi/wsi_common.c
@@ -29,7 +29,8 @@ VkResult
 wsi_device_init(struct wsi_device *wsi,
                 VkPhysicalDevice pdevice,
                 WSI_FN_GetPhysicalDeviceProcAddr proc_addr,
-                const VkAllocationCallbacks *alloc)
+                const VkAllocationCallbacks *alloc,
+                int device_fd)
 {
    VkResult result;
 
@@ -89,6 +90,19 @@ wsi_device_init(struct wsi_device *wsi,
    }
 #endif
 
+#ifdef VK_USE_PLATFORM_DISPLAY_KHR
+   result = wsi_display_init_wsi(wsi, alloc, pdevice, device_fd);
+   if (result != VK_SUCCESS) {
+#ifdef VK_USE_PLATFORM_WAYLAND_KHR
+      wsi_wayland_finish_wsi(wsi, alloc);
+#endif
+#ifdef VK_USE_PLATFORM_XCB_KHR
+      wsi_x11_finish_wsi(wsi, alloc);
+#endif
+      return result;
+   }
+#endif
+
    return VK_SUCCESS;
 }
 
@@ -96,6 +110,9 @@ void
 wsi_device_finish(struct wsi_device *wsi,
                   const VkAllocationCallbacks *alloc)
 {
+#ifdef VK_USE_PLATFORM_DISPLAY_KHR
+   wsi_display_finish_wsi(wsi, alloc);
+#endif
 #ifdef VK_USE_PLATFORM_WAYLAND_KHR
    wsi_wl_finish_wsi(wsi, alloc);
 #endif
diff --git a/src/vulkan/wsi/wsi_common.h b/src/vulkan/wsi/wsi_common.h
index 3e0d3be1c24..1cb6aaebca0 100644
--- a/src/vulkan/wsi/wsi_common.h
+++ b/src/vulkan/wsi/wsi_common.h
@@ -50,7 +50,7 @@ struct wsi_memory_allocate_info {
 
 struct wsi_interface;
 
-#define VK_ICD_WSI_PLATFORM_MAX 5
+#define VK_ICD_WSI_PLATFORM_MAX 6
 
 struct wsi_device {
    VkPhysicalDeviceMemoryProperties memory_props;
@@ -93,7 +93,8 @@ VkResult
 wsi_device_init(struct wsi_device *wsi,
                 VkPhysicalDevice pdevice,
                 WSI_FN_GetPhysicalDeviceProcAddr proc_addr,
-                const VkAllocationCallbacks *alloc);
+                const VkAllocationCallbacks *alloc,
+                int device_fd);
 
 void
 wsi_device_finish(struct wsi_device *wsi,
diff --git a/src/vulkan/wsi/wsi_common_display.c b/src/vulkan/wsi/wsi_common_display.c
new file mode 100644
index 00000000000..05b019cf957
--- /dev/null
+++ b/src/vulkan/wsi/wsi_common_display.c
@@ -0,0 +1,1782 @@
+/*
+ * Copyright © 2017 Keith Packard
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission.  The copyright holders make no representations
+ * about the suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+#include "util/macros.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <stdbool.h>
+#include <math.h>
+#include <xf86drm.h>
+#include <xf86drmMode.h>
+#ifdef VK_USE_PLATFORM_XLIB_XRANDR_EXT
+#include <xcb/randr.h>
+#include <X11/Xlib-xcb.h>
+#endif
+#include "util/hash_table.h"
+#include "util/list.h"
+
+#include "vk_util.h"
+#include "wsi_common_private.h"
+#include "wsi_common_display.h"
+#include "wsi_common_queue.h"
+
+#if 0
+#define wsi_display_debug(...) fprintf(stderr, __VA_ARGS__)
+#define wsi_display_debug_code(...)     __VA_ARGS__
+#else
+#define wsi_display_debug(...)
+#define wsi_display_debug_code(...)
+#endif
+
+/* These have lifetime equal to the instance, so they effectively
+ * never go away. This means we must keep track of them separately
+ * from all other resources.
+ */
+typedef struct wsi_display_mode {
+   struct list_head             list;
+   struct wsi_display_connector *connector;
+   bool                         valid;          /* was found in most recent poll */
+   bool                         preferred;
+   uint32_t                     clock;
+   uint16_t                     hdisplay, hsync_start, hsync_end, htotal, hskew;
+   uint16_t                     vdisplay, vsync_start, vsync_end, vtotal, vscan;
+   uint32_t                     flags;
+} wsi_display_mode;
+
+typedef struct wsi_display_connector {
+   struct list_head             list;
+   struct wsi_display           *wsi;
+   uint32_t                     id;
+   uint32_t                     crtc_id;
+   char                         *name;
+   bool                         connected;
+   bool                         active;
+   wsi_display_mode             *current_mode;
+   drmModeModeInfo              current_drm_mode;
+#ifdef VK_USE_PLATFORM_XLIB_XRANDR_EXT
+   xcb_randr_output_t           output;
+#endif
+} wsi_display_connector;
+
+struct wsi_display {
+   struct wsi_interface         base;
+
+   const VkAllocationCallbacks  *alloc;
+   VkPhysicalDevice             physical_device;
+
+   const struct wsi_callbacks   *cbs;
+
+   int                          master_fd;
+   int                          render_fd;
+
+   pthread_mutex_t              wait_mutex;
+   pthread_cond_t               wait_cond;
+   pthread_t                    wait_thread;
+
+   struct list_head             connectors;
+
+   struct list_head             display_modes;
+};
+
+enum wsi_image_state {
+   wsi_image_idle = 0,
+   wsi_image_drawing = 1,
+   wsi_image_flipping = 2,
+   wsi_image_displaying = 3
+};
+
+struct wsi_display_image {
+   struct wsi_image             base;
+   struct wsi_display_swapchain *chain;
+   enum wsi_image_state         state;
+   unsigned int                 frame;
+   unsigned int                 sec;
+   unsigned int                 usec;
+   uint32_t                     fb_id;
+};
+
+struct wsi_display_swapchain {
+   struct wsi_swapchain         base;
+   struct wsi_display           *wsi;
+   VkIcdSurfaceDisplay          *surface;
+   struct wsi_display_image     images[0];
+};
+
+ICD_DEFINE_NONDISP_HANDLE_CASTS(wsi_display_mode, VkDisplayModeKHR)
+ICD_DEFINE_NONDISP_HANDLE_CASTS(wsi_display_connector, VkDisplayKHR)
+
+static bool
+wsi_display_mode_matches_drm(wsi_display_mode   *wsi,
+                             drmModeModeInfoPtr drm)
+{
+   return wsi->clock == drm->clock &&
+      wsi->hdisplay == drm->hdisplay &&
+      wsi->hsync_start == drm->hsync_start &&
+      wsi->hsync_end == drm->hsync_end &&
+      wsi->htotal == drm->htotal &&
+      wsi->hskew == drm->hskew &&
+      wsi->vdisplay == drm->vdisplay &&
+      wsi->vsync_start == drm->vsync_start &&
+      wsi->vsync_end == drm->vsync_end &&
+      wsi->vtotal == drm->vtotal &&
+      wsi->vscan == drm->vscan &&
+      wsi->flags == drm->flags;
+}
+
+static bool
+wsi_display_mode_matches_x(struct wsi_display_mode      *wsi,
+                           xcb_randr_mode_info_t        *xcb)
+{
+   return wsi->clock == xcb->dot_clock &&
+      wsi->hdisplay == xcb->width &&
+      wsi->hsync_start == xcb->hsync_start &&
+      wsi->hsync_end == xcb->hsync_end &&
+      wsi->htotal == xcb->htotal &&
+      wsi->hskew == xcb->hskew &&
+      wsi->vdisplay == xcb->height &&
+      wsi->vsync_start == xcb->vsync_start &&
+      wsi->vsync_end == xcb->vsync_end &&
+      wsi->vtotal == xcb->vtotal &&
+      wsi->flags == xcb->mode_flags;
+}
+
+static double
+wsi_display_mode_refresh(struct wsi_display_mode        *wsi)
+{
+   return (double) wsi->clock / ((double) wsi->htotal * (double) wsi->vtotal * (double) (wsi->vscan + 1));
+}
+
+static uint64_t wsi_get_current_realtime(void)
+{
+   struct timespec tv;
+
+   clock_gettime(CLOCK_REALTIME, &tv);
+   return tv.tv_nsec + tv.tv_sec*1000000000ull;
+}
+
+static int
+wsi_display_wait_for_event(struct wsi_display           *wsi,
+                           uint64_t                     timeout_ns);
+
+static struct wsi_display_mode *
+wsi_display_find_drm_mode(struct wsi_device                 *wsi_device,
+                          struct wsi_display_connector      *connector,
+                          drmModeModeInfoPtr                mode)
+{
+   struct wsi_display           *wsi = (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
+   struct wsi_display_mode      *display_mode;
+
+   LIST_FOR_EACH_ENTRY(display_mode, &wsi->display_modes, list) {
+      if (display_mode->connector == connector &&
+          wsi_display_mode_matches_drm(display_mode, mode))
+         return display_mode;
+   }
+   return NULL;
+}
+
+static struct wsi_display_mode *
+wsi_display_find_x_mode(struct wsi_device                 *wsi_device,
+                        struct wsi_display_connector      *connector,
+                        xcb_randr_mode_info_t             *mode)
+{
+   struct wsi_display           *wsi = (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
+   struct wsi_display_mode      *display_mode;
+
+   LIST_FOR_EACH_ENTRY(display_mode, &wsi->display_modes, list) {
+      if (display_mode->connector == connector &&
+          wsi_display_mode_matches_x(display_mode, mode))
+         return display_mode;
+   }
+   return NULL;
+}
+
+static void
+wsi_display_invalidate_connector_modes(struct wsi_device            *wsi_device,
+                                       struct wsi_display_connector *connector)
+{
+   struct wsi_display           *wsi = (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
+   struct wsi_display_mode      *display_mode;
+
+   LIST_FOR_EACH_ENTRY(display_mode, &wsi->display_modes, list)
+      if (display_mode->connector == connector)
+         display_mode->valid = false;
+}
+
+static VkResult
+wsi_display_register_drm_mode(struct wsi_device            *wsi_device,
+                              struct wsi_display_connector *connector,
+                              drmModeModeInfoPtr           drm_mode)
+{
+   struct wsi_display           *wsi = (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
+   struct wsi_display_mode      *display_mode;
+
+   display_mode = wsi_display_find_drm_mode(wsi_device, connector, drm_mode);
+
+   if (display_mode) {
+      display_mode->valid = true;
+      return VK_SUCCESS;
+   }
+
+   display_mode = vk_alloc(wsi->alloc, sizeof (struct wsi_display_mode), 8, VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
+   if (!display_mode)
+      return VK_ERROR_OUT_OF_HOST_MEMORY;
+
+   display_mode->connector = connector;
+   display_mode->valid = true;
+   display_mode->preferred = (drm_mode->type & DRM_MODE_TYPE_PREFERRED) != 0;
+   display_mode->clock = drm_mode->clock;
+   display_mode->hdisplay = drm_mode->hdisplay;
+   display_mode->hsync_start = drm_mode->hsync_start;
+   display_mode->hsync_end = drm_mode->hsync_end;
+   display_mode->htotal = drm_mode->htotal;
+   display_mode->hskew = drm_mode->hskew;
+   display_mode->vdisplay = drm_mode->vdisplay;
+   display_mode->vsync_start = drm_mode->vsync_start;
+   display_mode->vsync_end = drm_mode->vsync_end;
+   display_mode->vtotal = drm_mode->vtotal;
+   display_mode->vscan = drm_mode->vscan;
+   display_mode->flags = drm_mode->flags;
+
+   LIST_ADDTAIL(&display_mode->list, &wsi->display_modes);
+   return VK_SUCCESS;
+}
+
+static VkResult
+wsi_display_register_x_mode(struct wsi_device                   *wsi_device,
+                            struct wsi_display_connector        *connector,
+                            xcb_randr_mode_info_t               *x_mode,
+                            bool                                preferred)
+{
+   struct wsi_display           *wsi = (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
+   struct wsi_display_mode      *display_mode;
+
+   display_mode = wsi_display_find_x_mode(wsi_device, connector, x_mode);
+
+   if (display_mode) {
+      display_mode->valid = true;
+      return VK_SUCCESS;
+   }
+
+   display_mode = vk_alloc(wsi->alloc, sizeof (struct wsi_display_mode), 8, VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
+   if (!display_mode)
+      return VK_ERROR_OUT_OF_HOST_MEMORY;
+
+   display_mode->connector = connector;
+   display_mode->valid = true;
+   display_mode->preferred = preferred;
+   display_mode->clock = x_mode->dot_clock / 1000;
+   display_mode->hdisplay = x_mode->width;
+   display_mode->hsync_start = x_mode->hsync_start;
+   display_mode->hsync_end = x_mode->hsync_end;
+   display_mode->htotal = x_mode->htotal;
+   display_mode->hskew = x_mode->hskew;
+   display_mode->vdisplay = x_mode->height;
+   display_mode->vsync_start = x_mode->vsync_start;
+   display_mode->vsync_end = x_mode->vsync_end;
+   display_mode->vtotal = x_mode->vtotal;
+   display_mode->vscan = 0;
+   if (x_mode->mode_flags & XCB_RANDR_MODE_FLAG_DOUBLE_SCAN)
+      display_mode->vscan = 1;
+   display_mode->flags = x_mode->mode_flags;
+
+   LIST_ADDTAIL(&display_mode->list, &wsi->display_modes);
+   return VK_SUCCESS;
+}
+
+/*
+ * Update our information about a specific connector
+ */
+
+static struct wsi_display_connector *
+wsi_display_find_connector(struct wsi_device    *wsi_device,
+                          uint32_t              connector_id)
+{
+   struct wsi_display           *wsi = (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
+   struct wsi_display_connector *connector;
+
+   connector = NULL;
+   LIST_FOR_EACH_ENTRY(connector, &wsi->connectors, list) {
+      if (connector->id == connector_id)
+         return connector;
+   }
+
+   return NULL;
+}
+
+static struct wsi_display_connector *
+wsi_display_alloc_connector(struct wsi_display  *wsi,
+                            uint32_t            connector_id)
+{
+   struct wsi_display_connector *connector;
+
+   connector = vk_alloc(wsi->alloc, sizeof (struct wsi_display_connector), 8, VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
+   memset(connector, '\0', sizeof (*connector));
+   connector->id = connector_id;
+   connector->wsi = wsi;
+   connector->active = false;
+   /* XXX use EDID name */
+   connector->name = "monitor";
+   return connector;
+}
+
+static struct wsi_display_connector *
+wsi_display_get_connector(struct wsi_device             *wsi_device,
+                          uint32_t                      connector_id)
+{
+   struct wsi_display           *wsi = (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
+   struct wsi_display_connector *connector;
+   drmModeConnectorPtr          drm_connector;
+   VkResult                     result;
+   int                          m;
+
+   if (wsi->master_fd < 0)
+      return NULL;
+
+   drm_connector = drmModeGetConnector(wsi->master_fd, connector_id);
+   if (!drm_connector)
+      return NULL;
+
+   connector = wsi_display_find_connector(wsi_device, connector_id);
+
+   if (!connector) {
+      connector = wsi_display_alloc_connector(wsi, connector_id);
+      if (!connector) {
+         drmModeFreeConnector(drm_connector);
+         return NULL;
+      }
+      LIST_ADDTAIL(&connector->list, &wsi->connectors);
+   }
+
+   connector->connected = drm_connector->connection != DRM_MODE_DISCONNECTED;
+
+   /* Mark all connector modes as invalid */
+   wsi_display_invalidate_connector_modes(wsi_device, connector);
+
+   /*
+    * List current modes, adding new ones and marking existing ones as
+    * valid
+    */
+   for (m = 0; m < drm_connector->count_modes; m++) {
+      result = wsi_display_register_drm_mode(wsi_device,
+                                             connector,
+                                             &drm_connector->modes[m]);
+      if (result != VK_SUCCESS) {
+         drmModeFreeConnector(drm_connector);
+         return NULL;
+      }
+   }
+
+   drmModeFreeConnector(drm_connector);
+
+   return connector;
+}
+
+#define MM_PER_PIXEL     (1.0/96.0 * 25.4)
+
+static void
+wsi_display_fill_in_display_properties(struct wsi_device                *wsi_device,
+                                       struct wsi_display_connector     *connector,
+                                       VkDisplayPropertiesKHR           *properties)
+{
+   struct wsi_display           *wsi = (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
+   struct wsi_display_mode      *display_mode, *preferred_mode = NULL;;
+
+   properties->display = wsi_display_connector_to_handle(connector);
+   properties->displayName = connector->name;
+
+   /* Find the preferred mode and assume that's the physical resolution */
+
+   LIST_FOR_EACH_ENTRY(display_mode, &wsi->display_modes, list) {
+      if (display_mode->valid && display_mode->connector == connector && display_mode->preferred) {
+         preferred_mode = display_mode;
+         break;
+      }
+   }
+
+   if (preferred_mode) {
+      properties->physicalResolution.width = preferred_mode->hdisplay;
+      properties->physicalResolution.height = preferred_mode->vdisplay;
+   } else {
+      properties->physicalResolution.width = 1024;
+      properties->physicalResolution.height = 768;
+   }
+
+   /* Make up physical size based on 96dpi */
+   properties->physicalDimensions.width = floor(properties->physicalResolution.width * MM_PER_PIXEL + 0.5);
+   properties->physicalDimensions.height = floor(properties->physicalResolution.height * MM_PER_PIXEL + 0.5);
+
+   properties->supportedTransforms = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
+   properties->persistentContent = 0;
+}
+
+/*
+ * Implement vkGetPhysicalDeviceDisplayPropertiesKHR (VK_KHR_display)
+ */
+VkResult
+wsi_display_get_physical_device_display_properties(VkPhysicalDevice             physical_device,
+                                                   struct wsi_device            *wsi_device,
+                                                   uint32_t                     *property_count,
+                                                   VkDisplayPropertiesKHR       *properties)
+{
+   struct wsi_display           *wsi = (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
+   struct wsi_display_connector *connector;
+   int                          c;
+   uint32_t                     connected;
+   uint32_t                     property_count_requested = *property_count;
+   drmModeResPtr                mode_res;
+
+   if (wsi->master_fd < 0)
+      return VK_ERROR_INITIALIZATION_FAILED;
+
+   mode_res = drmModeGetResources(wsi->master_fd);
+
+   if (!mode_res)
+      return VK_ERROR_INITIALIZATION_FAILED;
+
+   connected = 0;
+
+   /* Get current information */
+   for (c = 0; c < mode_res->count_connectors; c++) {
+      connector = wsi_display_get_connector(wsi_device, mode_res->connectors[c]);
+
+      if (!connector) {
+         drmModeFreeResources(mode_res);
+         return VK_ERROR_OUT_OF_HOST_MEMORY;
+      }
+
+      if (connector->connected)
+         connected++;
+   }
+
+   /* Fill in property information if requested */
+   if (properties != NULL) {
+      connected = 0;
+
+      for (c = 0; c < mode_res->count_connectors; c++) {
+         connector  = wsi_display_find_connector(wsi_device, mode_res->connectors[c]);
+
+         if (connector && connector->connected) {
+            if (connected < property_count_requested) {
+               wsi_display_fill_in_display_properties(wsi_device,
+                                                      connector,
+                                                      &properties[connected]);
+            }
+            connected++;
+         }
+      }
+   }
+
+   drmModeFreeResources(mode_res);
+
+   *property_count = connected;
+
+   if (connected > property_count_requested && properties != NULL)
+      return VK_INCOMPLETE;
+
+   return VK_SUCCESS;
+}
+
+/*
+ * Implement vkGetPhysicalDeviceDisplayPlanePropertiesKHR (VK_KHR_display
+ */
+VkResult
+wsi_display_get_physical_device_display_plane_properties(VkPhysicalDevice               physical_device,
+                                                         struct wsi_device              *wsi_device,
+                                                         uint32_t                       *property_count,
+                                                         VkDisplayPlanePropertiesKHR    *properties)
+{
+   struct wsi_display           *wsi = (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
+   struct wsi_display_connector *connector;
+   uint32_t                     property_count_requested = *property_count;
+   int                          c;
+
+   if (!properties)
+      property_count_requested = 0;
+
+   c = 0;
+   LIST_FOR_EACH_ENTRY(connector, &wsi->connectors, list) {
+      if (c < property_count_requested) {
+         if (connector && connector->active) {
+            properties[c].currentDisplay = wsi_display_connector_to_handle(connector);
+            properties[c].currentStackIndex = c;
+         } else {
+            properties[c].currentDisplay = NULL;
+            properties[c].currentStackIndex = 0;
+         }
+      }
+      c++;
+   }
+
+   *property_count = c;
+
+   if (c > property_count_requested && properties != NULL)
+      return VK_INCOMPLETE;
+
+   return VK_SUCCESS;
+}
+
+/*
+ * Implement vkGetDisplayPlaneSupportedDisplaysKHR (VK_KHR_display)
+ */
+
+VkResult
+wsi_display_get_display_plane_supported_displays(VkPhysicalDevice               physical_device,
+                                                 struct wsi_device              *wsi_device,
+                                                 uint32_t                       plane_index,
+                                                 uint32_t                       *display_count,
+                                                 VkDisplayKHR                   *displays)
+{
+   struct wsi_display           *wsi = (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
+   struct wsi_display_connector *connector;
+   int                          c;
+
+
+   if (displays == NULL) {
+      *display_count = 1;
+      return VK_SUCCESS;
+   }
+
+   if (*display_count < 1)
+      return VK_INCOMPLETE;
+
+   c = 0;
+   LIST_FOR_EACH_ENTRY(connector, &wsi->connectors, list) {
+      if (c == plane_index) {
+         *displays = wsi_display_connector_to_handle(connector);
+         *display_count = 1;
+         return VK_SUCCESS;
+      }
+      c++;
+   }
+
+   *displays = 0;
+   *display_count = 0;
+
+   return VK_SUCCESS;
+}
+
+/*
+ * Implement vkGetDisplayModePropertiesKHR (VK_KHR_display)
+ */
+
+VkResult
+wsi_display_get_display_mode_properties(VkPhysicalDevice               physical_device,
+                                        struct wsi_device              *wsi_device,
+                                        VkDisplayKHR                   display,
+                                        uint32_t                       *property_count,
+                                        VkDisplayModePropertiesKHR     *properties)
+{
+   struct wsi_display           *wsi = (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
+   struct wsi_display_connector *connector = wsi_display_connector_from_handle(display);
+   int                          i;
+   struct wsi_display_mode      *display_mode;
+   uint32_t                     property_count_requested = *property_count;
+
+   i = 0;
+
+   if (properties == NULL)
+      property_count_requested = 0;
+
+   LIST_FOR_EACH_ENTRY(display_mode, &wsi->display_modes, list) {
+      if (display_mode->valid && display_mode->connector == connector) {
+         if (i < property_count_requested) {
+            properties[i].displayMode = wsi_display_mode_to_handle(display_mode);
+            properties[i].parameters.visibleRegion.width = display_mode->hdisplay;
+            properties[i].parameters.visibleRegion.height = display_mode->vdisplay;
+            properties[i].parameters.refreshRate = (uint32_t) (wsi_display_mode_refresh(display_mode) * 1000 + 0.5);
+         }
+         i++;
+      }
+   }
+
+   *property_count = i;
+
+   if (i > property_count_requested && properties != NULL)
+      return VK_INCOMPLETE;
+
+   return VK_SUCCESS;
+
+}
+
+/*
+ * Implement vkGetDisplayPlaneCapabilities
+ */
+VkResult
+wsi_get_display_plane_capabilities(VkPhysicalDevice                     physical_device,
+                                   struct wsi_device                    *wsi_device,
+                                   VkDisplayModeKHR                     mode_khr,
+                                   uint32_t                             plane_index,
+                                   VkDisplayPlaneCapabilitiesKHR        *capabilities)
+{
+   struct wsi_display_mode      *mode = wsi_display_mode_from_handle(mode_khr);
+
+   /* XXX use actual values */
+   capabilities->supportedAlpha = VK_DISPLAY_PLANE_ALPHA_OPAQUE_BIT_KHR;
+   capabilities->minSrcPosition.x = 0;
+   capabilities->minSrcPosition.y = 0;
+   capabilities->maxSrcPosition.x = 0;
+   capabilities->maxSrcPosition.y = 0;
+   capabilities->minSrcExtent.width = mode->hdisplay;
+   capabilities->minSrcExtent.height = mode->vdisplay;
+   capabilities->maxSrcExtent.width = mode->hdisplay;
+   capabilities->maxSrcExtent.height = mode->vdisplay;
+   capabilities->minDstPosition.x = 0;
+   capabilities->minDstPosition.y = 0;
+   capabilities->maxDstPosition.x = 0;
+   capabilities->maxDstPosition.y = 0;
+   capabilities->minDstExtent.width = mode->hdisplay;
+   capabilities->minDstExtent.height = mode->vdisplay;
+   capabilities->maxDstExtent.width = mode->hdisplay;
+   capabilities->maxDstExtent.height = mode->vdisplay;
+   return VK_SUCCESS;
+}
+
+/*
+ * Implement vkReleaseDisplay
+ */
+VkResult
+wsi_release_display(VkPhysicalDevice            physical_device,
+                    struct wsi_device           *wsi_device,
+                    VkDisplayKHR                display)
+{
+   struct wsi_display *wsi = (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
+
+   pthread_mutex_lock(&wsi->wait_mutex);
+   if (wsi->wait_thread) {
+      pthread_cancel(wsi->wait_thread);
+      pthread_join(wsi->wait_thread, NULL);
+   }
+   pthread_mutex_unlock(&wsi->wait_mutex);
+   pthread_mutex_destroy(&wsi->wait_mutex);
+   pthread_cond_destroy(&wsi->wait_cond);
+
+   if (wsi->master_fd >= 0)
+      close(wsi->master_fd);
+
+   return VK_SUCCESS;
+}
+
+#ifdef VK_USE_PLATFORM_XLIB_XRANDR_EXT
+static struct wsi_display_connector *
+wsi_display_find_output(struct wsi_device               *wsi_device,
+                        RROutput                        output)
+{
+   struct wsi_display           *wsi = (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
+   struct wsi_display_connector *connector;
+
+   connector = NULL;
+   LIST_FOR_EACH_ENTRY(connector, &wsi->connectors, list) {
+      if (connector->output == output)
+         return connector;
+   }
+
+   return NULL;
+}
+
+/*
+ * Given a RandR output, find the associated kernel connector_id by
+ * looking at the CONNECTOR_ID property provided by the X server
+ */
+
+static uint32_t
+wsi_display_output_to_connector_id(xcb_connection_t     *connection,
+                                   xcb_atom_t           *connector_id_atom_p,
+                                   RROutput             output)
+{
+   uint32_t                     connector_id = 0;
+   xcb_atom_t                   connector_id_atom = *connector_id_atom_p;
+
+   if (connector_id_atom == 0) {
+   /* Go dig out the CONNECTOR_ID property */
+      xcb_intern_atom_cookie_t     ia_c = xcb_intern_atom(connection,
+                                                          true,
+                                                          12,
+                                                          "CONNECTOR_ID");
+      xcb_intern_atom_reply_t      *ia_r = xcb_intern_atom_reply(connection,
+                                                                 ia_c,
+                                                                 NULL);
+      if (ia_r) {
+         *connector_id_atom_p = connector_id_atom = ia_r->atom;
+         free(ia_r);
+      }
+   }
+
+   /* If there's an CONNECTOR_ID atom in the server, then there may be a CONNECTOR_ID property. Otherwise,
+    * there will not be and we don't even need to bother.
+    */
+   if (connector_id_atom) {
+
+      xcb_randr_query_version_cookie_t          qv_c = xcb_randr_query_version(connection, 1, 6);
+      xcb_randr_get_output_property_cookie_t    gop_c = xcb_randr_get_output_property(connection,
+                                                                                      output,
+                                                                                      connector_id_atom,
+                                                                                      0,
+                                                                                      0,
+                                                                                      0xffffffffUL,
+                                                                                      0,
+                                                                                      0);
+      xcb_randr_query_version_reply_t           *qv_r = xcb_randr_query_version_reply(connection, qv_c, NULL);
+      free(qv_r);
+      xcb_randr_get_output_property_reply_t     *gop_r = xcb_randr_get_output_property_reply(connection,
+                                                                                             gop_c,
+                                                                                             NULL);
+      if (gop_r) {
+         if (gop_r->num_items == 1 && gop_r->format == 32)
+            memcpy(&connector_id, xcb_randr_get_output_property_data(gop_r), 4);
+         free(gop_r);
+      }
+   }
+   return connector_id;
+}
+
+static bool
+wsi_display_check_randr_version(xcb_connection_t        *connection)
+{
+   xcb_randr_query_version_cookie_t     qv_c = xcb_randr_query_version(connection, 1, 6);
+   xcb_randr_query_version_reply_t      *qv_r = xcb_randr_query_version_reply(connection, qv_c, NULL);
+   bool                                 ret = false;
+
+   if (!qv_r)
+      return false;
+
+   /* Check for version 1.6 or newer */
+   ret = qv_r->major_version > 1 || (qv_r->major_version == 1 && qv_r->minor_version >= 6);
+
+   free(qv_r);
+   return ret;
+}
+
+/*
+ * Given a kernel connector id, find the associated RandR output using the
+ * CONNECTOR_ID property
+ */
+
+static xcb_randr_output_t
+wsi_display_connector_id_to_output(xcb_connection_t     *connection,
+                                   uint32_t             connector_id)
+{
+   if (!wsi_display_check_randr_version(connection))
+      return 0;
+
+   const xcb_setup_t                    *setup = xcb_get_setup(connection);
+
+   xcb_atom_t                           connector_id_atom = 0;
+   xcb_randr_output_t                   output = 0;
+
+   /* Search all of the screens for the provided output */
+   xcb_screen_iterator_t iter;
+   for (iter = xcb_setup_roots_iterator(setup); output == 0 && iter.rem; xcb_screen_next(&iter)) {
+
+      xcb_randr_get_screen_resources_cookie_t      gsr_c = xcb_randr_get_screen_resources(connection, iter.data->root);
+      xcb_randr_get_screen_resources_reply_t       *gsr_r = xcb_randr_get_screen_resources_reply(connection, gsr_c, NULL);
+
+      if (!gsr_r)
+         return 0;
+
+      xcb_randr_output_t        *ro = xcb_randr_get_screen_resources_outputs(gsr_r);
+      int                       o;
+
+      for (o = 0; o < gsr_r->num_outputs; o++) {
+         if (wsi_display_output_to_connector_id(connection, &connector_id_atom, ro[o]) == connector_id) {
+            output = ro[o];
+            break;
+         }
+      }
+      free(gsr_r);
+   }
+   return output;
+}
+
+/*
+ * Given a RandR output, find out which screen it's associated with
+ */
+static xcb_window_t
+wsi_display_output_to_root(xcb_connection_t   *connection,
+                           xcb_randr_output_t output)
+{
+   if (!wsi_display_check_randr_version(connection))
+      return 0;
+
+   const xcb_setup_t                    *setup = xcb_get_setup(connection);
+   xcb_window_t                         root = 0;
+
+   /* Search all of the screens for the provided output */
+   xcb_screen_iterator_t iter;
+   for (iter = xcb_setup_roots_iterator(setup); root == 0 && iter.rem; xcb_screen_next(&iter)) {
+      xcb_randr_get_screen_resources_cookie_t      gsr_c = xcb_randr_get_screen_resources(connection, iter.data->root);
+      xcb_randr_get_screen_resources_reply_t       *gsr_r = xcb_randr_get_screen_resources_reply(connection, gsr_c, NULL);
+
+      if (!gsr_r)
+         return 0;
+
+      xcb_randr_output_t        *ro = xcb_randr_get_screen_resources_outputs(gsr_r);
+      int                       o;
+
+      for (o = 0; o < gsr_r->num_outputs; o++) {
+         if (ro[o] == output) {
+            root = iter.data->root;
+            break;
+         }
+      }
+      free(gsr_r);
+   }
+   return root;
+}
+
+static struct wsi_display_connector *
+wsi_display_get_output(struct wsi_device        *wsi_device,
+                       xcb_connection_t         *connection,
+                       RROutput                 output)
+{
+   struct wsi_display                           *wsi = (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
+   struct wsi_display_connector                 *connector;
+   uint32_t                                     connector_id;
+   xcb_window_t                                 root;
+   xcb_randr_get_screen_resources_cookie_t      src;
+   xcb_randr_get_screen_resources_reply_t       *srr;
+   xcb_randr_get_output_info_cookie_t           oic;
+   xcb_randr_get_output_info_reply_t            *oir;
+   xcb_randr_mode_t                             *x_modes;
+   int                                          m;
+
+   root = wsi_display_output_to_root(connection, output);
+   if (!root)
+      return NULL;
+
+   src = xcb_randr_get_screen_resources(connection, root);
+   oic = xcb_randr_get_output_info(connection, output, XCB_CURRENT_TIME);
+   srr = xcb_randr_get_screen_resources_reply(connection, src, NULL);
+   oir = xcb_randr_get_output_info_reply(connection, oic, NULL);
+
+   /* See if we already have a connector for this output */
+   connector = wsi_display_find_output(wsi_device, output);
+
+   if (!connector) {
+      xcb_atom_t        connector_id_atom = 0;
+
+      /*
+       * Go get the kernel connector ID for this X output
+       */
+      connector_id = wsi_display_output_to_connector_id(connection, &connector_id_atom, output);
+
+      /* Any X server with lease support will have this atom */
+      if (!connector_id) {
+         free(oir);
+         free(srr);
+         return NULL;
+      }
+
+      if (!connector) {
+         /* See if we already have a connector for this id */
+         connector = wsi_display_find_connector(wsi_device, connector_id);
+
+         if (connector)
+            connector->output = output;
+      }
+   }
+
+   if (!connector) {
+      connector = wsi_display_alloc_connector(wsi, connector_id);
+      if (!connector) {
+         free(oir);
+         free(srr);
+         return NULL;
+      }
+      LIST_ADDTAIL(&connector->list, &wsi->connectors);
+      connector->output = output;
+   }
+
+   if (oir && srr) {
+      /* Get X modes and add them */
+
+      connector->connected = oir->connection != XCB_RANDR_CONNECTION_DISCONNECTED;
+
+      wsi_display_invalidate_connector_modes(wsi_device, connector);
+
+      x_modes = xcb_randr_get_output_info_modes(oir);
+      for (m = 0; m < oir->num_modes; m++) {
+         xcb_randr_mode_info_iterator_t i = xcb_randr_get_screen_resources_modes_iterator(srr);
+         while (i.rem) {
+            xcb_randr_mode_info_t *mi = i.data;
+            if (mi->id == x_modes[m]) {
+               VkResult result = wsi_display_register_x_mode(wsi_device, connector, mi, m < oir->num_preferred);
+               if (result != VK_SUCCESS) {
+                  free(oir);
+                  free(srr);
+                  return NULL;
+               }
+               break;
+            }
+            xcb_randr_mode_info_next(&i);
+         }
+      }
+   }
+
+   free(oir);
+   free(srr);
+   return connector;
+}
+
+static xcb_randr_crtc_t
+wsi_display_find_crtc_for_output(xcb_connection_t       *connection,
+                                 xcb_window_t           root,
+                                 xcb_randr_output_t     output)
+{
+   xcb_randr_get_screen_resources_cookie_t      gsr_c = xcb_randr_get_screen_resources(connection, root);
+   xcb_randr_get_screen_resources_reply_t       *gsr_r = xcb_randr_get_screen_resources_reply(connection, gsr_c, NULL);
+
+   if (!gsr_r)
+      return 0;
+
+   xcb_randr_crtc_t     *rc = xcb_randr_get_screen_resources_crtcs(gsr_r);
+   xcb_randr_crtc_t     idle_crtc = 0;
+   xcb_randr_crtc_t     active_crtc = 0;
+
+   /* Find either a crtc already connected to the desired output or idle */
+   int c;
+   for (c = 0; active_crtc == 0 && c < gsr_r->num_crtcs; c++) {
+      xcb_randr_get_crtc_info_cookie_t gci_c = xcb_randr_get_crtc_info(connection, rc[c], gsr_r->config_timestamp);
+      xcb_randr_get_crtc_info_reply_t *gci_r = xcb_randr_get_crtc_info_reply(connection, gci_c, NULL);
+      if (gci_r) {
+         if (gci_r->mode) {
+            int num_outputs = xcb_randr_get_crtc_info_outputs_length(gci_r);
+            xcb_randr_output_t *outputs = xcb_randr_get_crtc_info_outputs(gci_r);
+            for (int o = 0; o < num_outputs; o++)
+               if (outputs[o] == output && num_outputs == 1) {
+                  active_crtc = rc[c];
+                  break;
+               }
+         } else if (idle_crtc == 0) {
+            int num_possible = xcb_randr_get_crtc_info_possible_length(gci_r);
+            xcb_randr_output_t *possible = xcb_randr_get_crtc_info_possible(gci_r);
+            for (int p = 0; p < num_possible; p++)
+               if (possible[p] == output) {
+                  idle_crtc = rc[c];
+                  break;
+               }
+         }
+         free(gci_r);
+      }
+   }
+   free(gsr_r);
+
+   if (active_crtc)
+      return active_crtc;
+   return idle_crtc;
+}
+
+VkResult
+wsi_acquire_xlib_display(VkPhysicalDevice       physical_device,
+                         struct wsi_device      *wsi_device,
+                         Display                *dpy,
+                         VkDisplayKHR           display)
+{
+   struct wsi_display           *wsi = (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
+   xcb_connection_t             *connection = XGetXCBConnection(dpy);
+   struct wsi_display_connector *connector = wsi_display_connector_from_handle(display);
+   xcb_window_t                 root;
+
+   if (!connector->output) {
+      connector->output = wsi_display_connector_id_to_output(connection, connector->id);
+
+      /* Check and see if we found the output */
+      if (!connector->output)
+         return VK_ERROR_OUT_OF_DATE_KHR;
+   }
+
+   root = wsi_display_output_to_root(connection, connector->output);
+   if (!root)
+      return VK_ERROR_OUT_OF_DATE_KHR;
+
+   xcb_randr_crtc_t                     crtc = wsi_display_find_crtc_for_output(connection,
+                                                                                root,
+                                                                                connector->output);
+
+   if (!crtc)
+      return VK_ERROR_OUT_OF_DATE_KHR;
+
+   xcb_randr_lease_t                    lease = xcb_generate_id(connection);
+   xcb_randr_create_lease_cookie_t      cl_c = xcb_randr_create_lease(connection,
+                                                                      root,
+                                                                      lease,
+                                                                      1,
+                                                                      1,
+                                                                      &crtc,
+                                                                      &connector->output);
+   xcb_randr_create_lease_reply_t       *cl_r = xcb_randr_create_lease_reply(connection, cl_c, NULL);
+   if (!cl_r)
+      return VK_ERROR_OUT_OF_DATE_KHR;
+
+   int fd = -1;
+   if (cl_r->nfd > 0) {
+      int *rcl_f = xcb_randr_create_lease_reply_fds(connection, cl_r);
+
+      fd = rcl_f[0];
+   }
+   free (cl_r);
+   if (fd < 0)
+      return VK_ERROR_OUT_OF_DATE_KHR;
+
+   wsi->master_fd = fd;
+
+   return VK_SUCCESS;
+}
+
+VkResult
+wsi_get_randr_output_display(VkPhysicalDevice   physical_device,
+                             struct wsi_device  *wsi_device,
+                             Display            *dpy,
+                             RROutput           output,
+                             VkDisplayKHR       *display)
+{
+   xcb_connection_t             *connection = XGetXCBConnection(dpy);
+   struct wsi_display_connector *connector = wsi_display_get_output(wsi_device, connection, output);
+
+   if (connector)
+      *display = wsi_display_connector_to_handle(connector);
+   else
+      *display = NULL;
+   return VK_SUCCESS;
+}
+#endif /* VK_USE_PLATFORM_XLIB_XRANDR_EXT */
+
+VkResult
+wsi_create_display_surface(VkInstance instance,
+                           const VkAllocationCallbacks   *allocator,
+                           const VkDisplaySurfaceCreateInfoKHR *create_info,
+                           VkSurfaceKHR *surface_khr)
+{
+   VkIcdSurfaceDisplay *surface;
+
+   surface = vk_alloc(allocator, sizeof *surface, 8,
+                      VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
+   if (surface == NULL)
+      return VK_ERROR_OUT_OF_HOST_MEMORY;
+
+   surface->base.platform = VK_ICD_WSI_PLATFORM_DISPLAY;
+
+   surface->displayMode = create_info->displayMode;
+   surface->planeIndex = create_info->planeIndex;
+   surface->planeStackIndex = create_info->planeStackIndex;
+   surface->transform = create_info->transform;
+   surface->globalAlpha = create_info->globalAlpha;
+   surface->alphaMode = create_info->alphaMode;
+   surface->imageExtent = create_info->imageExtent;
+
+   *surface_khr = VkIcdSurfaceBase_to_handle(&surface->base);
+   return VK_SUCCESS;
+}
+
+
+static VkResult
+wsi_display_surface_get_support(VkIcdSurfaceBase *surface,
+                                struct wsi_device *wsi_device,
+                                const VkAllocationCallbacks *allocator,
+                                uint32_t queueFamilyIndex,
+                                int local_fd,
+                                VkBool32* pSupported)
+{
+   *pSupported = VK_TRUE;
+   return VK_SUCCESS;
+}
+
+static VkResult
+wsi_display_surface_get_capabilities(VkIcdSurfaceBase *surface_base,
+                                     VkSurfaceCapabilitiesKHR* caps)
+{
+   VkIcdSurfaceDisplay *surface = (VkIcdSurfaceDisplay *) surface_base;
+   wsi_display_mode *mode = wsi_display_mode_from_handle(surface->displayMode);
+
+   caps->currentExtent.width = mode->hdisplay;
+   caps->currentExtent.height = mode->vdisplay;
+
+   /* XXX Figure out extents based on driver capabilities */
+   caps->maxImageExtent = caps->minImageExtent = caps->currentExtent;
+
+   caps->supportedCompositeAlpha = (VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR |
+                                    VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR);
+
+   caps->minImageCount = 2;
+   caps->maxImageCount = 0;
+
+   caps->supportedTransforms = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
+   caps->currentTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
+   caps->maxImageArrayLayers = 1;
+   caps->supportedUsageFlags =
+      VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
+      VK_IMAGE_USAGE_SAMPLED_BIT |
+      VK_IMAGE_USAGE_TRANSFER_DST_BIT |
+      VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
+
+   return VK_SUCCESS;
+}
+
+static VkResult
+wsi_display_surface_get_capabilities2(VkIcdSurfaceBase *icd_surface,
+                                      const void *info_next,
+                                      VkSurfaceCapabilities2KHR *caps)
+{
+   assert(caps->sType == VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_KHR);
+
+   return wsi_display_surface_get_capabilities(icd_surface, &caps->surfaceCapabilities);
+}
+
+static const VkFormat available_surface_formats[] = {
+   VK_FORMAT_B8G8R8A8_SRGB,
+   VK_FORMAT_B8G8R8A8_UNORM,
+};
+
+static VkResult
+wsi_display_surface_get_formats(VkIcdSurfaceBase        *icd_surface,
+                                struct wsi_device       *wsi_device,
+                                uint32_t                *surface_format_count,
+                                VkSurfaceFormatKHR      *surface_formats)
+{
+   VK_OUTARRAY_MAKE(out, surface_formats, surface_format_count);
+
+   for (unsigned i = 0; i < ARRAY_SIZE(available_surface_formats); i++) {
+      vk_outarray_append(&out, f) {
+         f->format = available_surface_formats[i];
+         f->colorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR;
+      }
+   }
+
+   return vk_outarray_status(&out);
+}
+
+static VkResult
+wsi_display_surface_get_formats2(VkIcdSurfaceBase *surface,
+                                 struct wsi_device *wsi_device,
+                                 const void *info_next,
+                                 uint32_t *surface_format_count,
+                                 VkSurfaceFormat2KHR *surface_formats)
+{
+   VK_OUTARRAY_MAKE(out, surface_formats, surface_format_count);
+
+   for (unsigned i = 0; i < ARRAY_SIZE(available_surface_formats); i++) {
+      vk_outarray_append(&out, f) {
+         assert(f->sType == VK_STRUCTURE_TYPE_SURFACE_FORMAT_2_KHR);
+         f->surfaceFormat.format = available_surface_formats[i];
+         f->surfaceFormat.colorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR;
+      }
+   }
+
+   return vk_outarray_status(&out);
+}
+
+static const VkPresentModeKHR available_present_modes[] = {
+   VK_PRESENT_MODE_IMMEDIATE_KHR,
+   VK_PRESENT_MODE_MAILBOX_KHR,
+   VK_PRESENT_MODE_FIFO_KHR,
+};
+
+static VkResult
+wsi_display_surface_get_present_modes(VkIcdSurfaceBase  *surface,
+                                      uint32_t          *present_mode_count,
+                                      VkPresentModeKHR  *present_modes)
+{
+   if (present_modes == NULL) {
+      *present_mode_count = ARRAY_SIZE(available_present_modes);
+      return VK_SUCCESS;
+   }
+
+   *present_mode_count = MIN2(*present_mode_count, ARRAY_SIZE(available_present_modes));
+   typed_memcpy(present_modes, available_present_modes, *present_mode_count);
+
+   if (*present_mode_count < ARRAY_SIZE(available_present_modes))
+      return VK_INCOMPLETE;
+   return VK_SUCCESS;
+}
+
+static VkResult
+wsi_display_image_init(VkDevice                         device_h,
+                       struct wsi_swapchain             *drv_chain,
+                       const VkSwapchainCreateInfoKHR   *create_info,
+                       const VkAllocationCallbacks      *allocator,
+                       struct wsi_display_image         *image)
+{
+   struct wsi_display_swapchain *chain = (struct wsi_display_swapchain *) drv_chain;
+   struct wsi_display           *wsi = chain->wsi;
+   VkResult                     result;
+   int                          ret;
+   uint32_t                     image_handle;
+
+   if (chain->base.use_prime_blit)
+      result = wsi_create_prime_image(&chain->base, create_info, &image->base);
+   else
+      result = wsi_create_native_image(&chain->base, create_info, &image->base);
+   if (result != VK_SUCCESS)
+      return result;
+
+   ret = drmPrimeFDToHandle(wsi->master_fd, image->base.fd, &image_handle);
+
+   close(image->base.fd);
+   image->base.fd = -1;
+
+   if (ret < 0)
+      goto fail_handle;
+
+   image->state = wsi_image_idle;
+   image->frame = 0;
+   image->sec = 0;
+   image->usec = 0;
+
+   /* XXX extract depth and bpp from image somehow */
+   ret = drmModeAddFB(wsi->master_fd, create_info->imageExtent.width, create_info->imageExtent.height,
+                      24, 32, image->base.row_pitch, image_handle, &image->fb_id);
+
+   if (ret)
+      goto fail_fb;
+
+   return VK_SUCCESS;
+
+fail_fb:
+   /* fall through */
+
+fail_handle:
+   wsi_destroy_image(&chain->base, &image->base);
+
+   return VK_ERROR_OUT_OF_HOST_MEMORY;
+}
+
+static void
+wsi_display_image_finish(struct wsi_swapchain           *drv_chain,
+                         const VkAllocationCallbacks    *allocator,
+                         struct wsi_display_image       *image)
+{
+   struct wsi_display_swapchain *chain = (struct wsi_display_swapchain *) drv_chain;
+
+   wsi_destroy_image(&chain->base, &image->base);
+}
+
+static VkResult
+wsi_display_swapchain_destroy(struct wsi_swapchain              *drv_chain,
+                              const VkAllocationCallbacks       *allocator)
+{
+   struct wsi_display_swapchain *chain = (struct wsi_display_swapchain *) drv_chain;
+
+   for (uint32_t i = 0; i < chain->base.image_count; i++)
+      wsi_display_image_finish(drv_chain, allocator, &chain->images[i]);
+   vk_free(allocator, chain);
+   return VK_SUCCESS;
+}
+
+static struct wsi_image *
+wsi_display_get_wsi_image(struct wsi_swapchain  *drv_chain,
+                          uint32_t              image_index)
+{
+   struct wsi_display_swapchain *chain = (struct wsi_display_swapchain *) drv_chain;
+
+   return &chain->images[image_index].base;
+}
+
+static void
+wsi_display_idle_old_displaying(struct wsi_display_image *active_image)
+{
+   struct wsi_display_swapchain *chain = active_image->chain;
+
+   wsi_display_debug("idle everyone but %ld\n", active_image - &(chain->images[0]));
+   for (uint32_t i = 0; i < chain->base.image_count; i++)
+      if (chain->images[i].state == wsi_image_displaying && &chain->images[i] != active_image) {
+         wsi_display_debug("idle %d\n", i);
+         chain->images[i].state = wsi_image_idle;
+      }
+}
+
+static void
+wsi_display_page_flip_handler2(int              fd,
+                               unsigned int     frame,
+                               unsigned int     sec,
+                               unsigned int     usec,
+                               uint32_t         crtc_id,
+                               void             *data)
+{
+   struct wsi_display_image     *image = data;
+
+   wsi_display_debug("image %ld displayed at %d\n", image - &(image->chain->images[0]), frame);
+   image->state = wsi_image_displaying;
+   image->sec = sec;
+   image->usec = usec;
+   wsi_display_idle_old_displaying(image);
+}
+
+static void wsi_display_page_flip_handler(int fd, unsigned int frame,
+                                          unsigned int sec, unsigned int usec, void *data)
+{
+   wsi_display_page_flip_handler2(fd, frame, sec, usec, 0, data);
+}
+
+static drmEventContext event_context = {
+   .version = DRM_EVENT_CONTEXT_VERSION,
+   .page_flip_handler = wsi_display_page_flip_handler,
+#if DRM_EVENT_CONTEXT_VERSION >= 3
+   .page_flip_handler2 = wsi_display_page_flip_handler2,
+#endif
+};
+
+static void *
+wsi_display_wait_thread(void *data)
+{
+   struct wsi_display   *wsi = data;
+   struct pollfd pollfd = {
+      .fd = wsi->master_fd,
+      .events = POLLIN
+   };
+   int ret;
+
+   pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
+   for (;;) {
+      ret = poll(&pollfd, 1, -1);
+      if (ret > 0) {
+         pthread_mutex_lock(&wsi->wait_mutex);
+         (void) drmHandleEvent(wsi->master_fd, &event_context);
+         pthread_mutex_unlock(&wsi->wait_mutex);
+         pthread_cond_broadcast(&wsi->wait_cond);
+      }
+   }
+   return NULL;
+}
+
+static int
+wsi_display_wait_for_event(struct wsi_display           *wsi,
+                           uint64_t                     timeout_ns)
+{
+   int ret;
+
+   if (!wsi->wait_thread) {
+      ret = pthread_create(&wsi->wait_thread, NULL, wsi_display_wait_thread, wsi);
+      if (ret)
+         return ret;
+   }
+
+   struct timespec abs_timeout = {
+      .tv_sec = timeout_ns / ((uint64_t) 1000 * (uint64_t) 1000 * (uint64_t) 1000),
+      .tv_nsec = timeout_ns % ((uint64_t) 1000 * (uint64_t) 1000 * (uint64_t) 1000)
+   };
+
+   ret = pthread_cond_timedwait(&wsi->wait_cond, &wsi->wait_mutex, &abs_timeout);
+
+   wsi_display_debug("%9ld done waiting for event %d\n", pthread_self(), ret);
+   return ret;
+}
+
+static VkResult
+wsi_display_acquire_next_image(struct wsi_swapchain     *drv_chain,
+                               uint64_t                 timeout,
+                               VkSemaphore              semaphore,
+                               uint32_t                 *image_index)
+{
+   struct wsi_display_swapchain *chain = (struct wsi_display_swapchain *)drv_chain;
+   struct wsi_display           *wsi = chain->wsi;
+   int                          ret = 0;
+
+   if (timeout != 0 && timeout != UINT64_MAX)
+      timeout += wsi_get_current_realtime();
+
+   for (;;) {
+      for (uint32_t i = 0; i < chain->base.image_count; i++) {
+         if (chain->images[i].state == wsi_image_idle) {
+            *image_index = i;
+            wsi_display_debug("image %d available\n", i);
+            chain->images[i].state = wsi_image_drawing;
+            return VK_SUCCESS;
+         }
+         wsi_display_debug("image %d state %d\n", i, chain->images[i].state);
+      }
+
+      if (ret == ETIMEDOUT)
+         return VK_TIMEOUT;
+
+      ret = wsi_display_wait_for_event(wsi, timeout);
+
+      if (ret && ret != ETIMEDOUT)
+         return VK_ERROR_OUT_OF_DATE_KHR;
+   }
+}
+
+/*
+ * Check whether there are any other connectors driven by this crtc
+ */
+static bool
+wsi_display_crtc_solo(struct wsi_display        *wsi,
+                      drmModeResPtr             mode_res,
+                      drmModeConnectorPtr       connector,
+                      uint32_t                  crtc_id)
+{
+   int                  c, e;
+
+   /* See if any other connectors share the same encoder */
+   for (c = 0; c < mode_res->count_connectors; c++) {
+      if (mode_res->connectors[c] == connector->connector_id)
+         continue;
+
+      drmModeConnectorPtr       other_connector = drmModeGetConnector(wsi->master_fd, mode_res->connectors[c]);
+      if (other_connector) {
+         bool                      match = (other_connector->encoder_id == connector->encoder_id);
+         drmModeFreeConnector(other_connector);
+         if (match)
+            return false;
+      }
+   }
+
+   /* See if any other encoders share the same crtc */
+   for (e = 0; e < mode_res->count_encoders; e++) {
+      if (mode_res->encoders[e] == connector->encoder_id)
+         continue;
+
+      drmModeEncoderPtr         other_encoder = drmModeGetEncoder(wsi->master_fd, mode_res->encoders[e]);
+      if (other_encoder) {
+         bool                      match = (other_encoder->crtc_id == crtc_id);
+         drmModeFreeEncoder(other_encoder);
+         if (match)
+            return false;
+      }
+   }
+   return true;
+}
+
+/*
+ * Pick a suitable CRTC to drive this connector. Prefer a CRTC which is
+ * currently driving this connector and not any others. Settle for a CRTC
+ * which is currently idle.
+ */
+static uint32_t
+wsi_display_select_crtc(struct wsi_display_connector    *connector,
+                        drmModeResPtr                   mode_res,
+                        drmModeConnectorPtr             drm_connector)
+{
+   struct wsi_display   *wsi = connector->wsi;
+   int                  c;
+   uint32_t             crtc_id;
+
+   /* See what CRTC is currently driving this connector */
+   if (drm_connector->encoder_id) {
+      drmModeEncoderPtr encoder = drmModeGetEncoder(wsi->master_fd, drm_connector->encoder_id);
+      if (encoder) {
+         crtc_id = encoder->crtc_id;
+         drmModeFreeEncoder(encoder);
+         if (crtc_id) {
+            if (wsi_display_crtc_solo(wsi, mode_res, drm_connector, crtc_id))
+               return crtc_id;
+         }
+      }
+   }
+   crtc_id = 0;
+   for (c = 0; crtc_id == 0 && c < mode_res->count_crtcs; c++) {
+      drmModeCrtcPtr crtc = drmModeGetCrtc(wsi->master_fd, mode_res->crtcs[c]);
+      if (crtc && crtc->buffer_id == 0)
+         crtc_id = crtc->crtc_id;
+      drmModeFreeCrtc(crtc);
+   }
+   return crtc_id;
+}
+
+static void
+wsi_display_wait_flip_idle(struct wsi_swapchain         *drv_chain)
+{
+   struct wsi_display_swapchain *chain = (struct wsi_display_swapchain *) drv_chain;
+   struct wsi_display           *wsi = chain->wsi;
+   int ret;
+   uint64_t timeout = 0;
+
+   wsi_display_debug("%9lu wait flip idle\n", pthread_self());
+
+   wsi_display_debug_code(uint64_t start_ns = wsi_get_current_realtime());
+   pthread_mutex_lock(&wsi->wait_mutex);
+   for (;;) {
+      bool busy = false;
+      for (uint32_t i = 0; i < chain->base.image_count; i++)
+         if (chain->images[i].state == wsi_image_flipping) {
+            wsi_display_debug("image %d flipping\n", i);
+            busy = true;
+            break;
+         }
+      if (!busy)
+         break;
+
+      /* wait for at most 100ms; a flip shouldn't take that long... */
+      if (!timeout)
+         timeout = wsi_get_current_realtime() + 100000000ull;
+
+      ret = wsi_display_wait_for_event(wsi, timeout);
+
+      if (ret != 0) {
+         wsi_display_debug("wait for flip idle failed %d %s\n", ret, strerror(ret));
+         break;
+      }
+   }
+
+   wsi_display_debug("%9lu wait flip idle %f ms\n", pthread_self(), ((int64_t) (wsi_get_current_realtime() - start_ns)) / 1.0e6);
+   pthread_mutex_unlock(&wsi->wait_mutex);
+}
+
+static VkResult
+wsi_display_setup_connector(wsi_display_connector       *connector,
+                            wsi_display_mode            *display_mode)
+{
+   struct wsi_display   *wsi = connector->wsi;
+   drmModeModeInfoPtr   drm_mode;
+   drmModeConnectorPtr  drm_connector;
+   drmModeResPtr        mode_res;
+   VkResult             result;
+   int                  m;
+
+   if (connector->current_mode == display_mode && connector->crtc_id)
+      return VK_SUCCESS;
+
+   mode_res = drmModeGetResources(wsi->master_fd);
+   if (!mode_res) {
+      result = VK_ERROR_INITIALIZATION_FAILED;
+      goto bail;
+   }
+
+   drm_connector = drmModeGetConnectorCurrent(wsi->master_fd, connector->id);
+   if (!drm_connector) {
+      result = VK_ERROR_INITIALIZATION_FAILED;
+      goto bail_mode_res;
+   }
+
+   /* Pick a CRTC if we don't have one */
+   if (!connector->crtc_id) {
+      connector->crtc_id = wsi_display_select_crtc(connector, mode_res, drm_connector);
+      if (!connector->crtc_id) {
+         result = VK_ERROR_OUT_OF_DATE_KHR;
+         goto bail_connector;
+      }
+   }
+
+   if (connector->current_mode != display_mode) {
+
+      /* Find the drm mode cooresponding to the requested VkDisplayMode */
+      drm_mode = NULL;
+      for (m = 0; m < drm_connector->count_modes; m++) {
+         drm_mode = &drm_connector->modes[m];
+         if (wsi_display_mode_matches_drm(display_mode, drm_mode))
+            break;
+         drm_mode = NULL;
+      }
+
+      if (!drm_mode) {
+         result = VK_ERROR_OUT_OF_DATE_KHR;
+         goto bail_connector;
+      }
+
+      connector->current_mode = display_mode;
+      connector->current_drm_mode = *drm_mode;
+   }
+
+   result = VK_SUCCESS;
+
+bail_connector:
+   drmModeFreeConnector(drm_connector);
+bail_mode_res:
+   drmModeFreeResources(mode_res);
+bail:
+   return result;
+
+}
+
+static VkResult
+wsi_display_queue_present(struct wsi_swapchain          *drv_chain,
+                          uint32_t                      image_index,
+                          const VkPresentRegionKHR      *damage)
+{
+   struct wsi_display_swapchain *chain = (struct wsi_display_swapchain *) drv_chain;
+   struct wsi_display           *wsi = chain->wsi;
+   struct wsi_display_image     *image = &chain->images[image_index];
+   VkIcdSurfaceDisplay          *surface = chain->surface;
+   wsi_display_mode             *display_mode = wsi_display_mode_from_handle(surface->displayMode);
+   wsi_display_connector        *connector = display_mode->connector;
+   int                          ret;
+   VkResult                     result;
+
+   if (wsi->master_fd < 0)
+      return VK_ERROR_INITIALIZATION_FAILED;
+
+   if (display_mode != connector->current_mode)
+      connector->active = false;
+
+   assert(image->state == wsi_image_drawing);
+   wsi_display_debug("present %d\n", image_index);
+   for (;;) {
+      if (connector->active) {
+         wsi_display_wait_flip_idle(drv_chain);
+         ret = drmModePageFlip(wsi->master_fd, connector->crtc_id, image->fb_id,
+                               DRM_MODE_PAGE_FLIP_EVENT, image);
+         if (ret == 0) {
+            image->state = wsi_image_flipping;
+            return VK_SUCCESS;
+         }
+         wsi_display_debug("page flip err %d %s\n", ret, strerror(-ret));
+      } else
+         ret = -EINVAL;
+
+      if (ret) {
+         switch(-ret) {
+         case EINVAL:
+
+            result = wsi_display_setup_connector(connector, display_mode);
+
+            if (result != VK_SUCCESS) {
+               image->state = wsi_image_idle;
+               return result;
+            }
+
+            /* XXX allow setting of position */
+
+            ret = drmModeSetCrtc(wsi->master_fd, connector->crtc_id, image->fb_id, 0, 0,
+                                 &connector->id, 1, &connector->current_drm_mode);
+
+            if (ret == 0) {
+               image->state = wsi_image_displaying;
+               wsi_display_idle_old_displaying(image);
+               connector->active = true;
+               return VK_SUCCESS;
+            }
+            break;
+         case EACCES:
+            usleep(1000 * 1000);
+            connector->active = false;
+            break;
+         default:
+            connector->active = false;
+            image->state = wsi_image_idle;
+            return VK_ERROR_OUT_OF_DATE_KHR;
+         }
+      }
+   }
+}
+
+static VkResult
+wsi_display_surface_create_swapchain(VkIcdSurfaceBase                   *icd_surface,
+                                     VkDevice                           device,
+                                     struct wsi_device                  *wsi_device,
+                                     int                                local_fd,
+                                     const VkSwapchainCreateInfoKHR     *create_info,
+                                     const VkAllocationCallbacks        *allocator,
+                                     struct wsi_swapchain               **swapchain_out)
+{
+   struct wsi_display *wsi = (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
+   VkResult result;
+
+   assert(create_info->sType == VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR);
+
+   struct wsi_display_swapchain *chain;
+   const unsigned num_images = create_info->minImageCount;
+   size_t size = sizeof(*chain) + num_images * sizeof(chain->images[0]);
+
+   chain = vk_alloc(allocator, size, 8,
+                    VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
+
+   if (chain == NULL)
+      return VK_ERROR_OUT_OF_HOST_MEMORY;
+
+   result = wsi_swapchain_init(wsi_device, &chain->base, device,
+                               create_info, allocator);
+
+   chain->base.destroy = wsi_display_swapchain_destroy;
+   chain->base.get_wsi_image = wsi_display_get_wsi_image;
+   chain->base.acquire_next_image = wsi_display_acquire_next_image;
+   chain->base.queue_present = wsi_display_queue_present;
+   chain->base.present_mode = create_info->presentMode;
+   chain->base.image_count = num_images;
+
+   chain->wsi = wsi;
+
+   chain->surface = (VkIcdSurfaceDisplay *) icd_surface;
+
+   for (uint32_t image = 0; image < chain->base.image_count; image++) {
+      result = wsi_display_image_init(device, &chain->base, create_info, allocator,
+                                      &chain->images[image]);
+      if (result != VK_SUCCESS)
+         goto fail_init_images;
+   }
+
+   *swapchain_out = &chain->base;
+
+   return VK_SUCCESS;
+
+fail_init_images:
+   return result;
+}
+
+VkResult
+wsi_display_init_wsi(struct wsi_device *wsi_device,
+                     const VkAllocationCallbacks *alloc,
+                     VkPhysicalDevice physical_device,
+                     int device_fd)
+{
+   struct wsi_display *wsi;
+   VkResult result;
+
+   wsi = vk_alloc(alloc, sizeof(*wsi), 8,
+                   VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE);
+   if (!wsi) {
+      result = VK_ERROR_OUT_OF_HOST_MEMORY;
+      goto fail;
+   }
+   memset(wsi, '\0', sizeof (*wsi));
+
+   wsi->master_fd = -1;
+   wsi->render_fd = device_fd;
+
+   pthread_mutex_init(&wsi->wait_mutex, NULL);
+   wsi->physical_device = physical_device;
+   wsi->alloc = alloc;
+
+   LIST_INITHEAD(&wsi->display_modes);
+   LIST_INITHEAD(&wsi->connectors);
+
+   pthread_mutex_init(&wsi->wait_mutex, NULL);
+   pthread_cond_init(&wsi->wait_cond, NULL);
+
+   wsi->base.get_support = wsi_display_surface_get_support;
+   wsi->base.get_capabilities = wsi_display_surface_get_capabilities;
+   wsi->base.get_capabilities2 = wsi_display_surface_get_capabilities2;
+   wsi->base.get_formats = wsi_display_surface_get_formats;
+   wsi->base.get_formats2 = wsi_display_surface_get_formats2;
+   wsi->base.get_present_modes = wsi_display_surface_get_present_modes;
+   wsi->base.create_swapchain = wsi_display_surface_create_swapchain;
+
+   wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY] = &wsi->base;
+
+   return VK_SUCCESS;
+
+fail:
+   return result;
+}
+
+void
+wsi_display_finish_wsi(struct wsi_device *wsi_device,
+                       const VkAllocationCallbacks *alloc)
+{
+   struct wsi_display *wsi = (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
+
+   if (wsi) {
+      vk_free(alloc, wsi);
+   }
+}
diff --git a/src/vulkan/wsi/wsi_common_display.h b/src/vulkan/wsi/wsi_common_display.h
new file mode 100644
index 00000000000..2e47a21fb92
--- /dev/null
+++ b/src/vulkan/wsi/wsi_common_display.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright © 2017 Keith Packard
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission.  The copyright holders make no representations
+ * about the suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+#ifndef WSI_COMMON_DISPLAY_H
+#define WSI_COMMON_DISPLAY_H
+
+#include "wsi_common.h"
+#include <xf86drm.h>
+#include <xf86drmMode.h>
+
+#define typed_memcpy(dest, src, count) ({ \
+   STATIC_ASSERT(sizeof(*src) == sizeof(*dest)); \
+   memcpy((dest), (src), (count) * sizeof(*(src))); \
+})
+
+VkResult
+wsi_display_get_physical_device_display_properties(VkPhysicalDevice             physical_device,
+                                                   struct wsi_device            *wsi_device,
+                                                   uint32_t                     *property_count,
+                                                   VkDisplayPropertiesKHR       *properties);
+VkResult
+wsi_display_get_physical_device_display_plane_properties(VkPhysicalDevice               physical_device,
+                                                         struct wsi_device              *wsi_device,
+                                                         uint32_t                       *property_count,
+                                                         VkDisplayPlanePropertiesKHR    *properties);
+
+VkResult
+wsi_display_get_display_plane_supported_displays(VkPhysicalDevice               physical_device,
+                                                 struct wsi_device              *wsi_device,
+                                                 uint32_t                       plane_index,
+                                                 uint32_t                       *display_count,
+                                                 VkDisplayKHR                   *displays);
+VkResult
+wsi_display_get_display_mode_properties(VkPhysicalDevice               physical_device,
+                                        struct wsi_device              *wsi_device,
+                                        VkDisplayKHR                   display,
+                                        uint32_t                       *property_count,
+                                        VkDisplayModePropertiesKHR     *properties);
+
+VkResult
+wsi_get_display_plane_capabilities(VkPhysicalDevice                     physical_device,
+                                   struct wsi_device                    *wsi_device,
+                                    VkDisplayModeKHR                    mode_khr,
+                                    uint32_t                            plane_index,
+                                    VkDisplayPlaneCapabilitiesKHR       *capabilities);
+VkResult
+wsi_release_display(VkPhysicalDevice            physical_device,
+                    struct wsi_device           *wsi_device,
+                    VkDisplayKHR                display);
+
+#if VK_USE_PLATFORM_XLIB_XRANDR_EXT
+VkResult
+wsi_acquire_xlib_display(VkPhysicalDevice       physical_device,
+                         struct wsi_device      *wsi_device,
+                         Display                *dpy,
+                         VkDisplayKHR           display);
+
+VkResult
+wsi_get_randr_output_display(VkPhysicalDevice   physical_device,
+                             struct wsi_device  *wsi_device,
+                             Display            *dpy,
+                             RROutput           output,
+                             VkDisplayKHR       *display);
+
+#endif /* VK_USE_PLATFORM_XLIB_XRANDR_EXT */
+
+VkResult
+wsi_create_display_surface(VkInstance instance,
+                           const VkAllocationCallbacks *pAllocator,
+                           const VkDisplaySurfaceCreateInfoKHR *pCreateInfo,
+                           VkSurfaceKHR *pSurface);
+
+#endif
diff --git a/src/vulkan/wsi/wsi_common_private.h b/src/vulkan/wsi/wsi_common_private.h
index 503b2a015dc..d38d2efa116 100644
--- a/src/vulkan/wsi/wsi_common_private.h
+++ b/src/vulkan/wsi/wsi_common_private.h
@@ -135,6 +135,16 @@ void wsi_wl_finish_wsi(struct wsi_device *wsi_device,
                        const VkAllocationCallbacks *alloc);
 
 
+VkResult
+wsi_display_init_wsi(struct wsi_device *wsi_device,
+                     const VkAllocationCallbacks *alloc,
+                     VkPhysicalDevice physical_device,
+                     int device_fd);
+
+void
+wsi_display_finish_wsi(struct wsi_device *wsi_device,
+                       const VkAllocationCallbacks *alloc);
+
 #define WSI_DEFINE_NONDISP_HANDLE_CASTS(__wsi_type, __VkType)              \
                                                                            \
    static inline struct __wsi_type *                                       \
-- 
2.15.1




More information about the mesa-dev mailing list