wsi: Implement linux-drm-syncobj-v1

This implements explicit sync with linux-drm-syncobj-v1 for the
Wayland WSI.

Signed-off-by: Joshua Ashton <joshua@froggi.es>
diff --git a/src/vulkan/wsi/wsi_common_wayland.c b/src/vulkan/wsi/wsi_common_wayland.c
index 6f249bc..3fd3ccc 100644
--- a/src/vulkan/wsi/wsi_common_wayland.c
+++ b/src/vulkan/wsi/wsi_common_wayland.c
@@ -37,12 +37,14 @@
 #include "drm-uapi/drm_fourcc.h"
 
 #include "vk_instance.h"
+#include "vk_device.h"
 #include "vk_physical_device.h"
 #include "vk_util.h"
 #include "wsi_common_entrypoints.h"
 #include "wsi_common_private.h"
 #include "linux-dmabuf-unstable-v1-client-protocol.h"
 #include "presentation-time-client-protocol.h"
+#include "linux-drm-syncobj-v1-client-protocol.h"
 #include "tearing-control-v1-client-protocol.h"
 
 #include <util/compiler.h>
@@ -104,6 +106,7 @@
    struct zwp_linux_dmabuf_v1 *wl_dmabuf;
    struct zwp_linux_dmabuf_feedback_v1 *wl_dmabuf_feedback;
    struct wp_tearing_control_manager_v1 *tearing_control_manager;
+   struct wp_linux_drm_syncobj_manager_v1 *wl_syncobj;
 
    struct dmabuf_feedback_format_table format_table;
 
@@ -137,6 +140,8 @@
    int shm_fd;
    void *shm_ptr;
    unsigned shm_size;
+
+   struct wp_linux_drm_syncobj_timeline_v1 *wl_syncobj_timeline[WSI_ES_COUNT];
 };
 
 enum wsi_wl_buffer_type {
@@ -156,6 +161,8 @@
 
    struct zwp_linux_dmabuf_feedback_v1 *wl_dmabuf_feedback;
    struct dmabuf_feedback dmabuf_feedback, pending_dmabuf_feedback;
+
+   struct wp_linux_drm_syncobj_surface_v1 *wl_syncobj_surface;
 };
 
 struct wsi_wl_swapchain {
@@ -198,6 +205,14 @@
 VK_DEFINE_NONDISP_HANDLE_CASTS(wsi_wl_swapchain, base.base, VkSwapchainKHR,
                                VK_OBJECT_TYPE_SWAPCHAIN_KHR)
 
+static bool
+wsi_wl_use_explicit_sync(struct wsi_wl_display *display, struct wsi_device *device)
+{
+   return device->has_timeline_semaphore &&
+          (device->timeline_semaphore_export_handle_types & VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT) &&
+          display->wl_syncobj != NULL;
+}
+
 enum wsi_wl_fmt_flag {
    WSI_WL_FMT_ALPHA = 1 << 0,
    WSI_WL_FMT_OPAQUE = 1 << 1,
@@ -799,6 +814,9 @@
                              MIN2(version, ZWP_LINUX_DMABUF_V1_GET_DEFAULT_FEEDBACK_SINCE_VERSION));
          zwp_linux_dmabuf_v1_add_listener(display->wl_dmabuf,
                                           &dmabuf_listener, display);
+      } else if (strcmp(interface, wp_linux_drm_syncobj_manager_v1_interface.name) == 0) {
+         display->wl_syncobj =
+            wl_registry_bind(registry, name, &wp_linux_drm_syncobj_manager_v1_interface, 1);
       }
    }
 
@@ -830,6 +848,8 @@
    u_vector_finish(&display->formats);
    if (display->wl_shm)
       wl_shm_destroy(display->wl_shm);
+   if (display->wl_syncobj)
+      wp_linux_drm_syncobj_manager_v1_destroy(display->wl_syncobj);
    if (display->wl_dmabuf)
       zwp_linux_dmabuf_v1_destroy(display->wl_dmabuf);
    if (display->wp_presentation_notwrapped)
@@ -1337,6 +1357,9 @@
    struct wsi_wl_surface *wsi_wl_surface =
       wl_container_of((VkIcdSurfaceWayland *)icd_surface, wsi_wl_surface, base);
 
+   if (wsi_wl_surface->wl_syncobj_surface)
+      wp_linux_drm_syncobj_surface_v1_destroy(wsi_wl_surface->wl_syncobj_surface);
+
    if (wsi_wl_surface->wl_dmabuf_feedback) {
       zwp_linux_dmabuf_feedback_v1_destroy(wsi_wl_surface->wl_dmabuf_feedback);
       dmabuf_feedback_fini(&wsi_wl_surface->dmabuf_feedback);
@@ -1598,6 +1621,15 @@
                                  wsi_wl_surface->display->queue);
    }
 
+   if (wsi_wl_use_explicit_sync(wsi_wl_surface->display, wsi_device)) {
+      wsi_wl_surface->wl_syncobj_surface =
+         wp_linux_drm_syncobj_manager_v1_get_surface(wsi_wl_surface->display->wl_syncobj,
+                                                     wsi_wl_surface->surface);
+
+      if (!wsi_wl_surface->wl_syncobj_surface)
+         goto fail;
+   }
+
    return VK_SUCCESS;
 
 fail:
@@ -1812,9 +1844,34 @@
 }
 
 static VkResult
