Add Adapter::SupportsExternalImages

This commit adds a getter so that Chromium can check whether an
Adapter would support external images. External images are
necessary to implement the WebGPU swapchain and interop with the
rest of the web platform. We should not expose adapters that
cannot interop with the canvas and other web platform primitives
until WebGPU gains options to request more "limited" adapters.

Bug: dawn:1056
Change-Id: Iff83ac26b0e92dccdd4e29c0d854d9276a4a37bc
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/61580
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
Reviewed-by: Brian Ho <hob@chromium.org>
Commit-Queue: Austin Eng <enga@chromium.org>
diff --git a/src/dawn_native/Adapter.h b/src/dawn_native/Adapter.h
index 118bcdf..7587eeb 100644
--- a/src/dawn_native/Adapter.h
+++ b/src/dawn_native/Adapter.h
@@ -47,6 +47,8 @@
             const std::vector<const char*>& requestedExtensions) const;
         WGPUDeviceProperties GetAdapterProperties() const;
 
+        virtual bool SupportsExternalImages() const = 0;
+
       protected:
         PCIInfo mPCIInfo = {};
         wgpu::AdapterType mAdapterType = wgpu::AdapterType::Unknown;
diff --git a/src/dawn_native/DawnNative.cpp b/src/dawn_native/DawnNative.cpp
index 849c6c0..1ac8d98 100644
--- a/src/dawn_native/DawnNative.cpp
+++ b/src/dawn_native/DawnNative.cpp
@@ -103,6 +103,10 @@
         return mImpl->GetAdapterProperties();
     }
 
+    bool Adapter::SupportsExternalImages() const {
+        return mImpl->SupportsExternalImages();
+    }
+
     Adapter::operator bool() const {
         return mImpl != nullptr;
     }
diff --git a/src/dawn_native/d3d12/AdapterD3D12.cpp b/src/dawn_native/d3d12/AdapterD3D12.cpp
index b43c1da..d26603a 100644
--- a/src/dawn_native/d3d12/AdapterD3D12.cpp
+++ b/src/dawn_native/d3d12/AdapterD3D12.cpp
@@ -36,6 +36,11 @@
         CleanUpDebugLayerFilters();
     }
 
+    bool Adapter::SupportsExternalImages() const {
+        // Via dawn_native::d3d12::ExternalImageDXGI::Create
+        return true;
+    }
+
     const D3D12DeviceInfo& Adapter::GetDeviceInfo() const {
         return mDeviceInfo;
     }
diff --git a/src/dawn_native/d3d12/AdapterD3D12.h b/src/dawn_native/d3d12/AdapterD3D12.h
index f41b3a1..ea6975e 100644
--- a/src/dawn_native/d3d12/AdapterD3D12.h
+++ b/src/dawn_native/d3d12/AdapterD3D12.h
@@ -30,6 +30,9 @@
         Adapter(Backend* backend, ComPtr<IDXGIAdapter3> hardwareAdapter);
         ~Adapter() override;
 
+        // AdapterBase Implementation
+        bool SupportsExternalImages() const override;
+
         const D3D12DeviceInfo& GetDeviceInfo() const;
         IDXGIAdapter3* GetHardwareAdapter() const;
         Backend* GetBackend() const;
diff --git a/src/dawn_native/metal/BackendMTL.mm b/src/dawn_native/metal/BackendMTL.mm
index 2029673..a3a1212 100644
--- a/src/dawn_native/metal/BackendMTL.mm
+++ b/src/dawn_native/metal/BackendMTL.mm
@@ -205,6 +205,12 @@
             InitializeSupportedExtensions();
         }
 
