[Mesa-dev] [PATCH 3/3] radv: add VK_EXT_display_control to radv

Keith Packard keithp at keithp.com
Wed Aug 2 10:12:41 UTC 2017


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.

Signed-off-by: Keith Packard <keithp at keithp.com>
---
 src/amd/vulkan/radv_device.c           |  22 ++-
 src/amd/vulkan/radv_entrypoints_gen.py |   3 +-
 src/amd/vulkan/radv_private.h          |  11 +-
 src/amd/vulkan/radv_wsi_display.c      | 108 +++++++++++++++
 src/vulkan/wsi/wsi_common.h            |   9 ++
 src/vulkan/wsi/wsi_common_display.c    | 240 +++++++++++++++++++++++++++++++++
 src/vulkan/wsi/wsi_common_display.h    |  28 ++++
 7 files changed, 417 insertions(+), 4 deletions(-)

diff --git a/src/amd/vulkan/radv_device.c b/src/amd/vulkan/radv_device.c
index db68b0724fd..6531b5afb3a 100644
--- a/src/amd/vulkan/radv_device.c
+++ b/src/amd/vulkan/radv_device.c
@@ -198,6 +198,10 @@ static const VkExtensionProperties ext_sema_device_extensions[] = {
 		.extensionName = VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME,
 		.specVersion = 1,
 	},
+        {
+                .extensionName = VK_EXT_DISPLAY_CONTROL_EXTENSION_NAME,
+                .specVersion = 1,
+        },
 };
 
 static VkResult
@@ -2729,7 +2733,14 @@ void radv_DestroyFence(
 
 	if (!fence)
 		return;
-	device->ws->destroy_fence(fence->fence);
+        switch (fence->type) {
+        case RADV_FENCE_TYPE_WINSYS:
+           device->ws->destroy_fence(fence->fence);
+           break;
+        case RADV_FENCE_TYPE_WSI:
+           fence->fence_wsi->destroy(fence->fence_wsi);
+           break;
+        }
 	vk_free2(&device->alloc, pAllocator, fence);
 }
 
@@ -2770,7 +2781,14 @@ VkResult radv_WaitForFences(
 		if (!fence->submitted)
 			return VK_TIMEOUT;
 
-		expired = device->ws->fence_wait(device->ws, fence->fence, true, timeout);
+                switch (fence->type) {
+                case RADV_FENCE_TYPE_WINSYS:
+                   expired = device->ws->fence_wait(device->ws, fence->fence, true, timeout);
+                   break;
+                case RADV_FENCE_TYPE_WSI:
+                   expired = fence->fence_wsi->wait(fence->fence_wsi, true, timeout);
+                   break;
+                }
 		if (!expired)
 			return VK_TIMEOUT;
 
diff --git a/src/amd/vulkan/radv_entrypoints_gen.py b/src/amd/vulkan/radv_entrypoints_gen.py
index 2a151cc701c..3007d517a95 100644
--- a/src/amd/vulkan/radv_entrypoints_gen.py
+++ b/src/amd/vulkan/radv_entrypoints_gen.py
@@ -60,7 +60,8 @@ SUPPORTED_EXTENSIONS = [
     'VK_KEITHP_kms_display',
     'VK_KHR_display',
     'VK_EXT_direct_mode_display',
-    'VK_EXT_acquire_xlib_display'
+    'VK_EXT_acquire_xlib_display',
+    'VK_EXT_display_control'
 ]
 
 # We generate a static hash table for entry point lookup
diff --git a/src/amd/vulkan/radv_private.h b/src/amd/vulkan/radv_private.h
index 27e1e6d6ed7..146ffcc4e65 100644
--- a/src/amd/vulkan/radv_private.h
+++ b/src/amd/vulkan/radv_private.h
@@ -1519,8 +1519,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 0c9cf7e3315..7729ac621e2 100644
--- a/src/amd/vulkan/radv_wsi_display.c
+++ b/src/amd/vulkan/radv_wsi_display.c
@@ -184,3 +184,111 @@ radv_CreateDisplayPlaneSurfaceKHR(VkInstance                            _instanc
 
    return wsi_create_display_surface(_instance, alloc, create_info, surface);
 }
+
+/* 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/vulkan/wsi/wsi_common.h b/src/vulkan/wsi/wsi_common.h
index 9ce3b071122..3829a3dd79e 100644
--- a/src/vulkan/wsi/wsi_common.h
+++ b/src/vulkan/wsi/wsi_common.h
@@ -78,6 +78,15 @@ struct wsi_swapchain {
                                 VkImage *linear_image);
 };
 
+struct wsi_fence {
+   VkDevice                     device;
+   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 {
    VkResult (*get_support)(VkIcdSurfaceBase *surface,
                            struct wsi_device *wsi_device,
diff --git a/src/vulkan/wsi/wsi_common_display.c b/src/vulkan/wsi/wsi_common_display.c
index 81e009c1cc8..bdb5f01704e 100644
--- a/src/vulkan/wsi/wsi_common_display.c
+++ b/src/vulkan/wsi/wsi_common_display.c
@@ -70,6 +70,7 @@ typedef struct wsi_display_connector {
    bool                         active;
    drmModeConnectorPtr          connector;
    uint32_t                     edid_length;
+   uint32_t                     dpms_property;
    uint8_t                      *edid;
 #ifdef VK_USE_PLATFORM_XLIB_XRANDR_EXT
    xcb_randr_output_t           output;
@@ -127,6 +128,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)
 
@@ -283,6 +293,10 @@ wsi_display_get_connector(struct wsi_device             *wsi_device,
             /* XXX dig name out of EDID data */
          }
       }
