Define an interface to import dma-bufs

This CL adds an API to import a dma-buf into Dawn as a WGPUTexture.

We also add a descriptor type enum to the base
ExternalImageDescriptor struct. This is because all memory import
code (e.g. MemoryService, Texture::CreateFromExternal) takes the
a base ExternalImageDescriptor as a parameter. The dma-buf external
memory and image services, however, will need to downcast to
ExternalImageDescriptorDmaBuf to access import parameters like
stride. Explicitly adding a type enum will let us more safely verify
the type before downcasting.

BUG=chromium:996470

Change-Id: I2d9883a15e9059a91f2c7bdb7a96d74373e18c56
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/13782
Reviewed-by: Austin Eng <enga@chromium.org>
Commit-Queue: Brian Ho <hob@chromium.org>
diff --git a/src/dawn_native/vulkan/TextureVk.cpp b/src/dawn_native/vulkan/TextureVk.cpp
index 86104bb..3422ab0 100644
--- a/src/dawn_native/vulkan/TextureVk.cpp
+++ b/src/dawn_native/vulkan/TextureVk.cpp
@@ -518,11 +518,6 @@
                                            VkDeviceMemory externalMemoryAllocation,
                                            std::vector<VkSemaphore> waitSemaphores) {
         Device* device = ToBackend(GetDevice());
-        VkMemoryRequirements requirements;
-        device->fn.GetImageMemoryRequirements(device->GetVkDevice(), mHandle, &requirements);
-
-        ASSERT(requirements.size <= descriptor->allocationSize);
-
         DAWN_TRY(CheckVkSuccess(
             device->fn.BindImageMemory(device->GetVkDevice(), mHandle, externalMemoryAllocation, 0),
             "BindImageMemory (external)"));
diff --git a/src/dawn_native/vulkan/VulkanBackend.cpp b/src/dawn_native/vulkan/VulkanBackend.cpp
index e13b965..02c3c38 100644
--- a/src/dawn_native/vulkan/VulkanBackend.cpp
+++ b/src/dawn_native/vulkan/VulkanBackend.cpp
@@ -60,6 +60,23 @@
     }
 
 #ifdef DAWN_PLATFORM_LINUX
+    ExternalImageDescriptor::ExternalImageDescriptor(ExternalImageDescriptorType type)
+        : type(type) {
+    }
+
+    ExternalImageDescriptorFD::ExternalImageDescriptorFD(ExternalImageDescriptorType type)
+        : ExternalImageDescriptor(type) {
+    }
+
+    ExternalImageDescriptorOpaqueFD::ExternalImageDescriptorOpaqueFD()
+        : ExternalImageDescriptorFD(ExternalImageDescriptorType::OpaqueFD) {
+    }
+
+    ExternalImageDescriptorDmaBuf::ExternalImageDescriptorDmaBuf()
+        : ExternalImageDescriptorFD(ExternalImageDescriptorType::DmaBuf) {
+    }
+
+    // TODO(hob): Remove this once we switch over to WrapVulkanImage in Chromium.
     WGPUTexture WrapVulkanImageOpaqueFD(WGPUDevice cDevice,
                                         const ExternalImageDescriptorOpaqueFD* descriptor) {
         Device* device = reinterpret_cast<Device*>(cDevice);
@@ -85,6 +102,23 @@
 
         return outHandle;
     }
+
+    WGPUTexture WrapVulkanImage(WGPUDevice cDevice, const ExternalImageDescriptor* descriptor) {
+        Device* device = reinterpret_cast<Device*>(cDevice);
+
+        switch (descriptor->type) {
+            case ExternalImageDescriptorType::OpaqueFD:
+            case ExternalImageDescriptorType::DmaBuf: {
+                const ExternalImageDescriptorFD* fdDescriptor =
+                    static_cast<const ExternalImageDescriptorFD*>(descriptor);
+                TextureBase* texture = device->CreateTextureWrappingVulkanImage(
+                    descriptor, fdDescriptor->memoryFD, fdDescriptor->waitFDs);
+                return reinterpret_cast<WGPUTexture>(texture);
+            }
+            default:
+                return nullptr;
+        }
+    }
 #endif
 
 }}  // namespace dawn_native::vulkan