+        // AdapterBase Implementation
+        bool SupportsExternalImages() const override {
+            // Via dawn_native::metal::WrapIOSurface
+            return true;
+        }
+
       private:
         ResultOrError<DeviceBase*> CreateDeviceImpl(const DeviceDescriptor* descriptor) override {
             return Device::Create(this, mDevice, descriptor);
diff --git a/src/dawn_native/null/DeviceNull.cpp b/src/dawn_native/null/DeviceNull.cpp
index 6baad31..0c0b0d3 100644
--- a/src/dawn_native/null/DeviceNull.cpp
+++ b/src/dawn_native/null/DeviceNull.cpp
@@ -34,6 +34,10 @@
 
     Adapter::~Adapter() = default;
 
+    bool Adapter::SupportsExternalImages() const {
+        return false;
+    }
+
     // Used for the tests that intend to use an adapter without all extensions enabled.
     void Adapter::SetSupportedExtensions(const std::vector<const char*>& requiredExtensions) {
         mSupportedExtensions = GetInstance()->ExtensionNamesToExtensionsSet(requiredExtensions);
diff --git a/src/dawn_native/null/DeviceNull.h b/src/dawn_native/null/DeviceNull.h
index afa141f..0021056 100644
--- a/src/dawn_native/null/DeviceNull.h
+++ b/src/dawn_native/null/DeviceNull.h
@@ -169,6 +169,9 @@
         Adapter(InstanceBase* instance);
         ~Adapter() override;
 
+        // AdapterBase Implementation
+        bool SupportsExternalImages() const override;
+
         // Used for the tests that intend to use an adapter without all extensions enabled.
         void SetSupportedExtensions(const std::vector<const char*>& requiredExtensions);
 
diff --git a/src/dawn_native/opengl/BackendGL.cpp b/src/dawn_native/opengl/BackendGL.cpp
index 0f47e6c..c2b24be 100644
--- a/src/dawn_native/opengl/BackendGL.cpp
+++ b/src/dawn_native/opengl/BackendGL.cpp
@@ -194,6 +194,12 @@
 
         ~Adapter() override = default;
 
+        // AdapterBase Implementation
+        bool SupportsExternalImages() const override {
+            // Via dawn_native::opengl::WrapExternalEGLImage
+            return GetBackendType() == wgpu::BackendType::OpenGLES;
+        }
+
       private:
         OpenGLFunctions mFunctions;
 
diff --git a/src/dawn_native/vulkan/AdapterVk.cpp b/src/dawn_native/vulkan/AdapterVk.cpp
index 194cc82..6d52f31 100644
--- a/src/dawn_native/vulkan/AdapterVk.cpp
+++ b/src/dawn_native/vulkan/AdapterVk.cpp
@@ -251,6 +251,13 @@
         return {};
     }
 
+    bool Adapter::SupportsExternalImages() const {
+        // Via dawn_native::vulkan::WrapVulkanImage
+        return external_memory::Service::CheckSupport(mDeviceInfo) &&
+               external_semaphore::Service::CheckSupport(mDeviceInfo, mPhysicalDevice,
+                                                         mBackend->GetFunctions());
+    }
+
     void Adapter::InitializeSupportedExtensions() {
         if (mDeviceInfo.features.textureCompressionBC == VK_TRUE) {
             mSupportedExtensions.EnableExtension(Extension::TextureCompressionBC);
diff --git a/src/dawn_native/vulkan/AdapterVk.h b/src/dawn_native/vulkan/AdapterVk.h
index 94939d2..a6141c4 100644
--- a/src/dawn_native/vulkan/AdapterVk.h
+++ b/src/dawn_native/vulkan/AdapterVk.h
@@ -29,6 +29,9 @@
         Adapter(Backend* backend, VkPhysicalDevice physicalDevice);
         ~Adapter() override = default;
 
+        // AdapterBase Implementation
+        bool SupportsExternalImages() const override;
+
         const VulkanDeviceInfo& GetDeviceInfo() const;
         VkPhysicalDevice GetPhysicalDevice() const;
         Backend* GetBackend() const;
diff --git a/src/dawn_native/vulkan/external_memory/MemoryService.h b/src/dawn_native/vulkan/external_memory/MemoryService.h
index 0c4b64d..f0653f2 100644
--- a/src/dawn_native/vulkan/external_memory/MemoryService.h
+++ b/src/dawn_native/vulkan/external_memory/MemoryService.h
@@ -22,6 +22,7 @@
 
 namespace dawn_native { namespace vulkan {
     class Device;
+    struct VulkanDeviceInfo;
 }}  // namespace dawn_native::vulkan
 
 namespace dawn_native { namespace vulkan { namespace external_memory {
@@ -36,6 +37,8 @@
         explicit Service(Device* device);
         ~Service();
 
+        static bool CheckSupport(const VulkanDeviceInfo& deviceInfo);
+
         // True if the device reports it supports importing external memory.
         bool SupportsImportMemory(VkFormat format,
                                   VkImageType type,
diff --git a/src/dawn_native/vulkan/external_memory/MemoryServiceDmaBuf.cpp b/src/dawn_native/vulkan/external_memory/MemoryServiceDmaBuf.cpp
index d545890..d3611d9 100644
--- a/src/dawn_native/vulkan/external_memory/MemoryServiceDmaBuf.cpp
+++ b/src/dawn_native/vulkan/external_memory/MemoryServiceDmaBuf.cpp
@@ -60,15 +60,18 @@
 
     }  // anonymous namespace
 
-    Service::Service(Device* device) : mDevice(device) {
-        const VulkanDeviceInfo& deviceInfo = mDevice->GetDeviceInfo();
-
-        mSupported = deviceInfo.HasExt(DeviceExt::ExternalMemoryFD) &&
-                     deviceInfo.HasExt(DeviceExt::ImageDrmFormatModifier);
+    Service::Service(Device* device)
+        : mDevice(device), mSupported(CheckSupport(device->GetDeviceInfo())) {
     }
 
     Service::~Service() = default;
 
+    // static
+    bool Service::CheckSupport(const VulkanDeviceInfo& deviceInfo) {
+        return deviceInfo.HasExt(DeviceExt::ExternalMemoryFD) &&
+               deviceInfo.HasExt(DeviceExt::ImageDrmFormatModifier);
+    }
+
     bool Service::SupportsImportMemory(VkFormat format,
                                        VkImageType type,
                                        VkImageTiling tiling,
diff --git a/src/dawn_native/vulkan/external_memory/MemoryServiceNull.cpp b/src/dawn_native/vulkan/external_memory/MemoryServiceNull.cpp
index 14d882a..1bb4f38 100644
--- a/src/dawn_native/vulkan/external_memory/MemoryServiceNull.cpp
+++ b/src/dawn_native/vulkan/external_memory/MemoryServiceNull.cpp
@@ -24,6 +24,11 @@
 
     Service::~Service() = default;
 
+    // static
+    bool Service::CheckSupport(const VulkanDeviceInfo& deviceInfo) {
+        return false;
+    }
+
     bool Service::SupportsImportMemory(VkFormat format,
                                        VkImageType type,
                                        VkImageTiling tiling,
diff --git a/src/dawn_native/vulkan/external_memory/MemoryServiceOpaqueFD.cpp b/src/dawn_native/vulkan/external_memory/MemoryServiceOpaqueFD.cpp
index ebdab5b..70db38b 100644
--- a/src/dawn_native/vulkan/external_memory/MemoryServiceOpaqueFD.cpp
+++ b/src/dawn_native/vulkan/external_memory/MemoryServiceOpaqueFD.cpp
@@ -22,12 +22,17 @@
 
 namespace dawn_native { namespace vulkan { namespace external_memory {
 
-    Service::Service(Device* device) : mDevice(device) {
-        mSupported = device->GetDeviceInfo().HasExt(DeviceExt::ExternalMemoryFD);
+    Service::Service(Device* device)
+        : mDevice(device), mSupported(CheckSupport(device->GetDeviceInfo())) {
     }
 
     Service::~Service() = default;
 
+    // static
+    bool Service::CheckSupport(const VulkanDeviceInfo& deviceInfo) {
+        return deviceInfo.HasExt(DeviceExt::ExternalMemoryFD);
+    }
+
     bool Service::SupportsImportMemory(VkFormat format,
                                        VkImageType type,
                                        VkImageTiling tiling,
diff --git a/src/dawn_native/vulkan/external_memory/MemoryServiceZirconHandle.cpp b/src/dawn_native/vulkan/external_memory/MemoryServiceZirconHandle.cpp
index ae8744f..6dbfcf9 100644
--- a/src/dawn_native/vulkan/external_memory/MemoryServiceZirconHandle.cpp
+++ b/src/dawn_native/vulkan/external_memory/MemoryServiceZirconHandle.cpp
@@ -22,12 +22,17 @@
 
 namespace dawn_native { namespace vulkan { namespace external_memory {
 
-    Service::Service(Device* device) : mDevice(device) {
-        mSupported = device->GetDeviceInfo().HasExt(DeviceExt::ExternalMemoryZirconHandle);
+    Service::Service(Device* device)
+        : mDevice(device), mSupported(CheckSupport(device->GetDeviceInfo())) {
     }
 
     Service::~Service() = default;
 
+    // static
+    bool Service::CheckSupport(const VulkanDeviceInfo& deviceInfo) {
+        return deviceInfo.HasExt(DeviceExt::ExternalMemoryZirconHandle);
+    }
+
     bool Service::SupportsImportMemory(VkFormat format,
                                        VkImageType type,
                                        VkImageTiling tiling,
diff --git a/src/dawn_native/vulkan/external_semaphore/SemaphoreService.h b/src/dawn_native/vulkan/external_semaphore/SemaphoreService.h
index cceaa2d..5eace87 100644
--- a/src/dawn_native/vulkan/external_semaphore/SemaphoreService.h
+++ b/src/dawn_native/vulkan/external_semaphore/SemaphoreService.h
@@ -30,6 +30,10 @@
         explicit Service(Device* device);
         ~Service();
 
+        static bool CheckSupport(const VulkanDeviceInfo& deviceInfo,
+                                 VkPhysicalDevice physicalDevice,
+                                 const VulkanFunctions& fn);
+
         // True if the device reports it supports this feature
         bool Supported();
 
diff --git a/src/dawn_native/vulkan/external_semaphore/SemaphoreServiceFD.cpp b/src/dawn_native/vulkan/external_semaphore/SemaphoreServiceFD.cpp
index 6b228a9..b250056 100644
--- a/src/dawn_native/vulkan/external_semaphore/SemaphoreServiceFD.cpp
+++ b/src/dawn_native/vulkan/external_semaphore/SemaphoreServiceFD.cpp
@@ -27,12 +27,21 @@
 
 namespace dawn_native { namespace vulkan { namespace external_semaphore {
 
-    Service::Service(Device* device) : mDevice(device) {
-        mSupported = device->GetDeviceInfo().HasExt(DeviceExt::ExternalSemaphoreFD);
+    Service::Service(Device* device)
+        : mDevice(device),
+          mSupported(CheckSupport(device->GetDeviceInfo(),
+                                  ToBackend(device->GetAdapter())->GetPhysicalDevice(),
+                                  device->fn)) {
+    }
 
-        // Early out before we try using extension functions
-        if (!mSupported) {
-            return;
+    Service::~Service() = default;
+
+    // static
+    bool Service::CheckSupport(const VulkanDeviceInfo& deviceInfo,
+                               VkPhysicalDevice physicalDevice,
+                               const VulkanFunctions& fn) {
+        if (!deviceInfo.HasExt(DeviceExt::ExternalSemaphoreFD)) {
+            return false;
         }
 
         VkPhysicalDeviceExternalSemaphoreInfoKHR semaphoreInfo;
@@ -44,17 +53,14 @@
         semaphoreProperties.sType = VK_STRUCTURE_TYPE_EXTERNAL_SEMAPHORE_PROPERTIES_KHR;
         semaphoreProperties.pNext = nullptr;
 
-        mDevice->fn.GetPhysicalDeviceExternalSemaphoreProperties(
-            ToBackend(mDevice->GetAdapter())->GetPhysicalDevice(), &semaphoreInfo,
-            &semaphoreProperties);
+        fn.GetPhysicalDeviceExternalSemaphoreProperties(physicalDevice, &semaphoreInfo,
+                                                        &semaphoreProperties);
 
         VkFlags requiredFlags = VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT_KHR |
                                 VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT_KHR;
-        mSupported =
-            mSupported && IsSubset(requiredFlags, semaphoreProperties.externalSemaphoreFeatures);
-    }
 
-    Service::~Service() = default;
+        return IsSubset(requiredFlags, semaphoreProperties.externalSemaphoreFeatures);
+    }
 
     bool Service::Supported() {
         return mSupported;
diff --git a/src/dawn_native/vulkan/external_semaphore/SemaphoreServiceNull.cpp b/src/dawn_native/vulkan/external_semaphore/SemaphoreServiceNull.cpp
index aca4cb1..3053029 100644
--- a/src/dawn_native/vulkan/external_semaphore/SemaphoreServiceNull.cpp
+++ b/src/dawn_native/vulkan/external_semaphore/SemaphoreServiceNull.cpp
@@ -24,6 +24,13 @@
 
     Service::~Service() = default;
 
+    // static
+    bool Service::CheckSupport(const VulkanDeviceInfo& deviceInfo,
+                               VkPhysicalDevice physicalDevice,
+                               const VulkanFunctions& fn) {
+        return false;
+    }
+
     bool Service::Supported() {
         return false;
     }
diff --git a/src/dawn_native/vulkan/external_semaphore/SemaphoreServiceZirconHandle.cpp b/src/dawn_native/vulkan/external_semaphore/SemaphoreServiceZirconHandle.cpp
index 20ffbad..656b9fe 100644
--- a/src/dawn_native/vulkan/external_semaphore/SemaphoreServiceZirconHandle.cpp
+++ b/src/dawn_native/vulkan/external_semaphore/SemaphoreServiceZirconHandle.cpp
@@ -20,12 +20,21 @@
 
 namespace dawn_native { namespace vulkan { namespace external_semaphore {
 
-    Service::Service(Device* device) : mDevice(device) {
-        mSupported = device->GetDeviceInfo().hasExt(DeviceExt::ExternalSemaphoreZirconHandle);
+    Service::Service(Device* device)
+        : mDevice(device),
+          mSupported(CheckSupport(device->GetDeviceInfo(),
+                                  ToBackend(device->GetAdapter())->GetPhysicalDevice(),
+                                  device->fn)) {
+    }
 
-        // Early out before we try using extension functions
-        if (!mSupported) {
-            return;
+    Service::~Service() = default;
+
+    // static
+    bool Service::CheckSupport(const VulkanDeviceInfo& deviceInfo,
+                               VkPhysicalDevice physicalDevice,
+                               const VulkanFunctions& fn) {
+        if (!deviceInfo.HasExt(DeviceExt::ExternalSemaphoreZirconHandle)) {
+            return false;
         }
 
         VkPhysicalDeviceExternalSemaphoreInfoKHR semaphoreInfo;
@@ -37,17 +46,14 @@
         semaphoreProperties.sType = VK_STRUCTURE_TYPE_EXTERNAL_SEMAPHORE_PROPERTIES_KHR;
         semaphoreProperties.pNext = nullptr;
 
-        mDevice->fn.GetPhysicalDeviceExternalSemaphoreProperties(
-            ToBackend(mDevice->GetAdapter())->GetPhysicalDevice(), &semaphoreInfo,
-            &semaphoreProperties);
+        fn.GetPhysicalDeviceExternalSemaphoreProperties(physicalDevice, &semaphoreInfo,
+                                                        &semaphoreProperties);
 
         VkFlags requiredFlags = VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT_KHR |
                                 VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT_KHR;
-        mSupported =
-            mSupported && IsSubset(requiredFlags, semaphoreProperties.externalSemaphoreFeatures);
-    }
 
-    Service::~Service() = default;
+        return IsSubset(requiredFlags, semaphoreProperties.externalSemaphoreFeatures);
+    }
 
     bool Service::Supported() {
         return mSupported;
diff --git a/src/include/dawn_native/DawnNative.h b/src/include/dawn_native/DawnNative.h
index dd4d82c..9baf128 100644
--- a/src/include/dawn_native/DawnNative.h
+++ b/src/include/dawn_native/DawnNative.h
@@ -109,6 +109,10 @@
         std::vector<const char*> GetSupportedExtensions() const;
         WGPUDeviceProperties GetAdapterProperties() const;
 
+        // Check that the Adapter is able to support importing external images. This is necessary
+        // to implement the swapchain and interop APIs in Chromium.
+        bool SupportsExternalImages() const;
+
         explicit operator bool() const;
 
         // Create a device on this adapter, note that the interface will change to include at least