+      if (prop->flags & DRM_MODE_PROP_ENUM) {
+         if (!strcmp(prop->name, "DPMS"))
+            connector->dpms_property = drm_connector->props[p];
+      }
       drmModeFreeProperty(prop);
    }
 
@@ -893,6 +907,230 @@ wsi_get_randr_output_display(VkPhysicalDevice   physical_device,
 #endif /* VK_USE_PLATFORM_XLIB_XRANDR_EXT */
 
 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                          drm_fd = wsi->master_fd >= 0 ? wsi->master_fd : wsi->render_fd;
+   int                          mode;
+
+   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(drm_fd,
+                               connector->id,
+                               connector->dpms_property,
+                               mode);
+   return VK_SUCCESS;
+}
+
+static uint64_t wsi_get_current_monotonic(void)
+{
+   struct timespec tv;
+
+   clock_gettime(CLOCK_MONOTONIC, &tv);
+   return tv.tv_nsec + tv.tv_sec*1000000000ull;
+}
+
+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;
+}
+
+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 bool
+wsi_display_fence_wait(struct wsi_fence *fence_wsi,
+                       bool absolute,
+                       uint64_t timeout)
+{
+   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();
+
+   timeout += wsi_get_current_realtime();
+
+   wsi_display_debug("%9lu wait fence %lu %ld\n", pthread_self(), fence->sequence, (int64_t) (timeout - wsi_get_current_realtime()));
+   wsi_display_debug_code(uint64_t start_ns = wsi_get_current_realtime());
+   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_realtime() - start_ns)) / 1.0e6);
+   return value;
+}
+
+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 VkResult
+wsi_register_vblank_event(struct wsi_display_fence      *fence,
+                          struct wsi_device             *wsi_device,
+                          VkDisplayKHR                  display,
+                          const VkDisplayEventInfoEXT   *display_event_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 ret;
+
+   ret = drmCrtcQueueSequence(wsi->master_fd, connector->crtc_id,
+                              DRM_CRTC_SEQUENCE_RELATIVE|
+                              DRM_CRTC_SEQUENCE_FIRST_PIXEL_OUT,
+                              1,
+                              (uint64_t) fence);
+   if (ret) {
+      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;
+   }
+
+   return VK_SUCCESS;
+}
+
+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;
+
+   fence = vk_alloc(allocator, sizeof (*fence), 8,
+                    VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
+   if (!fence)
+      return VK_ERROR_OUT_OF_HOST_MEMORY;
+
+   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;
+
+   switch (display_event_info->displayEvent) {
+   case VK_DISPLAY_EVENT_TYPE_FIRST_PIXEL_OUT_EXT:
+
+      ret = wsi_register_vblank_event(fence, wsi_device, display, display_event_info);
+
+      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 (!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);
+}
+
+VkResult
 wsi_create_display_surface(VkInstance instance,
                            const VkAllocationCallbacks   *allocator,
                            const VkDisplaySurfaceCreateInfoKHR *create_info,
@@ -1223,6 +1461,8 @@ static drmEventContext event_context = {
 #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 *
diff --git a/src/vulkan/wsi/wsi_common_display.h b/src/vulkan/wsi/wsi_common_display.h
index 47430aa41e1..8e43eaf73f2 100644
--- a/src/vulkan/wsi/wsi_common_display.h
+++ b/src/vulkan/wsi/wsi_common_display.h
@@ -85,6 +85,34 @@ wsi_get_randr_output_display(VkPhysicalDevice   physical_device,
 #endif /* VK_USE_PLATFORM_XLIB_XRANDR_EXT */
 
 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);
+
+VkResult
 wsi_create_display_surface(VkInstance instance,
                            const VkAllocationCallbacks *pAllocator,
                            const VkDisplaySurfaceCreateInfoKHR *pCreateInfo,
-- 
2.11.0



More information about the mesa-dev mailing list