[intel][vulkan] Add VK_FUCHSIA_buffer_collection

Minimal implementation that passes current tests:
all plane bytes_per_row must match, and plane offsets
are just checked.

Change-Id: Ia13527936dd2a9351fea3790a7f33bb1198d65a4
diff --git a/include/vulkan/vulkan_core.h b/include/vulkan/vulkan_core.h
index 8c69900..1d03fc2 100644
--- a/include/vulkan/vulkan_core.h
+++ b/include/vulkan/vulkan_core.h
@@ -456,6 +456,8 @@
     VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_MEMORY_MODEL_FEATURES_KHR = 1000211000,
     VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PCI_BUS_INFO_PROPERTIES_EXT = 1000212000,
     VK_STRUCTURE_TYPE_IMAGEPIPE_SURFACE_CREATE_INFO_FUCHSIA = 1000214000,
+    VK_STRUCTURE_TYPE_BUFFER_COLLECTION_CREATE_INFO_FUCHSIA = 1001004000,
+    VK_STRUCTURE_TYPE_FUCHSIA_IMAGE_FORMAT_FUCHSIA = 1001004001,
     VK_STRUCTURE_TYPE_IMPORT_MEMORY_FUCHSIA_HANDLE_INFO_KHR = 1001000000,
     VK_STRUCTURE_TYPE_MEMORY_FUCHSIA_HANDLE_PROPERTIES_KHR = 1001000001,
     VK_STRUCTURE_TYPE_MEMORY_GET_FUCHSIA_HANDLE_INFO_KHR = 1001000002,
@@ -1285,6 +1287,7 @@
     VK_OBJECT_TYPE_DEBUG_UTILS_MESSENGER_EXT = 1000128000,
     VK_OBJECT_TYPE_VALIDATION_CACHE_EXT = 1000160000,
     VK_OBJECT_TYPE_ACCELERATION_STRUCTURE_NVX = 1000165000,
+    VK_OBJECT_TYPE_BUFFER_COLLECTION_FUCHSIA = 1001004002,
     VK_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_KHR = VK_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE,
     VK_OBJECT_TYPE_SAMPLER_YCBCR_CONVERSION_KHR = VK_OBJECT_TYPE_SAMPLER_YCBCR_CONVERSION,
     VK_OBJECT_TYPE_BEGIN_RANGE = VK_OBJECT_TYPE_UNKNOWN,
@@ -6191,6 +6194,7 @@
     VK_DEBUG_REPORT_OBJECT_TYPE_SAMPLER_YCBCR_CONVERSION_EXT = 1000156000,
     VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_EXT = 1000085000,
     VK_DEBUG_REPORT_OBJECT_TYPE_ACCELERATION_STRUCTURE_NVX_EXT = 1000165000,
+    VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_COLLECTION_FUCHSIA_EXT = 1001004003,
     VK_DEBUG_REPORT_OBJECT_TYPE_DEBUG_REPORT_EXT = VK_DEBUG_REPORT_OBJECT_TYPE_DEBUG_REPORT_CALLBACK_EXT_EXT,
     VK_DEBUG_REPORT_OBJECT_TYPE_VALIDATION_CACHE_EXT = VK_DEBUG_REPORT_OBJECT_TYPE_VALIDATION_CACHE_EXT_EXT,
     VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_KHR_EXT = VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_EXT,
diff --git a/include/vulkan/vulkan_fuchsia.h b/include/vulkan/vulkan_fuchsia.h
index 73a3152..9f32a9f 100644
--- a/include/vulkan/vulkan_fuchsia.h
+++ b/include/vulkan/vulkan_fuchsia.h
@@ -131,9 +131,50 @@
 #define VK_GOOGLE_IMAGE_USAGE_SCANOUT_SPEC_VERSION 1
 #define VK_GOOGLE_IMAGE_USAGE_SCANOUT_EXTENSION_NAME "VK_GOOGLE_image_usage_scanout"
 
