[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