Add support for multiplane swapchain images in Wayland
authorIason Paraskevopoulos <iason.paraskevopoulos@arm.com>
Fri, 12 Mar 2021 14:19:59 +0000 (14:19 +0000)
committerIason Paraskevopoulos <iason.paraskevopoulos@arm.com>
Fri, 23 Apr 2021 08:47:31 +0000 (09:47 +0100)
Updates wsialloc and wsi::wayland::swapchain to support multiplane
image allocations.

Uses the custom allocator for allocations in Wayland image creation.

Change-Id: I1950b14b75711a76521bbf1967c3c835d006f8a8
Signed-off-by: Iason Paraskevopoulos <iason.paraskevopoulos@arm.com>
layer/private_data.hpp
util/wsialloc/wsialloc_ion.c
wsi/headless/swapchain.cpp
wsi/headless/swapchain.hpp
wsi/swapchain_base.hpp
wsi/wayland/swapchain.cpp
wsi/wayland/swapchain.hpp

index b5b860c..0d8a252 100644 (file)
@@ -55,7 +55,8 @@ namespace layer
    OPTIONAL(GetPhysicalDeviceSurfaceFormatsKHR)         \
    OPTIONAL(GetPhysicalDeviceSurfacePresentModesKHR)    \
    OPTIONAL(GetPhysicalDeviceSurfaceSupportKHR)         \