+#define VK_FUCHSIA_buffer_collection 1
+VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkBufferCollectionFUCHSIA)
+
+#define VK_FUCHSIA_BUFFER_COLLECTION_SPEC_VERSION 1
+#define VK_FUCHSIA_BUFFER_COLLECTION_EXTENSION_NAME "VK_FUCHSIA_buffer_collection"
+
+typedef struct VkBufferCollectionCreateInfoFUCHSIA {
+    VkStructureType    sType;
+    const void*        pNext;
+    zx_handle_t        collectionToken;
+} VkBufferCollectionCreateInfoFUCHSIA;
+
+typedef struct VkFuchsiaImageFormatFUCHSIA {
+    VkStructureType    sType;
+    const void*        pNext;
+    const void*        imageFormat;
+    uint32_t           imageFormatSize;
+} VkFuchsiaImageFormatFUCHSIA;
+
+
+typedef VkResult (VKAPI_PTR *PFN_vkCreateBufferCollectionFUCHSIA)(VkDevice device, const VkBufferCollectionCreateInfoFUCHSIA* pImportInfo, const VkAllocationCallbacks* pAllocator, VkBufferCollectionFUCHSIA* pCollection);
+typedef VkResult (VKAPI_PTR *PFN_vkSetBufferCollectionConstraintsFUCHSIA)(VkDevice device, VkBufferCollectionFUCHSIA collection, const VkImageCreateInfo* pImageInfo);
+typedef void (VKAPI_PTR *PFN_vkDestroyBufferCollectionFUCHSIA)(VkDevice device, VkBufferCollectionFUCHSIA collection, const VkAllocationCallbacks* pAllocator);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR VkResult VKAPI_CALL vkCreateBufferCollectionFUCHSIA(
+    VkDevice                                    device,
+    const VkBufferCollectionCreateInfoFUCHSIA*  pImportInfo,
+    const VkAllocationCallbacks*                pAllocator,
+    VkBufferCollectionFUCHSIA*                  pCollection);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkSetBufferCollectionConstraintsFUCHSIA(
+    VkDevice                                    device,
+    VkBufferCollectionFUCHSIA                   collection,
+    const VkImageCreateInfo*                    pImageInfo);
+
+VKAPI_ATTR void VKAPI_CALL vkDestroyBufferCollectionFUCHSIA(
+    VkDevice                                    device,
+    VkBufferCollectionFUCHSIA                   collection,
+    const VkAllocationCallbacks*                pAllocator);
+#endif
 
 #ifdef __cplusplus
 }
 #endif
 
-#endif
+#endif
\ No newline at end of file
diff --git a/src/intel/vulkan/BUILD.gn b/src/intel/vulkan/BUILD.gn
index 7d95bad..8b39ea1 100644
--- a/src/intel/vulkan/BUILD.gn
+++ b/src/intel/vulkan/BUILD.gn
@@ -109,6 +109,7 @@
     "anv_image.c",
     "anv_intel.c",
     "anv_magma.c",
+    "anv_magma_buffer_collection.c",
     "anv_magma_connection.cc",
     "anv_nir.h",
     "anv_nir_add_base_work_group_id.c",
@@ -242,6 +243,7 @@
 
   inputs = [
     "anv_entrypoints_gen.py",
+    "anv_extensions.py",
     "$mesa_build_root/src/vulkan/registry/vk.xml",
   ]
 
diff --git a/src/intel/vulkan/anv_entrypoints_gen.py b/src/intel/vulkan/anv_entrypoints_gen.py
index 27b1153..c038ef1 100755
--- a/src/intel/vulkan/anv_entrypoints_gen.py
+++ b/src/intel/vulkan/anv_entrypoints_gen.py
@@ -515,6 +515,8 @@
         ext = '_KHR'
         if platform.upper() == 'XLIB_XRANDR':
             ext = '_EXT'
+        if platform.upper() == 'FUCHSIA':
+            ext = ''
         define = 'VK_USE_PLATFORM_' + platform.upper() + ext
         if 'protect' in extension.attrib:
           define = extension.attrib['protect']
diff --git a/src/intel/vulkan/anv_extensions.py b/src/intel/vulkan/anv_extensions.py
index 7ad7a19..82f1435 100755
--- a/src/intel/vulkan/anv_extensions.py
+++ b/src/intel/vulkan/anv_extensions.py
@@ -127,6 +127,7 @@
     Extension('VK_KHR_external_memory_fuchsia',           1, 'VK_USE_PLATFORM_FUCHSIA'),
     Extension('VK_KHR_external_semaphore_fuchsia',        1, 'VK_USE_PLATFORM_FUCHSIA'),
     Extension('VK_GOOGLE_image_usage_scanout',            1, 'VK_USE_PLATFORM_FUCHSIA'),
+    Extension('VK_FUCHSIA_buffer_collection',             1, 'VK_USE_PLATFORM_FUCHSIA'),
 ]
 
 class VkVersion:
diff --git a/src/intel/vulkan/anv_image.c b/src/intel/vulkan/anv_image.c
index c63c72b..6b6d2ee 100644
--- a/src/intel/vulkan/anv_image.c
+++ b/src/intel/vulkan/anv_image.c
@@ -35,7 +35,7 @@
 
 #include "vk_format_info.h"
 
-static isl_surf_usage_flags_t
+isl_surf_usage_flags_t
 choose_isl_surf_usage(VkImageCreateFlags vk_create_flags,
                       VkImageUsageFlags vk_usage,
                       isl_surf_usage_flags_t isl_extra_usage,
@@ -642,6 +642,46 @@
                                     pAllocator, pImage);
 #endif
 
