vulkan/util: generate instance/device dispatch tables
[mesa.git] / src / vulkan / wsi / wsi_common_x11.c
index e5e719e1d507d6ca4459d61bfa72b361fc79fefb..52d820869a8614a08861803fe1114e4d711525a4 100644 (file)
@@ -36,6 +36,7 @@
 #include <fcntl.h>
 #include <poll.h>
 #include <xf86drm.h>
+#include "drm-uapi/drm_fourcc.h"
 #include "util/hash_table.h"
 
 #include "vk_util.h"
@@ -50,6 +51,7 @@
 
 struct wsi_x11_connection {
    bool has_dri3;
+   bool has_dri3_modifiers;
    bool has_present;
    bool is_proprietary_x11;
 };
@@ -97,46 +99,41 @@ wsi_dri3_open(xcb_connection_t *conn,
 }
 
 static bool
-wsi_x11_check_dri3_compatible(xcb_connection_t *conn, int local_fd)
+wsi_x11_check_dri3_compatible(const struct wsi_device *wsi_dev,
+                              xcb_connection_t *conn)
 {
    xcb_screen_iterator_t screen_iter =
       xcb_setup_roots_iterator(xcb_get_setup(conn));
    xcb_screen_t *screen = screen_iter.data;
 
    int dri3_fd = wsi_dri3_open(conn, screen->root, None);
-   if (dri3_fd != -1) {
-      char *local_dev = drmGetRenderDeviceNameFromFd(local_fd);
-      char *dri3_dev = drmGetRenderDeviceNameFromFd(dri3_fd);
-      int ret;
-
-      close(dri3_fd);
+   if (dri3_fd == -1)
+      return true;
 
-      ret = strcmp(local_dev, dri3_dev);
+   bool match = wsi_device_matches_drm_fd(wsi_dev, dri3_fd);
 
-      free(local_dev);
-      free(dri3_dev);
+   close(dri3_fd);
 
-      if (ret != 0)
-         return false;
-   }
-   return true;
+   return match;
 }
 
 static struct wsi_x11_connection *
