fuchsia: Implement vkSetBufferCollectionImageConstraintsFUCHSIA

This implements the following Fuchsia Vulkan API:

VKAPI_ATTR VkResult VKAPI_CALL vkSetBufferCollectionImageConstraintsFUCHSIA(
    VkDevice                                    device,
    VkBufferCollectionFUCHSIA                   collection,
    const VkImageConstraintsInfoFUCHSIA*        pImageConstraintsInfo);

which can set buffer constraints using multiple VkImageCreateInfos.

Bug: 65168
Change-Id: I2df89bb0ecf59531365cac8b2b0a7c2e279aa2d0
diff --git a/system/vulkan_enc/ResourceTracker.cpp b/system/vulkan_enc/ResourceTracker.cpp
index c04703e..9684610 100644
--- a/system/vulkan_enc/ResourceTracker.cpp
+++ b/system/vulkan_enc/ResourceTracker.cpp
@@ -427,6 +427,10 @@
             constraints;
         android::base::Optional<VkBufferCollectionProperties2FUCHSIA>
             properties;
+
+        // the index of corresponding createInfo for each image format
+        // constraints in |constraints|.
+        std::vector<uint32_t> createInfoIndex;
 #endif  // VK_USE_PLATFORM_FUCHSIA
     };
 
@@ -1884,16 +1888,31 @@
         unregister_VkBufferCollectionFUCHSIA(collection);
     }
 
