[Mesa-dev] [RFC v2 16/22] RFC: anv: Implement VK_MESAX_external_image_dma_buf

Louis-Francis Ratté-Boulianne lfrb at collabora.com
Thu Aug 31 04:24:24 UTC 2017


From: Chad Versace <chadversary at chromium.org>

For now, we support dma_buf images for only a single format,
VK_FORMAT_R8G8B8A8_UNORM.  And the image must be a "simple" image: 2D,
single-sample, non-mipmappped, non-array, non-cube.
---
 src/intel/vulkan/anv_extensions.py |   1 +
 src/intel/vulkan/anv_formats.c     | 144 ++++++++++++++++++++++++++++-
 src/intel/vulkan/anv_image.c       | 182 ++++++++++++++++++++++++++++++++++---
 src/intel/vulkan/anv_private.h     |   6 ++
 4 files changed, 320 insertions(+), 13 deletions(-)

diff --git a/src/intel/vulkan/anv_extensions.py b/src/intel/vulkan/anv_extensions.py
index 3abadff2dc..5601aaf60d 100644
--- a/src/intel/vulkan/anv_extensions.py
+++ b/src/intel/vulkan/anv_extensions.py
@@ -76,6 +76,7 @@ EXTENSIONS = [
     Extension('VK_KHR_xcb_surface',                       6, 'VK_USE_PLATFORM_XCB_KHR'),
     Extension('VK_KHR_xlib_surface',                      6, 'VK_USE_PLATFORM_XLIB_KHR'),
     Extension('VK_KHX_multiview',                         1, True),
+    Extension('VK_MESAX_external_image_dma_buf',          0, True),
     Extension('VK_MESAX_external_memory_dma_buf',         0, True),
 ]
 
diff --git a/src/intel/vulkan/anv_formats.c b/src/intel/vulkan/anv_formats.c
index 83a0b5ad68..64cbe69718 100644
--- a/src/intel/vulkan/anv_formats.c
+++ b/src/intel/vulkan/anv_formats.c
@@ -466,16 +466,64 @@ void anv_GetPhysicalDeviceFormatProperties(
                pFormatProperties);
 }
 
