This uses X leases to provide the same API as nVidia, allowing an application to discover available display resources and acquire one from the X server using RandR leases.
Signed-off-by: Keith Packard <kei...@keithp.com> --- configure.ac | 11 + src/amd/vulkan/Makefile.am | 8 + src/amd/vulkan/radv_device.c | 6 + src/amd/vulkan/radv_entrypoints_gen.py | 3 +- src/amd/vulkan/radv_wsi_display.c | 31 +++ src/vulkan/Makefile.am | 8 + src/vulkan/wsi/wsi_common_display.c | 375 +++++++++++++++++++++++++++++++-- src/vulkan/wsi/wsi_common_display.h | 16 ++ 8 files changed, 444 insertions(+), 14 deletions(-) diff --git a/configure.ac b/configure.ac index d95b915b984..b4a17bc9c67 100644 --- a/configure.ac +++ b/configure.ac @@ -1412,6 +1412,10 @@ fi AC_SUBST([GL_LIB]) AC_SUBST([OSMESA_LIB]) +# Check for RandR leases +PKG_CHECK_MODULES([RANDRPROTO], [randrproto >= 1.6.0], + [have_xlease=yes], [have_xlease=no]) + # Check for libdrm PKG_CHECK_MODULES([LIBDRM], [libdrm >= $LIBDRM_REQUIRED], [have_libdrm=yes], [have_libdrm=no]) @@ -1451,6 +1455,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], @@ -1749,6 +1754,12 @@ if test x"$enable_dri3" = xyes; then PKG_CHECK_MODULES([XCB_DRI3], [$dri3_modules]) fi +if test x"$have_xlease" = xyes; then + DEFINES="$DEFINES -DHAVE_XLEASE" + 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') diff --git a/src/amd/vulkan/Makefile.am b/src/amd/vulkan/Makefile.am index 3a6ada825b5..5a3c714cc03 100644 --- a/src/amd/vulkan/Makefile.am +++ b/src/amd/vulkan/Makefile.am @@ -84,6 +84,14 @@ AM_CPPFLAGS += \ VULKAN_SOURCES += $(VULKAN_WSI_DISPLAY_FILES) +if HAVE_PLATFORM_X11 +if HAVE_XLEASE +AM_CPPFLAGS += \ + $(XCB_RANDR_CFLAGS) +VULKAN_LIB_DEPS += $(XCB_RANDR_LIBS) +endif +endif + endif if HAVE_PLATFORM_X11 diff --git a/src/amd/vulkan/radv_device.c b/src/amd/vulkan/radv_device.c index da8e7a89f66..db68b0724fd 100644 --- a/src/amd/vulkan/radv_device.c +++ b/src/amd/vulkan/radv_device.c @@ -123,6 +123,12 @@ static const VkExtensionProperties instance_extensions[] = { .specVersion = 1, }, #endif +#ifdef VK_USE_PLATFORM_XLIB_XRANDR_EXT + { + .extensionName = VK_EXT_ACQUIRE_XLIB_DISPLAY_EXTENSION_NAME, + .specVersion = 1, + }, +#endif }; static const VkExtensionProperties common_device_extensions[] = { diff --git a/src/amd/vulkan/radv_entrypoints_gen.py b/src/amd/vulkan/radv_entrypoints_gen.py index 48125da2e76..2a151cc701c 100644 --- a/src/amd/vulkan/radv_entrypoints_gen.py +++ b/src/amd/vulkan/radv_entrypoints_gen.py @@ -59,7 +59,8 @@ SUPPORTED_EXTENSIONS = [ 'VK_KHR_external_semaphore_fd', 'VK_KEITHP_kms_display', 'VK_KHR_display', - 'VK_EXT_direct_mode_display' + 'VK_EXT_direct_mode_display', + 'VK_EXT_acquire_xlib_display' ] # We generate a static hash table for entry point lookup diff --git a/src/amd/vulkan/radv_wsi_display.c b/src/amd/vulkan/radv_wsi_display.c index 26fb4bee803..0c9cf7e3315 100644 --- a/src/amd/vulkan/radv_wsi_display.c +++ b/src/amd/vulkan/radv_wsi_display.c @@ -137,6 +137,37 @@ radv_ReleaseDisplayEXT(VkPhysicalDevice physical_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 */ + VkResult radv_CreateDisplayPlaneSurfaceKHR(VkInstance _instance, const VkDisplaySurfaceCreateInfoKHR *create_info, diff --git a/src/vulkan/Makefile.am b/src/vulkan/Makefile.am index 693b93e0c11..2af6831bde3 100644 --- a/src/vulkan/Makefile.am +++ b/src/vulkan/Makefile.am @@ -53,6 +53,14 @@ AM_CPPFLAGS += \ -DVK_USE_PLATFORM_DISPLAY VULKAN_WSI_SOURCES += $(VULKAN_WSI_DISPLAY_FILES) + +if HAVE_PLATFORM_X11 +if HAVE_XLEASE +AM_CPPFLAGS += \ + -DVK_USE_PLATFORM_XLIB_XRANDR_EXT +endif +endif + endif BUILT_SOURCES += $(VULKAN_WSI_WAYLAND_GENERATED_FILES) diff --git a/src/vulkan/wsi/wsi_common_display.c b/src/vulkan/wsi/wsi_common_display.c index 1ec0004be91..81e009c1cc8 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" @@ -67,6 +71,9 @@ typedef struct wsi_display_connector { drmModeConnectorPtr connector; uint32_t edid_length; uint8_t *edid; +#ifdef VK_USE_PLATFORM_XLIB_XRANDR_EXT + xcb_randr_output_t output; +#endif } wsi_display_connector; struct wsi_display { @@ -78,6 +85,8 @@ struct wsi_display { const struct wsi_callbacks *cbs; int master_fd; + int render_fd; + bool master_is_render; pthread_mutex_t wait_mutex; pthread_cond_t wait_cond; @@ -226,7 +235,7 @@ wsi_display_get_connector(struct wsi_device *wsi_device, struct wsi_display *wsi = (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY]; struct wsi_display_connector *connector; drmModeConnectorPtr drm_connector; - int drm_fd = wsi->master_fd; + int drm_fd = wsi->master_fd >= 0 ? wsi->master_fd : wsi->render_fd; int p, m; VkResult result; @@ -339,7 +348,7 @@ wsi_display_get_physical_device_display_properties(VkPhysicalDevice VkDisplayPropertiesKHR *properties) { struct wsi_display *wsi = (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY]; - int drm_fd = wsi->master_fd; + int drm_fd = wsi->master_fd >= 0 ? wsi->master_fd : wsi->render_fd; struct wsi_display_connector *connector; int c; uint32_t connected = 0; @@ -568,6 +577,321 @@ wsi_release_display(VkPhysicalDevice physical_device, 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 struct wsi_display_connector * +wsi_display_get_output(struct wsi_device *wsi_device, + xcb_connection_t *connection, + RROutput output) +{ + struct wsi_display_connector *connector; + uint32_t connector_id; + xcb_atom_t connector_id_atom = 0; + + connector = wsi_display_find_output(wsi_device, output); + + if (connector) + return connector; + + connector_id = wsi_display_output_to_connector_id(connection, &connector_id_atom, output); + if (connector_id != 0) + connector = wsi_display_get_connector(wsi_device, connector_id); + + if (connector) + connector->output = output; + + 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; + wsi->master_is_render = false; + + 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 /* VK_USE_PLATFORM_XLIB_XRANDR_EXT */ + VkResult wsi_create_display_surface(VkInstance instance, const VkAllocationCallbacks *allocator, @@ -605,10 +929,13 @@ wsi_display_surface_get_support(VkIcdSurfaceBase *surface, bool can_handle_different_gpu, VkBool32* pSupported) { +#ifdef VK_USE_PLATFORM_XLIB_XRANDR_EXT + *pSupported = true; +#else struct wsi_display *wsi = (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY]; *pSupported = wsi->master_fd >= 0; - +#endif return VK_SUCCESS; } @@ -702,9 +1029,20 @@ wsi_display_image_init(VkDevice device_h, VkResult result; uint32_t row_pitch; uint32_t offset; - uint32_t handle; uint32_t size; int ret; + int image_fd; + uint32_t image_handle; + int *image_fd_p; + uint32_t *image_handle_p; + + if (wsi->master_is_render) { + image_fd_p = NULL; + image_handle_p = &image_handle; + } else { + image_fd_p = &image_fd; + image_handle_p = NULL; + } memset(image, '\0', sizeof (*image)); image->chain = chain; @@ -718,8 +1056,8 @@ wsi_display_image_init(VkDevice device_h, &size, &offset, &row_pitch, - NULL, - &handle); + image_fd_p, + image_handle_p); if (result != VK_SUCCESS) return result; @@ -735,19 +1073,27 @@ wsi_display_image_init(VkDevice device_h, &size, &offset, &row_pitch, - NULL, - &handle); - + image_fd_p, + image_handle_p); if (result != VK_SUCCESS) { goto fail_linear; } } - image->bo_handle = handle; + if (wsi->master_is_render) + image->bo_handle = image_handle; + else { + int ret = drmPrimeFDToHandle(wsi->master_fd, image_fd, &image_handle); + close(image_fd); + if (ret < 0) { + result = VK_ERROR_OUT_OF_HOST_MEMORY; + goto fail_fb; + } + } /* XXX extract depth and bpp from image somehow */ ret = drmModeAddFB(wsi->master_fd, create_info->imageExtent.width, create_info->imageExtent.height, - 24, 32, row_pitch, handle, &image->fb_id); + 24, 32, row_pitch, image_handle, &image->fb_id); if (ret) { result = VK_ERROR_OUT_OF_HOST_MEMORY; @@ -968,7 +1314,7 @@ wsi_display_crtc_solo(struct wsi_display *wsi, uint32_t crtc_id) { drmModeResPtr mode_res = wsi->mode_res; - int drm_fd = wsi->master_fd; + int drm_fd = wsi->master_fd >= 0 ? wsi->master_fd : wsi->render_fd; int c, e; /* See if any other connectors share the same encoder */ @@ -1007,7 +1353,7 @@ wsi_display_select_crtc(struct wsi_display_connector *connector) struct wsi_display *wsi = connector->wsi; int c; drmModeResPtr mode_res = wsi->mode_res; - int drm_fd = wsi->master_fd; + int drm_fd = wsi->master_fd >= 0 ? wsi->master_fd : wsi->render_fd; uint32_t crtc_id; if (!mode_res) @@ -1218,6 +1564,9 @@ wsi_display_init_wsi(struct wsi_device *wsi_device, memset(wsi, '\0', sizeof (*wsi)); wsi->master_fd = master_fd; + wsi->render_fd = render_fd; + if (master_fd >= 0) + wsi->master_is_render = true; pthread_mutex_init(&wsi->wait_mutex, NULL); wsi->physical_device = physical_device; wsi->alloc = alloc; diff --git a/src/vulkan/wsi/wsi_common_display.h b/src/vulkan/wsi/wsi_common_display.h index 173c019c4bd..47430aa41e1 100644 --- a/src/vulkan/wsi/wsi_common_display.h +++ b/src/vulkan/wsi/wsi_common_display.h @@ -68,6 +68,22 @@ 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 */ + VkResult wsi_create_display_surface(VkInstance instance, const VkAllocationCallbacks *pAllocator, -- 2.11.0 _______________________________________________ mesa-dev mailing list mesa-dev@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/mesa-dev