diff mbox

[5/7] vulkan: add VK_EXT_display_control [v4]

Message ID 20180210044516.16944-6-keithp@keithp.com (mailing list archive)
State New, archived
Headers show

Commit Message

Keith Packard Feb. 10, 2018, 4:45 a.m. UTC
This extension provides fences and frame count information to direct
display contexts. It uses new kernel ioctls to provide 64-bits of
vblank sequence and nanosecond resolution.

v2: Remove DRM_CRTC_SEQUENCE_FIRST_PIXEL_OUT flag. This has
    been removed from the proposed kernel API.

    Add NULL parameter to drmCrtcQueueSequence ioctl as we
    don't care what sequence the event was actually queued to.

v3: Adapt to pthread clock switch to MONOTONIC

v4: Add support for anv

Signed-off-by: Keith Packard <keithp@keithp.com>
---
 src/amd/vulkan/radv_extensions.py   |   1 +
 src/amd/vulkan/radv_private.h       |  11 +-
 src/amd/vulkan/radv_wsi_display.c   | 109 ++++++++++++++
 src/intel/vulkan/anv_extensions.py  |   1 +
 src/intel/vulkan/anv_private.h      |   4 +
 src/intel/vulkan/anv_queue.c        |  59 +++++++-
 src/intel/vulkan/anv_wsi_display.c  | 103 +++++++++++++
 src/vulkan/wsi/wsi_common.h         |   9 ++
 src/vulkan/wsi/wsi_common_display.c | 286 +++++++++++++++++++++++++++++++++++-
 src/vulkan/wsi/wsi_common_display.h |  29 ++++
 10 files changed, 603 insertions(+), 9 deletions(-)
diff mbox

Patch

