vulkan/wsi: disable the hardware cursor
[mesa.git] / src / vulkan / wsi / wsi_common_display.c
index 4f52e8087a6cfb687c0106dbe355475e6e898cd5..0f9a1ffe8d3af271f0ee5848e1c6090b75efc6b1 100644 (file)
@@ -32,7 +32,7 @@
 #include <math.h>
 #include <xf86drm.h>
 #include <xf86drmMode.h>
-#include <drm_fourcc.h>
+#include "drm-uapi/drm_fourcc.h"
 #ifdef VK_USE_PLATFORM_XLIB_XRANDR_EXT
 #include <xcb/randr.h>
 #include <X11/Xlib-xcb.h>
@@ -79,6 +79,7 @@ typedef struct wsi_display_connector {
    struct list_head             display_modes;
    wsi_display_mode             *current_mode;
    drmModeModeInfo              current_drm_mode;
+   uint32_t                     dpms_property;
 #ifdef VK_USE_PLATFORM_XLIB_XRANDR_EXT
    xcb_randr_output_t           output;
 #endif
@@ -95,7 +96,7 @@ struct wsi_display {
    pthread_cond_t               wait_cond;
    pthread_t                    wait_thread;
 
-   struct list_head             connectors;
+   struct list_head             connectors; /* list of all discovered connectors */
 };
 
 #define wsi_for_each_display_mode(_mode, _conn)                 \
@@ -132,6 +133,15 @@ struct wsi_display_swapchain {
    struct wsi_display_image     images[0];
 };
 
+struct wsi_display_fence {
+   struct wsi_fence             base;
+   bool                         event_received;
+   bool                         destroyed;
+   uint64_t                     sequence;
+};
+
+static uint64_t fence_sequence;
+
 ICD_DEFINE_NONDISP_HANDLE_CASTS(wsi_display_mode, VkDisplayModeKHR)
 ICD_DEFINE_NONDISP_HANDLE_CASTS(wsi_display_connector, VkDisplayKHR)
 
@@ -161,17 +171,9 @@ wsi_display_mode_refresh(struct wsi_display_mode *wsi)
                                           (double) MAX2(wsi->vscan, 1));
 }
 