-wsi_x11_connection_create(const VkAllocationCallbacks *alloc,
+wsi_x11_connection_create(struct wsi_device *wsi_dev,
                           xcb_connection_t *conn)
 {
    xcb_query_extension_cookie_t dri3_cookie, pres_cookie, amd_cookie, nv_cookie;
    xcb_query_extension_reply_t *dri3_reply, *pres_reply, *amd_reply, *nv_reply;
+   bool has_dri3_v1_2 = false;
+   bool has_present_v1_2 = false;
 
    struct wsi_x11_connection *wsi_conn =
-      vk_alloc(alloc, sizeof(*wsi_conn), 8,
+      vk_alloc(&wsi_dev->instance_alloc, sizeof(*wsi_conn), 8,
                 VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE);
    if (!wsi_conn)
       return NULL;
 
    dri3_cookie = xcb_query_extension(conn, 4, "DRI3");
-   pres_cookie = xcb_query_extension(conn, 7, "PRESENT");
+   pres_cookie = xcb_query_extension(conn, 7, "Present");
 
    /* We try to be nice to users and emit a warning if they try to use a
     * Vulkan application on a system without DRI3 enabled.  However, this ends
@@ -159,12 +156,39 @@ wsi_x11_connection_create(const VkAllocationCallbacks *alloc,
       free(pres_reply);
       free(amd_reply);
       free(nv_reply);
-      vk_free(alloc, wsi_conn);
+      vk_free(&wsi_dev->instance_alloc, wsi_conn);
       return NULL;
    }
 
    wsi_conn->has_dri3 = dri3_reply->present != 0;
+#ifdef HAVE_DRI3_MODIFIERS
+   if (wsi_conn->has_dri3) {
+      xcb_dri3_query_version_cookie_t ver_cookie;
+      xcb_dri3_query_version_reply_t *ver_reply;
+
+      ver_cookie = xcb_dri3_query_version(conn, 1, 2);
+      ver_reply = xcb_dri3_query_version_reply(conn, ver_cookie, NULL);
+      has_dri3_v1_2 =
+         (ver_reply->major_version > 1 || ver_reply->minor_version >= 2);
+      free(ver_reply);
+   }
+#endif
+
    wsi_conn->has_present = pres_reply->present != 0;
+#ifdef HAVE_DRI3_MODIFIERS
+   if (wsi_conn->has_present) {
+      xcb_present_query_version_cookie_t ver_cookie;
+      xcb_present_query_version_reply_t *ver_reply;
+
+      ver_cookie = xcb_present_query_version(conn, 1, 2);
+      ver_reply = xcb_present_query_version_reply(conn, ver_cookie, NULL);
+      has_present_v1_2 =
+        (ver_reply->major_version > 1 || ver_reply->minor_version >= 2);
+      free(ver_reply);
+   }
+#endif
+
+   wsi_conn->has_dri3_modifiers = has_dri3_v1_2 && has_present_v1_2;
    wsi_conn->is_proprietary_x11 = false;
    if (amd_reply && amd_reply->present)
       wsi_conn->is_proprietary_x11 = true;
@@ -180,10 +204,10 @@ wsi_x11_connection_create(const VkAllocationCallbacks *alloc,
 }
 
 static void
-wsi_x11_connection_destroy(const VkAllocationCallbacks *alloc,
+wsi_x11_connection_destroy(struct wsi_device *wsi_dev,
                            struct wsi_x11_connection *conn)
 {
-   vk_free(alloc, conn);
+   vk_free(&wsi_dev->instance_alloc, conn);
 }
 
 static bool
@@ -200,7 +224,6 @@ wsi_x11_check_for_dri3(struct wsi_x11_connection *wsi_conn)
 
 static struct wsi_x11_connection *
 wsi_x11_get_connection(struct wsi_device *wsi_dev,
-                      const VkAllocationCallbacks *alloc,
                        xcb_connection_t *conn)
 {
    struct wsi_x11 *wsi =
@@ -216,7 +239,7 @@ wsi_x11_get_connection(struct wsi_device *wsi_dev,
       pthread_mutex_unlock(&wsi->mutex);
 
       struct wsi_x11_connection *wsi_conn =
-         wsi_x11_connection_create(alloc, conn);
+         wsi_x11_connection_create(wsi_dev, conn);
       if (!wsi_conn)
          return NULL;
 
@@ -225,7 +248,7 @@ wsi_x11_get_connection(struct wsi_device *wsi_dev,
       entry = _mesa_hash_table_search(wsi->connections, conn);
       if (entry) {
          /* Oops, someone raced us to it */
-         wsi_x11_connection_destroy(alloc, wsi_conn);
+         wsi_x11_connection_destroy(wsi_dev, wsi_conn);
       } else {
          entry = _mesa_hash_table_insert(wsi->connections, conn, wsi_conn);
       }
@@ -351,15 +374,12 @@ visual_has_alpha(xcb_visualtype_t *visual, unsigned depth)
 
 VkBool32 wsi_get_physical_device_xcb_presentation_support(
     struct wsi_device *wsi_device,
-    VkAllocationCallbacks *alloc,
     uint32_t                                    queueFamilyIndex,
-    int fd,
-    bool can_handle_different_gpu,
     xcb_connection_t*                           connection,
     xcb_visualid_t                              visual_id)
 {
    struct wsi_x11_connection *wsi_conn =
-      wsi_x11_get_connection(wsi_device, alloc, connection);
+      wsi_x11_get_connection(wsi_device, connection);
 
    if (!wsi_conn)
       return false;
@@ -367,10 +387,6 @@ VkBool32 wsi_get_physical_device_xcb_presentation_support(
    if (!wsi_x11_check_for_dri3(wsi_conn))
       return false;
 
-   if (!can_handle_different_gpu)
-      if (!wsi_x11_check_dri3_compatible(connection, fd))
-         return false;
-
    unsigned visual_depth;
    if (!connection_get_visualtype(connection, visual_id, &visual_depth))
       return false;
@@ -402,16 +418,14 @@ x11_surface_get_window(VkIcdSurfaceBase *icd_surface)
 static VkResult
 x11_surface_get_support(VkIcdSurfaceBase *icd_surface,
                         struct wsi_device *wsi_device,
-                        const VkAllocationCallbacks *alloc,
                         uint32_t queueFamilyIndex,
-                        int local_fd,
                         VkBool32* pSupported)
 {
    xcb_connection_t *conn = x11_surface_get_connection(icd_surface);
    xcb_window_t window = x11_surface_get_window(icd_surface);
 
    struct wsi_x11_connection *wsi_conn =
-      wsi_x11_get_connection(wsi_device, alloc, conn);
+      wsi_x11_get_connection(wsi_device, conn);
    if (!wsi_conn)
       return VK_ERROR_OUT_OF_HOST_MEMORY;
 
@@ -437,6 +451,7 @@ x11_surface_get_support(VkIcdSurfaceBase *icd_surface,
 
 static VkResult
 x11_surface_get_capabilities(VkIcdSurfaceBase *icd_surface,
+                             struct wsi_device *wsi_device,
                              VkSurfaceCapabilitiesKHR *caps)
 {
    xcb_connection_t *conn = x11_surface_get_connection(icd_surface);
@@ -470,8 +485,10 @@ x11_surface_get_capabilities(VkIcdSurfaceBase *icd_surface,
        */
       caps->currentExtent = (VkExtent2D) { -1, -1 };
       caps->minImageExtent = (VkExtent2D) { 1, 1 };
-      /* This is the maximum supported size on Intel */
-      caps->maxImageExtent = (VkExtent2D) { 1 << 14, 1 << 14 };
+      caps->maxImageExtent = (VkExtent2D) {
+         wsi_device->maxImageDimension2D,
+         wsi_device->maxImageDimension2D,
+      };
    }
    free(err);
    free(geom);
@@ -501,6 +518,7 @@ x11_surface_get_capabilities(VkIcdSurfaceBase *icd_surface,
       VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
       VK_IMAGE_USAGE_SAMPLED_BIT |
       VK_IMAGE_USAGE_TRANSFER_DST_BIT |
+      VK_IMAGE_USAGE_STORAGE_BIT |
       VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
 
    return VK_SUCCESS;
@@ -508,12 +526,13 @@ x11_surface_get_capabilities(VkIcdSurfaceBase *icd_surface,
 
 static VkResult
 x11_surface_get_capabilities2(VkIcdSurfaceBase *icd_surface,
+                              struct wsi_device *wsi_device,
                               const void *info_next,
                               VkSurfaceCapabilities2KHR *caps)
 {
    assert(caps->sType == VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_KHR);
 
-   return x11_surface_get_capabilities(icd_surface, &caps->surfaceCapabilities);
+   return x11_surface_get_capabilities(icd_surface, wsi_device, &caps->surfaceCapabilities);
 }
 
 static VkResult
@@ -571,6 +590,64 @@ x11_surface_get_present_modes(VkIcdSurfaceBase *surface,
       VK_INCOMPLETE : VK_SUCCESS;
 }
 
+static bool
+x11_surface_is_local_to_gpu(struct wsi_device *wsi_dev,
+                            xcb_connection_t *conn)
+{
+   struct wsi_x11_connection *wsi_conn =
+      wsi_x11_get_connection(wsi_dev, conn);
+
+   if (!wsi_conn)
+      return false;
+
+   if (!wsi_x11_check_for_dri3(wsi_conn))
+      return false;
+
+   if (!wsi_x11_check_dri3_compatible(wsi_dev, conn))
+      return false;
+
+   return true;
+}
+
+static VkResult
+x11_surface_get_present_rectangles(VkIcdSurfaceBase *icd_surface,
+                                   struct wsi_device *wsi_device,
+                                   uint32_t* pRectCount,
+                                   VkRect2D* pRects)
+{
+   xcb_connection_t *conn = x11_surface_get_connection(icd_surface);
+   xcb_window_t window = x11_surface_get_window(icd_surface);
+   VK_OUTARRAY_MAKE(out, pRects, pRectCount);
+
+   if (x11_surface_is_local_to_gpu(wsi_device, conn)) {
+      vk_outarray_append(&out, rect) {
+         xcb_generic_error_t *err = NULL;
+         xcb_get_geometry_cookie_t geom_cookie = xcb_get_geometry(conn, window);
+         xcb_get_geometry_reply_t *geom =
+            xcb_get_geometry_reply(conn, geom_cookie, &err);
+         free(err);
+         if (geom) {
+            *rect = (VkRect2D) {
+               .offset = { 0, 0 },
+               .extent = { geom->width, geom->height },
+            };
+         } else {
+            /* This can happen if the client didn't wait for the configure event
+             * to come back from the compositor.  In that case, we don't know the
+             * size of the window so we just return valid "I don't know" stuff.
+             */
+            *rect = (VkRect2D) {
+               .offset = { 0, 0 },
+               .extent = { -1, -1 },
+            };
+         }
+         free(geom);
+      }
+   }
+
+   return vk_outarray_status(&out);
+}
+
 VkResult wsi_create_xcb_surface(const VkAllocationCallbacks *pAllocator,
                                const VkXcbSurfaceCreateInfoKHR *pCreateInfo,
                                VkSurfaceKHR *pSurface)
@@ -620,6 +697,8 @@ struct x11_image {
 struct x11_swapchain {
    struct wsi_swapchain                        base;
 
+   bool                                         has_dri3_modifiers;
+
    xcb_connection_t *                           conn;
    xcb_window_t                                 window;
    xcb_gc_t                                     gc;
@@ -634,6 +713,7 @@ struct x11_swapchain {
 
    bool                                         threaded;
    VkResult                                     status;
+   xcb_present_complete_mode_t                  last_present_mode;
    struct wsi_queue                             present_queue;
    struct wsi_queue                             acquire_queue;
    pthread_t                                    queue_manager;
@@ -667,6 +747,14 @@ x11_swapchain_result(struct x11_swapchain *chain, VkResult result)
    if (result == VK_TIMEOUT || result == VK_NOT_READY)
       return result;
 
+   /* Suboptimal isn't an error, but is a status which sticks to the swapchain
+    * and is always returned rather than success.
+    */
+   if (result == VK_SUBOPTIMAL_KHR) {
+      chain->status = result;
+      return result;
+   }
+
    /* No changes, so return the last status. */
    return chain->status;
 }
@@ -715,7 +803,28 @@ x11_handle_dri3_present_event(struct x11_swapchain *chain,
       xcb_present_complete_notify_event_t *complete = (void *) event;
       if (complete->kind == XCB_PRESENT_COMPLETE_KIND_PIXMAP)
          chain->last_present_msc = complete->msc;
-      break;
+
+      VkResult result = VK_SUCCESS;
+
+      /* The winsys is now trying to flip directly and cannot due to our
+       * configuration. Request the user reallocate.
+       */
+#ifdef HAVE_DRI3_MODIFIERS
+      if (complete->mode == XCB_PRESENT_COMPLETE_MODE_SUBOPTIMAL_COPY &&
+          chain->last_present_mode != XCB_PRESENT_COMPLETE_MODE_SUBOPTIMAL_COPY)
+         result = VK_SUBOPTIMAL_KHR;
+#endif
+
+      /* When we go from flipping to copying, the odds are very likely that
+       * we could reallocate in a more optimal way if we didn't have to care
+       * about scanout, so we always do this.
+       */
+      if (complete->mode == XCB_PRESENT_COMPLETE_MODE_COPY &&
+          chain->last_present_mode == XCB_PRESENT_COMPLETE_MODE_FLIP)
+         result = VK_SUBOPTIMAL_KHR;
+
+      chain->last_present_mode = complete->mode;
+      return result;
    }
 
    default:
@@ -851,6 +960,11 @@ x11_present_to_x11(struct x11_swapchain *chain, uint32_t image_index,
    if (chain->base.present_mode == VK_PRESENT_MODE_IMMEDIATE_KHR)
       options |= XCB_PRESENT_OPTION_ASYNC;
 
+#ifdef HAVE_DRI3_MODIFIERS
+   if (chain->has_dri3_modifiers)
+      options |= XCB_PRESENT_OPTION_SUBOPTIMAL;
+#endif
+
    xshmfence_reset(image->shm_fence);
 
    ++chain->send_sbc;
@@ -880,11 +994,11 @@ x11_present_to_x11(struct x11_swapchain *chain, uint32_t image_index,
 
 static VkResult
 x11_acquire_next_image(struct wsi_swapchain *anv_chain,
-                       uint64_t timeout,
-                       VkSemaphore semaphore,
+                       const VkAcquireNextImageInfoKHR *info,
                        uint32_t *image_index)
 {
    struct x11_swapchain *chain = (struct x11_swapchain *)anv_chain;
+   uint64_t timeout = info->timeout;
 
    if (chain->threaded) {
       return x11_acquire_next_image_from_queue(chain, image_index, timeout);
@@ -912,7 +1026,7 @@ static void *
 x11_manage_fifo_queues(void *state)
 {
    struct x11_swapchain *chain = state;
-   VkResult result;
+   VkResult result = VK_SUCCESS;
 
    assert(chain->base.present_mode == VK_PRESENT_MODE_FIFO_KHR);
 
@@ -923,7 +1037,7 @@ x11_manage_fifo_queues(void *state)
        * before that point so the client should be able to acquire any image
        * other than the currently presented one.
        */
-      uint32_t image_index;
+      uint32_t image_index = 0;
       result = wsi_queue_pull(&chain->present_queue, &image_index, INT64_MAX);
       assert(result != VK_TIMEOUT);
       if (result < 0) {
@@ -956,7 +1070,7 @@ x11_manage_fifo_queues(void *state)
    }
 
 fail:
-   result = x11_swapchain_result(chain, result);
+   x11_swapchain_result(chain, result);
    wsi_queue_push(&chain->acquire_queue, UINT32_MAX);
 
    return NULL;
@@ -966,38 +1080,73 @@ static VkResult
 x11_image_init(VkDevice device_h, struct x11_swapchain *chain,
                const VkSwapchainCreateInfoKHR *pCreateInfo,
                const VkAllocationCallbacks* pAllocator,
-               struct x11_image *image)
+               const uint64_t *const *modifiers,
+               const uint32_t *num_modifiers,
+               int num_tranches, struct x11_image *image)
 {
    xcb_void_cookie_t cookie;
    VkResult result;
    uint32_t bpp = 32;
 
    if (chain->base.use_prime_blit) {
-      result = wsi_create_prime_image(&chain->base, pCreateInfo, &image->base);
+      bool use_modifier = num_tranches > 0;
+      result = wsi_create_prime_image(&chain->base, pCreateInfo, use_modifier, &image->base);
    } else {
       result = wsi_create_native_image(&chain->base, pCreateInfo,
-                                       0, NULL, NULL, &image->base);
+                                       num_tranches, num_modifiers, modifiers,
+                                       &image->base);
    }
    if (result < 0)
       return result;
 
    image->pixmap = xcb_generate_id(chain->conn);
 
-   /* Without passing modifiers, we can't have multi-plane RGB images. */
-   assert(image->base.num_planes == 1);
-
-   cookie =
-      xcb_dri3_pixmap_from_buffer_checked(chain->conn,
-                                          image->pixmap,
-                                          chain->window,
-                                          image->base.sizes[0],
-                                          pCreateInfo->imageExtent.width,
-                                          pCreateInfo->imageExtent.height,
-                                          image->base.row_pitches[0],
-                                          chain->depth, bpp,
-                                          image->base.fds[0]);
+#ifdef HAVE_DRI3_MODIFIERS
+   if (image->base.drm_modifier != DRM_FORMAT_MOD_INVALID) {
+      /* If the image has a modifier, we must have DRI3 v1.2. */
+      assert(chain->has_dri3_modifiers);
+
+      cookie =
+         xcb_dri3_pixmap_from_buffers_checked(chain->conn,
+                                              image->pixmap,
+                                              chain->window,
+                                              image->base.num_planes,
+                                              pCreateInfo->imageExtent.width,
+                                              pCreateInfo->imageExtent.height,
+                                              image->base.row_pitches[0],
+                                              image->base.offsets[0],
+                                              image->base.row_pitches[1],
+                                              image->base.offsets[1],
+                                              image->base.row_pitches[2],
+                                              image->base.offsets[2],
+                                              image->base.row_pitches[3],
+                                              image->base.offsets[3],
+                                              chain->depth, bpp,
+                                              image->base.drm_modifier,
+                                              image->base.fds);
+   } else
+#endif
+   {
+      /* Without passing modifiers, we can't have multi-plane RGB images. */
+      assert(image->base.num_planes == 1);
+
+      cookie =
+         xcb_dri3_pixmap_from_buffer_checked(chain->conn,
+                                             image->pixmap,
+                                             chain->window,
+                                             image->base.sizes[0],
+                                             pCreateInfo->imageExtent.width,
+                                             pCreateInfo->imageExtent.height,
+                                             image->base.row_pitches[0],
+                                             chain->depth, bpp,
+                                             image->base.fds[0]);
+   }
+
    xcb_discard_reply(chain->conn, cookie.sequence);
-   image->base.fds[0] = -1; /* XCB has now taken ownership of the FD */
+
+   /* XCB has now taken ownership of the FDs. */
+   for (int i = 0; i < image->base.num_planes; i++)
+      image->base.fds[i] = -1;
 
    int fence_fd = xshmfence_alloc_shm();
    if (fence_fd < 0)
@@ -1048,6 +1197,83 @@ x11_image_finish(struct x11_swapchain *chain,
    wsi_destroy_image(&chain->base, &image->base);
 }
 
+static void
+wsi_x11_get_dri3_modifiers(struct wsi_x11_connection *wsi_conn,
+                           xcb_connection_t *conn, xcb_window_t window,
+                           uint8_t depth, uint8_t bpp,
+                           VkCompositeAlphaFlagsKHR vk_alpha,
+                           uint64_t **modifiers_in, uint32_t *num_modifiers_in,
+                           uint32_t *num_tranches_in,
+                           const VkAllocationCallbacks *pAllocator)
+{
+   if (!wsi_conn->has_dri3_modifiers)
+      goto out;
+
+#ifdef HAVE_DRI3_MODIFIERS
+   xcb_generic_error_t *error = NULL;
+   xcb_dri3_get_supported_modifiers_cookie_t mod_cookie =
+      xcb_dri3_get_supported_modifiers(conn, window, depth, bpp);
+   xcb_dri3_get_supported_modifiers_reply_t *mod_reply =
+      xcb_dri3_get_supported_modifiers_reply(conn, mod_cookie, &error);
+   free(error);
+
+   if (!mod_reply || (mod_reply->num_window_modifiers == 0 &&
+                      mod_reply->num_screen_modifiers == 0)) {
+      free(mod_reply);
+      goto out;
+   }
+
+   uint32_t n = 0;
+   uint32_t counts[2];
+   uint64_t *modifiers[2];
+
+   if (mod_reply->num_window_modifiers) {
+      counts[n] = mod_reply->num_window_modifiers;
+      modifiers[n] = vk_alloc(pAllocator,
+                              counts[n] * sizeof(uint64_t),
+                              8, VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
+      if (!modifiers[n]) {
+         free(mod_reply);
+         goto out;
+      }
+
+      memcpy(modifiers[n],
+             xcb_dri3_get_supported_modifiers_window_modifiers(mod_reply),
+             counts[n] * sizeof(uint64_t));
+      n++;
+   }
+
+   if (mod_reply->num_screen_modifiers) {
+      counts[n] = mod_reply->num_screen_modifiers;
+      modifiers[n] = vk_alloc(pAllocator,
+                              counts[n] * sizeof(uint64_t),
+                              8, VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
+      if (!modifiers[n]) {
+        if (n > 0)
+            vk_free(pAllocator, modifiers[0]);
+         free(mod_reply);
+         goto out;
+      }
+
+      memcpy(modifiers[n],
+             xcb_dri3_get_supported_modifiers_screen_modifiers(mod_reply),
+             counts[n] * sizeof(uint64_t));
+      n++;
+   }
+
+   for (int i = 0; i < n; i++) {
+      modifiers_in[i] = modifiers[i];
+      num_modifiers_in[i] = counts[i];
+   }
+   *num_tranches_in = n;
+
+   free(mod_reply);
+   return;
+#endif
+out:
+   *num_tranches_in = 0;
+}
+
 static VkResult
 x11_swapchain_destroy(struct wsi_swapchain *anv_chain,
                       const VkAllocationCallbacks *pAllocator)
@@ -1055,9 +1281,6 @@ x11_swapchain_destroy(struct wsi_swapchain *anv_chain,
    struct x11_swapchain *chain = (struct x11_swapchain *)anv_chain;
    xcb_void_cookie_t cookie;
 
-   for (uint32_t i = 0; i < chain->base.image_count; i++)
-      x11_image_finish(chain, pAllocator, &chain->images[i]);
-
    if (chain->threaded) {
       chain->status = VK_ERROR_OUT_OF_DATE_KHR;
       /* Push a UINT32_MAX to wake up the manager */
@@ -1067,6 +1290,9 @@ x11_swapchain_destroy(struct wsi_swapchain *anv_chain,
       wsi_queue_destroy(&chain->present_queue);
    }
 
+   for (uint32_t i = 0; i < chain->base.image_count; i++)
+      x11_image_finish(chain, pAllocator, &chain->images[i]);
+
    xcb_unregister_for_special_event(chain->conn, chain->special_event);
    cookie = xcb_present_select_input_checked(chain->conn, chain->event_id,
                                              chain->window,
@@ -1084,7 +1310,6 @@ static VkResult
 x11_surface_create_swapchain(VkIcdSurfaceBase *icd_surface,
                              VkDevice device,
                              struct wsi_device *wsi_device,
-                             int local_fd,
                              const VkSwapchainCreateInfoKHR *pCreateInfo,
                              const VkAllocationCallbacks* pAllocator,
                              struct wsi_swapchain **swapchain_out)
@@ -1097,8 +1322,13 @@ x11_surface_create_swapchain(VkIcdSurfaceBase *icd_surface,
 
    const unsigned num_images = pCreateInfo->minImageCount;
 
-   /* Check for whether or not we have a window up-front */
    xcb_connection_t *conn = x11_surface_get_connection(icd_surface);
+   struct wsi_x11_connection *wsi_conn =
+      wsi_x11_get_connection(wsi_device, conn);
+   if (!wsi_conn)
+      return VK_ERROR_OUT_OF_HOST_MEMORY;
+
+   /* Check for whether or not we have a window up-front */
    xcb_window_t window = x11_surface_get_window(icd_surface);
    xcb_get_geometry_reply_t *geometry =
       xcb_get_geometry_reply(conn, xcb_get_geometry(conn, window), NULL);
@@ -1132,8 +1362,21 @@ x11_surface_create_swapchain(VkIcdSurfaceBase *icd_surface,
    chain->last_present_msc = 0;
    chain->threaded = false;
    chain->status = VK_SUCCESS;
+   chain->has_dri3_modifiers = wsi_conn->has_dri3_modifiers;
 
-   if (!wsi_x11_check_dri3_compatible(conn, local_fd))
+   /* If we are reallocating from an old swapchain, then we inherit its
+    * last completion mode, to ensure we don't get into reallocation
+    * cycles. If we are starting anew, we set 'COPY', as that is the only
+    * mode which provokes reallocation when anything changes, to make
+    * sure we have the most optimal allocation.
+    */
+   struct x11_swapchain *old_chain = (void *)(intptr_t) pCreateInfo->oldSwapchain;
+   if (old_chain)
+      chain->last_present_mode = old_chain->last_present_mode;
+   else
+      chain->last_present_mode = XCB_PRESENT_COMPLETE_MODE_COPY;
+
+   if (!wsi_x11_check_dri3_compatible(wsi_device, conn))
        chain->base.use_prime_blit = true;
 
    chain->event_id = xcb_generate_id(chain->conn);
@@ -1163,9 +1406,20 @@ x11_surface_create_swapchain(VkIcdSurfaceBase *icd_surface,
                           (uint32_t []) { 0 });
    xcb_discard_reply(chain->conn, cookie.sequence);
 
+   uint64_t *modifiers[2] = {NULL, NULL};
+   uint32_t num_modifiers[2] = {0, 0};
+   uint32_t num_tranches = 0;
+   if (wsi_device->supports_modifiers)
+      wsi_x11_get_dri3_modifiers(wsi_conn, conn, window, chain->depth, 32,
+                                 pCreateInfo->compositeAlpha,
+                                 modifiers, num_modifiers, &num_tranches,
+                                 pAllocator);
+
    uint32_t image = 0;
    for (; image < chain->base.image_count; image++) {
       result = x11_image_init(device, chain, pCreateInfo, pAllocator,
+                              (const uint64_t *const *)modifiers,
+                              num_modifiers, num_tranches,
                               &chain->images[image]);
       if (result != VK_SUCCESS)
          goto fail_init_images;
@@ -1202,6 +1456,8 @@ x11_surface_create_swapchain(VkIcdSurfaceBase *icd_surface,
       }
    }
 
+   for (int i = 0; i < ARRAY_SIZE(modifiers); i++)
+      vk_free(pAllocator, modifiers[i]);
    *swapchain_out = &chain->base;
 
    return VK_SUCCESS;
@@ -1210,6 +1466,9 @@ fail_init_images:
    for (uint32_t j = 0; j < image; j++)
       x11_image_finish(chain, pAllocator, &chain->images[j]);
 
+   for (int i = 0; i < ARRAY_SIZE(modifiers); i++)
+      vk_free(pAllocator, modifiers[i]);
+
 fail_register:
    xcb_unregister_for_special_event(chain->conn, chain->special_event);
 
@@ -1255,11 +1514,11 @@ 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_formats = x11_surface_get_formats;
    wsi->base.get_formats2 = x11_surface_get_formats2;
    wsi->base.get_present_modes = x11_surface_get_present_modes;
+   wsi->base.get_present_rectangles = x11_surface_get_present_rectangles;
    wsi->base.create_swapchain = x11_surface_create_swapchain;
 
    wsi_device->wsi[VK_ICD_WSI_PLATFORM_XCB] = &wsi->base;
@@ -1286,9 +1545,8 @@ wsi_x11_finish_wsi(struct wsi_device *wsi_device,
       (struct wsi_x11 *)wsi_device->wsi[VK_ICD_WSI_PLATFORM_XCB];
 
    if (wsi) {
-      struct hash_entry *entry;
       hash_table_foreach(wsi->connections, entry)
-         wsi_x11_connection_destroy(alloc, entry->data);
+         wsi_x11_connection_destroy(wsi_device, entry->data);
 
       _mesa_hash_table_destroy(wsi->connections, NULL);