-wsi_wl_swapchain_acquire_next_image(struct wsi_swapchain *wsi_chain,
-                                    const VkAcquireNextImageInfoKHR *info,
-                                    uint32_t *image_index)
+wsi_wl_swapchain_acquire_next_image_explicit(struct wsi_swapchain *wsi_chain,
+                                             const VkAcquireNextImageInfoKHR *info,
+                                             uint32_t *image_index)
+{
+   struct wsi_wl_swapchain *chain = (struct wsi_wl_swapchain *)wsi_chain;
+
+   /* See comments in queue_present() */
+   if (chain->retired)
+      return VK_ERROR_OUT_OF_DATE_KHR;
+
+   STACK_ARRAY(struct wsi_image*, images, wsi_chain->image_count);
+   for (uint32_t i = 0; i < chain->base.image_count; i++)
+      images[i] = &chain->images[i].base;
+
+   VkResult result = wsi_drm_wait_for_explicit_sync_release(wsi_chain,
+                                                            wsi_chain->image_count,
+                                                            images,
+                                                            info->timeout,
+                                                            image_index);
+   STACK_ARRAY_FINISH(images);
+
+   return result;
+}
+
+static VkResult
+wsi_wl_swapchain_acquire_next_image_implicit(struct wsi_swapchain *wsi_chain,
+                                             const VkAcquireNextImageInfoKHR *info,
+                                             uint32_t *image_index)
 {
    struct wsi_wl_swapchain *chain = (struct wsi_wl_swapchain *)wsi_chain;
    struct timespec start_time, end_time;
@@ -1971,6 +2028,21 @@
          return VK_ERROR_OUT_OF_DATE_KHR;
    }
 
+   if (chain->base.image_info.explicit_sync) {
+      struct wsi_wl_image *image = &chain->images[image_index];
+      /* Incremented by signal in base queue_present. */
+      uint64_t acquire_point = image->base.explicit_sync[WSI_ES_ACQUIRE].timeline;
+      uint64_t release_point = image->base.explicit_sync[WSI_ES_RELEASE].timeline;
+      wp_linux_drm_syncobj_surface_v1_set_acquire_point(wsi_wl_surface->wl_syncobj_surface,
+                                                        image->wl_syncobj_timeline[WSI_ES_ACQUIRE],
+                                                        (uint32_t)(acquire_point >> 32),
+                                                        (uint32_t)(acquire_point & 0xffffffff));
+      wp_linux_drm_syncobj_surface_v1_set_release_point(wsi_wl_surface->wl_syncobj_surface,
+                                                        image->wl_syncobj_timeline[WSI_ES_RELEASE],
+                                                        (uint32_t)(release_point >> 32),
+                                                        (uint32_t)(release_point & 0xffffffff));
+   }
+
    assert(image_index < chain->base.image_count);
    wl_surface_attach(wsi_wl_surface->surface, chain->images[image_index].buffer, 0, 0);
 
@@ -2126,6 +2198,17 @@
                                                  chain->drm_format,
                                                  0);
       zwp_linux_buffer_params_v1_destroy(params);
+
+      if (chain->base.image_info.explicit_sync) {
+         for (uint32_t i = 0; i < WSI_ES_COUNT; i++) {
+            image->wl_syncobj_timeline[i] =
+               wp_linux_drm_syncobj_manager_v1_import_timeline(display->wl_syncobj,
+                                                               image->base.explicit_sync[i].fd);
+            if (!image->wl_syncobj_timeline[i])
+               goto fail_image;
+         }
+      }
+
       break;
    }
 
@@ -2136,11 +2219,17 @@
    if (!image->buffer)
       goto fail_image;
 
-   wl_buffer_add_listener(image->buffer, &buffer_listener, image);
+   /* No need to listen for release if we are explicit sync. */
+   if (!chain->base.image_info.explicit_sync)
+      wl_buffer_add_listener(image->buffer, &buffer_listener, image);
 
    return VK_SUCCESS;
 
 fail_image:
+   for (uint32_t i = 0; i < WSI_ES_COUNT; i++) {
+      if (image->wl_syncobj_timeline[i])
+         wp_linux_drm_syncobj_timeline_v1_destroy(image->wl_syncobj_timeline[i]);
+   }
    wsi_destroy_image(&chain->base, &image->base);
 
    return VK_ERROR_OUT_OF_HOST_MEMORY;
@@ -2150,6 +2239,10 @@
 wsi_wl_swapchain_images_free(struct wsi_wl_swapchain *chain)
 {
    for (uint32_t i = 0; i < chain->base.image_count; i++) {
+      for (uint32_t j = 0; j < WSI_ES_COUNT; j++) {
+         if (chain->images[i].wl_syncobj_timeline[j])
+            wp_linux_drm_syncobj_timeline_v1_destroy(chain->images[i].wl_syncobj_timeline[j]);
+      }
       if (chain->images[i].buffer) {
          wl_buffer_destroy(chain->images[i].buffer);
          wsi_destroy_image(&chain->base, &chain->images[i].base);
@@ -2321,6 +2414,7 @@
       drm_image_params = (struct wsi_drm_image_params) {
          .base.image_type = WSI_IMAGE_TYPE_DRM,
          .same_gpu = wsi_wl_surface->display->same_gpu,
+         .explicit_sync = wsi_wl_use_explicit_sync(wsi_wl_surface->display, wsi_device),
       };
       /* Use explicit DRM format modifiers when both the server and the driver
        * support them.
@@ -2361,7 +2455,9 @@
 
    chain->base.destroy = wsi_wl_swapchain_destroy;
    chain->base.get_wsi_image = wsi_wl_swapchain_get_wsi_image;
-   chain->base.acquire_next_image = wsi_wl_swapchain_acquire_next_image;
+   chain->base.acquire_next_image = chain->base.image_info.explicit_sync
+                                  ? wsi_wl_swapchain_acquire_next_image_explicit
+                                  : wsi_wl_swapchain_acquire_next_image_implicit;
    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;