Implement a dma-buf MemoryService

This CL implements the MemoryService for importing memory and
creating VkImages from a dma-buf handle. Under the hood, it uses the
VK_EXT_external_memory_dma_buf and
VK_EXT_image_drm_format_modifier extensions to find a memory type
that supports dma-buf import. In addition, the extensions are also
used to properly specify the stride and tiling of the dma-buf to
vkAllocateMemory and vkCreateImage.

BUG=chromium:996470

Change-Id: Ie72d73117a4cbafcb40468aab0952b783351d499
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/13785
Commit-Queue: Brian Ho <hob@chromium.org>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
diff --git a/BUILD.gn b/BUILD.gn
index 6079642..0fd9cf4 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -478,7 +478,12 @@
       "src/dawn_native/vulkan/external_semaphore/SemaphoreService.h",
     ]
 
-    if (is_linux) {
+    if (is_chromeos) {
+      sources += [
+        "src/dawn_native/vulkan/external_memory/MemoryServiceDmaBuf.cpp",
+        "src/dawn_native/vulkan/external_semaphore/SemaphoreServiceOpaqueFD.cpp",
+      ]
+    } else if (is_linux) {
       sources += [
         "src/dawn_native/vulkan/external_memory/MemoryServiceOpaqueFD.cpp",
         "src/dawn_native/vulkan/external_semaphore/SemaphoreServiceOpaqueFD.cpp",
diff --git a/src/dawn_native/vulkan/DeviceVk.cpp b/src/dawn_native/vulkan/DeviceVk.cpp
index f317588..b9f326a 100644
--- a/src/dawn_native/vulkan/DeviceVk.cpp
+++ b/src/dawn_native/vulkan/DeviceVk.cpp
@@ -722,6 +722,10 @@
         mResourceMemoryAllocator->Deallocate(allocation);
     }
 
+    int Device::FindBestMemoryTypeIndex(VkMemoryRequirements requirements, bool mappable) {
+        return mResourceMemoryAllocator->FindBestTypeIndex(requirements, mappable);
+    }
+
     ResourceMemoryAllocator* Device::GetResourceMemoryAllocatorForTesting() const {
         return mResourceMemoryAllocator.get();
     }
diff --git a/src/dawn_native/vulkan/DeviceVk.h b/src/dawn_native/vulkan/DeviceVk.h
index 74ae79d..e5210d6 100644
--- a/src/dawn_native/vulkan/DeviceVk.h
+++ b/src/dawn_native/vulkan/DeviceVk.h
@@ -95,6 +95,8 @@
                                                                bool mappable);
         void DeallocateMemory(ResourceMemoryAllocation* allocation);
 
+        int FindBestMemoryTypeIndex(VkMemoryRequirements requirements, bool mappable);
+
         ResourceMemoryAllocator* GetResourceMemoryAllocatorForTesting() const;
 
       private:
diff --git a/src/dawn_native/vulkan/TextureVk.cpp b/src/dawn_native/vulkan/TextureVk.cpp
index 3422ab0..5d11976 100644
--- a/src/dawn_native/vulkan/TextureVk.cpp
+++ b/src/dawn_native/vulkan/TextureVk.cpp
@@ -485,7 +485,8 @@
     MaybeError Texture::InitializeFromExternal(const ExternalImageDescriptor* descriptor,
                                                external_memory::Service* externalMemoryService) {
         VkFormat format = VulkanImageFormat(GetFormat().format);
-        if (!externalMemoryService->SupportsCreateImage(descriptor, format)) {
+        VkImageUsageFlags usage = VulkanImageUsage(GetUsage(), GetFormat());
+        if (!externalMemoryService->SupportsCreateImage(descriptor, format, usage)) {
             return DAWN_VALIDATION_ERROR("Creating an image from external memory is not supported");
         }
 
@@ -499,7 +500,7 @@
         baseCreateInfo.mipLevels = GetNumMipLevels();
         baseCreateInfo.arrayLayers = GetArrayLayers();
         baseCreateInfo.samples = VulkanSampleCount(GetSampleCount());
-        baseCreateInfo.usage = VulkanImageUsage(GetUsage(), GetFormat());
+        baseCreateInfo.usage = usage;
         baseCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
         baseCreateInfo.queueFamilyIndexCount = 0;
         baseCreateInfo.pQueueFamilyIndices = nullptr;
diff --git a/src/dawn_native/vulkan/external_memory/MemoryService.h b/src/dawn_native/vulkan/external_memory/MemoryService.h
index 1d0d475..0c4b64d 100644
--- a/src/dawn_native/vulkan/external_memory/MemoryService.h
+++ b/src/dawn_native/vulkan/external_memory/MemoryService.h
@@ -44,7 +44,9 @@
                                   VkImageCreateFlags flags);
 
         // True if the device reports it supports creating VkImages from external memory.
-        bool SupportsCreateImage(const ExternalImageDescriptor* descriptor, VkFormat format);
+        bool SupportsCreateImage(const ExternalImageDescriptor* descriptor,
+                                 VkFormat format,
+                                 VkImageUsageFlags usage);
 
         // Returns the parameters required for importing memory
         ResultOrError<MemoryImportParams> GetMemoryImportParams(
diff --git a/src/dawn_native/vulkan/external_memory/MemoryServiceDmaBuf.cpp b/src/dawn_native/vulkan/external_memory/MemoryServiceDmaBuf.cpp
new file mode 100644
index 0000000..e9944f8
--- /dev/null
+++ b/src/dawn_native/vulkan/external_memory/MemoryServiceDmaBuf.cpp
@@ -0,0 +1,271 @@
+// Copyright 2019 The Dawn Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "common/Assert.h"
+#include "dawn_native/vulkan/AdapterVk.h"
+#include "dawn_native/vulkan/BackendVk.h"
+#include "dawn_native/vulkan/DeviceVk.h"
+#include "dawn_native/vulkan/VulkanError.h"
+#include "dawn_native/vulkan/external_memory/MemoryService.h"
+
+namespace dawn_native { namespace vulkan { namespace external_memory {
+
+    namespace {
+
+        // Some modifiers use multiple planes (for example, see the comment for
+        // I915_FORMAT_MOD_Y_TILED_CCS in drm/drm_fourcc.h), but dma-buf import in Dawn only
+        // supports single-plane formats.
+        ResultOrError<uint32_t> GetModifierPlaneCount(const VulkanFunctions& fn,
+                                                      VkPhysicalDevice physicalDevice,
+                                                      VkFormat format,
+                                                      uint64_t modifier) {
+            VkDrmFormatModifierPropertiesListEXT formatModifierPropsList;
+            formatModifierPropsList.sType =
+                VK_STRUCTURE_TYPE_DRM_FORMAT_MODIFIER_PROPERTIES_LIST_EXT;
+            formatModifierPropsList.pNext = nullptr;
+            formatModifierPropsList.drmFormatModifierCount = 0;
+            formatModifierPropsList.pDrmFormatModifierProperties = nullptr;
+
+            VkFormatProperties2 formatProps;
+            formatProps.sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2;
+            formatProps.pNext = &formatModifierPropsList;
+
+            fn.GetPhysicalDeviceFormatProperties2KHR(physicalDevice, format, &formatProps);
+
+            uint32_t modifierCount = formatModifierPropsList.drmFormatModifierCount;
+            std::vector<VkDrmFormatModifierPropertiesEXT> formatModifierProps(modifierCount);
+            formatModifierPropsList.pDrmFormatModifierProperties = formatModifierProps.data();
+
+            fn.GetPhysicalDeviceFormatProperties2KHR(physicalDevice, format, &formatProps);
+            for (const auto& props : formatModifierProps) {
+                if (props.drmFormatModifier == modifier) {
+                    uint32_t count = props.drmFormatModifierPlaneCount;
+                    return count;
+                }
+            }
+            return DAWN_VALIDATION_ERROR("DRM format modifier not supported");
+        }
+
+    }  // anonymous namespace
+
+    Service::Service(Device* device) : mDevice(device) {
+        const VulkanDeviceInfo& deviceInfo = mDevice->GetDeviceInfo();
+        const VulkanGlobalInfo& globalInfo =
+            ToBackend(mDevice->GetAdapter())->GetBackend()->GetGlobalInfo();
+
+        mSupported = globalInfo.getPhysicalDeviceProperties2 &&
+                     globalInfo.externalMemoryCapabilities && deviceInfo.externalMemory &&
+                     deviceInfo.externalMemoryFD && deviceInfo.externalMemoryDmaBuf &&
+                     deviceInfo.imageDrmFormatModifier;
+    }
+
+    Service::~Service() = default;
+
+    bool Service::SupportsImportMemory(VkFormat format,
+                                       VkImageType type,
+                                       VkImageTiling tiling,
+                                       VkImageUsageFlags usage,
+                                       VkImageCreateFlags flags) {
+        return mSupported;
+    }
+
+    bool Service::SupportsCreateImage(const ExternalImageDescriptor* descriptor,
+                                      VkFormat format,
+                                      VkImageUsageFlags usage) {
+        // Early out before we try using extension functions
+        if (!mSupported) {
+            return false;
+        }
+        if (descriptor->type != ExternalImageDescriptorType::DmaBuf) {
+            return false;
+        }
+        const ExternalImageDescriptorDmaBuf* dmaBufDescriptor =
+            static_cast<const ExternalImageDescriptorDmaBuf*>(descriptor);
+
+        // Verify plane count for the modifier.
+        VkPhysicalDevice physicalDevice = ToBackend(mDevice->GetAdapter())->GetPhysicalDevice();
+        uint32_t planeCount = 0;
+        if (mDevice->ConsumedError(GetModifierPlaneCount(mDevice->fn, physicalDevice, format,
+                                                         dmaBufDescriptor->drmModifier),
+                                   &planeCount)) {
+            return false;
+        }
+        if (planeCount == 0) {
+            return false;
+        }
+        // TODO(hob): Support multi-plane formats like I915_FORMAT_MOD_Y_TILED_CCS.
+        if (planeCount > 1) {
+            return false;
+        }
+
+        // Verify that the format modifier of the external memory and the requested Vulkan format
+        // are actually supported together in a dma-buf import.
+        VkPhysicalDeviceImageDrmFormatModifierInfoEXT drmModifierInfo;
+        drmModifierInfo.sType =
+            VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_DRM_FORMAT_MODIFIER_INFO_EXT;
+        drmModifierInfo.pNext = nullptr;
+        drmModifierInfo.drmFormatModifier = dmaBufDescriptor->drmModifier;
+        drmModifierInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
+
+        VkPhysicalDeviceExternalImageFormatInfo externalImageFormatInfo;
+        externalImageFormatInfo.sType =
+            VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO;
+        externalImageFormatInfo.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT;
+        externalImageFormatInfo.pNext = &drmModifierInfo;
+
+        VkPhysicalDeviceImageFormatInfo2 imageFormatInfo;
+        imageFormatInfo.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2;
+        imageFormatInfo.format = format;
+        imageFormatInfo.type = VK_IMAGE_TYPE_2D;
+        imageFormatInfo.tiling = VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT;
+        imageFormatInfo.usage = usage;
+        imageFormatInfo.flags = 0;
+        imageFormatInfo.pNext = &externalImageFormatInfo;
+
+        VkExternalImageFormatProperties externalImageFormatProps;
+        externalImageFormatProps.sType = VK_STRUCTURE_TYPE_EXTERNAL_IMAGE_FORMAT_PROPERTIES;
+        externalImageFormatProps.pNext = nullptr;
+
+        VkImageFormatProperties2 imageFormatProps;
+        imageFormatProps.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2;
+        imageFormatProps.pNext = &externalImageFormatProps;
+
+        VkResult result = mDevice->fn.GetPhysicalDeviceImageFormatProperties2KHR(
+            physicalDevice, &imageFormatInfo, &imageFormatProps);
+        if (result != VK_SUCCESS) {
+            return false;
+        }
+        VkExternalMemoryFeatureFlags featureFlags =
+            externalImageFormatProps.externalMemoryProperties.externalMemoryFeatures;
+        return featureFlags & VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT;
+    }
+
+    ResultOrError<MemoryImportParams> Service::GetMemoryImportParams(
+        const ExternalImageDescriptor* descriptor,
+        VkImage image) {
+        if (descriptor->type != ExternalImageDescriptorType::DmaBuf) {
+            return DAWN_VALIDATION_ERROR("ExternalImageDescriptor is not a dma-buf descriptor");
+        }
+        const ExternalImageDescriptorDmaBuf* dmaBufDescriptor =
+            static_cast<const ExternalImageDescriptorDmaBuf*>(descriptor);
+        VkDevice device = mDevice->GetVkDevice();
+
+        // Get the valid memory types for the VkImage.
+        VkMemoryRequirements memoryRequirements;
+        mDevice->fn.GetImageMemoryRequirements(device, image, &memoryRequirements);
+
+        VkMemoryFdPropertiesKHR fdProperties;
+        fdProperties.sType = VK_STRUCTURE_TYPE_MEMORY_FD_PROPERTIES_KHR;
+        fdProperties.pNext = nullptr;
+
+        // Get the valid memory types that the external memory can be imported as.
+        mDevice->fn.GetMemoryFdPropertiesKHR(device, VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT,
+                                             dmaBufDescriptor->memoryFD, &fdProperties);
+        // Choose the best memory type that satisfies both the image's constraint and the import's
+        // constraint.
+        memoryRequirements.memoryTypeBits &= fdProperties.memoryTypeBits;
+        int memoryTypeIndex =
+            mDevice->FindBestMemoryTypeIndex(memoryRequirements, false /** mappable */);
+        if (memoryTypeIndex == -1) {
+            return DAWN_VALIDATION_ERROR("Unable to find appropriate memory type for import");
+        }
+        MemoryImportParams params = {memoryRequirements.size, memoryTypeIndex};
+        return params;
+    }
+
+    ResultOrError<VkDeviceMemory> Service::ImportMemory(ExternalMemoryHandle handle,
+                                                        const MemoryImportParams& importParams,
+                                                        VkImage image) {
+        if (handle < 0) {
+            return DAWN_VALIDATION_ERROR("Trying to import memory with invalid handle");
+        }
+
+        VkMemoryDedicatedAllocateInfo memoryDedicatedAllocateInfo;
+        memoryDedicatedAllocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO;
+        memoryDedicatedAllocateInfo.pNext = nullptr;
+        memoryDedicatedAllocateInfo.image = image;
+        memoryDedicatedAllocateInfo.buffer = VK_NULL_HANDLE;
+
+        VkImportMemoryFdInfoKHR importMemoryFdInfo;
+        importMemoryFdInfo.sType = VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHR;
+        importMemoryFdInfo.pNext = &memoryDedicatedAllocateInfo;
+        importMemoryFdInfo.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT,
+        importMemoryFdInfo.fd = handle;
+
+        VkMemoryAllocateInfo memoryAllocateInfo;
+        memoryAllocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
+        memoryAllocateInfo.pNext = &importMemoryFdInfo;
+        memoryAllocateInfo.allocationSize = importParams.allocationSize;
+        memoryAllocateInfo.memoryTypeIndex = importParams.memoryTypeIndex;
+
+        VkDeviceMemory allocatedMemory = VK_NULL_HANDLE;
+        DAWN_TRY(
+            CheckVkSuccess(mDevice->fn.AllocateMemory(mDevice->GetVkDevice(), &memoryAllocateInfo,
+                                                      nullptr, &allocatedMemory),
+                           "vkAllocateMemory"));
+        return allocatedMemory;
+    }
+
+    ResultOrError<VkImage> Service::CreateImage(const ExternalImageDescriptor* descriptor,
+                                                const VkImageCreateInfo& baseCreateInfo) {
+        if (descriptor->type != ExternalImageDescriptorType::DmaBuf) {
+            return DAWN_VALIDATION_ERROR("ExternalImageDescriptor is not a dma-buf descriptor");
+        }
+        const ExternalImageDescriptorDmaBuf* dmaBufDescriptor =
+            static_cast<const ExternalImageDescriptorDmaBuf*>(descriptor);
+        VkPhysicalDevice physicalDevice = ToBackend(mDevice->GetAdapter())->GetPhysicalDevice();
+        VkDevice device = mDevice->GetVkDevice();
+
+        // Dawn currently doesn't support multi-plane formats, so we only need to create a single
+        // VkSubresourceLayout here.
+        VkSubresourceLayout planeLayout;
+        planeLayout.offset = 0;
+        planeLayout.size = 0;  // VK_EXT_image_drm_format_modifier mandates size = 0.
+        planeLayout.rowPitch = dmaBufDescriptor->stride;
+        planeLayout.arrayPitch = 0;  // Not an array texture
+        planeLayout.depthPitch = 0;  // Not a depth texture
+
+        uint32_t planeCount;
+        DAWN_TRY_ASSIGN(planeCount,
+                        GetModifierPlaneCount(mDevice->fn, physicalDevice, baseCreateInfo.format,
+                                              dmaBufDescriptor->drmModifier));
+        ASSERT(planeCount == 1);
+
+        VkImageDrmFormatModifierExplicitCreateInfoEXT explicitCreateInfo;
+        explicitCreateInfo.sType =
+            VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_EXPLICIT_CREATE_INFO_EXT;
+        explicitCreateInfo.pNext = NULL;
+        explicitCreateInfo.drmFormatModifier = dmaBufDescriptor->drmModifier;
+        explicitCreateInfo.drmFormatModifierPlaneCount = planeCount;
+        explicitCreateInfo.pPlaneLayouts = &planeLayout;
+
+        VkExternalMemoryImageCreateInfo externalMemoryImageCreateInfo;
+        externalMemoryImageCreateInfo.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO;
+        externalMemoryImageCreateInfo.pNext = &explicitCreateInfo;
+        externalMemoryImageCreateInfo.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT;
+
+        VkImageCreateInfo createInfo = baseCreateInfo;
+        createInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+        createInfo.pNext = &externalMemoryImageCreateInfo;
+        createInfo.flags = 0;
+        createInfo.tiling = VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT;
+
+        // Create a new VkImage with tiling equal to the DRM format modifier.
+        VkImage image;
+        DAWN_TRY(CheckVkSuccess(mDevice->fn.CreateImage(device, &createInfo, nullptr, &image),
+                                "CreateImage"));
+        return image;
+    }
+
+}}}  // namespace dawn_native::vulkan::external_memory
diff --git a/src/dawn_native/vulkan/external_memory/MemoryServiceNull.cpp b/src/dawn_native/vulkan/external_memory/MemoryServiceNull.cpp
index 78144d6..14d882a 100644
--- a/src/dawn_native/vulkan/external_memory/MemoryServiceNull.cpp
+++ b/src/dawn_native/vulkan/external_memory/MemoryServiceNull.cpp
@@ -32,7 +32,9 @@
         return false;
     }
 
-    bool Service::SupportsCreateImage(const ExternalImageDescriptor* descriptor, VkFormat format) {
+    bool Service::SupportsCreateImage(const ExternalImageDescriptor* descriptor,
+                                      VkFormat format,
+                                      VkImageUsageFlags usage) {
         return false;
     }
 
diff --git a/src/dawn_native/vulkan/external_memory/MemoryServiceOpaqueFD.cpp b/src/dawn_native/vulkan/external_memory/MemoryServiceOpaqueFD.cpp
index 5e6bf7c..2a31b31 100644
--- a/src/dawn_native/vulkan/external_memory/MemoryServiceOpaqueFD.cpp
+++ b/src/dawn_native/vulkan/external_memory/MemoryServiceOpaqueFD.cpp
@@ -81,7 +81,9 @@
                !(memoryFlags & VK_EXTERNAL_MEMORY_FEATURE_DEDICATED_ONLY_BIT_KHR);
     }
 
-    bool Service::SupportsCreateImage(const ExternalImageDescriptor* descriptor, VkFormat format) {
+    bool Service::SupportsCreateImage(const ExternalImageDescriptor* descriptor,
+                                      VkFormat format,
+                                      VkImageUsageFlags usage) {
         return mSupported;
     }
 
diff --git a/src/dawn_native/vulkan/external_memory/MemoryServiceZirconHandle.cpp b/src/dawn_native/vulkan/external_memory/MemoryServiceZirconHandle.cpp
index bfbd5cc..8c70c67 100644
--- a/src/dawn_native/vulkan/external_memory/MemoryServiceZirconHandle.cpp
+++ b/src/dawn_native/vulkan/external_memory/MemoryServiceZirconHandle.cpp
@@ -81,7 +81,9 @@
                !(memoryFlags & VK_EXTERNAL_MEMORY_FEATURE_DEDICATED_ONLY_BIT_KHR);
     }
 
-    bool Service::SupportsCreateImage(const ExternalImageDescriptor* descriptor, VkFormat format) {
+    bool Service::SupportsCreateImage(const ExternalImageDescriptor* descriptor,
+                                      VkFormat format,
+                                      VkImageUsageFlags usage) {
         return mSupported;
     }