diff --git a/src/amd/vulkan/radv_extensions.py b/src/amd/vulkan/radv_extensions.py
index 3f315e074b5..775d1ed4be6 100644
--- a/src/amd/vulkan/radv_extensions.py
+++ b/src/amd/vulkan/radv_extensions.py
@@ -85,6 +85,7 @@  EXTENSIONS = [
     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_EXT_display_surface_counter',           1, 'VK_USE_PLATFORM_DISPLAY_KHR'),
+    Extension('VK_EXT_display_control',                   1, 'VK_USE_PLATFORM_DISPLAY_KHR'),
     Extension('VK_KHX_multiview',                         1, '!ANDROID'),
     Extension('VK_EXT_debug_report',                      9, True),
     Extension('VK_EXT_discard_rectangles',                1, True),
diff --git a/src/amd/vulkan/radv_private.h b/src/amd/vulkan/radv_private.h
index 1e3719bcc4f..2aee38f0159 100644
--- a/src/amd/vulkan/radv_private.h
+++ b/src/amd/vulkan/radv_private.h
@@ -1647,8 +1647,17 @@  void radv_initialise_cmask(struct radv_cmd_buffer *cmd_buffer,
 void radv_initialize_dcc(struct radv_cmd_buffer *cmd_buffer,
 			 struct radv_image *image, uint32_t value);
 
+enum radv_fence_type {
+        RADV_FENCE_TYPE_WINSYS = 0,
+        RADV_FENCE_TYPE_WSI = 1
+};
+
 struct radv_fence {
-	struct radeon_winsys_fence *fence;
+        enum radv_fence_type type;
+        union {
+                struct radeon_winsys_fence      *fence;
+                struct wsi_fence                *fence_wsi;
+        };
 	bool submitted;
 	bool signalled;
 
diff --git a/src/amd/vulkan/radv_wsi_display.c b/src/amd/vulkan/radv_wsi_display.c
index 9b76ce623b0..5dd66b34a1a 100644
--- a/src/amd/vulkan/radv_wsi_display.c
+++ b/src/amd/vulkan/radv_wsi_display.c
@@ -182,3 +182,112 @@  radv_GetRandROutputDisplayEXT(VkPhysicalDevice  physical_device,
                                        display);
 }
 #endif /* VK_USE_PLATFORM_XLIB_XRANDR_EXT */
+
+/* VK_EXT_display_control */
+
+VkResult
+radv_DisplayPowerControlEXT(VkDevice                    _device,
+                            VkDisplayKHR                display,
+                            const VkDisplayPowerInfoEXT *display_power_info)
+{
+   RADV_FROM_HANDLE(radv_device, device, _device);
+
+   return wsi_display_power_control(_device,
+                                    &device->physical_device->wsi_device,
+                                    display,
+                                    display_power_info);
+}
+
+VkResult
+radv_RegisterDeviceEventEXT(VkDevice                    _device,
+                            const VkDeviceEventInfoEXT  *device_event_info,
+                            const VkAllocationCallbacks *allocator,
+                            VkFence                     *_fence)
+{
+   RADV_FROM_HANDLE(radv_device, device, _device);
+   const VkAllocationCallbacks  *alloc;
+   struct radv_fence            *fence;
+   VkResult                     ret;
+
+   if (allocator)
+     alloc = allocator;
+   else
+     alloc = &device->instance->alloc;
+
+   fence = vk_alloc(alloc, sizeof (*fence), 8,
+                    VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
+   if (!fence)
+      return VK_ERROR_OUT_OF_HOST_MEMORY;
+
+   fence->type = RADV_FENCE_TYPE_WSI;
+   fence->submitted = true;
+   fence->signalled = false;
+
+   ret = wsi_register_device_event(_device,
+                                   &device->physical_device->wsi_device,
+                                   device_event_info,
+                                   alloc,
+                                   &fence->fence_wsi);
+   if (ret == VK_SUCCESS)
+      *_fence = radv_fence_to_handle(fence);
+   else
+      vk_free(alloc, fence);
+   return ret;
+}
+
+VkResult
+radv_RegisterDisplayEventEXT(VkDevice                           _device,
+                             VkDisplayKHR                       display,
+                             const VkDisplayEventInfoEXT        *display_event_info,
+                             const VkAllocationCallbacks        *allocator,
+                             VkFence                            *_fence)
+{
+   RADV_FROM_HANDLE(radv_device, device, _device);
+
+   const VkAllocationCallbacks  *alloc;
+   struct radv_fence            *fence;
+   VkResult                     ret;
+
+   if (allocator)
+     alloc = allocator;
+   else
+     alloc = &device->instance->alloc;
+
+   fence = vk_alloc(alloc, sizeof (*fence), 8,
+                    VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
+   if (!fence)
+      return VK_ERROR_OUT_OF_HOST_MEMORY;
+
+   fence->type = RADV_FENCE_TYPE_WSI;
+   fence->submitted = true;
+   fence->signalled = false;
+
+   ret = wsi_register_display_event(_device,
+                                    &device->physical_device->wsi_device,
+                                    display,
+                                    display_event_info,
+                                    alloc,
+                                    &(fence->fence_wsi));
+
+   if (ret == VK_SUCCESS)
+      *_fence = radv_fence_to_handle(fence);
+   else
+      vk_free(alloc, fence);
+   return ret;
+}
+
+VkResult
+radv_GetSwapchainCounterEXT(VkDevice                    _device,
+                            VkSwapchainKHR              swapchain,
+                            VkSurfaceCounterFlagBitsEXT flag_bits,
+                            uint64_t                    *value)
+{
+   RADV_FROM_HANDLE(radv_device, device, _device);
+
+   return wsi_get_swapchain_counter(_device,
+                                    &device->physical_device->wsi_device,
+                                    swapchain,
+                                    flag_bits,
+                                    value);
+}
+
diff --git a/src/intel/vulkan/anv_extensions.py b/src/intel/vulkan/anv_extensions.py
index 8265077d894..f44598b305a 100644
--- a/src/intel/vulkan/anv_extensions.py
+++ b/src/intel/vulkan/anv_extensions.py
@@ -90,6 +90,7 @@  EXTENSIONS = [
     Extension('VK_EXT_debug_report',                      8, True),
     Extension('VK_EXT_external_memory_dma_buf',           1, True),
     Extension('VK_EXT_display_surface_counter',           1, 'VK_USE_PLATFORM_DISPLAY_KHR'),
+    Extension('VK_EXT_display_control',                   1, 'VK_USE_PLATFORM_DISPLAY_KHR'),
 ]
 
 class VkVersion:
diff --git a/src/intel/vulkan/anv_private.h b/src/intel/vulkan/anv_private.h
index d38dd9e4220..1d3e5fcd921 100644
--- a/src/intel/vulkan/anv_private.h
+++ b/src/intel/vulkan/anv_private.h
@@ -1941,6 +1941,7 @@  enum anv_fence_type {
    ANV_FENCE_TYPE_NONE = 0,
    ANV_FENCE_TYPE_BO,
    ANV_FENCE_TYPE_SYNCOBJ,
+   ANV_FENCE_TYPE_WSI,
 };
 
 enum anv_bo_fence_state {
@@ -1975,6 +1976,9 @@  struct anv_fence_impl {
 
       /** DRM syncobj handle for syncobj-based fences */
       uint32_t syncobj;
+
+      /** WSI fence */
+      struct wsi_fence *fence_wsi;
    };
 };
 
diff --git a/src/intel/vulkan/anv_queue.c b/src/intel/vulkan/anv_queue.c
index c6b2e01c628..d0bfad742d7 100644
--- a/src/intel/vulkan/anv_queue.c
+++ b/src/intel/vulkan/anv_queue.c
@@ -320,6 +320,10 @@  anv_fence_impl_cleanup(struct anv_device *device,
    case ANV_FENCE_TYPE_SYNCOBJ:
       anv_gem_syncobj_destroy(device, impl->syncobj);
       return;
+
+   case ANV_FENCE_TYPE_WSI:
+      impl->fence_wsi->destroy(impl->fence_wsi);
+      return;
    }
 
    unreachable("Invalid fence type");
@@ -465,11 +469,32 @@  anv_wait_for_syncobj_fences(struct anv_device *device,
    uint32_t *syncobjs = vk_zalloc(&device->alloc,
                                   sizeof(*syncobjs) * fenceCount, 8,
                                   VK_SYSTEM_ALLOCATION_SCOPE_COMMAND);
+   uint32_t syncobjCount = 0;
    if (!syncobjs)
       return vk_error(VK_ERROR_OUT_OF_HOST_MEMORY);
 
    for (uint32_t i = 0; i < fenceCount; i++) {
       ANV_FROM_HANDLE(anv_fence, fence, pFences[i]);
+
+      if (fence->permanent.type == ANV_FENCE_TYPE_WSI) {
+         struct anv_fence_impl *impl = &fence->permanent;
+         bool expired = impl->fence_wsi->wait(impl->fence_wsi, true, _timeout);
+
+         VkResult result;
+
+         if (!expired) {
+            result = VK_TIMEOUT;
+            goto done;
+         }
+         if (!waitAll) {
+            result = VK_SUCCESS;
+            goto done;
+         }
+         continue;
+      done:
+         vk_free(&device->alloc, syncobjs);
+         return result;
+      }
       assert(fence->permanent.type == ANV_FENCE_TYPE_SYNCOBJ);
 
       struct anv_fence_impl *impl =
@@ -477,7 +502,7 @@  anv_wait_for_syncobj_fences(struct anv_device *device,
          &fence->temporary : &fence->permanent;
 
       assert(impl->type == ANV_FENCE_TYPE_SYNCOBJ);
-      syncobjs[i] = impl->syncobj;
+      syncobjs[syncobjCount++] = impl->syncobj;
    }
 
    int64_t abs_timeout_ns = 0;
@@ -499,7 +524,7 @@  anv_wait_for_syncobj_fences(struct anv_device *device,
     */
    int ret;
    do {
-      ret = anv_gem_syncobj_wait(device, syncobjs, fenceCount,
+      ret = anv_gem_syncobj_wait(device, syncobjs, syncobjCount,
                                  abs_timeout_ns, waitAll);
    } while (ret == -1 && errno == ETIME && gettime_ns() < abs_timeout_ns);
 
@@ -545,14 +570,33 @@  anv_wait_for_bo_fences(struct anv_device *device,
       for (uint32_t i = 0; i < fenceCount; i++) {
          ANV_FROM_HANDLE(anv_fence, fence, pFences[i]);
 
-         /* This function assumes that all fences are BO fences and that they
-          * have no temporary state.  Since BO fences will never be exported,
-          * this should be a safe assumption.
+         /* This function assumes that all fences have no temporary
+          * state.Since BO fences will never be exported, this should be a
+          * safe assumption.
           */
-         assert(fence->permanent.type == ANV_FENCE_TYPE_BO);
          assert(fence->temporary.type == ANV_FENCE_TYPE_NONE);
          struct anv_fence_impl *impl = &fence->permanent;
 
+         /* This function assumes that all fences are either BO fences or WSI
+          * fences
+          */
+
+         if (impl->type == ANV_FENCE_TYPE_WSI) {
+            bool expired = impl->fence_wsi->wait(impl->fence_wsi, true, timeout);
+
+            if (!expired) {
+               result = VK_TIMEOUT;
+               goto done;
+            }
+            if (!waitAll) {
+               result = VK_SUCCESS;
+               goto done;
+            }
+            continue;
+         }
+
+         assert(impl->type == ANV_FENCE_TYPE_BO);
+
          switch (impl->bo.state) {
          case ANV_BO_FENCE_STATE_RESET:
             /* This fence hasn't been submitted yet, we'll catch it the next
@@ -610,7 +654,8 @@  anv_wait_for_bo_fences(struct anv_device *device,
          uint32_t now_pending_fences = 0;
          for (uint32_t i = 0; i < fenceCount; i++) {
             ANV_FROM_HANDLE(anv_fence, fence, pFences[i]);
-            if (fence->permanent.bo.state == ANV_BO_FENCE_STATE_RESET)
+            if (fence->permanent.type == ANV_FENCE_TYPE_BO &&
+                fence->permanent.bo.state == ANV_BO_FENCE_STATE_RESET)
                now_pending_fences++;
          }
          assert(now_pending_fences <= pending_fences);
diff --git a/src/intel/vulkan/anv_wsi_display.c b/src/intel/vulkan/anv_wsi_display.c
index e87aed49f7d..32a948d76ca 100644
--- a/src/intel/vulkan/anv_wsi_display.c
+++ b/src/intel/vulkan/anv_wsi_display.c
@@ -168,3 +168,106 @@  anv_GetRandROutputDisplayEXT(VkPhysicalDevice  physical_device,
                                        display);
 }
 #endif /* VK_USE_PLATFORM_XLIB_XRANDR_EXT */
+
+/* VK_EXT_display_control */
+
+VkResult
+anv_DisplayPowerControlEXT(VkDevice                    _device,
+                            VkDisplayKHR                display,
+                            const VkDisplayPowerInfoEXT *display_power_info)
+{
+   ANV_FROM_HANDLE(anv_device, device, _device);
+
+   return wsi_display_power_control(_device,
+                                    &device->instance->physicalDevice.wsi_device,
+                                    display,
+                                    display_power_info);
+}
+
+VkResult
+anv_RegisterDeviceEventEXT(VkDevice                    _device,
+                            const VkDeviceEventInfoEXT  *device_event_info,
+                            const VkAllocationCallbacks *allocator,
+                            VkFence                     *_fence)
+{
+   ANV_FROM_HANDLE(anv_device, device, _device);
+   const VkAllocationCallbacks  *alloc;
+   struct anv_fence            *fence;
+   VkResult                     ret;
+
+   if (allocator)
+     alloc = allocator;
+   else
+     alloc = &device->instance->alloc;
+
+   fence = vk_alloc(alloc, sizeof (*fence), 8,
+                    VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
+   if (!fence)
+      return vk_error(VK_ERROR_OUT_OF_HOST_MEMORY);
+
+   fence->permanent.type = ANV_FENCE_TYPE_WSI;
+
+   ret = wsi_register_device_event(_device,
+                                   &device->instance->physicalDevice.wsi_device,
+                                   device_event_info,
+                                   alloc,
+                                   &fence->permanent.fence_wsi);
+   if (ret == VK_SUCCESS)
+      *_fence = anv_fence_to_handle(fence);
+   else
+      vk_free(alloc, fence);
+   return ret;
+}
+
+VkResult
+anv_RegisterDisplayEventEXT(VkDevice                           _device,
+                             VkDisplayKHR                       display,
+                             const VkDisplayEventInfoEXT        *display_event_info,
+                             const VkAllocationCallbacks        *allocator,
+                             VkFence                            *_fence)
+{
+   ANV_FROM_HANDLE(anv_device, device, _device);
+   const VkAllocationCallbacks  *alloc;
+   struct anv_fence            *fence;
+   VkResult                     ret;
+
+   if (allocator)
+     alloc = allocator;
+   else
+     alloc = &device->instance->alloc;
+
+   fence = vk_zalloc2(&device->alloc, allocator, sizeof (*fence), 8,
+                      VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
+   if (!fence)
+      return VK_ERROR_OUT_OF_HOST_MEMORY;
+
+   fence->permanent.type = ANV_FENCE_TYPE_WSI;
+
+   ret = wsi_register_display_event(_device,
+                                    &device->instance->physicalDevice.wsi_device,
+                                    display,
+                                    display_event_info,
+                                    alloc,
+                                    &(fence->permanent.fence_wsi));
+
+   if (ret == VK_SUCCESS)
+      *_fence = anv_fence_to_handle(fence);
+   else
+      vk_free(alloc, fence);
+   return ret;
+}
+
+VkResult
+anv_GetSwapchainCounterEXT(VkDevice                    _device,
+                            VkSwapchainKHR              swapchain,
+                            VkSurfaceCounterFlagBitsEXT flag_bits,
+                            uint64_t                    *value)
+{
+   ANV_FROM_HANDLE(anv_device, device, _device);
+
+   return wsi_get_swapchain_counter(_device,
+                                    &device->instance->physicalDevice.wsi_device,
+                                    swapchain,
+                                    flag_bits,
+                                    value);
+}
diff --git a/src/vulkan/wsi/wsi_common.h b/src/vulkan/wsi/wsi_common.h
index 124d096170a..e504f4120ad 100644
--- a/src/vulkan/wsi/wsi_common.h
+++ b/src/vulkan/wsi/wsi_common.h
@@ -48,6 +48,15 @@  struct wsi_memory_allocate_info {
     bool implicit_sync;
 };
 
+struct wsi_fence {
+   VkDevice                     device;
+   const struct wsi_device      *wsi_device;
+   VkDisplayKHR                 display;
+   const VkAllocationCallbacks  *alloc;
+   bool                         (*wait)(struct wsi_fence *fence, bool absolute, uint64_t timeout);
+   void                         (*destroy)(struct wsi_fence *fence);
+};
+
 struct wsi_interface;
 
 #define VK_ICD_WSI_PLATFORM_MAX 6
diff --git a/src/vulkan/wsi/wsi_common_display.c b/src/vulkan/wsi/wsi_common_display.c
index e63700e2e65..c3608f13e54 100644
--- a/src/vulkan/wsi/wsi_common_display.c
+++ b/src/vulkan/wsi/wsi_common_display.c
@@ -77,6 +77,7 @@  typedef struct wsi_display_connector {
    bool                         active;
    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
@@ -124,6 +125,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)
 
@@ -271,6 +281,7 @@  wsi_display_get_connector(struct wsi_device             *wsi_device,
    drmModeConnectorPtr          drm_connector;
    VkResult                     result;
    int                          m;
+   int                          p;
 
    if (wsi->master_fd < 0)
       return NULL;
@@ -292,6 +303,18 @@  wsi_display_get_connector(struct wsi_device             *wsi_device,
 
    connector->connected = drm_connector->connection != DRM_MODE_DISCONNECTED;
 
+   /* Look for a DPMS property */
+   for (p = 0; p < drm_connector->count_props; p++) {
+      drmModePropertyPtr prop = drmModeGetProperty(wsi->master_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);
 
@@ -677,7 +700,7 @@  wsi_display_surface_get_capabilities2ext(VkIcdSurfaceBase *icd_surface,
    caps->currentTransform = khr_caps.currentTransform;
    caps->supportedCompositeAlpha = khr_caps.supportedCompositeAlpha;
    caps->supportedUsageFlags = khr_caps.supportedUsageFlags;
-   caps->supportedSurfaceCounters = 0;
+   caps->supportedSurfaceCounters = VK_SURFACE_COUNTER_VBLANK_EXT;
    return ret;
 }
 
@@ -865,12 +888,20 @@  static void wsi_display_page_flip_handler(int fd, unsigned int frame,
    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);
+
+static void wsi_display_sequence_handler(int fd, uint64_t frame,
+                                         uint64_t ns, uint64_t user_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
+   .vblank_handler = wsi_display_vblank_handler,
+   .sequence_handler = wsi_display_sequence_handler,
 };
 
 static void *
@@ -1117,6 +1148,135 @@  bail:
 
 }
 
+static bool
+wsi_display_fence_wait(struct wsi_fence *fence_wsi,
+                       bool absolute,
+                       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;
+   int                          ret = 0;
+   bool                         value;
+
+   if (!absolute)
+      timeout += wsi_get_current_monotonic();
+
+   wsi_display_debug("%9lu wait fence %lu %ld\n", pthread_self(), fence->sequence, (int64_t) (timeout - wsi_get_current_monotonic()));
+   wsi_display_debug_code(uint64_t start_ns = wsi_get_current_monotonic());
+   pthread_mutex_lock(&wsi->wait_mutex);
+   for (;;) {
+      if (fence->event_received) {
+         wsi_display_debug("%9lu fence %lu passed\n", pthread_self(), fence->sequence);
+         value = true;
+         break;
+      }
+
+      if (ret == ETIMEDOUT) {
+         wsi_display_debug("%9lu fence %lu timeout\n", pthread_self(), fence->sequence);
+         value = false;
+         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);
+         value = false;
+         break;
+      }
+   }
+   pthread_mutex_unlock(&wsi->wait_mutex);
+   wsi_display_debug("%9lu fence wait %f ms\n", pthread_self(), ((int64_t) (wsi_get_current_monotonic() - start_ns)) / 1.0e6);
+   return value;
+}
+
+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_destroy(struct wsi_fence *fence_wsi)
+{
+   struct wsi_display_fence *fence = (struct wsi_display_fence *) fence_wsi;
+
+   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_fence *fence = vk_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;
+   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);
+   int ret;
+
+   if (wsi->master_fd < 0)
+      return VK_ERROR_INITIALIZATION_FAILED;
+
+   for (;;) {
+      ret = drmCrtcQueueSequence(wsi->master_fd, connector->crtc_id,
+                                 flags,
+                                 frame_requested,
+                                 frame_queued,
+                                 (uint64_t) fence);
+
+      if (!ret)
+         return VK_SUCCESS;
+
+      if (errno != ENOMEM) {
+         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;
+      }
+
+      pthread_mutex_lock(&wsi->wait_mutex);
+      ret = wsi_display_wait_for_event(wsi, wsi_get_current_monotonic() + 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.
@@ -1882,3 +2042,127 @@  wsi_get_randr_output_display(VkPhysicalDevice   physical_device,
 }
 
 #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->master_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->master_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_fence     *fence = NULL;
+   VkResult                     ret = VK_ERROR_FEATURE_NOT_PRESENT;
+
+   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);
+
+      break;
+   default:
+      ret = VK_ERROR_FEATURE_NOT_PRESENT;
+   }
+
+   if (ret == VK_SUCCESS)
+      *fence_p = &fence->base;
+   else if (fence != NULL)
+      vk_free(allocator, fence);
+   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;
+   int ret;
+
+   if (wsi->master_fd < 0)
+      return VK_ERROR_INITIALIZATION_FAILED;
+
+   if (!connector->active) {
+      *value = 0;
+      return VK_SUCCESS;
+   }
+
+   ret = drmCrtcGetSequence(wsi->master_fd, connector->crtc_id, value, NULL);
+   if (ret)
+      *value = 0;
+
+   return VK_SUCCESS;
+}
+
+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_debug("%9lu fence %lu received %d\n", pthread_self(), fence->sequence, frame);
+   fence->event_received = true;
+   wsi_display_fence_check_free(fence);
+}
+
+static void wsi_display_sequence_handler(int fd, uint64_t frame,
+                                         uint64_t ns, uint64_t user_data)
+{
+   struct wsi_display_fence     *fence = (struct wsi_display_fence *) (uintptr_t) user_data;
+
+   wsi_display_debug("%9lu fence %lu received %lu\n", pthread_self(), fence->sequence, frame);
+   fence->event_received = true;
+   wsi_display_fence_check_free(fence);
+}
+
diff --git a/src/vulkan/wsi/wsi_common_display.h b/src/vulkan/wsi/wsi_common_display.h
index 1997c2a3c40..ec91ba471ae 100644
--- a/src/vulkan/wsi/wsi_common_display.h
+++ b/src/vulkan/wsi/wsi_common_display.h
@@ -91,4 +91,33 @@  wsi_get_randr_output_display(VkPhysicalDevice   physical_device,
 
 #endif /* VK_USE_PLATFORM_XLIB_XRANDR_EXT */
 
+/* VK_EXT_display_control */
+VkResult
+wsi_display_power_control(VkDevice                      device,
+                          struct wsi_device             *wsi_device,
+                          VkDisplayKHR                  display,
+                          const VkDisplayPowerInfoEXT   *display_power_info);
+
+VkResult
+wsi_register_device_event(VkDevice                      device,
+                          struct wsi_device             *wsi_device,
+                          const VkDeviceEventInfoEXT    *device_event_info,
+                          const VkAllocationCallbacks   *allocator,
+                          struct wsi_fence              **fence);
+
+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);
+
+VkResult
+wsi_get_swapchain_counter(VkDevice                      device,
+                          struct wsi_device             *wsi_device,
+                          VkSwapchainKHR                swapchain,
+                          VkSurfaceCounterFlagBitsEXT   flag_bits,
+                          uint64_t                      *value);
+
 #endif