+#if VK_USE_PLATFORM_FUCHSIA
+   const struct VkFuchsiaImageFormatFUCHSIA *image_format_fuchsia =
+      vk_find_struct_const(pCreateInfo->pNext, FUCHSIA_IMAGE_FORMAT_FUCHSIA);
+   if (image_format_fuchsia) {
+      const int kParamCount = 4;
+      struct anv_fuchsia_image_plane_params params[kParamCount];
+      isl_tiling_flags_t tiling_flags;
+      VkResult result = anv_image_params_from_fuchsia_image(device, pCreateInfo, params, &tiling_flags);
+      if (result != VK_SUCCESS)
+          return result;
+
+      // We support only one bytes_per_row for all planes.
+      uint32_t bytes_per_row = params[0].bytes_per_row;
+      for (uint32_t i = 1; i < kParamCount; i++) {
+         assert(params[i].bytes_per_row == 0 || params[i].bytes_per_row == bytes_per_row);
+      }
+      result = anv_image_create(device,
+        &(struct anv_image_create_info) {
+           .vk_info = pCreateInfo,
+           .stride = bytes_per_row,
+           .isl_tiling_flags = tiling_flags,
+           // Disable compression bc sysmem doesn't support it.
+           .isl_extra_usage_flags = ISL_SURF_USAGE_DISABLE_AUX_BIT,
+        },
+        pAllocator,
+        pImage);
+      if (result != VK_SUCCESS)
+        return result;
+
+      // Check that byte offsets match.
+      ANV_FROM_HANDLE(anv_image, image, *pImage);
+      for (uint32_t i = 0; i < kParamCount; i++) {
+         if (params[i].bytes_per_row) {
+            assert(params[i].byte_offset == image->planes[i].offset);
+         }
+      }
+      return VK_SUCCESS;
+   }
+#endif
+
    return anv_image_create(device,
       &(struct anv_image_create_info) {
          .vk_info = pCreateInfo,
diff --git a/src/intel/vulkan/anv_magma.h b/src/intel/vulkan/anv_magma.h
index c67e2824..9272702 100644
--- a/src/intel/vulkan/anv_magma.h
+++ b/src/intel/vulkan/anv_magma.h
@@ -10,6 +10,14 @@
 #include "i915_drm.h"
 #include "magma.h"
 
+#include <stdio.h>
+
+#if DEBUG
+#define ANV_MAGMA_DRET(ret) (ret == 0 ? ret : anv_magma_dret(__FILE__, __LINE__, ret))
+#else
+#define ANV_MAGMA_DRET(ret) (ret)
+#endif
+
 struct anv_connection {
    magma_connection_t connection;
 };
@@ -28,6 +36,9 @@
 
 void AnvMagmaReleaseConnection(struct anv_connection* connection);
 
+magma_status_t AnvMagmaGetSysmemConnection(struct anv_connection* connection,
+                                           magma_sysmem_connection_t* sysmem_connection_out);
+
 void AnvMagmaConnectionWait(struct anv_connection* connection, uint64_t buffer_id,
                             int64_t* timeout_ns);
 
@@ -42,6 +53,12 @@
 
 void AnvMagmaReleaseBuffer(struct anv_connection* connection, struct anv_magma_buffer* buffer);
 
+static inline int anv_magma_dret(const char* file, const int line, const int64_t ret)
+{
+   printf("%s:%d returning %ld\n", file, line, ret);
+   return ret;
+}
+
 #ifdef __cplusplus
 } // extern "C"
 #endif
diff --git a/src/intel/vulkan/anv_magma_buffer_collection.c b/src/intel/vulkan/anv_magma_buffer_collection.c
new file mode 100644
index 0000000..431b51d
--- /dev/null
+++ b/src/intel/vulkan/anv_magma_buffer_collection.c
@@ -0,0 +1,314 @@
+// Copyright 2019 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "anv_magma.h"
+#include "anv_private.h"
+#include "isl.h"
+#include "magma_sysmem.h"
+#include "vk_util.h"
+
+#if VK_USE_PLATFORM_FUCHSIA
+
+struct anv_buffer_collection {
+   magma_buffer_collection_t buffer_collection;
+};
+
+ANV_DEFINE_HANDLE_CASTS(anv_buffer_collection, VkBufferCollectionFUCHSIA)
+
+VkResult anv_CreateBufferCollectionFUCHSIA(VkDevice vk_device,
+                                           const VkBufferCollectionCreateInfoFUCHSIA* pCreateInfo,
+                                           const VkAllocationCallbacks* pAllocator,
+                                           VkBufferCollectionFUCHSIA* pCollection)
+{
+   ANV_FROM_HANDLE(anv_device, device, vk_device);
+
+   magma_sysmem_connection_t sysmem_connection;
+   magma_status_t status = AnvMagmaGetSysmemConnection(device->connection, &sysmem_connection);
+   if (status != MAGMA_STATUS_OK)
+      return ANV_MAGMA_DRET(VK_ERROR_DEVICE_LOST);
+
+   magma_buffer_collection_t magma_buffer_collection;
+   status = magma_buffer_collection_import(sysmem_connection, pCreateInfo->collectionToken,
+                                           &magma_buffer_collection);
+   if (status != MAGMA_STATUS_OK)
+      return ANV_MAGMA_DRET(VK_ERROR_INVALID_EXTERNAL_HANDLE);
+
+   struct anv_buffer_collection* buffer_collection =
+       vk_alloc2(&device->alloc, pAllocator, sizeof(*buffer_collection), 8,
+                 VK_SYSTEM_ALLOCATION_SCOPE_DEVICE);
+   buffer_collection->buffer_collection = magma_buffer_collection;
+   *pCollection = anv_buffer_collection_to_handle(buffer_collection);
+
+   return VK_SUCCESS;
+}
+
+void anv_DestroyBufferCollectionFUCHSIA(VkDevice vk_device, VkBufferCollectionFUCHSIA vk_collection,
+                                        const VkAllocationCallbacks* pAllocator)
+{
+   ANV_FROM_HANDLE(anv_device, device, vk_device);
+   ANV_FROM_HANDLE(anv_buffer_collection, buffer_collection, vk_collection);
+
+   magma_sysmem_connection_t sysmem_connection;
+   magma_status_t status = AnvMagmaGetSysmemConnection(device->connection, &sysmem_connection);
+   if (status != MAGMA_STATUS_OK)
+      return;
+
+   magma_buffer_collection_release(sysmem_connection, buffer_collection->buffer_collection);
+   vk_free2(&device->alloc, pAllocator, buffer_collection);
+}
+
+static VkResult
+get_image_format_constraints(VkDevice vk_device, const VkImageCreateInfo* pImageInfo,
+                             magma_image_format_constraints_t* image_constraints_out,
+                             isl_tiling_flags_t isl_tiling_flags)
+{
+   ANV_FROM_HANDLE(anv_device, device, vk_device);
+
+   const struct anv_format_plane plane_format = anv_get_format_plane(
+       &device->info, pImageInfo->format, VK_IMAGE_ASPECT_COLOR_BIT, pImageInfo->tiling);
+
+   const isl_surf_usage_flags_t isl_surf_usage =
+       choose_isl_surf_usage(pImageInfo->flags, // vk_create_flags
+                             pImageInfo->usage, // vk_usage
+                             0,                 // isl_extra_usage
+                             VK_IMAGE_ASPECT_COLOR_BIT);
+   enum isl_surf_dim dim;
+   switch (pImageInfo->imageType) {
+   case VK_IMAGE_TYPE_1D:
+      dim = ISL_SURF_DIM_1D;
+      break;
+   case VK_IMAGE_TYPE_2D:
+      dim = ISL_SURF_DIM_2D;
+      break;
+   default:
+      return ANV_MAGMA_DRET(VK_ERROR_FORMAT_NOT_SUPPORTED);
+   }
+
+   struct isl_surf_init_info isl_surf_init_info = {
+       .dim = dim,
+       .format = plane_format.isl_format,
+       .width = pImageInfo->extent.width / plane_format.denominator_scales[0],
+       .height = pImageInfo->extent.height / plane_format.denominator_scales[1],
+       .depth = pImageInfo->extent.depth,
+       .levels = pImageInfo->mipLevels,
+       .array_len = pImageInfo->arrayLayers,
+       .samples = pImageInfo->samples,
+       .min_alignment = 0,
+       .row_pitch = 0,
+       .usage = isl_surf_usage,
+       .tiling_flags = isl_tiling_flags};
+
+   struct isl_surf isl_surf;
+   if (!isl_surf_init_s(&device->isl_dev, &isl_surf, &isl_surf_init_info))
+      return ANV_MAGMA_DRET(VK_ERROR_FORMAT_NOT_SUPPORTED);
+
+   assert(pImageInfo->extent.width);
+   magma_image_format_constraints_t image_constraints = {.width = pImageInfo->extent.width,
+                                                         .height = pImageInfo->extent.height,
+                                                         .layers = 1,
+                                                         .bytes_per_row_divisor = 1,
+                                                         .min_bytes_per_row = isl_surf.row_pitch};
+
+   switch (isl_surf.tiling) {
+   case ISL_TILING_LINEAR:
+      image_constraints.has_format_modifier = false;
+      break;
+   case ISL_TILING_X:
+      image_constraints.has_format_modifier = true;
+      image_constraints.format_modifier = MAGMA_FORMAT_MODIFIER_INTEL_X_TILED;
+      break;
+   case ISL_TILING_Y0:
+      image_constraints.has_format_modifier = true;
+      image_constraints.format_modifier = MAGMA_FORMAT_MODIFIER_INTEL_Y_TILED;
+      break;
+   case ISL_TILING_Yf:
+      image_constraints.has_format_modifier = true;
+      image_constraints.format_modifier = MAGMA_FORMAT_MODIFIER_INTEL_YF_TILED;
+      break;
+   default:
+      return ANV_MAGMA_DRET(VK_ERROR_FORMAT_NOT_SUPPORTED);
+   }
+
+   switch (pImageInfo->format) {
+   case VK_FORMAT_B8G8R8A8_SINT:
+   case VK_FORMAT_B8G8R8A8_UNORM:
+   case VK_FORMAT_B8G8R8A8_SRGB:
+   case VK_FORMAT_B8G8R8A8_SNORM:
+   case VK_FORMAT_B8G8R8A8_SSCALED:
+   case VK_FORMAT_B8G8R8A8_USCALED:
+      image_constraints.image_format = MAGMA_FORMAT_BGRA32;
+      break;
+   case VK_FORMAT_R8G8B8A8_SINT:
+   case VK_FORMAT_R8G8B8A8_UNORM:
+   case VK_FORMAT_R8G8B8A8_SRGB:
+   case VK_FORMAT_R8G8B8A8_SNORM:
+   case VK_FORMAT_R8G8B8A8_SSCALED:
+   case VK_FORMAT_R8G8B8A8_USCALED:
+      image_constraints.image_format = MAGMA_FORMAT_R8G8B8A8;
+      break;
+   case VK_FORMAT_G8_B8R8_2PLANE_420_UNORM:
+      image_constraints.image_format = MAGMA_FORMAT_NV12;
+      break;
+   default:
+      return ANV_MAGMA_DRET(VK_ERROR_FORMAT_NOT_SUPPORTED);
+   }
+
+   *image_constraints_out = image_constraints;
+
+   return VK_SUCCESS;
+}
+
+VkResult anv_SetBufferCollectionConstraintsFUCHSIA(VkDevice vk_device,
+                                                   VkBufferCollectionFUCHSIA vk_collection,
+                                                   const VkImageCreateInfo* pImageInfo)
+{
+   ANV_FROM_HANDLE(anv_device, device, vk_device);
+   ANV_FROM_HANDLE(anv_buffer_collection, buffer_collection, vk_collection);
+
+   magma_sysmem_connection_t sysmem_connection;
+   magma_status_t status = AnvMagmaGetSysmemConnection(device->connection, &sysmem_connection);
+   if (status != MAGMA_STATUS_OK)
+      return ANV_MAGMA_DRET(VK_ERROR_DEVICE_LOST);
+
+   magma_image_format_constraints_t image_constraints[2];
+   uint32_t slot_count = 0;
+   VkResult result;
+
+   switch (pImageInfo->tiling) {
+   case VK_IMAGE_TILING_OPTIMAL: {
+      // We always support X tiled for scanout but there may be a more optimal tiling format.
+      result = get_image_format_constraints(vk_device, pImageInfo, &image_constraints[slot_count],
+                                            ISL_TILING_X_BIT);
+      if (result != VK_SUCCESS) {
+         break;
+      }
+
+      if (image_constraints[0].image_format == MAGMA_FORMAT_NV12) {
+         // Sysmem can't handle tiled NV12.
+         result = get_image_format_constraints(vk_device, pImageInfo, &image_constraints[0],
+                                               ISL_TILING_LINEAR_BIT);
+         if (result == VK_SUCCESS) {
+            slot_count = 1;
+         }
+      } else {
+         assert(image_constraints[0].has_format_modifier);
+         slot_count = 1;
+         result = get_image_format_constraints(vk_device, pImageInfo, &image_constraints[1],
+                                               ISL_TILING_ANY_MASK);
+         if (result == VK_SUCCESS) {
+            assert(image_constraints[1].has_format_modifier);
+            if (image_constraints[1].format_modifier != image_constraints[0].format_modifier) {
+               slot_count++;
+            }
+         }
+      }
+      break;
+   }
+   case VK_IMAGE_TILING_LINEAR: {
+      result = get_image_format_constraints(vk_device, pImageInfo, &image_constraints[0],
+                                            ISL_TILING_LINEAR_BIT);
+      if (result == VK_SUCCESS) {
+         assert(!image_constraints[0].has_format_modifier);
+         slot_count = 1;
+      }
+      break;
+   }
+   default:
+      return ANV_MAGMA_DRET(VK_ERROR_FORMAT_NOT_SUPPORTED);
+   }
+
+   if (result != VK_SUCCESS)
+      return result;
+
+   magma_buffer_format_constraints_t format_constraints = {
+       .count = 1, .usage = 0, .secure_permitted = false, .secure_required = false};
+
+   magma_sysmem_buffer_constraints_t constraints;
+   status = magma_buffer_constraints_create(sysmem_connection, &format_constraints, &constraints);
+   if (status != MAGMA_STATUS_OK)
+      return VK_ERROR_OUT_OF_HOST_MEMORY;
+
+   for (uint32_t slot = 0; slot < slot_count; slot++) {
+      assert(slot < sizeof(image_constraints) / sizeof(image_constraints[0]));
+      status = magma_buffer_constraints_set_format(sysmem_connection, constraints, slot,
+                                                   &image_constraints[slot]);
+      if (status != MAGMA_STATUS_OK) {
+         break;
+      }
+   }
+
+   if (status == MAGMA_STATUS_OK) {
+      status = magma_buffer_collection_set_constraints(
+          sysmem_connection, buffer_collection->buffer_collection, constraints);
+   }
+
+   magma_buffer_constraints_release(sysmem_connection, constraints);
+
+   if (status != MAGMA_STATUS_OK)
+      return VK_ERROR_FORMAT_NOT_SUPPORTED;
+
+   return VK_SUCCESS;
+}
+
+VkResult anv_image_params_from_fuchsia_image(
+    VkDevice vk_device, const VkImageCreateInfo* pCreateInfo,
+    struct anv_fuchsia_image_plane_params params_out[MAGMA_MAX_IMAGE_PLANES],
+    isl_tiling_flags_t* tiling_flags_out)
+{
+   assert(pCreateInfo->arrayLayers == 1);
+   assert(pCreateInfo->extent.depth == 1);
+
+   const struct VkFuchsiaImageFormatFUCHSIA* image_format_fuchsia =
+       vk_find_struct_const(pCreateInfo->pNext, FUCHSIA_IMAGE_FORMAT_FUCHSIA);
+   assert(image_format_fuchsia);
+
+   magma_buffer_format_description_t description;
+   magma_status_t status;
+   status = magma_get_buffer_format_description(
+       image_format_fuchsia->imageFormat, image_format_fuchsia->imageFormatSize, &description);
+   if (status != MAGMA_STATUS_OK)
+      return ANV_MAGMA_DRET(VK_ERROR_FORMAT_NOT_SUPPORTED);
+
+   magma_bool_t has_format_modifier;
+   uint64_t format_modifier;
+   magma_image_plane_t planes[MAGMA_MAX_IMAGE_PLANES];
+
+   status = magma_get_buffer_format_plane_info(description, planes);
+   if (status == MAGMA_STATUS_OK) {
+      status =
+          magma_get_buffer_format_modifier(description, &has_format_modifier, &format_modifier);
+   }
+
+   magma_buffer_format_description_release(description);
+
+   if (status != MAGMA_STATUS_OK)
+      return ANV_MAGMA_DRET(VK_ERROR_FORMAT_NOT_SUPPORTED);
+
+   *tiling_flags_out = ISL_TILING_LINEAR_BIT;
+
+   if (has_format_modifier) {
+      switch (format_modifier) {
+      case MAGMA_FORMAT_MODIFIER_INTEL_X_TILED:
+         *tiling_flags_out = ISL_TILING_X_BIT;
+         break;
+      case MAGMA_FORMAT_MODIFIER_INTEL_Y_TILED:
+         *tiling_flags_out = ISL_TILING_Y0_BIT;
+         break;
+      case MAGMA_FORMAT_MODIFIER_INTEL_YF_TILED:
+         *tiling_flags_out = ISL_TILING_Yf_BIT;
+         break;
+      default:
+         assert(false);
+      }
+   }
+
+   for (uint32_t i = 0; i < MAGMA_MAX_IMAGE_PLANES; i++) {
+      params_out[i].bytes_per_row = planes[i].bytes_per_row;
+      params_out[i].byte_offset = planes[i].byte_offset;
+   }
+
+   return VK_SUCCESS;
+}
+
+#endif // VK_USE_PLATFORM_FUCHSIA
diff --git a/src/intel/vulkan/anv_magma_connection.cc b/src/intel/vulkan/anv_magma_connection.cc
index 32c4d6e..4384472 100644
--- a/src/intel/vulkan/anv_magma_connection.cc
+++ b/src/intel/vulkan/anv_magma_connection.cc
@@ -4,6 +4,7 @@
 
 #include "anv_magma.h"
 #include "drm_command_buffer.h"
+#include "magma_sysmem.h"
 #include "magma_util/inflight_list.h"
 #include "magma_util/macros.h"
 #include "magma_util/simple_allocator.h"
@@ -78,7 +79,13 @@
       anv_connection::connection = magma_connection;
    }
 