diff --git a/src/dawn_native/vulkan/external_memory/MemoryServiceOpaqueFD.cpp b/src/dawn_native/vulkan/external_memory/MemoryServiceOpaqueFD.cpp
index 00cb446..5e6bf7c 100644
--- a/src/dawn_native/vulkan/external_memory/MemoryServiceOpaqueFD.cpp
+++ b/src/dawn_native/vulkan/external_memory/MemoryServiceOpaqueFD.cpp
@@ -88,7 +88,14 @@
     ResultOrError<MemoryImportParams> Service::GetMemoryImportParams(
         const ExternalImageDescriptor* descriptor,
         VkImage image) {
-        MemoryImportParams params = {descriptor->allocationSize, descriptor->memoryTypeIndex};
+        if (descriptor->type != ExternalImageDescriptorType::OpaqueFD) {
+            return DAWN_VALIDATION_ERROR("ExternalImageDescriptor is not an OpaqueFD descriptor");
+        }
+        const ExternalImageDescriptorOpaqueFD* opaqueFDDescriptor =
+            static_cast<const ExternalImageDescriptorOpaqueFD*>(descriptor);
+
+        MemoryImportParams params = {opaqueFDDescriptor->allocationSize,
+                                     opaqueFDDescriptor->memoryTypeIndex};
         return params;
     }
 
@@ -99,6 +106,12 @@
             return DAWN_VALIDATION_ERROR("Trying to import memory with invalid handle");
         }
 
+        VkMemoryRequirements requirements;
+        mDevice->fn.GetImageMemoryRequirements(mDevice->GetVkDevice(), image, &requirements);
+        if (requirements.size > importParams.allocationSize) {
+            return DAWN_VALIDATION_ERROR("Requested allocation size is too small for image");
+        }
+
         VkImportMemoryFdInfoKHR importMemoryFdInfo;
         importMemoryFdInfo.sType = VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHR;
         importMemoryFdInfo.pNext = nullptr;
diff --git a/src/dawn_native/vulkan/external_memory/MemoryServiceZirconHandle.cpp b/src/dawn_native/vulkan/external_memory/MemoryServiceZirconHandle.cpp
index 0a72205..bfbd5cc 100644
--- a/src/dawn_native/vulkan/external_memory/MemoryServiceZirconHandle.cpp
+++ b/src/dawn_native/vulkan/external_memory/MemoryServiceZirconHandle.cpp
@@ -88,7 +88,14 @@
     ResultOrError<MemoryImportParams> Service::GetMemoryImportParams(
         const ExternalImageDescriptor* descriptor,
         VkImage image) {
-        MemoryImportParams params = {descriptor->allocationSize, descriptor->memoryTypeIndex};
+        if (descriptor->type != ExternalImageDescriptorType::OpaqueFD) {
+            return DAWN_VALIDATION_ERROR("ExternalImageDescriptor is not an OpaqueFD descriptor");
+        }
+        const ExternalImageDescriptorOpaqueFD* opaqueFDDescriptor =
+            static_cast<const ExternalImageDescriptorOpaqueFD*>(descriptor);
+
+        MemoryImportParams params = {opaqueFDDescriptor->allocationSize,
+                                     opaqueFDDescriptor->memoryTypeIndex};
         return params;
     }
 
@@ -99,6 +106,12 @@
             return DAWN_VALIDATION_ERROR("Trying to import memory with invalid handle");
         }
 
+        VkMemoryRequirements requirements;
+        mDevice->fn.GetImageMemoryRequirements(mDevice->GetVkDevice(), image, &requirements);
+        if (requirements.size > importParams.allocationSize) {
+            return DAWN_VALIDATION_ERROR("Requested allocation size is too small for image");
+        }
+
         VkImportMemoryZirconHandleInfoFUCHSIA importMemoryHandleInfo;
         importMemoryHandleInfo.sType =
             VK_STRUCTURE_TYPE_TEMP_MEMORY_ZIRCON_HANDLE_PROPERTIES_FUCHSIA;
