vulkan/wsi/wayland: Remove roundtrip when creating image
[mesa.git] / src / vulkan / wsi / wsi_common_wayland.c
index 32a0a51349d60193aa7ea8d8df3fb1dec302ca08..2ff29736aeb6f574a5991be56f5af8681f778b0d 100644 (file)
@@ -22,7 +22,6 @@
  */
 
 #include <wayland-client.h>
-#include <wayland-drm-client-protocol.h>
 
 #include <assert.h>
 #include <stdlib.h>
 #include <unistd.h>
 #include <errno.h>
 #include <string.h>
+#include <pthread.h>
 
+#include "util/vk_util.h"
 #include "wsi_common_wayland.h"
+#include "wayland-drm-client-protocol.h"
 
 #include <util/hash_table.h>
 #include <util/u_vector.h>
 
 #define typed_memcpy(dest, src, count) ({ \
-   static_assert(sizeof(*src) == sizeof(*dest), ""); \
+   STATIC_ASSERT(sizeof(*src) == sizeof(*dest)); \
    memcpy((dest), (src), (count) * sizeof(*(src))); \
 })
 
-#define MIN_NUM_IMAGES 2
-
 struct wsi_wayland;
 
 struct wsi_wl_display {
@@ -273,7 +273,7 @@ wsi_wl_display_create(struct wsi_wayland *wsi, struct wl_display *wl_display)
 
    struct wl_registry *registry = wl_display_get_registry(wl_display);
    if (!registry)
-      return NULL;
+      goto fail;
 
    wl_registry_add_listener(registry, &registry_listener, display);
 
@@ -281,24 +281,25 @@ wsi_wl_display_create(struct wsi_wayland *wsi, struct wl_display *wl_display)
    wl_display_roundtrip(wl_display);
 
    if (!display->drm)
-      goto fail;
+      goto fail_registry;
 
    /* Round-rip to get wl_drm formats and capabilities */
    wl_display_roundtrip(wl_display);
 
    /* We need prime support */
    if (!(display->capabilities & WL_DRM_CAPABILITY_PRIME))
-      goto fail;
+      goto fail_registry;
 
    /* We don't need this anymore */
    wl_registry_destroy(registry);
 
    return display;
 
-fail:
+fail_registry:
    if (registry)
       wl_registry_destroy(registry);
 
+fail:
    wsi_wl_display_destroy(wsi, display);
    return NULL;
 }
