[Mesa-dev] [PATCH mesa 3/3] radv: add VK_EXT_display_control to radv [v2]
Keith Packard
keithp at keithp.com
Fri Dec 22 03:19:15 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.
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.
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 | 108 +++++++++++++
src/vulkan/wsi/wsi_common.h | 9 ++
src/vulkan/wsi/wsi_common_display.c | 295 +++++++++++++++++++++++++++++++++++-
src/vulkan/wsi/wsi_common_display.h | 28 ++++
6 files changed, 445 insertions(+), 7 deletions(-)
diff --git a/src/amd/vulkan/radv_extensions.py b/src/amd/vulkan/radv_extensions.py
index 8afba9d9057..08aa694e3c1 100644
--- a/src/amd/vulkan/radv_extensions.py
+++ b/src/amd/vulkan/radv_extensions.py
@@ -84,6 +84,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, True),
Extension('VK_EXT_external_memory_dma_buf', 1, True),
Extension('VK_EXT_global_priority', 1, 'device->rad_info.has_ctx_priority'),
diff --git a/src/amd/vulkan/radv_private.h b/src/amd/vulkan/radv_private.h
index bf0643a6a5e..7fac98def11 100644
--- a/src/amd/vulkan/radv_private.h
+++ b/src/amd/vulkan/radv_private.h
@@ -1597,8 +1597,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 6bfce5f37ed..7dd06db6bef 100644
--- a/src/amd/vulkan/radv_wsi_display.c
+++ b/src/amd/vulkan/radv_wsi_display.c
@@ -183,3 +183,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 124d096170a..e96801c7641 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;
+ 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 bf13a359ac7..5907ac3052e 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
@@ -126,6 +127,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)
@@ -352,6 +362,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;
@@ -373,6 +384,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);
@@ -1064,6 +1087,251 @@ 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 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;
+}
+
+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;
+
+ if (wsi->master_fd < 0)
+ return VK_ERROR_INITIALIZATION_FAILED;
+
+ for (;;) {
+ ret = drmCrtcQueueSequence(wsi->master_fd, connector->crtc_id,
+ DRM_CRTC_SEQUENCE_RELATIVE,
+ 1,
+ NULL,
+ (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_realtime() + 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;
+ }
+ }
+}
+
+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 (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);
+}
+
VkResult
wsi_create_display_surface(VkInstance instance,
const VkAllocationCallbacks *allocator,
@@ -1167,7 +1435,7 @@ wsi_display_surface_get_capabilities_2ext(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;
}
@@ -1266,10 +1534,12 @@ wsi_display_image_init(VkDevice device_h,
if (ret < 0)
goto fail_handle;
+ image->chain = chain;
image->state = wsi_image_idle;
image->frame = 0;
image->sec = 0;
image->usec = 0;
+ image->fb_id = 0;
/* XXX extract depth and bpp from image somehow */
ret = drmModeAddFB(wsi->master_fd, create_info->imageExtent.width, create_info->imageExtent.height,
@@ -1362,6 +1632,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 *
@@ -1387,6 +1659,7 @@ wsi_display_wait_thread(void *data)
return NULL;
}
+/* call with wait_mutex held */
static int
wsi_display_wait_for_event(struct wsi_display *wsi,
uint64_t timeout_ns)
@@ -1419,29 +1692,39 @@ wsi_display_acquire_next_image(struct wsi_swapchain *drv_chain,
struct wsi_display_swapchain *chain = (struct wsi_display_swapchain *)drv_chain;
struct wsi_display *wsi = chain->wsi;
int ret = 0;
+ VkResult result;
if (timeout != 0 && timeout != UINT64_MAX)
timeout += wsi_get_current_realtime();
+ pthread_mutex_lock(&wsi->wait_mutex);
for (;;) {
for (uint32_t i = 0; i < chain->base.image_count; i++) {
if (chain->images[i].state == wsi_image_idle) {
*image_index = i;
wsi_display_debug("image %d available\n", i);
chain->images[i].state = wsi_image_drawing;
- return VK_SUCCESS;
+ result = VK_SUCCESS;
+ goto done;
}
wsi_display_debug("image %d state %d\n", i, chain->images[i].state);
}
- if (ret == ETIMEDOUT)
- return VK_TIMEOUT;
+ if (ret == ETIMEDOUT) {
+ result = VK_TIMEOUT;
+ goto done;
+ }
ret = wsi_display_wait_for_event(wsi, timeout);
- if (ret && ret != ETIMEDOUT)
- return VK_ERROR_OUT_OF_DATE_KHR;
+ if (ret && ret != ETIMEDOUT) {
+ result = VK_ERROR_OUT_OF_DATE_KHR;
+ break;
+ }
}
+done:
+ pthread_mutex_unlock(&wsi->wait_mutex);
+ return result;
}
/*
diff --git a/src/vulkan/wsi/wsi_common_display.h b/src/vulkan/wsi/wsi_common_display.h
index 2e47a21fb92..d0aa5289f7a 100644
--- a/src/vulkan/wsi/wsi_common_display.h
+++ b/src/vulkan/wsi/wsi_common_display.h
@@ -83,6 +83,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,
--
2.15.1
More information about the mesa-dev
mailing list