Dave Airlie asked me to split these up so that the core and anv/radv bits were all in separate patches.
There are no code changes here aside from fixing the KHR_display version from 1 to 23. I also fixed the indentation in the changes to the radv driver from spaces to tabs as per convention.
-keith
This adds support for the KHR_display extension support to the vulkan WSI layer. Driver support will be added separately.
Signed-off-by: Keith Packard keithp@keithp.com --- configure.ac | 1 + meson.build | 4 +- src/amd/vulkan/radv_wsi.c | 3 +- src/intel/vulkan/anv_wsi.c | 3 +- src/vulkan/Makefile.am | 7 + src/vulkan/Makefile.sources | 4 + src/vulkan/wsi/meson.build | 10 + src/vulkan/wsi/wsi_common.c | 19 +- src/vulkan/wsi/wsi_common.h | 5 +- src/vulkan/wsi/wsi_common_display.c | 1368 +++++++++++++++++++++++++++++++++++ src/vulkan/wsi/wsi_common_display.h | 72 ++ src/vulkan/wsi/wsi_common_private.h | 10 + 12 files changed, 1500 insertions(+), 6 deletions(-) create mode 100644 src/vulkan/wsi/wsi_common_display.c create mode 100644 src/vulkan/wsi/wsi_common_display.h
diff --git a/configure.ac b/configure.ac index 8ed606c7694..46318365603 100644 --- a/configure.ac +++ b/configure.ac @@ -1849,6 +1849,7 @@ fi AM_CONDITIONAL(HAVE_PLATFORM_X11, echo "$platforms" | grep -q 'x11') AM_CONDITIONAL(HAVE_PLATFORM_WAYLAND, echo "$platforms" | grep -q 'wayland') AM_CONDITIONAL(HAVE_PLATFORM_DRM, echo "$platforms" | grep -q 'drm') +AM_CONDITIONAL(HAVE_PLATFORM_DISPLAY, echo "$platforms" | grep -q 'drm') AM_CONDITIONAL(HAVE_PLATFORM_SURFACELESS, echo "$platforms" | grep -q 'surfaceless') AM_CONDITIONAL(HAVE_PLATFORM_ANDROID, echo "$platforms" | grep -q 'android')
diff --git a/meson.build b/meson.build index b39e2f8ab96..aeb7f5e2917 100644 --- a/meson.build +++ b/meson.build @@ -239,11 +239,12 @@ with_platform_wayland = false with_platform_x11 = false with_platform_drm = false with_platform_surfaceless = false +with_platform_display = false egl_native_platform = '' _platforms = get_option('platforms') if _platforms == 'auto' if system_has_kms_drm - _platforms = 'x11,wayland,drm,surfaceless' + _platforms = 'x11,wayland,drm,surfaceless,display' elif ['darwin', 'windows', 'cygwin'].contains(host_machine.system()) _platforms = 'x11,surfaceless' else @@ -257,6 +258,7 @@ if _platforms != '' with_platform_wayland = _split.contains('wayland') with_platform_drm = _split.contains('drm') with_platform_surfaceless = _split.contains('surfaceless') + with_platform_display = _split.contains('display') egl_native_platform = _split[0] endif
diff --git a/src/amd/vulkan/radv_wsi.c b/src/amd/vulkan/radv_wsi.c index e016e837102..9bdd55ef11c 100644 --- a/src/amd/vulkan/radv_wsi.c +++ b/src/amd/vulkan/radv_wsi.c @@ -41,7 +41,8 @@ radv_init_wsi(struct radv_physical_device *physical_device) return wsi_device_init(&physical_device->wsi_device, radv_physical_device_to_handle(physical_device), radv_wsi_proc_addr, - &physical_device->instance->alloc); + &physical_device->instance->alloc, + physical_device->local_fd); }
void diff --git a/src/intel/vulkan/anv_wsi.c b/src/intel/vulkan/anv_wsi.c index 6082c3dd093..f86d83589ea 100644 --- a/src/intel/vulkan/anv_wsi.c +++ b/src/intel/vulkan/anv_wsi.c @@ -39,7 +39,8 @@ anv_init_wsi(struct anv_physical_device *physical_device) return wsi_device_init(&physical_device->wsi_device, anv_physical_device_to_handle(physical_device), anv_wsi_proc_addr, - &physical_device->instance->alloc); + &physical_device->instance->alloc, + physical_device->local_fd); }
void diff --git a/src/vulkan/Makefile.am b/src/vulkan/Makefile.am index 037436c1cd7..c33ac5758f7 100644 --- a/src/vulkan/Makefile.am +++ b/src/vulkan/Makefile.am @@ -57,6 +57,13 @@ AM_CPPFLAGS += \ VULKAN_WSI_SOURCES += $(VULKAN_WSI_X11_FILES) endif
+if HAVE_PLATFORM_DISPLAY +AM_CPPFLAGS += \ + -DVK_USE_PLATFORM_DISPLAY_KHR + +VULKAN_WSI_SOURCES += $(VULKAN_WSI_DISPLAY_FILES) +endif + BUILT_SOURCES += $(VULKAN_WSI_WAYLAND_GENERATED_FILES) CLEANFILES = $(BUILT_SOURCES)
diff --git a/src/vulkan/Makefile.sources b/src/vulkan/Makefile.sources index a0a24ce7de8..3642c7662c4 100644 --- a/src/vulkan/Makefile.sources +++ b/src/vulkan/Makefile.sources @@ -17,6 +17,10 @@ VULKAN_WSI_X11_FILES := \ wsi/wsi_common_x11.c \ wsi/wsi_common_x11.h
+VULKAN_WSI_DISPLAY_FILES := \ + wsi/wsi_common_display.c \ + wsi/wsi_common_display.h + VULKAN_UTIL_FILES := \ util/vk_alloc.h \ util/vk_debug_report.c \ diff --git a/src/vulkan/wsi/meson.build b/src/vulkan/wsi/meson.build index bd0fd3cc53e..743631a6113 100644 --- a/src/vulkan/wsi/meson.build +++ b/src/vulkan/wsi/meson.build @@ -57,6 +57,16 @@ if with_platform_wayland ] endif
+if with_platform_display + vulkan_wsi_args += [ + '-DVK_USE_PLATFORM_DISPLAY_KHR', + ] + files_vulkan_wsi += files( + 'wsi_common_display.c', + 'wsi_common_display.h', + ) +endif + libvulkan_wsi = static_library( 'vulkan_wsi', files_vulkan_wsi, diff --git a/src/vulkan/wsi/wsi_common.c b/src/vulkan/wsi/wsi_common.c index 90ed07b7857..c0a285e5814 100644 --- a/src/vulkan/wsi/wsi_common.c +++ b/src/vulkan/wsi/wsi_common.c @@ -29,7 +29,8 @@ VkResult wsi_device_init(struct wsi_device *wsi, VkPhysicalDevice pdevice, WSI_FN_GetPhysicalDeviceProcAddr proc_addr, - const VkAllocationCallbacks *alloc) + const VkAllocationCallbacks *alloc, + int device_fd) { VkResult result;
@@ -89,6 +90,19 @@ wsi_device_init(struct wsi_device *wsi, } #endif
+#ifdef VK_USE_PLATFORM_DISPLAY_KHR + result = wsi_display_init_wsi(wsi, alloc, pdevice, device_fd); + if (result != VK_SUCCESS) { +#ifdef VK_USE_PLATFORM_WAYLAND_KHR + wsi_wl_finish_wsi(wsi, alloc); +#endif +#ifdef VK_USE_PLATFORM_XCB_KHR + wsi_x11_finish_wsi(wsi, alloc); +#endif + return result; + } +#endif + return VK_SUCCESS; }
@@ -96,6 +110,9 @@ void wsi_device_finish(struct wsi_device *wsi, const VkAllocationCallbacks *alloc) { +#ifdef VK_USE_PLATFORM_DISPLAY_KHR + wsi_display_finish_wsi(wsi, alloc); +#endif #ifdef VK_USE_PLATFORM_WAYLAND_KHR wsi_wl_finish_wsi(wsi, alloc); #endif diff --git a/src/vulkan/wsi/wsi_common.h b/src/vulkan/wsi/wsi_common.h index 3e0d3be1c24..1cb6aaebca0 100644 --- a/src/vulkan/wsi/wsi_common.h +++ b/src/vulkan/wsi/wsi_common.h @@ -50,7 +50,7 @@ struct wsi_memory_allocate_info {
struct wsi_interface;
-#define VK_ICD_WSI_PLATFORM_MAX 5 +#define VK_ICD_WSI_PLATFORM_MAX 6
struct wsi_device { VkPhysicalDeviceMemoryProperties memory_props; @@ -93,7 +93,8 @@ VkResult wsi_device_init(struct wsi_device *wsi, VkPhysicalDevice pdevice, WSI_FN_GetPhysicalDeviceProcAddr proc_addr, - const VkAllocationCallbacks *alloc); + const VkAllocationCallbacks *alloc, + int device_fd);
void wsi_device_finish(struct wsi_device *wsi, diff --git a/src/vulkan/wsi/wsi_common_display.c b/src/vulkan/wsi/wsi_common_display.c new file mode 100644 index 00000000000..2732b1dd721 --- /dev/null +++ b/src/vulkan/wsi/wsi_common_display.c @@ -0,0 +1,1368 @@ +/* + * Copyright © 2017 Keith Packard + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#include "util/macros.h" +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <errno.h> +#include <string.h> +#include <fcntl.h> +#include <poll.h> +#include <stdbool.h> +#include <math.h> +#include <xf86drm.h> +#include <xf86drmMode.h> +#include "util/hash_table.h" +#include "util/list.h" + +#include "vk_util.h" +#include "wsi_common_private.h" +#include "wsi_common_display.h" +#include "wsi_common_queue.h" + +#if 0 +#define wsi_display_debug(...) fprintf(stderr, __VA_ARGS__) +#define wsi_display_debug_code(...) __VA_ARGS__ +#else +#define wsi_display_debug(...) +#define wsi_display_debug_code(...) +#endif + +/* These have lifetime equal to the instance, so they effectively + * never go away. This means we must keep track of them separately + * from all other resources. + */ +typedef struct wsi_display_mode { + struct list_head list; + struct wsi_display_connector *connector; + bool valid; /* was found in most recent poll */ + bool preferred; + uint32_t clock; /* in kHz */ + uint16_t hdisplay, hsync_start, hsync_end, htotal, hskew; + uint16_t vdisplay, vsync_start, vsync_end, vtotal, vscan; + uint32_t flags; +} wsi_display_mode; + +typedef struct wsi_display_connector { + struct list_head list; + struct wsi_display *wsi; + uint32_t id; + uint32_t crtc_id; + char *name; + bool connected; + bool active; + wsi_display_mode *current_mode; + drmModeModeInfo current_drm_mode; +} wsi_display_connector; + +struct wsi_display { + struct wsi_interface base; + + const VkAllocationCallbacks *alloc; + VkPhysicalDevice physical_device; + + int master_fd; + int render_fd; + + pthread_mutex_t wait_mutex; + pthread_cond_t wait_cond; + pthread_t wait_thread; + + struct list_head connectors; + + struct list_head display_modes; +}; + +enum wsi_image_state { + wsi_image_idle, + wsi_image_drawing, + wsi_image_queued, + wsi_image_flipping, + wsi_image_displaying +}; + +struct wsi_display_image { + struct wsi_image base; + struct wsi_display_swapchain *chain; + enum wsi_image_state state; + uint32_t fb_id; + uint64_t flip_sequence; +}; + +struct wsi_display_swapchain { + struct wsi_swapchain base; + struct wsi_display *wsi; + VkIcdSurfaceDisplay *surface; + uint64_t flip_sequence; + struct wsi_display_image images[0]; +}; + +ICD_DEFINE_NONDISP_HANDLE_CASTS(wsi_display_mode, VkDisplayModeKHR) +ICD_DEFINE_NONDISP_HANDLE_CASTS(wsi_display_connector, VkDisplayKHR) + +static bool +wsi_display_mode_matches_drm(wsi_display_mode *wsi, + drmModeModeInfoPtr drm) +{ + return wsi->clock == drm->clock && + wsi->hdisplay == drm->hdisplay && + wsi->hsync_start == drm->hsync_start && + wsi->hsync_end == drm->hsync_end && + wsi->htotal == drm->htotal && + wsi->hskew == drm->hskew && + wsi->vdisplay == drm->vdisplay && + wsi->vsync_start == drm->vsync_start && + wsi->vsync_end == drm->vsync_end && + wsi->vtotal == drm->vtotal && + wsi->vscan == drm->vscan && + wsi->flags == drm->flags; +} + +static double +wsi_display_mode_refresh(struct wsi_display_mode *wsi) +{ + return (double) wsi->clock * 1000.0 / ((double) wsi->htotal * (double) wsi->vtotal * (double) (wsi->vscan + 1)); +} + +static uint64_t wsi_get_current_monotonic(void) +{ + struct timespec tv; + + clock_gettime(CLOCK_MONOTONIC, &tv); + return tv.tv_nsec + tv.tv_sec*1000000000ull; +} + +static struct wsi_display_mode * +wsi_display_find_drm_mode(struct wsi_device *wsi_device, + struct wsi_display_connector *connector, + drmModeModeInfoPtr mode) +{ + struct wsi_display *wsi = (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY]; + struct wsi_display_mode *display_mode; + + LIST_FOR_EACH_ENTRY(display_mode, &wsi->display_modes, list) { + if (display_mode->connector == connector && + wsi_display_mode_matches_drm(display_mode, mode)) + return display_mode; + } + return NULL; +} + +static void +wsi_display_invalidate_connector_modes(struct wsi_device *wsi_device, + struct wsi_display_connector *connector) +{ + struct wsi_display *wsi = (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY]; + struct wsi_display_mode *display_mode; + + LIST_FOR_EACH_ENTRY(display_mode, &wsi->display_modes, list) + if (display_mode->connector == connector) + display_mode->valid = false; +} + +static VkResult +wsi_display_register_drm_mode(struct wsi_device *wsi_device, + struct wsi_display_connector *connector, + drmModeModeInfoPtr drm_mode) +{ + struct wsi_display *wsi = (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY]; + struct wsi_display_mode *display_mode; + + display_mode = wsi_display_find_drm_mode(wsi_device, connector, drm_mode); + + if (display_mode) { + display_mode->valid = true; + return VK_SUCCESS; + } + + display_mode = vk_alloc(wsi->alloc, sizeof (struct wsi_display_mode), 8, VK_SYSTEM_ALLOCATION_SCOPE_OBJECT); + if (!display_mode) + return VK_ERROR_OUT_OF_HOST_MEMORY; + + display_mode->connector = connector; + display_mode->valid = true; + display_mode->preferred = (drm_mode->type & DRM_MODE_TYPE_PREFERRED) != 0; + display_mode->clock = drm_mode->clock; /* kHz */ + display_mode->hdisplay = drm_mode->hdisplay; + display_mode->hsync_start = drm_mode->hsync_start; + display_mode->hsync_end = drm_mode->hsync_end; + display_mode->htotal = drm_mode->htotal; + display_mode->hskew = drm_mode->hskew; + display_mode->vdisplay = drm_mode->vdisplay; + display_mode->vsync_start = drm_mode->vsync_start; + display_mode->vsync_end = drm_mode->vsync_end; + display_mode->vtotal = drm_mode->vtotal; + display_mode->vscan = drm_mode->vscan; + display_mode->flags = drm_mode->flags; + + LIST_ADDTAIL(&display_mode->list, &wsi->display_modes); + return VK_SUCCESS; +} + +/* + * Update our information about a specific connector + */ + +static struct wsi_display_connector * +wsi_display_find_connector(struct wsi_device *wsi_device, + uint32_t connector_id) +{ + struct wsi_display *wsi = (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY]; + struct wsi_display_connector *connector; + + connector = NULL; + LIST_FOR_EACH_ENTRY(connector, &wsi->connectors, list) { + if (connector->id == connector_id) + return connector; + } + + return NULL; +} + +static struct wsi_display_connector * +wsi_display_alloc_connector(struct wsi_display *wsi, + uint32_t connector_id) +{ + struct wsi_display_connector *connector; + + connector = vk_alloc(wsi->alloc, sizeof (struct wsi_display_connector), 8, VK_SYSTEM_ALLOCATION_SCOPE_OBJECT); + memset(connector, '\0', sizeof (*connector)); + connector->id = connector_id; + connector->wsi = wsi; + connector->active = false; + /* XXX use EDID name */ + connector->name = "monitor"; + return connector; +} + +static struct wsi_display_connector * +wsi_display_get_connector(struct wsi_device *wsi_device, + uint32_t connector_id) +{ + struct wsi_display *wsi = (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY]; + struct wsi_display_connector *connector; + drmModeConnectorPtr drm_connector; + VkResult result; + int m; + + if (wsi->master_fd < 0) + return NULL; + + drm_connector = drmModeGetConnector(wsi->master_fd, connector_id); + if (!drm_connector) + return NULL; + + connector = wsi_display_find_connector(wsi_device, connector_id); + + if (!connector) { + connector = wsi_display_alloc_connector(wsi, connector_id); + if (!connector) { + drmModeFreeConnector(drm_connector); + return NULL; + } + LIST_ADDTAIL(&connector->list, &wsi->connectors); + } + + connector->connected = drm_connector->connection != DRM_MODE_DISCONNECTED; + + /* Mark all connector modes as invalid */ + wsi_display_invalidate_connector_modes(wsi_device, connector); + + /* + * List current modes, adding new ones and marking existing ones as + * valid + */ + for (m = 0; m < drm_connector->count_modes; m++) { + result = wsi_display_register_drm_mode(wsi_device, + connector, + &drm_connector->modes[m]); + if (result != VK_SUCCESS) { + drmModeFreeConnector(drm_connector); + return NULL; + } + } + + drmModeFreeConnector(drm_connector); + + return connector; +} + +#define MM_PER_PIXEL (1.0/96.0 * 25.4) + +static void +wsi_display_fill_in_display_properties(struct wsi_device *wsi_device, + struct wsi_display_connector *connector, + VkDisplayPropertiesKHR *properties) +{ + struct wsi_display *wsi = (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY]; + struct wsi_display_mode *display_mode, *preferred_mode = NULL;; + + properties->display = wsi_display_connector_to_handle(connector); + properties->displayName = connector->name; + + /* Find the preferred mode and assume that's the physical resolution */ + + LIST_FOR_EACH_ENTRY(display_mode, &wsi->display_modes, list) { + if (display_mode->valid && display_mode->connector == connector && display_mode->preferred) { + preferred_mode = display_mode; + break; + } + } + + if (preferred_mode) { + properties->physicalResolution.width = preferred_mode->hdisplay; + properties->physicalResolution.height = preferred_mode->vdisplay; + } else { + properties->physicalResolution.width = 1024; + properties->physicalResolution.height = 768; + } + + /* Make up physical size based on 96dpi */ + properties->physicalDimensions.width = floor(properties->physicalResolution.width * MM_PER_PIXEL + 0.5); + properties->physicalDimensions.height = floor(properties->physicalResolution.height * MM_PER_PIXEL + 0.5); + + properties->supportedTransforms = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; + properties->persistentContent = 0; +} + +/* + * Implement vkGetPhysicalDeviceDisplayPropertiesKHR (VK_KHR_display) + */ +VkResult +wsi_display_get_physical_device_display_properties(VkPhysicalDevice physical_device, + struct wsi_device *wsi_device, + uint32_t *property_count, + VkDisplayPropertiesKHR *properties) +{ + struct wsi_display *wsi = (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY]; + struct wsi_display_connector *connector; + int c; + uint32_t connected; + uint32_t property_count_requested = *property_count; + drmModeResPtr mode_res; + + if (wsi->master_fd < 0) + return VK_ERROR_INITIALIZATION_FAILED; + + mode_res = drmModeGetResources(wsi->master_fd); + + if (!mode_res) + return VK_ERROR_INITIALIZATION_FAILED; + + connected = 0; + + /* Get current information */ + for (c = 0; c < mode_res->count_connectors; c++) { + connector = wsi_display_get_connector(wsi_device, mode_res->connectors[c]); + + if (!connector) { + drmModeFreeResources(mode_res); + return VK_ERROR_OUT_OF_HOST_MEMORY; + } + + if (connector->connected) + connected++; + } + + /* Fill in property information if requested */ + if (properties != NULL) { + connected = 0; + + for (c = 0; c < mode_res->count_connectors; c++) { + connector = wsi_display_find_connector(wsi_device, mode_res->connectors[c]); + + if (connector && connector->connected) { + if (connected < property_count_requested) { + wsi_display_fill_in_display_properties(wsi_device, + connector, + &properties[connected]); + } + connected++; + } + } + } + + drmModeFreeResources(mode_res); + + *property_count = connected; + + if (connected > property_count_requested && properties != NULL) + return VK_INCOMPLETE; + + return VK_SUCCESS; +} + +/* + * Implement vkGetPhysicalDeviceDisplayPlanePropertiesKHR (VK_KHR_display + */ +VkResult +wsi_display_get_physical_device_display_plane_properties(VkPhysicalDevice physical_device, + struct wsi_device *wsi_device, + uint32_t *property_count, + VkDisplayPlanePropertiesKHR *properties) +{ + struct wsi_display *wsi = (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY]; + struct wsi_display_connector *connector; + uint32_t property_count_requested = *property_count; + int c; + + if (!properties) + property_count_requested = 0; + + c = 0; + LIST_FOR_EACH_ENTRY(connector, &wsi->connectors, list) { + if (c < property_count_requested) { + if (connector && connector->active) { + properties[c].currentDisplay = wsi_display_connector_to_handle(connector); + properties[c].currentStackIndex = c; + } else { + properties[c].currentDisplay = NULL; + properties[c].currentStackIndex = 0; + } + } + c++; + } + + *property_count = c; + + if (c > property_count_requested && properties != NULL) + return VK_INCOMPLETE; + + return VK_SUCCESS; +} + +/* + * Implement vkGetDisplayPlaneSupportedDisplaysKHR (VK_KHR_display) + */ + +VkResult +wsi_display_get_display_plane_supported_displays(VkPhysicalDevice physical_device, + struct wsi_device *wsi_device, + uint32_t plane_index, + uint32_t *display_count, + VkDisplayKHR *displays) +{ + struct wsi_display *wsi = (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY]; + struct wsi_display_connector *connector; + int c; + + + if (displays == NULL) { + *display_count = 1; + return VK_SUCCESS; + } + + if (*display_count < 1) + return VK_INCOMPLETE; + + c = 0; + LIST_FOR_EACH_ENTRY(connector, &wsi->connectors, list) { + if (c == plane_index) { + *displays = wsi_display_connector_to_handle(connector); + *display_count = 1; + return VK_SUCCESS; + } + c++; + } + + *displays = 0; + *display_count = 0; + + return VK_SUCCESS; +} + +/* + * Implement vkGetDisplayModePropertiesKHR (VK_KHR_display) + */ + +VkResult +wsi_display_get_display_mode_properties(VkPhysicalDevice physical_device, + struct wsi_device *wsi_device, + VkDisplayKHR display, + uint32_t *property_count, + VkDisplayModePropertiesKHR *properties) +{ + 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 i; + struct wsi_display_mode *display_mode; + uint32_t property_count_requested = *property_count; + + i = 0; + + if (properties == NULL) + property_count_requested = 0; + + LIST_FOR_EACH_ENTRY(display_mode, &wsi->display_modes, list) { + if (display_mode->valid && display_mode->connector == connector) { + if (i < property_count_requested) { + properties[i].displayMode = wsi_display_mode_to_handle(display_mode); + properties[i].parameters.visibleRegion.width = display_mode->hdisplay; + properties[i].parameters.visibleRegion.height = display_mode->vdisplay; + properties[i].parameters.refreshRate = (uint32_t) (wsi_display_mode_refresh(display_mode) * 1000 + 0.5); + } + i++; + } + } + + *property_count = i; + + if (i > property_count_requested && properties != NULL) + return VK_INCOMPLETE; + + return VK_SUCCESS; + +} + +/* + * Implement vkGetDisplayPlaneCapabilities + */ +VkResult +wsi_get_display_plane_capabilities(VkPhysicalDevice physical_device, + struct wsi_device *wsi_device, + VkDisplayModeKHR mode_khr, + uint32_t plane_index, + VkDisplayPlaneCapabilitiesKHR *capabilities) +{ + struct wsi_display_mode *mode = wsi_display_mode_from_handle(mode_khr); + + /* XXX use actual values */ + capabilities->supportedAlpha = VK_DISPLAY_PLANE_ALPHA_OPAQUE_BIT_KHR; + capabilities->minSrcPosition.x = 0; + capabilities->minSrcPosition.y = 0; + capabilities->maxSrcPosition.x = 0; + capabilities->maxSrcPosition.y = 0; + capabilities->minSrcExtent.width = mode->hdisplay; + capabilities->minSrcExtent.height = mode->vdisplay; + capabilities->maxSrcExtent.width = mode->hdisplay; + capabilities->maxSrcExtent.height = mode->vdisplay; + capabilities->minDstPosition.x = 0; + capabilities->minDstPosition.y = 0; + capabilities->maxDstPosition.x = 0; + capabilities->maxDstPosition.y = 0; + capabilities->minDstExtent.width = mode->hdisplay; + capabilities->minDstExtent.height = mode->vdisplay; + capabilities->maxDstExtent.width = mode->hdisplay; + capabilities->maxDstExtent.height = mode->vdisplay; + return VK_SUCCESS; +} + +VkResult +wsi_create_display_surface(VkInstance instance, + const VkAllocationCallbacks *allocator, + const VkDisplaySurfaceCreateInfoKHR *create_info, + VkSurfaceKHR *surface_khr) +{ + VkIcdSurfaceDisplay *surface; + + surface = vk_alloc(allocator, sizeof *surface, 8, + VK_SYSTEM_ALLOCATION_SCOPE_OBJECT); + if (surface == NULL) + return VK_ERROR_OUT_OF_HOST_MEMORY; + + surface->base.platform = VK_ICD_WSI_PLATFORM_DISPLAY; + + surface->displayMode = create_info->displayMode; + surface->planeIndex = create_info->planeIndex; + surface->planeStackIndex = create_info->planeStackIndex; + surface->transform = create_info->transform; + surface->globalAlpha = create_info->globalAlpha; + surface->alphaMode = create_info->alphaMode; + surface->imageExtent = create_info->imageExtent; + + *surface_khr = VkIcdSurfaceBase_to_handle(&surface->base); + return VK_SUCCESS; +} + + +static VkResult +wsi_display_surface_get_support(VkIcdSurfaceBase *surface, + struct wsi_device *wsi_device, + const VkAllocationCallbacks *allocator, + uint32_t queueFamilyIndex, + int local_fd, + VkBool32* pSupported) +{ + *pSupported = VK_TRUE; + return VK_SUCCESS; +} + +static VkResult +wsi_display_surface_get_capabilities(VkIcdSurfaceBase *surface_base, + VkSurfaceCapabilitiesKHR* caps) +{ + VkIcdSurfaceDisplay *surface = (VkIcdSurfaceDisplay *) surface_base; + wsi_display_mode *mode = wsi_display_mode_from_handle(surface->displayMode); + + caps->currentExtent.width = mode->hdisplay; + caps->currentExtent.height = mode->vdisplay; + + /* XXX Figure out extents based on driver capabilities */ + caps->maxImageExtent = caps->minImageExtent = caps->currentExtent; + + caps->supportedCompositeAlpha = (VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR | + VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR); + + caps->minImageCount = 2; + caps->maxImageCount = 0; + + caps->supportedTransforms = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; + caps->currentTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; + caps->maxImageArrayLayers = 1; + caps->supportedUsageFlags = + VK_IMAGE_USAGE_TRANSFER_SRC_BIT | + VK_IMAGE_USAGE_SAMPLED_BIT | + VK_IMAGE_USAGE_TRANSFER_DST_BIT | + VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + + return VK_SUCCESS; +} + +static VkResult +wsi_display_surface_get_capabilities2(VkIcdSurfaceBase *icd_surface, + const void *info_next, + VkSurfaceCapabilities2KHR *caps) +{ + assert(caps->sType == VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_KHR); + + return wsi_display_surface_get_capabilities(icd_surface, &caps->surfaceCapabilities); +} + +static const VkFormat available_surface_formats[] = { + VK_FORMAT_B8G8R8A8_SRGB, + VK_FORMAT_B8G8R8A8_UNORM, +}; + +static VkResult +wsi_display_surface_get_formats(VkIcdSurfaceBase *icd_surface, + struct wsi_device *wsi_device, + uint32_t *surface_format_count, + VkSurfaceFormatKHR *surface_formats) +{ + VK_OUTARRAY_MAKE(out, surface_formats, surface_format_count); + + for (unsigned i = 0; i < ARRAY_SIZE(available_surface_formats); i++) { + vk_outarray_append(&out, f) { + f->format = available_surface_formats[i]; + f->colorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR; + } + } + + return vk_outarray_status(&out); +} + +static VkResult +wsi_display_surface_get_formats2(VkIcdSurfaceBase *surface, + struct wsi_device *wsi_device, + const void *info_next, + uint32_t *surface_format_count, + VkSurfaceFormat2KHR *surface_formats) +{ + VK_OUTARRAY_MAKE(out, surface_formats, surface_format_count); + + for (unsigned i = 0; i < ARRAY_SIZE(available_surface_formats); i++) { + vk_outarray_append(&out, f) { + assert(f->sType == VK_STRUCTURE_TYPE_SURFACE_FORMAT_2_KHR); + f->surfaceFormat.format = available_surface_formats[i]; + f->surfaceFormat.colorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR; + } + } + + return vk_outarray_status(&out); +} + +static const VkPresentModeKHR available_present_modes[] = { + VK_PRESENT_MODE_FIFO_KHR, +}; + +static VkResult +wsi_display_surface_get_present_modes(VkIcdSurfaceBase *surface, + uint32_t *present_mode_count, + VkPresentModeKHR *present_modes) +{ + if (present_modes == NULL) { + *present_mode_count = ARRAY_SIZE(available_present_modes); + return VK_SUCCESS; + } + + *present_mode_count = MIN2(*present_mode_count, ARRAY_SIZE(available_present_modes)); + typed_memcpy(present_modes, available_present_modes, *present_mode_count); + + if (*present_mode_count < ARRAY_SIZE(available_present_modes)) + return VK_INCOMPLETE; + return VK_SUCCESS; +} + +static VkResult +wsi_display_image_init(VkDevice device_h, + struct wsi_swapchain *drv_chain, + const VkSwapchainCreateInfoKHR *create_info, + const VkAllocationCallbacks *allocator, + struct wsi_display_image *image) +{ + struct wsi_display_swapchain *chain = (struct wsi_display_swapchain *) drv_chain; + struct wsi_display *wsi = chain->wsi; + VkResult result; + int ret; + uint32_t image_handle; + + if (chain->base.use_prime_blit) + result = wsi_create_prime_image(&chain->base, create_info, &image->base); + else + result = wsi_create_native_image(&chain->base, create_info, &image->base); + if (result != VK_SUCCESS) + return result; + + ret = drmPrimeFDToHandle(wsi->master_fd, image->base.fd, &image_handle); + + close(image->base.fd); + image->base.fd = -1; + + if (ret < 0) + goto fail_handle; + + image->chain = chain; + image->state = wsi_image_idle; + 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, + 24, 32, image->base.row_pitch, image_handle, &image->fb_id); + + if (ret) + goto fail_fb; + + return VK_SUCCESS; + +fail_fb: + /* fall through */ + +fail_handle: + wsi_destroy_image(&chain->base, &image->base); + + return VK_ERROR_OUT_OF_HOST_MEMORY; +} + +static void +wsi_display_image_finish(struct wsi_swapchain *drv_chain, + const VkAllocationCallbacks *allocator, + struct wsi_display_image *image) +{ + struct wsi_display_swapchain *chain = (struct wsi_display_swapchain *) drv_chain; + + wsi_destroy_image(&chain->base, &image->base); +} + +static VkResult +wsi_display_swapchain_destroy(struct wsi_swapchain *drv_chain, + const VkAllocationCallbacks *allocator) +{ + struct wsi_display_swapchain *chain = (struct wsi_display_swapchain *) drv_chain; + + for (uint32_t i = 0; i < chain->base.image_count; i++) + wsi_display_image_finish(drv_chain, allocator, &chain->images[i]); + vk_free(allocator, chain); + return VK_SUCCESS; +} + +static struct wsi_image * +wsi_display_get_wsi_image(struct wsi_swapchain *drv_chain, + uint32_t image_index) +{ + struct wsi_display_swapchain *chain = (struct wsi_display_swapchain *) drv_chain; + + return &chain->images[image_index].base; +} + +static void +wsi_display_idle_old_displaying(struct wsi_display_image *active_image) +{ + struct wsi_display_swapchain *chain = active_image->chain; + + wsi_display_debug("idle everyone but %ld\n", active_image - &(chain->images[0])); + for (uint32_t i = 0; i < chain->base.image_count; i++) + if (chain->images[i].state == wsi_image_displaying && &chain->images[i] != active_image) { + wsi_display_debug("idle %d\n", i); + chain->images[i].state = wsi_image_idle; + } +} + +static VkResult +_wsi_display_queue_next(struct wsi_swapchain *drv_chain); + +static void +wsi_display_page_flip_handler2(int fd, + unsigned int frame, + unsigned int sec, + unsigned int usec, + uint32_t crtc_id, + void *data) +{ + struct wsi_display_image *image = data; + + wsi_display_debug("image %ld displayed at %d\n", image - &(image->chain->images[0]), frame); + image->state = wsi_image_displaying; + wsi_display_idle_old_displaying(image); + (void) _wsi_display_queue_next(&(image->chain->base)); +} + +static void wsi_display_page_flip_handler(int fd, unsigned int frame, + unsigned int sec, unsigned int usec, void *data) +{ + wsi_display_page_flip_handler2(fd, frame, sec, usec, 0, 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 +}; + +static void * +wsi_display_wait_thread(void *data) +{ + struct wsi_display *wsi = data; + struct pollfd pollfd = { + .fd = wsi->master_fd, + .events = POLLIN + }; + int ret; + + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); + for (;;) { + ret = poll(&pollfd, 1, -1); + if (ret > 0) { + pthread_mutex_lock(&wsi->wait_mutex); + (void) drmHandleEvent(wsi->master_fd, &event_context); + pthread_mutex_unlock(&wsi->wait_mutex); + pthread_cond_broadcast(&wsi->wait_cond); + } + } + return NULL; +} + +static int +wsi_display_start_wait_thread(struct wsi_display *wsi) +{ + if (!wsi->wait_thread) { + int ret = pthread_create(&wsi->wait_thread, NULL, wsi_display_wait_thread, wsi); + if (ret) + return ret; + } + return 0; +} + +/* call with wait_mutex held */ +static int +wsi_display_wait_for_event(struct wsi_display *wsi, + uint64_t timeout_ns) +{ + int ret; + + ret = wsi_display_start_wait_thread(wsi); + + if (ret) + return ret; + + struct timespec abs_timeout = { + .tv_sec = timeout_ns / ((uint64_t) 1000 * (uint64_t) 1000 * (uint64_t) 1000), + .tv_nsec = timeout_ns % ((uint64_t) 1000 * (uint64_t) 1000 * (uint64_t) 1000) + }; + + ret = pthread_cond_timedwait(&wsi->wait_cond, &wsi->wait_mutex, &abs_timeout); + + wsi_display_debug("%9ld done waiting for event %d\n", pthread_self(), ret); + return ret; +} + +static VkResult +wsi_display_acquire_next_image(struct wsi_swapchain *drv_chain, + uint64_t timeout, + VkSemaphore semaphore, + uint32_t *image_index) +{ + struct wsi_display_swapchain *chain = (struct wsi_display_swapchain *)drv_chain; + struct wsi_display *wsi = chain->wsi; + int ret = 0; + VkResult result = VK_SUCCESS; + + if (timeout != 0 && timeout != UINT64_MAX) + timeout += wsi_get_current_monotonic(); + + 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; + result = VK_SUCCESS; + goto done; + } + wsi_display_debug("image %d state %d\n", i, chain->images[i].state); + } + + if (ret == ETIMEDOUT) { + result = VK_TIMEOUT; + goto done; + } + + ret = wsi_display_wait_for_event(wsi, timeout); + + if (ret && ret != ETIMEDOUT) { + result = VK_ERROR_OUT_OF_DATE_KHR; + goto done; + } + } +done: + pthread_mutex_unlock(&wsi->wait_mutex); + return result; +} + +/* + * Check whether there are any other connectors driven by this crtc + */ +static bool +wsi_display_crtc_solo(struct wsi_display *wsi, + drmModeResPtr mode_res, + drmModeConnectorPtr connector, + uint32_t crtc_id) +{ + int c, e; + + /* See if any other connectors share the same encoder */ + for (c = 0; c < mode_res->count_connectors; c++) { + if (mode_res->connectors[c] == connector->connector_id) + continue; + + drmModeConnectorPtr other_connector = drmModeGetConnector(wsi->master_fd, mode_res->connectors[c]); + if (other_connector) { + bool match = (other_connector->encoder_id == connector->encoder_id); + drmModeFreeConnector(other_connector); + if (match) + return false; + } + } + + /* See if any other encoders share the same crtc */ + for (e = 0; e < mode_res->count_encoders; e++) { + if (mode_res->encoders[e] == connector->encoder_id) + continue; + + drmModeEncoderPtr other_encoder = drmModeGetEncoder(wsi->master_fd, mode_res->encoders[e]); + if (other_encoder) { + bool match = (other_encoder->crtc_id == crtc_id); + drmModeFreeEncoder(other_encoder); + if (match) + return false; + } + } + return true; +} + +/* + * Pick a suitable CRTC to drive this connector. Prefer a CRTC which is + * currently driving this connector and not any others. Settle for a CRTC + * which is currently idle. + */ +static uint32_t +wsi_display_select_crtc(struct wsi_display_connector *connector, + drmModeResPtr mode_res, + drmModeConnectorPtr drm_connector) +{ + struct wsi_display *wsi = connector->wsi; + int c; + uint32_t crtc_id; + + /* See what CRTC is currently driving this connector */ + if (drm_connector->encoder_id) { + drmModeEncoderPtr encoder = drmModeGetEncoder(wsi->master_fd, drm_connector->encoder_id); + if (encoder) { + crtc_id = encoder->crtc_id; + drmModeFreeEncoder(encoder); + if (crtc_id) { + if (wsi_display_crtc_solo(wsi, mode_res, drm_connector, crtc_id)) + return crtc_id; + } + } + } + crtc_id = 0; + for (c = 0; crtc_id == 0 && c < mode_res->count_crtcs; c++) { + drmModeCrtcPtr crtc = drmModeGetCrtc(wsi->master_fd, mode_res->crtcs[c]); + if (crtc && crtc->buffer_id == 0) + crtc_id = crtc->crtc_id; + drmModeFreeCrtc(crtc); + } + return crtc_id; +} + +static VkResult +wsi_display_setup_connector(wsi_display_connector *connector, + wsi_display_mode *display_mode) +{ + struct wsi_display *wsi = connector->wsi; + drmModeModeInfoPtr drm_mode; + drmModeConnectorPtr drm_connector; + drmModeResPtr mode_res; + VkResult result; + int m; + + if (connector->current_mode == display_mode && connector->crtc_id) + return VK_SUCCESS; + + mode_res = drmModeGetResources(wsi->master_fd); + if (!mode_res) { + result = VK_ERROR_INITIALIZATION_FAILED; + goto bail; + } + + drm_connector = drmModeGetConnectorCurrent(wsi->master_fd, connector->id); + if (!drm_connector) { + result = VK_ERROR_INITIALIZATION_FAILED; + goto bail_mode_res; + } + + /* Pick a CRTC if we don't have one */ + if (!connector->crtc_id) { + connector->crtc_id = wsi_display_select_crtc(connector, mode_res, drm_connector); + if (!connector->crtc_id) { + result = VK_ERROR_OUT_OF_DATE_KHR; + goto bail_connector; + } + } + + if (connector->current_mode != display_mode) { + + /* Find the drm mode cooresponding to the requested VkDisplayMode */ + drm_mode = NULL; + for (m = 0; m < drm_connector->count_modes; m++) { + drm_mode = &drm_connector->modes[m]; + if (wsi_display_mode_matches_drm(display_mode, drm_mode)) + break; + drm_mode = NULL; + } + + if (!drm_mode) { + result = VK_ERROR_OUT_OF_DATE_KHR; + goto bail_connector; + } + + connector->current_mode = display_mode; + connector->current_drm_mode = *drm_mode; + } + + result = VK_SUCCESS; + +bail_connector: + drmModeFreeConnector(drm_connector); +bail_mode_res: + drmModeFreeResources(mode_res); +bail: + return result; + +} + +/* + * Check to see if the kernel has no flip queued and if there's an image + * waiting to be displayed. + */ +static VkResult +_wsi_display_queue_next(struct wsi_swapchain *drv_chain) +{ + struct wsi_display_swapchain *chain = (struct wsi_display_swapchain *) drv_chain; + struct wsi_display *wsi = chain->wsi; + uint32_t i; + struct wsi_display_image *image = NULL; + VkIcdSurfaceDisplay *surface = chain->surface; + wsi_display_mode *display_mode = wsi_display_mode_from_handle(surface->displayMode); + wsi_display_connector *connector = display_mode->connector; + int ret; + VkResult result; + + if (wsi->master_fd < 0) + return VK_ERROR_INITIALIZATION_FAILED; + + if (display_mode != connector->current_mode) + connector->active = false; + + for (;;) { + /* Check to see if there is an image to display, or if some image is already queued */ + + for (i = 0; i < chain->base.image_count; i++) { + struct wsi_display_image *tmp_image = &chain->images[i]; + + switch (tmp_image->state) { + case wsi_image_flipping: + /* already flipping, don't send another to the kernel yet */ + return VK_SUCCESS; + case wsi_image_queued: + /* find the oldest queued */ + if (!image || tmp_image->flip_sequence < image->flip_sequence) + image = tmp_image; + break; + default: + break; + } + } + + if (!image) + return VK_SUCCESS; + + if (connector->active) { + ret = drmModePageFlip(wsi->master_fd, connector->crtc_id, image->fb_id, + DRM_MODE_PAGE_FLIP_EVENT, image); + if (ret == 0) { + image->state = wsi_image_flipping; + return VK_SUCCESS; + } + wsi_display_debug("page flip err %d %s\n", ret, strerror(-ret)); + } else + ret = -EINVAL; + + if (ret) { + switch(-ret) { + case EINVAL: + + result = wsi_display_setup_connector(connector, display_mode); + + if (result != VK_SUCCESS) { + image->state = wsi_image_idle; + return result; + } + + /* XXX allow setting of position */ + + ret = drmModeSetCrtc(wsi->master_fd, connector->crtc_id, image->fb_id, 0, 0, + &connector->id, 1, &connector->current_drm_mode); + + if (ret == 0) { + image->state = wsi_image_displaying; + wsi_display_idle_old_displaying(image); + connector->active = true; + return VK_SUCCESS; + } + break; + case EACCES: + usleep(1000 * 1000); + connector->active = false; + break; + default: + connector->active = false; + image->state = wsi_image_idle; + return VK_ERROR_OUT_OF_DATE_KHR; + } + } + } +} + +static VkResult +wsi_display_queue_present(struct wsi_swapchain *drv_chain, + uint32_t image_index, + const VkPresentRegionKHR *damage) +{ + struct wsi_display_swapchain *chain = (struct wsi_display_swapchain *) drv_chain; + struct wsi_display *wsi = chain->wsi; + struct wsi_display_image *image = &chain->images[image_index]; + VkResult result; + + assert(image->state == wsi_image_drawing); + wsi_display_debug("present %d\n", image_index); + + pthread_mutex_lock(&wsi->wait_mutex); + + image->flip_sequence = ++chain->flip_sequence; + image->state = wsi_image_queued; + + result = _wsi_display_queue_next(drv_chain); + + pthread_mutex_unlock(&wsi->wait_mutex); + + return result; +} + +static VkResult +wsi_display_surface_create_swapchain(VkIcdSurfaceBase *icd_surface, + VkDevice device, + struct wsi_device *wsi_device, + int local_fd, + const VkSwapchainCreateInfoKHR *create_info, + const VkAllocationCallbacks *allocator, + struct wsi_swapchain **swapchain_out) +{ + struct wsi_display *wsi = (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY]; + VkResult result; + + assert(create_info->sType == VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR); + + struct wsi_display_swapchain *chain; + const unsigned num_images = create_info->minImageCount; + size_t size = sizeof(*chain) + num_images * sizeof(chain->images[0]); + + chain = vk_alloc(allocator, size, 8, + VK_SYSTEM_ALLOCATION_SCOPE_OBJECT); + + if (chain == NULL) + return VK_ERROR_OUT_OF_HOST_MEMORY; + + result = wsi_swapchain_init(wsi_device, &chain->base, device, + create_info, allocator); + + chain->base.destroy = wsi_display_swapchain_destroy; + chain->base.get_wsi_image = wsi_display_get_wsi_image; + chain->base.acquire_next_image = wsi_display_acquire_next_image; + chain->base.queue_present = wsi_display_queue_present; + chain->base.present_mode = create_info->presentMode; + chain->base.image_count = num_images; + + chain->wsi = wsi; + + chain->surface = (VkIcdSurfaceDisplay *) icd_surface; + + for (uint32_t image = 0; image < chain->base.image_count; image++) { + result = wsi_display_image_init(device, &chain->base, create_info, allocator, + &chain->images[image]); + if (result != VK_SUCCESS) + goto fail_init_images; + } + + *swapchain_out = &chain->base; + + return VK_SUCCESS; + +fail_init_images: + return result; +} + +VkResult +wsi_display_init_wsi(struct wsi_device *wsi_device, + const VkAllocationCallbacks *alloc, + VkPhysicalDevice physical_device, + int device_fd) +{ + struct wsi_display *wsi; + VkResult result; + + wsi = vk_alloc(alloc, sizeof(*wsi), 8, + VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE); + if (!wsi) { + result = VK_ERROR_OUT_OF_HOST_MEMORY; + goto fail; + } + memset(wsi, '\0', sizeof (*wsi)); + + wsi->master_fd = -1; + if (drmGetNodeTypeFromFd(device_fd) == DRM_NODE_PRIMARY) + wsi->master_fd = device_fd; + wsi->render_fd = device_fd; + + pthread_mutex_init(&wsi->wait_mutex, NULL); + wsi->physical_device = physical_device; + wsi->alloc = alloc; + + LIST_INITHEAD(&wsi->display_modes); + LIST_INITHEAD(&wsi->connectors); + + pthread_condattr_t condattr; + int ret; + + ret = pthread_mutex_init(&wsi->wait_mutex, NULL); + if (ret) { + result = VK_ERROR_OUT_OF_HOST_MEMORY; + goto fail_mutex; + } + + ret = pthread_condattr_init(&condattr); + if (ret) { + result = VK_ERROR_OUT_OF_HOST_MEMORY; + goto fail_condattr; + } + + ret = pthread_condattr_setclock(&condattr, CLOCK_MONOTONIC); + if (ret) { + result = VK_ERROR_OUT_OF_HOST_MEMORY; + goto fail_setclock; + } + + ret = pthread_cond_init(&wsi->wait_cond, &condattr); + if (ret) { + result = VK_ERROR_OUT_OF_HOST_MEMORY; + goto fail_cond; + } + + pthread_condattr_destroy(&condattr); + + wsi->base.get_support = wsi_display_surface_get_support; + wsi->base.get_capabilities = wsi_display_surface_get_capabilities; + wsi->base.get_capabilities2 = wsi_display_surface_get_capabilities2; + wsi->base.get_formats = wsi_display_surface_get_formats; + wsi->base.get_formats2 = wsi_display_surface_get_formats2; + wsi->base.get_present_modes = wsi_display_surface_get_present_modes; + wsi->base.create_swapchain = wsi_display_surface_create_swapchain; + + wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY] = &wsi->base; + + return VK_SUCCESS; + +fail_cond: +fail_setclock: + pthread_condattr_destroy(&condattr); +fail_condattr: + pthread_mutex_destroy(&wsi->wait_mutex); +fail_mutex: + vk_free(alloc, wsi); +fail: + return result; +} + +void +wsi_display_finish_wsi(struct wsi_device *wsi_device, + const VkAllocationCallbacks *alloc) +{ + struct wsi_display *wsi = (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY]; + + if (wsi) { + + struct wsi_display_connector *connector, *connector_storage; + LIST_FOR_EACH_ENTRY_SAFE(connector, connector_storage, &wsi->connectors, list) { + vk_free(wsi->alloc, connector); + } + + struct wsi_display_mode *mode, *mode_storage; + LIST_FOR_EACH_ENTRY_SAFE(mode, mode_storage, &wsi->display_modes, list) { + vk_free(wsi->alloc, mode); + } + + pthread_mutex_lock(&wsi->wait_mutex); + if (wsi->wait_thread) { + pthread_cancel(wsi->wait_thread); + pthread_join(wsi->wait_thread, NULL); + } + pthread_mutex_unlock(&wsi->wait_mutex); + pthread_mutex_destroy(&wsi->wait_mutex); + pthread_cond_destroy(&wsi->wait_cond); + + vk_free(alloc, wsi); + } +} diff --git a/src/vulkan/wsi/wsi_common_display.h b/src/vulkan/wsi/wsi_common_display.h new file mode 100644 index 00000000000..b414a226293 --- /dev/null +++ b/src/vulkan/wsi/wsi_common_display.h @@ -0,0 +1,72 @@ +/* + * Copyright © 2017 Keith Packard + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#ifndef WSI_COMMON_DISPLAY_H +#define WSI_COMMON_DISPLAY_H + +#include "wsi_common.h" +#include <xf86drm.h> +#include <xf86drmMode.h> + +#define typed_memcpy(dest, src, count) ({ \ + STATIC_ASSERT(sizeof(*src) == sizeof(*dest)); \ + memcpy((dest), (src), (count) * sizeof(*(src))); \ +}) + +VkResult +wsi_display_get_physical_device_display_properties(VkPhysicalDevice physical_device, + struct wsi_device *wsi_device, + uint32_t *property_count, + VkDisplayPropertiesKHR *properties); +VkResult +wsi_display_get_physical_device_display_plane_properties(VkPhysicalDevice physical_device, + struct wsi_device *wsi_device, + uint32_t *property_count, + VkDisplayPlanePropertiesKHR *properties); + +VkResult +wsi_display_get_display_plane_supported_displays(VkPhysicalDevice physical_device, + struct wsi_device *wsi_device, + uint32_t plane_index, + uint32_t *display_count, + VkDisplayKHR *displays); +VkResult +wsi_display_get_display_mode_properties(VkPhysicalDevice physical_device, + struct wsi_device *wsi_device, + VkDisplayKHR display, + uint32_t *property_count, + VkDisplayModePropertiesKHR *properties); + +VkResult +wsi_get_display_plane_capabilities(VkPhysicalDevice physical_device, + struct wsi_device *wsi_device, + VkDisplayModeKHR mode_khr, + uint32_t plane_index, + VkDisplayPlaneCapabilitiesKHR *capabilities); + +VkResult +wsi_create_display_surface(VkInstance instance, + const VkAllocationCallbacks *pAllocator, + const VkDisplaySurfaceCreateInfoKHR *pCreateInfo, + VkSurfaceKHR *pSurface); + +#endif diff --git a/src/vulkan/wsi/wsi_common_private.h b/src/vulkan/wsi/wsi_common_private.h index 503b2a015dc..d38d2efa116 100644 --- a/src/vulkan/wsi/wsi_common_private.h +++ b/src/vulkan/wsi/wsi_common_private.h @@ -135,6 +135,16 @@ void wsi_wl_finish_wsi(struct wsi_device *wsi_device, const VkAllocationCallbacks *alloc);
+VkResult +wsi_display_init_wsi(struct wsi_device *wsi_device, + const VkAllocationCallbacks *alloc, + VkPhysicalDevice physical_device, + int device_fd); + +void +wsi_display_finish_wsi(struct wsi_device *wsi_device, + const VkAllocationCallbacks *alloc); + #define WSI_DEFINE_NONDISP_HANDLE_CASTS(__wsi_type, __VkType) \ \ static inline struct __wsi_type * \
Continuing on the new version of the patch...
On Tue, Feb 13, 2018 at 4:31 PM, Keith Packard keithp@keithp.com wrote:
+enum wsi_image_state {
- wsi_image_idle,
- wsi_image_drawing,
- wsi_image_queued,
- wsi_image_flipping,
- wsi_image_displaying
+};
With the exception of NIR (which is a bit odd), we usually use ALL_CAPS for enum values.
+static const VkPresentModeKHR available_present_modes[] = {
- VK_PRESENT_MODE_FIFO_KHR,
Yes, this does seem like the only reasonable mode for now. I suppose you could check to see if the platform supports ASYNC_FLIP and advertise IMMEDIATE in that case. I know intel doesn't advertise it but I thought amdgpu does.
+};
+static VkResult +wsi_display_surface_get_present_modes(VkIcdSurfaceBase *surface,
uint32_t
*present_mode_count,
VkPresentModeKHR *present_modes)
+{
- if (present_modes == NULL) {
*present_mode_count = ARRAY_SIZE(available_present_modes);
return VK_SUCCESS;
- }
- *present_mode_count = MIN2(*present_mode_count,
ARRAY_SIZE(available_present_modes));
- typed_memcpy(present_modes, available_present_modes,
*present_mode_count);
- if (*present_mode_count < ARRAY_SIZE(available_present_modes))
return VK_INCOMPLETE;
- return VK_SUCCESS;
vk_outarray, though I suppose you've probably already made that change.
+}
+static VkResult +wsi_display_image_init(VkDevice device_h,
struct wsi_swapchain *drv_chain,
const VkSwapchainCreateInfoKHR *create_info,
const VkAllocationCallbacks *allocator,
struct wsi_display_image *image)
+{
- struct wsi_display_swapchain *chain = (struct wsi_display_swapchain *)
drv_chain;
- struct wsi_display *wsi = chain->wsi;
- VkResult result;
- int ret;
- uint32_t image_handle;
- if (chain->base.use_prime_blit)
result = wsi_create_prime_image(&chain->base, create_info,
&image->base);
I don't see use_prime_blit being set anywhere. Perhaps that comes in a later patch? The line is obviously correct, so I'm fine with leaving it.
- else
result = wsi_create_native_image(&chain->base, create_info,
&image->base);
This will have to be updated for modifiers. I'm reasonably happy for it to be the no-op update for now though KHR_display supporting real modifiers would be nice. :-)
- if (result != VK_SUCCESS)
return result;
- ret = drmPrimeFDToHandle(wsi->master_fd, image->base.fd,
&image_handle);
This opens up some interesting questions. GEM handles are not reference counted by the kernel. If the driver that we're running on is using master_fd, then we shouldn't close our handle because that would close the handle for the driver as well. If it's not using master_fd, we should close the handle to avoid leaking it. The later is only a worry in the prime case but given that I saw a line above about use_prime_blit, you have me scared. :-)
One solution to this connundrum would be to simply never use the master FD for the Vulkan driver itself and always open a render node. If handed a master FD, you could check if it matches your render node and set use_prime_blit accordingly. Then the driver and WSI would have separate handle domains and this problem would be solved. You could also just open the master FD twice, once for WSI and once for the driver.
- close(image->base.fd);
- image->base.fd = -1;
- if (ret < 0)
goto fail_handle;
- image->chain = chain;
- image->state = wsi_image_idle;
- image->fb_id = 0;
- /* XXX extract depth and bpp from image somehow */
You have the format in create_info. We could add some generic VkFormat introspection or we can just handle the 2-4 cases we care about directly.
- ret = drmModeAddFB(wsi->master_fd, create_info->imageExtent.width,
create_info->imageExtent.height,
24, 32, image->base.row_pitch, image_handle,
&image->fb_id);
What happens if we close the handle immediately after calling drmModeAddFB? Does the FB become invalid? If so, then we probably want to stash the handle so we can clean it up when we destroy the swapchain. Unless, of course, we're willing to make the assumption (as stated above) that the driver and WSI are always using the same FD.
- if (ret)
goto fail_fb;
- return VK_SUCCESS;
+fail_fb:
- /* fall through */
+fail_handle:
- wsi_destroy_image(&chain->base, &image->base);
- return VK_ERROR_OUT_OF_HOST_MEMORY;
+}
+static void +wsi_display_image_finish(struct wsi_swapchain *drv_chain,
const VkAllocationCallbacks *allocator,
struct wsi_display_image *image)
+{
- struct wsi_display_swapchain *chain = (struct wsi_display_swapchain *)
drv_chain;
- wsi_destroy_image(&chain->base, &image->base);
+}
+static VkResult +wsi_display_swapchain_destroy(struct wsi_swapchain *drv_chain,
const VkAllocationCallbacks
*allocator) +{
- struct wsi_display_swapchain *chain = (struct wsi_display_swapchain *)
drv_chain;
- for (uint32_t i = 0; i < chain->base.image_count; i++)
wsi_display_image_finish(drv_chain, allocator, &chain->images[i]);
- vk_free(allocator, chain);
- return VK_SUCCESS;
+}
+static struct wsi_image * +wsi_display_get_wsi_image(struct wsi_swapchain *drv_chain,
uint32_t image_index)
+{
- struct wsi_display_swapchain *chain = (struct wsi_display_swapchain *)
drv_chain;
- return &chain->images[image_index].base;
+}
+static void +wsi_display_idle_old_displaying(struct wsi_display_image *active_image) +{
- struct wsi_display_swapchain *chain = active_image->chain;
- wsi_display_debug("idle everyone but %ld\n", active_image -
&(chain->images[0]));
- for (uint32_t i = 0; i < chain->base.image_count; i++)
if (chain->images[i].state == wsi_image_displaying &&
&chain->images[i] != active_image) {
wsi_display_debug("idle %d\n", i);
chain->images[i].state = wsi_image_idle;
}
+}
+static VkResult +_wsi_display_queue_next(struct wsi_swapchain *drv_chain);
+static void +wsi_display_page_flip_handler2(int fd,
unsigned int frame,
unsigned int sec,
unsigned int usec,
uint32_t crtc_id,
void *data)
+{
- struct wsi_display_image *image = data;
- wsi_display_debug("image %ld displayed at %d\n", image -
&(image->chain->images[0]), frame);
- image->state = wsi_image_displaying;
- wsi_display_idle_old_displaying(image);
- (void) _wsi_display_queue_next(&(image->chain->base));
+}
+static void wsi_display_page_flip_handler(int fd, unsigned int frame,
unsigned int sec, unsigned int
usec, void *data) +{
- wsi_display_page_flip_handler2(fd, frame, sec, usec, 0, 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 +};
+static void * +wsi_display_wait_thread(void *data) +{
- struct wsi_display *wsi = data;
- struct pollfd pollfd = {
.fd = wsi->master_fd,
.events = POLLIN
- };
- int ret;
- pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
- for (;;) {
ret = poll(&pollfd, 1, -1);
if (ret > 0) {
pthread_mutex_lock(&wsi->wait_mutex);
(void) drmHandleEvent(wsi->master_fd, &event_context);
pthread_mutex_unlock(&wsi->wait_mutex);
pthread_cond_broadcast(&wsi->wait_cond);
}
- }
- return NULL;
+}
+static int +wsi_display_start_wait_thread(struct wsi_display *wsi) +{
- if (!wsi->wait_thread) {
int ret = pthread_create(&wsi->wait_thread, NULL,
wsi_display_wait_thread, wsi);
if (ret)
return ret;
- }
- return 0;
+}
+/* call with wait_mutex held */
It might be worth a line in the comment to say that this function does not guarntee that any particular type of event (or indeed any event at all) has been processed. It's just a yield.
+static int +wsi_display_wait_for_event(struct wsi_display *wsi,
uint64_t timeout_ns)
+{
- int ret;
- ret = wsi_display_start_wait_thread(wsi);
- if (ret)
return ret;
- struct timespec abs_timeout = {
.tv_sec = timeout_ns / ((uint64_t) 1000 * (uint64_t) 1000 *
(uint64_t) 1000),
.tv_nsec = timeout_ns % ((uint64_t) 1000 * (uint64_t) 1000 *
(uint64_t) 1000)
- };
- ret = pthread_cond_timedwait(&wsi->wait_cond, &wsi->wait_mutex,
&abs_timeout);
+
- wsi_display_debug("%9ld done waiting for event %d\n", pthread_self(),
ret);
- return ret;
+}
+static VkResult +wsi_display_acquire_next_image(struct wsi_swapchain *drv_chain,
uint64_t timeout,
VkSemaphore semaphore,
uint32_t *image_index)
+{
- struct wsi_display_swapchain *chain = (struct wsi_display_swapchain
*)drv_chain;
- struct wsi_display *wsi = chain->wsi;
- int ret = 0;
- VkResult result = VK_SUCCESS;
- if (timeout != 0 && timeout != UINT64_MAX)
timeout += wsi_get_current_monotonic();
- 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;
result = VK_SUCCESS;
goto done;
}
wsi_display_debug("image %d state %d\n", i,
chain->images[i].state);
}
if (ret == ETIMEDOUT) {
result = VK_TIMEOUT;
goto done;
}
ret = wsi_display_wait_for_event(wsi, timeout);
if (ret && ret != ETIMEDOUT) {
result = VK_ERROR_OUT_OF_DATE_KHR;
I don't see anywhere where you track any sort of per-swapchain error status. We need something so that, once you get VK_ERROR_OUT_OF_DATE, most of the other functions will start bailing immediately with OUT_OF_DATE instead of assuming the client catches it the first time it's returned. In particular, if wsi_display_queue_next fails inside an event handler, we need to know. In the X11 WSI patches, we just have a VkResult called "status" in the swapchain.
goto done;
}
- }
+done:
- pthread_mutex_unlock(&wsi->wait_mutex);
- return result;
+}
+/*
- Check whether there are any other connectors driven by this crtc
- */
+static bool +wsi_display_crtc_solo(struct wsi_display *wsi,
drmModeResPtr mode_res,
drmModeConnectorPtr connector,
uint32_t crtc_id)
+{
- int c, e;
- /* See if any other connectors share the same encoder */
- for (c = 0; c < mode_res->count_connectors; c++) {
if (mode_res->connectors[c] == connector->connector_id)
continue;
drmModeConnectorPtr other_connector =
drmModeGetConnector(wsi->master_fd, mode_res->connectors[c]);
if (other_connector) {
bool match = (other_connector->encoder_id
== connector->encoder_id);
Lots of whitespace in here. We don't usually try to line stuff up in code unless there's a good reason.
drmModeFreeConnector(other_connector);
if (match)
return false;
}
- }
- /* See if any other encoders share the same crtc */
- for (e = 0; e < mode_res->count_encoders; e++) {
if (mode_res->encoders[e] == connector->encoder_id)
continue;
drmModeEncoderPtr other_encoder =
drmModeGetEncoder(wsi->master_fd, mode_res->encoders[e]);
if (other_encoder) {
bool match = (other_encoder->crtc_id ==
crtc_id);
Same here.
drmModeFreeEncoder(other_encoder);
if (match)
return false;
}
- }
- return true;
+}
+/*
- Pick a suitable CRTC to drive this connector. Prefer a CRTC which is
- currently driving this connector and not any others. Settle for a CRTC
- which is currently idle.
- */
+static uint32_t +wsi_display_select_crtc(struct wsi_display_connector *connector,
drmModeResPtr mode_res,
drmModeConnectorPtr drm_connector)
+{
- struct wsi_display *wsi = connector->wsi;
- int c;
- uint32_t crtc_id;
- /* See what CRTC is currently driving this connector */
- if (drm_connector->encoder_id) {
drmModeEncoderPtr encoder = drmModeGetEncoder(wsi->master_fd,
drm_connector->encoder_id);
if (encoder) {
crtc_id = encoder->crtc_id;
drmModeFreeEncoder(encoder);
if (crtc_id) {
if (wsi_display_crtc_solo(wsi, mode_res, drm_connector,
crtc_id))
return crtc_id;
}
}
- }
- crtc_id = 0;
- for (c = 0; crtc_id == 0 && c < mode_res->count_crtcs; c++) {
drmModeCrtcPtr crtc = drmModeGetCrtc(wsi->master_fd,
mode_res->crtcs[c]);
if (crtc && crtc->buffer_id == 0)
crtc_id = crtc->crtc_id;
drmModeFreeCrtc(crtc);
I take it that 0 is not a valid crtc_id?
- }
- return crtc_id;
+}
+static VkResult +wsi_display_setup_connector(wsi_display_connector *connector,
wsi_display_mode *display_mode)
+{
- struct wsi_display *wsi = connector->wsi;
- drmModeModeInfoPtr drm_mode;
- drmModeConnectorPtr drm_connector;
- drmModeResPtr mode_res;
- VkResult result;
- int m;
- if (connector->current_mode == display_mode && connector->crtc_id)
return VK_SUCCESS;
- mode_res = drmModeGetResources(wsi->master_fd);
- if (!mode_res) {
result = VK_ERROR_INITIALIZATION_FAILED;
This looks more like an OUT_OF_DATE to me. Unless, of course, it's actually an OUT_OF_HOST_MEMORY.
goto bail;
- }
- drm_connector = drmModeGetConnectorCurrent(wsi->master_fd,
connector->id);
- if (!drm_connector) {
result = VK_ERROR_INITIALIZATION_FAILED;
Same here.
goto bail_mode_res;
- }
- /* Pick a CRTC if we don't have one */
- if (!connector->crtc_id) {
connector->crtc_id = wsi_display_select_crtc(connector, mode_res,
drm_connector);
if (!connector->crtc_id) {
result = VK_ERROR_OUT_OF_DATE_KHR;
goto bail_connector;
}
- }
- if (connector->current_mode != display_mode) {
Extra new line?
/* Find the drm mode cooresponding to the requested VkDisplayMode */
drm_mode = NULL;
for (m = 0; m < drm_connector->count_modes; m++) {
drm_mode = &drm_connector->modes[m];
if (wsi_display_mode_matches_drm(display_mode, drm_mode))
break;
drm_mode = NULL;
}
if (!drm_mode) {
result = VK_ERROR_OUT_OF_DATE_KHR;
goto bail_connector;
}
connector->current_mode = display_mode;
connector->current_drm_mode = *drm_mode;
- }
- result = VK_SUCCESS;
+bail_connector:
- drmModeFreeConnector(drm_connector);
+bail_mode_res:
- drmModeFreeResources(mode_res);
+bail:
- return result;
+}
+/*
- Check to see if the kernel has no flip queued and if there's an image
- waiting to be displayed.
- */
+static VkResult +_wsi_display_queue_next(struct wsi_swapchain *drv_chain) +{
- struct wsi_display_swapchain *chain = (struct wsi_display_swapchain *)
drv_chain;
- struct wsi_display *wsi = chain->wsi;
- uint32_t i;
- struct wsi_display_image *image = NULL;
- VkIcdSurfaceDisplay *surface = chain->surface;
- wsi_display_mode *display_mode =
wsi_display_mode_from_handle(surface->displayMode);
- wsi_display_connector *connector = display_mode->connector;
- int ret;
- VkResult result;
- if (wsi->master_fd < 0)
return VK_ERROR_INITIALIZATION_FAILED;
Is this possible? If so, it should be OUT_OF_DATE
- if (display_mode != connector->current_mode)
connector->active = false;
- for (;;) {
/* Check to see if there is an image to display, or if some image
is already queued */
for (i = 0; i < chain->base.image_count; i++) {
struct wsi_display_image *tmp_image = &chain->images[i];
switch (tmp_image->state) {
case wsi_image_flipping:
/* already flipping, don't send another to the kernel yet */
return VK_SUCCESS;
case wsi_image_queued:
/* find the oldest queued */
if (!image || tmp_image->flip_sequence < image->flip_sequence)
image = tmp_image;
break;
default:
break;
}
}
if (!image)
return VK_SUCCESS;
if (connector->active) {
ret = drmModePageFlip(wsi->master_fd, connector->crtc_id,
image->fb_id,
DRM_MODE_PAGE_FLIP_EVENT, image);
if (ret == 0) {
image->state = wsi_image_flipping;
return VK_SUCCESS;
}
wsi_display_debug("page flip err %d %s\n", ret, strerror(-ret));
} else
ret = -EINVAL;
If one half uses braces, please use braces on the other half.
if (ret) {
switch(-ret) {
case EINVAL:
result = wsi_display_setup_connector(connector,
display_mode);
if (result != VK_SUCCESS) {
image->state = wsi_image_idle;
return result;
As I stated earlier, I think we want some sort of chain->status so that we can easily know that things have gone out-of-date and we don't end up retrying just because the client called QueuePresent again.
}
/* XXX allow setting of position */
ret = drmModeSetCrtc(wsi->master_fd, connector->crtc_id,
image->fb_id, 0, 0,
&connector->id, 1,
&connector->current_drm_mode);
if (ret == 0) {
image->state = wsi_image_displaying;
wsi_display_idle_old_displaying(image);
Can we really idle them immediately or do we need to wait until this image has begun to scan out before returning a new image from AcquireNextImage? I suppose we do use implicit fencing for all WSI images today so this is probably ok. Still, might be worth a TODO comment or something.
connector->active = true;
return VK_SUCCESS;
}
break;
case EACCES:
usleep(1000 * 1000);
?
connector->active = false;
break;
default:
connector->active = false;
image->state = wsi_image_idle;
return VK_ERROR_OUT_OF_DATE_KHR;
}
}
- }
+}
+static VkResult +wsi_display_queue_present(struct wsi_swapchain *drv_chain,
uint32_t image_index,
const VkPresentRegionKHR *damage)
+{
- struct wsi_display_swapchain *chain = (struct wsi_display_swapchain *)
drv_chain;
- struct wsi_display *wsi = chain->wsi;
- struct wsi_display_image *image = &chain->images[image_index];
- VkResult result;
- assert(image->state == wsi_image_drawing);
- wsi_display_debug("present %d\n", image_index);
- pthread_mutex_lock(&wsi->wait_mutex);
assert(image->state == wsi_image_idle);
- image->flip_sequence = ++chain->flip_sequence;
- image->state = wsi_image_queued;
- result = _wsi_display_queue_next(drv_chain);
- pthread_mutex_unlock(&wsi->wait_mutex);
- return result;
+}
+static VkResult +wsi_display_surface_create_swapchain(VkIcdSurfaceBase *icd_surface,
VkDevice
device,
struct wsi_device
*wsi_device,
int
local_fd,
const VkSwapchainCreateInfoKHR
*create_info,
const VkAllocationCallbacks
*allocator,
struct wsi_swapchain
**swapchain_out) +{
- struct wsi_display *wsi = (struct wsi_display *)
wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
- VkResult result;
- assert(create_info->sType == VK_STRUCTURE_TYPE_SWAPCHAIN_
CREATE_INFO_KHR);
- struct wsi_display_swapchain *chain;
- const unsigned num_images = create_info->minImageCount;
- size_t size = sizeof(*chain) + num_images * sizeof(chain->images[0]);
- chain = vk_alloc(allocator, size, 8,
VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
- if (chain == NULL)
return VK_ERROR_OUT_OF_HOST_MEMORY;
- result = wsi_swapchain_init(wsi_device, &chain->base, device,
create_info, allocator);
- chain->base.destroy = wsi_display_swapchain_destroy;
- chain->base.get_wsi_image = wsi_display_get_wsi_image;
- chain->base.acquire_next_image = wsi_display_acquire_next_image;
- chain->base.queue_present = wsi_display_queue_present;
- chain->base.present_mode = create_info->presentMode;
- chain->base.image_count = num_images;
- chain->wsi = wsi;
- chain->surface = (VkIcdSurfaceDisplay *) icd_surface;
- for (uint32_t image = 0; image < chain->base.image_count; image++) {
result = wsi_display_image_init(device, &chain->base, create_info,
allocator,
&chain->images[image]);
if (result != VK_SUCCESS)
goto fail_init_images;
- }
- *swapchain_out = &chain->base;
- return VK_SUCCESS;
+fail_init_images:
You need to clean up if you are only able to create some of the images.
- return result;
+}
+VkResult +wsi_display_init_wsi(struct wsi_device *wsi_device,
const VkAllocationCallbacks *alloc,
VkPhysicalDevice physical_device,
You can get the physical device from the wsi_device. No need to pass it in separately.
int device_fd)
+{
- struct wsi_display *wsi;
- VkResult result;
- wsi = vk_alloc(alloc, sizeof(*wsi), 8,
VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE);
- if (!wsi) {
result = VK_ERROR_OUT_OF_HOST_MEMORY;
goto fail;
- }
- memset(wsi, '\0', sizeof (*wsi));
- wsi->master_fd = -1;
- if (drmGetNodeTypeFromFd(device_fd) == DRM_NODE_PRIMARY)
wsi->master_fd = device_fd;
- wsi->render_fd = device_fd;
- pthread_mutex_init(&wsi->wait_mutex, NULL);
- wsi->physical_device = physical_device;
I don't see this being used anywhere. Is it needed?
- wsi->alloc = alloc;
- LIST_INITHEAD(&wsi->display_modes);
- LIST_INITHEAD(&wsi->connectors);
- pthread_condattr_t condattr;
- int ret;
- ret = pthread_mutex_init(&wsi->wait_mutex, NULL);
- if (ret) {
result = VK_ERROR_OUT_OF_HOST_MEMORY;
goto fail_mutex;
- }
- ret = pthread_condattr_init(&condattr);
- if (ret) {
result = VK_ERROR_OUT_OF_HOST_MEMORY;
goto fail_condattr;
- }
- ret = pthread_condattr_setclock(&condattr, CLOCK_MONOTONIC);
- if (ret) {
result = VK_ERROR_OUT_OF_HOST_MEMORY;
goto fail_setclock;
- }
- ret = pthread_cond_init(&wsi->wait_cond, &condattr);
- if (ret) {
result = VK_ERROR_OUT_OF_HOST_MEMORY;
goto fail_cond;
- }
- pthread_condattr_destroy(&condattr);
- wsi->base.get_support = wsi_display_surface_get_support;
- wsi->base.get_capabilities = wsi_display_surface_get_capabilities;
- wsi->base.get_capabilities2 = wsi_display_surface_get_capabilities2;
- wsi->base.get_formats = wsi_display_surface_get_formats;
- wsi->base.get_formats2 = wsi_display_surface_get_formats2;
- wsi->base.get_present_modes = wsi_display_surface_get_present_modes;
- wsi->base.create_swapchain = wsi_display_surface_create_swapchain;
- wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY] = &wsi->base;
- return VK_SUCCESS;
+fail_cond: +fail_setclock:
- pthread_condattr_destroy(&condattr);
+fail_condattr:
- pthread_mutex_destroy(&wsi->wait_mutex);
+fail_mutex:
- vk_free(alloc, wsi);
+fail:
- return result;
+}
+void +wsi_display_finish_wsi(struct wsi_device *wsi_device,
const VkAllocationCallbacks *alloc)
+{
- struct wsi_display *wsi = (struct wsi_display *)
wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
- if (wsi) {
struct wsi_display_connector *connector, *connector_storage;
LIST_FOR_EACH_ENTRY_SAFE(connector, connector_storage,
&wsi->connectors, list) {
vk_free(wsi->alloc, connector);
}
struct wsi_display_mode *mode, *mode_storage;
LIST_FOR_EACH_ENTRY_SAFE(mode, mode_storage, &wsi->display_modes,
list) {
vk_free(wsi->alloc, mode);
}
pthread_mutex_lock(&wsi->wait_mutex);
if (wsi->wait_thread) {
pthread_cancel(wsi->wait_thread);
pthread_join(wsi->wait_thread, NULL);
}
pthread_mutex_unlock(&wsi->wait_mutex);
pthread_mutex_destroy(&wsi->wait_mutex);
pthread_cond_destroy(&wsi->wait_cond);
vk_free(alloc, wsi);
- }
+} diff --git a/src/vulkan/wsi/wsi_common_display.h b/src/vulkan/wsi/wsi_common_display.h new file mode 100644 index 00000000000..b414a226293 --- /dev/null +++ b/src/vulkan/wsi/wsi_common_display.h @@ -0,0 +1,72 @@ +/*
- Copyright © 2017 Keith Packard
- Permission to use, copy, modify, distribute, and sell this software
and its
- documentation for any purpose is hereby granted without fee, provided
that
- the above copyright notice appear in all copies and that both that
copyright
- notice and this permission notice appear in supporting documentation,
and
- that the name of the copyright holders not be used in advertising or
- publicity pertaining to distribution of the software without specific,
- written prior permission. The copyright holders make no
representations
- about the suitability of this software for any purpose. It is
provided "as
- is" without express or implied warranty.
- THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
SOFTWARE,
- INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
- EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT
OR
- CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
USE,
- DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
- TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE
- OF THIS SOFTWARE.
- */
+#ifndef WSI_COMMON_DISPLAY_H +#define WSI_COMMON_DISPLAY_H
+#include "wsi_common.h" +#include <xf86drm.h> +#include <xf86drmMode.h>
+#define typed_memcpy(dest, src, count) ({ \
- STATIC_ASSERT(sizeof(*src) == sizeof(*dest)); \
- memcpy((dest), (src), (count) * sizeof(*(src))); \
+})
+VkResult +wsi_display_get_physical_device_display_properties(VkPhysicalDevice physical_device,
struct wsi_device *wsi_device,
uint32_t *property_count,
VkDisplayPropertiesKHR *properties); +VkResult +wsi_display_get_physical_device_display_plane_properties(VkPhysicalDevice physical_device,
struct
wsi_device *wsi_device,
uint32_t *property_count,
VkDisplayPlanePropertiesKHR *properties);
+VkResult +wsi_display_get_display_plane_supported_displays(VkPhysicalDevice physical_device,
struct wsi_device *wsi_device,
uint32_t plane_index,
uint32_t *display_count,
VkDisplayKHR *displays);
+VkResult +wsi_display_get_display_mode_properties(VkPhysicalDevice physical_device,
struct wsi_device
*wsi_device,
VkDisplayKHR
display,
uint32_t
*property_count,
VkDisplayModePropertiesKHR
*properties);
+VkResult +wsi_get_display_plane_capabilities(VkPhysicalDevice physical_device,
struct wsi_device
*wsi_device,
VkDisplayModeKHR
mode_khr,
uint32_t
plane_index,
VkDisplayPlaneCapabilitiesKHR
*capabilities);
+VkResult +wsi_create_display_surface(VkInstance instance,
const VkAllocationCallbacks *pAllocator,
const VkDisplaySurfaceCreateInfoKHR
*pCreateInfo,
VkSurfaceKHR *pSurface);
+#endif diff --git a/src/vulkan/wsi/wsi_common_private.h b/src/vulkan/wsi/wsi_common_private.h index 503b2a015dc..d38d2efa116 100644 --- a/src/vulkan/wsi/wsi_common_private.h +++ b/src/vulkan/wsi/wsi_common_private.h @@ -135,6 +135,16 @@ void wsi_wl_finish_wsi(struct wsi_device *wsi_device, const VkAllocationCallbacks *alloc);
+VkResult +wsi_display_init_wsi(struct wsi_device *wsi_device,
const VkAllocationCallbacks *alloc,
VkPhysicalDevice physical_device,
int device_fd);
+void +wsi_display_finish_wsi(struct wsi_device *wsi_device,
const VkAllocationCallbacks *alloc);
#define WSI_DEFINE_NONDISP_HANDLE_CASTS(__wsi_type, __VkType) \
\ static inline struct __wsi_type * \ -- 2.15.1
dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
Hi,
On 24 February 2018 at 00:43, Jason Ekstrand jason@jlekstrand.net wrote:
On Tue, Feb 13, 2018 at 4:31 PM, Keith Packard keithp@keithp.com wrote:
- image->chain = chain;
- image->state = wsi_image_idle;
- image->fb_id = 0;
- /* XXX extract depth and bpp from image somehow */
You have the format in create_info. We could add some generic VkFormat introspection or we can just handle the 2-4 cases we care about directly.
Or better, just use drmModeAddFB2 and pass the format directly. That landed back in 3.2 or so, and I don't think there's anyone trying to use Vulkan on a kernel from 2011.
Cheers, Daniel
Daniel Stone daniel@fooishbar.org writes:
Or better, just use drmModeAddFB2 and pass the format directly. That landed back in 3.2 or so, and I don't think there's anyone trying to use Vulkan on a kernel from 2011.
Yeah, that's probably a better plan. I've pushed a patch that does this on top of the long list of patches made in response to Jason's email.
Once he's replied to that, I'll go ahead and smash the new patches back on top of the original series and re-publish that.
Jason Ekstrand jason@jlekstrand.net writes:
Thanks a million for the intense review. I've pushed 15 tiny patches that address individual questions in this message. If you want to look at those separately, that would be great. When we're done, I'll merge them back into the giant patch.
Sorry for the delay in answering; you asked a hard question about GEM handles and reference counting which I think I've resolved by eliminating the ability to share the same fd for modesetting and rendering.
Let me know if I've deleted too much context from your answers.
With the exception of NIR (which is a bit odd), we usually use ALL_CAPS for enum values.
Thanks, fixed.
Yes, this does seem like the only reasonable mode for now. I suppose you could check to see if the platform supports ASYNC_FLIP and advertise IMMEDIATE in that case. I know intel doesn't advertise it but I thought amdgpu does.
Ok, we'll put that on the wish list, along with MAILBOX (which applications actually want :-)
vk_outarray, though I suppose you've probably already made that change.
Yup, vk_outarray everywhere now.
I don't see use_prime_blit being set anywhere. Perhaps that comes in a later patch? The line is obviously correct, so I'm fine with leaving it.
You're right -- this was a cult-n-paste error. I don't support prime at all, so I've removed this bit.
This will have to be updated for modifiers. I'm reasonably happy for it to be the no-op update for now though KHR_display supporting real modifiers would be nice. :-)
Yeah, "patches welcome", of course. I don't have a requirement for them on Radeon at this point.
- if (result != VK_SUCCESS)
return result;
- ret = drmPrimeFDToHandle(wsi->master_fd, image->base.fd,
&image_handle);
This opens up some interesting questions. GEM handles are not reference counted by the kernel. If the driver that we're running on is using master_fd, then we shouldn't close our handle because that would close the handle for the driver as well. If it's not using master_fd, we should close the handle to avoid leaking it. The later is only a worry in the prime case but given that I saw a line above about use_prime_blit, you have me scared. :-)
Ok, as I mentioned in the header of this message, I've eliminated any code which talks about potentially sharing master_fd and render_fd. I removed render_fd from the wsi layer entirely as it is no longer used anywhere.
In the drivers, when you request KHR_display, I attempt to open the related primary node and if that succeeds, I pass that to the wsi layer for use there. That fd is otherwise unused by the driver, and in fact the driver doesn't need to close it as the wsi layer will.
Obviously having two FDs is "wasteful" at some level as we really don't need that, but that's what the acquire_xlib extension will have to do anyways.
One solution to this connundrum would be to simply never use the master FD for the Vulkan driver itself and always open a render node. If handed a master FD, you could check if it matches your render node and set use_prime_blit accordingly. Then the driver and WSI would have separate handle domains and this problem would be solved. You could also just open the master FD twice, once for WSI and once for the driver.
Yup, two FDs, one master, one render. That way, the kludge seen in this patch where it opens the master when you request the KHR_display extension works the same as the acquire_xlib code in the later patch.
You have the format in create_info. We could add some generic VkFormat introspection or we can just handle the 2-4 cases we care about directly.
Given that this layer provides the list of valid surface formats which that image could contain, I've added depth/bpp information to that list and the image_init code walks over the list of possible formats to find the associated depth/bpp values.
If the application provides an invalid format, I'm returning VK_ERROR_DEVICE_LOST as that is a valid error return and I couldn't find anything else that seemed like a better value. If we get here, the application made a mistake anyways.
What happens if we close the handle immediately after calling drmModeAddFB? Does the FB become invalid? If so, then we probably want to stash the handle so we can clean it up when we destroy the swapchain. Unless, of course, we're willing to make the assumption (as stated above) that the driver and WSI are always using the same FD.
It looks like the FB ends up with a reference to the object, so it would should be safe to just close the handle at this point. However, to make it less dependent on the kernel behavior, I've changed the code to store the buffer_object handle in the image and then added code to free both the buffer object and the frame buffer in wsi_display_image_finish.
Thanks for finding the leak.
+/* call with wait_mutex held */
It might be worth a line in the comment to say that this function does not guarntee that any particular type of event (or indeed any event at all) has been processed. It's just a yield.
Yeah, more comments are always good :-)
I don't see anywhere where you track any sort of per-swapchain error status. We need something so that, once you get VK_ERROR_OUT_OF_DATE, most of the other functions will start bailing immediately with OUT_OF_DATE instead of assuming the client catches it the first time it's returned. In particular, if wsi_display_queue_next fails inside an event handler, we need to know. In the X11 WSI patches, we just have a VkResult called "status" in the swapchain.
Failure is not an option? In reality, failures in the display backend should only result from an actual unexpected error condition, unlike the X backend where windows get destroyed or resized without the applications knowledge.
However, that doesn't mean we shouldn't handle them correctly. I now record asynchronous failures in a 'chain->status' value and return that to applications in acquire_next_image and queue_present.
Lots of whitespace in here. We don't usually try to line stuff up in code unless there's a good reason.
Same here.
Fixed.
I take it that 0 is not a valid crtc_id?
Correct.
- mode_res = drmModeGetResources(wsi->master_fd);
- if (!mode_res) {
result = VK_ERROR_INITIALIZATION_FAILED;
This looks more like an OUT_OF_DATE to me. Unless, of course, it's actually an OUT_OF_HOST_MEMORY.
Yeah, VK_ERROR_OUT_OF_HOST_MEMORY if errno is ENOMEM, otherwise the ioctl failed and we've lost our ability to do master operations (vt switched away, or a revoked lease), in which case OUT_OF_DATE_KHR seems appropriate.
- if (!drm_connector) {
result = VK_ERROR_INITIALIZATION_FAILED;
Same here.
Same fix.
Extra new line?
There's a comment afterwards, so I like a bit of whitespace to highlight that.
- if (wsi->master_fd < 0)
return VK_ERROR_INITIALIZATION_FAILED;
Is this possible? If so, it should be OUT_OF_DATE
It's only possible if you call into this API without having gotten a master_fd set, or after calling vkReleaseDisplay (part of the direct_mode_display extension).
I don't know what the right error is in this case as the application did something wrong or didn't have the right permissions. It "can't happen" in normal operation, so OUT_OF_DATE seems inappropriate to me.
Suggestions welcome.
If one half uses braces, please use braces on the other half.
I found another case doing this
As I stated earlier, I think we want some sort of chain->status so that we can easily know that things have gone out-of-date and we don't end up retrying just because the client called QueuePresent again.
All fixed -- queue_next returns are captured in chain->status and returned at subsequent calls to queue_present or acquire_next_image.
ret = drmModeSetCrtc(wsi->master_fd, connector->crtc_id,
image->fb_id, 0, 0,
&connector->id, 1,
&connector->current_drm_mode);
if (ret == 0) {
image->state = wsi_image_displaying;
wsi_display_idle_old_displaying(image);
Can we really idle them immediately or do we need to wait until this image has begun to scan out before returning a new image from AcquireNextImage?
After ModeSetCrtc, the old image is no longer being used. There's no event delivered that could tell us that the scanout started later anyways.
I suppose we do use implicit fencing for all WSI images today so this is probably ok. Still, might be worth a TODO comment or something.
Given that we're not going to receive an event telling us that the image is idle, I'm not sure what we could ever do, but a comment is probably wise.
case EACCES:
usleep(1000 * 1000);
?
We get EACCES when VT switched away, so we just want to idle until VT switched back. After we get back, we'll get EINVAL from the page flip ioctl, which will cause us to re-set the mode.
If there were an event delivered on VT switch, I could monitor for that and make the switch back happen faster.
I'll add a comment.
+static VkResult +wsi_display_queue_present(struct wsi_swapchain *drv_chain,
uint32_t image_index,
const VkPresentRegionKHR *damage)
+{
- struct wsi_display_swapchain *chain = (struct wsi_display_swapchain *)
drv_chain;
- struct wsi_display *wsi = chain->wsi;
- struct wsi_display_image *image = &chain->images[image_index];
- VkResult result;
- assert(image->state == wsi_image_drawing);
- wsi_display_debug("present %d\n", image_index);
- pthread_mutex_lock(&wsi->wait_mutex);
assert(image->state == wsi_image_idle);
I don't understand. I'm asserting that you can't queue an image that you haven't gotten from acquire_next_image, which puts the image in WSI_IMAGE_DRAWING state.
You need to clean up if you are only able to create some of the images.
Oops.
+VkResult +wsi_display_init_wsi(struct wsi_device *wsi_device,
const VkAllocationCallbacks *alloc,
VkPhysicalDevice physical_device,
You can get the physical device from the wsi_device. No need to pass it in separately.
No, you can't (there's very little in wsi_device), but it turns out that I never needed this value anyways. I've removed it from the API and from 'struct wsi_display'.
I don't see this being used anywhere. Is it needed?
Nope. See above.
This adds support for the KHR_display extension to the anv Vulkan driver. The driver now attempts to open the master DRM node when the KHR_display extension is requested so that the common winsys code can perform the necessary operations.
Signed-off-by: Keith Packard keithp@keithp.com --- src/intel/Makefile.sources | 3 + src/intel/Makefile.vulkan.am | 7 ++ src/intel/vulkan/anv_device.c | 18 ++++- src/intel/vulkan/anv_extensions.py | 1 + src/intel/vulkan/anv_extensions_gen.py | 5 +- src/intel/vulkan/anv_wsi_display.c | 129 +++++++++++++++++++++++++++++++++ src/intel/vulkan/meson.build | 7 ++ 7 files changed, 165 insertions(+), 5 deletions(-) create mode 100644 src/intel/vulkan/anv_wsi_display.c
diff --git a/src/intel/Makefile.sources b/src/intel/Makefile.sources index 9595bf42582..6c142729d94 100644 --- a/src/intel/Makefile.sources +++ b/src/intel/Makefile.sources @@ -240,6 +240,9 @@ VULKAN_WSI_WAYLAND_FILES := \ VULKAN_WSI_X11_FILES := \ vulkan/anv_wsi_x11.c
+VULKAN_WSI_DISPLAY_FILES := \ + vulkan/anv_wsi_display.c + VULKAN_GEM_FILES := \ vulkan/anv_gem.c
diff --git a/src/intel/Makefile.vulkan.am b/src/intel/Makefile.vulkan.am index 23fa877e77d..7c428a799d7 100644 --- a/src/intel/Makefile.vulkan.am +++ b/src/intel/Makefile.vulkan.am @@ -187,6 +187,13 @@ VULKAN_SOURCES += $(VULKAN_WSI_WAYLAND_FILES) VULKAN_LIB_DEPS += $(WAYLAND_CLIENT_LIBS) endif
+if HAVE_PLATFORM_DISPLAY +VULKAN_CPPFLAGS += \ + -DVK_USE_PLATFORM_DISPLAY_KHR + +VULKAN_SOURCES += $(VULKAN_WSI_DISPLAY_FILES) +endif + noinst_LTLIBRARIES += vulkan/libvulkan_common.la vulkan_libvulkan_common_la_SOURCES = $(VULKAN_SOURCES) vulkan_libvulkan_common_la_CFLAGS = $(VULKAN_CFLAGS) diff --git a/src/intel/vulkan/anv_device.c b/src/intel/vulkan/anv_device.c index 86c1bdc1d51..9614907fda3 100644 --- a/src/intel/vulkan/anv_device.c +++ b/src/intel/vulkan/anv_device.c @@ -277,14 +277,25 @@ anv_physical_device_init_uuids(struct anv_physical_device *device) static VkResult anv_physical_device_init(struct anv_physical_device *device, struct anv_instance *instance, - const char *path) + const char *primary_path, + const char *render_path) { VkResult result; - int fd; + int fd = -1; + const char *path;
brw_process_intel_debug_variable();
- fd = open(path, O_RDWR | O_CLOEXEC); + if (instance->enabled_extensions.KHR_display) { + path = primary_path; + fd = open(path, O_RDWR | O_CLOEXEC); + } + + if (fd < 0) { + path = render_path; + fd = open(path, O_RDWR | O_CLOEXEC); + } + if (fd < 0) return vk_error(VK_ERROR_INCOMPATIBLE_DRIVER);
@@ -652,6 +663,7 @@ anv_enumerate_devices(struct anv_instance *instance)
result = anv_physical_device_init(&instance->physicalDevice, instance, + devices[i]->nodes[DRM_NODE_PRIMARY], devices[i]->nodes[DRM_NODE_RENDER]); if (result != VK_ERROR_INCOMPATIBLE_DRIVER) break; diff --git a/src/intel/vulkan/anv_extensions.py b/src/intel/vulkan/anv_extensions.py index 581921e62a1..112dcef5bbd 100644 --- a/src/intel/vulkan/anv_extensions.py +++ b/src/intel/vulkan/anv_extensions.py @@ -83,6 +83,7 @@ EXTENSIONS = [ Extension('VK_KHR_wayland_surface', 6, 'VK_USE_PLATFORM_WAYLAND_KHR'), Extension('VK_KHR_xcb_surface', 6, 'VK_USE_PLATFORM_XCB_KHR'), Extension('VK_KHR_xlib_surface', 6, 'VK_USE_PLATFORM_XLIB_KHR'), + Extension('VK_KHR_display', 23, 'VK_USE_PLATFORM_DISPLAY_KHR'), Extension('VK_KHX_multiview', 1, True), Extension('VK_EXT_debug_report', 8, True), Extension('VK_EXT_external_memory_dma_buf', 1, True), diff --git a/src/intel/vulkan/anv_extensions_gen.py b/src/intel/vulkan/anv_extensions_gen.py index 33827ecd015..84d07f9767a 100644 --- a/src/intel/vulkan/anv_extensions_gen.py +++ b/src/intel/vulkan/anv_extensions_gen.py @@ -113,7 +113,7 @@ _TEMPLATE_C = Template(COPYRIGHT + """ #include "vk_util.h"
/* Convert the VK_USE_PLATFORM_* defines to booleans */ -%for platform in ['ANDROID', 'WAYLAND', 'XCB', 'XLIB']: +%for platform in ['ANDROID', 'WAYLAND', 'XCB', 'XLIB', 'DISPLAY']: #ifdef VK_USE_PLATFORM_${platform}_KHR # undef VK_USE_PLATFORM_${platform}_KHR # define VK_USE_PLATFORM_${platform}_KHR true @@ -132,7 +132,8 @@ _TEMPLATE_C = Template(COPYRIGHT + """
#define ANV_HAS_SURFACE (VK_USE_PLATFORM_WAYLAND_KHR || \ VK_USE_PLATFORM_XCB_KHR || \ - VK_USE_PLATFORM_XLIB_KHR) + VK_USE_PLATFORM_XLIB_KHR || \ + VK_USE_PLATFORM_DISPLAY_KHR)
const VkExtensionProperties anv_instance_extensions[ANV_INSTANCE_EXTENSION_COUNT] = { %for ext in instance_extensions: diff --git a/src/intel/vulkan/anv_wsi_display.c b/src/intel/vulkan/anv_wsi_display.c new file mode 100644 index 00000000000..9b00d7f02e4 --- /dev/null +++ b/src/intel/vulkan/anv_wsi_display.c @@ -0,0 +1,129 @@ +/* + * Copyright © 2017 Keith Packard + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#include "anv_private.h" +#include "wsi_common.h" +#include "vk_format_info.h" +#include "vk_util.h" +#include "wsi_common_display.h" + +#define MM_PER_PIXEL (1.0/96.0 * 25.4) + +VkResult +anv_GetPhysicalDeviceDisplayPropertiesKHR(VkPhysicalDevice physical_device, + uint32_t *property_count, + VkDisplayPropertiesKHR *properties) +{ + ANV_FROM_HANDLE(anv_physical_device, pdevice, physical_device); + + return wsi_display_get_physical_device_display_properties(physical_device, + &pdevice->wsi_device, + property_count, + properties); +} + +VkResult +anv_GetPhysicalDeviceDisplayPlanePropertiesKHR(VkPhysicalDevice physical_device, + uint32_t *property_count, + VkDisplayPlanePropertiesKHR *properties) +{ + ANV_FROM_HANDLE(anv_physical_device, pdevice, physical_device); + + return wsi_display_get_physical_device_display_plane_properties(physical_device, + &pdevice->wsi_device, + property_count, + properties); +} + +VkResult +anv_GetDisplayPlaneSupportedDisplaysKHR(VkPhysicalDevice physical_device, + uint32_t plane_index, + uint32_t *display_count, + VkDisplayKHR *displays) +{ + ANV_FROM_HANDLE(anv_physical_device, pdevice, physical_device); + + return wsi_display_get_display_plane_supported_displays(physical_device, + &pdevice->wsi_device, + plane_index, + display_count, + displays); +} + + +VkResult +anv_GetDisplayModePropertiesKHR(VkPhysicalDevice physical_device, + VkDisplayKHR display, + uint32_t *property_count, + VkDisplayModePropertiesKHR *properties) +{ + ANV_FROM_HANDLE(anv_physical_device, pdevice, physical_device); + + return wsi_display_get_display_mode_properties(physical_device, + &pdevice->wsi_device, + display, + property_count, + properties); +} + +VkResult +anv_CreateDisplayModeKHR(VkPhysicalDevice physical_device, + VkDisplayKHR display, + const VkDisplayModeCreateInfoKHR *create_info, + const VkAllocationCallbacks *allocator, + VkDisplayModeKHR *mode) +{ + return VK_ERROR_INITIALIZATION_FAILED; +} + + +VkResult +anv_GetDisplayPlaneCapabilitiesKHR(VkPhysicalDevice physical_device, + VkDisplayModeKHR mode_khr, + uint32_t plane_index, + VkDisplayPlaneCapabilitiesKHR *capabilities) +{ + ANV_FROM_HANDLE(anv_physical_device, pdevice, physical_device); + + return wsi_get_display_plane_capabilities(physical_device, + &pdevice->wsi_device, + mode_khr, + plane_index, + capabilities); +} + +VkResult +anv_CreateDisplayPlaneSurfaceKHR(VkInstance _instance, + const VkDisplaySurfaceCreateInfoKHR *create_info, + const VkAllocationCallbacks *allocator, + VkSurfaceKHR *surface) +{ + ANV_FROM_HANDLE(anv_instance, instance, _instance); + const VkAllocationCallbacks *alloc; + + if (allocator) + alloc = allocator; + else + alloc = &instance->alloc; + + return wsi_create_display_surface(_instance, alloc, create_info, surface); +} diff --git a/src/intel/vulkan/meson.build b/src/intel/vulkan/meson.build index 69ec26e19b6..2e2ab8f7ecd 100644 --- a/src/intel/vulkan/meson.build +++ b/src/intel/vulkan/meson.build @@ -171,6 +171,13 @@ if with_platform_wayland libanv_files += files('anv_wsi_wayland.c') endif
+if with_platform_display + anv_flags += [ + '-DVK_USE_PLATFORM_DISPLAY_KHR', + ] + libanv_files += files('anv_wsi_display.c') +endif + libanv_common = static_library( 'anv_common', [libanv_files, anv_entrypoints, anv_extensions_c, anv_extensions_h],
This adds support for the KHR_display extension to the radv Vulkan driver. The driver now attempts to open the master DRM node when the KHR_display extension is requested so that the common winsys code can perform the necessary operations.
Signed-off-by: Keith Packard keithp@keithp.com --- src/amd/vulkan/Makefile.am | 8 +++ src/amd/vulkan/Makefile.sources | 3 + src/amd/vulkan/meson.build | 7 ++ src/amd/vulkan/radv_device.c | 25 ++++++- src/amd/vulkan/radv_extensions.py | 7 +- src/amd/vulkan/radv_private.h | 2 + src/amd/vulkan/radv_wsi_display.c | 143 ++++++++++++++++++++++++++++++++++++++ 7 files changed, 191 insertions(+), 4 deletions(-) create mode 100644 src/amd/vulkan/radv_wsi_display.c
diff --git a/src/amd/vulkan/Makefile.am b/src/amd/vulkan/Makefile.am index 61025968942..061b8144b88 100644 --- a/src/amd/vulkan/Makefile.am +++ b/src/amd/vulkan/Makefile.am @@ -76,6 +76,14 @@ VULKAN_LIB_DEPS = \ $(DLOPEN_LIBS) \ -lm
+if HAVE_PLATFORM_DISPLAY +AM_CPPFLAGS += \ + -DVK_USE_PLATFORM_DISPLAY_KHR + +VULKAN_SOURCES += $(VULKAN_WSI_DISPLAY_FILES) + +endif + if HAVE_PLATFORM_X11 AM_CPPFLAGS += \ $(XCB_DRI3_CFLAGS) \ diff --git a/src/amd/vulkan/Makefile.sources b/src/amd/vulkan/Makefile.sources index a510d88d965..618a6cdaed0 100644 --- a/src/amd/vulkan/Makefile.sources +++ b/src/amd/vulkan/Makefile.sources @@ -78,6 +78,9 @@ VULKAN_WSI_WAYLAND_FILES := \ VULKAN_WSI_X11_FILES := \ radv_wsi_x11.c
+VULKAN_WSI_DISPLAY_FILES := \ + radv_wsi_display.c + VULKAN_GENERATED_FILES := \ radv_entrypoints.c \ radv_entrypoints.h \ diff --git a/src/amd/vulkan/meson.build b/src/amd/vulkan/meson.build index 0a7b7c0bf3c..b7bb1075e7d 100644 --- a/src/amd/vulkan/meson.build +++ b/src/amd/vulkan/meson.build @@ -112,6 +112,13 @@ if with_platform_wayland libradv_files += files('radv_wsi_wayland.c') endif
+if with_platform_display + radv_flags += [ + '-DVK_USE_PLATFORM_DISPLAY_KHR', + ] + libradv_files += files('radv_wsi_display.c') +endif + libvulkan_radeon = shared_library( 'vulkan_radeon', [libradv_files, radv_entrypoints, radv_extensions_c, vk_format_table_c], diff --git a/src/amd/vulkan/radv_device.c b/src/amd/vulkan/radv_device.c index 09bb382eeb8..8ae7391c891 100644 --- a/src/amd/vulkan/radv_device.c +++ b/src/amd/vulkan/radv_device.c @@ -191,9 +191,26 @@ radv_physical_device_init(struct radv_physical_device *device, const char *path = drm_device->nodes[DRM_NODE_RENDER]; VkResult result; drmVersionPtr version; - int fd; + int fd = -1; + + if (instance->khr_display_requested) { + fd = open(drm_device->nodes[DRM_NODE_PRIMARY], O_RDWR | O_CLOEXEC); + if (fd >= 0) { + uint32_t accel_working = 0; + struct drm_amdgpu_info request = { + .return_pointer = (uintptr_t)&accel_working, + .return_size = sizeof(accel_working), + .query = AMDGPU_INFO_ACCEL_WORKING + };
- fd = open(path, O_RDWR | O_CLOEXEC); + if (drmCommandWrite(fd, DRM_AMDGPU_INFO, &request, sizeof (struct drm_amdgpu_info)) < 0 || !accel_working) { + close(fd); + fd = -1; + } + } + } + if (fd < 0) + fd = open(path, O_RDWR | O_CLOEXEC); if (fd < 0) return vk_error(VK_ERROR_INCOMPATIBLE_DRIVER);
@@ -387,6 +404,7 @@ VkResult radv_CreateInstance( { struct radv_instance *instance; VkResult result; + bool khr_display_requested = false;
assert(pCreateInfo->sType == VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO);
@@ -411,6 +429,8 @@ VkResult radv_CreateInstance( const char *ext_name = pCreateInfo->ppEnabledExtensionNames[i]; if (!radv_instance_extension_supported(ext_name)) return vk_error(VK_ERROR_EXTENSION_NOT_PRESENT); + if (strcmp(ext_name, VK_KHR_DISPLAY_EXTENSION_NAME) == 0) + khr_display_requested = true; }
instance = vk_zalloc2(&default_alloc, pAllocator, sizeof(*instance), 8, @@ -427,6 +447,7 @@ VkResult radv_CreateInstance(
instance->apiVersion = client_version; instance->physicalDeviceCount = -1; + instance->khr_display_requested = khr_display_requested;
result = vk_debug_report_instance_init(&instance->debug_report_callbacks); if (result != VK_SUCCESS) { diff --git a/src/amd/vulkan/radv_extensions.py b/src/amd/vulkan/radv_extensions.py index d761895d3a0..8a079595d40 100644 --- a/src/amd/vulkan/radv_extensions.py +++ b/src/amd/vulkan/radv_extensions.py @@ -81,6 +81,7 @@ EXTENSIONS = [ Extension('VK_KHR_wayland_surface', 6, 'VK_USE_PLATFORM_WAYLAND_KHR'), Extension('VK_KHR_xcb_surface', 6, 'VK_USE_PLATFORM_XCB_KHR'), Extension('VK_KHR_xlib_surface', 6, 'VK_USE_PLATFORM_XLIB_KHR'), + Extension('VK_KHR_display', 23, '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), @@ -168,7 +169,7 @@ _TEMPLATE = Template(COPYRIGHT + """ #include "vk_util.h"
/* Convert the VK_USE_PLATFORM_* defines to booleans */ -%for platform in ['ANDROID', 'WAYLAND', 'XCB', 'XLIB']: +%for platform in ['ANDROID', 'WAYLAND', 'XCB', 'XLIB', 'DISPLAY']: #ifdef VK_USE_PLATFORM_${platform}_KHR # undef VK_USE_PLATFORM_${platform}_KHR # define VK_USE_PLATFORM_${platform}_KHR true @@ -187,7 +188,9 @@ _TEMPLATE = Template(COPYRIGHT + """
#define RADV_HAS_SURFACE (VK_USE_PLATFORM_WAYLAND_KHR || \ VK_USE_PLATFORM_XCB_KHR || \ - VK_USE_PLATFORM_XLIB_KHR) + VK_USE_PLATFORM_XLIB_KHR || \ + VK_USE_PLATFORM_DISPLAY_KHR) +
bool radv_instance_extension_supported(const char *name) diff --git a/src/amd/vulkan/radv_private.h b/src/amd/vulkan/radv_private.h index be9e8f43964..1e3719bcc4f 100644 --- a/src/amd/vulkan/radv_private.h +++ b/src/amd/vulkan/radv_private.h @@ -75,6 +75,7 @@ typedef uint32_t xcb_window_t; #include "radv_entrypoints.h"
#include "wsi_common.h" +#include "wsi_common_display.h"
#define ATI_VENDOR_ID 0x1002
@@ -300,6 +301,7 @@ struct radv_instance { uint64_t perftest_flags;
struct vk_debug_report_instance debug_report_callbacks; + bool khr_display_requested; };
VkResult radv_init_wsi(struct radv_physical_device *physical_device); diff --git a/src/amd/vulkan/radv_wsi_display.c b/src/amd/vulkan/radv_wsi_display.c new file mode 100644 index 00000000000..f98a071513d --- /dev/null +++ b/src/amd/vulkan/radv_wsi_display.c @@ -0,0 +1,143 @@ +/* + * Copyright © 2017 Keith Packard + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#include <stdbool.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include "radv_private.h" +#include "radv_cs.h" +#include "util/disk_cache.h" +#include "util/strtod.h" +#include "vk_util.h" +#include <xf86drm.h> +#include <xf86drmMode.h> +#include <amdgpu.h> +#include <amdgpu_drm.h> +#include "winsys/amdgpu/radv_amdgpu_winsys_public.h" +#include "ac_llvm_util.h" +#include "vk_format.h" +#include "sid.h" +#include "util/debug.h" +#include "wsi_common_display.h" + +#define MM_PER_PIXEL (1.0/96.0 * 25.4) + +VkResult +radv_GetPhysicalDeviceDisplayPropertiesKHR(VkPhysicalDevice physical_device, + uint32_t *property_count, + VkDisplayPropertiesKHR *properties) +{ + RADV_FROM_HANDLE(radv_physical_device, pdevice, physical_device); + + return wsi_display_get_physical_device_display_properties(physical_device, + &pdevice->wsi_device, + property_count, + properties); +} + +VkResult +radv_GetPhysicalDeviceDisplayPlanePropertiesKHR(VkPhysicalDevice physical_device, + uint32_t *property_count, + VkDisplayPlanePropertiesKHR *properties) +{ + RADV_FROM_HANDLE(radv_physical_device, pdevice, physical_device); + + return wsi_display_get_physical_device_display_plane_properties(physical_device, + &pdevice->wsi_device, + property_count, + properties); +} + +VkResult +radv_GetDisplayPlaneSupportedDisplaysKHR(VkPhysicalDevice physical_device, + uint32_t plane_index, + uint32_t *display_count, + VkDisplayKHR *displays) +{ + RADV_FROM_HANDLE(radv_physical_device, pdevice, physical_device); + + return wsi_display_get_display_plane_supported_displays(physical_device, + &pdevice->wsi_device, + plane_index, + display_count, + displays); +} + + +VkResult +radv_GetDisplayModePropertiesKHR(VkPhysicalDevice physical_device, + VkDisplayKHR display, + uint32_t *property_count, + VkDisplayModePropertiesKHR *properties) +{ + RADV_FROM_HANDLE(radv_physical_device, pdevice, physical_device); + + return wsi_display_get_display_mode_properties(physical_device, + &pdevice->wsi_device, + display, + property_count, + properties); +} + +VkResult +radv_CreateDisplayModeKHR(VkPhysicalDevice physical_device, + VkDisplayKHR display, + const VkDisplayModeCreateInfoKHR *create_info, + const VkAllocationCallbacks *allocator, + VkDisplayModeKHR *mode) +{ + return VK_ERROR_INITIALIZATION_FAILED; +} + + +VkResult +radv_GetDisplayPlaneCapabilitiesKHR(VkPhysicalDevice physical_device, + VkDisplayModeKHR mode_khr, + uint32_t plane_index, + VkDisplayPlaneCapabilitiesKHR *capabilities) +{ + RADV_FROM_HANDLE(radv_physical_device, pdevice, physical_device); + + return wsi_get_display_plane_capabilities(physical_device, + &pdevice->wsi_device, + mode_khr, + plane_index, + capabilities); +} + +VkResult +radv_CreateDisplayPlaneSurfaceKHR(VkInstance _instance, + const VkDisplaySurfaceCreateInfoKHR *create_info, + const VkAllocationCallbacks *allocator, + VkSurfaceKHR *surface) +{ + RADV_FROM_HANDLE(radv_instance, instance, _instance); + const VkAllocationCallbacks *alloc; + + if (allocator) + alloc = allocator; + else + alloc = &instance->alloc; + + return wsi_create_display_surface(_instance, alloc, create_info, surface); +}
Add support for the EXT_direct_mode_display extension. This just provides the vkReleaseDisplayEXT function.
Signed-off-by: Keith Packard keithp@keithp.com --- src/vulkan/wsi/wsi_common_display.c | 17 +++++++++++++++++ src/vulkan/wsi/wsi_common_display.h | 5 +++++ 2 files changed, 22 insertions(+)
diff --git a/src/vulkan/wsi/wsi_common_display.c b/src/vulkan/wsi/wsi_common_display.c index 2732b1dd721..5c123e6465e 100644 --- a/src/vulkan/wsi/wsi_common_display.c +++ b/src/vulkan/wsi/wsi_common_display.c @@ -1366,3 +1366,20 @@ wsi_display_finish_wsi(struct wsi_device *wsi_device, vk_free(alloc, wsi); } } + +/* + * Implement vkReleaseDisplay + */ +VkResult +wsi_release_display(VkPhysicalDevice physical_device, + struct wsi_device *wsi_device, + VkDisplayKHR display) +{ + struct wsi_display *wsi = (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY]; + + if (wsi->master_fd >= 0 && wsi->master_fd != wsi->render_fd) { + close(wsi->master_fd); + wsi->master_fd = -1; + } + return VK_SUCCESS; +} diff --git a/src/vulkan/wsi/wsi_common_display.h b/src/vulkan/wsi/wsi_common_display.h index b414a226293..5fbb6925e4a 100644 --- a/src/vulkan/wsi/wsi_common_display.h +++ b/src/vulkan/wsi/wsi_common_display.h @@ -69,4 +69,9 @@ wsi_create_display_surface(VkInstance instance, const VkDisplaySurfaceCreateInfoKHR *pCreateInfo, VkSurfaceKHR *pSurface);
+VkResult +wsi_release_display(VkPhysicalDevice physical_device, + struct wsi_device *wsi_device, + VkDisplayKHR display); + #endif
Add support for the EXT_direct_mode_display extension. This just provides the vkReleaseDisplayEXT function.
Signed-off-by: Keith Packard keithp@keithp.com --- src/intel/vulkan/anv_extensions.py | 1 + src/intel/vulkan/anv_wsi_display.c | 11 +++++++++++ 2 files changed, 12 insertions(+)
diff --git a/src/intel/vulkan/anv_extensions.py b/src/intel/vulkan/anv_extensions.py index 112dcef5bbd..75b6bf0b41f 100644 --- a/src/intel/vulkan/anv_extensions.py +++ b/src/intel/vulkan/anv_extensions.py @@ -84,6 +84,7 @@ EXTENSIONS = [ Extension('VK_KHR_xcb_surface', 6, 'VK_USE_PLATFORM_XCB_KHR'), Extension('VK_KHR_xlib_surface', 6, 'VK_USE_PLATFORM_XLIB_KHR'), Extension('VK_KHR_display', 23, 'VK_USE_PLATFORM_DISPLAY_KHR'), + Extension('VK_EXT_direct_mode_display', 1, 'VK_USE_PLATFORM_DISPLAY_KHR'), Extension('VK_KHX_multiview', 1, True), Extension('VK_EXT_debug_report', 8, True), Extension('VK_EXT_external_memory_dma_buf', 1, True), diff --git a/src/intel/vulkan/anv_wsi_display.c b/src/intel/vulkan/anv_wsi_display.c index 9b00d7f02e4..e6f67f7dec9 100644 --- a/src/intel/vulkan/anv_wsi_display.c +++ b/src/intel/vulkan/anv_wsi_display.c @@ -127,3 +127,14 @@ anv_CreateDisplayPlaneSurfaceKHR(VkInstance _instance
return wsi_create_display_surface(_instance, alloc, create_info, surface); } + +VkResult +anv_ReleaseDisplayEXT(VkPhysicalDevice physical_device, + VkDisplayKHR display) +{ + ANV_FROM_HANDLE(anv_physical_device, pdevice, physical_device); + + return wsi_release_display(physical_device, + &pdevice->wsi_device, + display); +}
Add support for the EXT_direct_mode_display extension. This just provides the vkReleaseDisplayEXT function.
Signed-off-by: Keith Packard keithp@keithp.com --- src/amd/vulkan/radv_extensions.py | 1 + src/amd/vulkan/radv_wsi_display.c | 11 +++++++++++ 2 files changed, 12 insertions(+)
diff --git a/src/amd/vulkan/radv_extensions.py b/src/amd/vulkan/radv_extensions.py index 8a079595d40..b04b9bf87b0 100644 --- a/src/amd/vulkan/radv_extensions.py +++ b/src/amd/vulkan/radv_extensions.py @@ -82,6 +82,7 @@ EXTENSIONS = [ Extension('VK_KHR_xcb_surface', 6, 'VK_USE_PLATFORM_XCB_KHR'), Extension('VK_KHR_xlib_surface', 6, 'VK_USE_PLATFORM_XLIB_KHR'), Extension('VK_KHR_display', 23, 'VK_USE_PLATFORM_DISPLAY_KHR'), + Extension('VK_EXT_direct_mode_display', 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_wsi_display.c b/src/amd/vulkan/radv_wsi_display.c index f98a071513d..deaf61ce0df 100644 --- a/src/amd/vulkan/radv_wsi_display.c +++ b/src/amd/vulkan/radv_wsi_display.c @@ -141,3 +141,14 @@ radv_CreateDisplayPlaneSurfaceKHR(VkInstance _instanc
return wsi_create_display_surface(_instance, alloc, create_info, surface); } + +VkResult +radv_ReleaseDisplayEXT(VkPhysicalDevice physical_device, + VkDisplayKHR display) +{ + RADV_FROM_HANDLE(radv_physical_device, pdevice, physical_device); + + return wsi_release_display(physical_device, + &pdevice->wsi_device, + display); +}
This extension adds the ability to borrow an X RandR output for temporary use directly by a Vulkan application. For DRM, we use the Linux resource leasing mechanism.
Signed-off-by: Keith Packard keithp@keithp.com --- configure.ac | 32 +++ meson.build | 17 ++ meson_options.txt | 7 + src/vulkan/Makefile.am | 5 + src/vulkan/wsi/meson.build | 7 + src/vulkan/wsi/wsi_common_display.c | 472 ++++++++++++++++++++++++++++++++++++ src/vulkan/wsi/wsi_common_display.h | 17 ++ 7 files changed, 557 insertions(+)
diff --git a/configure.ac b/configure.ac index 46318365603..cf05d049c26 100644 --- a/configure.ac +++ b/configure.ac @@ -1547,6 +1547,7 @@ AM_CONDITIONAL(HAVE_APPLEDRI, test "x$enable_dri" = xyes -a "x$dri_platform" = x AM_CONDITIONAL(HAVE_LMSENSORS, test "x$enable_lmsensors" = xyes ) AM_CONDITIONAL(HAVE_GALLIUM_EXTRA_HUD, test "x$enable_gallium_extra_hud" = xyes ) AM_CONDITIONAL(HAVE_WINDOWSDRI, test "x$enable_dri" = xyes -a "x$dri_platform" = xwindows ) +AM_CONDITIONAL(HAVE_XLEASE, test "x$have_xlease" = xyes )
AC_ARG_ENABLE([shared-glapi], [AS_HELP_STRING([--enable-shared-glapi], @@ -1846,6 +1847,18 @@ if test x"$enable_dri3" = xyes; then PKG_CHECK_MODULES([XCB_DRI3], [$dri3_modules]) fi
+ +if echo "$platforms" | grep -q 'x11' && echo "$platforms" | grep -q 'drm'; then + have_xlease=yes +else + have_xlease=no +fi + +if test x"$have_xlease" = xyes; then + randr_modules="x11-xcb xcb-randr" + PKG_CHECK_MODULES([XCB_RANDR], [$randr_modules]) +fi + AM_CONDITIONAL(HAVE_PLATFORM_X11, echo "$platforms" | grep -q 'x11') AM_CONDITIONAL(HAVE_PLATFORM_WAYLAND, echo "$platforms" | grep -q 'wayland') AM_CONDITIONAL(HAVE_PLATFORM_DRM, echo "$platforms" | grep -q 'drm') @@ -1853,6 +1866,25 @@ AM_CONDITIONAL(HAVE_PLATFORM_DISPLAY, echo "$platforms" | grep -q 'drm') AM_CONDITIONAL(HAVE_PLATFORM_SURFACELESS, echo "$platforms" | grep -q 'surfaceless') AM_CONDITIONAL(HAVE_PLATFORM_ANDROID, echo "$platforms" | grep -q 'android')
+AC_ARG_ENABLE(xlib-lease, + [AS_HELP_STRING([--enable-xlib-lease] + [enable VK_acquire_xlib_display using X leases])], + [enable_xlib_lease=$enableval], [enable_xlib_lease=auto]) +case "x$enable_xlib_lease" in +xyes) + ;; +xno) + ;; +*) + if echo "$platforms" | grep -q 'x11' && echo "$platforms" | grep -q 'drm'; then + enable_xlib_lease=yes + else + enable_xlib_lease=no + fi +esac + +AM_CONDITIONAL(HAVE_XLIB_LEASE, test "x$enable_xlib_lease" = xyes) + dnl dnl More DRI setup dnl diff --git a/meson.build b/meson.build index aeb7f5e2917..595b0f66cd7 100644 --- a/meson.build +++ b/meson.build @@ -262,6 +262,19 @@ if _platforms != '' egl_native_platform = _split[0] endif
+with_xlib_lease = get_option('xlib-lease') +if with_xlib_lease == 'auto' + if with_platform_x11 and with_platform_display + with_xlib_lease = true + else + with_xlib_lease = false + endif +elif with_xlib_lease == 'true' + with_xlib_lease = true +else + with_xlib_lease = false +endif + with_glx = get_option('glx') if with_glx == 'auto' if with_dri @@ -1151,6 +1164,7 @@ dep_xcb_present = [] dep_xcb_sync = [] dep_xcb_xfixes = [] dep_xshmfence = [] +dep_xcb_xrandr = [] if with_platform_x11 if with_glx == 'xlib' or with_glx == 'gallium-xlib' dep_x11 = dependency('x11') @@ -1190,6 +1204,9 @@ if with_platform_x11 if with_egl dep_xcb_xfixes = dependency('xcb-xfixes') endif + if with_xlib_lease + dep_xcb_xrandr = dependency('xcb-randr', version : '>= 1.12') + endif endif
if get_option('gallium-extra-hud') diff --git a/meson_options.txt b/meson_options.txt index 7fafe2deaac..d38c9aa6149 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -286,3 +286,10 @@ option( value : '', description : 'Comma delimited list of tools to build. choices : freedreno,glsl,intel,nir,nouveau or all' ) +option( + 'xlib-lease', + type : 'combo', + value : 'auto', + choices : ['auto', 'true', 'false'], + description : 'Enable VK_EXT_acquire_xlib_display.' +) diff --git a/src/vulkan/Makefile.am b/src/vulkan/Makefile.am index c33ac5758f7..e96ef68972c 100644 --- a/src/vulkan/Makefile.am +++ b/src/vulkan/Makefile.am @@ -64,6 +64,11 @@ AM_CPPFLAGS += \ VULKAN_WSI_SOURCES += $(VULKAN_WSI_DISPLAY_FILES) endif
+if HAVE_XLIB_LEASE +AM_CPPFLAGS += \ + -DVK_USE_PLATFORM_XLIB_XRANDR_EXT +endif + BUILT_SOURCES += $(VULKAN_WSI_WAYLAND_GENERATED_FILES) CLEANFILES = $(BUILT_SOURCES)
diff --git a/src/vulkan/wsi/meson.build b/src/vulkan/wsi/meson.build index 743631a6113..5e3d43a2748 100644 --- a/src/vulkan/wsi/meson.build +++ b/src/vulkan/wsi/meson.build @@ -67,6 +67,13 @@ if with_platform_display ) endif
+if with_xlib_lease + vulkan_wsi_deps += dep_xcb_xrandr + vulkan_wsi_args += [ + '-DVK_USE_PLATFORM_XLIB_XRANDR_EXT', + ] +endif + libvulkan_wsi = static_library( 'vulkan_wsi', files_vulkan_wsi, diff --git a/src/vulkan/wsi/wsi_common_display.c b/src/vulkan/wsi/wsi_common_display.c index 5c123e6465e..29d64b21aff 100644 --- a/src/vulkan/wsi/wsi_common_display.c +++ b/src/vulkan/wsi/wsi_common_display.c @@ -32,6 +32,10 @@ #include <math.h> #include <xf86drm.h> #include <xf86drmMode.h> +#ifdef VK_USE_PLATFORM_XLIB_XRANDR_EXT +#include <xcb/randr.h> +#include <X11/Xlib-xcb.h> +#endif #include "util/hash_table.h" #include "util/list.h"
@@ -73,6 +77,9 @@ typedef struct wsi_display_connector { bool active; wsi_display_mode *current_mode; drmModeModeInfo current_drm_mode; +#ifdef VK_USE_PLATFORM_XLIB_XRANDR_EXT + xcb_randr_output_t output; +#endif } wsi_display_connector;
struct wsi_display { @@ -1381,5 +1388,470 @@ wsi_release_display(VkPhysicalDevice physical_device, close(wsi->master_fd); wsi->master_fd = -1; } +#ifdef VK_USE_PLATFORM_XLIB_XRANDR_EXT + wsi_display_connector_from_handle(display)->output = None; +#endif + return VK_SUCCESS; } + +#ifdef VK_USE_PLATFORM_XLIB_XRANDR_EXT + +static struct wsi_display_connector * +wsi_display_find_output(struct wsi_device *wsi_device, + RROutput output) +{ + struct wsi_display *wsi = (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY]; + struct wsi_display_connector *connector; + + connector = NULL; + LIST_FOR_EACH_ENTRY(connector, &wsi->connectors, list) { + if (connector->output == output) + return connector; + } + + return NULL; +} + +/* + * Given a RandR output, find the associated kernel connector_id by + * looking at the CONNECTOR_ID property provided by the X server + */ + +static uint32_t +wsi_display_output_to_connector_id(xcb_connection_t *connection, + xcb_atom_t *connector_id_atom_p, + RROutput output) +{ + uint32_t connector_id = 0; + xcb_atom_t connector_id_atom = *connector_id_atom_p; + + if (connector_id_atom == 0) { + /* Go dig out the CONNECTOR_ID property */ + xcb_intern_atom_cookie_t ia_c = xcb_intern_atom(connection, + true, + 12, + "CONNECTOR_ID"); + xcb_intern_atom_reply_t *ia_r = xcb_intern_atom_reply(connection, + ia_c, + NULL); + if (ia_r) { + *connector_id_atom_p = connector_id_atom = ia_r->atom; + free(ia_r); + } + } + + /* If there's an CONNECTOR_ID atom in the server, then there may be a CONNECTOR_ID property. Otherwise, + * there will not be and we don't even need to bother. + */ + if (connector_id_atom) { + + xcb_randr_query_version_cookie_t qv_c = xcb_randr_query_version(connection, 1, 6); + xcb_randr_get_output_property_cookie_t gop_c = xcb_randr_get_output_property(connection, + output, + connector_id_atom, + 0, + 0, + 0xffffffffUL, + 0, + 0); + xcb_randr_query_version_reply_t *qv_r = xcb_randr_query_version_reply(connection, qv_c, NULL); + free(qv_r); + xcb_randr_get_output_property_reply_t *gop_r = xcb_randr_get_output_property_reply(connection, + gop_c, + NULL); + if (gop_r) { + if (gop_r->num_items == 1 && gop_r->format == 32) + memcpy(&connector_id, xcb_randr_get_output_property_data(gop_r), 4); + free(gop_r); + } + } + return connector_id; +} + +static bool +wsi_display_check_randr_version(xcb_connection_t *connection) +{ + xcb_randr_query_version_cookie_t qv_c = xcb_randr_query_version(connection, 1, 6); + xcb_randr_query_version_reply_t *qv_r = xcb_randr_query_version_reply(connection, qv_c, NULL); + bool ret = false; + + if (!qv_r) + return false; + + /* Check for version 1.6 or newer */ + ret = qv_r->major_version > 1 || (qv_r->major_version == 1 && qv_r->minor_version >= 6); + + free(qv_r); + return ret; +} + +/* + * Given a kernel connector id, find the associated RandR output using the + * CONNECTOR_ID property + */ + +static xcb_randr_output_t +wsi_display_connector_id_to_output(xcb_connection_t *connection, + uint32_t connector_id) +{ + if (!wsi_display_check_randr_version(connection)) + return 0; + + const xcb_setup_t *setup = xcb_get_setup(connection); + + xcb_atom_t connector_id_atom = 0; + xcb_randr_output_t output = 0; + + /* Search all of the screens for the provided output */ + xcb_screen_iterator_t iter; + for (iter = xcb_setup_roots_iterator(setup); output == 0 && iter.rem; xcb_screen_next(&iter)) { + + xcb_randr_get_screen_resources_cookie_t gsr_c = xcb_randr_get_screen_resources(connection, iter.data->root); + xcb_randr_get_screen_resources_reply_t *gsr_r = xcb_randr_get_screen_resources_reply(connection, gsr_c, NULL); + + if (!gsr_r) + return 0; + + xcb_randr_output_t *ro = xcb_randr_get_screen_resources_outputs(gsr_r); + int o; + + for (o = 0; o < gsr_r->num_outputs; o++) { + if (wsi_display_output_to_connector_id(connection, &connector_id_atom, ro[o]) == connector_id) { + output = ro[o]; + break; + } + } + free(gsr_r); + } + return output; +} + +/* + * Given a RandR output, find out which screen it's associated with + */ +static xcb_window_t +wsi_display_output_to_root(xcb_connection_t *connection, + xcb_randr_output_t output) +{ + if (!wsi_display_check_randr_version(connection)) + return 0; + + const xcb_setup_t *setup = xcb_get_setup(connection); + xcb_window_t root = 0; + + /* Search all of the screens for the provided output */ + xcb_screen_iterator_t iter; + for (iter = xcb_setup_roots_iterator(setup); root == 0 && iter.rem; xcb_screen_next(&iter)) { + xcb_randr_get_screen_resources_cookie_t gsr_c = xcb_randr_get_screen_resources(connection, iter.data->root); + xcb_randr_get_screen_resources_reply_t *gsr_r = xcb_randr_get_screen_resources_reply(connection, gsr_c, NULL); + + if (!gsr_r) + return 0; + + xcb_randr_output_t *ro = xcb_randr_get_screen_resources_outputs(gsr_r); + int o; + + for (o = 0; o < gsr_r->num_outputs; o++) { + if (ro[o] == output) { + root = iter.data->root; + break; + } + } + free(gsr_r); + } + return root; +} + +static bool +wsi_display_mode_matches_x(struct wsi_display_mode *wsi, + xcb_randr_mode_info_t *xcb) +{ + return wsi->clock == (xcb->dot_clock + 500) / 1000 && + wsi->hdisplay == xcb->width && + wsi->hsync_start == xcb->hsync_start && + wsi->hsync_end == xcb->hsync_end && + wsi->htotal == xcb->htotal && + wsi->hskew == xcb->hskew && + wsi->vdisplay == xcb->height && + wsi->vsync_start == xcb->vsync_start && + wsi->vsync_end == xcb->vsync_end && + wsi->vtotal == xcb->vtotal && + wsi->flags == xcb->mode_flags; +} + +static struct wsi_display_mode * +wsi_display_find_x_mode(struct wsi_device *wsi_device, + struct wsi_display_connector *connector, + xcb_randr_mode_info_t *mode) +{ + struct wsi_display *wsi = (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY]; + struct wsi_display_mode *display_mode; + + LIST_FOR_EACH_ENTRY(display_mode, &wsi->display_modes, list) { + if (display_mode->connector == connector && + wsi_display_mode_matches_x(display_mode, mode)) + return display_mode; + } + return NULL; +} + +static VkResult +wsi_display_register_x_mode(struct wsi_device *wsi_device, + struct wsi_display_connector *connector, + xcb_randr_mode_info_t *x_mode, + bool preferred) +{ + struct wsi_display *wsi = (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY]; + struct wsi_display_mode *display_mode; + + display_mode = wsi_display_find_x_mode(wsi_device, connector, x_mode); + + if (display_mode) { + display_mode->valid = true; + return VK_SUCCESS; + } + + display_mode = vk_alloc(wsi->alloc, sizeof (struct wsi_display_mode), 8, VK_SYSTEM_ALLOCATION_SCOPE_OBJECT); + if (!display_mode) + return VK_ERROR_OUT_OF_HOST_MEMORY; + + display_mode->connector = connector; + display_mode->valid = true; + display_mode->preferred = preferred; + display_mode->clock = (x_mode->dot_clock + 500) / 1000; /* kHz */ + display_mode->hdisplay = x_mode->width; + display_mode->hsync_start = x_mode->hsync_start; + display_mode->hsync_end = x_mode->hsync_end; + display_mode->htotal = x_mode->htotal; + display_mode->hskew = x_mode->hskew; + display_mode->vdisplay = x_mode->height; + display_mode->vsync_start = x_mode->vsync_start; + display_mode->vsync_end = x_mode->vsync_end; + display_mode->vtotal = x_mode->vtotal; + display_mode->vscan = 0; + if (x_mode->mode_flags & XCB_RANDR_MODE_FLAG_DOUBLE_SCAN) + display_mode->vscan = 1; + display_mode->flags = x_mode->mode_flags; + + LIST_ADDTAIL(&display_mode->list, &wsi->display_modes); + return VK_SUCCESS; +} + +static struct wsi_display_connector * +wsi_display_get_output(struct wsi_device *wsi_device, + xcb_connection_t *connection, + RROutput output) +{ + struct wsi_display *wsi = (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY]; + struct wsi_display_connector *connector; + uint32_t connector_id; + xcb_window_t root; + xcb_randr_get_screen_resources_cookie_t src; + xcb_randr_get_screen_resources_reply_t *srr; + xcb_randr_get_output_info_cookie_t oic; + xcb_randr_get_output_info_reply_t *oir; + xcb_randr_mode_t *x_modes; + int m; + + root = wsi_display_output_to_root(connection, output); + if (!root) + return NULL; + + src = xcb_randr_get_screen_resources(connection, root); + oic = xcb_randr_get_output_info(connection, output, XCB_CURRENT_TIME); + srr = xcb_randr_get_screen_resources_reply(connection, src, NULL); + oir = xcb_randr_get_output_info_reply(connection, oic, NULL); + + /* See if we already have a connector for this output */ + connector = wsi_display_find_output(wsi_device, output); + + if (!connector) { + xcb_atom_t connector_id_atom = 0; + + /* + * Go get the kernel connector ID for this X output + */ + connector_id = wsi_display_output_to_connector_id(connection, &connector_id_atom, output); + + /* Any X server with lease support will have this atom */ + if (!connector_id) { + free(oir); + free(srr); + return NULL; + } + + if (!connector) { + /* See if we already have a connector for this id */ + connector = wsi_display_find_connector(wsi_device, connector_id); + + if (connector) + connector->output = output; + } + } + + if (!connector) { + connector = wsi_display_alloc_connector(wsi, connector_id); + if (!connector) { + free(oir); + free(srr); + return NULL; + } + LIST_ADDTAIL(&connector->list, &wsi->connectors); + connector->output = output; + } + + if (oir && srr) { + /* Get X modes and add them */ + + connector->connected = oir->connection != XCB_RANDR_CONNECTION_DISCONNECTED; + + wsi_display_invalidate_connector_modes(wsi_device, connector); + + x_modes = xcb_randr_get_output_info_modes(oir); + for (m = 0; m < oir->num_modes; m++) { + xcb_randr_mode_info_iterator_t i = xcb_randr_get_screen_resources_modes_iterator(srr); + while (i.rem) { + xcb_randr_mode_info_t *mi = i.data; + if (mi->id == x_modes[m]) { + VkResult result = wsi_display_register_x_mode(wsi_device, connector, mi, m < oir->num_preferred); + if (result != VK_SUCCESS) { + free(oir); + free(srr); + return NULL; + } + break; + } + xcb_randr_mode_info_next(&i); + } + } + } + + free(oir); + free(srr); + return connector; +} + +static xcb_randr_crtc_t +wsi_display_find_crtc_for_output(xcb_connection_t *connection, + xcb_window_t root, + xcb_randr_output_t output) +{ + xcb_randr_get_screen_resources_cookie_t gsr_c = xcb_randr_get_screen_resources(connection, root); + xcb_randr_get_screen_resources_reply_t *gsr_r = xcb_randr_get_screen_resources_reply(connection, gsr_c, NULL); + + if (!gsr_r) + return 0; + + xcb_randr_crtc_t *rc = xcb_randr_get_screen_resources_crtcs(gsr_r); + xcb_randr_crtc_t idle_crtc = 0; + xcb_randr_crtc_t active_crtc = 0; + + /* Find either a crtc already connected to the desired output or idle */ + int c; + for (c = 0; active_crtc == 0 && c < gsr_r->num_crtcs; c++) { + xcb_randr_get_crtc_info_cookie_t gci_c = xcb_randr_get_crtc_info(connection, rc[c], gsr_r->config_timestamp); + xcb_randr_get_crtc_info_reply_t *gci_r = xcb_randr_get_crtc_info_reply(connection, gci_c, NULL); + if (gci_r) { + if (gci_r->mode) { + int num_outputs = xcb_randr_get_crtc_info_outputs_length(gci_r); + xcb_randr_output_t *outputs = xcb_randr_get_crtc_info_outputs(gci_r); + for (int o = 0; o < num_outputs; o++) + if (outputs[o] == output && num_outputs == 1) { + active_crtc = rc[c]; + break; + } + } else if (idle_crtc == 0) { + int num_possible = xcb_randr_get_crtc_info_possible_length(gci_r); + xcb_randr_output_t *possible = xcb_randr_get_crtc_info_possible(gci_r); + for (int p = 0; p < num_possible; p++) + if (possible[p] == output) { + idle_crtc = rc[c]; + break; + } + } + free(gci_r); + } + } + free(gsr_r); + + if (active_crtc) + return active_crtc; + return idle_crtc; +} + +VkResult +wsi_acquire_xlib_display(VkPhysicalDevice physical_device, + struct wsi_device *wsi_device, + Display *dpy, + VkDisplayKHR display) +{ + struct wsi_display *wsi = (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY]; + xcb_connection_t *connection = XGetXCBConnection(dpy); + struct wsi_display_connector *connector = wsi_display_connector_from_handle(display); + xcb_window_t root; + + if (!connector->output) { + connector->output = wsi_display_connector_id_to_output(connection, connector->id); + + /* Check and see if we found the output */ + if (!connector->output) + return VK_ERROR_OUT_OF_DATE_KHR; + } + + root = wsi_display_output_to_root(connection, connector->output); + if (!root) + return VK_ERROR_OUT_OF_DATE_KHR; + + xcb_randr_crtc_t crtc = wsi_display_find_crtc_for_output(connection, + root, + connector->output); + + if (!crtc) + return VK_ERROR_OUT_OF_DATE_KHR; + + xcb_randr_lease_t lease = xcb_generate_id(connection); + xcb_randr_create_lease_cookie_t cl_c = xcb_randr_create_lease(connection, + root, + lease, + 1, + 1, + &crtc, + &connector->output); + xcb_randr_create_lease_reply_t *cl_r = xcb_randr_create_lease_reply(connection, cl_c, NULL); + if (!cl_r) + return VK_ERROR_OUT_OF_DATE_KHR; + + int fd = -1; + if (cl_r->nfd > 0) { + int *rcl_f = xcb_randr_create_lease_reply_fds(connection, cl_r); + + fd = rcl_f[0]; + } + free (cl_r); + if (fd < 0) + return VK_ERROR_OUT_OF_DATE_KHR; + + wsi->master_fd = fd; + + return VK_SUCCESS; +} + +VkResult +wsi_get_randr_output_display(VkPhysicalDevice physical_device, + struct wsi_device *wsi_device, + Display *dpy, + RROutput output, + VkDisplayKHR *display) +{ + xcb_connection_t *connection = XGetXCBConnection(dpy); + struct wsi_display_connector *connector = wsi_display_get_output(wsi_device, connection, output); + + if (connector) + *display = wsi_display_connector_to_handle(connector); + else + *display = NULL; + return VK_SUCCESS; +} + +#endif diff --git a/src/vulkan/wsi/wsi_common_display.h b/src/vulkan/wsi/wsi_common_display.h index 5fbb6925e4a..1997c2a3c40 100644 --- a/src/vulkan/wsi/wsi_common_display.h +++ b/src/vulkan/wsi/wsi_common_display.h @@ -74,4 +74,21 @@ wsi_release_display(VkPhysicalDevice physical_device, struct wsi_device *wsi_device, VkDisplayKHR display);
+ +#if VK_USE_PLATFORM_XLIB_XRANDR_EXT +VkResult +wsi_acquire_xlib_display(VkPhysicalDevice physical_device, + struct wsi_device *wsi_device, + Display *dpy, + VkDisplayKHR display); + +VkResult +wsi_get_randr_output_display(VkPhysicalDevice physical_device, + struct wsi_device *wsi_device, + Display *dpy, + RROutput output, + VkDisplayKHR *display); + +#endif /* VK_USE_PLATFORM_XLIB_XRANDR_EXT */ + #endif
On Tuesday, 2018-02-13 16:31:20 -0800, Keith Packard wrote:
This extension adds the ability to borrow an X RandR output for temporary use directly by a Vulkan application. For DRM, we use the Linux resource leasing mechanism.
Signed-off-by: Keith Packard keithp@keithp.com
configure.ac | 32 +++ meson.build | 17 ++ meson_options.txt | 7 + src/vulkan/Makefile.am | 5 + src/vulkan/wsi/meson.build | 7 + src/vulkan/wsi/wsi_common_display.c | 472 ++++++++++++++++++++++++++++++++++++ src/vulkan/wsi/wsi_common_display.h | 17 ++ 7 files changed, 557 insertions(+)
diff --git a/configure.ac b/configure.ac index 46318365603..cf05d049c26 100644 --- a/configure.ac +++ b/configure.ac @@ -1547,6 +1547,7 @@ AM_CONDITIONAL(HAVE_APPLEDRI, test "x$enable_dri" = xyes -a "x$dri_platform" = x AM_CONDITIONAL(HAVE_LMSENSORS, test "x$enable_lmsensors" = xyes ) AM_CONDITIONAL(HAVE_GALLIUM_EXTRA_HUD, test "x$enable_gallium_extra_hud" = xyes ) AM_CONDITIONAL(HAVE_WINDOWSDRI, test "x$enable_dri" = xyes -a "x$dri_platform" = xwindows ) +AM_CONDITIONAL(HAVE_XLEASE, test "x$have_xlease" = xyes )
AC_ARG_ENABLE([shared-glapi], [AS_HELP_STRING([--enable-shared-glapi], @@ -1846,6 +1847,18 @@ if test x"$enable_dri3" = xyes; then PKG_CHECK_MODULES([XCB_DRI3], [$dri3_modules]) fi
+if echo "$platforms" | grep -q 'x11' && echo "$platforms" | grep -q 'drm'; then
- have_xlease=yes
+else
- have_xlease=no
+fi
+if test x"$have_xlease" = xyes; then
- randr_modules="x11-xcb xcb-randr"
- PKG_CHECK_MODULES([XCB_RANDR], [$randr_modules])
+fi
AM_CONDITIONAL(HAVE_PLATFORM_X11, echo "$platforms" | grep -q 'x11') AM_CONDITIONAL(HAVE_PLATFORM_WAYLAND, echo "$platforms" | grep -q 'wayland') AM_CONDITIONAL(HAVE_PLATFORM_DRM, echo "$platforms" | grep -q 'drm') @@ -1853,6 +1866,25 @@ AM_CONDITIONAL(HAVE_PLATFORM_DISPLAY, echo "$platforms" | grep -q 'drm') AM_CONDITIONAL(HAVE_PLATFORM_SURFACELESS, echo "$platforms" | grep -q 'surfaceless') AM_CONDITIONAL(HAVE_PLATFORM_ANDROID, echo "$platforms" | grep -q 'android')
+AC_ARG_ENABLE(xlib-lease,
- [AS_HELP_STRING([--enable-xlib-lease]
[enable VK_acquire_xlib_display using X leases])],
- [enable_xlib_lease=$enableval], [enable_xlib_lease=auto])
+case "x$enable_xlib_lease" in +xyes)
- ;;
+xno)
- ;;
+*)
- if echo "$platforms" | grep -q 'x11' && echo "$platforms" | grep -q 'drm'; then
enable_xlib_lease=yes
- else
enable_xlib_lease=no
- fi
+esac
+AM_CONDITIONAL(HAVE_XLIB_LEASE, test "x$enable_xlib_lease" = xyes)
dnl dnl More DRI setup dnl diff --git a/meson.build b/meson.build index aeb7f5e2917..595b0f66cd7 100644 --- a/meson.build +++ b/meson.build @@ -262,6 +262,19 @@ if _platforms != '' egl_native_platform = _split[0] endif
+with_xlib_lease = get_option('xlib-lease') +if with_xlib_lease == 'auto'
- if with_platform_x11 and with_platform_display
- with_xlib_lease = true
- else
- with_xlib_lease = false
- endif
+elif with_xlib_lease == 'true'
- with_xlib_lease = true
+else
- with_xlib_lease = false
+endif
Can be simplified a bit:
_xlib_lease = get_option('xlib-lease') if _xlib_lease == 'auto' with_xlib_lease = with_platform_x11 and with_platform_display else with_xlib_lease = _xlib_lease == 'true' endif
(We also usually try to avoid changing the type of a var, and meson might start being more strict with types in future releases)
with_glx = get_option('glx') if with_glx == 'auto' if with_dri @@ -1151,6 +1164,7 @@ dep_xcb_present = [] dep_xcb_sync = [] dep_xcb_xfixes = [] dep_xshmfence = [] +dep_xcb_xrandr = [] if with_platform_x11 if with_glx == 'xlib' or with_glx == 'gallium-xlib' dep_x11 = dependency('x11') @@ -1190,6 +1204,9 @@ if with_platform_x11 if with_egl dep_xcb_xfixes = dependency('xcb-xfixes') endif
- if with_xlib_lease
- dep_xcb_xrandr = dependency('xcb-randr', version : '>= 1.12')
- endif
endif
if get_option('gallium-extra-hud') diff --git a/meson_options.txt b/meson_options.txt index 7fafe2deaac..d38c9aa6149 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -286,3 +286,10 @@ option( value : '', description : 'Comma delimited list of tools to build. choices : freedreno,glsl,intel,nir,nouveau or all' ) +option(
- 'xlib-lease',
- type : 'combo',
- value : 'auto',
- choices : ['auto', 'true', 'false'],
- description : 'Enable VK_EXT_acquire_xlib_display.'
+) diff --git a/src/vulkan/Makefile.am b/src/vulkan/Makefile.am index c33ac5758f7..e96ef68972c 100644 --- a/src/vulkan/Makefile.am +++ b/src/vulkan/Makefile.am @@ -64,6 +64,11 @@ AM_CPPFLAGS += \ VULKAN_WSI_SOURCES += $(VULKAN_WSI_DISPLAY_FILES) endif
+if HAVE_XLIB_LEASE +AM_CPPFLAGS += \
- -DVK_USE_PLATFORM_XLIB_XRANDR_EXT
+endif
BUILT_SOURCES += $(VULKAN_WSI_WAYLAND_GENERATED_FILES) CLEANFILES = $(BUILT_SOURCES)
diff --git a/src/vulkan/wsi/meson.build b/src/vulkan/wsi/meson.build index 743631a6113..5e3d43a2748 100644 --- a/src/vulkan/wsi/meson.build +++ b/src/vulkan/wsi/meson.build @@ -67,6 +67,13 @@ if with_platform_display ) endif
+if with_xlib_lease
- vulkan_wsi_deps += dep_xcb_xrandr
- vulkan_wsi_args += [
- '-DVK_USE_PLATFORM_XLIB_XRANDR_EXT',
- ]
vulkan_wsi_args += '-DVK_USE_PLATFORM_XLIB_XRANDR_EXT'
with that, the meson part of this is Reviewed-by: Eric Engestrom eric.engestrom@imgtec.com
+endif
libvulkan_wsi = static_library( 'vulkan_wsi', files_vulkan_wsi, diff --git a/src/vulkan/wsi/wsi_common_display.c b/src/vulkan/wsi/wsi_common_display.c index 5c123e6465e..29d64b21aff 100644 --- a/src/vulkan/wsi/wsi_common_display.c +++ b/src/vulkan/wsi/wsi_common_display.c @@ -32,6 +32,10 @@ #include <math.h> #include <xf86drm.h> #include <xf86drmMode.h> +#ifdef VK_USE_PLATFORM_XLIB_XRANDR_EXT +#include <xcb/randr.h> +#include <X11/Xlib-xcb.h> +#endif #include "util/hash_table.h" #include "util/list.h"
@@ -73,6 +77,9 @@ typedef struct wsi_display_connector { bool active; wsi_display_mode *current_mode; drmModeModeInfo current_drm_mode; +#ifdef VK_USE_PLATFORM_XLIB_XRANDR_EXT
- xcb_randr_output_t output;
+#endif } wsi_display_connector;
struct wsi_display { @@ -1381,5 +1388,470 @@ wsi_release_display(VkPhysicalDevice physical_device, close(wsi->master_fd); wsi->master_fd = -1; } +#ifdef VK_USE_PLATFORM_XLIB_XRANDR_EXT
- wsi_display_connector_from_handle(display)->output = None;
+#endif
- return VK_SUCCESS;
}
+#ifdef VK_USE_PLATFORM_XLIB_XRANDR_EXT
+static struct wsi_display_connector * +wsi_display_find_output(struct wsi_device *wsi_device,
RROutput output)
+{
- struct wsi_display *wsi = (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
- struct wsi_display_connector *connector;
- connector = NULL;
- LIST_FOR_EACH_ENTRY(connector, &wsi->connectors, list) {
if (connector->output == output)
return connector;
- }
- return NULL;
+}
+/*
- Given a RandR output, find the associated kernel connector_id by
- looking at the CONNECTOR_ID property provided by the X server
- */
+static uint32_t +wsi_display_output_to_connector_id(xcb_connection_t *connection,
xcb_atom_t *connector_id_atom_p,
RROutput output)
+{
- uint32_t connector_id = 0;
- xcb_atom_t connector_id_atom = *connector_id_atom_p;
- if (connector_id_atom == 0) {
- /* Go dig out the CONNECTOR_ID property */
xcb_intern_atom_cookie_t ia_c = xcb_intern_atom(connection,
true,
12,
"CONNECTOR_ID");
xcb_intern_atom_reply_t *ia_r = xcb_intern_atom_reply(connection,
ia_c,
NULL);
if (ia_r) {
*connector_id_atom_p = connector_id_atom = ia_r->atom;
free(ia_r);
}
- }
- /* If there's an CONNECTOR_ID atom in the server, then there may be a CONNECTOR_ID property. Otherwise,
- there will not be and we don't even need to bother.
- */
- if (connector_id_atom) {
xcb_randr_query_version_cookie_t qv_c = xcb_randr_query_version(connection, 1, 6);
xcb_randr_get_output_property_cookie_t gop_c = xcb_randr_get_output_property(connection,
output,
connector_id_atom,
0,
0,
0xffffffffUL,
0,
0);
xcb_randr_query_version_reply_t *qv_r = xcb_randr_query_version_reply(connection, qv_c, NULL);
free(qv_r);
xcb_randr_get_output_property_reply_t *gop_r = xcb_randr_get_output_property_reply(connection,
gop_c,
NULL);
if (gop_r) {
if (gop_r->num_items == 1 && gop_r->format == 32)
memcpy(&connector_id, xcb_randr_get_output_property_data(gop_r), 4);
free(gop_r);
}
- }
- return connector_id;
+}
+static bool +wsi_display_check_randr_version(xcb_connection_t *connection) +{
- xcb_randr_query_version_cookie_t qv_c = xcb_randr_query_version(connection, 1, 6);
- xcb_randr_query_version_reply_t *qv_r = xcb_randr_query_version_reply(connection, qv_c, NULL);
- bool ret = false;
- if (!qv_r)
return false;
- /* Check for version 1.6 or newer */
- ret = qv_r->major_version > 1 || (qv_r->major_version == 1 && qv_r->minor_version >= 6);
- free(qv_r);
- return ret;
+}
+/*
- Given a kernel connector id, find the associated RandR output using the
- CONNECTOR_ID property
- */
+static xcb_randr_output_t +wsi_display_connector_id_to_output(xcb_connection_t *connection,
uint32_t connector_id)
+{
- if (!wsi_display_check_randr_version(connection))
return 0;
- const xcb_setup_t *setup = xcb_get_setup(connection);
- xcb_atom_t connector_id_atom = 0;
- xcb_randr_output_t output = 0;
- /* Search all of the screens for the provided output */
- xcb_screen_iterator_t iter;
- for (iter = xcb_setup_roots_iterator(setup); output == 0 && iter.rem; xcb_screen_next(&iter)) {
xcb_randr_get_screen_resources_cookie_t gsr_c = xcb_randr_get_screen_resources(connection, iter.data->root);
xcb_randr_get_screen_resources_reply_t *gsr_r = xcb_randr_get_screen_resources_reply(connection, gsr_c, NULL);
if (!gsr_r)
return 0;
xcb_randr_output_t *ro = xcb_randr_get_screen_resources_outputs(gsr_r);
int o;
for (o = 0; o < gsr_r->num_outputs; o++) {
if (wsi_display_output_to_connector_id(connection, &connector_id_atom, ro[o]) == connector_id) {
output = ro[o];
break;
}
}
free(gsr_r);
- }
- return output;
+}
+/*
- Given a RandR output, find out which screen it's associated with
- */
+static xcb_window_t +wsi_display_output_to_root(xcb_connection_t *connection,
xcb_randr_output_t output)
+{
- if (!wsi_display_check_randr_version(connection))
return 0;
- const xcb_setup_t *setup = xcb_get_setup(connection);
- xcb_window_t root = 0;
- /* Search all of the screens for the provided output */
- xcb_screen_iterator_t iter;
- for (iter = xcb_setup_roots_iterator(setup); root == 0 && iter.rem; xcb_screen_next(&iter)) {
xcb_randr_get_screen_resources_cookie_t gsr_c = xcb_randr_get_screen_resources(connection, iter.data->root);
xcb_randr_get_screen_resources_reply_t *gsr_r = xcb_randr_get_screen_resources_reply(connection, gsr_c, NULL);
if (!gsr_r)
return 0;
xcb_randr_output_t *ro = xcb_randr_get_screen_resources_outputs(gsr_r);
int o;
for (o = 0; o < gsr_r->num_outputs; o++) {
if (ro[o] == output) {
root = iter.data->root;
break;
}
}
free(gsr_r);
- }
- return root;
+}
+static bool +wsi_display_mode_matches_x(struct wsi_display_mode *wsi,
xcb_randr_mode_info_t *xcb)
+{
- return wsi->clock == (xcb->dot_clock + 500) / 1000 &&
wsi->hdisplay == xcb->width &&
wsi->hsync_start == xcb->hsync_start &&
wsi->hsync_end == xcb->hsync_end &&
wsi->htotal == xcb->htotal &&
wsi->hskew == xcb->hskew &&
wsi->vdisplay == xcb->height &&
wsi->vsync_start == xcb->vsync_start &&
wsi->vsync_end == xcb->vsync_end &&
wsi->vtotal == xcb->vtotal &&
wsi->flags == xcb->mode_flags;
+}
+static struct wsi_display_mode * +wsi_display_find_x_mode(struct wsi_device *wsi_device,
struct wsi_display_connector *connector,
xcb_randr_mode_info_t *mode)
+{
- struct wsi_display *wsi = (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
- struct wsi_display_mode *display_mode;
- LIST_FOR_EACH_ENTRY(display_mode, &wsi->display_modes, list) {
if (display_mode->connector == connector &&
wsi_display_mode_matches_x(display_mode, mode))
return display_mode;
- }
- return NULL;
+}
+static VkResult +wsi_display_register_x_mode(struct wsi_device *wsi_device,
struct wsi_display_connector *connector,
xcb_randr_mode_info_t *x_mode,
bool preferred)
+{
- struct wsi_display *wsi = (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
- struct wsi_display_mode *display_mode;
- display_mode = wsi_display_find_x_mode(wsi_device, connector, x_mode);
- if (display_mode) {
display_mode->valid = true;
return VK_SUCCESS;
- }
- display_mode = vk_alloc(wsi->alloc, sizeof (struct wsi_display_mode), 8, VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
- if (!display_mode)
return VK_ERROR_OUT_OF_HOST_MEMORY;
- display_mode->connector = connector;
- display_mode->valid = true;
- display_mode->preferred = preferred;
- display_mode->clock = (x_mode->dot_clock + 500) / 1000; /* kHz */
- display_mode->hdisplay = x_mode->width;
- display_mode->hsync_start = x_mode->hsync_start;
- display_mode->hsync_end = x_mode->hsync_end;
- display_mode->htotal = x_mode->htotal;
- display_mode->hskew = x_mode->hskew;
- display_mode->vdisplay = x_mode->height;
- display_mode->vsync_start = x_mode->vsync_start;
- display_mode->vsync_end = x_mode->vsync_end;
- display_mode->vtotal = x_mode->vtotal;
- display_mode->vscan = 0;
- if (x_mode->mode_flags & XCB_RANDR_MODE_FLAG_DOUBLE_SCAN)
display_mode->vscan = 1;
- display_mode->flags = x_mode->mode_flags;
- LIST_ADDTAIL(&display_mode->list, &wsi->display_modes);
- return VK_SUCCESS;
+}
+static struct wsi_display_connector * +wsi_display_get_output(struct wsi_device *wsi_device,
xcb_connection_t *connection,
RROutput output)
+{
- struct wsi_display *wsi = (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
- struct wsi_display_connector *connector;
- uint32_t connector_id;
- xcb_window_t root;
- xcb_randr_get_screen_resources_cookie_t src;
- xcb_randr_get_screen_resources_reply_t *srr;
- xcb_randr_get_output_info_cookie_t oic;
- xcb_randr_get_output_info_reply_t *oir;
- xcb_randr_mode_t *x_modes;
- int m;
- root = wsi_display_output_to_root(connection, output);
- if (!root)
return NULL;
- src = xcb_randr_get_screen_resources(connection, root);
- oic = xcb_randr_get_output_info(connection, output, XCB_CURRENT_TIME);
- srr = xcb_randr_get_screen_resources_reply(connection, src, NULL);
- oir = xcb_randr_get_output_info_reply(connection, oic, NULL);
- /* See if we already have a connector for this output */
- connector = wsi_display_find_output(wsi_device, output);
- if (!connector) {
xcb_atom_t connector_id_atom = 0;
/*
* Go get the kernel connector ID for this X output
*/
connector_id = wsi_display_output_to_connector_id(connection, &connector_id_atom, output);
/* Any X server with lease support will have this atom */
if (!connector_id) {
free(oir);
free(srr);
return NULL;
}
if (!connector) {
/* See if we already have a connector for this id */
connector = wsi_display_find_connector(wsi_device, connector_id);
if (connector)
connector->output = output;
}
- }
- if (!connector) {
connector = wsi_display_alloc_connector(wsi, connector_id);
if (!connector) {
free(oir);
free(srr);
return NULL;
}
LIST_ADDTAIL(&connector->list, &wsi->connectors);
connector->output = output;
- }
- if (oir && srr) {
/* Get X modes and add them */
connector->connected = oir->connection != XCB_RANDR_CONNECTION_DISCONNECTED;
wsi_display_invalidate_connector_modes(wsi_device, connector);
x_modes = xcb_randr_get_output_info_modes(oir);
for (m = 0; m < oir->num_modes; m++) {
xcb_randr_mode_info_iterator_t i = xcb_randr_get_screen_resources_modes_iterator(srr);
while (i.rem) {
xcb_randr_mode_info_t *mi = i.data;
if (mi->id == x_modes[m]) {
VkResult result = wsi_display_register_x_mode(wsi_device, connector, mi, m < oir->num_preferred);
if (result != VK_SUCCESS) {
free(oir);
free(srr);
return NULL;
}
break;
}
xcb_randr_mode_info_next(&i);
}
}
- }
- free(oir);
- free(srr);
- return connector;
+}
+static xcb_randr_crtc_t +wsi_display_find_crtc_for_output(xcb_connection_t *connection,
xcb_window_t root,
xcb_randr_output_t output)
+{
- xcb_randr_get_screen_resources_cookie_t gsr_c = xcb_randr_get_screen_resources(connection, root);
- xcb_randr_get_screen_resources_reply_t *gsr_r = xcb_randr_get_screen_resources_reply(connection, gsr_c, NULL);
- if (!gsr_r)
return 0;
- xcb_randr_crtc_t *rc = xcb_randr_get_screen_resources_crtcs(gsr_r);
- xcb_randr_crtc_t idle_crtc = 0;
- xcb_randr_crtc_t active_crtc = 0;
- /* Find either a crtc already connected to the desired output or idle */
- int c;
- for (c = 0; active_crtc == 0 && c < gsr_r->num_crtcs; c++) {
xcb_randr_get_crtc_info_cookie_t gci_c = xcb_randr_get_crtc_info(connection, rc[c], gsr_r->config_timestamp);
xcb_randr_get_crtc_info_reply_t *gci_r = xcb_randr_get_crtc_info_reply(connection, gci_c, NULL);
if (gci_r) {
if (gci_r->mode) {
int num_outputs = xcb_randr_get_crtc_info_outputs_length(gci_r);
xcb_randr_output_t *outputs = xcb_randr_get_crtc_info_outputs(gci_r);
for (int o = 0; o < num_outputs; o++)
if (outputs[o] == output && num_outputs == 1) {
active_crtc = rc[c];
break;
}
} else if (idle_crtc == 0) {
int num_possible = xcb_randr_get_crtc_info_possible_length(gci_r);
xcb_randr_output_t *possible = xcb_randr_get_crtc_info_possible(gci_r);
for (int p = 0; p < num_possible; p++)
if (possible[p] == output) {
idle_crtc = rc[c];
break;
}
}
free(gci_r);
}
- }
- free(gsr_r);
- if (active_crtc)
return active_crtc;
- return idle_crtc;
+}
+VkResult +wsi_acquire_xlib_display(VkPhysicalDevice physical_device,
struct wsi_device *wsi_device,
Display *dpy,
VkDisplayKHR display)
+{
- struct wsi_display *wsi = (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
- xcb_connection_t *connection = XGetXCBConnection(dpy);
- struct wsi_display_connector *connector = wsi_display_connector_from_handle(display);
- xcb_window_t root;
- if (!connector->output) {
connector->output = wsi_display_connector_id_to_output(connection, connector->id);
/* Check and see if we found the output */
if (!connector->output)
return VK_ERROR_OUT_OF_DATE_KHR;
- }
- root = wsi_display_output_to_root(connection, connector->output);
- if (!root)
return VK_ERROR_OUT_OF_DATE_KHR;
- xcb_randr_crtc_t crtc = wsi_display_find_crtc_for_output(connection,
root,
connector->output);
- if (!crtc)
return VK_ERROR_OUT_OF_DATE_KHR;
- xcb_randr_lease_t lease = xcb_generate_id(connection);
- xcb_randr_create_lease_cookie_t cl_c = xcb_randr_create_lease(connection,
root,
lease,
1,
1,
&crtc,
&connector->output);
- xcb_randr_create_lease_reply_t *cl_r = xcb_randr_create_lease_reply(connection, cl_c, NULL);
- if (!cl_r)
return VK_ERROR_OUT_OF_DATE_KHR;
- int fd = -1;
- if (cl_r->nfd > 0) {
int *rcl_f = xcb_randr_create_lease_reply_fds(connection, cl_r);
fd = rcl_f[0];
- }
- free (cl_r);
- if (fd < 0)
return VK_ERROR_OUT_OF_DATE_KHR;
- wsi->master_fd = fd;
- return VK_SUCCESS;
+}
+VkResult +wsi_get_randr_output_display(VkPhysicalDevice physical_device,
struct wsi_device *wsi_device,
Display *dpy,
RROutput output,
VkDisplayKHR *display)
+{
- xcb_connection_t *connection = XGetXCBConnection(dpy);
- struct wsi_display_connector *connector = wsi_display_get_output(wsi_device, connection, output);
- if (connector)
*display = wsi_display_connector_to_handle(connector);
- else
*display = NULL;
- return VK_SUCCESS;
+}
+#endif diff --git a/src/vulkan/wsi/wsi_common_display.h b/src/vulkan/wsi/wsi_common_display.h index 5fbb6925e4a..1997c2a3c40 100644 --- a/src/vulkan/wsi/wsi_common_display.h +++ b/src/vulkan/wsi/wsi_common_display.h @@ -74,4 +74,21 @@ wsi_release_display(VkPhysicalDevice physical_device, struct wsi_device *wsi_device, VkDisplayKHR display);
+#if VK_USE_PLATFORM_XLIB_XRANDR_EXT +VkResult +wsi_acquire_xlib_display(VkPhysicalDevice physical_device,
struct wsi_device *wsi_device,
Display *dpy,
VkDisplayKHR display);
+VkResult +wsi_get_randr_output_display(VkPhysicalDevice physical_device,
struct wsi_device *wsi_device,
Display *dpy,
RROutput output,
VkDisplayKHR *display);
+#endif /* VK_USE_PLATFORM_XLIB_XRANDR_EXT */
#endif
2.15.1
Eric Engestrom eric.engestrom@imgtec.com writes:
Can be simplified a bit:
_xlib_lease = get_option('xlib-lease') if _xlib_lease == 'auto' with_xlib_lease = with_platform_x11 and with_platform_display else with_xlib_lease = _xlib_lease == 'true' endif
(We also usually try to avoid changing the type of a var, and meson might start being more strict with types in future releases)
I wondered about that in the places I copied my code from. Good to know there's a better practice. I've switched to using this form.
+if with_xlib_lease
- vulkan_wsi_deps += dep_xcb_xrandr
- vulkan_wsi_args += [
- '-DVK_USE_PLATFORM_XLIB_XRANDR_EXT',
- ]
vulkan_wsi_args += '-DVK_USE_PLATFORM_XLIB_XRANDR_EXT'
I switched all of the inappropriate usage to this form for six separate patches (three each for core/anv/radv by two extensions (DISPLAY and XLIB_XRANDR).
with that, the meson part of this is Reviewed-by: Eric Engestrom eric.engestrom@imgtec.com
Awesome!
Thanks for reviewing the build system bits; I'm just starting to use meson and every new change is a learning opportunity at this point.
This extension adds the ability to borrow an X RandR output for temporary use directly by a Vulkan application to the anv driver.
Signed-off-by: Keith Packard keithp@keithp.com --- src/intel/Makefile.vulkan.am | 7 +++++++ src/intel/vulkan/anv_extensions.py | 1 + src/intel/vulkan/anv_extensions_gen.py | 10 +++++----- src/intel/vulkan/anv_wsi_display.c | 30 ++++++++++++++++++++++++++++++ src/intel/vulkan/meson.build | 7 +++++++ 5 files changed, 50 insertions(+), 5 deletions(-)
diff --git a/src/intel/Makefile.vulkan.am b/src/intel/Makefile.vulkan.am index 7c428a799d7..0f0d3815097 100644 --- a/src/intel/Makefile.vulkan.am +++ b/src/intel/Makefile.vulkan.am @@ -194,6 +194,13 @@ VULKAN_CPPFLAGS += \ VULKAN_SOURCES += $(VULKAN_WSI_DISPLAY_FILES) endif
+if HAVE_XLIB_LEASE +VULKAN_CPPFLAGS += \ + -DVK_USE_PLATFORM_XLIB_XRANDR_EXT \ + $(XCB_RANDR_CFLAGS) +VULKAN_LIB_DEPS += $(XCB_RANDR_LIBS) +endif + noinst_LTLIBRARIES += vulkan/libvulkan_common.la vulkan_libvulkan_common_la_SOURCES = $(VULKAN_SOURCES) vulkan_libvulkan_common_la_CFLAGS = $(VULKAN_CFLAGS) diff --git a/src/intel/vulkan/anv_extensions.py b/src/intel/vulkan/anv_extensions.py index 75b6bf0b41f..47993f45d41 100644 --- a/src/intel/vulkan/anv_extensions.py +++ b/src/intel/vulkan/anv_extensions.py @@ -85,6 +85,7 @@ EXTENSIONS = [ Extension('VK_KHR_xlib_surface', 6, 'VK_USE_PLATFORM_XLIB_KHR'), Extension('VK_KHR_display', 23, 'VK_USE_PLATFORM_DISPLAY_KHR'), 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_KHX_multiview', 1, True), Extension('VK_EXT_debug_report', 8, True), Extension('VK_EXT_external_memory_dma_buf', 1, True), diff --git a/src/intel/vulkan/anv_extensions_gen.py b/src/intel/vulkan/anv_extensions_gen.py index 84d07f9767a..025907aff72 100644 --- a/src/intel/vulkan/anv_extensions_gen.py +++ b/src/intel/vulkan/anv_extensions_gen.py @@ -113,12 +113,12 @@ _TEMPLATE_C = Template(COPYRIGHT + """ #include "vk_util.h"
/* Convert the VK_USE_PLATFORM_* defines to booleans */ -%for platform in ['ANDROID', 'WAYLAND', 'XCB', 'XLIB', 'DISPLAY']: -#ifdef VK_USE_PLATFORM_${platform}_KHR -# undef VK_USE_PLATFORM_${platform}_KHR -# define VK_USE_PLATFORM_${platform}_KHR true +%for platform in ['ANDROID_KHR', 'WAYLAND_KHR', 'XCB_KHR', 'XLIB_KHR', 'DISPLAY_KHR', 'XLIB_XRANDR_EXT']: +#ifdef VK_USE_PLATFORM_${platform} +# undef VK_USE_PLATFORM_${platform} +# define VK_USE_PLATFORM_${platform} true #else -# define VK_USE_PLATFORM_${platform}_KHR false +# define VK_USE_PLATFORM_${platform} false #endif %endfor
diff --git a/src/intel/vulkan/anv_wsi_display.c b/src/intel/vulkan/anv_wsi_display.c index e6f67f7dec9..e87aed49f7d 100644 --- a/src/intel/vulkan/anv_wsi_display.c +++ b/src/intel/vulkan/anv_wsi_display.c @@ -138,3 +138,33 @@ anv_ReleaseDisplayEXT(VkPhysicalDevice physical_device, &pdevice->wsi_device, display); } + +#ifdef VK_USE_PLATFORM_XLIB_XRANDR_EXT +VkResult +anv_AcquireXlibDisplayEXT(VkPhysicalDevice physical_device, + Display *dpy, + VkDisplayKHR display) +{ + ANV_FROM_HANDLE(anv_physical_device, pdevice, physical_device); + + return wsi_acquire_xlib_display(physical_device, + &pdevice->wsi_device, + dpy, + display); +} + +VkResult +anv_GetRandROutputDisplayEXT(VkPhysicalDevice physical_device, + Display *dpy, + RROutput output, + VkDisplayKHR *display) +{ + ANV_FROM_HANDLE(anv_physical_device, pdevice, physical_device); + + return wsi_get_randr_output_display(physical_device, + &pdevice->wsi_device, + dpy, + output, + display); +} +#endif /* VK_USE_PLATFORM_XLIB_XRANDR_EXT */ diff --git a/src/intel/vulkan/meson.build b/src/intel/vulkan/meson.build index 2e2ab8f7ecd..d3ab5ac8a64 100644 --- a/src/intel/vulkan/meson.build +++ b/src/intel/vulkan/meson.build @@ -178,6 +178,13 @@ if with_platform_display libanv_files += files('anv_wsi_display.c') endif
+if with_xlib_lease + anv_deps += dep_xcb_xrandr + anv_flags += [ + '-DVK_USE_PLATFORM_XLIB_XRANDR_EXT', + ] +endif + libanv_common = static_library( 'anv_common', [libanv_files, anv_entrypoints, anv_extensions_c, anv_extensions_h],
This extension adds the ability to borrow an X RandR output for temporary use directly by a Vulkan application to the radv driver.
Signed-off-by: Keith Packard keithp@keithp.com --- src/amd/vulkan/Makefile.am | 7 +++++++ src/amd/vulkan/meson.build | 7 +++++++ src/amd/vulkan/radv_extensions.py | 11 ++++++----- src/amd/vulkan/radv_wsi_display.c | 30 ++++++++++++++++++++++++++++++ 4 files changed, 50 insertions(+), 5 deletions(-)
diff --git a/src/amd/vulkan/Makefile.am b/src/amd/vulkan/Makefile.am index 061b8144b88..94ece06e99e 100644 --- a/src/amd/vulkan/Makefile.am +++ b/src/amd/vulkan/Makefile.am @@ -81,7 +81,14 @@ AM_CPPFLAGS += \ -DVK_USE_PLATFORM_DISPLAY_KHR
VULKAN_SOURCES += $(VULKAN_WSI_DISPLAY_FILES) +endif + +if HAVE_XLIB_LEASE +AM_CPPFLAGS += \ + -DVK_USE_PLATFORM_XLIB_XRANDR_EXT \ + $(XCB_RANDR_CFLAGS)
+VULKAN_LIB_DEPS += $(XCB_RANDR_LIBS) endif
if HAVE_PLATFORM_X11 diff --git a/src/amd/vulkan/meson.build b/src/amd/vulkan/meson.build index b7bb1075e7d..0b92a1763a1 100644 --- a/src/amd/vulkan/meson.build +++ b/src/amd/vulkan/meson.build @@ -119,6 +119,13 @@ if with_platform_display libradv_files += files('radv_wsi_display.c') endif
+if with_xlib_lease + radv_deps += dep_xcb_xrandr + radv_flags += [ + '-DVK_USE_PLATFORM_XLIB_XRANDR_EXT', + ] +endif + libvulkan_radeon = shared_library( 'vulkan_radeon', [libradv_files, radv_entrypoints, radv_extensions_c, vk_format_table_c], diff --git a/src/amd/vulkan/radv_extensions.py b/src/amd/vulkan/radv_extensions.py index b04b9bf87b0..df3325d47d4 100644 --- a/src/amd/vulkan/radv_extensions.py +++ b/src/amd/vulkan/radv_extensions.py @@ -83,6 +83,7 @@ EXTENSIONS = [ Extension('VK_KHR_xlib_surface', 6, 'VK_USE_PLATFORM_XLIB_KHR'), Extension('VK_KHR_display', 23, 'VK_USE_PLATFORM_DISPLAY_KHR'), 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_KHX_multiview', 1, '!ANDROID'), Extension('VK_EXT_debug_report', 9, True), Extension('VK_EXT_discard_rectangles', 1, True), @@ -170,12 +171,12 @@ _TEMPLATE = Template(COPYRIGHT + """ #include "vk_util.h"
/* Convert the VK_USE_PLATFORM_* defines to booleans */ -%for platform in ['ANDROID', 'WAYLAND', 'XCB', 'XLIB', 'DISPLAY']: -#ifdef VK_USE_PLATFORM_${platform}_KHR -# undef VK_USE_PLATFORM_${platform}_KHR -# define VK_USE_PLATFORM_${platform}_KHR true +%for platform in ['ANDROID_KHR', 'WAYLAND_KHR', 'XCB_KHR', 'XLIB_KHR', 'DISPLAY_KHR', 'XLIB_XRANDR_EXT']: +#ifdef VK_USE_PLATFORM_${platform} +# undef VK_USE_PLATFORM_${platform} +# define VK_USE_PLATFORM_${platform} true #else -# define VK_USE_PLATFORM_${platform}_KHR false +# define VK_USE_PLATFORM_${platform} false #endif %endfor
diff --git a/src/amd/vulkan/radv_wsi_display.c b/src/amd/vulkan/radv_wsi_display.c index deaf61ce0df..d7a5956ad97 100644 --- a/src/amd/vulkan/radv_wsi_display.c +++ b/src/amd/vulkan/radv_wsi_display.c @@ -152,3 +152,33 @@ radv_ReleaseDisplayEXT(VkPhysicalDevice physical_device, &pdevice->wsi_device, display); } + +#ifdef VK_USE_PLATFORM_XLIB_XRANDR_EXT +VkResult +radv_AcquireXlibDisplayEXT(VkPhysicalDevice physical_device, + Display *dpy, + VkDisplayKHR display) +{ + RADV_FROM_HANDLE(radv_physical_device, pdevice, physical_device); + + return wsi_acquire_xlib_display(physical_device, + &pdevice->wsi_device, + dpy, + display); +} + +VkResult +radv_GetRandROutputDisplayEXT(VkPhysicalDevice physical_device, + Display *dpy, + RROutput output, + VkDisplayKHR *display) +{ + RADV_FROM_HANDLE(radv_physical_device, pdevice, physical_device); + + return wsi_get_randr_output_display(physical_device, + &pdevice->wsi_device, + dpy, + output, + display); +} +#endif /* VK_USE_PLATFORM_XLIB_XRANDR_EXT */
This extension is required to support EXT_display_control as it offers a way to query whether the vblank counter is supported.
v2: Thanks to kisak
Fix spelling of VkSurfaceCapabilities2EXT in wsi_common_wayland.c, it was using ext instead of EXT.
Fix spelling of VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_EXT
v3: Fix wayland WSI, regularize spelling
Misspelled 'surface' in get_capabilities2ext Remove extra _ from get_capabilities_2ext in a couple of places
Signed-off-by: Keith Packard keithp@keithp.com --- src/vulkan/wsi/wsi_common.c | 11 +++++++++++ src/vulkan/wsi/wsi_common.h | 5 +++++ src/vulkan/wsi/wsi_common_display.c | 27 +++++++++++++++++++++++++++ src/vulkan/wsi/wsi_common_private.h | 2 ++ src/vulkan/wsi/wsi_common_wayland.c | 27 +++++++++++++++++++++++++++ src/vulkan/wsi/wsi_common_x11.c | 27 +++++++++++++++++++++++++++ 6 files changed, 99 insertions(+)
diff --git a/src/vulkan/wsi/wsi_common.c b/src/vulkan/wsi/wsi_common.c index c0a285e5814..02abc9ef7fb 100644 --- a/src/vulkan/wsi/wsi_common.c +++ b/src/vulkan/wsi/wsi_common.c @@ -579,6 +579,17 @@ wsi_common_get_surface_capabilities2(struct wsi_device *wsi_device, pSurfaceCapabilities); }
+VkResult +wsi_common_get_surface_capabilities2ext(struct wsi_device *wsi_device, + VkSurfaceKHR _surface, + VkSurfaceCapabilities2EXT *pSurfaceCapabilities) +{ + ICD_FROM_HANDLE(VkIcdSurfaceBase, surface, _surface); + struct wsi_interface *iface = wsi_device->wsi[surface->platform]; + + return iface->get_capabilities2ext(surface, pSurfaceCapabilities); +} + VkResult wsi_common_get_surface_formats(struct wsi_device *wsi_device, VkSurfaceKHR _surface, diff --git a/src/vulkan/wsi/wsi_common.h b/src/vulkan/wsi/wsi_common.h index 1cb6aaebca0..124d096170a 100644 --- a/src/vulkan/wsi/wsi_common.h +++ b/src/vulkan/wsi/wsi_common.h @@ -155,6 +155,11 @@ wsi_common_get_surface_present_modes(struct wsi_device *wsi_device, uint32_t *pPresentModeCount, VkPresentModeKHR *pPresentModes);
+VkResult +wsi_common_get_surface_capabilities2ext(struct wsi_device *wsi_device, + VkSurfaceKHR surface, + VkSurfaceCapabilities2EXT *pSurfaceCapabilities); + VkResult wsi_common_get_images(VkSwapchainKHR _swapchain, uint32_t *pSwapchainImageCount, diff --git a/src/vulkan/wsi/wsi_common_display.c b/src/vulkan/wsi/wsi_common_display.c index 29d64b21aff..e63700e2e65 100644 --- a/src/vulkan/wsi/wsi_common_display.c +++ b/src/vulkan/wsi/wsi_common_display.c @@ -655,6 +655,32 @@ wsi_display_surface_get_capabilities2(VkIcdSurfaceBase *icd_surface, return wsi_display_surface_get_capabilities(icd_surface, &caps->surfaceCapabilities); }
+static VkResult +wsi_display_surface_get_capabilities2ext(VkIcdSurfaceBase *icd_surface, + VkSurfaceCapabilities2EXT *caps) +{ + VkSurfaceCapabilitiesKHR khr_caps; + VkResult ret; + + assert(caps->sType == VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_EXT); + ret = wsi_display_surface_get_capabilities(icd_surface, &khr_caps); + if (ret) + return ret; + + caps->minImageCount = khr_caps.minImageCount; + caps->maxImageCount = khr_caps.maxImageCount; + caps->currentExtent = khr_caps.currentExtent; + caps->minImageExtent = khr_caps.minImageExtent; + caps->maxImageExtent = khr_caps.maxImageExtent; + caps->maxImageArrayLayers = khr_caps.maxImageArrayLayers; + caps->supportedTransforms = khr_caps.supportedTransforms; + caps->currentTransform = khr_caps.currentTransform; + caps->supportedCompositeAlpha = khr_caps.supportedCompositeAlpha; + caps->supportedUsageFlags = khr_caps.supportedUsageFlags; + caps->supportedSurfaceCounters = 0; + return ret; +} + static const VkFormat available_surface_formats[] = { VK_FORMAT_B8G8R8A8_SRGB, VK_FORMAT_B8G8R8A8_UNORM, @@ -1323,6 +1349,7 @@ wsi_display_init_wsi(struct wsi_device *wsi_device, wsi->base.get_support = wsi_display_surface_get_support; wsi->base.get_capabilities = wsi_display_surface_get_capabilities; wsi->base.get_capabilities2 = wsi_display_surface_get_capabilities2; + wsi->base.get_capabilities2ext = wsi_display_surface_get_capabilities2ext; wsi->base.get_formats = wsi_display_surface_get_formats; wsi->base.get_formats2 = wsi_display_surface_get_formats2; wsi->base.get_present_modes = wsi_display_surface_get_present_modes; diff --git a/src/vulkan/wsi/wsi_common_private.h b/src/vulkan/wsi/wsi_common_private.h index d38d2efa116..0d902846238 100644 --- a/src/vulkan/wsi/wsi_common_private.h +++ b/src/vulkan/wsi/wsi_common_private.h @@ -103,6 +103,8 @@ struct wsi_interface { VkResult (*get_capabilities2)(VkIcdSurfaceBase *surface, const void *info_next, VkSurfaceCapabilities2KHR* pSurfaceCapabilities); + VkResult (*get_capabilities2ext)(VkIcdSurfaceBase *surface, + VkSurfaceCapabilities2EXT* pSurfaceCapabilities); VkResult (*get_formats)(VkIcdSurfaceBase *surface, struct wsi_device *wsi_device, uint32_t* pSurfaceFormatCount, diff --git a/src/vulkan/wsi/wsi_common_wayland.c b/src/vulkan/wsi/wsi_common_wayland.c index be7635bbf84..54b9449ab8c 100644 --- a/src/vulkan/wsi/wsi_common_wayland.c +++ b/src/vulkan/wsi/wsi_common_wayland.c @@ -457,6 +457,32 @@ wsi_wl_surface_get_capabilities2(VkIcdSurfaceBase *surface, return wsi_wl_surface_get_capabilities(surface, &caps->surfaceCapabilities); }
+static VkResult +wsi_wl_surface_get_capabilities2ext(VkIcdSurfaceBase *surface, + VkSurfaceCapabilities2EXT* caps) +{ + VkSurfaceCapabilitiesKHR khr_caps; + VkResult ret; + + assert(caps->sType == VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_EXT); + ret = wsi_wl_surface_get_capabilities(surface, &khr_caps); + if (ret) + return ret; + + caps->minImageCount = khr_caps.minImageCount; + caps->maxImageCount = khr_caps.maxImageCount; + caps->currentExtent = khr_caps.currentExtent; + caps->minImageExtent = khr_caps.minImageExtent; + caps->maxImageExtent = khr_caps.maxImageExtent; + caps->maxImageArrayLayers = khr_caps.maxImageArrayLayers; + caps->supportedTransforms = khr_caps.supportedTransforms; + caps->currentTransform = khr_caps.currentTransform; + caps->supportedCompositeAlpha = khr_caps.supportedCompositeAlpha; + caps->supportedUsageFlags = khr_caps.supportedUsageFlags; + caps->supportedSurfaceCounters = 0; + return ret; +} + static VkResult wsi_wl_surface_get_formats(VkIcdSurfaceBase *icd_surface, struct wsi_device *wsi_device, @@ -891,6 +917,7 @@ wsi_wl_init_wsi(struct wsi_device *wsi_device, wsi->base.get_support = wsi_wl_surface_get_support; wsi->base.get_capabilities = wsi_wl_surface_get_capabilities; wsi->base.get_capabilities2 = wsi_wl_surface_get_capabilities2; + wsi->base.get_capabilities2ext = wsi_wl_surface_get_capabilities2ext; wsi->base.get_formats = wsi_wl_surface_get_formats; wsi->base.get_formats2 = wsi_wl_surface_get_formats2; wsi->base.get_present_modes = wsi_wl_surface_get_present_modes; diff --git a/src/vulkan/wsi/wsi_common_x11.c b/src/vulkan/wsi/wsi_common_x11.c index c29e0a2d30d..714523678d4 100644 --- a/src/vulkan/wsi/wsi_common_x11.c +++ b/src/vulkan/wsi/wsi_common_x11.c @@ -516,6 +516,32 @@ x11_surface_get_capabilities2(VkIcdSurfaceBase *icd_surface, return x11_surface_get_capabilities(icd_surface, &caps->surfaceCapabilities); }
+static VkResult +x11_surface_get_capabilities2ext(VkIcdSurfaceBase *icd_surface, + VkSurfaceCapabilities2EXT *caps) +{ + VkSurfaceCapabilitiesKHR khr_caps; + VkResult ret; + + assert(caps->sType == VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_EXT); + ret = x11_surface_get_capabilities(icd_surface, &khr_caps); + if (ret) + return ret; + + caps->minImageCount = khr_caps.minImageCount; + caps->maxImageCount = khr_caps.maxImageCount; + caps->currentExtent = khr_caps.currentExtent; + caps->minImageExtent = khr_caps.minImageExtent; + caps->maxImageExtent = khr_caps.maxImageExtent; + caps->maxImageArrayLayers = khr_caps.maxImageArrayLayers; + caps->supportedTransforms = khr_caps.supportedTransforms; + caps->currentTransform = khr_caps.currentTransform; + caps->supportedCompositeAlpha = khr_caps.supportedCompositeAlpha; + caps->supportedUsageFlags = khr_caps.supportedUsageFlags; + caps->supportedSurfaceCounters = 0; + return ret; +} + static VkResult x11_surface_get_formats(VkIcdSurfaceBase *surface, struct wsi_device *wsi_device, @@ -1207,6 +1233,7 @@ wsi_x11_init_wsi(struct wsi_device *wsi_device, wsi->base.get_support = x11_surface_get_support; wsi->base.get_capabilities = x11_surface_get_capabilities; wsi->base.get_capabilities2 = x11_surface_get_capabilities2; + wsi->base.get_capabilities2ext = x11_surface_get_capabilities2ext; wsi->base.get_formats = x11_surface_get_formats; wsi->base.get_formats2 = x11_surface_get_formats2; wsi->base.get_present_modes = x11_surface_get_present_modes;
This extension is required to support EXT_display_control as it offers a way to query whether the vblank counter is supported.
v4: Add anv support
Signed-off-by: Keith Packard keithp@keithp.com --- src/intel/vulkan/anv_extensions.py | 1 + src/intel/vulkan/anv_wsi.c | 12 ++++++++++++ 2 files changed, 13 insertions(+)
diff --git a/src/intel/vulkan/anv_extensions.py b/src/intel/vulkan/anv_extensions.py index 47993f45d41..32643e31045 100644 --- a/src/intel/vulkan/anv_extensions.py +++ b/src/intel/vulkan/anv_extensions.py @@ -89,6 +89,7 @@ EXTENSIONS = [ Extension('VK_KHX_multiview', 1, True), 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'), ]
class VkVersion: diff --git a/src/intel/vulkan/anv_wsi.c b/src/intel/vulkan/anv_wsi.c index f86d83589ea..52362adfb71 100644 --- a/src/intel/vulkan/anv_wsi.c +++ b/src/intel/vulkan/anv_wsi.c @@ -104,6 +104,18 @@ VkResult anv_GetPhysicalDeviceSurfaceCapabilities2KHR( pSurfaceCapabilities); }
+VkResult anv_GetPhysicalDeviceSurfaceCapabilities2EXT( + VkPhysicalDevice physicalDevice, + VkSurfaceKHR surface, + VkSurfaceCapabilities2EXT* pSurfaceCapabilities) +{ + ANV_FROM_HANDLE(anv_physical_device, device, physicalDevice); + + return wsi_common_get_surface_capabilities2ext(&device->wsi_device, + surface, + pSurfaceCapabilities); +} + VkResult anv_GetPhysicalDeviceSurfaceFormatsKHR( VkPhysicalDevice physicalDevice, VkSurfaceKHR surface,
This extension is required to support EXT_display_control as it offers a way to query whether the vblank counter is supported.
Signed-off-by: Keith Packard keithp@keithp.com --- src/amd/vulkan/radv_extensions.py | 1 + src/amd/vulkan/radv_wsi.c | 12 ++++++++++++ 2 files changed, 13 insertions(+)
diff --git a/src/amd/vulkan/radv_extensions.py b/src/amd/vulkan/radv_extensions.py index df3325d47d4..048192b9e05 100644 --- a/src/amd/vulkan/radv_extensions.py +++ b/src/amd/vulkan/radv_extensions.py @@ -84,6 +84,7 @@ EXTENSIONS = [ Extension('VK_KHR_display', 23, 'VK_USE_PLATFORM_DISPLAY_KHR'), 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_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_wsi.c b/src/amd/vulkan/radv_wsi.c index 9bdd55ef11c..2433390562b 100644 --- a/src/amd/vulkan/radv_wsi.c +++ b/src/amd/vulkan/radv_wsi.c @@ -103,6 +103,18 @@ VkResult radv_GetPhysicalDeviceSurfaceCapabilities2KHR( pSurfaceCapabilities); }
+VkResult radv_GetPhysicalDeviceSurfaceCapabilities2EXT( + VkPhysicalDevice physicalDevice, + VkSurfaceKHR surface, + VkSurfaceCapabilities2EXT* pSurfaceCapabilities) +{ + RADV_FROM_HANDLE(radv_physical_device, device, physicalDevice); + + return wsi_common_get_surface_capabilities2ext(&device->wsi_device, + surface, + pSurfaceCapabilities); +} + VkResult radv_GetPhysicalDeviceSurfaceFormatsKHR( VkPhysicalDevice physicalDevice, VkSurfaceKHR surface,
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
Signed-off-by: Keith Packard keithp@keithp.com --- src/vulkan/wsi/wsi_common.h | 9 ++ src/vulkan/wsi/wsi_common_display.c | 286 +++++++++++++++++++++++++++++++++++- src/vulkan/wsi/wsi_common_display.h | 29 ++++ 3 files changed, 323 insertions(+), 1 deletion(-)
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
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@keithp.com --- 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 +++++++++++++++++++++++++++++++++++++ 4 files changed, 160 insertions(+), 7 deletions(-)
diff --git a/src/intel/vulkan/anv_extensions.py b/src/intel/vulkan/anv_extensions.py index 32643e31045..1c6c4f8e27b 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); +}
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@keithp.com --- src/amd/vulkan/radv_extensions.py | 1 + src/amd/vulkan/radv_private.h | 11 +++- src/amd/vulkan/radv_wsi_display.c | 109 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 120 insertions(+), 1 deletion(-)
diff --git a/src/amd/vulkan/radv_extensions.py b/src/amd/vulkan/radv_extensions.py index 048192b9e05..42b8d996b6a 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..fc6a5781847 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 d7a5956ad97..92d8936bba0 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); +} +
This extension adds a single function to query the current GPU timestamp, just like glGetInteger64v(GL_TIMESTAMP, ×tamp). This function is needed to complete the implementation of GOOGLE_display_timing, which needs to be able to coorelate GPU and CPU timestamps.
Signed-off-by: Keith Packard keithp@keithp.com --- include/vulkan/vulkan.h | 6 ++++++ src/Makefile.am | 1 + src/vulkan/meson.build | 1 + src/vulkan/registry/vk_mesa_query_timestamp.xml | 22 ++++++++++++++++++++++ 4 files changed, 30 insertions(+) create mode 100644 src/vulkan/registry/vk_mesa_query_timestamp.xml
diff --git a/include/vulkan/vulkan.h b/include/vulkan/vulkan.h index d3e2e246cf3..5523eb7586f 100644 --- a/include/vulkan/vulkan.h +++ b/include/vulkan/vulkan.h @@ -7025,6 +7025,12 @@ VKAPI_ATTR VkResult VKAPI_CALL vkGetMemoryHostPointerPropertiesEXT( VkMemoryHostPointerPropertiesEXT* pMemoryHostPointerProperties); #endif
+typedef VkResult (VKAPI_PTR *PFN_vkQueryCurrentTimestampMESA)(VkDevice device, uint64_t *timestamp); + +VKAPI_ATTR VkResult VKAPI_CALL vkQueryCurrentTimestampMESA( + VkDevice _device, + uint64_t *timestamp); + #ifdef __cplusplus } #endif diff --git a/src/Makefile.am b/src/Makefile.am index 014ffaf3e29..74ff305d7c6 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -68,6 +68,7 @@ endif
EXTRA_DIST += vulkan/registry/vk.xml EXTRA_DIST += vulkan/registry/vk_android_native_buffer.xml +EXTRA_DIST += vulkan/registry/vk_mesa_query_timestamp.xml
if HAVE_AMD_DRIVERS SUBDIRS += amd diff --git a/src/vulkan/meson.build b/src/vulkan/meson.build index 3908005b8a0..6ab0966b7c5 100644 --- a/src/vulkan/meson.build +++ b/src/vulkan/meson.build @@ -20,6 +20,7 @@
vk_api_xml = files('registry/vk.xml') vk_android_native_buffer_xml = files('registry/vk_android_native_buffer.xml') +vk_mesa_query_timestamp_xml = files('registry/vk_mesa_query_timestamp.xml')
inc_vulkan_util = include_directories('util') inc_vulkan_wsi = include_directories('wsi') diff --git a/src/vulkan/registry/vk_mesa_query_timestamp.xml b/src/vulkan/registry/vk_mesa_query_timestamp.xml new file mode 100644 index 00000000000..7fd4d974872 --- /dev/null +++ b/src/vulkan/registry/vk_mesa_query_timestamp.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<registry> + <commands> + <command> + <proto><type>VkResult</type> <name>vkQueryCurrentTimestampMESA</name></proto> + <param><type>VkDevice</type> <name>device</name></param> + <param><type>uint64_t</type>* <name>pTimestamp</name></param> + </command> + </commands> + <extensions> + <extension name="VK_MESA_query_timestamp" number="200" + type="device" author="MESA" + contact="Keith Packard @keithp" + supported="vulkan"> + <require> + <enum value="1" name="VK_MESA_QUERY_TIMESTAMP_SPEC_VERSION"/> + <enum value=""VK_MESA_query_timestamp"" name="VK_MESA_QUERY_TIMESTAMP_NAME"/> + <command name="vkQueryCurrentTimestampMESA"/> + </require> + </extension> + </extensions> +</registry>
This extension adds a single function to query the current GPU timestamp, just like glGetInteger64v(GL_TIMESTAMP, ×tamp). This function is needed to complete the implementation of GOOGLE_display_timing, which needs to be able to coorelate GPU and CPU timestamps.
Signed-off-by: Keith Packard keithp@keithp.com --- src/intel/Makefile.vulkan.am | 7 +++++++ src/intel/vulkan/anv_extensions.py | 1 + src/intel/vulkan/anv_gem.c | 13 +++++++++++++ src/intel/vulkan/anv_private.h | 1 + src/intel/vulkan/genX_query.c | 15 +++++++++++++++ src/intel/vulkan/meson.build | 12 ++++++------ 6 files changed, 43 insertions(+), 6 deletions(-)
diff --git a/src/intel/Makefile.vulkan.am b/src/intel/Makefile.vulkan.am index 0f0d3815097..5d4298c040a 100644 --- a/src/intel/Makefile.vulkan.am +++ b/src/intel/Makefile.vulkan.am @@ -24,36 +24,43 @@ # out and we'll fail at `make dist' vulkan_api_xml = $(top_srcdir)/src/vulkan/registry/vk.xml vk_android_native_buffer_xml = $(top_srcdir)/src/vulkan/registry/vk_android_native_buffer.xml +vk_mesa_query_timestamp_xml = $(top_srcdir)/src/vulkan/registry/vk_mesa_query_timestamp.xml
vulkan/anv_entrypoints.c: vulkan/anv_entrypoints_gen.py \ vulkan/anv_extensions.py \ $(vulkan_api_xml) \ + $(vk_mesa_query_timestamp_xml) \ $(vk_android_native_buffer_xml) $(MKDIR_GEN) $(AM_V_GEN)$(PYTHON2) $(srcdir)/vulkan/anv_entrypoints_gen.py \ --xml $(vulkan_api_xml) \ --xml $(vk_android_native_buffer_xml) \ + --xml $(vk_mesa_query_timestamp_xml) \ --outdir $(builddir)/vulkan vulkan/anv_entrypoints.h: vulkan/anv_entrypoints.c
vulkan/anv_extensions.c: vulkan/anv_extensions_gen.py \ vulkan/anv_extensions.py \ $(vulkan_api_xml) \ + $(vk_mesa_query_timestamp_xml) \ $(vk_android_native_buffer_xml) $(MKDIR_GEN) $(AM_V_GEN)$(PYTHON2) $(srcdir)/vulkan/anv_extensions_gen.py \ --xml $(vulkan_api_xml) \ --xml $(vk_android_native_buffer_xml) \ + --xml $(vk_mesa_query_timestamp_xml) \ --out-c $@
vulkan/anv_extensions.h: vulkan/anv_extensions_gen.py \ vulkan/anv_extensions.py \ $(vulkan_api_xml) \ + $(vk_mesa_query_timestamp_xml) \ $(vk_android_native_buffer_xml) $(MKDIR_GEN) $(AM_V_GEN)$(PYTHON2) $(srcdir)/vulkan/anv_extensions_gen.py \ --xml $(vulkan_api_xml) \ --xml $(vk_android_native_buffer_xml) \ + --xml $(vk_mesa_query_timestamp_xml) \ --out-h $@
BUILT_SOURCES += $(VULKAN_GENERATED_FILES) diff --git a/src/intel/vulkan/anv_extensions.py b/src/intel/vulkan/anv_extensions.py index 1c6c4f8e27b..7706ccdb5bb 100644 --- a/src/intel/vulkan/anv_extensions.py +++ b/src/intel/vulkan/anv_extensions.py @@ -91,6 +91,7 @@ EXTENSIONS = [ 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'), + Extension('VK_MESA_query_timestamp', 1, True), ]
class VkVersion: diff --git a/src/intel/vulkan/anv_gem.c b/src/intel/vulkan/anv_gem.c index 34c09891086..588b323d113 100644 --- a/src/intel/vulkan/anv_gem.c +++ b/src/intel/vulkan/anv_gem.c @@ -418,6 +418,19 @@ anv_gem_fd_to_handle(struct anv_device *device, int fd) return args.handle; }
+int +anv_gem_reg_read(struct anv_device *device, uint32_t offset, uint64_t *result) +{ + struct drm_i915_reg_read args = { + .offset = offset + }; + + int ret = anv_ioctl(device->fd, DRM_IOCTL_I915_REG_READ, &args); + + *result = args.val; + return ret; +} + #ifndef SYNC_IOC_MAGIC /* duplicated from linux/sync_file.h to avoid build-time dependency * on new (v4.7) kernel headers. Once distro's are mostly using diff --git a/src/intel/vulkan/anv_private.h b/src/intel/vulkan/anv_private.h index 1d3e5fcd921..337d8ca3f15 100644 --- a/src/intel/vulkan/anv_private.h +++ b/src/intel/vulkan/anv_private.h @@ -932,6 +932,7 @@ bool anv_gem_supports_48b_addresses(int fd); int anv_gem_gpu_get_reset_stats(struct anv_device *device, uint32_t *active, uint32_t *pending); int anv_gem_handle_to_fd(struct anv_device *device, uint32_t gem_handle); +int anv_gem_reg_read(struct anv_device *device, uint32_t offset, uint64_t *result); uint32_t anv_gem_fd_to_handle(struct anv_device *device, int fd); int anv_gem_set_caching(struct anv_device *device, uint32_t gem_handle, uint32_t caching); int anv_gem_set_domain(struct anv_device *device, uint32_t gem_handle, diff --git a/src/intel/vulkan/genX_query.c b/src/intel/vulkan/genX_query.c index 4efcc57e475..d2de7e5f45e 100644 --- a/src/intel/vulkan/genX_query.c +++ b/src/intel/vulkan/genX_query.c @@ -546,6 +546,21 @@ void genX(CmdWriteTimestamp)( } }
+VkResult genX(QueryCurrentTimestampMESA)( + VkDevice _device, + uint64_t *timestamp) +{ + ANV_FROM_HANDLE(anv_device, device, _device); + int ret; + + /* XXX older kernels don't support this interface. */ + ret = anv_gem_reg_read(device, TIMESTAMP | 1, timestamp); + + if (ret != 0) + return VK_ERROR_DEVICE_LOST; + return VK_SUCCESS; +} + #if GEN_GEN > 7 || GEN_IS_HASWELL
static uint32_t diff --git a/src/intel/vulkan/meson.build b/src/intel/vulkan/meson.build index d3ab5ac8a64..d4622ffdfdf 100644 --- a/src/intel/vulkan/meson.build +++ b/src/intel/vulkan/meson.build @@ -20,11 +20,11 @@
anv_entrypoints = custom_target( 'anv_entrypoints.[ch]', - input : ['anv_entrypoints_gen.py', vk_api_xml, vk_android_native_buffer_xml, + input : ['anv_entrypoints_gen.py', vk_api_xml, vk_android_native_buffer_xml, vk_mesa_query_timestamp_xml, 'anv_extensions.py'], output : ['anv_entrypoints.h', 'anv_entrypoints.c'], command : [ - prog_python2, '@INPUT0@', '--xml', '@INPUT1@', '--xml', '@INPUT2@', + prog_python2, '@INPUT0@', '--xml', '@INPUT1@', '--xml', '@INPUT2@', '--xml', '@INPUT3@', '--outdir', meson.current_build_dir(), ], depend_files : files('anv_extensions.py'), @@ -32,22 +32,22 @@ anv_entrypoints = custom_target(
anv_extensions_c = custom_target( 'anv_extensions.c', - input : ['anv_extensions_gen.py', vk_api_xml, vk_android_native_buffer_xml, + input : ['anv_extensions_gen.py', vk_api_xml, vk_android_native_buffer_xml, vk_mesa_query_timestamp_xml, 'anv_extensions.py'], output : 'anv_extensions.c', command : [ - prog_python2, '@INPUT0@', '--xml', '@INPUT1@', '--xml', '@INPUT2@', + prog_python2, '@INPUT0@', '--xml', '@INPUT1@', '--xml', '@INPUT2@', '--xml', '@INPUT3@', '--out-c', '@OUTPUT@', ], )
anv_extensions_h = custom_target( 'anv_extensions.h', - input : ['anv_extensions_gen.py', vk_api_xml, vk_android_native_buffer_xml, + input : ['anv_extensions_gen.py', vk_api_xml, vk_android_native_buffer_xml, vk_mesa_query_timestamp_xml, 'anv_extensions.py'], output : 'anv_extensions.h', command : [ - prog_python2, '@INPUT0@', '--xml', '@INPUT1@', '--xml', '@INPUT2@', + prog_python2, '@INPUT0@', '--xml', '@INPUT1@', '--xml', '@INPUT2@', '--xml', '@INPUT3@', '--out-h', '@OUTPUT@', ], )
This extension adds a single function to query the current GPU timestamp, just like glGetInteger64v(GL_TIMESTAMP, ×tamp). This function is needed to complete the implementation of GOOGLE_display_timing, which needs to be able to coorelate GPU and CPU timestamps.
Signed-off-by: Keith Packard keithp@keithp.com --- src/amd/vulkan/Makefile.am | 3 +++ src/amd/vulkan/meson.build | 8 ++++---- src/amd/vulkan/radv_device.c | 8 ++++++++ src/amd/vulkan/radv_extensions.py | 1 + 4 files changed, 16 insertions(+), 4 deletions(-)
diff --git a/src/amd/vulkan/Makefile.am b/src/amd/vulkan/Makefile.am index 94ece06e99e..86dfb0265d6 100644 --- a/src/amd/vulkan/Makefile.am +++ b/src/amd/vulkan/Makefile.am @@ -129,12 +129,14 @@ libvulkan_radeon_la_SOURCES = $(VULKAN_GEM_FILES)
vulkan_api_xml = $(top_srcdir)/src/vulkan/registry/vk.xml vk_android_native_buffer_xml = $(top_srcdir)/src/vulkan/registry/vk_android_native_buffer.xml +vk_mesa_query_timestamp_xml = $(top_srcdir)/src/vulkan/registry/vk_mesa_query_timestamp.xml
radv_entrypoints.c: radv_entrypoints_gen.py radv_extensions.py $(vulkan_api_xml) $(MKDIR_GEN) $(AM_V_GEN)$(PYTHON2) $(srcdir)/radv_entrypoints_gen.py \ --xml $(vulkan_api_xml) \ --xml $(vk_android_native_buffer_xml) \ + --xml $(vk_mesa_query_timestamp_xml) \ --outdir $(builddir) radv_entrypoints.h: radv_entrypoints.c
@@ -144,6 +146,7 @@ radv_extensions.c: radv_extensions.py \ $(AM_V_GEN)$(PYTHON2) $(srcdir)/radv_extensions.py \ --xml $(vulkan_api_xml) \ --xml $(vk_android_native_buffer_xml) \ + --xml $(vk_mesa_query_timestamp_xml) \ --out $@
vk_format_table.c: vk_format_table.py \ diff --git a/src/amd/vulkan/meson.build b/src/amd/vulkan/meson.build index 0b92a1763a1..34f578476c0 100644 --- a/src/amd/vulkan/meson.build +++ b/src/amd/vulkan/meson.build @@ -20,10 +20,10 @@
radv_entrypoints = custom_target( 'radv_entrypoints.[ch]', - input : ['radv_entrypoints_gen.py', vk_api_xml], + input : ['radv_entrypoints_gen.py', vk_api_xml, vk_android_native_buffer_xml, vk_mesa_query_timestamp_xml], output : ['radv_entrypoints.h', 'radv_entrypoints.c'], command : [ - prog_python2, '@INPUT0@', '--xml', '@INPUT1@', '--outdir', + prog_python2, '@INPUT0@', '--xml', '@INPUT1@', '--xml', '@INPUT2@', '--xml', '@INPUT3@', '--outdir', meson.current_build_dir() ], depend_files : files('radv_extensions.py'), @@ -31,10 +31,10 @@ radv_entrypoints = custom_target(
radv_extensions_c = custom_target( 'radv_extensions.c', - input : ['radv_extensions.py', vk_api_xml, vk_android_native_buffer_xml], + input : ['radv_extensions.py', vk_api_xml, vk_android_native_buffer_xml, vk_mesa_query_timestamp_xml], output : ['radv_extensions.c'], command : [ - prog_python2, '@INPUT0@', '--xml', '@INPUT1@', '--xml', '@INPUT2@', '--out', '@OUTPUT@', + prog_python2, '@INPUT0@', '--xml', '@INPUT1@', '--xml', '@INPUT2@', '--xml', '@INPUT3@', '--out', '@OUTPUT@', ], )
diff --git a/src/amd/vulkan/radv_device.c b/src/amd/vulkan/radv_device.c index 8ae7391c891..09c5ca8edcc 100644 --- a/src/amd/vulkan/radv_device.c +++ b/src/amd/vulkan/radv_device.c @@ -4083,3 +4083,11 @@ radv_DebugReportMessageEXT(VkInstance _instance, vk_debug_report(&instance->debug_report_callbacks, flags, objectType, object, location, messageCode, pLayerPrefix, pMessage); } + +VkResult radv_QueryCurrentTimestampMESA(VkDevice _device, uint64_t *timestamp) +{ + RADV_FROM_HANDLE(radv_device, device, _device); + + *timestamp = device->ws->query_value(device->ws, RADEON_TIMESTAMP); + return VK_SUCCESS; +} diff --git a/src/amd/vulkan/radv_extensions.py b/src/amd/vulkan/radv_extensions.py index 42b8d996b6a..0e7b18dc104 100644 --- a/src/amd/vulkan/radv_extensions.py +++ b/src/amd/vulkan/radv_extensions.py @@ -95,6 +95,7 @@ EXTENSIONS = [ Extension('VK_AMD_draw_indirect_count', 1, True), Extension('VK_AMD_rasterization_order', 1, 'device->rad_info.chip_class >= VI && device->rad_info.max_se >= 2'), Extension('VK_AMD_shader_info', 1, True), + Extension('VK_MESA_query_timestamp', 1, True), ]
class VkVersion:
This adds support for the VK_GOOGLE_display timing extension, which provides two things:
1) Detailed information about when frames are displayed, including slack time between GPU execution and display frame.
2) Absolute time control over swapchain queue processing. This allows the application to request frames be displayed at specific absolute times, using the same timebase as that provided in vblank events.
Support for this extension has been implemented for the x11 and display backends; adding support to other backends should be reasonable straightforward for one familiar with those systems and should not require any additional device-specific code.
Signed-off-by: Keith Packard keithp@keithp.com --- src/vulkan/wsi/wsi_common.c | 254 +++++++++++++++++++++++++++++++++++- src/vulkan/wsi/wsi_common.h | 24 ++++ src/vulkan/wsi/wsi_common_display.c | 143 +++++++++++++++++++- src/vulkan/wsi/wsi_common_private.h | 35 +++++ src/vulkan/wsi/wsi_common_x11.c | 69 +++++++++- 5 files changed, 514 insertions(+), 11 deletions(-)
diff --git a/src/vulkan/wsi/wsi_common.c b/src/vulkan/wsi/wsi_common.c index 02abc9ef7fb..c36e4ccdcbe 100644 --- a/src/vulkan/wsi/wsi_common.c +++ b/src/vulkan/wsi/wsi_common.c @@ -40,11 +40,16 @@ wsi_device_init(struct wsi_device *wsi, PFN_vk##func func = (PFN_vk##func)proc_addr(pdevice, "vk" #func) WSI_GET_CB(GetPhysicalDeviceMemoryProperties); WSI_GET_CB(GetPhysicalDeviceQueueFamilyProperties); + WSI_GET_CB(GetPhysicalDeviceProperties); #undef WSI_GET_CB
GetPhysicalDeviceMemoryProperties(pdevice, &wsi->memory_props); GetPhysicalDeviceQueueFamilyProperties(pdevice, &wsi->queue_family_count, NULL);
+ VkPhysicalDeviceProperties properties; + GetPhysicalDeviceProperties(pdevice, &properties); + wsi->timestamp_period = properties.limits.timestampPeriod; + #define WSI_GET_CB(func) \ wsi->func = (PFN_vk##func)proc_addr(pdevice, "vk" #func) WSI_GET_CB(AllocateMemory); @@ -53,14 +58,18 @@ wsi_device_init(struct wsi_device *wsi, WSI_GET_CB(BindImageMemory); WSI_GET_CB(BeginCommandBuffer); WSI_GET_CB(CmdCopyImageToBuffer); + WSI_GET_CB(CmdResetQueryPool); + WSI_GET_CB(CmdWriteTimestamp); WSI_GET_CB(CreateBuffer); WSI_GET_CB(CreateCommandPool); WSI_GET_CB(CreateFence); WSI_GET_CB(CreateImage); + WSI_GET_CB(CreateQueryPool); WSI_GET_CB(DestroyBuffer); WSI_GET_CB(DestroyCommandPool); WSI_GET_CB(DestroyFence); WSI_GET_CB(DestroyImage); + WSI_GET_CB(DestroyQueryPool); WSI_GET_CB(EndCommandBuffer); WSI_GET_CB(FreeMemory); WSI_GET_CB(FreeCommandBuffers); @@ -68,9 +77,13 @@ wsi_device_init(struct wsi_device *wsi, WSI_GET_CB(GetImageMemoryRequirements); WSI_GET_CB(GetImageSubresourceLayout); WSI_GET_CB(GetMemoryFdKHR); + WSI_GET_CB(GetPhysicalDeviceProperties); WSI_GET_CB(GetPhysicalDeviceFormatProperties); + WSI_GET_CB(GetPhysicalDeviceQueueFamilyProperties); + WSI_GET_CB(GetQueryPoolResults); WSI_GET_CB(ResetFences); WSI_GET_CB(QueueSubmit); + WSI_GET_CB(QueryCurrentTimestampMESA); WSI_GET_CB(WaitForFences); #undef WSI_GET_CB
@@ -136,6 +149,8 @@ wsi_swapchain_init(const struct wsi_device *wsi, chain->device = device; chain->alloc = *pAllocator; chain->use_prime_blit = false; + chain->timing_insert = 0; + chain->timing_count = 0;
chain->cmd_pools = vk_zalloc(pAllocator, sizeof(VkCommandPool) * wsi->queue_family_count, 8, @@ -209,6 +224,60 @@ align_u32(uint32_t v, uint32_t a) return (v + a - 1) & ~(a - 1); }
+static VkResult +wsi_image_init_timestamp(const struct wsi_swapchain *chain, + struct wsi_image *image) +{ + const struct wsi_device *wsi = chain->wsi; + VkResult result; + /* Set up command buffer to get timestamp info */ + + result = wsi->CreateQueryPool(chain->device, + &(const VkQueryPoolCreateInfo) { + .sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO, + .queryType = VK_QUERY_TYPE_TIMESTAMP, + .queryCount = 1, + }, + NULL, + &image->query_pool); + + if (result != VK_SUCCESS) + goto fail; + + result = wsi->AllocateCommandBuffers(chain->device, + &(const VkCommandBufferAllocateInfo) { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, + .pNext = NULL, + .commandPool = chain->cmd_pools[0], + .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, + .commandBufferCount = 1, + }, + &image->timestamp_buffer); + if (result != VK_SUCCESS) + goto fail; + + wsi->BeginCommandBuffer(image->timestamp_buffer, + &(VkCommandBufferBeginInfo) { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + .flags = 0 + }); + + wsi->CmdResetQueryPool(image->timestamp_buffer, + image->query_pool, + 0, 1); + + wsi->CmdWriteTimestamp(image->timestamp_buffer, + VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, + image->query_pool, + 0); + + wsi->EndCommandBuffer(image->timestamp_buffer); + + return VK_SUCCESS; +fail: + return result; +} + VkResult wsi_create_native_image(const struct wsi_swapchain *chain, const VkSwapchainCreateInfoKHR *pCreateInfo, @@ -303,6 +372,11 @@ wsi_create_native_image(const struct wsi_swapchain *chain, }; int fd; result = wsi->GetMemoryFdKHR(chain->device, &memory_get_fd_info, &fd); + if (result != VK_SUCCESS) + goto fail; + + result = wsi_image_init_timestamp(chain, image); + if (result != VK_SUCCESS) goto fail;
@@ -497,6 +571,11 @@ wsi_create_prime_image(const struct wsi_swapchain *chain, goto fail; }
+ result = wsi_image_init_timestamp(chain, image); + + if (result != VK_SUCCESS) + goto fail; + const VkMemoryGetFdInfoKHR linear_memory_get_fd_info = { .sType = VK_STRUCTURE_TYPE_MEMORY_GET_FD_INFO_KHR, .pNext = NULL, @@ -695,6 +774,87 @@ wsi_common_acquire_next_image(const struct wsi_device *wsi, semaphore, pImageIndex); }
+static struct wsi_timing * +wsi_get_timing(struct wsi_swapchain *chain, uint32_t i) +{ + uint32_t j = WSI_TIMING_HISTORY + chain->timing_insert - chain->timing_count + i; + + if (j >= WSI_TIMING_HISTORY) + j -= WSI_TIMING_HISTORY; + return &chain->timing[j]; +} + +static struct wsi_timing * +wsi_next_timing(struct wsi_swapchain *chain, int image_index) +{ + uint32_t j; + struct wsi_timing *timing; + + j = chain->timing_insert; + ++chain->timing_insert; + if (chain->timing_insert >= WSI_TIMING_HISTORY) + chain->timing_insert = 0; + if (chain->timing_count < WSI_TIMING_HISTORY) + ++chain->timing_count; + timing = &chain->timing[j]; + memset(timing, '\0', sizeof (*timing)); + return timing; +} + +void +wsi_mark_timing(struct wsi_swapchain *swapchain, + struct wsi_image *image, + uint64_t ust, + uint64_t msc) +{ + const struct wsi_device *wsi = swapchain->wsi; + struct wsi_timing *timing = image->timing; + VkResult result; + + if (!timing) + return; + + swapchain->frame_msc = msc; + swapchain->frame_ust = ust; + + uint64_t render_timestamp; + + result = wsi->GetQueryPoolResults(swapchain->device, image->query_pool, + 0, 1, sizeof(render_timestamp), &render_timestamp, + sizeof (uint64_t), + VK_QUERY_RESULT_64_BIT|VK_QUERY_RESULT_WAIT_BIT); + + if (result != VK_SUCCESS) + return; + + uint64_t current_gpu_timestamp; + result = wsi->QueryCurrentTimestampMESA(swapchain->device, ¤t_gpu_timestamp); + if (result != VK_SUCCESS) + return; + uint64_t current_time = swapchain->get_current_time(); + + VkRefreshCycleDurationGOOGLE display_timings; + swapchain->get_refresh_cycle_duration(swapchain, &display_timings); + + uint64_t refresh_duration = display_timings.refreshDuration; + + /* When did drawing complete (in nsec) */ + + uint64_t render_time = current_time - (current_gpu_timestamp - render_timestamp) * wsi->timestamp_period; + + if (render_time > ust) + render_time = ust; + + uint64_t render_frames = (ust - render_time) / refresh_duration; + + uint64_t earliest_time = ust - render_frames * refresh_duration; + + timing->timing.actualPresentTime = ust; + timing->timing.earliestPresentTime = earliest_time; + timing->timing.presentMargin = earliest_time - render_time; + timing->complete = true; +} + VkResult wsi_common_queue_present(const struct wsi_device *wsi, VkDevice device, @@ -706,10 +866,13 @@ wsi_common_queue_present(const struct wsi_device *wsi,
const VkPresentRegionsKHR *regions = vk_find_struct_const(pPresentInfo->pNext, PRESENT_REGIONS_KHR); + const VkPresentTimesInfoGOOGLE *present_times_info = + vk_find_struct_const(pPresentInfo->pNext, PRESENT_TIMES_INFO_GOOGLE);
for (uint32_t i = 0; i < pPresentInfo->swapchainCount; i++) { WSI_FROM_HANDLE(wsi_swapchain, swapchain, pPresentInfo->pSwapchains[i]); VkResult result; + struct wsi_timing *timing = NULL;
if (swapchain->fences[0] == VK_NULL_HANDLE) { const VkFenceCreateInfo fence_info = { @@ -726,9 +889,12 @@ wsi_common_queue_present(const struct wsi_device *wsi, wsi->ResetFences(device, 1, &swapchain->fences[0]); }
+ VkCommandBuffer submit_buffers[2]; VkSubmitInfo submit_info = { .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, .pNext = NULL, + .pCommandBuffers = submit_buffers, + .commandBufferCount = 0 };
VkPipelineStageFlags *stage_flags = NULL; @@ -761,9 +927,44 @@ wsi_common_queue_present(const struct wsi_device *wsi, */ struct wsi_image *image = swapchain->get_wsi_image(swapchain, pPresentInfo->pImageIndices[i]); - submit_info.commandBufferCount = 1; - submit_info.pCommandBuffers = - &image->prime.blit_cmd_buffers[queue_family_index]; + submit_buffers[submit_info.commandBufferCount++] = + image->prime.blit_cmd_buffers[queue_family_index]; + } + + /* Set up GOOGLE_display_timing bits */ + if (present_times_info && + present_times_info->pTimes != NULL && + i < present_times_info->swapchainCount) + { + const VkPresentTimeGOOGLE *present_time = &present_times_info->pTimes[i]; + + struct wsi_image *image = swapchain->get_wsi_image(swapchain, pPresentInfo->pImageIndices[i]); + + timing = wsi_next_timing(swapchain, pPresentInfo->pImageIndices[i]); + timing->timing.presentID = present_time->presentID; + timing->timing.desiredPresentTime = present_time->desiredPresentTime; + timing->target_msc = 0; + image->timing = timing; + + if (present_time->desiredPresentTime != 0) + { + int64_t delta_nsec = (int64_t) (present_time->desiredPresentTime - swapchain->frame_ust); + + /* Set the target msc only if it's no more than two seconds from + * now, and not stale + */ + if (0 <= delta_nsec && delta_nsec <= 2000000000ul) { + VkRefreshCycleDurationGOOGLE refresh_timing; + + swapchain->get_refresh_cycle_duration(swapchain, &refresh_timing); + + int64_t refresh = (int64_t) refresh_timing.refreshDuration; + int64_t frames = (delta_nsec + refresh/2) / refresh; + timing->target_msc = swapchain->frame_msc + frames; + } + } + + submit_buffers[submit_info.commandBufferCount++] = image->timestamp_buffer; }
result = wsi->QueueSubmit(queue, 1, &submit_info, swapchain->fences[0]); @@ -801,3 +1002,50 @@ wsi_common_queue_present(const struct wsi_device *wsi,
return final_result; } + +VkResult +wsi_common_get_refresh_cycle_duration(const struct wsi_device *wsi, + VkDevice device_h, + VkSwapchainKHR _swapchain, + VkRefreshCycleDurationGOOGLE *pDisplayTimingProperties) +{ + WSI_FROM_HANDLE(wsi_swapchain, swapchain, _swapchain); + + if (!swapchain->get_refresh_cycle_duration) + return VK_ERROR_EXTENSION_NOT_PRESENT; + return swapchain->get_refresh_cycle_duration(swapchain, pDisplayTimingProperties); +} + + +VkResult +wsi_common_get_past_presentation_timing(const struct wsi_device *wsi, + VkDevice device_h, + VkSwapchainKHR _swapchain, + uint32_t *count, + VkPastPresentationTimingGOOGLE *timings) +{ + WSI_FROM_HANDLE(wsi_swapchain, swapchain, _swapchain); + uint32_t timing_count_requested = *count; + uint32_t timing_count_available = 0; + uint32_t t; + + /* Count the number of completed entries, copy */ + for (t = 0; t < swapchain->timing_count; t++) { + struct wsi_timing *timing = wsi_get_timing(swapchain, t); + + if (timing->complete && !timing->consumed) { + if (timings && timing_count_available < timing_count_requested) { + timings[timing_count_available] = timing->timing; + timing->consumed = true; + } + timing_count_available++; + } + } + + *count = timing_count_available; + + if (timing_count_available > timing_count_requested && timings != NULL) + return VK_INCOMPLETE; + + return VK_SUCCESS; +} diff --git a/src/vulkan/wsi/wsi_common.h b/src/vulkan/wsi/wsi_common.h index e504f4120ad..47786810b66 100644 --- a/src/vulkan/wsi/wsi_common.h +++ b/src/vulkan/wsi/wsi_common.h @@ -64,6 +64,7 @@ struct wsi_interface; struct wsi_device { VkPhysicalDeviceMemoryProperties memory_props; uint32_t queue_family_count; + float timestamp_period;
#define WSI_CB(cb) PFN_vk##cb cb WSI_CB(AllocateMemory); @@ -72,14 +73,18 @@ struct wsi_device { WSI_CB(BindImageMemory); WSI_CB(BeginCommandBuffer); WSI_CB(CmdCopyImageToBuffer); + WSI_CB(CmdResetQueryPool); + WSI_CB(CmdWriteTimestamp); WSI_CB(CreateBuffer); WSI_CB(CreateCommandPool); WSI_CB(CreateFence); WSI_CB(CreateImage); + WSI_CB(CreateQueryPool); WSI_CB(DestroyBuffer); WSI_CB(DestroyCommandPool); WSI_CB(DestroyFence); WSI_CB(DestroyImage); + WSI_CB(DestroyQueryPool); WSI_CB(EndCommandBuffer); WSI_CB(FreeMemory); WSI_CB(FreeCommandBuffers); @@ -87,9 +92,13 @@ struct wsi_device { WSI_CB(GetImageMemoryRequirements); WSI_CB(GetImageSubresourceLayout); WSI_CB(GetMemoryFdKHR); + WSI_CB(GetPhysicalDeviceProperties); WSI_CB(GetPhysicalDeviceFormatProperties); + WSI_CB(GetPhysicalDeviceQueueFamilyProperties); + WSI_CB(GetQueryPoolResults); WSI_CB(ResetFences); WSI_CB(QueueSubmit); + WSI_CB(QueryCurrentTimestampMESA); WSI_CB(WaitForFences); #undef WSI_CB
@@ -201,4 +210,19 @@ wsi_common_queue_present(const struct wsi_device *wsi, int queue_family_index, const VkPresentInfoKHR *pPresentInfo);
+/* VK_GOOGLE_display_timing */ +VkResult +wsi_common_get_refresh_cycle_duration(const struct wsi_device *wsi, + VkDevice device_h, + VkSwapchainKHR swapchain, + VkRefreshCycleDurationGOOGLE *pDisplayTimingProperties); + + +VkResult +wsi_common_get_past_presentation_timing(const struct wsi_device *wsi, + VkDevice device_h, + VkSwapchainKHR swapchain, + uint32_t *pPresentationTimingCount, + VkPastPresentationTimingGOOGLE *pPresentationTimings); + #endif diff --git a/src/vulkan/wsi/wsi_common_display.c b/src/vulkan/wsi/wsi_common_display.c index c3608f13e54..60bc6e8be00 100644 --- a/src/vulkan/wsi/wsi_common_display.c +++ b/src/vulkan/wsi/wsi_common_display.c @@ -75,6 +75,8 @@ typedef struct wsi_display_connector { char *name; bool connected; bool active; + uint64_t last_frame; + uint64_t last_nsec; wsi_display_mode *current_mode; drmModeModeInfo current_drm_mode; uint32_t dpms_property; @@ -104,6 +106,7 @@ struct wsi_display { enum wsi_image_state { wsi_image_idle, wsi_image_drawing, + wsi_image_waiting, wsi_image_queued, wsi_image_flipping, wsi_image_displaying @@ -113,6 +116,7 @@ struct wsi_display_image { struct wsi_image base; struct wsi_display_swapchain *chain; enum wsi_image_state state; + struct wsi_display_fence *fence; uint32_t fb_id; uint64_t flip_sequence; }; @@ -122,6 +126,7 @@ struct wsi_display_swapchain { struct wsi_display *wsi; VkIcdSurfaceDisplay *surface; uint64_t flip_sequence; + const VkAllocationCallbacks *allocator; struct wsi_display_image images[0]; };
@@ -130,6 +135,7 @@ struct wsi_display_fence { bool event_received; bool destroyed; uint64_t sequence; + struct wsi_display_image *image; };
static uint64_t fence_sequence; @@ -799,6 +805,7 @@ wsi_display_image_init(VkDevice device_h,
image->chain = chain; image->state = wsi_image_idle; + image->fence = NULL; image->fb_id = 0;
/* XXX extract depth and bpp from image somehow */ @@ -866,6 +873,11 @@ wsi_display_idle_old_displaying(struct wsi_display_image *active_image) static VkResult _wsi_display_queue_next(struct wsi_swapchain *drv_chain);
+static uint64_t widen_32_to_64(uint32_t narrow, uint64_t near) +{ + return near + (int32_t) (narrow - near); +} + static void wsi_display_page_flip_handler2(int fd, unsigned int frame, @@ -875,11 +887,25 @@ wsi_display_page_flip_handler2(int fd, void *data) { struct wsi_display_image *image = data; + struct wsi_display_swapchain *chain = image->chain; + VkIcdSurfaceDisplay *surface = chain->surface; + wsi_display_mode *display_mode = wsi_display_mode_from_handle(surface->displayMode); + wsi_display_connector *connector = display_mode->connector; + uint64_t frame64 = widen_32_to_64(frame, connector->last_frame); + uint64_t nsec = (uint64_t) sec * 1000000000 + (uint64_t) usec * 1000; + + /* Don't let time go backwards because this function has lower resolution than ktime */ + if (nsec < connector->last_nsec) + nsec = connector->last_nsec;
- wsi_display_debug("image %ld displayed at %d\n", image - &(image->chain->images[0]), frame); + wsi_display_debug("image %ld displayed at %ld\n", image - &(image->chain->images[0]), frame64); image->state = wsi_image_displaying; + connector->last_frame = frame64; + connector->last_nsec = nsec; + wsi_mark_timing(&image->chain->base, &image->base, + nsec, frame64); wsi_display_idle_old_displaying(image); - (void) _wsi_display_queue_next(&(image->chain->base)); + (void) _wsi_display_queue_next(&(chain->base)); }
static void wsi_display_page_flip_handler(int fd, unsigned int frame, @@ -1228,6 +1254,7 @@ wsi_display_fence_alloc(VkDevice device, fence->event_received = false; fence->destroyed = false; fence->sequence = ++fence_sequence; + fence->image = NULL; return fence; }
@@ -1323,6 +1350,12 @@ _wsi_display_queue_next(struct wsi_swapchain *drv_chain) if (!image) return VK_SUCCESS;
+ if (image->fence) { + image->fence->image = NULL; + wsi_display_fence_destroy(&image->fence->base); + image->fence = NULL; + } + if (connector->active) { ret = drmModePageFlip(wsi->master_fd, connector->crtc_id, image->fb_id, DRM_MODE_PAGE_FLIP_EVENT, image); @@ -1385,16 +1418,83 @@ wsi_display_queue_present(struct wsi_swapchain *drv_chain,
pthread_mutex_lock(&wsi->wait_mutex);
+ if (image->base.timing && image->base.timing->target_msc != 0) { + VkIcdSurfaceDisplay *surface = chain->surface; + wsi_display_mode *display_mode = wsi_display_mode_from_handle(surface->displayMode); + wsi_display_connector *connector = display_mode->connector; + + wsi_display_debug("delta frame %ld\n", image->base.timing->target_msc - connector->last_frame); + if (image->base.timing->target_msc > connector->last_frame) { + uint64_t frame_queued; + VkDisplayKHR display = wsi_display_connector_to_handle(connector); + + wsi_display_debug_code(uint64_t current_frame, current_nsec; + drmCrtcGetSequence(wsi->master_fd, connector->crtc_id, ¤t_frame, ¤t_nsec); + wsi_display_debug("from current: %ld\n", image->base.timing->target_msc - current_frame)); + + image->fence = wsi_display_fence_alloc(chain->base.device, chain->base.wsi, display, &chain->base.alloc); + + if (!image->fence) { + result = VK_ERROR_OUT_OF_HOST_MEMORY; + goto bail_unlock; + } + + result = wsi_register_vblank_event(image->fence, + chain->base.wsi, + display, + 0, + image->base.timing->target_msc - 1, + &frame_queued); + + if (result != VK_SUCCESS) + goto bail_unlock; + + /* Check and make sure we are queued for the right frame, otherwise just + * go queue an image + */ + if (frame_queued <= image->base.timing->target_msc - 1) { + image->state = wsi_image_waiting; + + /* + * Don't set the image member until we're going to wait for the + * event to arrive before flipping to the image. That way, if the + * register_vblank_event call happens to process the event, it + * won't actually do anything + */ + image->fence->image = image; + wsi_display_start_wait_thread(wsi); + result = VK_SUCCESS; + goto bail_unlock; + } + + } + } + + image->flip_sequence = ++chain->flip_sequence; image->state = wsi_image_queued;
result = _wsi_display_queue_next(drv_chain);
+bail_unlock: pthread_mutex_unlock(&wsi->wait_mutex);
return result; }
+static VkResult +wsi_display_get_refresh_cycle_duration(struct wsi_swapchain *drv_chain, + VkRefreshCycleDurationGOOGLE *duration) +{ + struct wsi_display_swapchain *chain = (struct wsi_display_swapchain *) drv_chain; + VkIcdSurfaceDisplay *surface = chain->surface; + wsi_display_mode *display_mode = wsi_display_mode_from_handle(surface->displayMode); + double refresh = wsi_display_mode_refresh(display_mode); + + duration->refreshDuration = (uint64_t) (floor (1.0/refresh * 1e9 + 0.5)); + return VK_SUCCESS; +} + static VkResult wsi_display_surface_create_swapchain(VkIcdSurfaceBase *icd_surface, VkDevice device, @@ -1426,10 +1526,13 @@ wsi_display_surface_create_swapchain(VkIcdSurfaceBase *icd_sur chain->base.get_wsi_image = wsi_display_get_wsi_image; chain->base.acquire_next_image = wsi_display_acquire_next_image; chain->base.queue_present = wsi_display_queue_present; + chain->base.get_refresh_cycle_duration = wsi_display_get_refresh_cycle_duration; + chain->base.get_current_time = wsi_get_current_monotonic; chain->base.present_mode = create_info->presentMode; chain->base.image_count = num_images;
chain->wsi = wsi; + chain->allocator = allocator;
chain->surface = (VkIcdSurfaceDisplay *) icd_surface;
@@ -2130,6 +2233,7 @@ wsi_get_swapchain_counter(VkDevice device, 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; + uint64_t nsec;
if (wsi->master_fd < 0) return VK_ERROR_INITIALIZATION_FAILED; @@ -2139,9 +2243,13 @@ wsi_get_swapchain_counter(VkDevice device, return VK_SUCCESS; }
- ret = drmCrtcGetSequence(wsi->master_fd, connector->crtc_id, value, NULL); + ret = drmCrtcGetSequence(wsi->master_fd, connector->crtc_id, value, &nsec); if (ret) *value = 0; + else { + connector->last_frame = *value; + connector->last_nsec = nsec; + }
return VK_SUCCESS; } @@ -2150,19 +2258,44 @@ 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); + struct wsi_display_connector *connector = wsi_display_connector_from_handle(fence->base.display); + uint64_t frame64 = widen_32_to_64(frame, connector->last_frame); + uint64_t nsec = (uint64_t) sec * 1000000000 + (uint64_t) usec * 1000; + struct wsi_display_image *image = fence->image; + + /* Don't let time go backwards because this function has lower resolution than ktime */ + if (nsec < connector->last_nsec) + nsec = connector->last_nsec; + + wsi_display_debug("%9lu fence %lu received %lu nsec %lu\n", pthread_self(), fence->sequence, frame64, nsec); + connector->last_nsec = nsec; + connector->last_frame = frame64; fence->event_received = true; wsi_display_fence_check_free(fence); + if (image) { + image->flip_sequence = ++image->chain->flip_sequence; + image->state = wsi_image_queued; + (void) _wsi_display_queue_next(&image->chain->base); + } }
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; + struct wsi_display_connector *connector = wsi_display_connector_from_handle(fence->base.display); + struct wsi_display_image *image = fence->image;
wsi_display_debug("%9lu fence %lu received %lu\n", pthread_self(), fence->sequence, frame); + connector->last_nsec = ns; + connector->last_frame = frame; fence->event_received = true; wsi_display_fence_check_free(fence); + if (image) { + image->flip_sequence = ++image->chain->flip_sequence; + image->state = wsi_image_queued; + (void) _wsi_display_queue_next(&image->chain->base); + } }
+/* VK_GOOGLE_display_timing */ diff --git a/src/vulkan/wsi/wsi_common_private.h b/src/vulkan/wsi/wsi_common_private.h index 0d902846238..6f9d7323085 100644 --- a/src/vulkan/wsi/wsi_common_private.h +++ b/src/vulkan/wsi/wsi_common_private.h @@ -25,6 +25,13 @@
#include "wsi_common.h"
+struct wsi_timing { + bool complete; + bool consumed; + uint64_t target_msc; + VkPastPresentationTimingGOOGLE timing; +}; + struct wsi_image { VkImage image; VkDeviceMemory memory; @@ -39,8 +46,16 @@ struct wsi_image { uint32_t offset; uint32_t row_pitch; int fd; + + VkQueryPool query_pool; + + VkCommandBuffer timestamp_buffer; + + struct wsi_timing *timing; };
+#define WSI_TIMING_HISTORY 16 + struct wsi_swapchain { const struct wsi_device *wsi;
@@ -52,6 +67,16 @@ struct wsi_swapchain {
bool use_prime_blit;
+ uint32_t timing_insert; + uint32_t timing_count; + + struct wsi_timing timing[WSI_TIMING_HISTORY]; + + uint64_t frame_msc; + uint64_t frame_ust; + + float timestamp_period; + /* Command pools, one per queue family */ VkCommandPool *cmd_pools;
@@ -65,6 +90,10 @@ struct wsi_swapchain { VkResult (*queue_present)(struct wsi_swapchain *swap_chain, uint32_t image_index, const VkPresentRegionKHR *damage); + VkResult (*get_refresh_cycle_duration)(struct wsi_swapchain *swap_chain, + VkRefreshCycleDurationGOOGLE *pDisplayTimingProperties); + + uint64_t (*get_current_time)(void); };
VkResult @@ -91,6 +120,12 @@ wsi_destroy_image(const struct wsi_swapchain *chain, struct wsi_image *image);
+void +wsi_mark_timing(struct wsi_swapchain *swapchain, + struct wsi_image *image, + uint64_t ust, + uint64_t msc); + struct wsi_interface { VkResult (*get_support)(VkIcdSurfaceBase *surface, struct wsi_device *wsi_device, diff --git a/src/vulkan/wsi/wsi_common_x11.c b/src/vulkan/wsi/wsi_common_x11.c index 714523678d4..e0d9135da53 100644 --- a/src/vulkan/wsi/wsi_common_x11.c +++ b/src/vulkan/wsi/wsi_common_x11.c @@ -641,6 +641,7 @@ struct x11_image { bool busy; struct xshmfence * shm_fence; uint32_t sync_fence; + uint32_t serial; };
struct x11_swapchain { @@ -657,6 +658,8 @@ struct x11_swapchain { uint64_t send_sbc; uint64_t last_present_msc; uint32_t stamp; + uint64_t last_present_nsec; + uint64_t refresh_period;
bool threaded; VkResult status; @@ -706,8 +709,36 @@ x11_handle_dri3_present_event(struct x11_swapchain *chain,
case XCB_PRESENT_EVENT_COMPLETE_NOTIFY: { xcb_present_complete_notify_event_t *complete = (void *) event; - if (complete->kind == XCB_PRESENT_COMPLETE_KIND_PIXMAP) + if (complete->kind == XCB_PRESENT_COMPLETE_KIND_PIXMAP) { + uint64_t frames = complete->msc - chain->last_present_msc; + uint64_t present_nsec = complete->ust * 1000; + + /* + * Well, this is about as good as we can do -- measure the refresh + * instead of asking for the current mode and using that. Turns out, + * for eDP panels, this works better anyways as they used the builtin + * fixed mode for everything + */ + if (0 < frames && frames < 10 && present_nsec > chain->last_present_nsec) { + + uint64_t refresh_period = (present_nsec - chain->last_present_nsec + frames / 2) / frames; + + if (chain->refresh_period) + refresh_period = (3 * chain->refresh_period + refresh_period) >> 2; + + chain->refresh_period = refresh_period; + } + chain->last_present_msc = complete->msc; + chain->last_present_nsec = present_nsec; + for (unsigned i = 0; i < chain->base.image_count; i++) { + if (chain->images[i].serial == complete->serial) { + wsi_mark_timing(&chain->base, &chain->images[i].base, + present_nsec, complete->msc); + break; + } + } + } break; }
@@ -823,7 +854,7 @@ x11_acquire_next_image_from_queue(struct x11_swapchain *chain,
static VkResult x11_present_to_x11(struct x11_swapchain *chain, uint32_t image_index, - uint32_t target_msc) + uint64_t target_msc) { struct x11_image *image = &chain->images[image_index];
@@ -840,11 +871,12 @@ x11_present_to_x11(struct x11_swapchain *chain, uint32_t image_index, xshmfence_reset(image->shm_fence);
++chain->send_sbc; + image->serial = (uint32_t) chain->send_sbc; xcb_void_cookie_t cookie = xcb_present_pixmap(chain->conn, chain->window, image->pixmap, - (uint32_t) chain->send_sbc, + image->serial, 0, /* valid */ 0, /* update */ 0, /* x_off */ @@ -894,6 +926,26 @@ x11_queue_present(struct wsi_swapchain *anv_chain, } }
+static uint64_t +x11_refresh_duration(struct x11_swapchain *chain) +{ + /* Pick 60Hz if we don't know what it actually is yet */ + if (!chain->refresh_period) + return (uint64_t) (1e9 / 59.98 + 0.5); + + return chain->refresh_period; +} + +static VkResult +x11_get_refresh(struct wsi_swapchain *wsi_chain, + VkRefreshCycleDurationGOOGLE *timings) +{ + struct x11_swapchain *chain = (struct x11_swapchain *)wsi_chain; + + timings->refreshDuration = x11_refresh_duration(chain); + return VK_SUCCESS; +} + static void * x11_manage_fifo_queues(void *state) { @@ -910,6 +962,7 @@ x11_manage_fifo_queues(void *state) * other than the currently presented one. */ uint32_t image_index; + struct x11_image *image; result = wsi_queue_pull(&chain->present_queue, &image_index, INT64_MAX); if (result != VK_SUCCESS) { goto fail; @@ -918,6 +971,13 @@ x11_manage_fifo_queues(void *state) }
uint64_t target_msc = chain->last_present_msc + 1; + + image = &chain->images[image_index]; + + struct wsi_timing *timing = image->base.timing; + if (timing && timing->target_msc != 0 && timing->target_msc > target_msc) + target_msc = timing->target_msc; + result = x11_present_to_x11(chain, image_index, target_msc); if (result != VK_SUCCESS) goto fail; @@ -1098,7 +1158,9 @@ x11_surface_create_swapchain(VkIcdSurfaceBase *icd_surface, chain->base.get_wsi_image = x11_get_wsi_image; chain->base.acquire_next_image = x11_acquire_next_image; chain->base.queue_present = x11_queue_present; + chain->base.get_current_time = wsi_get_current_time; chain->base.present_mode = pCreateInfo->presentMode; + chain->base.get_refresh_cycle_duration = x11_get_refresh; chain->base.image_count = num_images; chain->conn = conn; chain->window = window; @@ -1106,6 +1168,7 @@ x11_surface_create_swapchain(VkIcdSurfaceBase *icd_surface, chain->extent = pCreateInfo->imageExtent; chain->send_sbc = 0; chain->last_present_msc = 0; + chain->last_present_nsec = 0; chain->threaded = false; chain->status = VK_SUCCESS;
This adds support for the VK_GOOGLE_display timing extension.
Signed-off-by: Keith Packard keithp@keithp.com --- src/intel/vulkan/anv_extensions.py | 1 + src/intel/vulkan/anv_wsi.c | 29 +++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+)
diff --git a/src/intel/vulkan/anv_extensions.py b/src/intel/vulkan/anv_extensions.py index 7706ccdb5bb..b441b8e1d8e 100644 --- a/src/intel/vulkan/anv_extensions.py +++ b/src/intel/vulkan/anv_extensions.py @@ -92,6 +92,7 @@ EXTENSIONS = [ 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_MESA_query_timestamp', 1, True), + Extension('VK_GOOGLE_display_timing', 1, True), ]
class VkVersion: diff --git a/src/intel/vulkan/anv_wsi.c b/src/intel/vulkan/anv_wsi.c index 52362adfb71..7801a989e0d 100644 --- a/src/intel/vulkan/anv_wsi.c +++ b/src/intel/vulkan/anv_wsi.c @@ -239,3 +239,32 @@ VkResult anv_QueuePresentKHR( _queue, 0, pPresentInfo); } + +/* VK_GOOGLE_display_timing */ +VkResult +anv_GetRefreshCycleDurationGOOGLE(VkDevice _device, + VkSwapchainKHR swapchain, + VkRefreshCycleDurationGOOGLE *pDisplayTimingProperties) +{ + ANV_FROM_HANDLE(anv_device, device, _device); + + return wsi_common_get_refresh_cycle_duration(&device->instance->physicalDevice.wsi_device, + _device, + swapchain, + pDisplayTimingProperties); +} + +VkResult +anv_GetPastPresentationTimingGOOGLE(VkDevice _device, + VkSwapchainKHR swapchain, + uint32_t *pPresentationTimingCount, + VkPastPresentationTimingGOOGLE *pPresentationTimings) +{ + ANV_FROM_HANDLE(anv_device, device, _device); + + return wsi_common_get_past_presentation_timing(&device->instance->physicalDevice.wsi_device, + _device, + swapchain, + pPresentationTimingCount, + pPresentationTimings); +}
This adds support for the VK_GOOGLE_display timing extension.
Signed-off-by: Keith Packard keithp@keithp.com --- src/amd/vulkan/radv_extensions.py | 1 + src/amd/vulkan/radv_wsi.c | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+)
diff --git a/src/amd/vulkan/radv_extensions.py b/src/amd/vulkan/radv_extensions.py index 0e7b18dc104..a8d9c8410e1 100644 --- a/src/amd/vulkan/radv_extensions.py +++ b/src/amd/vulkan/radv_extensions.py @@ -96,6 +96,7 @@ EXTENSIONS = [ Extension('VK_AMD_rasterization_order', 1, 'device->rad_info.chip_class >= VI && device->rad_info.max_se >= 2'), Extension('VK_AMD_shader_info', 1, True), Extension('VK_MESA_query_timestamp', 1, True), + Extension('VK_GOOGLE_display_timing', 1, True), ]
class VkVersion: diff --git a/src/amd/vulkan/radv_wsi.c b/src/amd/vulkan/radv_wsi.c index 2433390562b..fd26194f6ce 100644 --- a/src/amd/vulkan/radv_wsi.c +++ b/src/amd/vulkan/radv_wsi.c @@ -247,3 +247,35 @@ VkResult radv_QueuePresentKHR( queue->queue_family_index, pPresentInfo); } + +/* VK_GOOGLE_display_timing */ +VkResult +radv_GetRefreshCycleDurationGOOGLE( + VkDevice _device, + VkSwapchainKHR swapchain, + VkRefreshCycleDurationGOOGLE *pDisplayTimingProperties) +{ + RADV_FROM_HANDLE(radv_device, device, _device); + struct radv_physical_device *pdevice = device->physical_device; + + return wsi_common_get_refresh_cycle_duration(&pdevice->wsi_device, + _device, + swapchain, + pDisplayTimingProperties); +} + +VkResult +radv_GetPastPresentationTimingGOOGLE(VkDevice _device, + VkSwapchainKHR swapchain, + uint32_t *pPresentationTimingCount, + VkPastPresentationTimingGOOGLE *pPresentationTimings) +{ + RADV_FROM_HANDLE(radv_device, device, _device); + struct radv_physical_device *pdevice = device->physical_device; + + return wsi_common_get_past_presentation_timing(&pdevice->wsi_device, + _device, + swapchain, + pPresentationTimingCount, + pPresentationTimings); +}
dri-devel@lists.freedesktop.org