Add CreateImage support to MemoryService

This CL introduces SupportsCreateImage and CreateImage to the
external memory service API to provide different implementations for
creating VkImages for different types of external image handles.

While opaque FD and Zircon seem to share the same vkCreateImage
implementation, dma-buf import will require the use of the
VkImageDrmFormatModifierExplicitCreateInfoEXT [1] struct (among
other differences) with vkCreateImage.

[1] https://www.khronos.org/registry/vulkan/specs/1.1-extensions/man/html/VkImageDrmFormatModifierExplicitCreateInfoEXT.html

BUG=chromium:996470

Change-Id: I3eb11f8877d4465f5fcdd4089d5fdd8acbc1da10
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/13781
Reviewed-by: Austin Eng <enga@chromium.org>
Commit-Queue: Brian Ho <hob@chromium.org>
diff --git a/src/dawn_native/vulkan/DeviceVk.cpp b/src/dawn_native/vulkan/DeviceVk.cpp
index ed85121..f317588 100644
--- a/src/dawn_native/vulkan/DeviceVk.cpp
+++ b/src/dawn_native/vulkan/DeviceVk.cpp
@@ -615,7 +615,7 @@
         if (!mExternalSemaphoreService->Supported()) {
             return DAWN_VALIDATION_ERROR("External semaphore usage not supported");
         }
