wsi/wayland: Implement EXT_swapchain_maintenance1.
authorHans-Kristian Arntzen <post@arntzen-software.no>
Wed, 30 Nov 2022 16:18:00 +0000 (17:18 +0100)
committerMarge Bot <emma+marge@anholt.net>
Fri, 3 Mar 2023 03:59:13 +0000 (03:59 +0000)
Signed-off-by: Hans-Kristian Arntzen <post@arntzen-software.no>
Reviewed-by: Joshua Ashton <joshua@froggi.es>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/20235>

src/vulkan/wsi/wsi_common_wayland.c

index 86d26a8..f12e2cc 100644 (file)
@@ -963,18 +963,56 @@ static const VkPresentModeKHR present_modes[] = {
    VK_PRESENT_MODE_FIFO_KHR,
 };
 
+static uint32_t
+wsi_wl_surface_get_min_image_count(const VkSurfacePresentModeEXT *present_mode)
+{
+   if (present_mode && (present_mode->presentMode == VK_PRESENT_MODE_FIFO_KHR ||
+                        present_mode->presentMode == VK_PRESENT_MODE_FIFO_RELAXED_KHR)) {
+      /* If we receive a FIFO present mode, only 2 images is required for forward progress.
+       * Performance with 2 images will be questionable, but we only allow it for applications
+       * using the new API, so we don't risk breaking any existing apps this way.
+       * Other ICDs expose 2 images here already. */
+       return 2;
+   } else {
+      /* For true mailbox mode, we need at least 4 images:
+       *  1) One to scan out from
+       *  2) One to have queued for scan-out
+       *  3) One to be currently held by the Wayland compositor
+       *  4) One to render to
+       */
+      return 4;
+   }
+}
+
+static uint32_t
+wsi_wl_surface_get_min_image_count_for_mode_group(const VkSwapchainPresentModesCreateInfoEXT *modes)
+{
+   /* If we don't provide the PresentModeCreateInfo struct, we must be backwards compatible,
+    * and assume that minImageCount is the default one, i.e. 4, which supports both FIFO and MAILBOX. */
+   if (!modes) {
+      return wsi_wl_surface_get_min_image_count(NULL);
+   }
+
+   uint32_t max_required = 0;
+   for (uint32_t i = 0; i < modes->presentModeCount; i++) {
+      const VkSurfacePresentModeEXT mode = {
+         VK_STRUCTURE_TYPE_SURFACE_PRESENT_MODE_EXT,
+         NULL,
+         modes->pPresentModes[i]
+      };
+      max_required = MAX2(max_required, wsi_wl_surface_get_min_image_count(&mode));
+   }
+
+   return max_required;
+}
+
 static VkResult
 wsi_wl_surface_get_capabilities(VkIcdSurfaceBase *surface,
                                 struct wsi_device *wsi_device,
+                                const VkSurfacePresentModeEXT *present_mode,
                                 VkSurfaceCapabilitiesKHR* caps)
 {
-   /* For true mailbox mode, we need at least 4 images:
-    *  1) One to scan out from
-    *  2) One to have queued for scan-out
-    *  3) One to be currently held by the Wayland compositor
-    *  4) One to render to
-    */
-   caps->minImageCount = 4;
+   caps->minImageCount = wsi_wl_surface_get_min_image_count(present_mode);
    /* There is no real maximum */
    caps->maxImageCount = 0;
 
@@ -1012,8 +1050,10 @@ wsi_wl_surface_get_capabilities2(VkIcdSurfaceBase *surface,
 {
    assert(caps->sType == VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_KHR);
 
+   const VkSurfacePresentModeEXT *present_mode = vk_find_struct_const(info_next, SURFACE_PRESENT_MODE_EXT);
+
    VkResult result =
-      wsi_wl_surface_get_capabilities(surface, wsi_device,
+      wsi_wl_surface_get_capabilities(surface, wsi_device, present_mode,
                                       &caps->surfaceCapabilities);
 
    vk_foreach_struct(ext, caps->pNext) {
@@ -1024,6 +1064,40 @@ wsi_wl_surface_get_capabilities2(VkIcdSurfaceBase *surface,
          break;
       }
 
+      case VK_STRUCTURE_TYPE_SURFACE_PRESENT_SCALING_CAPABILITIES_EXT: {
+         /* Unsupported. */
+         VkSurfacePresentScalingCapabilitiesEXT *scaling = (void *)ext;
+         scaling->supportedPresentScaling = 0;
+         scaling->supportedPresentGravityX = 0;
+         scaling->supportedPresentGravityY = 0;
+         scaling->minScaledImageExtent = caps->surfaceCapabilities.minImageExtent;
+         scaling->maxScaledImageExtent = caps->surfaceCapabilities.maxImageExtent;
+         break;
+      }
+
+      case VK_STRUCTURE_TYPE_SURFACE_PRESENT_MODE_COMPATIBILITY_EXT: {
+         /* Can easily toggle between FIFO and MAILBOX on Wayland. */
+         assert(present_mode);
+         VkSurfacePresentModeCompatibilityEXT *compat = (void *)ext;
+         if (compat->pPresentModes) {
+            VK_OUTARRAY_MAKE_TYPED(VkPresentModeKHR, modes, compat->pPresentModes, &compat->presentModeCount);
+            /* Must always return queried present mode even when truncating. */
+            vk_outarray_append_typed(VkPresentModeKHR, &modes, mode) {
+               *mode = present_mode->presentMode;
+            }
+            for (unsigned i = 0; i < ARRAY_SIZE(present_modes); i++) {
+               if (present_modes[i] != present_mode->presentMode) {
+                  vk_outarray_append_typed(VkPresentModeKHR, &modes, mode) {
+                     *mode = present_modes[i];
+                  }
+               }
+            }
+         } else {
+            compat->presentModeCount = ARRAY_SIZE(present_modes);
+         }
+         break;
+      }
+
       default:
          /* Ignored */
          break;
@@ -1461,6 +1535,27 @@ wsi_wl_swapchain_get_wsi_image(struct wsi_swapchain *wsi_chain,
 }
 
 static VkResult
+wsi_wl_swapchain_release_images(struct wsi_swapchain *wsi_chain,
+                                uint32_t count, const uint32_t *indices)
+{
+   struct wsi_wl_swapchain *chain = (struct wsi_wl_swapchain *)wsi_chain;
+   for (uint32_t i = 0; i < count; i++) {
+      uint32_t index = indices[i];
+      assert(chain->images[index].busy);
+      chain->images[index].busy = false;
+   }
+   return VK_SUCCESS;
+}
+
+static void
+wsi_wl_swapchain_set_present_mode(struct wsi_swapchain *wsi_chain,
+                                  VkPresentModeKHR mode)
+{
+   struct wsi_wl_swapchain *chain = (struct wsi_wl_swapchain *)wsi_chain;
+   chain->base.present_mode = mode;
+}
+
+static VkResult
 wsi_wl_swapchain_acquire_next_image(struct wsi_swapchain *wsi_chain,
                                     const VkAcquireNextImageInfoKHR *info,
                                     uint32_t *image_index)
@@ -1565,13 +1660,14 @@ wsi_wl_swapchain_queue_present(struct wsi_swapchain *wsi_chain,
       memcpy(image->shm_ptr, image->base.cpu_map,
              image->base.row_pitches[0] * chain->extent.height);
    }
-   if (chain->base.present_mode == VK_PRESENT_MODE_FIFO_KHR) {
-      while (!chain->fifo_ready) {
-         int ret = wl_display_dispatch_queue(wsi_wl_surface->display->wl_display,
-                                             wsi_wl_surface->display->queue);
-         if (ret < 0)
-            return VK_ERROR_OUT_OF_DATE_KHR;
-      }
+
+   /* For EXT_swapchain_maintenance1. We might have transitioned from FIFO to MAILBOX.
+    * In this case we need to let the FIFO request complete, before presenting MAILBOX. */
+   while (!chain->fifo_ready) {
+      int ret = wl_display_dispatch_queue(wsi_wl_surface->display->wl_display,
+                                          wsi_wl_surface->display->queue);
+      if (ret < 0)
+         return VK_ERROR_OUT_OF_DATE_KHR;
    }
 
    assert(image_index < chain->base.image_count);
@@ -1594,6 +1690,9 @@ wsi_wl_swapchain_queue_present(struct wsi_swapchain *wsi_chain,
       chain->frame = wl_surface_frame(wsi_wl_surface->surface);
       wl_callback_add_listener(chain->frame, &frame_listener, chain);
       chain->fifo_ready = false;
+   } else {
+      /* If we present MAILBOX, any subsequent presentation in FIFO can replace this image. */
+      chain->fifo_ready = true;
    }
 
    chain->images[image_index].busy = true;
@@ -1871,6 +1970,8 @@ wsi_wl_surface_create_swapchain(VkIcdSurfaceBase *icd_surface,
    chain->base.get_wsi_image = wsi_wl_swapchain_get_wsi_image;
    chain->base.acquire_next_image = wsi_wl_swapchain_acquire_next_image;
    chain->base.queue_present = wsi_wl_swapchain_queue_present;
+   chain->base.release_images = wsi_wl_swapchain_release_images;
+   chain->base.set_present_mode = wsi_wl_swapchain_set_present_mode;
    chain->base.present_mode = wsi_swapchain_get_present_mode(wsi_device, pCreateInfo);
    chain->base.image_count = num_images;
    chain->extent = pCreateInfo->imageExtent;