-   ~Connection() { magma_release_connection(magma_connection()); }
+   ~Connection()
+   {
+      if (sysmem_connection_) {
+         magma_sysmem_connection_release(sysmem_connection_);
+      }
+      magma_release_connection(magma_connection());
+   }
 
    magma_connection_t magma_connection() { return anv_connection::connection; }
 
@@ -102,12 +109,24 @@
 
    void UnmapGpu(uint64_t gpu_addr) { allocator_->Free(gpu_addr); }
 
+   magma_status_t GetSysmemConnection(magma_sysmem_connection_t* sysmem_connection_out)
+   {
+      if (!sysmem_connection_) {
+         magma_status_t status = magma_sysmem_connection_create(&sysmem_connection_);
+         if (status != MAGMA_STATUS_OK)
+            return DRET(status);
+      }
+      *sysmem_connection_out = sysmem_connection_;
+      return MAGMA_STATUS_OK;
+   }
+
    static Connection* cast(anv_connection* connection)
    {
       return static_cast<Connection*>(connection);
    }
 
 private:
+   magma_sysmem_connection_t sysmem_connection_{};
    magma::InflightList inflight_list_;
    std::unique_ptr<magma::AddressSpaceAllocator> allocator_;
    uint64_t guard_page_count_;
@@ -123,6 +142,12 @@
    delete static_cast<Connection*>(connection);
 }
 
