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 <kei...@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 _______________________________________________ mesa-dev mailing list mesa-dev@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/mesa-dev