+static void
+get_dma_buf_format_props(struct anv_physical_device *phys_dev,
+                         VkFormat vk_format,
+                         VkDmaBufFormatPropertiesMESAX *props)
+{
+   struct anv_format anv_format = anv_formats[vk_format];
+   VK_OUTARRAY_MAKE(mod_props, props->pModifierProperties,
+                    &props->modifierCount);
+
+   VkFormatFeatureFlags image_features = 0;
+   if (vk_format == VK_FORMAT_R8G8B8A8_UNORM) {
+      /* FINISHME: Support more formats for dma_buf images. */
+
+      /* For dma_buf images, we must use the exact format provided by the
+       * user.  We must not adjust the format, as we do for non-external
+       * images, with swizzles and other tricks. In other words, the image's
+       * "base" format and "adjusted" format must be the same.
+       */
+      image_features = get_image_format_properties(&phys_dev->info,
+                        /*base format*/ anv_format.isl_format,
+                        /*adjusted format*/ anv_format);
+   }
+
+   if (image_features == 0)
+      return;
+
+   /* Return DRM format modifiers in order of decreasing preference. */
+   vk_outarray_append(&mod_props, p) {
+      p->drmFormatModifier = I915_FORMAT_MOD_Y_TILED;
+      p->imageFeatures = image_features;
+   }
+
+   vk_outarray_append(&mod_props, p) {
+      p->drmFormatModifier = I915_FORMAT_MOD_X_TILED;
+      p->imageFeatures = image_features;
+   }
+
+   vk_outarray_append(&mod_props, p) {
+      p->drmFormatModifier = DRM_FORMAT_MOD_LINEAR;
+      p->imageFeatures = image_features;
+   }
+}
+
 void anv_GetPhysicalDeviceFormatProperties2KHR(
     VkPhysicalDevice                            physicalDevice,
     VkFormat                                    format,
     VkFormatProperties2KHR*                     pFormatProperties)
 {
+   ANV_FROM_HANDLE(anv_physical_device, phys_dev, physicalDevice);
+
    anv_GetPhysicalDeviceFormatProperties(physicalDevice, format,
                                          &pFormatProperties->formatProperties);
 
    vk_foreach_struct(ext, pFormatProperties->pNext) {
       switch (ext->sType) {
+      case VK_STRUCTURE_TYPE_DMA_BUF_FORMAT_PROPERTIES_MESAX:
+         get_dma_buf_format_props(phys_dev, format, (VkDmaBufFormatPropertiesMESAX *) ext);
+         break;
       default:
          anv_debug_ignored_stype(ext->sType);
          break;
@@ -680,6 +728,91 @@ static const VkExternalMemoryPropertiesKHR dma_buf_mem_props = {
       VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_MESAX,
 };
 
+static VkResult
+get_dma_buf_image_format_props(struct anv_physical_device *phys_dev,
+                               const VkPhysicalDeviceImageFormatInfo2KHR *base_info,
+                               VkImageFormatProperties2KHR *base_props,
+                               VkExternalImageFormatPropertiesKHR *external_props,
+                               VkDmaBufImageFormatPropertiesMESAX *dma_buf_props)
+{
+   /* We reject vkGetPhysicalDeviceImageFormatProperties2KHR() on the
+    * DMA_BUF_BIT unless the user adds VkDmaBufImageFormatPropertiesMESAX to
+    * the output chain. The spec permits this behavior but does not require
+    * it.
+    */
+   if (dma_buf_props == NULL) {
+      return vk_errorf(VK_ERROR_FORMAT_NOT_SUPPORTED,
+                       "dma_buf images require VkDmaBufImageFormatPropertiesMESAX");
+   }
+
+   VK_OUTARRAY_MAKE(mod_props, dma_buf_props->pModifierProperties,
+                    &dma_buf_props->modifierCount);
+
+   if (base_info->type != VK_IMAGE_TYPE_2D) {
+      return vk_errorf(VK_ERROR_FORMAT_NOT_SUPPORTED,
+                       "dma_buf images require VK_IMAGE_TYPE_2D");
+   }
+
+   if (base_info->flags != 0) {
+      return vk_errorf(VK_ERROR_FORMAT_NOT_SUPPORTED,
+                       "dma_buf images support no VkImageCreateFlags");
+   }
+
+   if (base_info->usage & VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) {
+      return vk_errorf(VK_ERROR_FORMAT_NOT_SUPPORTED,
+                       "dma_buf images do not support "
+                       "VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT");
+   }
+
+   if (base_info->format != VK_FORMAT_R8G8B8A8_UNORM) {
+      /* FINISHME: Support more formats for dma_buf images. */
+      return vk_errorf(VK_ERROR_FORMAT_NOT_SUPPORTED,
+                       "dma_buf images do not support VkFormat 0x%x",
+                       base_info->format);
+   }
+
+   /* We restrict some properties on dma_buf images. */
+   base_props->imageFormatProperties.maxMipLevels = 1;
+   base_props->imageFormatProperties.maxArrayLayers = 1;
+   base_props->imageFormatProperties.sampleCounts = VK_SAMPLE_COUNT_1_BIT;
+
+   /* Return DRM format modifiers in order of decreasing preference. */
+   if (base_info->tiling == VK_IMAGE_TILING_OPTIMAL) {
+      vk_outarray_append(&mod_props, p) {
+         p->drmFormatModifier = I915_FORMAT_MOD_Y_TILED;
+         p->maxRowPitch = 0;
+         p->rowPitchAlignment = 0;
+         p->imageFormatProperties = base_props->imageFormatProperties;
+      }
+
+      vk_outarray_append(&mod_props, p) {
+         p->drmFormatModifier = I915_FORMAT_MOD_X_TILED;
+         p->maxRowPitch = 0;
+         p->rowPitchAlignment = 0;
+         p->imageFormatProperties = base_props->imageFormatProperties;
+      }
+   }
+
+   vk_outarray_append(&mod_props, p) {
+      p->drmFormatModifier = DRM_FORMAT_MOD_LINEAR;
+      p->maxRowPitch = 1 << 18; /* See RENDER_SURFACE_STATE::SurfacePitch */
+      p->rowPitchAlignment = 1;
+      p->imageFormatProperties = base_props->imageFormatProperties;
+   }
+
+   /* Clobber the base properties.
+    *
+    * TODO(chadv): Explain why the interaction between
+    * VK_MESAX_external_image_dma_buf and VK_KHX_external_memory_capabilities
+    * requires us to zero the base properties.
+    */
+   base_props->imageFormatProperties = (VkImageFormatProperties) {0};
+
+   external_props->externalMemoryProperties = dma_buf_mem_props;
+
+   return vk_outarray_status(&mod_props);
+}
+
 VkResult anv_GetPhysicalDeviceImageFormatProperties2KHR(
     VkPhysicalDevice                            physicalDevice,
     const VkPhysicalDeviceImageFormatInfo2KHR*  base_info,
@@ -688,6 +821,7 @@ VkResult anv_GetPhysicalDeviceImageFormatProperties2KHR(
    ANV_FROM_HANDLE(anv_physical_device, physical_device, physicalDevice);
    const VkPhysicalDeviceExternalImageFormatInfoKHR *external_info = NULL;
    VkExternalImageFormatPropertiesKHR *external_props = NULL;
+   VkDmaBufImageFormatPropertiesMESAX *dma_buf_props = NULL;
    VkResult result;
 
    result = anv_get_image_format_properties(physical_device, base_info,
@@ -713,6 +847,9 @@ VkResult anv_GetPhysicalDeviceImageFormatProperties2KHR(
       case VK_STRUCTURE_TYPE_EXTERNAL_IMAGE_FORMAT_PROPERTIES_KHR:
          external_props = (void *) s;
          break;
+      case VK_STRUCTURE_TYPE_DMA_BUF_IMAGE_FORMAT_PROPERTIES_MESAX:
+         dma_buf_props = (void *) s;
+         break;
       default:
          anv_debug_ignored_stype(s->sType);
          break;
@@ -732,7 +869,12 @@ VkResult anv_GetPhysicalDeviceImageFormatProperties2KHR(
             external_props->externalMemoryProperties = opaque_fd_props;
          break;
       case VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_MESAX:
-         /* Fallthrough. We support dma_buf for VkBuffer but not yet VkImage. */
+         result = get_dma_buf_image_format_props(physical_device, base_info,
+                                                 base_props, external_props,
+                                                 dma_buf_props);
+         if (result != VK_SUCCESS)
+            goto fail;
+         break;
       default:
          /* From the Vulkan 1.0.42 spec:
           *
diff --git a/src/intel/vulkan/anv_image.c b/src/intel/vulkan/anv_image.c
index 48101d4461..1f8e5a208d 100644
--- a/src/intel/vulkan/anv_image.c
+++ b/src/intel/vulkan/anv_image.c
@@ -108,11 +108,31 @@ get_surface(struct anv_image *image, VkImageAspectFlags aspect)
 }
 
 static isl_tiling_flags_t
-choose_isl_tiling_flags(const struct anv_image_create_info *anv_info)
+choose_isl_tiling_flags(const struct anv_image_create_info *anv_info,
+                        const VkImportImageDmaBufInfoMESAX *import_dma_buf_info,
+                        const VkExportImageDmaBufInfoMESAX *export_dma_buf_info)
 {
-   isl_tiling_flags_t flags;
-
-   if (anv_info->vk_info->tiling == VK_IMAGE_TILING_LINEAR) {
+   isl_tiling_flags_t flags = 0;
+
+   if (import_dma_buf_info) {
+      uint64_t mod = import_dma_buf_info->drmFormatModifier;
+      enum isl_tiling t;
+      enum isl_aux_usage a;
+      if (isl_tiling_from_drm_format_mod(mod, &t, &a) &&
+          a == ISL_AUX_USAGE_NONE) {
+         flags = 1 << t;
+      }
+   } else if (export_dma_buf_info) {
+      for (uint32_t i = 0; i < export_dma_buf_info->drmFormatModifierCount; ++i) {
+         uint64_t mod = export_dma_buf_info->pDrmFormatModifiers[i];
+         enum isl_tiling t;
+         enum isl_aux_usage a;
+         if (isl_tiling_from_drm_format_mod(mod, &t, &a) &&
+             a == ISL_AUX_USAGE_NONE) {
+            flags |= 1 << t;
+         }
+      }
+   } else if (anv_info->vk_info->tiling == VK_IMAGE_TILING_LINEAR) {
       flags = ISL_TILING_LINEAR_BIT;
    } else {
       flags = ISL_TILING_ANY_MASK;
@@ -121,9 +141,49 @@ choose_isl_tiling_flags(const struct anv_image_create_info *anv_info)
    if (anv_info->isl_tiling_flags)
       flags &= anv_info->isl_tiling_flags;
 
+   assert(flags != 0);
+
    return flags;
 }
 
+static enum isl_format
+choose_isl_format(const struct anv_device *dev,
+                  const VkImageCreateInfo *base_info,
+                  VkImageAspectFlagBits aspect,
+                  VkExternalMemoryHandleTypeFlagsKHR handle_types)
+{
+   /* We don't yet support images compatible with multiple handle types
+    * because our choice of format depends on the handle type.  For
+    * non-external images and opaque fd images, we choose an "adjusted"
+    * format. For dma_buf images, we must use the exact format provided by the
+    * user.
+    */
+   assert(__builtin_popcount(handle_types) <= 1);
+
+   if (handle_types & VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_MESAX) {
+      return anv_get_raw_isl_format(&dev->info, base_info->format);
+   } else {
+      return anv_get_isl_format(&dev->info, base_info->format, aspect,
+                                base_info->tiling);
+   }
+}
+
+static uint32_t
+choose_row_pitch(const struct anv_image_create_info *anv_info,
+                 const VkImportImageDmaBufPlaneInfoMESAX *plane_info)
+{
+   if (anv_info->stride != 0)
+      return anv_info->stride;
+
+   if (plane_info) {
+      assert(plane_info->rowPitch > 0);
+      return plane_info->rowPitch;
+   }
+
+   /* Let isl choose the pitch. */
+   return 0;
+}
+
 static void
 set_min_surface_offset(const struct anv_image *image, struct anv_surface *surf)
 {
@@ -328,8 +388,24 @@ static void
 make_aux_surface_maybe(const struct anv_device *dev,
                        const VkImageCreateInfo *base_info,
                        VkImageAspectFlags aspect,
+                       VkExternalMemoryHandleTypeFlagsKHR handle_types,
                        struct anv_image *image)
 {
+   /* We don't yet support images compatible with multiple handle types
+    * because our choice of aux surface depends on the handle type. For
+    * non-external images and opaque fd images, we choose the optimal aux
+    * surface. For dma_buf images, we must use the aux surface specified by
+    * the user.
+    */
+   assert(__builtin_popcount(handle_types) <= 1);
+
+   if (handle_types & VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_MESAX) {
+      /* As of 2017-02-25, drm_fourcc.h still does not define a format modifier
+       * for any aux surface.
+       */
+      return;
+   }
+
    if (aspect == VK_IMAGE_ASPECT_DEPTH_BIT) {
       make_hiz_surface_maybe(dev, base_info, image);
    } else if (aspect == VK_IMAGE_ASPECT_COLOR_BIT && base_info->samples == 1) {
@@ -348,7 +424,10 @@ make_aux_surface_maybe(const struct anv_device *dev,
 static void
 make_main_surface(const struct anv_device *dev,
                   const struct anv_image_create_info *anv_info,
+                  const VkImportImageDmaBufInfoMESAX *import_dma_buf_info,
+                  const VkExportImageDmaBufInfoMESAX *export_dma_buf_info,
                   VkImageAspectFlags aspect,
+                  VkExternalMemoryHandleTypeFlagsKHR handle_types,
                   struct anv_image *image)
 {
    const VkImageCreateInfo *base_info = anv_info->vk_info;
@@ -360,14 +439,21 @@ make_main_surface(const struct anv_device *dev,
       [VK_IMAGE_TYPE_3D] = ISL_SURF_DIM_3D,
    };
 
-   const isl_tiling_flags_t tiling_flags = choose_isl_tiling_flags(anv_info);
+   const VkImportImageDmaBufPlaneInfoMESAX *plane_info = NULL;
+   if (import_dma_buf_info)
+      plane_info = &import_dma_buf_info->pPlanes[0];
+
+   const isl_tiling_flags_t tiling_flags =
+      choose_isl_tiling_flags(anv_info, import_dma_buf_info, export_dma_buf_info);
+   const uint32_t row_pitch = choose_row_pitch(anv_info, plane_info);
+
    struct anv_surface *anv_surf = get_surface(image, aspect);
 
    image->extent = anv_sanitize_image_extent(base_info->imageType,
                                              base_info->extent);
 
-   enum isl_format format = anv_get_isl_format(&dev->info, base_info->format,
-                                               aspect, base_info->tiling);
+   const enum isl_format format =
+      choose_isl_format(dev, base_info, aspect, handle_types);
    assert(format != ISL_FORMAT_UNSUPPORTED);
 
    ok = isl_surf_init(&dev->isl_dev, &anv_surf->isl,
@@ -380,7 +466,7 @@ make_main_surface(const struct anv_device *dev,
       .array_len = base_info->arrayLayers,
       .samples = base_info->samples,
       .min_alignment = 0,
-      .row_pitch = anv_info->stride,
+      .row_pitch = row_pitch,
       .usage = choose_isl_surf_usage(base_info->flags, image->usage, aspect),
       .tiling_flags = tiling_flags);
 
@@ -389,7 +475,12 @@ make_main_surface(const struct anv_device *dev,
     */
    assert(ok);
 
-   set_min_surface_offset(image, anv_surf);
+   if (plane_info) {
+      anv_surf->offset = plane_info->offset;
+   } else {
+      set_min_surface_offset(image, anv_surf);
+   }
+
    add_surface(image, anv_surf);
 }
 
@@ -401,10 +492,36 @@ anv_image_create(VkDevice _device,
 {
    ANV_FROM_HANDLE(anv_device, device, _device);
    const VkImageCreateInfo *base_info = anv_info->vk_info;
+   const VkImportImageDmaBufInfoMESAX *import_dma_buf_info = NULL;
+   const VkExportImageDmaBufInfoMESAX *export_dma_buf_info = NULL;
+   VkExternalMemoryHandleTypeFlagsKHR handle_types = 0;
    struct anv_image *image = NULL;
 
    assert(base_info->sType == VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO);
 
+   vk_foreach_struct_const(s, base_info->pNext) {
+      switch (s->sType) {
+      case VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO_KHR:
+         handle_types =
+            ((const VkExternalMemoryImageCreateInfoKHR *) s)->handleTypes;
+         assert((handle_types & ~ANV_SUPPORTED_MEMORY_HANDLE_TYPES) == 0);
+         break;
+      case VK_STRUCTURE_TYPE_IMPORT_IMAGE_DMA_BUF_INFO_MESAX:
+         import_dma_buf_info = (const void *) s;
+         break;
+      case VK_STRUCTURE_TYPE_EXPORT_IMAGE_DMA_BUF_INFO_MESAX:
+         export_dma_buf_info = (const void *) s;
+         break;
+      default:
+         anv_debug_ignored_stype(s->sType);
+         break;
+      }
+   }
+
+   /* For dma_buf images, we require the user to provide DRM format modifiers. */
+   assert((bool)(handle_types & VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_MESAX) ==
+          (import_dma_buf_info || export_dma_buf_info));
+
    anv_assert(base_info->mipLevels > 0);
    anv_assert(base_info->arrayLayers > 0);
    anv_assert(base_info->samples > 0);
@@ -431,8 +548,9 @@ anv_image_create(VkDevice _device,
    uint32_t b;
    for_each_bit(b, image->aspects) {
       VkImageAspectFlagBits aspect = 1 << b;
-      make_main_surface(device, anv_info, aspect, image);
-      make_aux_surface_maybe(device, base_info, aspect, image);
+      make_main_surface(device, anv_info, import_dma_buf_info,
+                        export_dma_buf_info, aspect, handle_types, image);
+      make_aux_surface_maybe(device, base_info, aspect, handle_types, image);
    }
 
    *pImage = anv_image_to_handle(image);
@@ -535,13 +653,53 @@ void anv_GetImageSubresourceLayout(
    }
 }
 
+static VkResult
+get_image_dma_buf_props(const struct anv_image *image,
+                        VkImageDmaBufPropertiesMESAX* dma_buf_props)
+{
+   VK_OUTARRAY_MAKE(planes, dma_buf_props->pPlanes,
+                    &dma_buf_props->planeCount);
+   bool ok UNUSED;
+
+   /* For now, we support exactly one format for dma_buf images. */
+   assert(image->vk_format == VK_FORMAT_R8G8B8A8_UNORM);
+
+   /* For now, We don't support dma_buf images with auxiliary surfaces. */
+   assert(image->aux_surface.isl.size == 0);
+
+   ok = isl_surf_get_drm_format_mod(&image->color_surface.isl,
+                                    image->aux_usage,
+                                    &dma_buf_props->drmFormatModifier);
+   assert(ok);
+
+   vk_outarray_append(&planes, p) {
+      p->offset = image->color_surface.offset;
+      p->rowPitch = image->color_surface.isl.row_pitch;
+   }
+
+   return vk_outarray_status(&planes);
+}
+
 VkResult anv_GetImagePropertiesEXT(
     VkDevice                                    device_h,
     VkImage                                     image_h,
     VkImagePropertiesEXT*                       base_props)
 {
+   ANV_FROM_HANDLE(anv_image, image, image_h);
+   VkResult result;
+
    vk_foreach_struct(s, base_props->pNext) {
-      anv_debug_ignored_stype(s->sType);
+      switch (s->sType) {
+      case VK_STRUCTURE_TYPE_IMAGE_DMA_BUF_PROPERTIES_MESAX:
+         result = get_image_dma_buf_props(image,
+                                          (VkImageDmaBufPropertiesMESAX *) s);
+         if (result != VK_SUCCESS)
+            return result;
+         break;
+      default:
+         anv_debug_ignored_stype(s->sType);
+         break;
+      }
    }
 
    return VK_SUCCESS;
diff --git a/src/intel/vulkan/anv_private.h b/src/intel/vulkan/anv_private.h
index 08f69a0c8c..e841291a10 100644
--- a/src/intel/vulkan/anv_private.h
+++ b/src/intel/vulkan/anv_private.h
@@ -30,7 +30,13 @@
 #include <pthread.h>
 #include <assert.h>
 #include <stdint.h>
+
 #include <i915_drm.h>
+#include <drm_fourcc.h>
+
+#ifndef DRM_FORMAT_MOD_LINEAR /* new in Linux 4.10 */
+#define DRM_FORMAT_MOD_LINEAR 0
+#endif
 
 #ifdef HAVE_VALGRIND
 #include <valgrind.h>
-- 
2.13.0



More information about the mesa-dev mailing list