-    inline llcpp::fuchsia::sysmem::BufferCollectionConstraints defaultBufferCollectionConstraints(
-        size_t min_size_bytes,
-        size_t buffer_count) {
+    inline llcpp::fuchsia::sysmem::BufferCollectionConstraints
+    defaultBufferCollectionConstraints(
+        size_t minSizeBytes,
+        size_t minBufferCount,
+        size_t maxBufferCount = 0u,
+        size_t minBufferCountForCamping = 0u,
+        size_t minBufferCountForDedicatedSlack = 0u,
+        size_t minBufferCountForSharedSlack = 0u) {
         llcpp::fuchsia::sysmem::BufferCollectionConstraints constraints = {};
-        constraints.min_buffer_count = buffer_count;
+        constraints.min_buffer_count = minBufferCount;
+        if (maxBufferCount > 0) {
+            constraints.max_buffer_count = maxBufferCount;
+        }
+        if (minBufferCountForCamping) {
+            constraints.min_buffer_count_for_camping = minBufferCountForCamping;
+        }
+        if (minBufferCountForSharedSlack) {
+            constraints.min_buffer_count_for_shared_slack =
+                minBufferCountForSharedSlack;
+        }
         constraints.has_buffer_memory_constraints = true;
         llcpp::fuchsia::sysmem::BufferMemoryConstraints& buffer_constraints =
             constraints.buffer_memory_constraints;
 
-        buffer_constraints.min_size_bytes = min_size_bytes;
+        buffer_constraints.min_size_bytes = minSizeBytes;
         buffer_constraints.max_size_bytes = 0xffffffff;
         buffer_constraints.physically_contiguous_required = false;
         buffer_constraints.secure_required = false;
@@ -1955,6 +1974,106 @@
         return usage;
     }
 
+    static llcpp::fuchsia::sysmem::PixelFormatType vkFormatTypeToSysmem(
+        VkFormat format) {
+        switch (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:
+                return llcpp::fuchsia::sysmem::PixelFormatType::BGRA32;
+            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:
+                return llcpp::fuchsia::sysmem::PixelFormatType::R8G8B8A8;
+            case VK_FORMAT_R8_UNORM:
+            case VK_FORMAT_R8_UINT:
+            case VK_FORMAT_R8_USCALED:
+            case VK_FORMAT_R8_SNORM:
+            case VK_FORMAT_R8_SINT:
+            case VK_FORMAT_R8_SSCALED:
+            case VK_FORMAT_R8_SRGB:
+                return llcpp::fuchsia::sysmem::PixelFormatType::R8;
+            case VK_FORMAT_R8G8_UNORM:
+            case VK_FORMAT_R8G8_UINT:
+            case VK_FORMAT_R8G8_USCALED:
+            case VK_FORMAT_R8G8_SNORM:
+            case VK_FORMAT_R8G8_SINT:
+            case VK_FORMAT_R8G8_SSCALED:
+            case VK_FORMAT_R8G8_SRGB:
+                return llcpp::fuchsia::sysmem::PixelFormatType::R8G8;
+            default:
+                return llcpp::fuchsia::sysmem::PixelFormatType::INVALID;
+        }
+    }
+
+    static bool vkFormatMatchesSysmemFormat(
+        VkFormat vkFormat,
+        llcpp::fuchsia::sysmem::PixelFormatType sysmemFormat) {
+        switch (vkFormat) {
+            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:
+                return sysmemFormat ==
+                       llcpp::fuchsia::sysmem::PixelFormatType::BGRA32;
+            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:
+                return sysmemFormat ==
+                       llcpp::fuchsia::sysmem::PixelFormatType::R8G8B8A8;
+            case VK_FORMAT_R8_UNORM:
+            case VK_FORMAT_R8_UINT:
+            case VK_FORMAT_R8_USCALED:
+            case VK_FORMAT_R8_SNORM:
+            case VK_FORMAT_R8_SINT:
+            case VK_FORMAT_R8_SSCALED:
+            case VK_FORMAT_R8_SRGB:
+                return sysmemFormat ==
+                           llcpp::fuchsia::sysmem::PixelFormatType::R8 ||
+                       sysmemFormat ==
+                           llcpp::fuchsia::sysmem::PixelFormatType::L8;
+            case VK_FORMAT_R8G8_UNORM:
+            case VK_FORMAT_R8G8_UINT:
+            case VK_FORMAT_R8G8_USCALED:
+            case VK_FORMAT_R8G8_SNORM:
+            case VK_FORMAT_R8G8_SINT:
+            case VK_FORMAT_R8G8_SSCALED:
+            case VK_FORMAT_R8G8_SRGB:
+                return sysmemFormat ==
+                       llcpp::fuchsia::sysmem::PixelFormatType::R8G8;
+            default:
+                return false;
+        }
+    }
+
+    static VkFormat sysmemPixelFormatTypeToVk(
+        llcpp::fuchsia::sysmem::PixelFormatType format) {
+        switch (format) {
+            case llcpp::fuchsia::sysmem::PixelFormatType::BGRA32:
+                return VK_FORMAT_B8G8R8A8_SRGB;
+            case llcpp::fuchsia::sysmem::PixelFormatType::R8G8B8A8:
+                return VK_FORMAT_R8G8B8A8_SRGB;
+            case llcpp::fuchsia::sysmem::PixelFormatType::L8:
+            case llcpp::fuchsia::sysmem::PixelFormatType::R8:
+                return VK_FORMAT_R8_UNORM;
+            case llcpp::fuchsia::sysmem::PixelFormatType::R8G8:
+                return VK_FORMAT_R8G8_UNORM;
+            default:
+                return VK_FORMAT_UNDEFINED;
+        }
+    }
+
     VkResult setBufferCollectionConstraints(
         VkEncoder* enc, VkDevice device,
         llcpp::fuchsia::sysmem::BufferCollection::SyncClient* collection,
@@ -1964,137 +2083,301 @@
             return VK_ERROR_OUT_OF_DEVICE_MEMORY;
         }
 
+        std::vector<VkImageCreateInfo> createInfos;
+        if (pImageInfo->format == VK_FORMAT_UNDEFINED) {
+            const auto kFormats = {
+                VK_FORMAT_B8G8R8A8_SRGB,
+                VK_FORMAT_R8G8B8A8_SRGB,
+            };
+            for (auto format : kFormats) {
+                // shallow copy, using pNext from pImageInfo directly.
+                auto createInfo = *pImageInfo;
+                createInfo.format = format;
+                createInfos.push_back(createInfo);
+            }
+        } else {
+            createInfos.push_back(*pImageInfo);
+        }
+
+        VkImageConstraintsInfoFUCHSIA imageConstraints;
+        imageConstraints.sType =
+            VK_STRUCTURE_TYPE_IMAGE_CONSTRAINTS_INFO_FUCHSIA;
+        imageConstraints.pNext = nullptr;
+        imageConstraints.createInfoCount = createInfos.size();
+        imageConstraints.pCreateInfos = createInfos.data();
+        imageConstraints.pFormatConstraints = nullptr;
+        imageConstraints.maxBufferCount = 0;
+        imageConstraints.minBufferCount = 1;
+        imageConstraints.minBufferCountForCamping = 0;
+        imageConstraints.minBufferCountForDedicatedSlack = 0;
+        imageConstraints.minBufferCountForSharedSlack = 0;
+        imageConstraints.flags = 0u;
+
+        return setBufferCollectionImageConstraints(enc, device, collection,
+                                                   &imageConstraints);
+    }
+
+    VkResult addImageBufferCollectionConstraints(
+        VkEncoder* enc,
+        VkDevice device,
+        VkPhysicalDevice physicalDevice,
+        const VkImageCreateInfo* createInfo,
+        const VkImageFormatConstraintsInfoFUCHSIA* formatConstraints,
+        VkImageTiling tiling,
+        llcpp::fuchsia::sysmem::BufferCollectionConstraints* constraints) {
+        // First check if the format, tiling and usage is supported on host.
+        VkImageFormatProperties imageFormatProperties;
+        auto result = enc->vkGetPhysicalDeviceImageFormatProperties(
+            physicalDevice, createInfo->format, createInfo->imageType, tiling,
+            createInfo->usage, createInfo->flags, &imageFormatProperties,
+            true /* do lock */);
+        if (result != VK_SUCCESS) {
+            ALOGW(
+                "%s: Image format (%u) type (%u) tiling (%u) "
+                "usage (%u) flags (%u) not supported by physical "
+                "device",
+                __func__, static_cast<uint32_t>(createInfo->format),
+                static_cast<uint32_t>(createInfo->imageType),
+                static_cast<uint32_t>(tiling),
+                static_cast<uint32_t>(createInfo->usage),
+                static_cast<uint32_t>(createInfo->flags));
+            return VK_ERROR_FORMAT_NOT_SUPPORTED;
+        }
+
+        // Check if format constraints contains unsupported format features.
+        if (formatConstraints) {
+            VkFormatProperties formatProperties;
+            enc->vkGetPhysicalDeviceFormatProperties(
+                physicalDevice, createInfo->format, &formatProperties,
+                true /* do lock */);
+
+            auto supportedFeatures =
+                (tiling == VK_IMAGE_TILING_LINEAR)
+                    ? formatProperties.linearTilingFeatures
+                    : formatProperties.optimalTilingFeatures;
+            auto requiredFeatures = formatConstraints->requiredFormatFeatures;
+            if ((~supportedFeatures) & requiredFeatures) {
+                ALOGW(
+                    "%s: Host device support features for %s tiling: %08x, "
+                    "required features: %08x, feature bits %08x missing",
+                    __func__,
+                    tiling == VK_IMAGE_TILING_LINEAR ? "LINEAR" : "OPTIMAL",
+                    static_cast<uint32_t>(requiredFeatures),
+                    static_cast<uint32_t>(supportedFeatures),
+                    static_cast<uint32_t>((~supportedFeatures) &
+                                          requiredFeatures));
+                return VK_ERROR_FORMAT_NOT_SUPPORTED;
+            }
+        }
+
+        llcpp::fuchsia::sysmem::ImageFormatConstraints imageConstraints;
+        if (formatConstraints && formatConstraints->sysmemFormat != 0) {
+            auto pixelFormat =
+                static_cast<llcpp::fuchsia::sysmem::PixelFormatType>(
+                    formatConstraints->sysmemFormat);
+            if (createInfo->format != VK_FORMAT_UNDEFINED &&
+                !vkFormatMatchesSysmemFormat(createInfo->format, pixelFormat)) {
+                ALOGW("%s: VkFormat %u doesn't match sysmem pixelFormat %lu",
+                      __func__, static_cast<uint32_t>(createInfo->format),
+                      formatConstraints->sysmemFormat);
+                return VK_ERROR_FORMAT_NOT_SUPPORTED;
+            }
+            imageConstraints.pixel_format.type = pixelFormat;
+        } else {
+            auto pixel_format = vkFormatTypeToSysmem(createInfo->format);
+            if (pixel_format ==
+                llcpp::fuchsia::sysmem::PixelFormatType::INVALID) {
+                ALOGW("%s: Unsupported VkFormat %u", __func__,
+                      static_cast<uint32_t>(createInfo->format));
+                return VK_ERROR_FORMAT_NOT_SUPPORTED;
+            }
+            imageConstraints.pixel_format.type = pixel_format;
+        }
+
+        if (!formatConstraints || formatConstraints->colorSpaceCount == 0u) {
+            imageConstraints.color_spaces_count = 1;
+            imageConstraints.color_space[0].type =
+                llcpp::fuchsia::sysmem::ColorSpaceType::SRGB;
+        } else {
+            imageConstraints.color_spaces_count =
+                formatConstraints->colorSpaceCount;
+            for (size_t i = 0; i < formatConstraints->colorSpaceCount; i++) {
+                imageConstraints.color_space[0].type =
+                    static_cast<llcpp::fuchsia::sysmem::ColorSpaceType>(
+                        formatConstraints->pColorSpaces[i].colorSpace);
+            }
+        }
+
+        // Get row alignment from host GPU.
+        VkDeviceSize offset;
+        VkDeviceSize rowPitchAlignment;
+        enc->vkGetLinearImageLayoutGOOGLE(device, createInfo->format, &offset,
+                                          &rowPitchAlignment,
+                                          true /* do lock */);
+        ALOGD(
+            "vkGetLinearImageLayoutGOOGLE: format %d offset %lu "
+            "rowPitchAlignment = %lu",
+            (int)createInfo->format, offset, rowPitchAlignment);
+
+        imageConstraints.min_coded_width = createInfo->extent.width;
+        imageConstraints.max_coded_width = 0xfffffff;
+        imageConstraints.min_coded_height = createInfo->extent.height;
+        imageConstraints.max_coded_height = 0xffffffff;
+        // The min_bytes_per_row can be calculated by sysmem using
+        // |min_coded_width|, |bytes_per_row_divisor| and color format.
+        imageConstraints.min_bytes_per_row = 0;
+        imageConstraints.max_bytes_per_row = 0xffffffff;
+        imageConstraints.max_coded_width_times_coded_height = 0xffffffff;
+
+        imageConstraints.layers = 1;
+        imageConstraints.coded_width_divisor = 1;
+        imageConstraints.coded_height_divisor = 1;
+        imageConstraints.bytes_per_row_divisor = rowPitchAlignment;
+        imageConstraints.start_offset_divisor = 1;
+        imageConstraints.display_width_divisor = 1;
+        imageConstraints.display_height_divisor = 1;
+        imageConstraints.pixel_format.has_format_modifier = true;
+        imageConstraints.pixel_format.format_modifier.value =
+            (tiling == VK_IMAGE_TILING_LINEAR)
+                ? llcpp::fuchsia::sysmem::FORMAT_MODIFIER_LINEAR
+                : llcpp::fuchsia::sysmem::
+                      FORMAT_MODIFIER_GOOGLE_GOLDFISH_OPTIMAL;
+
+        constraints->image_format_constraints
+            [constraints->image_format_constraints_count++] =
+            std::move(imageConstraints);
+        return VK_SUCCESS;
+    }
+
+    VkResult setBufferCollectionImageConstraints(
+        VkEncoder* enc,
+        VkDevice device,
+        llcpp::fuchsia::sysmem::BufferCollection::SyncClient* collection,
+        const VkImageConstraintsInfoFUCHSIA* pImageConstraintsInfo) {
+        if (!pImageConstraintsInfo ||
+            pImageConstraintsInfo->sType !=
+                VK_STRUCTURE_TYPE_IMAGE_CONSTRAINTS_INFO_FUCHSIA) {
+            ALOGE("%s: invalid pImageConstraintsInfo", __func__);
+            return VK_ERROR_INITIALIZATION_FAILED;
+        }
+
+        if (pImageConstraintsInfo->createInfoCount == 0) {
+            ALOGE("%s: createInfoCount must be greater than 0", __func__);
+            return VK_ERROR_INITIALIZATION_FAILED;
+        }
+
         llcpp::fuchsia::sysmem::BufferCollectionConstraints constraints =
             defaultBufferCollectionConstraints(
-                /* min_size_bytes */ 0, /* buffer_count */ 1u);
+                /* min_size_bytes */ 0, pImageConstraintsInfo->minBufferCount,
+                pImageConstraintsInfo->maxBufferCount,
+                pImageConstraintsInfo->minBufferCountForCamping,
+                pImageConstraintsInfo->minBufferCountForDedicatedSlack,
+                pImageConstraintsInfo->minBufferCountForSharedSlack);
 
