vulkan/wsi/x11: Consistently update and return swapchain status
[mesa.git] / src / vulkan / wsi / wsi_common_x11.c
index 35801947f8e2cb65ae9cdd319343f41697646551..e5e719e1d507d6ca4459d61bfa72b361fc79fefb 100644 (file)
@@ -38,8 +38,8 @@
 #include <xf86drm.h>
 #include "util/hash_table.h"
 
-#include "util/vk_util.h"
-#include "wsi_common.h"
+#include "vk_util.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;
@@ -511,6 +506,16 @@ x11_surface_get_capabilities(VkIcdSurfaceBase *icd_surface,
    return VK_SUCCESS;
 }
 
+static VkResult
+x11_surface_get_capabilities2(VkIcdSurfaceBase *icd_surface,
+                              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);
+}
+
 static VkResult
 x11_surface_get_formats(VkIcdSurfaceBase *surface,
                         struct wsi_device *wsi_device,
@@ -529,6 +534,26 @@ x11_surface_get_formats(VkIcdSurfaceBase *surface,
    return vk_outarray_status(&out);
 }
 
+static VkResult
+x11_surface_get_formats2(VkIcdSurfaceBase *surface,
+                        struct wsi_device *wsi_device,
+                        const void *info_next,
+                        uint32_t *pSurfaceFormatCount,
+                        VkSurfaceFormat2KHR *pSurfaceFormats)
+{
+   VK_OUTARRAY_MAKE(out, pSurfaceFormats, pSurfaceFormatCount);
+
+   for (unsigned i = 0; i < ARRAY_SIZE(formats); i++) {
+      vk_outarray_append(&out, f) {
+         assert(f->sType == VK_STRUCTURE_TYPE_SURFACE_FORMAT_2_KHR);
+         f->surfaceFormat.format = formats[i];
+         f->surfaceFormat.colorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR;
+      }
+   }
+
+   return vk_outarray_status(&out);
+}
+
 static VkResult
 x11_surface_get_present_modes(VkIcdSurfaceBase *surface,
                               uint32_t *pPresentModeCount,
@@ -585,10 +610,7 @@ VkResult wsi_create_xlib_surface(const VkAllocationCallbacks *pAllocator,
 }
 
 struct x11_image {
-   VkImage image;
-   VkImage linear_image; // for prime
-   VkDeviceMemory memory;
-   VkDeviceMemory linear_memory; // for prime
+   struct wsi_image                          base;
    xcb_pixmap_t                              pixmap;
    bool                                      busy;
    struct xshmfence *                        shm_fence;
@@ -619,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].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].image;
-   *linear_image = chain->images[imageIndex].linear_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)
@@ -732,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);
          }
       }
 
@@ -741,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);
 
@@ -755,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.
@@ -771,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);
    }
 }
 
@@ -787,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;
    }
 
@@ -798,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
@@ -841,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
@@ -882,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
@@ -891,31 +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);
-         if (result != VK_SUCCESS)
+         free(event);
+         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;
@@ -929,57 +970,34 @@ x11_image_init(VkDevice device_h, struct x11_swapchain *chain,
 {
    xcb_void_cookie_t cookie;
    VkResult result;
-   uint32_t row_pitch;
-   uint32_t offset;
    uint32_t bpp = 32;
-   int fd;
-   uint32_t size;
-
-   result = chain->base.image_fns->create_wsi_image(device_h,
-                                                    pCreateInfo,
-                                                    pAllocator,
-                                                    chain->base.needs_linear_copy,
-                                                    false,
-                                                    &image->image,
-                                                    &image->memory,
-                                                    &size,
-                                                    &offset,
-                                                    &row_pitch,
-                                                    &fd);
-   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_image,
-                                                       &image->linear_memory,
-                                                       &size,
-                                                       &offset,
-                                                       &row_pitch,
-                                                       &fd);
-      if (result != VK_SUCCESS) {
-         chain->base.image_fns->free_wsi_image(device_h, pAllocator,
-                                               image->image, image->memory);
-         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);
 
+   /* 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,
-                                          size,
+                                          image->base.sizes[0],
                                           pCreateInfo->imageExtent.width,
                                           pCreateInfo->imageExtent.height,
-                                          row_pitch,
-                                          chain->depth, bpp, fd);
+                                          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 */
 
    int fence_fd = xshmfence_alloc_shm();
    if (fence_fd < 0)
@@ -1008,12 +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_image, image->linear_memory);
-   }
-   chain->base.image_fns->free_wsi_image(device_h, pAllocator,
-                                         image->image, image->memory);
+   wsi_destroy_image(&chain->base, &image->base);
 
    return result;
 }
@@ -1032,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_image, image->linear_memory);
-   }
-   chain->base.image_fns->free_wsi_image(chain->base.device, pAllocator,
-                                        image->image, image->memory);
+   wsi_destroy_image(&chain->base, &image->base);
 }
 
 static VkResult
@@ -1065,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;
@@ -1077,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;
@@ -1088,43 +1097,44 @@ x11_surface_create_swapchain(VkIcdSurfaceBase *icd_surface,
 
    const unsigned num_images = pCreateInfo->minImageCount;
 
-   size_t size = sizeof(*chain) + num_images * sizeof(chain->images[0]);
-   chain = vk_alloc(pAllocator, size, 8,
-                      VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
-   if (chain == NULL)
-      return VK_ERROR_OUT_OF_HOST_MEMORY;
-
+   /* Check for whether or not we have a window up-front */
    xcb_connection_t *conn = x11_surface_get_connection(icd_surface);
    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);
-
    if (geometry == NULL)
       return VK_ERROR_SURFACE_LOST_KHR;
+   const uint32_t bit_depth = geometry->depth;
+   free(geometry);
+
+   size_t size = sizeof(*chain) + num_images * sizeof(chain->images[0]);
+   chain = vk_alloc(pAllocator, 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,
+                               pCreateInfo, pAllocator);
+   if (result != VK_SUCCESS)
+      goto fail_alloc;
 
-   chain->base.device = device;
    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;
    chain->window = window;
-   chain->depth = geometry->depth;
+   chain->depth = bit_depth;
    chain->extent = pCreateInfo->imageExtent;
    chain->send_sbc = 0;
    chain->last_present_msc = 0;
    chain->threaded = false;
    chain->status = VK_SUCCESS;
 
-   free(geometry);
-
-   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,
@@ -1203,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;
@@ -1243,7 +1256,9 @@ 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.create_swapchain = x11_surface_create_swapchain;