@@ -321,6 +322,8 @@ wsi_wl_get_display(struct wsi_device *wsi_device,
       pthread_mutex_unlock(&wsi->mutex);
 
       struct wsi_wl_display *display = wsi_wl_display_create(wsi, wl_display);
+      if (!display)
+         return NULL;
 
       pthread_mutex_lock(&wsi->mutex);
 
@@ -350,6 +353,8 @@ wsi_wl_surface_get_support(VkIcdSurfaceBase *surface,
                            struct wsi_device *wsi_device,
                            const VkAllocationCallbacks *alloc,
                            uint32_t queueFamilyIndex,
+                           int local_fd,
+                           bool can_handle_different_gpu,
                            VkBool32* pSupported)
 {
    *pSupported = true;
@@ -366,11 +371,20 @@ static VkResult
 wsi_wl_surface_get_capabilities(VkIcdSurfaceBase *surface,
                                 VkSurfaceCapabilitiesKHR* caps)
 {
-   caps->minImageCount = MIN_NUM_IMAGES;
-   caps->maxImageCount = 4;
+   /* 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;
+   /* There is no real maximum */
+   caps->maxImageCount = 0;
+
    caps->currentExtent = (VkExtent2D) { -1, -1 };
    caps->minImageExtent = (VkExtent2D) { 1, 1 };
-   caps->maxImageExtent = (VkExtent2D) { INT16_MAX, INT16_MAX };
+   /* This is the maximum supported size on Intel */
+   caps->maxImageExtent = (VkExtent2D) { 1 << 14, 1 << 14 };
    caps->supportedTransforms = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
    caps->currentTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
    caps->maxImageArrayLayers = 1;
@@ -388,6 +402,16 @@ wsi_wl_surface_get_capabilities(VkIcdSurfaceBase *surface,
    return VK_SUCCESS;
 }
 
+static VkResult
+wsi_wl_surface_get_capabilities2(VkIcdSurfaceBase *surface,
+                                 const void *info_next,
+                                 VkSurfaceCapabilities2KHR* caps)
+{
+   assert(caps->sType == VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_KHR);
+
+   return wsi_wl_surface_get_capabilities(surface, &caps->surfaceCapabilities);
+}
+
 static VkResult
 wsi_wl_surface_get_formats(VkIcdSurfaceBase *icd_surface,
                           struct wsi_device *wsi_device,
@@ -397,27 +421,46 @@ wsi_wl_surface_get_formats(VkIcdSurfaceBase *icd_surface,
    VkIcdSurfaceWayland *surface = (VkIcdSurfaceWayland *)icd_surface;
    struct wsi_wl_display *display =
       wsi_wl_get_display(wsi_device, surface->display);
+   if (!display)
+      return VK_ERROR_OUT_OF_HOST_MEMORY;
 
-   uint32_t count = u_vector_length(&display->formats);
+   VK_OUTARRAY_MAKE(out, pSurfaceFormats, pSurfaceFormatCount);
 
-   if (pSurfaceFormats == NULL) {
-      *pSurfaceFormatCount = count;
-      return VK_SUCCESS;
+   VkFormat *disp_fmt;
+   u_vector_foreach(disp_fmt, &display->formats) {
+      vk_outarray_append(&out, out_fmt) {
+         out_fmt->format = *disp_fmt;
+         out_fmt->colorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR;
+      }
    }
 
-   assert(*pSurfaceFormatCount >= count);
-   *pSurfaceFormatCount = count;
+   return vk_outarray_status(&out);
+}
 
-   VkFormat *f;
-   u_vector_foreach(f, &display->formats) {
-      *(pSurfaceFormats++) = (VkSurfaceFormatKHR) {
-         .format = *f,
-         /* TODO: We should get this from the compositor somehow */
-         .colorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR,
-      };
+static VkResult
+wsi_wl_surface_get_formats2(VkIcdSurfaceBase *icd_surface,
+                           struct wsi_device *wsi_device,
+                            const void *info_next,
+                            uint32_t* pSurfaceFormatCount,
+                            VkSurfaceFormat2KHR* pSurfaceFormats)
+{
+   VkIcdSurfaceWayland *surface = (VkIcdSurfaceWayland *)icd_surface;
+   struct wsi_wl_display *display =
+      wsi_wl_get_display(wsi_device, surface->display);
+   if (!display)
+      return VK_ERROR_OUT_OF_HOST_MEMORY;
+
+   VK_OUTARRAY_MAKE(out, pSurfaceFormats, pSurfaceFormatCount);
+
+   VkFormat *disp_fmt;
+   u_vector_foreach(disp_fmt, &display->formats) {
+      vk_outarray_append(&out, out_fmt) {
+         out_fmt->surfaceFormat.format = *disp_fmt;
+         out_fmt->surfaceFormat.colorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR;
+      }
    }
 
-   return VK_SUCCESS;
+   return vk_outarray_status(&out);
 }
 
 static VkResult
@@ -430,11 +473,13 @@ wsi_wl_surface_get_present_modes(VkIcdSurfaceBase *surface,
       return VK_SUCCESS;
    }
 
-   assert(*pPresentModeCount >= ARRAY_SIZE(present_modes));
+   *pPresentModeCount = MIN2(*pPresentModeCount, ARRAY_SIZE(present_modes));
    typed_memcpy(pPresentModes, present_modes, *pPresentModeCount);
-   *pPresentModeCount = ARRAY_SIZE(present_modes);
 
-   return VK_SUCCESS;
+   if (*pPresentModeCount < ARRAY_SIZE(present_modes))
+      return VK_INCOMPLETE;
+   else
+      return VK_SUCCESS;
 }
 
 VkResult wsi_create_wl_surface(const VkAllocationCallbacks *pAllocator,
@@ -452,7 +497,7 @@ VkResult wsi_create_wl_surface(const VkAllocationCallbacks *pAllocator,
    surface->display = pCreateInfo->display;
    surface->surface = pCreateInfo->surface;
 
-   *pSurface = _VkIcdSurfaceBase_to_handle(&surface->base);
+   *pSurface = VkIcdSurfaceBase_to_handle(&surface->base);
 
    return VK_SUCCESS;
 }
@@ -470,6 +515,7 @@ struct wsi_wl_swapchain {
    struct wsi_wl_display *                      display;
    struct wl_event_queue *                      queue;
    struct wl_surface *                          surface;
+   uint32_t                                     surface_version;
 
    VkExtent2D                                   extent;
    VkFormat                                     vk_format;
@@ -478,7 +524,6 @@ struct wsi_wl_swapchain {
    VkPresentModeKHR                             present_mode;
    bool                                         fifo_ready;
 
-   uint32_t                                     image_count;
    struct wsi_wl_image                          images[0];
 };
 
@@ -487,19 +532,25 @@ wsi_wl_swapchain_get_images(struct wsi_swapchain *wsi_chain,
                             uint32_t *pCount, VkImage *pSwapchainImages)
 {
    struct wsi_wl_swapchain *chain = (struct wsi_wl_swapchain *)wsi_chain;
+   uint32_t ret_count;
+   VkResult result;
 
    if (pSwapchainImages == NULL) {
-      *pCount = chain->image_count;
+      *pCount = chain->base.image_count;
       return VK_SUCCESS;
    }
 
-   assert(chain->image_count <= *pCount);
-   for (uint32_t i = 0; i < chain->image_count; i++)
-      pSwapchainImages[i] = chain->images[i].image;
+   result = VK_SUCCESS;
+   ret_count = chain->base.image_count;
+   if (chain->base.image_count > *pCount) {
+     ret_count = *pCount;
+     result = VK_INCOMPLETE;
+   }
 
-   *pCount = chain->image_count;
+   for (uint32_t i = 0; i < ret_count; i++)
+      pSwapchainImages[i] = chain->images[i].image;
 
-   return VK_SUCCESS;
+   return result;
 }
 
 static VkResult
@@ -520,7 +571,7 @@ wsi_wl_swapchain_acquire_next_image(struct wsi_swapchain *wsi_chain,
       return VK_ERROR_OUT_OF_DATE_KHR;
 
    while (1) {
-      for (uint32_t i = 0; i < chain->image_count; i++) {
+      for (uint32_t i = 0; i < chain->base.image_count; i++) {
          if (!chain->images[i].busy) {
             /* We found a non-busy image */
             *image_index = i;
@@ -555,11 +606,12 @@ static const struct wl_callback_listener frame_listener = {
 
 static VkResult
 wsi_wl_swapchain_queue_present(struct wsi_swapchain *wsi_chain,
-                               uint32_t image_index)
+                               uint32_t image_index,
+                               const VkPresentRegionKHR *damage)
 {
    struct wsi_wl_swapchain *chain = (struct wsi_wl_swapchain *)wsi_chain;
 
-   if (chain->present_mode == VK_PRESENT_MODE_FIFO_KHR) {
+   if (chain->base.present_mode == VK_PRESENT_MODE_FIFO_KHR) {
       while (!chain->fifo_ready) {
          int ret = wl_display_dispatch_queue(chain->display->display,
                                              chain->queue);
@@ -568,11 +620,23 @@ wsi_wl_swapchain_queue_present(struct wsi_swapchain *wsi_chain,
       }
    }
 
-   assert(image_index < chain->image_count);
+   assert(image_index < chain->base.image_count);
    wl_surface_attach(chain->surface, chain->images[image_index].buffer, 0, 0);
-   wl_surface_damage(chain->surface, 0, 0, INT32_MAX, INT32_MAX);
 
-   if (chain->present_mode == VK_PRESENT_MODE_FIFO_KHR) {
+   if (chain->surface_version >= 4 && damage &&
+       damage->pRectangles && damage->rectangleCount > 0) {
+      for (unsigned i = 0; i < damage->rectangleCount; i++) {
+         const VkRectLayerKHR *rect = &damage->pRectangles[i];
+         assert(rect->layer == 0);
+         wl_surface_damage_buffer(chain->surface,
+                                  rect->offset.x, rect->offset.y,
+                                  rect->extent.width, rect->extent.height);
+      }
+   } else {
+      wl_surface_damage(chain->surface, 0, 0, INT32_MAX, INT32_MAX);
+   }
+
+   if (chain->base.present_mode == VK_PRESENT_MODE_FIFO_KHR) {
       struct wl_callback *frame = wl_surface_frame(chain->surface);
       wl_proxy_set_queue((struct wl_proxy *)frame, chain->queue);
       wl_callback_add_listener(frame, &frame_listener, chain);
@@ -615,6 +679,8 @@ wsi_wl_image_init(struct wsi_wl_swapchain *chain,
    result = chain->base.image_fns->create_wsi_image(vk_device,
                                                     pCreateInfo,
                                                     pAllocator,
+                                                    false,
+                                                    false,
                                                     &image->image,
                                                     &image->memory,
                                                     &size,
@@ -632,14 +698,17 @@ wsi_wl_image_init(struct wsi_wl_swapchain *chain,
                                               offset,
                                               row_pitch,
                                               0, 0, 0, 0 /* unused */);
-   wl_display_roundtrip(chain->display->display);
    close(fd);
 
+   if (!image->buffer)
+      goto fail_image;
+
    wl_proxy_set_queue((struct wl_proxy *)image->buffer, chain->queue);
    wl_buffer_add_listener(image->buffer, &buffer_listener, image);
 
    return VK_SUCCESS;
 
+fail_image:
    chain->base.image_fns->free_wsi_image(vk_device, pAllocator,
                                          image->image, image->memory);
 
@@ -652,7 +721,7 @@ wsi_wl_swapchain_destroy(struct wsi_swapchain *wsi_chain,
 {
    struct wsi_wl_swapchain *chain = (struct wsi_wl_swapchain *)wsi_chain;
 
-   for (uint32_t i = 0; i < chain->image_count; i++) {
+   for (uint32_t i = 0; i < chain->base.image_count; i++) {
       if (chain->images[i].buffer)
          chain->base.image_fns->free_wsi_image(chain->base.device, pAllocator,
                                                chain->images[i].image,
@@ -668,6 +737,7 @@ static VkResult
 wsi_wl_surface_create_swapchain(VkIcdSurfaceBase *icd_surface,
                                 VkDevice device,
                                 struct wsi_device *wsi_device,
+                                int local_fd,
                                 const VkSwapchainCreateInfoKHR* pCreateInfo,
                                 const VkAllocationCallbacks* pAllocator,
                                 const struct wsi_image_fns *image_fns,
@@ -681,43 +751,36 @@ wsi_wl_surface_create_swapchain(VkIcdSurfaceBase *icd_surface,
 
    int num_images = pCreateInfo->minImageCount;
 
-   assert(num_images >= MIN_NUM_IMAGES);
-
-   /* 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
-    */
-   if (pCreateInfo->presentMode == VK_PRESENT_MODE_MAILBOX_KHR)
-      num_images = MAX2(num_images, 4);
-
    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;
 
+   bool alpha = pCreateInfo->compositeAlpha ==
+                      VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR;
+
    chain->base.device = device;
    chain->base.destroy = wsi_wl_swapchain_destroy;
    chain->base.get_images = wsi_wl_swapchain_get_images;
    chain->base.acquire_next_image = wsi_wl_swapchain_acquire_next_image;
    chain->base.queue_present = wsi_wl_swapchain_queue_present;
    chain->base.image_fns = image_fns;
+   chain->base.present_mode = pCreateInfo->presentMode;
+   chain->base.image_count = num_images;
+   chain->base.needs_linear_copy = false;
    chain->surface = surface->surface;
+   chain->surface_version = wl_proxy_get_version((void *)surface->surface);
    chain->extent = pCreateInfo->imageExtent;
    chain->vk_format = pCreateInfo->imageFormat;
-   chain->drm_format = wl_drm_format_for_vk_format(chain->vk_format, false);
+   chain->drm_format = wl_drm_format_for_vk_format(chain->vk_format, alpha);
 
-   chain->present_mode = pCreateInfo->presentMode;
    chain->fifo_ready = true;
 
-   chain->image_count = num_images;
-
    /* Mark a bunch of stuff as NULL.  This way we can just call
     * destroy_swapchain for cleanup.
     */
-   for (uint32_t i = 0; i < chain->image_count; i++)
+   for (uint32_t i = 0; i < chain->base.image_count; i++)
       chain->images[i].buffer = NULL;
    chain->queue = NULL;
 
@@ -734,7 +797,7 @@ wsi_wl_surface_create_swapchain(VkIcdSurfaceBase *icd_surface,
       goto fail;
    }
 
-   for (uint32_t i = 0; i < chain->image_count; i++) {
+   for (uint32_t i = 0; i < chain->base.image_count; i++) {
       result = wsi_wl_image_init(chain, &chain->images[i],
                                  pCreateInfo, pAllocator);
       if (result != VK_SUCCESS)
@@ -792,7 +855,9 @@ wsi_wl_init_wsi(struct wsi_device *wsi_device,
 
    wsi->base.get_support = wsi_wl_surface_get_support;
    wsi->base.get_capabilities = wsi_wl_surface_get_capabilities;
+   wsi->base.get_capabilities2 = wsi_wl_surface_get_capabilities2;
    wsi->base.get_formats = wsi_wl_surface_get_formats;
+   wsi->base.get_formats2 = wsi_wl_surface_get_formats2;
    wsi->base.get_present_modes = wsi_wl_surface_get_present_modes;
    wsi->base.create_swapchain = wsi_wl_surface_create_swapchain;
 
@@ -819,6 +884,10 @@ wsi_wl_finish_wsi(struct wsi_device *wsi_device,
       (struct wsi_wayland *)wsi_device->wsi[VK_ICD_WSI_PLATFORM_WAYLAND];
 
    if (wsi) {
+      struct hash_entry *entry;
+      hash_table_foreach(wsi->displays, entry)
+         wsi_wl_display_destroy(wsi, entry->data);
+
       _mesa_hash_table_destroy(wsi->displays, NULL);
 
       pthread_mutex_destroy(&wsi->mutex);