vulkan/wsi/x11: Consistently update and return swapchain status
[mesa.git] / src / vulkan / wsi / wsi_common_x11.c
index 51c103e967d72306ff1410736f48eaa3716fc639..e5e719e1d507d6ca4459d61bfa72b361fc79fefb 100644 (file)
@@ -39,7 +39,7 @@
 #include "util/hash_table.h"
 
 #include "vk_util.h"
-#include "wsi_common.h"
+#include "wsi_common_private.h"
 #include "wsi_common_x11.h"
 #include "wsi_common_queue.h"
 
@@ -405,7 +405,6 @@ x11_surface_get_support(VkIcdSurfaceBase *icd_surface,
                         const VkAllocationCallbacks *alloc,
                         uint32_t queueFamilyIndex,
                         int local_fd,
-                        bool can_handle_different_gpu,
                         VkBool32* pSupported)
 {
    xcb_connection_t *conn = x11_surface_get_connection(icd_surface);
@@ -421,10 +420,6 @@ x11_surface_get_support(VkIcdSurfaceBase *icd_surface,
       return VK_SUCCESS;
    }
 
-   if (!can_handle_different_gpu)
-      if (!wsi_x11_check_dri3_compatible(conn, local_fd))
-         return false;
-
    unsigned visual_depth;
    if (!get_visualtype_for_window(conn, window, &visual_depth)) {
       *pSupported = false;
@@ -616,7 +611,6 @@ VkResult wsi_create_xlib_surface(const VkAllocationCallbacks *pAllocator,
 
 struct x11_image {
    struct wsi_image                          base;
-   struct wsi_image                          linear_base;
    xcb_pixmap_t                              pixmap;
    bool                                      busy;
    struct xshmfence *                        shm_fence;
@@ -647,41 +641,46 @@ struct x11_swapchain {
    struct x11_image                             images[0];
 };
 
+/**
+ * Update the swapchain status with the result of an operation, and return
+ * the combined status. The chain status will eventually be returned from
+ * AcquireNextImage and QueuePresent.
+ *
+ * We make sure to 'stick' more pessimistic statuses: an out-of-date error
+ * is permanent once seen, and every subsequent call will return this. If
+ * this has not been seen, success will be returned.
+ */
 static VkResult
-x11_get_images(struct wsi_swapchain *anv_chain,
-               uint32_t* pCount, VkImage *pSwapchainImages)
+x11_swapchain_result(struct x11_swapchain *chain, VkResult result)
 {
-   struct x11_swapchain *chain = (struct x11_swapchain *)anv_chain;
-   uint32_t ret_count;
-   VkResult result;
-
-   if (pSwapchainImages == NULL) {
-      *pCount = chain->base.image_count;
-      return VK_SUCCESS;
-   }
+   /* Prioritise returning existing errors for consistency. */
+   if (chain->status < 0)
+      return chain->status;
 
-   result = VK_SUCCESS;
-   ret_count = chain->base.image_count;
-   if (chain->base.image_count > *pCount) {
-     ret_count = *pCount;
-     result = VK_INCOMPLETE;
+   /* If we have a new error, mark it as permanent on the chain and return. */
+   if (result < 0) {
+      chain->status = result;
+      return result;
    }
 
-   for (uint32_t i = 0; i < ret_count; i++)
-      pSwapchainImages[i] = chain->images[i].base.image;
+   /* Return temporary errors, but don't persist them. */
+   if (result == VK_TIMEOUT || result == VK_NOT_READY)
+      return result;
 
-   return result;
+   /* No changes, so return the last status. */
+   return chain->status;
 }
 
-static void
-x11_get_image_and_linear(struct wsi_swapchain *drv_chain,
-                         int imageIndex, VkImage *image, VkImage *linear_image)
+static struct wsi_image *
+x11_get_wsi_image(struct wsi_swapchain *wsi_chain, uint32_t image_index)
 {
-   struct x11_swapchain *chain = (struct x11_swapchain *)drv_chain;
-   *image = chain->images[imageIndex].base.image;
-   *linear_image = chain->images[imageIndex].linear_base.image;
+   struct x11_swapchain *chain = (struct x11_swapchain *)wsi_chain;
+   return &chain->images[image_index].base;
 }
 
+/**
+ * Process an X11 Present event. Does not update chain->status.
+ */
 static VkResult
 x11_handle_dri3_present_event(struct x11_swapchain *chain,
                               xcb_present_generic_event_t *event)
@@ -760,7 +759,7 @@ x11_acquire_next_image_poll_x11(struct x11_swapchain *chain,
             xshmfence_await(chain->images[i].shm_fence);
             *image_index = i;
             chain->images[i].busy = true;
-            return VK_SUCCESS;
+            return x11_swapchain_result(chain, VK_SUCCESS);
          }
       }
 
@@ -769,13 +768,13 @@ x11_acquire_next_image_poll_x11(struct x11_swapchain *chain,
       if (timeout == UINT64_MAX) {
          event = xcb_wait_for_special_event(chain->conn, chain->special_event);
          if (!event)
-            return VK_ERROR_OUT_OF_DATE_KHR;
+            return x11_swapchain_result(chain, VK_ERROR_OUT_OF_DATE_KHR);
       } else {
          event = xcb_poll_for_special_event(chain->conn, chain->special_event);
          if (!event) {
             int ret;
             if (timeout == 0)
-               return VK_NOT_READY;
+               return x11_swapchain_result(chain, VK_NOT_READY);
 
             atimeout = wsi_get_absolute_timeout(timeout);
 
@@ -783,9 +782,9 @@ x11_acquire_next_image_poll_x11(struct x11_swapchain *chain,
             pfds.events = POLLIN;
             ret = poll(&pfds, 1, timeout / 1000 / 1000);
             if (ret == 0)
-               return VK_TIMEOUT;
+               return x11_swapchain_result(chain, VK_TIMEOUT);
             if (ret == -1)
-               return VK_ERROR_OUT_OF_DATE_KHR;
+               return x11_swapchain_result(chain, VK_ERROR_OUT_OF_DATE_KHR);
 
             /* If a non-special event happens, the fd will still
              * poll. So recalculate the timeout now just in case.
@@ -799,10 +798,13 @@ x11_acquire_next_image_poll_x11(struct x11_swapchain *chain,
          }
       }
 
+      /* Update the swapchain status here. We may catch non-fatal errors here,
+       * in which case we need to update the status and continue.
+       */
       VkResult result = x11_handle_dri3_present_event(chain, (void *)event);
       free(event);
-      if (result != VK_SUCCESS)
-         return result;
+      if (result < 0)
+         return x11_swapchain_result(chain, result);
    }
 }
 
@@ -815,9 +817,13 @@ x11_acquire_next_image_from_queue(struct x11_swapchain *chain,
    uint32_t image_index;
    VkResult result = wsi_queue_pull(&chain->acquire_queue,
                                     &image_index, timeout);
-   if (result != VK_SUCCESS) {
-      return result;
-   } else if (chain->status != VK_SUCCESS) {
+   if (result < 0 || result == VK_TIMEOUT) {
+      /* On error, the thread has shut down, so safe to update chain->status.
+       * Calling x11_swapchain_result with VK_TIMEOUT won't modify
+       * chain->status so that is also safe.
+       */
+      return x11_swapchain_result(chain, result);
+   } else if (chain->status < 0) {
       return chain->status;
    }
 
@@ -826,7 +832,7 @@ x11_acquire_next_image_from_queue(struct x11_swapchain *chain,
 
    *image_index_out = image_index;
 
-   return VK_SUCCESS;
+   return chain->status;
 }
 
 static VkResult
@@ -869,7 +875,7 @@ x11_present_to_x11(struct x11_swapchain *chain, uint32_t image_index,
 
    xcb_flush(chain->conn);
 
-   return VK_SUCCESS;
+   return x11_swapchain_result(chain, VK_SUCCESS);
 }
 
 static VkResult
@@ -910,7 +916,7 @@ x11_manage_fifo_queues(void *state)
 
    assert(chain->base.present_mode == VK_PRESENT_MODE_FIFO_KHR);
 
-   while (chain->status == VK_SUCCESS) {
+   while (chain->status >= 0) {
       /* It should be safe to unconditionally block here.  Later in the loop
        * we blocks until the previous present has landed on-screen.  At that
        * point, we should have received IDLE_NOTIFY on all images presented
@@ -919,32 +925,38 @@ x11_manage_fifo_queues(void *state)
        */
       uint32_t image_index;
       result = wsi_queue_pull(&chain->present_queue, &image_index, INT64_MAX);
-      if (result != VK_SUCCESS) {
+      assert(result != VK_TIMEOUT);
+      if (result < 0) {
          goto fail;
-      } else if (chain->status != VK_SUCCESS) {
+      } else if (chain->status < 0) {
+         /* The status can change underneath us if the swapchain is destroyed
+          * from another thread.
+          */
          return NULL;
       }
 
       uint64_t target_msc = chain->last_present_msc + 1;
       result = x11_present_to_x11(chain, image_index, target_msc);
-      if (result != VK_SUCCESS)
+      if (result < 0)
          goto fail;
 
       while (chain->last_present_msc < target_msc) {
          xcb_generic_event_t *event =
             xcb_wait_for_special_event(chain->conn, chain->special_event);
-         if (!event)
+         if (!event) {
+            result = VK_ERROR_OUT_OF_DATE_KHR;
             goto fail;
+         }
 
          result = x11_handle_dri3_present_event(chain, (void *)event);
          free(event);
-         if (result != VK_SUCCESS)
+         if (result < 0)
             goto fail;
       }
    }
 
 fail:
-   chain->status = result;
+   result = x11_swapchain_result(chain, result);
    wsi_queue_push(&chain->acquire_queue, UINT32_MAX);
 
    return NULL;
@@ -960,46 +972,32 @@ x11_image_init(VkDevice device_h, struct x11_swapchain *chain,
    VkResult result;
    uint32_t bpp = 32;
 
-   result = chain->base.image_fns->create_wsi_image(device_h,
-                                                    pCreateInfo,
-                                                    pAllocator,
-                                                    chain->base.needs_linear_copy,
-                                                    false,
-                                                    &image->base);
-   if (result != VK_SUCCESS)
-      return result;
-
-   if (chain->base.needs_linear_copy) {
-      result = chain->base.image_fns->create_wsi_image(device_h,
-                                                       pCreateInfo,
-                                                       pAllocator,
-                                                       chain->base.needs_linear_copy,
-                                                       true,
-                                                       &image->linear_base);
-
-      if (result != VK_SUCCESS) {
-         chain->base.image_fns->free_wsi_image(device_h, pAllocator,
-                                               &image->base);
-         return result;
-      }
+   if (chain->base.use_prime_blit) {
+      result = wsi_create_prime_image(&chain->base, pCreateInfo, &image->base);
+   } else {
+      result = wsi_create_native_image(&chain->base, pCreateInfo,
+                                       0, NULL, NULL, &image->base);
    }
+   if (result < 0)
+      return result;
 
    image->pixmap = xcb_generate_id(chain->conn);
 
-   struct wsi_image *image_ws =
-      chain->base.needs_linear_copy ? &image->linear_base : &image->base;
+   /* 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_ws->size,
+                                          image->base.sizes[0],
                                           pCreateInfo->imageExtent.width,
                                           pCreateInfo->imageExtent.height,
-                                          image_ws->row_pitch,
+                                          image->base.row_pitches[0],
                                           chain->depth, bpp,
-                                          image_ws->fd);
+                                          image->base.fds[0]);
    xcb_discard_reply(chain->conn, cookie.sequence);
-   image_ws->fd = -1; /* XCB has now taken ownership of the FD */
+   image->base.fds[0] = -1; /* XCB has now taken ownership of the FD */
 
    int fence_fd = xshmfence_alloc_shm();
    if (fence_fd < 0)
@@ -1028,11 +1026,7 @@ fail_pixmap:
    cookie = xcb_free_pixmap(chain->conn, image->pixmap);
    xcb_discard_reply(chain->conn, cookie.sequence);
 
-   if (chain->base.needs_linear_copy) {
-      chain->base.image_fns->free_wsi_image(device_h, pAllocator,
-                                            &image->linear_base);
-   }
-   chain->base.image_fns->free_wsi_image(device_h, pAllocator, &image->base);
+   wsi_destroy_image(&chain->base, &image->base);
 
    return result;
 }
@@ -1051,12 +1045,7 @@ x11_image_finish(struct x11_swapchain *chain,
    cookie = xcb_free_pixmap(chain->conn, image->pixmap);
    xcb_discard_reply(chain->conn, cookie.sequence);
 
-   if (chain->base.needs_linear_copy) {
-      chain->base.image_fns->free_wsi_image(chain->base.device, pAllocator,
-                                            &image->linear_base);
-   }
-   chain->base.image_fns->free_wsi_image(chain->base.device, pAllocator,
-                                         &image->base);
+   wsi_destroy_image(&chain->base, &image->base);
 }
 
 static VkResult
@@ -1084,6 +1073,8 @@ x11_swapchain_destroy(struct wsi_swapchain *anv_chain,
                                              XCB_PRESENT_EVENT_MASK_NO_EVENT);
    xcb_discard_reply(chain->conn, cookie.sequence);
 
+   wsi_swapchain_finish(&chain->base);
+
    vk_free(pAllocator, chain);
 
    return VK_SUCCESS;
@@ -1096,7 +1087,6 @@ x11_surface_create_swapchain(VkIcdSurfaceBase *icd_surface,
                              int local_fd,
                              const VkSwapchainCreateInfoKHR *pCreateInfo,
                              const VkAllocationCallbacks* pAllocator,
-                             const struct wsi_image_fns *image_fns,
                              struct wsi_swapchain **swapchain_out)
 {
    struct x11_swapchain *chain;
@@ -1123,13 +1113,15 @@ x11_surface_create_swapchain(VkIcdSurfaceBase *icd_surface,
    if (chain == NULL)
       return VK_ERROR_OUT_OF_HOST_MEMORY;
 
-   chain->base.device = device;
+   result = wsi_swapchain_init(wsi_device, &chain->base, device,
+                               pCreateInfo, pAllocator);
+   if (result != VK_SUCCESS)
+      goto fail_alloc;
+
    chain->base.destroy = x11_swapchain_destroy;
-   chain->base.get_images = x11_get_images;
-   chain->base.get_image_and_linear = x11_get_image_and_linear;
+   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.image_fns = image_fns;
    chain->base.present_mode = pCreateInfo->presentMode;
    chain->base.image_count = num_images;
    chain->conn = conn;
@@ -1141,10 +1133,8 @@ x11_surface_create_swapchain(VkIcdSurfaceBase *icd_surface,
    chain->threaded = false;
    chain->status = VK_SUCCESS;
 
-
-   chain->base.needs_linear_copy = false;
    if (!wsi_x11_check_dri3_compatible(conn, local_fd))
-       chain->base.needs_linear_copy = true;
+       chain->base.use_prime_blit = true;
 
    chain->event_id = xcb_generate_id(chain->conn);
    xcb_present_select_input(chain->conn, chain->event_id, chain->window,
@@ -1223,6 +1213,9 @@ fail_init_images:
 fail_register:
    xcb_unregister_for_special_event(chain->conn, chain->special_event);
 
+   wsi_swapchain_finish(&chain->base);
+
+fail_alloc:
    vk_free(pAllocator, chain);
 
    return result;