[PATCH 5/7] vulkan: add VK_EXT_display_control [v5]
Keith Packard
keithp at keithp.com
Fri Jun 15 02:52:54 UTC 2018
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: Fix scope for wsi_display_mode andwsi_display_connector allocs
Suggested-by: Jason Ekstrand <jason at jlekstrand.net>
v5: Adopt Jason Ekstrand's coding conventions
Declare variables at first use, eliminate extra whitespace between
types and names. Wrap lines to 80 columns.
Use wsi_rel_to_abs_time helper function to convert relative
timeouts to absolute timeouts without causing overflow.
Suggested-by: Jason Ekstrand <jason.ekstrand at intel.com>
Signed-off-by: Keith Packard <keithp at keithp.com>
---
src/vulkan/wsi/wsi_common.h | 10 +
src/vulkan/wsi/wsi_common_display.c | 307 +++++++++++++++++++++++++++-
src/vulkan/wsi/wsi_common_display.h | 29 +++
3 files changed, 345 insertions(+), 1 deletion(-)
diff --git a/src/vulkan/wsi/wsi_common.h b/src/vulkan/wsi/wsi_common.h
index 054aad23c1c..081fe1dcf8d 100644
--- a/src/vulkan/wsi/wsi_common.h
+++ b/src/vulkan/wsi/wsi_common.h
@@ -66,6 +66,16 @@ struct wsi_format_modifier_properties_list {
struct wsi_format_modifier_properties *modifier_properties;
};
+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 (VK_ICD_WSI_PLATFORM_DISPLAY + 1)
diff --git a/src/vulkan/wsi/wsi_common_display.c b/src/vulkan/wsi/wsi_common_display.c
index 504f7741d73..40eaa6a322e 100644
--- a/src/vulkan/wsi/wsi_common_display.c
+++ b/src/vulkan/wsi/wsi_common_display.c
@@ -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
@@ -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)
@@ -307,6 +317,19 @@ wsi_display_get_connector(struct wsi_device *wsi_device,
connector->connected = drm_connector->connection != DRM_MODE_DISCONNECTED;
+ /* Look for a DPMS property */
+ for (int p = 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);
@@ -663,7 +686,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;
}
@@ -896,12 +919,21 @@ 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);
+
+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 *
@@ -1168,6 +1200,147 @@ 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;
+
+ if (!absolute)
+ timeout = wsi_rel_to_abs_time(timeout);
+
+ 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);
+
+ bool value = false;
+ int ret = 0;
+ 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_zalloc(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);
+
+ if (wsi->fd < 0)
+ return VK_ERROR_INITIALIZATION_FAILED;
+
+ for (;;) {
+ int ret = drmCrtcQueueSequence(wsi->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_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.
@@ -1963,3 +2136,135 @@ 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->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_fence *fence;
+ 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);
+
+ if (ret == VK_SUCCESS)
+ *fence_p = &fence->base;
+ else if (fence != NULL)
+ vk_free(allocator, fence);
+
+ 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;
+}
+
+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_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);
+}
+
diff --git a/src/vulkan/wsi/wsi_common_display.h b/src/vulkan/wsi/wsi_common_display.h
index 58447d2c6eb..2f760f825fb 100644
--- a/src/vulkan/wsi/wsi_common_display.h
+++ b/src/vulkan/wsi/wsi_common_display.h
@@ -96,4 +96,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
--
2.17.1
More information about the dri-devel
mailing list