-        if (!mExternalMemoryService->Supported(
+        if (!mExternalMemoryService->SupportsImportMemory(
                 VulkanImageFormat(textureDescriptor->format), VK_IMAGE_TYPE_2D,
                 VK_IMAGE_TILING_OPTIMAL,
                 VulkanImageUsage(textureDescriptor->usage,
@@ -683,7 +683,8 @@
         // if a failure happems.
         Texture* result = nullptr;
         // TODO(crbug.com/1026480): Consolidate this into a single CreateFromExternal call.
-        if (ConsumedError(Texture::CreateFromExternal(this, descriptor, textureDescriptor),
+        if (ConsumedError(Texture::CreateFromExternal(this, descriptor, textureDescriptor,
+                                                      mExternalMemoryService.get()),
                           &result) ||
             ConsumedError(ImportExternalImage(descriptor, memoryHandle, result->GetHandle(),
                                               waitHandles, &signalSemaphore, &allocation,
diff --git a/src/dawn_native/vulkan/TextureVk.cpp b/src/dawn_native/vulkan/TextureVk.cpp
index 211719d..86104bb 100644
--- a/src/dawn_native/vulkan/TextureVk.cpp
+++ b/src/dawn_native/vulkan/TextureVk.cpp
@@ -200,22 +200,6 @@
             return {extent.width, extent.height, extent.depth};
         }
 
-        bool IsSampleCountSupported(const dawn_native::vulkan::Device* device,
-                                    const VkImageCreateInfo& imageCreateInfo) {
-            ASSERT(device);
-
-            VkPhysicalDevice physicalDevice = ToBackend(device->GetAdapter())->GetPhysicalDevice();
-            VkImageFormatProperties properties;
-            if (device->fn.GetPhysicalDeviceImageFormatProperties(
-                    physicalDevice, imageCreateInfo.format, imageCreateInfo.imageType,
-                    imageCreateInfo.tiling, imageCreateInfo.usage, imageCreateInfo.flags,
-                    &properties) != VK_SUCCESS) {
-                UNREACHABLE();
-            }
-
-            return properties.sampleCounts & imageCreateInfo.samples;
-        }
-
     }  // namespace
 
     // Converts Dawn texture format to Vulkan formats.
@@ -397,6 +381,22 @@
         return {};
     }
 
+    bool IsSampleCountSupported(const dawn_native::vulkan::Device* device,
+                                const VkImageCreateInfo& imageCreateInfo) {
+        ASSERT(device);
+
+        VkPhysicalDevice physicalDevice = ToBackend(device->GetAdapter())->GetPhysicalDevice();
+        VkImageFormatProperties properties;
+        if (device->fn.GetPhysicalDeviceImageFormatProperties(
+                physicalDevice, imageCreateInfo.format, imageCreateInfo.imageType,
+                imageCreateInfo.tiling, imageCreateInfo.usage, imageCreateInfo.flags,
+                &properties) != VK_SUCCESS) {
+            UNREACHABLE();
+        }
+
+        return properties.sampleCounts & imageCreateInfo.samples;
+    }
+
     // static
     ResultOrError<Texture*> Texture::Create(Device* device, const TextureDescriptor* descriptor) {
         std::unique_ptr<Texture> texture =
@@ -409,10 +409,11 @@
     ResultOrError<Texture*> Texture::CreateFromExternal(
         Device* device,
         const ExternalImageDescriptor* descriptor,
-        const TextureDescriptor* textureDescriptor) {
+        const TextureDescriptor* textureDescriptor,
+        external_memory::Service* externalMemoryService) {
         std::unique_ptr<Texture> texture =
             std::make_unique<Texture>(device, textureDescriptor, TextureState::OwnedInternal);
-        DAWN_TRY(texture->InitializeFromExternal(descriptor));
+        DAWN_TRY(texture->InitializeFromExternal(descriptor, externalMemoryService));
         return texture.release();
     }
 
@@ -481,38 +482,34 @@
     }
 
     // Internally managed, but imported from external handle
-    MaybeError Texture::InitializeFromExternal(const ExternalImageDescriptor* descriptor) {
+    MaybeError Texture::InitializeFromExternal(const ExternalImageDescriptor* descriptor,
+                                               external_memory::Service* externalMemoryService) {
+        VkFormat format = VulkanImageFormat(GetFormat().format);
+        if (!externalMemoryService->SupportsCreateImage(descriptor, format)) {
+            return DAWN_VALIDATION_ERROR("Creating an image from external memory is not supported");
+        }
+
         mExternalState = ExternalState::PendingAcquire;
-        Device* device = ToBackend(GetDevice());
-
-        VkImageCreateInfo createInfo = {};
-        createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
-        createInfo.pNext = nullptr;
-        createInfo.flags = VK_IMAGE_CREATE_ALIAS_BIT_KHR;
-        createInfo.imageType = VulkanImageType(GetDimension());
-        createInfo.format = VulkanImageFormat(GetFormat().format);
-        createInfo.extent = VulkanExtent3D(GetSize());
-        createInfo.mipLevels = GetNumMipLevels();
-        createInfo.arrayLayers = GetArrayLayers();
-        createInfo.samples = VulkanSampleCount(GetSampleCount());
-        createInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
-        createInfo.usage = VulkanImageUsage(GetUsage(), GetFormat());
-        createInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
-        createInfo.queueFamilyIndexCount = 0;
-        createInfo.pQueueFamilyIndices = nullptr;
-        createInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
-
-        ASSERT(IsSampleCountSupported(device, createInfo));
+        VkImageCreateInfo baseCreateInfo = {};
+        baseCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
+        baseCreateInfo.pNext = nullptr;
+        baseCreateInfo.imageType = VulkanImageType(GetDimension());
+        baseCreateInfo.format = format;
+        baseCreateInfo.extent = VulkanExtent3D(GetSize());
+        baseCreateInfo.mipLevels = GetNumMipLevels();
+        baseCreateInfo.arrayLayers = GetArrayLayers();
+        baseCreateInfo.samples = VulkanSampleCount(GetSampleCount());
+        baseCreateInfo.usage = VulkanImageUsage(GetUsage(), GetFormat());
+        baseCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
+        baseCreateInfo.queueFamilyIndexCount = 0;
+        baseCreateInfo.pQueueFamilyIndices = nullptr;
 
         // We always set VK_IMAGE_USAGE_TRANSFER_DST_BIT unconditionally beause the Vulkan images
         // that are used in vkCmdClearColorImage() must have been created with this flag, which is
         // also required for the implementation of robust resource initialization.
-        createInfo.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
+        baseCreateInfo.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
 
-        DAWN_TRY(CheckVkSuccess(
-            device->fn.CreateImage(device->GetVkDevice(), &createInfo, nullptr, &mHandle),
-            "CreateImage"));
-
+        DAWN_TRY_ASSIGN(mHandle, externalMemoryService->CreateImage(descriptor, baseCreateInfo));
         return {};
     }
 
diff --git a/src/dawn_native/vulkan/TextureVk.h b/src/dawn_native/vulkan/TextureVk.h
index 5174292..82366ae 100644
--- a/src/dawn_native/vulkan/TextureVk.h
+++ b/src/dawn_native/vulkan/TextureVk.h
@@ -20,6 +20,7 @@
 #include "common/vulkan_platform.h"
 #include "dawn_native/ResourceMemoryAllocation.h"
 #include "dawn_native/vulkan/ExternalHandle.h"
+#include "dawn_native/vulkan/external_memory/MemoryService.h"
 
 namespace dawn_native { namespace vulkan {
 
@@ -34,6 +35,9 @@
     MaybeError ValidateVulkanImageCanBeWrapped(const DeviceBase* device,
                                                const TextureDescriptor* descriptor);
 
+    bool IsSampleCountSupported(const dawn_native::vulkan::Device* device,
+                                const VkImageCreateInfo& imageCreateInfo);
+
     class Texture : public TextureBase {
       public:
         // Used to create a regular texture from a descriptor.
@@ -45,7 +49,8 @@
         static ResultOrError<Texture*> CreateFromExternal(
             Device* device,
             const ExternalImageDescriptor* descriptor,
-            const TextureDescriptor* textureDescriptor);
+            const TextureDescriptor* textureDescriptor,
+            external_memory::Service* externalMemoryService);
 
         Texture(Device* device, const TextureDescriptor* descriptor, VkImage nativeImage);
         ~Texture();
@@ -76,7 +81,8 @@
         using TextureBase::TextureBase;
         MaybeError InitializeAsInternalTexture();
 
-        MaybeError InitializeFromExternal(const ExternalImageDescriptor* descriptor);
+        MaybeError InitializeFromExternal(const ExternalImageDescriptor* descriptor,
+                                          external_memory::Service* externalMemoryService);
 
         void DestroyImpl() override;
         MaybeError ClearTexture(CommandRecordingContext* recordingContext,
diff --git a/src/dawn_native/vulkan/external_memory/MemoryService.h b/src/dawn_native/vulkan/external_memory/MemoryService.h
index bcd3ea4..1d0d475 100644
--- a/src/dawn_native/vulkan/external_memory/MemoryService.h
+++ b/src/dawn_native/vulkan/external_memory/MemoryService.h
@@ -36,12 +36,15 @@
         explicit Service(Device* device);
         ~Service();
 
-        // True if the device reports it supports this feature
-        bool Supported(VkFormat format,
-                       VkImageType type,
-                       VkImageTiling tiling,
-                       VkImageUsageFlags usage,
-                       VkImageCreateFlags flags);
+        // True if the device reports it supports importing external memory.
+        bool SupportsImportMemory(VkFormat format,
+                                  VkImageType type,
+                                  VkImageTiling tiling,
+                                  VkImageUsageFlags usage,
+                                  VkImageCreateFlags flags);
+
+        // True if the device reports it supports creating VkImages from external memory.
+        bool SupportsCreateImage(const ExternalImageDescriptor* descriptor, VkFormat format);
 
         // Returns the parameters required for importing memory
         ResultOrError<MemoryImportParams> GetMemoryImportParams(
@@ -53,6 +56,10 @@
                                                    const MemoryImportParams& importParams,
                                                    VkImage image);
 
+        // Create a VkImage for the given handle type
+        ResultOrError<VkImage> CreateImage(const ExternalImageDescriptor* descriptor,
+                                           const VkImageCreateInfo& baseCreateInfo);
+
       private:
         Device* mDevice = nullptr;
 
diff --git a/src/dawn_native/vulkan/external_memory/MemoryServiceNull.cpp b/src/dawn_native/vulkan/external_memory/MemoryServiceNull.cpp
index c7417d7..78144d6 100644
--- a/src/dawn_native/vulkan/external_memory/MemoryServiceNull.cpp
+++ b/src/dawn_native/vulkan/external_memory/MemoryServiceNull.cpp
@@ -24,11 +24,15 @@
 
     Service::~Service() = default;
 
-    bool Service::Supported(VkFormat format,
-                            VkImageType type,
-                            VkImageTiling tiling,
-                            VkImageUsageFlags usage,
-                            VkImageCreateFlags flags) {
+    bool Service::SupportsImportMemory(VkFormat format,
+                                       VkImageType type,
+                                       VkImageTiling tiling,
+                                       VkImageUsageFlags usage,
+                                       VkImageCreateFlags flags) {
+        return false;
+    }
+
+    bool Service::SupportsCreateImage(const ExternalImageDescriptor* descriptor, VkFormat format) {
         return false;
     }
 
@@ -44,4 +48,9 @@
         return DAWN_UNIMPLEMENTED_ERROR("Using null memory service to interop inside Vulkan");
     }
 
+    ResultOrError<VkImage> Service::CreateImage(const ExternalImageDescriptor* descriptor,
+                                                const VkImageCreateInfo& baseCreateInfo) {
+        return DAWN_UNIMPLEMENTED_ERROR("Using null memory service to interop inside Vulkan");
+    }
+
 }}}  // namespace dawn_native::vulkan::external_memory
diff --git a/src/dawn_native/vulkan/external_memory/MemoryServiceOpaqueFD.cpp b/src/dawn_native/vulkan/external_memory/MemoryServiceOpaqueFD.cpp
index 64cd3a5..00cb446 100644
--- a/src/dawn_native/vulkan/external_memory/MemoryServiceOpaqueFD.cpp
+++ b/src/dawn_native/vulkan/external_memory/MemoryServiceOpaqueFD.cpp
@@ -16,6 +16,7 @@
 #include "dawn_native/vulkan/AdapterVk.h"
 #include "dawn_native/vulkan/BackendVk.h"
 #include "dawn_native/vulkan/DeviceVk.h"
+#include "dawn_native/vulkan/TextureVk.h"
 #include "dawn_native/vulkan/VulkanError.h"
 #include "dawn_native/vulkan/external_memory/MemoryService.h"
 
@@ -33,11 +34,11 @@
 
     Service::~Service() = default;
 
-    bool Service::Supported(VkFormat format,
-                            VkImageType type,
-                            VkImageTiling tiling,
-                            VkImageUsageFlags usage,
-                            VkImageCreateFlags flags) {
+    bool Service::SupportsImportMemory(VkFormat format,
+                                       VkImageType type,
+                                       VkImageTiling tiling,
+                                       VkImageUsageFlags usage,
+                                       VkImageCreateFlags flags) {
         // Early out before we try using extension functions
         if (!mSupported) {
             return false;
@@ -80,6 +81,10 @@
                !(memoryFlags & VK_EXTERNAL_MEMORY_FEATURE_DEDICATED_ONLY_BIT_KHR);
     }
 
+    bool Service::SupportsCreateImage(const ExternalImageDescriptor* descriptor, VkFormat format) {
+        return mSupported;
+    }
+
     ResultOrError<MemoryImportParams> Service::GetMemoryImportParams(
         const ExternalImageDescriptor* descriptor,
         VkImage image) {
@@ -113,4 +118,20 @@
         return allocatedMemory;
     }
 
+    ResultOrError<VkImage> Service::CreateImage(const ExternalImageDescriptor* descriptor,
+                                                const VkImageCreateInfo& baseCreateInfo) {
+        VkImageCreateInfo createInfo = baseCreateInfo;
+        createInfo.flags = VK_IMAGE_CREATE_ALIAS_BIT_KHR;
+        createInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
+        createInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+
+        ASSERT(IsSampleCountSupported(mDevice, createInfo));
+
+        VkImage image;
+        DAWN_TRY(CheckVkSuccess(
+            mDevice->fn.CreateImage(mDevice->GetVkDevice(), &createInfo, nullptr, &image),
+            "CreateImage"));
+        return image;
+    }
+
 }}}  // namespace dawn_native::vulkan::external_memory
diff --git a/src/dawn_native/vulkan/external_memory/MemoryServiceZirconHandle.cpp b/src/dawn_native/vulkan/external_memory/MemoryServiceZirconHandle.cpp
index d461d44..0a72205 100644
--- a/src/dawn_native/vulkan/external_memory/MemoryServiceZirconHandle.cpp
+++ b/src/dawn_native/vulkan/external_memory/MemoryServiceZirconHandle.cpp
@@ -16,6 +16,7 @@
 #include "dawn_native/vulkan/AdapterVk.h"
 #include "dawn_native/vulkan/BackendVk.h"
 #include "dawn_native/vulkan/DeviceVk.h"
+#include "dawn_native/vulkan/TextureVk.h"
 #include "dawn_native/vulkan/VulkanError.h"
 #include "dawn_native/vulkan/external_memory/MemoryService.h"
 
@@ -33,11 +34,11 @@
 
     Service::~Service() = default;
 
-    bool Service::Supported(VkFormat format,
-                            VkImageType type,
-                            VkImageTiling tiling,
-                            VkImageUsageFlags usage,
-                            VkImageCreateFlags flags) {
+    bool Service::SupportsImportMemory(VkFormat format,
+                                       VkImageType type,
+                                       VkImageTiling tiling,
+                                       VkImageUsageFlags usage,
+                                       VkImageCreateFlags flags) {
         // Early out before we try using extension functions
         if (!mSupported) {
             return false;
@@ -80,6 +81,10 @@
                !(memoryFlags & VK_EXTERNAL_MEMORY_FEATURE_DEDICATED_ONLY_BIT_KHR);
     }
 
+    bool Service::SupportsCreateImage(const ExternalImageDescriptor* descriptor, VkFormat format) {
+        return mSupported;
+    }
+
     ResultOrError<MemoryImportParams> Service::GetMemoryImportParams(
         const ExternalImageDescriptor* descriptor,
         VkImage image) {
@@ -115,4 +120,20 @@
         return allocatedMemory;
     }
 
+    ResultOrError<VkImage> Service::CreateImage(const ExternalImageDescriptor* descriptor,
+                                                const VkImageCreateInfo& baseCreateInfo) {
+        VkImageCreateInfo createInfo = baseCreateInfo;
+        createInfo.flags = VK_IMAGE_CREATE_ALIAS_BIT_KHR;
+        createInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
+        createInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+
+        ASSERT(IsSampleCountSupported(mDevice, createInfo));
+
+        VkImage image;
+        DAWN_TRY(CheckVkSuccess(
+            mDevice->fn.CreateImage(mDevice->GetVkDevice(), &createInfo, nullptr, &image),
+            "CreateImage"));
+        return image;
+    }
+
 }}}  // namespace dawn_native::vulkan::external_memory