[Mesa-dev] [PATCH 5/7] vulkan: add VK_EXT_display_control [v4]
Keith Packard
keithp at keithp.com
Sat Feb 10 04:45:14 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: Add support for anv
Signed-off-by: Keith Packard <keithp at 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 --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
--
2.15.1
More information about the mesa-dev
mailing list