-static uint64_t wsi_get_current_monotonic(void)
-{
-   struct timespec tv;
-
-   clock_gettime(CLOCK_MONOTONIC, &tv);
-   return tv.tv_nsec + tv.tv_sec*1000000000ull;
-}
-
 static uint64_t wsi_rel_to_abs_time(uint64_t rel_time)
 {
-   uint64_t current_time = wsi_get_current_monotonic();
+   uint64_t current_time = wsi_common_get_current_time();
 
    /* check for overflow */
    if (rel_time > UINT64_MAX - current_time)
@@ -307,6 +309,21 @@ wsi_display_get_connector(struct wsi_device *wsi_device,
 
    connector->connected = drm_connector->connection != DRM_MODE_DISCONNECTED;
 
+   /* Look for a DPMS property if we haven't already found one */
+   for (int p = 0; connector->dpms_property == 0 &&
+           p < drm_connector->count_props; p++)
+   {
+      drmModePropertyPtr prop = drmModeGetProperty(wsi->fd,
+                                                   drm_connector->props[p]);
+      if (!prop)
+         continue;
+      if (prop->flags & DRM_MODE_PROP_ENUM) {
+         if (!strcmp(prop->name, "DPMS"))
+            connector->dpms_property = drm_connector->props[p];
+      }
+      drmModeFreeProperty(prop);
+   }
+
    /* Mark all connector modes as invalid */
    wsi_display_invalidate_connector_modes(wsi_device, connector);
 
@@ -341,8 +358,11 @@ mode_size(struct wsi_display_mode *mode)
 static void
 wsi_display_fill_in_display_properties(struct wsi_device *wsi_device,
                                        struct wsi_display_connector *connector,
-                                       VkDisplayPropertiesKHR *properties)
+                                       VkDisplayProperties2KHR *properties2)
 {
+   assert(properties2->sType == VK_STRUCTURE_TYPE_DISPLAY_PROPERTIES_2_KHR);
+   VkDisplayPropertiesKHR *properties = &properties2->displayProperties;
+
    properties->display = wsi_display_connector_to_handle(connector);
    properties->displayName = connector->name;
 
@@ -401,6 +421,50 @@ wsi_display_get_physical_device_display_properties(
    struct wsi_display *wsi =
       (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
 
+   if (properties == NULL) {
+      return wsi_display_get_physical_device_display_properties2(
+            physical_device, wsi_device, property_count, NULL);
+   } else {
+      /* If we're actually returning properties, allocate a temporary array of
+       * VkDisplayProperties2KHR structs, call properties2 to fill them out,
+       * and then copy them to the client.  This seems a bit expensive but
+       * wsi_display_get_physical_device_display_properties2() calls
+       * drmModeGetResources() which does an ioctl and then a bunch of
+       * allocations so this should get lost in the noise.
+       */
+      VkDisplayProperties2KHR *props2 =
+         vk_zalloc(wsi->alloc, sizeof(*props2) * *property_count, 8,
+                   VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
+      if (props2 == NULL)
+         return VK_ERROR_OUT_OF_HOST_MEMORY;
+
+      for (uint32_t i = 0; i < *property_count; i++)
+         props2[i].sType = VK_STRUCTURE_TYPE_DISPLAY_PROPERTIES_2_KHR;
+
+      VkResult result = wsi_display_get_physical_device_display_properties2(
+            physical_device, wsi_device, property_count, props2);
+
+      if (result == VK_SUCCESS || result == VK_INCOMPLETE) {
+         for (uint32_t i = 0; i < *property_count; i++)
+            properties[i] = props2[i].displayProperties;
+      }
+
+      vk_free(wsi->alloc, props2);
+
+      return result;
+   }
+}
+
+VkResult
+wsi_display_get_physical_device_display_properties2(
+   VkPhysicalDevice physical_device,
+   struct wsi_device *wsi_device,
+   uint32_t *property_count,
+   VkDisplayProperties2KHR *properties)
+{
+   struct wsi_display *wsi =
+      (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
+
    if (wsi->fd < 0)
       goto bail;
 
@@ -443,6 +507,24 @@ bail:
 /*
  * Implement vkGetPhysicalDeviceDisplayPlanePropertiesKHR (VK_KHR_display
  */
+static void
+wsi_display_fill_in_display_plane_properties(
+   struct wsi_device *wsi_device,
+   struct wsi_display_connector *connector,
+   VkDisplayPlaneProperties2KHR *properties)
+{
+   assert(properties->sType == VK_STRUCTURE_TYPE_DISPLAY_PLANE_PROPERTIES_2_KHR);
+   VkDisplayPlanePropertiesKHR *prop = &properties->displayPlaneProperties;
+
+   if (connector && connector->active) {
+      prop->currentDisplay = wsi_display_connector_to_handle(connector);
+      prop->currentStackIndex = 0;
+   } else {
+      prop->currentDisplay = VK_NULL_HANDLE;
+      prop->currentStackIndex = 0;
+   }
+}
+
 VkResult
 wsi_display_get_physical_device_display_plane_properties(
    VkPhysicalDevice physical_device,
@@ -457,13 +539,33 @@ wsi_display_get_physical_device_display_plane_properties(
 
    wsi_for_each_connector(connector, wsi) {
       vk_outarray_append(&conn, prop) {
-         if (connector && connector->active) {
-            prop->currentDisplay = wsi_display_connector_to_handle(connector);
-            prop->currentStackIndex = 0;
-         } else {
-            prop->currentDisplay = VK_NULL_HANDLE;
-            prop->currentStackIndex = 0;
-         }
+         VkDisplayPlaneProperties2KHR prop2 = {
+            .sType = VK_STRUCTURE_TYPE_DISPLAY_PLANE_PROPERTIES_2_KHR,
+         };
+         wsi_display_fill_in_display_plane_properties(wsi_device, connector,
+                                                      &prop2);
+         *prop = prop2.displayPlaneProperties;
+      }
+   }
+   return vk_outarray_status(&conn);
+}
+
+VkResult
+wsi_display_get_physical_device_display_plane_properties2(
+   VkPhysicalDevice physical_device,
+   struct wsi_device *wsi_device,
+   uint32_t *property_count,
+   VkDisplayPlaneProperties2KHR *properties)
+{
+   struct wsi_display *wsi =
+      (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
+
+   VK_OUTARRAY_MAKE(conn, properties, property_count);
+
+   wsi_for_each_connector(connector, wsi) {
+      vk_outarray_append(&conn, prop) {
+         wsi_display_fill_in_display_plane_properties(wsi_device, connector,
+                                                      prop);
       }
    }
    return vk_outarray_status(&conn);
@@ -503,6 +605,22 @@ wsi_display_get_display_plane_supported_displays(
  * Implement vkGetDisplayModePropertiesKHR (VK_KHR_display)
  */
 
+static void
+wsi_display_fill_in_display_mode_properties(
+   struct wsi_device *wsi_device,
+   struct wsi_display_mode *display_mode,
+   VkDisplayModeProperties2KHR *properties)
+{
+   assert(properties->sType == VK_STRUCTURE_TYPE_DISPLAY_MODE_PROPERTIES_2_KHR);
+   VkDisplayModePropertiesKHR *prop = &properties->displayModeProperties;
+
+   prop->displayMode = wsi_display_mode_to_handle(display_mode);
+   prop->parameters.visibleRegion.width = display_mode->hdisplay;
+   prop->parameters.visibleRegion.height = display_mode->vdisplay;
+   prop->parameters.refreshRate =
+      (uint32_t) (wsi_display_mode_refresh(display_mode) * 1000 + 0.5);
+}
+
 VkResult
 wsi_display_get_display_mode_properties(VkPhysicalDevice physical_device,
                                         struct wsi_device *wsi_device,
@@ -516,14 +634,40 @@ wsi_display_get_display_mode_properties(VkPhysicalDevice physical_device,
    VK_OUTARRAY_MAKE(conn, properties, property_count);
 
    wsi_for_each_display_mode(display_mode, connector) {
-      if (display_mode->valid) {
-         vk_outarray_append(&conn, prop) {
-            prop->displayMode = wsi_display_mode_to_handle(display_mode);
-            prop->parameters.visibleRegion.width = display_mode->hdisplay;
-            prop->parameters.visibleRegion.height = display_mode->vdisplay;
-            prop->parameters.refreshRate =
-               (uint32_t) (wsi_display_mode_refresh(display_mode) * 1000 + 0.5);
-         }
+      if (!display_mode->valid)
+         continue;
+
+      vk_outarray_append(&conn, prop) {
+         VkDisplayModeProperties2KHR prop2 = {
+            .sType = VK_STRUCTURE_TYPE_DISPLAY_MODE_PROPERTIES_2_KHR,
+         };
+         wsi_display_fill_in_display_mode_properties(wsi_device,
+                                                     display_mode, &prop2);
+         *prop = prop2.displayModeProperties;
+      }
+   }
+   return vk_outarray_status(&conn);
+}
+
+VkResult
+wsi_display_get_display_mode_properties2(VkPhysicalDevice physical_device,
+                                         struct wsi_device *wsi_device,
+                                         VkDisplayKHR display,
+                                         uint32_t *property_count,
+                                         VkDisplayModeProperties2KHR *properties)
+{
+   struct wsi_display_connector *connector =
+      wsi_display_connector_from_handle(display);
+
+   VK_OUTARRAY_MAKE(conn, properties, property_count);
+
+   wsi_for_each_display_mode(display_mode, connector) {
+      if (!display_mode->valid)
+         continue;
+
+      vk_outarray_append(&conn, prop) {
+         wsi_display_fill_in_display_mode_properties(wsi_device,
+                                                     display_mode, prop);
       }
    }
    return vk_outarray_status(&conn);
@@ -604,6 +748,39 @@ wsi_get_display_plane_capabilities(VkPhysicalDevice physical_device,
    return VK_SUCCESS;
 }
 
+VkResult
+wsi_get_display_plane_capabilities2(
+   VkPhysicalDevice physical_device,
+   struct wsi_device *wsi_device,
+   const VkDisplayPlaneInfo2KHR *pDisplayPlaneInfo,
+   VkDisplayPlaneCapabilities2KHR *capabilities)
+{
+   assert(capabilities->sType ==
+          VK_STRUCTURE_TYPE_DISPLAY_PLANE_CAPABILITIES_2_KHR);
+
+   VkResult result =
+      wsi_get_display_plane_capabilities(physical_device, wsi_device,
+                                         pDisplayPlaneInfo->mode,
+                                         pDisplayPlaneInfo->planeIndex,
+                                         &capabilities->capabilities);
+
+   vk_foreach_struct(ext, capabilities->pNext) {
+      switch (ext->sType) {
+      case VK_STRUCTURE_TYPE_SURFACE_PROTECTED_CAPABILITIES_KHR: {
+         VkSurfaceProtectedCapabilitiesKHR *protected = (void *)ext;
+         protected->supportsProtected = VK_FALSE;
+         break;
+      }
+
+      default:
+         /* Ignored */
+         break;
+      }
+   }
+
+   return result;
+}
+
 VkResult
 wsi_create_display_surface(VkInstance instance,
                            const VkAllocationCallbacks *allocator,
@@ -634,9 +811,7 @@ wsi_create_display_surface(VkInstance instance,
 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;
@@ -645,6 +820,7 @@ wsi_display_surface_get_support(VkIcdSurfaceBase *surface,
 
 static VkResult
 wsi_display_surface_get_capabilities(VkIcdSurfaceBase *surface_base,
+                                     struct wsi_device *wsi_device,
                                      VkSurfaceCapabilitiesKHR* caps)
 {
    VkIcdSurfaceDisplay *surface = (VkIcdSurfaceDisplay *) surface_base;
@@ -653,8 +829,11 @@ wsi_display_surface_get_capabilities(VkIcdSurfaceBase *surface_base,
    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->minImageExtent = (VkExtent2D) { 1, 1 };
+   caps->maxImageExtent = (VkExtent2D) {
+      wsi_device->maxImageDimension2D,
+      wsi_device->maxImageDimension2D,
+   };
 
    caps->supportedCompositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
 
@@ -668,20 +847,45 @@ wsi_display_surface_get_capabilities(VkIcdSurfaceBase *surface_base,
       VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
       VK_IMAGE_USAGE_SAMPLED_BIT |
       VK_IMAGE_USAGE_TRANSFER_DST_BIT |
+      VK_IMAGE_USAGE_STORAGE_BIT |
       VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
 
    return VK_SUCCESS;
 }
 
+static VkResult
+wsi_display_surface_get_surface_counters(
+   VkIcdSurfaceBase *surface_base,
+   VkSurfaceCounterFlagsEXT *counters)
+{
+   *counters = VK_SURFACE_COUNTER_VBLANK_EXT;
+   return VK_SUCCESS;
+}
+
 static VkResult
 wsi_display_surface_get_capabilities2(VkIcdSurfaceBase *icd_surface,
+                                      struct wsi_device *wsi_device,
                                       const void *info_next,
                                       VkSurfaceCapabilities2KHR *caps)
 {
    assert(caps->sType == VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_KHR);
+   VkResult result;
+
+   result = wsi_display_surface_get_capabilities(icd_surface, wsi_device,
+                                                 &caps->surfaceCapabilities);
+   if (result != VK_SUCCESS)
+      return result;
+
+   struct wsi_surface_supported_counters *counters =
+      vk_find_struct( caps->pNext, WSI_SURFACE_SUPPORTED_COUNTERS_MESA);
+
+   if (counters) {
+      result = wsi_display_surface_get_surface_counters(
+         icd_surface,
+         &counters->supported_surface_counters);
+   }
 
-   return wsi_display_surface_get_capabilities(icd_surface,
-                                               &caps->surfaceCapabilities);
+   return result;
 }
 
 static const struct {
@@ -692,6 +896,23 @@ static const struct {
    { .format = VK_FORMAT_B8G8R8A8_UNORM, .drm_format = DRM_FORMAT_XRGB8888 },
 };
 
+static void
+get_sorted_vk_formats(struct wsi_device *wsi_device, VkFormat *sorted_formats)
+{
+   for (unsigned i = 0; i < ARRAY_SIZE(available_surface_formats); i++)
+      sorted_formats[i] = available_surface_formats[i].format;
+
+   if (wsi_device->force_bgra8_unorm_first) {
+      for (unsigned i = 0; i < ARRAY_SIZE(available_surface_formats); i++) {
+         if (sorted_formats[i] == VK_FORMAT_B8G8R8A8_UNORM) {
+            sorted_formats[i] = sorted_formats[0];
+            sorted_formats[0] = VK_FORMAT_B8G8R8A8_UNORM;
+            break;
+         }
+      }
+   }
+}
+
 static VkResult
 wsi_display_surface_get_formats(VkIcdSurfaceBase *icd_surface,
                                 struct wsi_device *wsi_device,
@@ -700,9 +921,12 @@ wsi_display_surface_get_formats(VkIcdSurfaceBase *icd_surface,
 {
    VK_OUTARRAY_MAKE(out, surface_formats, surface_format_count);
 
-   for (unsigned i = 0; i < ARRAY_SIZE(available_surface_formats); i++) {
+   VkFormat sorted_formats[ARRAY_SIZE(available_surface_formats)];
+   get_sorted_vk_formats(wsi_device, sorted_formats);
+
+   for (unsigned i = 0; i < ARRAY_SIZE(sorted_formats); i++) {
       vk_outarray_append(&out, f) {
-         f->format = available_surface_formats[i].format;
+         f->format = sorted_formats[i];
          f->colorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR;
       }
    }
@@ -719,10 +943,13 @@ wsi_display_surface_get_formats2(VkIcdSurfaceBase *surface,
 {
    VK_OUTARRAY_MAKE(out, surface_formats, surface_format_count);
 
-   for (unsigned i = 0; i < ARRAY_SIZE(available_surface_formats); i++) {
+   VkFormat sorted_formats[ARRAY_SIZE(available_surface_formats)];
+   get_sorted_vk_formats(wsi_device, sorted_formats);
+
+   for (unsigned i = 0; i < ARRAY_SIZE(sorted_formats); i++) {
       vk_outarray_append(&out, f) {
          assert(f->sType == VK_STRUCTURE_TYPE_SURFACE_FORMAT_2_KHR);
-         f->surfaceFormat.format = available_surface_formats[i].format;
+         f->surfaceFormat.format = sorted_formats[i];
          f->surfaceFormat.colorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR;
       }
    }
@@ -744,12 +971,34 @@ wsi_display_surface_get_present_modes(VkIcdSurfaceBase *surface,
    return vk_outarray_status(&conn);
 }
 
+static VkResult
+wsi_display_surface_get_present_rectangles(VkIcdSurfaceBase *surface_base,
+                                           struct wsi_device *wsi_device,
+                                           uint32_t* pRectCount,
+                                           VkRect2D* pRects)
+{
+   VkIcdSurfaceDisplay *surface = (VkIcdSurfaceDisplay *) surface_base;
+   wsi_display_mode *mode = wsi_display_mode_from_handle(surface->displayMode);
+   VK_OUTARRAY_MAKE(out, pRects, pRectCount);
+
+   if (wsi_device_matches_drm_fd(wsi_device, mode->connector->wsi->fd)) {
+      vk_outarray_append(&out, rect) {
+         *rect = (VkRect2D) {
+            .offset = { 0, 0 },
+            .extent = { mode->hdisplay, mode->vdisplay },
+         };
+      }
+   }
+
+   return vk_outarray_status(&out);
+}
+
 static void
 wsi_display_destroy_buffer(struct wsi_display *wsi,
                            uint32_t buffer)
 {
-   (void) drmIoctl(wsi->fd, DRM_IOCTL_MODE_DESTROY_DUMB,
-                   &((struct drm_mode_destroy_dumb) { .handle = buffer }));
+   (void) drmIoctl(wsi->fd, DRM_IOCTL_GEM_CLOSE,
+                   &((struct drm_gem_close) { .handle = buffer }));
 }
 
 static VkResult
@@ -851,6 +1100,8 @@ wsi_display_swapchain_destroy(struct wsi_swapchain *drv_chain,
 
    for (uint32_t i = 0; i < chain->base.image_count; i++)
       wsi_display_image_finish(drv_chain, allocator, &chain->images[i]);
+
+   wsi_swapchain_finish(&chain->base);
    vk_free(allocator, chain);
    return VK_SUCCESS;
 }
@@ -904,6 +1155,8 @@ wsi_display_page_flip_handler2(int fd,
       chain->status = result;
 }
 
+static void wsi_display_fence_event_handler(struct wsi_display_fence *fence);
+
 static void wsi_display_page_flip_handler(int fd,
                                           unsigned int frame,
                                           unsigned int sec,
@@ -913,12 +1166,32 @@ static void wsi_display_page_flip_handler(int fd,
    wsi_display_page_flip_handler2(fd, frame, sec, usec, 0, data);
 }
 
+static void wsi_display_vblank_handler(int fd, unsigned int frame,
+                                       unsigned int sec, unsigned int usec,
+                                       void *data)
+{
+   struct wsi_display_fence *fence = data;
+
+   wsi_display_fence_event_handler(fence);
+}
+
+static void wsi_display_sequence_handler(int fd, uint64_t frame,
+                                         uint64_t nsec, uint64_t user_data)
+{
+   struct wsi_display_fence *fence =
+      (struct wsi_display_fence *) (uintptr_t) user_data;
+
+   wsi_display_fence_event_handler(fence);
+}
+
 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
+   .vblank_handler = wsi_display_vblank_handler,
+   .sequence_handler = wsi_display_sequence_handler,
 };
 
 static void *
@@ -984,8 +1257,7 @@ wsi_display_wait_for_event(struct wsi_display *wsi,
 
 static VkResult
 wsi_display_acquire_next_image(struct wsi_swapchain *drv_chain,
-                               uint64_t timeout,
-                               VkSemaphore semaphore,
+                               const VkAcquireNextImageInfoKHR *info,
                                uint32_t *image_index)
 {
    struct wsi_display_swapchain *chain =
@@ -998,6 +1270,7 @@ wsi_display_acquire_next_image(struct wsi_swapchain *drv_chain,
    if (chain->status != VK_SUCCESS)
       return chain->status;
 
+   uint64_t timeout = info->timeout;
    if (timeout != 0 && timeout != UINT64_MAX)
       timeout = wsi_rel_to_abs_time(timeout);
 
@@ -1022,7 +1295,7 @@ wsi_display_acquire_next_image(struct wsi_swapchain *drv_chain,
       ret = wsi_display_wait_for_event(wsi, timeout);
 
       if (ret && ret != ETIMEDOUT) {
-         result = VK_ERROR_OUT_OF_DATE_KHR;
+         result = VK_ERROR_SURFACE_LOST_KHR;
          goto done;
       }
    }
@@ -1084,7 +1357,7 @@ wsi_display_crtc_solo(struct wsi_display *wsi,
  * which is currently idle.
  */
 static uint32_t
-wsi_display_select_crtc(struct wsi_display_connector *connector,
+wsi_display_select_crtc(const struct wsi_display_connector *connector,
                         drmModeResPtr mode_res,
                         drmModeConnectorPtr drm_connector)
 {
@@ -1130,7 +1403,7 @@ wsi_display_setup_connector(wsi_display_connector *connector,
       if (errno == ENOMEM)
          result = VK_ERROR_OUT_OF_HOST_MEMORY;
       else
-         result = VK_ERROR_OUT_OF_DATE_KHR;
+         result = VK_ERROR_SURFACE_LOST_KHR;
       goto bail;
    }
 
@@ -1141,7 +1414,7 @@ wsi_display_setup_connector(wsi_display_connector *connector,
       if (errno == ENOMEM)
          result = VK_ERROR_OUT_OF_HOST_MEMORY;
       else
-         result = VK_ERROR_OUT_OF_DATE_KHR;
+         result = VK_ERROR_SURFACE_LOST_KHR;
       goto bail_mode_res;
    }
 
@@ -1150,7 +1423,7 @@ wsi_display_setup_connector(wsi_display_connector *connector,
       connector->crtc_id = wsi_display_select_crtc(connector,
                                                    mode_res, drm_connector);
       if (!connector->crtc_id) {
-         result = VK_ERROR_OUT_OF_DATE_KHR;
+         result = VK_ERROR_SURFACE_LOST_KHR;
          goto bail_connector;
       }
    }
@@ -1168,7 +1441,7 @@ wsi_display_setup_connector(wsi_display_connector *connector,
       }
 
       if (!drm_mode) {
-         result = VK_ERROR_OUT_OF_DATE_KHR;
+         result = VK_ERROR_SURFACE_LOST_KHR;
          goto bail_connector;
       }
 
@@ -1185,6 +1458,160 @@ bail:
 
 }
 
+static VkResult
+wsi_display_fence_wait(struct wsi_fence *fence_wsi, uint64_t timeout)
+{
+   const struct wsi_device *wsi_device = fence_wsi->wsi_device;
+   struct wsi_display *wsi =
+      (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
+   struct wsi_display_fence *fence = (struct wsi_display_fence *) fence_wsi;
+
+   wsi_display_debug("%9lu wait fence %lu %ld\n",
+                     pthread_self(), fence->sequence,
+                     (int64_t) (timeout - wsi_common_get_current_time()));
+   wsi_display_debug_code(uint64_t start_ns = wsi_common_get_current_time());
+   pthread_mutex_lock(&wsi->wait_mutex);
+
+   VkResult result;
+   int ret = 0;
+   for (;;) {
+      if (fence->event_received) {
+         wsi_display_debug("%9lu fence %lu passed\n",
+                           pthread_self(), fence->sequence);
+         result = VK_SUCCESS;
+         break;
+      }
+
+      if (ret == ETIMEDOUT) {
+         wsi_display_debug("%9lu fence %lu timeout\n",
+                           pthread_self(), fence->sequence);
+         result = VK_TIMEOUT;
+         break;
+      }
+
+      ret = wsi_display_wait_for_event(wsi, timeout);
+
+      if (ret && ret != ETIMEDOUT) {
+         wsi_display_debug("%9lu fence %lu error\n",
+                           pthread_self(), fence->sequence);
+         result = VK_ERROR_DEVICE_LOST;
+         break;
+      }
+   }
+   pthread_mutex_unlock(&wsi->wait_mutex);
+   wsi_display_debug("%9lu fence wait %f ms\n",
+                     pthread_self(),
+                     ((int64_t) (wsi_common_get_current_time() - start_ns)) /
+                     1.0e6);
+   return result;
+}
+
+static void
+wsi_display_fence_check_free(struct wsi_display_fence *fence)
+{
+   if (fence->event_received && fence->destroyed)
+      vk_free(fence->base.alloc, fence);
+}
+
+static void wsi_display_fence_event_handler(struct wsi_display_fence *fence)
+{
+   fence->event_received = true;
+   wsi_display_fence_check_free(fence);
+}
+
+static void
+wsi_display_fence_destroy(struct wsi_fence *fence_wsi)
+{
+   struct wsi_display_fence *fence = (struct wsi_display_fence *) fence_wsi;
+
+   assert(!fence->destroyed);
+   fence->destroyed = true;
+   wsi_display_fence_check_free(fence);
+}
+
+static struct wsi_display_fence *
+wsi_display_fence_alloc(VkDevice device,
+                        const struct wsi_device *wsi_device,
+                        VkDisplayKHR display,
+                        const VkAllocationCallbacks *allocator)
+{
+   struct wsi_display *wsi =
+      (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
+   struct wsi_display_fence *fence =
+      vk_zalloc2(wsi->alloc, allocator, sizeof (*fence),
+                8, VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
+
+   if (!fence)
+      return NULL;
+
+   fence->base.device = device;
+   fence->base.display = display;
+   fence->base.wsi_device = wsi_device;
+   fence->base.alloc = allocator ? allocator : wsi->alloc;
+   fence->base.wait = wsi_display_fence_wait;
+   fence->base.destroy = wsi_display_fence_destroy;
+   fence->event_received = false;
+   fence->destroyed = false;
+   fence->sequence = ++fence_sequence;
+   return fence;
+}
+
+static VkResult
+wsi_register_vblank_event(struct wsi_display_fence *fence,
+                          const struct wsi_device *wsi_device,
+                          VkDisplayKHR display,
+                          uint32_t flags,
+                          uint64_t frame_requested,
+                          uint64_t *frame_queued)
+{
+   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);
+
+   if (wsi->fd < 0)
+      return VK_ERROR_INITIALIZATION_FAILED;
+
+   for (;;) {
+      int ret = drmCrtcQueueSequence(wsi->fd, connector->crtc_id,
+                                     flags,
+                                     frame_requested,
+                                     frame_queued,
+                                     (uintptr_t) fence);
+
+      if (!ret)
+         return VK_SUCCESS;
+
+      if (errno != ENOMEM) {
+
+         /* Something unexpected happened. Pause for a moment so the
+          * application doesn't just spin and then return a failure indication
+          */
+
+         wsi_display_debug("queue vblank event %lu failed\n", fence->sequence);
+         struct timespec delay = {
+            .tv_sec = 0,
+            .tv_nsec = 100000000ull,
+         };
+         nanosleep(&delay, NULL);
+         return VK_ERROR_OUT_OF_HOST_MEMORY;
+      }
+
+      /* The kernel event queue is full. Wait for some events to be
+       * processed and try again
+       */
+
+      pthread_mutex_lock(&wsi->wait_mutex);
+      ret = wsi_display_wait_for_event(wsi, wsi_rel_to_abs_time(100000000ull));
+      pthread_mutex_unlock(&wsi->wait_mutex);
+
+      if (ret) {
+         wsi_display_debug("vblank queue full, event wait failed\n");
+         return VK_ERROR_OUT_OF_HOST_MEMORY;
+      }
+   }
+}
+
 /*
  * Check to see if the kernel has no flip queued and if there's an image
  * waiting to be displayed.
@@ -1201,7 +1628,7 @@ _wsi_display_queue_next(struct wsi_swapchain *drv_chain)
    wsi_display_connector *connector = display_mode->connector;
 
    if (wsi->fd < 0)
-      return VK_ERROR_OUT_OF_DATE_KHR;
+      return VK_ERROR_SURFACE_LOST_KHR;
 
    if (display_mode != connector->current_mode)
       connector->active = false;
@@ -1260,6 +1687,15 @@ _wsi_display_queue_next(struct wsi_swapchain *drv_chain)
                               &connector->id, 1,
                               &connector->current_drm_mode);
          if (ret == 0) {
+            /* Disable the HW cursor as the app doesn't have a mechanism
+             * to control it.
+             * Refer to question 12 of the VK_KHR_display spec.
+             */
+            ret = drmModeSetCursor(wsi->fd, connector->crtc_id, 0, 0, 0 );
+            if (ret != 0) {
+               wsi_display_debug("failed to hide cursor err %d %s\n", ret, strerror(-ret));
+            }
+
             /* Assume that the mode set is synchronous and that any
              * previous image is now idle.
              */
@@ -1273,7 +1709,7 @@ _wsi_display_queue_next(struct wsi_swapchain *drv_chain)
       if (ret != -EACCES) {
          connector->active = false;
          image->state = WSI_IMAGE_IDLE;
-         return VK_ERROR_OUT_OF_DATE_KHR;
+         return VK_ERROR_SURFACE_LOST_KHR;
       }
 
       /* Some other VT is currently active. Sit here waiting for
@@ -1324,7 +1760,6 @@ 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)
@@ -1345,12 +1780,16 @@ wsi_display_surface_create_swapchain(
 
    VkResult result = wsi_swapchain_init(wsi_device, &chain->base, device,
                                         create_info, allocator);
+   if (result != VK_SUCCESS) {
+      vk_free(allocator, chain);
+      return result;
+   }
 
    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.present_mode = wsi_swapchain_get_present_mode(wsi_device, create_info);
    chain->base.image_count = num_images;
 
    chain->wsi = wsi;
@@ -1405,6 +1844,30 @@ fail_attr_init:
    return ret;
 }
 
+
+/*
+ * Local version fo the libdrm helper. Added to avoid depending on bleeding
+ * edge version of the library.
+ */
+static int
+local_drmIsMaster(int fd)
+{
+   /* Detect master by attempting something that requires master.
+    *
+    * Authenticating magic tokens requires master and 0 is an
+    * internal kernel detail which we could use. Attempting this on
+    * a master fd would fail therefore fail with EINVAL because 0
+    * is invalid.
+    *
+    * A non-master fd will fail with EACCES, as the kernel checks
+    * for master before attempting to do anything else.
+    *
+    * Since we don't want to leak implementation details, use
+    * EACCES.
+    */
+   return drmAuthMagic(fd, 0) != -EACCES;
+}
+
 VkResult
 wsi_display_init_wsi(struct wsi_device *wsi_device,
                      const VkAllocationCallbacks *alloc,
@@ -1420,6 +1883,9 @@ wsi_display_init_wsi(struct wsi_device *wsi_device,
    }
 
    wsi->fd = display_fd;
+   if (wsi->fd != -1 && !local_drmIsMaster(wsi->fd))
+      wsi->fd = -1;
+
    wsi->alloc = alloc;
 
    list_inithead(&wsi->connectors);
@@ -1436,11 +1902,11 @@ wsi_display_init_wsi(struct wsi_device *wsi_device,
    }
 
    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.get_present_rectangles = wsi_display_surface_get_present_rectangles;
    wsi->base.create_swapchain = wsi_display_surface_create_swapchain;
 
    wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY] = &wsi->base;
@@ -1938,6 +2404,7 @@ wsi_acquire_xlib_display(VkPhysicalDevice physical_device,
    if (!crtc)
       return VK_ERROR_INITIALIZATION_FAILED;
 
+#ifdef HAVE_DRI3_MODIFIERS
    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,
@@ -1958,6 +2425,7 @@ wsi_acquire_xlib_display(VkPhysicalDevice physical_device,
       return VK_ERROR_INITIALIZATION_FAILED;
 
    wsi->fd = fd;
+#endif
 
    return VK_SUCCESS;
 }
@@ -1976,8 +2444,121 @@ wsi_get_randr_output_display(VkPhysicalDevice physical_device,
    if (connector)
       *display = wsi_display_connector_to_handle(connector);
    else
-      *display = NULL;
+      *display = VK_NULL_HANDLE;
    return VK_SUCCESS;
 }
 
 #endif
+
+/* VK_EXT_display_control */
+VkResult
+wsi_display_power_control(VkDevice device,
+                          struct wsi_device *wsi_device,
+                          VkDisplayKHR display,
+                          const VkDisplayPowerInfoEXT *display_power_info)
+{
+   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 mode;
+
+   if (wsi->fd < 0)
+      return VK_ERROR_INITIALIZATION_FAILED;
+
+   switch (display_power_info->powerState) {
+   case VK_DISPLAY_POWER_STATE_OFF_EXT:
+      mode = DRM_MODE_DPMS_OFF;
+      break;
+   case VK_DISPLAY_POWER_STATE_SUSPEND_EXT:
+      mode = DRM_MODE_DPMS_SUSPEND;
+      break;
+   default:
+      mode = DRM_MODE_DPMS_ON;
+      break;
+   }
+   drmModeConnectorSetProperty(wsi->fd,
+                               connector->id,
+                               connector->dpms_property,
+                               mode);
+   return VK_SUCCESS;
+}
+
+VkResult
+wsi_register_device_event(VkDevice device,
+                          struct wsi_device *wsi_device,
+                          const VkDeviceEventInfoEXT *device_event_info,
+                          const VkAllocationCallbacks *allocator,
+                          struct wsi_fence **fence_p)
+{
+   return VK_ERROR_FEATURE_NOT_PRESENT;
+}
+
+VkResult
+wsi_register_display_event(VkDevice device,
+                           struct wsi_device *wsi_device,
+                           VkDisplayKHR display,
+                           const VkDisplayEventInfoEXT *display_event_info,
+                           const VkAllocationCallbacks *allocator,
+                           struct wsi_fence **fence_p)
+{
+   struct wsi_display *wsi =
+      (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
+   struct wsi_display_fence *fence;
+   VkResult ret;
+
+   switch (display_event_info->displayEvent) {
+   case VK_DISPLAY_EVENT_TYPE_FIRST_PIXEL_OUT_EXT:
+
+      fence = wsi_display_fence_alloc(device, wsi_device, display, allocator);
+
+      if (!fence)
+         return VK_ERROR_OUT_OF_HOST_MEMORY;
+
+      ret = wsi_register_vblank_event(fence, wsi_device, display,
+                                      DRM_CRTC_SEQUENCE_RELATIVE, 1, NULL);
+
+      if (ret == VK_SUCCESS)
+         *fence_p = &fence->base;
+      else if (fence != NULL)
+         vk_free2(wsi->alloc, allocator, fence);
+
+      break;
+   default:
+      ret = VK_ERROR_FEATURE_NOT_PRESENT;
+      break;
+   }
+
+   return ret;
+}
+
+
+VkResult
+wsi_get_swapchain_counter(VkDevice device,
+                          struct wsi_device *wsi_device,
+                          VkSwapchainKHR _swapchain,
+                          VkSurfaceCounterFlagBitsEXT flag_bits,
+                          uint64_t *value)
+{
+   struct wsi_display *wsi =
+      (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
+   struct wsi_display_swapchain *swapchain =
+      (struct wsi_display_swapchain *) wsi_swapchain_from_handle(_swapchain);
+   struct wsi_display_connector *connector =
+      wsi_display_mode_from_handle(swapchain->surface->displayMode)->connector;
+
+   if (wsi->fd < 0)
+      return VK_ERROR_INITIALIZATION_FAILED;
+
+   if (!connector->active) {
+      *value = 0;
+      return VK_SUCCESS;
+   }
+
+   int ret = drmCrtcGetSequence(wsi->fd, connector->crtc_id, value, NULL);
+   if (ret)
+      *value = 0;
+
+   return VK_SUCCESS;
+}
+