+magma_status_t AnvMagmaGetSysmemConnection(struct anv_connection* connection,
+                                           magma_sysmem_connection_t* sysmem_connection_out)
+{
+   return Connection::cast(connection)->GetSysmemConnection(sysmem_connection_out);
+}
+
 void AnvMagmaConnectionWait(anv_connection* connection, uint64_t buffer_id, int64_t* timeout_ns)
 {
    magma::InflightList* inflight_list = Connection::cast(connection)->inflight_list();
diff --git a/src/intel/vulkan/anv_private.h b/src/intel/vulkan/anv_private.h
index 50df89c..a75b005 100644
--- a/src/intel/vulkan/anv_private.h
+++ b/src/intel/vulkan/anv_private.h
@@ -3315,6 +3315,26 @@
    return subpass_id;
 }
 
+isl_surf_usage_flags_t
+choose_isl_surf_usage(VkImageCreateFlags vk_create_flags,
+                      VkImageUsageFlags vk_usage,
+                      isl_surf_usage_flags_t isl_extra_usage,
+                      VkImageAspectFlagBits aspect);
+
+#if VK_USE_PLATFORM_FUCHSIA
+struct anv_fuchsia_image_plane_params {
+   uint32_t bytes_per_row;
+   uint32_t byte_offset;
+};
+
+VkResult anv_image_params_from_fuchsia_image(
+   VkDevice vk_device,
+   const VkImageCreateInfo *pCreateInfo,
+   struct anv_fuchsia_image_plane_params params_out[4],
+   isl_tiling_flags_t* tiling_flags_out);
+
+#endif
+
 #define ANV_DEFINE_HANDLE_CASTS(__anv_type, __VkType)                      \
                                                                            \
    static inline struct __anv_type *                                       \