-   OPTIONAL(GetPhysicalDeviceImageFormatProperties2KHR)
+   OPTIONAL(GetPhysicalDeviceImageFormatProperties2KHR) \
+   OPTIONAL(GetPhysicalDeviceFormatProperties2KHR)
 
 struct instance_dispatch_table
 {
@@ -102,7 +103,8 @@ struct instance_dispatch_table
    OPTIONAL(GetSwapchainImagesKHR)                  \
    OPTIONAL(AcquireNextImageKHR)                    \
    OPTIONAL(QueuePresentKHR)                        \
-   OPTIONAL(GetMemoryFdPropertiesKHR)
+   OPTIONAL(GetMemoryFdPropertiesKHR)               \
+   OPTIONAL(BindImageMemory2KHR)
 
 struct device_dispatch_table
 {
index f403bf9..e0e32c8 100644 (file)
@@ -42,7 +42,8 @@
 /** Default alignment */
 #define WSIALLOCP_MIN_ALIGN_SZ (64u)
 
-struct ion_allocator {
+struct ion_allocator
+{
    /* File descriptor of /dev/ion. */
    int fd;
    /* Allocator heap id. */
@@ -221,43 +222,44 @@ int wsialloc_alloc(
    assert(new_fd != NULL);
    assert(offset != NULL);
 
-   int ret = 0;
    struct ion_allocator *ion = allocator->ptr;
 
-   if(modifier != NULL && *modifier != 0)
+   if (modifier != NULL && *modifier != 0)
    {
       return -ENOTSUP;
    }
 
-   size_t size = 0;
-
    /* Validate format and determine per-plane bits per pixel. */
    uint32_t nr_planes, bits_per_pixel[WSIALLOCP_MAX_PLANES];
-   ret = wsiallocp_get_fmt_info(fourcc, &nr_planes, bits_per_pixel);
+   int ret = wsiallocp_get_fmt_info(fourcc, &nr_planes, bits_per_pixel);
    if (ret != 0)
    {
       return ret;
    }
 
-   /* Only single plane formats supported. */
-   if (nr_planes != 1)
+   size_t size = 0;
+   for (uint32_t plane = 0; plane < nr_planes; plane++)
    {
-      return -ENOTSUP;
-   }
+      offset[plane] = size;
 
-   /* Assumes multiple of 8--rework otherwise. */
-   uint32_t plane0_bytes_per_pixel = bits_per_pixel[0] / 8;
-   assert(plane0_bytes_per_pixel * 8 == bits_per_pixel[0]);
+      /* Assumes multiple of 8--rework otherwise. */
+      const uint32_t plane_bytes_per_pixel = bits_per_pixel[plane] / 8;
+      assert(plane_bytes_per_pixel * 8 == bits_per_pixel[plane]);
 
-   *stride = round_size_up_to_align(width * plane0_bytes_per_pixel);
-   size = *stride * height;
+      stride[plane] = round_size_up_to_align(width * plane_bytes_per_pixel);
+      size += stride[plane] * height;
+   }
 
-   *new_fd = allocate(ion->fd, size, ion->alloc_heap_id);
-   if (*new_fd < 0)
+   new_fd[0] = allocate(ion->fd, size, ion->alloc_heap_id);
+   if (new_fd[0] < 0)
    {
       return -errno;
    }
 
-   *offset = 0;
+   for (uint32_t plane = 1; plane < nr_planes; plane++)
+   {
+      new_fd[plane] = new_fd[0];
+   }
+
    return 0;
 }
index 03af79b..fdf9627 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017-2020 Arm Limited.
+ * Copyright (c) 2017-2021 Arm Limited.
  *
  * SPDX-License-Identifier: MIT
  *
@@ -57,7 +57,7 @@ swapchain::~swapchain()
    teardown();
 }
 
-VkResult swapchain::create_image(const VkImageCreateInfo &image_create, wsi::swapchain_image &image)
+VkResult swapchain::create_image(VkImageCreateInfo image_create, wsi::swapchain_image &image)
 {
    VkResult res = VK_SUCCESS;
    res = m_device_data.disp.CreateImage(m_device, &image_create, nullptr, &image.image);
index c25d5c7..e4440ae 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017-2019 Arm Limited.
+ * Copyright (c) 2017-2019, 2021 Arm Limited.
  *
  * SPDX-License-Identifier: MIT
  *
@@ -72,7 +72,7 @@ protected:
     * will return VK_ERROR_OUT_OF_DEVICE_MEMORY or VK_ERROR_INITIALIZATION_FAILED
     * depending on the error that occured.
     */
-   VkResult create_image(const VkImageCreateInfo &image_create_info, wsi::swapchain_image &image);
+   VkResult create_image(VkImageCreateInfo image_create_info, wsi::swapchain_image &image);
 
    /**
     * @brief Method to perform a present - just calls unpresent_image on headless
@@ -87,7 +87,7 @@ protected:
     *
     * @param image Handle to the image about to be released.
     */
-    void destroy_image(wsi::swapchain_image &image);
+   void destroy_image(wsi::swapchain_image &image);
 };
 
 } /* namespace headless */
index ccd5270..2cff142 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017-2020 Arm Limited.
+ * Copyright (c) 2017-2021 Arm Limited.
  *
  * SPDX-License-Identifier: MIT
  *
@@ -306,7 +306,7 @@ protected:
     * will return VK_ERROR_OUT_OF_DEVICE_MEMORY or VK_ERROR_INITIALIZATION_FAILED
     * depending on the error that occured.
     */
-   virtual VkResult create_image(const VkImageCreateInfo &image_create_info, swapchain_image &image) = 0;
+   virtual VkResult create_image(VkImageCreateInfo image_create_info, swapchain_image &image) = 0;
 
    /**
     * @brief Method to present and image
@@ -364,7 +364,7 @@ private:
 
    /**
     * @brief Per swapchain thread function that handles page flipping.
-    * 
+    *
     * This thread should be running for the lifetime of the swapchain.
     * The thread simply calls the implementation's present_image() method.
     * There are 3 main cases we cover here:
index 7a62de7..a2435af 100644 (file)
@@ -27,6 +27,7 @@
 #include "swapchain.hpp"
 #include "wl_helpers.hpp"
 
+#include <stdint.h>
 #include <cstring>
 #include <cassert>
 #include <unistd.h>
 #include <cerrno>
 #include <cstdio>
 #include <climits>
+#include <functional>
 
 #include "util/drm/drm_utils.hpp"
 
+#define MAX_PLANES 4
+
 namespace wsi
 {
 namespace wayland
@@ -60,14 +64,23 @@ make_proxy_with_queue(T *object, wl_event_queue *queue)
    return std::unique_ptr<T, std::function<void(T *)>>(proxy, delete_proxy);
 }
 
+const VkImageAspectFlagBits plane_flag_bits[MAX_PLANES] = {
+   VK_IMAGE_ASPECT_MEMORY_PLANE_0_BIT_EXT,
+   VK_IMAGE_ASPECT_MEMORY_PLANE_1_BIT_EXT,
+   VK_IMAGE_ASPECT_MEMORY_PLANE_2_BIT_EXT,
+   VK_IMAGE_ASPECT_MEMORY_PLANE_3_BIT_EXT,
+};
+
 struct swapchain::wayland_image_data
 {
-   int buffer_fd;
-   int stride;
-   uint32_t offset;
+   int buffer_fd[MAX_PLANES];
+   int stride[MAX_PLANES];
+   uint32_t offset[MAX_PLANES];
 
    wl_buffer *buffer;
-   VkDeviceMemory memory;
+   VkDeviceMemory memory[MAX_PLANES];
+
+   uint32_t num_planes;
 };
 
 swapchain::swapchain(layer::device_private_data &dev_data, const VkAllocationCallbacks *pAllocator)
@@ -136,7 +149,7 @@ VkResult swapchain::init_platform(VkDevice device, const VkSwapchainCreateInfoKH
       return VK_ERROR_INITIALIZATION_FAILED;
    }
 
-   auto registry = registry_owner{wl_display_get_registry(display_proxy.get())};
+   auto registry = registry_owner{ wl_display_get_registry(display_proxy.get()) };
    if (registry == nullptr)
    {
       WSI_PRINT_ERROR("Failed to get wl display registry.\n");
@@ -173,17 +186,18 @@ VkResult swapchain::init_platform(VkDevice device, const VkSwapchainCreateInfoKH
    return VK_SUCCESS;
 }
 
-static void create_succeeded(void *data, struct zwp_linux_buffer_params_v1 *params, struct wl_buffer *buffer)
+extern "C" void create_succeeded(void *data, struct zwp_linux_buffer_params_v1 *params,
+                                 struct wl_buffer *buffer)
 {
-   struct wl_buffer **wayland_buffer = (struct wl_buffer **)data;
+   auto wayland_buffer = reinterpret_cast<wl_buffer **>(data);
    *wayland_buffer = buffer;
 }
 
 static const struct zwp_linux_buffer_params_v1_listener params_listener = { create_succeeded, NULL };
 
-static void buffer_release(void *data, struct wl_buffer *wayl_buffer)
+extern "C" void buffer_release(void *data, struct wl_buffer *wayl_buffer)
 {
-   swapchain *sc = (swapchain *)data;
+   auto sc = reinterpret_cast<swapchain *>(data);
    sc->release_buffer(wayl_buffer);
 }
 
@@ -192,8 +206,7 @@ void swapchain::release_buffer(struct wl_buffer *wayl_buffer)
    uint32_t i;
    for (i = 0; i < m_swapchain_images.size(); i++)
    {
-      wayland_image_data *data;
-      data = (wayland_image_data *)m_swapchain_images[i].data;
+      auto data = reinterpret_cast<wayland_image_data *>(m_swapchain_images[i].data);
       if (data->buffer == wayl_buffer)
       {
          unpresent_image(i);
@@ -207,14 +220,145 @@ void swapchain::release_buffer(struct wl_buffer *wayl_buffer)
 
 static struct wl_buffer_listener buffer_listener = { buffer_release };
 
-VkResult swapchain::allocate_image(const VkImageCreateInfo &image_create_info, wayland_image_data *image_data,
+VkResult swapchain::allocate_plane_memory(int fd, VkDeviceMemory *memory)
+{
+   uint32_t mem_index = -1;
+   VkResult result = get_fd_mem_type_index(fd, mem_index);
+   if (result != VK_SUCCESS)
+   {
+      return result;
+   }
+
+   const off_t dma_buf_size = lseek(fd, 0, SEEK_END);
+   if (dma_buf_size < 0)
+   {
+      WSI_PRINT_ERROR("Failed to get DMA Buf size.\n");
+      return VK_ERROR_OUT_OF_HOST_MEMORY;
+   }
+
+   VkImportMemoryFdInfoKHR import_mem_info = {};
+   import_mem_info.sType = VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHR;
+   import_mem_info.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT;
+   import_mem_info.fd = fd;
+
+   VkMemoryAllocateInfo alloc_info = {};
+   alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
+   alloc_info.pNext = &import_mem_info;
+   alloc_info.allocationSize = static_cast<uint64_t>(dma_buf_size);
+   alloc_info.memoryTypeIndex = mem_index;
+
+   result = m_device_data.disp.AllocateMemory(
+      m_device, &alloc_info, get_allocation_callbacks(), memory);
+
+   if (result != VK_SUCCESS)
+   {
+      WSI_PRINT_ERROR("Failed to import memory.\n");
+      return result;
+   }
+
+   return VK_SUCCESS;
+}
+
+VkResult swapchain::get_fd_mem_type_index(int fd, uint32_t &mem_idx)
+{
+   VkMemoryFdPropertiesKHR mem_props = {};
+   mem_props.sType = VK_STRUCTURE_TYPE_MEMORY_FD_PROPERTIES_KHR;
+
+   VkResult result = m_device_data.disp.GetMemoryFdPropertiesKHR(
+      m_device, VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT, fd, &mem_props);
+   if (result != VK_SUCCESS)
+   {
+      WSI_PRINT_ERROR("Error querying Fd properties.\n");
+      return result;
+   }
+
+   for (mem_idx = 0; mem_idx < VK_MAX_MEMORY_TYPES; mem_idx++)
+   {
+      if (mem_props.memoryTypeBits & (1 << mem_idx))
+      {
+         break;
+      }
+   }
+
+   assert(mem_idx < VK_MAX_MEMORY_TYPES);
+
+   return VK_SUCCESS;
+}
+
+VkResult swapchain::get_drm_format_properties(
+   VkFormat format, util::vector<VkDrmFormatModifierPropertiesEXT> &format_props_list)
+{
+   VkDrmFormatModifierPropertiesListEXT format_modifier_props = {};
+   format_modifier_props.sType = VK_STRUCTURE_TYPE_DRM_FORMAT_MODIFIER_PROPERTIES_LIST_EXT;
+
+   VkFormatProperties2KHR format_props = {};
+   format_props.sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR;
+   format_props.pNext = &format_modifier_props;
+
+   m_device_data.instance_data.disp.GetPhysicalDeviceFormatProperties2KHR(
+      m_device_data.physical_device, format, &format_props);
+
+   if (!format_props_list.try_resize(format_modifier_props.drmFormatModifierCount))
+   {
+      return VK_ERROR_OUT_OF_HOST_MEMORY;
+   }
+
+   format_modifier_props.pDrmFormatModifierProperties = format_props_list.data();
+   m_device_data.instance_data.disp.GetPhysicalDeviceFormatProperties2KHR(
+      m_device_data.physical_device, format, &format_props);
+
+   return VK_SUCCESS;
+}
+
+static bool is_disjoint_supported(
+   const util::vector<VkDrmFormatModifierPropertiesEXT> &format_props, uint64_t modifier)
+{
+   for (const auto &prop : format_props)
+   {
+      if (prop.drmFormatModifier == modifier &&
+          prop.drmFormatModifierTilingFeatures & VK_FORMAT_FEATURE_DISJOINT_BIT)
+      {
+         return true;
+      }
+   }
+
+   return false;
+}
+
+static uint32_t get_same_fd_index(int fd, int const *fds)
+{
+   uint32_t index = 0;
+   while (fd != fds[index])
+   {
+      index++;
+   }
+
+   return index;
+}
+
+VkResult swapchain::allocate_image(VkImageCreateInfo &image_create_info, wayland_image_data *image_data,
                                    VkImage *image)
 {
    VkResult result = VK_SUCCESS;
+   const uint64_t modifier = DRM_FORMAT_MOD_LINEAR;
 
    image_data->buffer = nullptr;
-   image_data->buffer_fd = -1;
-   image_data->memory = VK_NULL_HANDLE;
+   image_data->num_planes = 0;
+   for (uint32_t plane = 0; plane < MAX_PLANES; plane++)
+   {
+      image_data->buffer_fd[plane] = -1;
+      image_data->memory[plane] = VK_NULL_HANDLE;
+   }
+
+   /* Query support for disjoint images. */
+   util::vector<VkDrmFormatModifierPropertiesEXT> drm_format_props(m_allocator);
+   result = get_drm_format_properties(image_create_info.format, drm_format_props);
+   if (result != VK_SUCCESS)
+   {
+      WSI_PRINT_ERROR("Failed to get format properties.\n");
+      return result;
+   }
+   auto is_disjoint = is_disjoint_supported(drm_format_props, modifier);
 
    VkExternalImageFormatPropertiesKHR external_props = {};
    external_props.sType = VK_STRUCTURE_TYPE_EXTERNAL_IMAGE_FORMAT_PROPERTIES_KHR;
@@ -230,7 +374,7 @@ VkResult swapchain::allocate_image(const VkImageCreateInfo &image_create_info, w
       VkPhysicalDeviceImageDrmFormatModifierInfoEXT drm_mod_info = {};
       drm_mod_info.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_DRM_FORMAT_MODIFIER_INFO_EXT;
       drm_mod_info.pNext = &external_info;
-      drm_mod_info.drmFormatModifier = DRM_FORMAT_MOD_LINEAR;
+      drm_mod_info.drmFormatModifier = modifier;
       drm_mod_info.sharingMode = image_create_info.sharingMode;
       drm_mod_info.queueFamilyIndexCount = image_create_info.queueFamilyIndexCount;
       drm_mod_info.pQueueFamilyIndices = image_create_info.pQueueFamilyIndices;
@@ -284,27 +428,51 @@ VkResult swapchain::allocate_image(const VkImageCreateInfo &image_create_info, w
    else
    {
       /* TODO: Handle Dedicated allocation bit. */
-      uint32_t fourcc = util::drm::vk_to_drm_format(image_create_info.format);
+      const auto fourcc = util::drm::vk_to_drm_format(image_create_info.format);
 
-      int res =
+      const auto res =
          wsialloc_alloc(&m_wsi_allocator, fourcc, image_create_info.extent.width, image_create_info.extent.height,
-                        &image_data->stride, &image_data->buffer_fd, &image_data->offset, nullptr);
+                        image_data->stride, image_data->buffer_fd, image_data->offset, nullptr);
       if (res != 0)
       {
          WSI_PRINT_ERROR("Failed allocation of DMA Buffer.\n");
          return VK_ERROR_OUT_OF_HOST_MEMORY;
       }
+
+      for (uint32_t plane = 0; plane < MAX_PLANES; plane++)
       {
-         assert(image_data->stride >= 0);
-         VkSubresourceLayout image_layout = {};
-         image_layout.offset = image_data->offset;
-         image_layout.rowPitch = static_cast<uint32_t>(image_data->stride);
+         if (image_data->buffer_fd[plane] == -1)
+         {
+            break;
+         }
+         image_data->num_planes++;
+      }
+
+      {
+         util::vector<VkSubresourceLayout> image_layout(m_allocator);
+         if (!image_layout.try_resize(image_data->num_planes))
+         {
+            return VK_ERROR_OUT_OF_HOST_MEMORY;
+         }
+
+         for (uint32_t plane = 0; plane < image_data->num_planes; plane++)
+         {
+            assert(image_data->stride[plane] >= 0);
+            image_layout[plane].offset = image_data->offset[plane];
+            image_layout[plane].rowPitch = static_cast<uint32_t>(image_data->stride[plane]);
+         }
+
+         if (is_disjoint)
+         {
+            image_create_info.flags |= VK_IMAGE_CREATE_DISJOINT_BIT;
+         }
+
          VkImageDrmFormatModifierExplicitCreateInfoEXT drm_mod_info = {};
          drm_mod_info.sType = VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_EXPLICIT_CREATE_INFO_EXT;
          drm_mod_info.pNext = image_create_info.pNext;
          drm_mod_info.drmFormatModifier = DRM_FORMAT_MOD_LINEAR;
-         drm_mod_info.drmFormatModifierPlaneCount = 1;
-         drm_mod_info.pPlaneLayouts = &image_layout;
+         drm_mod_info.drmFormatModifierPlaneCount = image_data->num_planes;
+         drm_mod_info.pPlaneLayouts = image_layout.data();
 
          VkExternalMemoryImageCreateInfoKHR external_info = {};
          external_info.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO_KHR;
@@ -322,88 +490,87 @@ VkResult swapchain::allocate_image(const VkImageCreateInfo &image_create_info, w
          return result;
       }
       {
-         VkMemoryFdPropertiesKHR mem_props = {};
-         mem_props.sType = VK_STRUCTURE_TYPE_MEMORY_FD_PROPERTIES_KHR;
-
-         result = m_device_data.disp.GetMemoryFdPropertiesKHR(m_device, VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT,
-                                                              image_data->buffer_fd, &mem_props);
-         if (result != VK_SUCCESS)
+         if (is_disjoint)
          {
-            WSI_PRINT_ERROR("Error querying Fd properties.\n");
-            return result;
-         }
+            util::vector<VkBindImageMemoryInfo> bind_img_mem_infos(m_allocator);
+            if (!bind_img_mem_infos.try_resize(image_data->num_planes))
+            {
+               return VK_ERROR_OUT_OF_HOST_MEMORY;
+            }
 
-         uint32_t mem_idx;
-         for (mem_idx = 0; mem_idx < VK_MAX_MEMORY_TYPES; mem_idx++)
-         {
-            if (mem_props.memoryTypeBits & (1 << mem_idx))
+            util::vector<VkBindImagePlaneMemoryInfo> bind_plane_mem_infos(m_allocator);
+            if (!bind_plane_mem_infos.try_resize(image_data->num_planes))
+            {
+               return VK_ERROR_OUT_OF_HOST_MEMORY;
+            }
+
+            for (uint32_t plane = 0; plane < image_data->num_planes; plane++)
             {
-               break;
+               const auto fd_index = get_same_fd_index(image_data->buffer_fd[plane], image_data->buffer_fd);
+               if (fd_index == plane)
+               {
+                  result = allocate_plane_memory(image_data->buffer_fd[plane], &image_data->memory[fd_index]);
+                  if (result != VK_SUCCESS)
+                  {
+                     return result;
+                  }
+               }
+
+               bind_plane_mem_infos[plane].planeAspect = plane_flag_bits[plane];
+               bind_plane_mem_infos[plane].sType = VK_STRUCTURE_TYPE_BIND_IMAGE_PLANE_MEMORY_INFO;
+               bind_plane_mem_infos[plane].pNext = NULL;
+
+               bind_img_mem_infos[plane].sType = VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO;
+               bind_img_mem_infos[plane].pNext = &bind_plane_mem_infos[plane];
+               bind_img_mem_infos[plane].image = *image;
+               bind_img_mem_infos[plane].memory = image_data->memory[fd_index];
             }
+
+            result = m_device_data.disp.BindImageMemory2KHR(m_device, bind_img_mem_infos.size(),
+                                                            bind_img_mem_infos.data());
          }
-         off_t dma_buf_size = lseek(image_data->buffer_fd, 0, SEEK_END);
-         if (dma_buf_size < 0)
+         else
          {
-            WSI_PRINT_ERROR("Failed to get DMA Buf size.\n");
-            return VK_ERROR_OUT_OF_HOST_MEMORY;
-         }
-
-         VkImportMemoryFdInfoKHR import_mem_info = {};
-         import_mem_info.sType = VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHR;
-         import_mem_info.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT;
-         import_mem_info.fd = image_data->buffer_fd;
+            /* Make sure one fd has been allocated. */
+            for (uint32_t plane = 1; plane < image_data->num_planes; plane++)
+            {
+               if (image_data->buffer_fd[plane] != image_data->buffer_fd[0])
+               {
+                  WSI_PRINT_ERROR("Different fds per plane for a non disjoint image.\n");
+                  return VK_ERROR_INITIALIZATION_FAILED;
+               }
+            }
 
-         VkMemoryAllocateInfo alloc_info = {};
-         alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
-         alloc_info.pNext = &import_mem_info;
-         alloc_info.allocationSize = static_cast<uint64_t>(dma_buf_size);
-         alloc_info.memoryTypeIndex = mem_idx;
+            result = allocate_plane_memory(image_data->buffer_fd[0], &image_data->memory[0]);
+            if (result != VK_SUCCESS)
+            {
+               return result;
+            }
 
-         result = m_device_data.disp.AllocateMemory(m_device, &alloc_info, get_allocation_callbacks(), &image_data->memory);
-      }
-      if (result != VK_SUCCESS)
-      {
-         WSI_PRINT_ERROR("Failed to import memory.\n");
-         return result;
+            result = m_device_data.disp.BindImageMemory(m_device, *image, image_data->memory[0], 0);
+         }
       }
-      result = m_device_data.disp.BindImageMemory(m_device, *image, image_data->memory, 0);
    }
 
    return result;
 }
 
-VkResult swapchain::create_image(const VkImageCreateInfo &image_create_info, swapchain_image &image)
+VkResult swapchain::create_image(VkImageCreateInfo image_create_info, swapchain_image &image)
 {
-   uint32_t fourcc = util::drm::vk_to_drm_format(image_create_info.format);
-
-   int res;
-   VkResult result = VK_SUCCESS;
-
-   wayland_image_data *image_data = nullptr;
-   VkFenceCreateInfo fenceInfo = { VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, nullptr, 0 };
-
    /* Create image_data */
-   if (get_allocation_callbacks() != nullptr)
-   {
-      image_data = static_cast<wayland_image_data *>(
-         get_allocation_callbacks()->pfnAllocation(get_allocation_callbacks()->pUserData, sizeof(wayland_image_data),
-                                                   alignof(wayland_image_data), VK_SYSTEM_ALLOCATION_SCOPE_OBJECT));
-   }
-   else
-   {
-      image_data = static_cast<wayland_image_data *>(malloc(sizeof(wayland_image_data)));
-   }
+   auto image_data = m_allocator.create<wayland_image_data>(1);
    if (image_data == nullptr)
    {
       return VK_ERROR_OUT_OF_HOST_MEMORY;
    }
 
-   image.data = reinterpret_cast<void *>(image_data);
+   image.data = image_data;
    image.status = swapchain_image::FREE;
-   result = allocate_image(image_create_info, image_data, &image.image);
+   VkResult result = allocate_image(image_create_info, image_data, &image.image);
    if (result != VK_SUCCESS)
    {
       WSI_PRINT_ERROR("Failed to allocate image.\n");
+      destroy_image(image);
       return result;
    }
 
@@ -417,15 +584,22 @@ VkResult swapchain::create_image(const VkImageCreateInfo &image_create_info, swa
    }
 
    zwp_linux_buffer_params_v1 *params = zwp_linux_dmabuf_v1_create_params(dmabuf_interface_proxy.get());
-   zwp_linux_buffer_params_v1_add(params, image_data->buffer_fd, 0, image_data->offset, image_data->stride, 0, 0);
-   res = zwp_linux_buffer_params_v1_add_listener(params, &params_listener, &image_data->buffer);
+
+   for (uint32_t plane = 0; plane < image_data->num_planes; plane++)
+   {
+      zwp_linux_buffer_params_v1_add(params, image_data->buffer_fd[plane], plane,
+                                     image_data->offset[plane], image_data->stride[plane], 0, 0);
+   }
+
+   auto res = zwp_linux_buffer_params_v1_add_listener(params, &params_listener, &image_data->buffer);
    if (res < 0)
    {
       destroy_image(image);
       return VK_ERROR_INITIALIZATION_FAILED;
    }
-   zwp_linux_buffer_params_v1_create(params, image_create_info.extent.width, image_create_info.extent.height, fourcc,
-                                     0);
+   const auto fourcc = util::drm::vk_to_drm_format(image_create_info.format);
+   zwp_linux_buffer_params_v1_create(params, image_create_info.extent.width,
+                                     image_create_info.extent.height, fourcc, 0);
 
    /* TODO: don't roundtrip - we should be able to send the create request now,
     * and only wait for it on first present. only do this once, not for all buffers created */
@@ -448,6 +622,7 @@ VkResult swapchain::create_image(const VkImageCreateInfo &image_create_info, swa
    }
 
    /* Initialize presentation fence. */
+   VkFenceCreateInfo fenceInfo = { VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, nullptr, 0 };
    result = m_device_data.disp.CreateFence(m_device, &fenceInfo, get_allocation_callbacks(), &image.present_fence);
    if (result != VK_SUCCESS)
    {
@@ -571,23 +746,24 @@ void swapchain::destroy_image(swapchain_image &image)
       {
          wl_buffer_destroy(image_data->buffer);
       }
-      if (image_data->memory != VK_NULL_HANDLE)
-      {
-         m_device_data.disp.FreeMemory(m_device, image_data->memory, get_allocation_callbacks());
-      }
-      else if (image_data->buffer_fd >= 0)
-      {
-         close(image_data->buffer_fd);
-      }
 
-      if (get_allocation_callbacks() != nullptr)
-      {
-         get_allocation_callbacks()->pfnFree(get_allocation_callbacks()->pUserData, image_data);
-      }
-      else
+      for (uint32_t plane = 0; plane < image_data->num_planes; plane++)
       {
-         free(image_data);
+         if (image_data->memory[plane] != VK_NULL_HANDLE)
+         {
+            m_device_data.disp.FreeMemory(m_device, image_data->memory[plane], get_allocation_callbacks());
+         }
+         else if (image_data->buffer_fd[plane] >= 0)
+         {
+            const auto same_fd_index = get_same_fd_index(image_data->buffer_fd[plane], image_data->buffer_fd);
+            if (same_fd_index == plane)
+            {
+               close(image_data->buffer_fd[plane]);
+            }
+         }
       }
+
+      m_allocator.destroy(1, image_data);
       image.data = nullptr;
    }
 
index 59bac5c..66bd5f7 100644 (file)
@@ -67,7 +67,7 @@ protected:
     * will return VK_ERROR_OUT_OF_DEVICE_MEMORY or VK_ERROR_INITIALIZATION_FAILED
     * depending on the error that occurred.
     */
-   VkResult create_image(const VkImageCreateInfo &image_create_info, swapchain_image &image) override;
+   VkResult create_image(VkImageCreateInfo image_create_info, swapchain_image &image) override;
 
    /**
     * @brief Method to present an image
@@ -103,7 +103,8 @@ protected:
 
 private:
    struct wayland_image_data;
-   VkResult allocate_image(const VkImageCreateInfo &image_create_info, wayland_image_data *image_data, VkImage *image);
+
+   VkResult allocate_image(VkImageCreateInfo &image_create_info, wayland_image_data *image_data, VkImage *image);
 
    struct wl_display *m_display;
    struct wl_surface *m_surface;
@@ -126,6 +127,47 @@ private:
     * callback to indicate the server is ready for the next buffer.
     */
    bool m_present_pending;
+
+   /*
+    * @brief Allocate memory for an image plane.
+    *
+    * Allocates a VkDeviceMemory object from a given fd for an image plane. First
+    * it makes a call to get_fd_mem_type_index() to acquire the memory type for
+    * the given fd and then it allocates device memory by calling vkAllocateMemory().
+    *
+    * @param      fd     The plane's fd.
+    * @param[out] memory The allocated VkDeviceMemory object.
+    *
+    * @return VK_SUCCESS on success. If one of the functions that are being called
+    * fails its return value is returned. VK_ERROR_OUT_OF_HOST_MEMORY is returned
+    * when the host gets out of memory.
+    */
+   VkResult allocate_plane_memory(int fd, VkDeviceMemory *memory);
+
+   /*
+    * @brief Get the memory type which the specified file descriptor can be
+    * imported as.
+    *
+    * @param      fd      The given fd.
+    * @param[out] mem_idx The index of the supported memory type.
+    *
+    * @return VK_SUCCESS on success. On failure the error value of
+    * vkGetMemoryFdPropertiesKHR is returned.
+    */
+   VkResult get_fd_mem_type_index(int fd, uint32_t &mem_idx);
+
+   /*
+    * @brief Get the properties a format has when combined with a DRM modifier.
+    *
+    * @param      format            The target format.
+    * @param[out] format_props_list A vector which will store the supported properties
+    *                               for every modifier.
+    *
+    * @return VK_SUCCESS on success. VK_ERROR_OUT_OF_HOST_MEMORY is returned when
+    * the host gets out of memory.
+    */
+   VkResult get_drm_format_properties(
+      VkFormat format, util::vector<VkDrmFormatModifierPropertiesEXT> &format_props_list);
 };
 } // namespace wayland
 } // namespace wsi