diff --git a/src/include/dawn_native/VulkanBackend.h b/src/include/dawn_native/VulkanBackend.h
index 77a4c4f..46dbdb9 100644
--- a/src/include/dawn_native/VulkanBackend.h
+++ b/src/include/dawn_native/VulkanBackend.h
@@ -24,12 +24,23 @@
 
 namespace dawn_native { namespace vulkan {
 
+    // The different types of ExternalImageDescriptors
+    enum ExternalImageDescriptorType {
+#ifdef __linux__
+        OpaqueFD,
+        DmaBuf,
+#endif  // __linux__
+    };
+
     // Common properties of external images
     struct ExternalImageDescriptor {
+      public:
+        const ExternalImageDescriptorType type;           // Must match the subclass
         const WGPUTextureDescriptor* cTextureDescriptor;  // Must match image creation params
-        bool isCleared;               // Sets whether the texture will be cleared before use
-        VkDeviceSize allocationSize;  // Must match VkMemoryAllocateInfo from image creation
-        uint32_t memoryTypeIndex;     // Must match VkMemoryAllocateInfo from image creation
+        bool isCleared;  // Sets whether the texture will be cleared before use
+
+      protected:
+        ExternalImageDescriptor(ExternalImageDescriptorType type);
     };
 
     DAWN_NATIVE_EXPORT VkInstance GetInstance(WGPUDevice device);
@@ -43,10 +54,30 @@
 
 // Can't use DAWN_PLATFORM_LINUX since header included in both dawn and chrome
 #ifdef __linux__
-        // Descriptor for opaque file descriptor image import
-        struct ExternalImageDescriptorOpaqueFD : ExternalImageDescriptor {
+        // Common properties of external images represented by FDs
+        struct ExternalImageDescriptorFD : ExternalImageDescriptor {
+          public:
             int memoryFD;  // A file descriptor from an export of the memory of the image
             std::vector<int> waitFDs;  // File descriptors of semaphores which will be waited on
+
+          protected:
+            ExternalImageDescriptorFD(ExternalImageDescriptorType type);
+        };
+
+        // Descriptor for opaque file descriptor image import
+        struct ExternalImageDescriptorOpaqueFD : ExternalImageDescriptorFD {
+            ExternalImageDescriptorOpaqueFD();
+
+            VkDeviceSize allocationSize;  // Must match VkMemoryAllocateInfo from image creation
+            uint32_t memoryTypeIndex;     // Must match VkMemoryAllocateInfo from image creation
+        };
+
+        // Descriptor for dma-buf file descriptor image import
+        struct ExternalImageDescriptorDmaBuf : ExternalImageDescriptorFD {
+            ExternalImageDescriptorDmaBuf();
+
+            uint32_t stride;       // Stride of the buffer in bytes
+            uint64_t drmModifier;  // DRM modifier of the buffer
         };
 
         // Imports an external vulkan image from an opaque file descriptor. Internally, this uses
@@ -54,6 +85,8 @@
         // |descriptor->waitFDs| before the texture can be used. Finally, a signal semaphore
         // can be exported, transferring control back to the caller.
         // On failure, returns a nullptr
+        // NOTE: This is deprecated. Use WrapVulkanImage instead.
+        // TODO(hob): Remove this once Chromium has switched over to WrapVulkanImage.
         DAWN_NATIVE_EXPORT WGPUTexture
         WrapVulkanImageOpaqueFD(WGPUDevice cDevice,
                                 const ExternalImageDescriptorOpaqueFD* descriptor);
@@ -62,6 +95,13 @@
         // textures before they are destroyed. On failure, returns -1
         DAWN_NATIVE_EXPORT int ExportSignalSemaphoreOpaqueFD(WGPUDevice cDevice,
                                                              WGPUTexture cTexture);
+
+        // Imports external memory into a Vulkan image. Internally, this uses external memory /
+        // semaphore extensions to import the image and wait on the provided synchronizaton
+        // primitives before the texture can be used.
+        // On failure, returns a nullptr.
+        DAWN_NATIVE_EXPORT WGPUTexture WrapVulkanImage(WGPUDevice cDevice,
+                                                       const ExternalImageDescriptor* descriptor);
 #endif  // __linux__
 }}  // namespace dawn_native::vulkan