diff --git a/src/vulkan/registry/vk.xml b/src/vulkan/registry/vk.xml
index 82df3d6..73fbf99 100644
--- a/src/vulkan/registry/vk.xml
+++ b/src/vulkan/registry/vk.xml
@@ -352,6 +352,7 @@
         <type category="handle" name="VkSamplerYcbcrConversionKHR"   alias="VkSamplerYcbcrConversion"/>
         <type category="handle" parent="VkDevice"><type>VK_DEFINE_NON_DISPATCHABLE_HANDLE</type>(<name>VkValidationCacheEXT</name>)</type>
         <type category="handle" parent="VkDevice"><type>VK_DEFINE_NON_DISPATCHABLE_HANDLE</type>(<name>VkAccelerationStructureNVX</name>)</type>
+        <type category="handle" parent="VkDevice"><type>VK_DEFINE_NON_DISPATCHABLE_HANDLE</type>(<name>VkBufferCollectionFUCHSIA</name>)</type>
 
             <comment>WSI extensions</comment>
         <type category="handle"><type>VK_DEFINE_NON_DISPATCHABLE_HANDLE</type>(<name>VkDisplayKHR</name>)</type>
@@ -2142,6 +2143,17 @@
             <member><type>VkDeviceMemory</type>                   <name>memory</name></member>
             <member><type>VkExternalMemoryHandleTypeFlagBits</type> <name>handleType</name></member>
         </type>