-        constraints.usage.vulkan =
-            getBufferCollectionConstraintsVulkanImageUsage(pImageInfo);
+        std::vector<llcpp::fuchsia::sysmem::ImageFormatConstraints>
+            format_constraints;
 
-        // Set image format constraints for VkImage allocation.
-        if (pImageInfo) {
-            std::vector<VkFormat> formats{pImageInfo->format};
-            if (pImageInfo->format == VK_FORMAT_UNDEFINED) {
-                // This is a hack to allow the client to say it supports every
-                // vulkan format the driver does. TODO(fxb/13247): Modify this
-                // function to take a list of vulkan formats to use.
-                formats = std::vector<VkFormat>{
-                        VK_FORMAT_B8G8R8A8_UNORM,
-                        VK_FORMAT_R8G8B8A8_UNORM,
-                };
-            } else {
-                // If image format is predefined, check on host if the format,
-                // tiling and usage is supported.
-                VkPhysicalDevice physicalDevice;
-                {
-                    AutoLock lock(mLock);
-                    auto deviceIt = info_VkDevice.find(device);
-                    if (deviceIt == info_VkDevice.end()) {
-                        return VK_ERROR_INITIALIZATION_FAILED;
-                    }
-                    physicalDevice = deviceIt->second.physdev;
-                }
+        VkPhysicalDevice physicalDevice;
+        {
+            AutoLock lock(mLock);
+            auto deviceIt = info_VkDevice.find(device);
+            if (deviceIt == info_VkDevice.end()) {
+                return VK_ERROR_INITIALIZATION_FAILED;
+            }
+            physicalDevice = deviceIt->second.physdev;
+        }
 
-                VkImageFormatProperties format_properties;
-                auto result = enc->vkGetPhysicalDeviceImageFormatProperties(
-                    physicalDevice, pImageInfo->format, pImageInfo->imageType,
-                    pImageInfo->tiling, pImageInfo->usage, pImageInfo->flags,
-                    &format_properties, true /* do lock */);
-                if (result != VK_SUCCESS) {
-                    ALOGE(
-                        "%s: Image format (%u) type (%u) tiling (%u) "
-                        "usage (%u) flags (%u) not supported by physical "
-                        "device",
-                        __func__, static_cast<uint32_t>(pImageInfo->format),
-                        static_cast<uint32_t>(pImageInfo->imageType),
-                        static_cast<uint32_t>(pImageInfo->tiling),
-                        static_cast<uint32_t>(pImageInfo->usage),
-                        static_cast<uint32_t>(pImageInfo->flags));
-                    return VK_ERROR_FORMAT_NOT_SUPPORTED;
+        std::vector<uint32_t> createInfoIndex;
+
+        bool hasOptimalTiling = false;
+        for (uint32_t i = 0; i < pImageConstraintsInfo->createInfoCount; i++) {
+            const VkImageCreateInfo* createInfo =
+                &pImageConstraintsInfo->pCreateInfos[i];
+            const VkImageFormatConstraintsInfoFUCHSIA* formatConstraints =
+                pImageConstraintsInfo->pFormatConstraints
+                    ? &pImageConstraintsInfo->pFormatConstraints[i]
+                    : nullptr;
+
+            // add ImageFormatConstraints for *optimal* tiling
+            VkResult optimalResult = VK_ERROR_FORMAT_NOT_SUPPORTED;
+            if (createInfo->tiling == VK_IMAGE_TILING_OPTIMAL) {
+                optimalResult = addImageBufferCollectionConstraints(
+                    enc, device, physicalDevice, createInfo, formatConstraints,
+                    VK_IMAGE_TILING_OPTIMAL, &constraints);
+                if (optimalResult == VK_SUCCESS) {
+                    createInfoIndex.push_back(i);
+                    hasOptimalTiling = true;
                 }
             }
-            constraints.image_format_constraints_count = formats.size();
-            uint32_t format_index = 0;
-            for (VkFormat format : formats) {
-                // Get row alignment from host GPU.
-                VkDeviceSize offset;
-                VkDeviceSize rowPitchAlignment;
-                enc->vkGetLinearImageLayoutGOOGLE(device, format, &offset, &rowPitchAlignment, true /* do lock */);
 
-                ALOGD("vkGetLinearImageLayoutGOOGLE: format %d offset %lu rowPitchAlignment = %lu",
-                    (int)format, offset, rowPitchAlignment);
-
-                llcpp::fuchsia::sysmem::ImageFormatConstraints&
-                    image_constraints =
-                        constraints.image_format_constraints[format_index++];
-                switch (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.pixel_format.type =
-                            llcpp::fuchsia::sysmem::PixelFormatType::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.pixel_format.type =
-                            llcpp::fuchsia::sysmem::PixelFormatType::R8G8B8A8;
-                        break;
-                    case VK_FORMAT_R8_UNORM:
-                    case VK_FORMAT_R8_UINT:
-                    case VK_FORMAT_R8_USCALED:
-                    case VK_FORMAT_R8_SNORM:
-                    case VK_FORMAT_R8_SINT:
-                    case VK_FORMAT_R8_SSCALED:
-                    case VK_FORMAT_R8_SRGB:
-                        image_constraints.pixel_format.type =
-                            llcpp::fuchsia::sysmem::PixelFormatType::R8;
-                        break;
-                    case VK_FORMAT_R8G8_UNORM:
-                    case VK_FORMAT_R8G8_UINT:
-                    case VK_FORMAT_R8G8_USCALED:
-                    case VK_FORMAT_R8G8_SNORM:
-                    case VK_FORMAT_R8G8_SINT:
-                    case VK_FORMAT_R8G8_SSCALED:
-                    case VK_FORMAT_R8G8_SRGB:
-                        image_constraints.pixel_format.type =
-                            llcpp::fuchsia::sysmem::PixelFormatType::R8G8;
-                        break;
-                    default:
-                        return VK_ERROR_FORMAT_NOT_SUPPORTED;
-                }
-                image_constraints.color_spaces_count = 1;
-                image_constraints.color_space[0].type =
-                    llcpp::fuchsia::sysmem::ColorSpaceType::SRGB;
-                image_constraints.min_coded_width = pImageInfo->extent.width;
-                image_constraints.max_coded_width = 0xfffffff;
-                image_constraints.min_coded_height = pImageInfo->extent.height;
-                image_constraints.max_coded_height = 0xffffffff;
-                // The min_bytes_per_row can be calculated by sysmem using
-                // |min_coded_width|, |bytes_per_row_divisor| and color format.
-                image_constraints.min_bytes_per_row = 0;
-                image_constraints.max_bytes_per_row = 0xffffffff;
-                image_constraints.max_coded_width_times_coded_height =
-                        0xffffffff;
-                image_constraints.layers = 1;
-                image_constraints.coded_width_divisor = 1;
-                image_constraints.coded_height_divisor = 1;
-                image_constraints.bytes_per_row_divisor = rowPitchAlignment;
-                image_constraints.start_offset_divisor = 1;
-                image_constraints.display_width_divisor = 1;
-                image_constraints.display_height_divisor = 1;
+            // Add ImageFormatConstraints for *linear* tiling
+            VkResult linearResult = addImageBufferCollectionConstraints(
+                enc, device, physicalDevice, createInfo, formatConstraints,
+                VK_IMAGE_TILING_LINEAR, &constraints);
+            if (linearResult == VK_SUCCESS) {
+                createInfoIndex.push_back(i);
             }
+
+            // Update usage and BufferMemoryConstraints
+            if (linearResult == VK_SUCCESS || optimalResult == VK_SUCCESS) {
+                constraints.usage.vulkan |=
+                    getBufferCollectionConstraintsVulkanImageUsage(createInfo);
+
+                if (formatConstraints && formatConstraints->flags) {
+                    ALOGW(
+                        "%s: Non-zero flags (%08x) in image format "
+                        "constraints; this is currently not supported, see "
+                        "fxbug.dev/68833.",
+                        __func__, formatConstraints->flags);
+                }
+            }
+        }
+
+        // Set buffer memory constraints based on optimal/linear tiling support
+        // and flags.
+        VkImageConstraintsInfoFlagsFUCHSIA flags = pImageConstraintsInfo->flags;
+        if (flags & VK_IMAGE_CONSTRAINTS_INFO_CPU_READ_RARELY_FUCHSIA)
+            constraints.usage.cpu |= llcpp::fuchsia::sysmem::cpuUsageRead;
+        if (flags & VK_IMAGE_CONSTRAINTS_INFO_CPU_READ_OFTEN_FUCHSIA)
+            constraints.usage.cpu |= llcpp::fuchsia::sysmem::cpuUsageReadOften;
+        if (flags & VK_IMAGE_CONSTRAINTS_INFO_CPU_WRITE_RARELY_FUCHSIA)
+            constraints.usage.cpu |= llcpp::fuchsia::sysmem::cpuUsageWrite;
+        if (flags & VK_IMAGE_CONSTRAINTS_INFO_CPU_WRITE_OFTEN_FUCHSIA)
+            constraints.usage.cpu |= llcpp::fuchsia::sysmem::cpuUsageWriteOften;
+
+        constraints.has_buffer_memory_constraints = true;
+        auto& memory_constraints = constraints.buffer_memory_constraints;
+        memory_constraints.cpu_domain_supported = true;
+        memory_constraints.ram_domain_supported = true;
+        memory_constraints.inaccessible_domain_supported =
+            hasOptimalTiling &&
+            !(flags & (VK_IMAGE_CONSTRAINTS_INFO_CPU_READ_RARELY_FUCHSIA |
+                       VK_IMAGE_CONSTRAINTS_INFO_CPU_READ_OFTEN_FUCHSIA |
+                       VK_IMAGE_CONSTRAINTS_INFO_CPU_WRITE_RARELY_FUCHSIA |
+                       VK_IMAGE_CONSTRAINTS_INFO_CPU_WRITE_OFTEN_FUCHSIA));
+
+        if (memory_constraints.inaccessible_domain_supported) {
+            memory_constraints.heap_permitted_count = 2;
+            memory_constraints.heap_permitted[0] =
+                llcpp::fuchsia::sysmem::HeapType::GOLDFISH_DEVICE_LOCAL;
+            memory_constraints.heap_permitted[1] =
+                llcpp::fuchsia::sysmem::HeapType::GOLDFISH_HOST_VISIBLE;
+        } else {
+            memory_constraints.heap_permitted_count = 1;
+            memory_constraints.heap_permitted[0] =
+                llcpp::fuchsia::sysmem::HeapType::GOLDFISH_HOST_VISIBLE;
+        }
+
+        if (constraints.image_format_constraints_count == 0) {
+            ALOGE("%s: none of the specified formats is supported by device",
+                  __func__);
+            return VK_ERROR_FORMAT_NOT_SUPPORTED;
         }
 
         constexpr uint32_t kVulkanPriority = 5;
         const char* kName = "GoldfishSysmemShared";
-        collection->SetName(kVulkanPriority, fidl::unowned_str(kName, strlen(kName)));
+        collection->SetName(kVulkanPriority,
+                            fidl::unowned_str(kName, strlen(kName)));
 
         auto result = collection->SetConstraints(true, constraints);
         if (!result.ok()) {
@@ -2112,6 +2395,8 @@
             info_VkBufferCollectionFUCHSIA.end()) {
             info_VkBufferCollectionFUCHSIA[buffer_collection].constraints =
                 android::base::makeOptional(std::move(constraints));
+            info_VkBufferCollectionFUCHSIA[buffer_collection].createInfoIndex =
+                std::move(createInfoIndex);
         }
 
         return VK_SUCCESS;
@@ -2170,6 +2455,19 @@
         return setBufferCollectionConstraints(enc, device, sysmem_collection, pImageInfo);
     }
 
+    VkResult on_vkSetBufferCollectionImageConstraintsFUCHSIA(
+        void* context,
+        VkResult,
+        VkDevice device,
+        VkBufferCollectionFUCHSIA collection,
+        const VkImageConstraintsInfoFUCHSIA* pImageConstraintsInfo) {
+        VkEncoder* enc = (VkEncoder*)context;
+        auto sysmem_collection = reinterpret_cast<
+            llcpp::fuchsia::sysmem::BufferCollection::SyncClient*>(collection);
+        return setBufferCollectionImageConstraints(
+            enc, device, sysmem_collection, pImageConstraintsInfo);
+    }
+
     VkResult on_vkSetBufferCollectionBufferConstraintsFUCHSIA(
         void*,
         VkResult,
@@ -6274,6 +6572,16 @@
         context, input_result, device, collection, pBufferDConstraintsInfo);
 }
 
+VkResult ResourceTracker::on_vkSetBufferCollectionImageConstraintsFUCHSIA(
+    void* context,
+    VkResult input_result,
+    VkDevice device,
+    VkBufferCollectionFUCHSIA collection,
+    const VkImageConstraintsInfoFUCHSIA* pImageConstraintsInfo) {
+    return mImpl->on_vkSetBufferCollectionImageConstraintsFUCHSIA(
+        context, input_result, device, collection, pImageConstraintsInfo);
+}
+
 VkResult ResourceTracker::on_vkGetBufferCollectionPropertiesFUCHSIA(
         void* context, VkResult input_result,
         VkDevice device,