+        <type category="struct" name="VkFuchsiaImageFormatFUCHSIA" structextends="VkImageCreateInfo">
+            <member values="VK_STRUCTURE_TYPE_FUCHSIA_IMAGE_FORMAT_FUCHSIA"><type>VkStructureType</type> <name>sType</name></member>
+            <member>const <type>void</type>*                      <name>pNext</name></member>
+            <member>const <type>void</type>*                      <name>imageFormat</name></member>
+            <member><type>uint32_t</type>                         <name>imageFormatSize</name></member>
+        </type>
+        <type category="struct" name="VkBufferCollectionCreateInfoFUCHSIA">
+            <member values="VK_STRUCTURE_TYPE_BUFFER_COLLECTION_CREATE_INFO_FUCHSIA"><type>VkStructureType</type> <name>sType</name></member>
+            <member>const <type>void</type>*                      <name>pNext</name></member>
+            <member><type>zx_handle_t</type>                      <name>collectionToken</name></member>
+        </type>
         <type category="struct" name="VkWin32KeyedMutexAcquireReleaseInfoKHR" structextends="VkSubmitInfo">
             <member values="VK_STRUCTURE_TYPE_WIN32_KEYED_MUTEX_ACQUIRE_RELEASE_INFO_KHR"><type>VkStructureType</type> <name>sType</name></member>
             <member>const <type>void</type>*                      <name>pNext</name></member>
@@ -5937,6 +5949,25 @@
             <param optional="true">const <type>VkAllocationCallbacks</type>* <name>pAllocator</name></param>
             <param><type>VkSurfaceKHR</type>* <name>pSurface</name></param>
         </command>
+        <command successcodes="VK_SUCCESS" errorcodes="VK_ERROR_OUT_OF_HOST_MEMORY,VK_ERROR_OUT_OF_DEVICE_MEMORY">
+            <proto><type>VkResult</type> <name>vkCreateBufferCollectionFUCHSIA</name></proto>
+            <param><type>VkDevice</type> <name>device</name></param>
+            <param>const <type>VkBufferCollectionCreateInfoFUCHSIA</type>* <name>pImportInfo</name></param>
+            <param optional="true">const <type>VkAllocationCallbacks</type>* <name>pAllocator</name></param>
+            <param><type>VkBufferCollectionFUCHSIA</type>* <name>pCollection</name></param>
+        </command>
+        <command successcodes="VK_SUCCESS" errorcodes="VK_ERROR_OUT_OF_HOST_MEMORY,VK_ERROR_OUT_OF_DEVICE_MEMORY">
+            <proto><type>VkResult</type> <name>vkSetBufferCollectionConstraintsFUCHSIA</name></proto>
+            <param><type>VkDevice</type> <name>device</name></param>
+            <param><type>VkBufferCollectionFUCHSIA</type> <name>collection</name></param>
+            <param>const <type>VkImageCreateInfo</type>* <name>pImageInfo</name></param>
+        </command>
+        <command>
+            <proto><type>void</type> <name>vkDestroyBufferCollectionFUCHSIA</name></proto>
+            <param><type>VkDevice</type> <name>device</name></param>
+            <param><type>VkBufferCollectionFUCHSIA</type> <name>collection</name></param>
+            <param optional="true">const <type>VkAllocationCallbacks</type>* <name>pAllocator</name></param>
+        </command>
         <command successcodes="VK_SUCCESS" errorcodes="VK_ERROR_OUT_OF_HOST_MEMORY">
             <proto><type>VkResult</type> <name>vkCreateDebugReportCallbackEXT</name></proto>
             <param><type>VkInstance</type> <name>instance</name></param>
@@ -10196,5 +10227,21 @@
                 <command name="vkGetSemaphoreFuchsiaHandleKHR"/>
             </require>
         </extension>
+        <extension name="VK_FUCHSIA_buffer_collection" number="1005" type="device" requires="VK_KHR_external_memory_fuchsia" author="FUCHSIA" contact="John Bauman @jbauman" supported="vulkan" platform="fuchsia">
+            <require>
+                <enum value="1"                                         name="VK_FUCHSIA_BUFFER_COLLECTION_SPEC_VERSION"/>
+                <enum value="&quot;VK_FUCHSIA_buffer_collection&quot;"  name="VK_FUCHSIA_BUFFER_COLLECTION_EXTENSION_NAME"/>
+                <enum offset="0" extends="VkStructureType"              name="VK_STRUCTURE_TYPE_BUFFER_COLLECTION_CREATE_INFO_FUCHSIA"/>
+                <enum offset="1" extends="VkStructureType"              name="VK_STRUCTURE_TYPE_FUCHSIA_IMAGE_FORMAT_FUCHSIA"/>
+                <enum offset="2" extends="VkObjectType"                 name="VK_OBJECT_TYPE_BUFFER_COLLECTION_FUCHSIA" comment="VkBufferCollectionFUCHSIA"/>
+                <enum offset="3" extends="VkDebugReportObjectTypeEXT"   name="VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_COLLECTION_FUCHSIA_EXT"/>
+                <type name="VkBufferCollectionFUCHSIA"/>
+                <type name="VkBufferCollectionCreateInfoFUCHSIA"/>
+                <type name="VkFuchsiaImageFormatFUCHSIA"/>
+                <command name="vkCreateBufferCollectionFUCHSIA"/>
+                <command name="vkSetBufferCollectionConstraintsFUCHSIA"/>
+                <command name="vkDestroyBufferCollectionFUCHSIA"/>
+            </require>
+        </extension>
     </extensions>
 </registry>