Add ServiceImplementation class in external_memory

This CL introduced ServiceImplementation class. Service class deleate
its work to ServiceImplementation class.

Different platforms inherit ServiceImplementation class and implement
it to support Service class. It helps isolate implementaion and
interface. And it's the base for supporting choosing implementation
in runtime.

Bug: dawn:1593
Change-Id: Ib18dfaa41ef4a1ce9554a5241cac1ef0fede8dd8
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/125481
Reviewed-by: Austin Eng <enga@chromium.org>
Commit-Queue: Shaobo Yan <shaobo.yan@intel.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
diff --git a/src/dawn/native/BUILD.gn b/src/dawn/native/BUILD.gn
index bc8abb8..0a8b917 100644
--- a/src/dawn/native/BUILD.gn
+++ b/src/dawn/native/BUILD.gn
@@ -696,8 +696,11 @@
       "vulkan/VulkanFunctions.h",
       "vulkan/VulkanInfo.cpp",
       "vulkan/VulkanInfo.h",
+      "vulkan/external_memory/MemoryImportParams.h",
       "vulkan/external_memory/MemoryService.cpp",
       "vulkan/external_memory/MemoryService.h",
+      "vulkan/external_memory/MemoryServiceImplementation.cpp",
+      "vulkan/external_memory/MemoryServiceImplementation.h",
       "vulkan/external_semaphore/SemaphoreService.cpp",
       "vulkan/external_semaphore/SemaphoreService.h",
       "vulkan/external_semaphore/SemaphoreServiceImplementation.cpp",
diff --git a/src/dawn/native/CMakeLists.txt b/src/dawn/native/CMakeLists.txt
index 5136811..fe39ea4 100644
--- a/src/dawn/native/CMakeLists.txt
+++ b/src/dawn/native/CMakeLists.txt
@@ -569,8 +569,11 @@
         "vulkan/VulkanFunctions.h"
         "vulkan/VulkanInfo.cpp"
         "vulkan/VulkanInfo.h"
+        "vulkan/external_memory/MemoryImportParams.h"
         "vulkan/external_memory/MemoryService.cpp"
         "vulkan/external_memory/MemoryService.h"
+        "vulkan/external_memory/MemoryServiceImplementation.cpp"
+        "vulkan/external_memory/MemoryServiceImplementation.h"
         "vulkan/external_semaphore/SemaphoreService.cpp"
         "vulkan/external_semaphore/SemaphoreService.h"
         "vulkan/external_semaphore/SemaphoreServiceImplementation.cpp"
diff --git a/src/dawn/native/vulkan/external_memory/MemoryImportParams.h b/src/dawn/native/vulkan/external_memory/MemoryImportParams.h
new file mode 100644
index 0000000..8f9c666
--- /dev/null
+++ b/src/dawn/native/vulkan/external_memory/MemoryImportParams.h
@@ -0,0 +1,30 @@
+// Copyright 2023 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.
+
+#ifndef SRC_DAWN_NATIVE_VULKAN_EXTERNAL_MEMORY_MEMORYIMPORTPARAMS_H_
+#define SRC_DAWN_NATIVE_VULKAN_EXTERNAL_MEMORY_MEMORYIMPORTPARAMS_H_
+
+#include "dawn/native/VulkanBackend.h"
+
+namespace dawn::native::vulkan::external_memory {
+
+struct MemoryImportParams {
+    VkDeviceSize allocationSize;
+    uint32_t memoryTypeIndex;
+    bool dedicatedAllocation = false;
+};
+
+}  // namespace dawn::native::vulkan::external_memory
+
+#endif  // SRC_DAWN_NATIVE_VULKAN_EXTERNAL_MEMORY_MEMORYIMPORTPARAMS_H_
diff --git a/src/dawn/native/vulkan/external_memory/MemoryService.cpp b/src/dawn/native/vulkan/external_memory/MemoryService.cpp
index 02361e2..1f74778 100644
--- a/src/dawn/native/vulkan/external_memory/MemoryService.cpp
+++ b/src/dawn/native/vulkan/external_memory/MemoryService.cpp
@@ -13,46 +13,46 @@
 // limitations under the License.
 
 #include "dawn/native/vulkan/external_memory/MemoryService.h"
-
-#include "dawn/native/vulkan/DeviceVk.h"
+#include "dawn/native/vulkan/external_memory/MemoryServiceImplementation.h"
 
 namespace dawn::native::vulkan::external_memory {
 
-bool Service::RequiresDedicatedAllocation(const ExternalImageDescriptorVk* descriptor,
-                                          VkImage image) {
-    switch (descriptor->dedicatedAllocation) {
-        case NeedsDedicatedAllocation::Yes:
-            return true;
+Service::~Service() = default;
 
-        case NeedsDedicatedAllocation::No:
-            return false;
+bool Service::SupportsImportMemory(VkFormat format,
+                                   VkImageType type,
+                                   VkImageTiling tiling,
+                                   VkImageUsageFlags usage,
+                                   VkImageCreateFlags flags) {
+    return mImpl->SupportsImportMemory(format, type, tiling, usage, flags);
+}
 
-        case NeedsDedicatedAllocation::Detect:
-            if (!mDevice->GetDeviceInfo().HasExt(DeviceExt::DedicatedAllocation)) {
-                return false;
-            }
+bool Service::SupportsCreateImage(const ExternalImageDescriptor* descriptor,
+                                  VkFormat format,
+                                  VkImageUsageFlags usage,
+                                  bool* supportsDisjoint) {
+    return mImpl->SupportsCreateImage(descriptor, format, usage, supportsDisjoint);
+}
 
-            VkMemoryDedicatedRequirements dedicatedRequirements;
-            dedicatedRequirements.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS;
-            dedicatedRequirements.pNext = nullptr;
+ResultOrError<MemoryImportParams> Service::GetMemoryImportParams(
+    const ExternalImageDescriptor* descriptor,
+    VkImage image) {
+    return mImpl->GetMemoryImportParams(descriptor, image);
+}
 
-            VkMemoryRequirements2 baseRequirements;
-            baseRequirements.sType = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2;
-            baseRequirements.pNext = &dedicatedRequirements;
+uint32_t Service::GetQueueFamilyIndex() {
+    return mImpl->GetQueueFamilyIndex();
+}
 
-            VkImageMemoryRequirementsInfo2 imageInfo;
-            imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2;
-            imageInfo.pNext = nullptr;
-            imageInfo.image = image;
+ResultOrError<VkDeviceMemory> Service::ImportMemory(ExternalMemoryHandle handle,
+                                                    const MemoryImportParams& importParams,
+                                                    VkImage image) {
+    return mImpl->ImportMemory(handle, importParams, image);
+}
 
-            mDevice->fn.GetImageMemoryRequirements2(mDevice->GetVkDevice(), &imageInfo,
-                                                    &baseRequirements);
-
-            // The Vulkan spec requires that prefersDA is set if requiresDA is, so we can just check
-            // for prefersDA.
-            return dedicatedRequirements.prefersDedicatedAllocation;
-    }
-    DAWN_UNREACHABLE();
+ResultOrError<VkImage> Service::CreateImage(const ExternalImageDescriptor* descriptor,
+                                            const VkImageCreateInfo& baseCreateInfo) {
+    return mImpl->CreateImage(descriptor, baseCreateInfo);
 }
 
 }  // namespace dawn::native::vulkan::external_memory
diff --git a/src/dawn/native/vulkan/external_memory/MemoryService.h b/src/dawn/native/vulkan/external_memory/MemoryService.h
index 0800bf2..adafd40 100644
--- a/src/dawn/native/vulkan/external_memory/MemoryService.h
+++ b/src/dawn/native/vulkan/external_memory/MemoryService.h
@@ -15,10 +15,11 @@
 #ifndef SRC_DAWN_NATIVE_VULKAN_EXTERNAL_MEMORY_MEMORYSERVICE_H_
 #define SRC_DAWN_NATIVE_VULKAN_EXTERNAL_MEMORY_MEMORYSERVICE_H_
 
-#include "dawn/common/vulkan_platform.h"
+#include <memory>
+
 #include "dawn/native/Error.h"
-#include "dawn/native/VulkanBackend.h"
 #include "dawn/native/vulkan/ExternalHandle.h"
+#include "dawn/native/vulkan/external_memory/MemoryImportParams.h"
 
 namespace dawn::native::vulkan {
 class Device;
@@ -27,11 +28,7 @@
 
 namespace dawn::native::vulkan::external_memory {
 
-struct MemoryImportParams {
-    VkDeviceSize allocationSize;
-    uint32_t memoryTypeIndex;
-    bool dedicatedAllocation = false;
-};
+class ServiceImplementation;
 
 class Service {
   public:
@@ -71,12 +68,7 @@
                                        const VkImageCreateInfo& baseCreateInfo);
 
   private:
-    bool RequiresDedicatedAllocation(const ExternalImageDescriptorVk* descriptor, VkImage image);
-
-    Device* mDevice = nullptr;
-
-    // True if early checks pass that determine if the service is supported
-    bool mSupported = false;
+    std::unique_ptr<ServiceImplementation> mImpl;
 };
 
 }  // namespace dawn::native::vulkan::external_memory
diff --git a/src/dawn/native/vulkan/external_memory/MemoryServiceImplementation.cpp b/src/dawn/native/vulkan/external_memory/MemoryServiceImplementation.cpp
new file mode 100644
index 0000000..ec907f5
--- /dev/null
+++ b/src/dawn/native/vulkan/external_memory/MemoryServiceImplementation.cpp
@@ -0,0 +1,60 @@
+// Copyright 2023 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 "dawn/native/vulkan/external_memory/MemoryServiceImplementation.h"
+#include "dawn/native/vulkan/DeviceVk.h"
+
+namespace dawn::native::vulkan::external_memory {
+
+ServiceImplementation::ServiceImplementation(Device* device) : mDevice(device) {}
+ServiceImplementation::~ServiceImplementation() = default;
+
+bool ServiceImplementation::RequiresDedicatedAllocation(const ExternalImageDescriptorVk* descriptor,
+                                                        VkImage image) const {
+    switch (descriptor->dedicatedAllocation) {
+        case NeedsDedicatedAllocation::Yes:
+            return true;
+
+        case NeedsDedicatedAllocation::No:
+            return false;
+
+        case NeedsDedicatedAllocation::Detect:
+            if (!mDevice->GetDeviceInfo().HasExt(DeviceExt::DedicatedAllocation)) {
+                return false;
+            }
+
+            VkMemoryDedicatedRequirements dedicatedRequirements;
+            dedicatedRequirements.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS;
+            dedicatedRequirements.pNext = nullptr;
+
+            VkMemoryRequirements2 baseRequirements;
+            baseRequirements.sType = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2;
+            baseRequirements.pNext = &dedicatedRequirements;
+
+            VkImageMemoryRequirementsInfo2 imageInfo;
+            imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2;
+            imageInfo.pNext = nullptr;
+            imageInfo.image = image;
+
+            mDevice->fn.GetImageMemoryRequirements2(mDevice->GetVkDevice(), &imageInfo,
+                                                    &baseRequirements);
+
+            // The Vulkan spec requires that prefersDA is set if requiresDA is, so we can just check
+            // for prefersDA.
+            return dedicatedRequirements.prefersDedicatedAllocation;
+    }
+    DAWN_UNREACHABLE();
+}
+
+}  // namespace dawn::native::vulkan::external_memory
diff --git a/src/dawn/native/vulkan/external_memory/MemoryServiceImplementation.h b/src/dawn/native/vulkan/external_memory/MemoryServiceImplementation.h
new file mode 100644
index 0000000..e129c2f
--- /dev/null
+++ b/src/dawn/native/vulkan/external_memory/MemoryServiceImplementation.h
@@ -0,0 +1,78 @@
+// Copyright 2023 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.
+
+#ifndef SRC_DAWN_NATIVE_VULKAN_EXTERNAL_MEMORY_SERVICEIMPLEMENTATION_H_
+#define SRC_DAWN_NATIVE_VULKAN_EXTERNAL_MEMORY_SERVICEIMPLEMENTATION_H_
+
+#include "dawn/common/vulkan_platform.h"
+#include "dawn/native/Error.h"
+#include "dawn/native/VulkanBackend.h"
+#include "dawn/native/vulkan/ExternalHandle.h"
+#include "dawn/native/vulkan/external_memory/MemoryImportParams.h"
+
+namespace dawn::native::vulkan {
+class Device;
+struct VulkanDeviceInfo;
+}  // namespace dawn::native::vulkan
+
+namespace dawn::native::vulkan::external_memory {
+
+class ServiceImplementation {
+  public:
+    explicit ServiceImplementation(Device* device);
+    virtual ~ServiceImplementation();
+
+    // True if the device reports it supports importing external memory.
+    virtual bool SupportsImportMemory(VkFormat format,
+                                      VkImageType type,
+                                      VkImageTiling tiling,
+                                      VkImageUsageFlags usage,
+                                      VkImageCreateFlags flags) = 0;
+
+    // True if the device reports it supports creating VkImages from external memory.
+    virtual bool SupportsCreateImage(const ExternalImageDescriptor* descriptor,
+                                     VkFormat format,
+                                     VkImageUsageFlags usage,
+                                     bool* supportsDisjoint) = 0;
+
+    // Returns the parameters required for importing memory
+    virtual ResultOrError<MemoryImportParams> GetMemoryImportParams(
+        const ExternalImageDescriptor* descriptor,
+        VkImage image) = 0;
+
+    // Returns the index of the queue memory from this services should be exported with.
+    virtual uint32_t GetQueueFamilyIndex() = 0;
+
+    // Given an external handle pointing to memory, import it into a VkDeviceMemory
+    virtual ResultOrError<VkDeviceMemory> ImportMemory(ExternalMemoryHandle handle,
+                                                       const MemoryImportParams& importParams,
+                                                       VkImage image) = 0;
+
+    // Create a VkImage for the given handle type
+    virtual ResultOrError<VkImage> CreateImage(const ExternalImageDescriptor* descriptor,
+                                               const VkImageCreateInfo& baseCreateInfo) = 0;
+
+    // True if the device reports it supports this feature
+    virtual bool Supported() const = 0;
+
+  protected:
+    bool RequiresDedicatedAllocation(const ExternalImageDescriptorVk* descriptor,
+                                     VkImage image) const;
+
+    Device* mDevice = nullptr;
+};
+
+}  // namespace dawn::native::vulkan::external_memory
+
+#endif  // SRC_DAWN_NATIVE_VULKAN_EXTERNAL_MEMORY_SERVICEIMPLEMENTATION_H_
diff --git a/src/dawn/native/vulkan/external_memory/MemoryServiceImplementationAHardwareBuffer.cpp b/src/dawn/native/vulkan/external_memory/MemoryServiceImplementationAHardwareBuffer.cpp
index 2031365..2ecd4ee 100644
--- a/src/dawn/native/vulkan/external_memory/MemoryServiceImplementationAHardwareBuffer.cpp
+++ b/src/dawn/native/vulkan/external_memory/MemoryServiceImplementationAHardwareBuffer.cpp
@@ -12,6 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include <memory>
+
 #include "dawn/common/Assert.h"
 #include "dawn/native/vulkan/AdapterVk.h"
 #include "dawn/native/vulkan/BackendVk.h"
@@ -20,178 +22,194 @@
 #include "dawn/native/vulkan/UtilsVulkan.h"
 #include "dawn/native/vulkan/VulkanError.h"
 #include "dawn/native/vulkan/external_memory/MemoryService.h"
+#include "dawn/native/vulkan/external_memory/MemoryServiceImplementation.h"
 
 namespace dawn::native::vulkan::external_memory {
 
-Service::Service(Device* device)
-    : mDevice(device), mSupported(CheckSupport(device->GetDeviceInfo())) {}
+class ServiceImplementationAHardwareBuffer : public ServiceImplementation {
+  public:
+    explicit ServiceImplementationAHardwareBuffer(Device* device)
+        : ServiceImplementation(device), mSupported(CheckSupport(device->GetDeviceInfo())) {}
+    ~ServiceImplementationAHardwareBuffer() override = default;
 
-Service::~Service() = default;
+    static bool CheckSupport(const VulkanDeviceInfo& deviceInfo) {
+        return deviceInfo.HasExt(DeviceExt::ExternalMemoryAndroidHardwareBuffer);
+    }
+
+    bool SupportsImportMemory(VkFormat format,
+                              VkImageType type,
+                              VkImageTiling tiling,
+                              VkImageUsageFlags usage,
+                              VkImageCreateFlags flags) override {
+        // Early out before we try using extension functions
+        if (!mSupported) {
+            return false;
+        }
+
+        VkPhysicalDeviceImageFormatInfo2 formatInfo = {
+            .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2_KHR,
+            .pNext = nullptr,
+            .format = format,
+            .type = type,
+            .tiling = tiling,
+            .usage = usage,
+            .flags = flags,
+        };
+
+        PNextChainBuilder formatInfoChain(&formatInfo);
+
+        VkPhysicalDeviceExternalImageFormatInfo externalFormatInfo = {
+            .handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID,
+        };
+        formatInfoChain.Add(&externalFormatInfo,
+                            VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO_KHR);
+
+        VkImageFormatProperties2 formatProperties = {
+            .sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2_KHR,
+            .pNext = nullptr,
+        };
+
+        PNextChainBuilder formatPropertiesChain(&formatProperties);
+
+        VkExternalImageFormatProperties externalFormatProperties;
+        formatPropertiesChain.Add(&externalFormatProperties,
+                                  VK_STRUCTURE_TYPE_EXTERNAL_IMAGE_FORMAT_PROPERTIES_KHR);
+
+        VkResult result = VkResult::WrapUnsafe(mDevice->fn.GetPhysicalDeviceImageFormatProperties2(
+            ToBackend(mDevice->GetAdapter())->GetPhysicalDevice(), &formatInfo, &formatProperties));
+
+        // If handle not supported, result == VK_ERROR_FORMAT_NOT_SUPPORTED
+        if (result != VK_SUCCESS) {
+            return false;
+        }
+
+        // TODO(http://crbug.com/dawn/206): Investigate dedicated only images
+        VkFlags memoryFlags =
+            externalFormatProperties.externalMemoryProperties.externalMemoryFeatures;
+        return (memoryFlags & VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT_KHR) != 0;
+    }
+
+    bool SupportsCreateImage(const ExternalImageDescriptor* descriptor,
+                             VkFormat format,
+                             VkImageUsageFlags usage,
+                             bool* supportsDisjoint) override {
+        *supportsDisjoint = false;
+        return mSupported;
+    }
+
+    ResultOrError<MemoryImportParams> GetMemoryImportParams(
+        const ExternalImageDescriptor* descriptor,
+        VkImage image) override {
+        DAWN_INVALID_IF(descriptor->GetType() != ExternalImageType::AHardwareBuffer,
+                        "ExternalImageDescriptor is not an AHardwareBuffer descriptor.");
+
+        const ExternalImageDescriptorAHardwareBuffer* aHardwareBufferDescriptor =
+            static_cast<const ExternalImageDescriptorAHardwareBuffer*>(descriptor);
+
+        VkAndroidHardwareBufferPropertiesANDROID bufferProperties = {
+            .sType = VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_PROPERTIES_ANDROID,
+            .pNext = nullptr,
+        };
+
+        PNextChainBuilder bufferPropertiesChain(&bufferProperties);
+
+        VkAndroidHardwareBufferFormatPropertiesANDROID bufferFormatProperties;
+        bufferPropertiesChain.Add(
+            &bufferFormatProperties,
+            VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_FORMAT_PROPERTIES_ANDROID);
+
+        DAWN_TRY(CheckVkSuccess(
+            mDevice->fn.GetAndroidHardwareBufferPropertiesANDROID(
+                mDevice->GetVkDevice(), aHardwareBufferDescriptor->handle, &bufferProperties),
+            "vkGetAndroidHardwareBufferPropertiesANDROID"));
+
+        MemoryImportParams params;
+        params.allocationSize = bufferProperties.allocationSize;
+        params.memoryTypeIndex = bufferProperties.memoryTypeBits;
+        params.dedicatedAllocation = RequiresDedicatedAllocation(aHardwareBufferDescriptor, image);
+        return params;
+    }
+
+    uint32_t GetQueueFamilyIndex() override { return VK_QUEUE_FAMILY_FOREIGN_EXT; }
+
+    ResultOrError<VkDeviceMemory> ImportMemory(ExternalMemoryHandle handle,
+                                               const MemoryImportParams& importParams,
+                                               VkImage image) override {
+        DAWN_INVALID_IF(handle == nullptr, "Importing memory with an invalid handle.");
+
+        VkMemoryRequirements requirements;
+        mDevice->fn.GetImageMemoryRequirements(mDevice->GetVkDevice(), image, &requirements);
+        DAWN_INVALID_IF(requirements.size > importParams.allocationSize,
+                        "Requested allocation size (%u) is smaller than the image requires (%u).",
+                        importParams.allocationSize, requirements.size);
+
+        VkMemoryAllocateInfo allocateInfo = {
+            .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
+            .pNext = nullptr,
+            .allocationSize = importParams.allocationSize,
+            .memoryTypeIndex = importParams.memoryTypeIndex,
+        };
+
+        PNextChainBuilder allocateInfoChain(&allocateInfo);
+
+        VkImportAndroidHardwareBufferInfoANDROID importMemoryAHBInfo = {
+            .buffer = handle,
+        };
+        allocateInfoChain.Add(&importMemoryAHBInfo,
+                              VK_STRUCTURE_TYPE_IMPORT_ANDROID_HARDWARE_BUFFER_INFO_ANDROID);
+
+        VkMemoryDedicatedAllocateInfo dedicatedAllocateInfo;
+        if (importParams.dedicatedAllocation) {
+            dedicatedAllocateInfo.image = image;
+            dedicatedAllocateInfo.buffer = VkBuffer{};
+            allocateInfoChain.Add(&dedicatedAllocateInfo,
+                                  VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO);
+        }
+
+        VkDeviceMemory allocatedMemory = VK_NULL_HANDLE;
+        DAWN_TRY(CheckVkSuccess(mDevice->fn.AllocateMemory(mDevice->GetVkDevice(), &allocateInfo,
+                                                           nullptr, &*allocatedMemory),
+                                "vkAllocateMemory"));
+        return allocatedMemory;
+    }
+
+    ResultOrError<VkImage> CreateImage(const ExternalImageDescriptor* descriptor,
+                                       const VkImageCreateInfo& baseCreateInfo) override {
+        VkImageCreateInfo createInfo = baseCreateInfo;
+        createInfo.flags |= VK_IMAGE_CREATE_ALIAS_BIT_KHR;
+        createInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
+        createInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+
+        PNextChainBuilder createInfoChain(&createInfo);
+
+        VkExternalMemoryImageCreateInfo externalMemoryImageCreateInfo = {
+            .handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID,
+        };
+        createInfoChain.Add(&externalMemoryImageCreateInfo,
+                            VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO);
+
+        ASSERT(IsSampleCountSupported(mDevice, createInfo));
+
+        VkImage image;
+        DAWN_TRY(CheckVkSuccess(
+            mDevice->fn.CreateImage(mDevice->GetVkDevice(), &createInfo, nullptr, &*image),
+            "CreateImage"));
+        return image;
+    }
+
+    bool Supported() const override { return mSupported; }
+
+  private:
+    bool mSupported = false;
+};
+
+Service::Service(Device* device) {
+    mImpl = std::make_unique<ServiceImplementationAHardwareBuffer>(device);
+}
 
 // static
 bool Service::CheckSupport(const VulkanDeviceInfo& deviceInfo) {
     return deviceInfo.HasExt(DeviceExt::ExternalMemoryAndroidHardwareBuffer);
 }
 
-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;
-    }
-
-    VkPhysicalDeviceImageFormatInfo2 formatInfo = {
-        .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2_KHR,
-        .pNext = nullptr,
-        .format = format,
-        .type = type,
-        .tiling = tiling,
-        .usage = usage,
-        .flags = flags,
-    };
-
-    PNextChainBuilder formatInfoChain(&formatInfo);
-
-    VkPhysicalDeviceExternalImageFormatInfo externalFormatInfo = {
-        .handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID,
-    };
-    formatInfoChain.Add(&externalFormatInfo,
-                        VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO_KHR);
-
-    VkImageFormatProperties2 formatProperties = {
-        .sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2_KHR,
-        .pNext = nullptr,
-    };
-
-    PNextChainBuilder formatPropertiesChain(&formatProperties);
-
-    VkExternalImageFormatProperties externalFormatProperties;
-    formatPropertiesChain.Add(&externalFormatProperties,
-                              VK_STRUCTURE_TYPE_EXTERNAL_IMAGE_FORMAT_PROPERTIES_KHR);
-
-    VkResult result = VkResult::WrapUnsafe(mDevice->fn.GetPhysicalDeviceImageFormatProperties2(
-        ToBackend(mDevice->GetAdapter())->GetPhysicalDevice(), &formatInfo, &formatProperties));
-
-    // If handle not supported, result == VK_ERROR_FORMAT_NOT_SUPPORTED
-    if (result != VK_SUCCESS) {
-        return false;
-    }
-
-    // TODO(http://crbug.com/dawn/206): Investigate dedicated only images
-    VkFlags memoryFlags = externalFormatProperties.externalMemoryProperties.externalMemoryFeatures;
-    return (memoryFlags & VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT_KHR) != 0;
-}
-
-bool Service::SupportsCreateImage(const ExternalImageDescriptor* descriptor,
-                                  VkFormat format,
-                                  VkImageUsageFlags usage,
-                                  bool* supportsDisjoint) {
-    *supportsDisjoint = false;
-    return mSupported;
-}
-
-ResultOrError<MemoryImportParams> Service::GetMemoryImportParams(
-    const ExternalImageDescriptor* descriptor,
-    VkImage image) {
-    DAWN_INVALID_IF(descriptor->GetType() != ExternalImageType::AHardwareBuffer,
-                    "ExternalImageDescriptor is not an AHardwareBuffer descriptor.");
-
-    const ExternalImageDescriptorAHardwareBuffer* aHardwareBufferDescriptor =
-        static_cast<const ExternalImageDescriptorAHardwareBuffer*>(descriptor);
-
-    VkAndroidHardwareBufferPropertiesANDROID bufferProperties = {
-        .sType = VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_PROPERTIES_ANDROID,
-        .pNext = nullptr,
-    };
-
-    PNextChainBuilder bufferPropertiesChain(&bufferProperties);
-
-    VkAndroidHardwareBufferFormatPropertiesANDROID bufferFormatProperties;
-    bufferPropertiesChain.Add(&bufferFormatProperties,
-                              VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_FORMAT_PROPERTIES_ANDROID);
-
-    DAWN_TRY(CheckVkSuccess(
-        mDevice->fn.GetAndroidHardwareBufferPropertiesANDROID(
-            mDevice->GetVkDevice(), aHardwareBufferDescriptor->handle, &bufferProperties),
-        "vkGetAndroidHardwareBufferPropertiesANDROID"));
-
-    MemoryImportParams params;
-    params.allocationSize = bufferProperties.allocationSize;
-    params.memoryTypeIndex = bufferProperties.memoryTypeBits;
-    params.dedicatedAllocation = RequiresDedicatedAllocation(aHardwareBufferDescriptor, image);
-    return params;
-}
-
-uint32_t Service::GetQueueFamilyIndex() {
-    return VK_QUEUE_FAMILY_FOREIGN_EXT;
-}
-
-ResultOrError<VkDeviceMemory> Service::ImportMemory(ExternalMemoryHandle handle,
-                                                    const MemoryImportParams& importParams,
-                                                    VkImage image) {
-    DAWN_INVALID_IF(handle == nullptr, "Importing memory with an invalid handle.");
-
-    VkMemoryRequirements requirements;
-    mDevice->fn.GetImageMemoryRequirements(mDevice->GetVkDevice(), image, &requirements);
-    DAWN_INVALID_IF(requirements.size > importParams.allocationSize,
-                    "Requested allocation size (%u) is smaller than the image requires (%u).",
-                    importParams.allocationSize, requirements.size);
-
-    VkMemoryAllocateInfo allocateInfo = {
-        .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
-        .pNext = nullptr,
-        .allocationSize = importParams.allocationSize,
-        .memoryTypeIndex = importParams.memoryTypeIndex,
-    };
-
-    PNextChainBuilder allocateInfoChain(&allocateInfo);
-
-    VkImportAndroidHardwareBufferInfoANDROID importMemoryAHBInfo = {
-        .buffer = handle,
-    };
-    allocateInfoChain.Add(&importMemoryAHBInfo,
-                          VK_STRUCTURE_TYPE_IMPORT_ANDROID_HARDWARE_BUFFER_INFO_ANDROID);
-
-    VkMemoryDedicatedAllocateInfo dedicatedAllocateInfo;
-    if (importParams.dedicatedAllocation) {
-        dedicatedAllocateInfo.image = image;
-        dedicatedAllocateInfo.buffer = VkBuffer{};
-        allocateInfoChain.Add(&dedicatedAllocateInfo,
-                              VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO);
-    }
-
-    VkDeviceMemory allocatedMemory = VK_NULL_HANDLE;
-    DAWN_TRY(CheckVkSuccess(mDevice->fn.AllocateMemory(mDevice->GetVkDevice(), &allocateInfo,
-                                                       nullptr, &*allocatedMemory),
-                            "vkAllocateMemory"));
-    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;
-
-    PNextChainBuilder createInfoChain(&createInfo);
-
-    VkExternalMemoryImageCreateInfo externalMemoryImageCreateInfo = {
-        .handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID,
-    };
-    createInfoChain.Add(&externalMemoryImageCreateInfo,
-                        VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO);
-
-    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/MemoryServiceImplementationDmaBuf.cpp b/src/dawn/native/vulkan/external_memory/MemoryServiceImplementationDmaBuf.cpp
index fc6a1e3..f461060 100644
--- a/src/dawn/native/vulkan/external_memory/MemoryServiceImplementationDmaBuf.cpp
+++ b/src/dawn/native/vulkan/external_memory/MemoryServiceImplementationDmaBuf.cpp
@@ -12,6 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include <memory>
 #include <vector>
 
 #include "dawn/common/Assert.h"
@@ -22,6 +23,7 @@
 #include "dawn/native/vulkan/UtilsVulkan.h"
 #include "dawn/native/vulkan/VulkanError.h"
 #include "dawn/native/vulkan/external_memory/MemoryService.h"
+#include "dawn/native/vulkan/external_memory/MemoryServiceImplementation.h"
 
 namespace dawn::native::vulkan::external_memory {
 
@@ -115,10 +117,250 @@
 
 }  // namespace
 
-Service::Service(Device* device)
-    : mDevice(device), mSupported(CheckSupport(device->GetDeviceInfo())) {}
+class ServiceImplementationDmaBuf : public ServiceImplementation {
+  public:
+    explicit ServiceImplementationDmaBuf(Device* device)
+        : ServiceImplementation(device), mSupported(CheckSupport(device->GetDeviceInfo())) {}
+    ~ServiceImplementationDmaBuf() override = default;
 
-Service::~Service() = default;
+    static bool CheckSupport(const VulkanDeviceInfo& deviceInfo) {
+        return deviceInfo.HasExt(DeviceExt::ExternalMemoryFD) &&
+               deviceInfo.HasExt(DeviceExt::ImageDrmFormatModifier);
+    }
+
+    bool SupportsImportMemory(VkFormat format,
+                              VkImageType type,
+                              VkImageTiling tiling,
+                              VkImageUsageFlags usage,
+                              VkImageCreateFlags flags) override {
+        return mSupported && (!IsMultiPlanarVkFormat(format) ||
+                              (format == VK_FORMAT_G8_B8R8_2PLANE_420_UNORM &&
+                               mDevice->GetDeviceInfo().HasExt(DeviceExt::ImageFormatList)));
+    }
+
+    bool SupportsCreateImage(const ExternalImageDescriptor* descriptor,
+                             VkFormat format,
+                             VkImageUsageFlags usage,
+                             bool* supportsDisjoint) override {
+        *supportsDisjoint = false;
+        // Early out before we try using extension functions
+        if (!mSupported) {
+            return false;
+        }
+        if (descriptor->GetType() != ExternalImageType::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;
+        }
+        // Only support the NV12 multi-planar format for now.
+        if (planeCount > 1 && format != VK_FORMAT_G8_B8R8_2PLANE_420_UNORM) {
+            return false;
+        }
+        *supportsDisjoint =
+            SupportsDisjoint(mDevice->fn, physicalDevice, format, dmaBufDescriptor->drmModifier);
+
+        // Verify that the format modifier of the external memory and the requested Vulkan format
+        // are actually supported together in a dma-buf import.
+        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;
+        PNextChainBuilder imageFormatInfoChain(&imageFormatInfo);
+
+        VkPhysicalDeviceExternalImageFormatInfo externalImageFormatInfo = {};
+        externalImageFormatInfo.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT;
+        imageFormatInfoChain.Add(&externalImageFormatInfo,
+                                 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO);
+
+        VkPhysicalDeviceImageDrmFormatModifierInfoEXT drmModifierInfo = {};
+        drmModifierInfo.drmFormatModifier = dmaBufDescriptor->drmModifier;
+        drmModifierInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
+        imageFormatInfoChain.Add(
+            &drmModifierInfo, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_DRM_FORMAT_MODIFIER_INFO_EXT);
+
+        // For mutable vkimage of multi-planar format, we also need to make sure the each
+        // plane's view format can be supported.
+        std::array<VkFormat, 2> viewFormats;
+        VkImageFormatListCreateInfo imageFormatListInfo = {};
+
+        if (planeCount > 1) {
+            ASSERT(format == VK_FORMAT_G8_B8R8_2PLANE_420_UNORM);
+            viewFormats = {VK_FORMAT_R8_UNORM, VK_FORMAT_R8G8_UNORM};
+            imageFormatListInfo.viewFormatCount = 2;
+            imageFormatListInfo.pViewFormats = viewFormats.data();
+            imageFormatInfo.flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT;
+            imageFormatInfoChain.Add(&imageFormatListInfo,
+                                     VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO);
+        }
+
+        VkImageFormatProperties2 imageFormatProps = {};
+        imageFormatProps.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2;
+        PNextChainBuilder imageFormatPropsChain(&imageFormatProps);
+
+        VkExternalImageFormatProperties externalImageFormatProps = {};
+        imageFormatPropsChain.Add(&externalImageFormatProps,
+                                  VK_STRUCTURE_TYPE_EXTERNAL_IMAGE_FORMAT_PROPERTIES);
+
+        VkResult result = VkResult::WrapUnsafe(mDevice->fn.GetPhysicalDeviceImageFormatProperties2(
+            physicalDevice, &imageFormatInfo, &imageFormatProps));
+        if (result != VK_SUCCESS) {
+            return false;
+        }
+        VkExternalMemoryFeatureFlags featureFlags =
+            externalImageFormatProps.externalMemoryProperties.externalMemoryFeatures;
+        return featureFlags & VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT;
+    }
+
+    ResultOrError<MemoryImportParams> GetMemoryImportParams(
+        const ExternalImageDescriptor* descriptor,
+        VkImage image) override {
+        DAWN_INVALID_IF(descriptor->GetType() != ExternalImageType::DmaBuf,
+                        "ExternalImageDescriptor is not a ExternalImageDescriptorDmaBuf.");
+
+        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->GetResourceMemoryAllocator()->FindBestTypeIndex(
+            memoryRequirements, MemoryKind::Opaque);
+        DAWN_INVALID_IF(memoryTypeIndex == -1,
+                        "Unable to find an appropriate memory type for import.");
+
+        MemoryImportParams params;
+        params.allocationSize = memoryRequirements.size;
+        params.memoryTypeIndex = static_cast<uint32_t>(memoryTypeIndex);
+        params.dedicatedAllocation = RequiresDedicatedAllocation(dmaBufDescriptor, image);
+        return params;
+    }
+
+    uint32_t GetQueueFamilyIndex() override { return VK_QUEUE_FAMILY_EXTERNAL_KHR; }
+
+    ResultOrError<VkDeviceMemory> ImportMemory(ExternalMemoryHandle handle,
+                                               const MemoryImportParams& importParams,
+                                               VkImage image) override {
+        DAWN_INVALID_IF(handle < 0, "Importing memory with an invalid handle.");
+
+        VkMemoryAllocateInfo memoryAllocateInfo = {};
+        memoryAllocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
+        memoryAllocateInfo.allocationSize = importParams.allocationSize;
+        memoryAllocateInfo.memoryTypeIndex = importParams.memoryTypeIndex;
+        PNextChainBuilder memoryAllocateInfoChain(&memoryAllocateInfo);
+
+        VkImportMemoryFdInfoKHR importMemoryFdInfo;
+        importMemoryFdInfo.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT,
+        importMemoryFdInfo.fd = handle;
+        memoryAllocateInfoChain.Add(&importMemoryFdInfo,
+                                    VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHR);
+
+        VkMemoryDedicatedAllocateInfo memoryDedicatedAllocateInfo;
+        if (importParams.dedicatedAllocation) {
+            memoryDedicatedAllocateInfo.image = image;
+            memoryDedicatedAllocateInfo.buffer = VkBuffer{};
+            memoryAllocateInfoChain.Add(&memoryDedicatedAllocateInfo,
+                                        VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO);
+        }
+
+        VkDeviceMemory allocatedMemory = VK_NULL_HANDLE;
+        DAWN_TRY(
+            CheckVkSuccess(mDevice->fn.AllocateMemory(mDevice->GetVkDevice(), &memoryAllocateInfo,
+                                                      nullptr, &*allocatedMemory),
+                           "vkAllocateMemory"));
+        return allocatedMemory;
+    }
+
+    ResultOrError<VkImage> CreateImage(const ExternalImageDescriptor* descriptor,
+                                       const VkImageCreateInfo& baseCreateInfo) override {
+        DAWN_INVALID_IF(descriptor->GetType() != ExternalImageType::DmaBuf,
+                        "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();
+
+        uint32_t planeCount;
+        DAWN_TRY_ASSIGN(planeCount,
+                        GetModifierPlaneCount(mDevice->fn, physicalDevice, baseCreateInfo.format,
+                                              dmaBufDescriptor->drmModifier));
+
+        VkImageCreateInfo createInfo = baseCreateInfo;
+        createInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+        createInfo.tiling = VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT;
+
+        PNextChainBuilder createInfoChain(&createInfo);
+
+        VkExternalMemoryImageCreateInfo externalMemoryImageCreateInfo = {};
+        externalMemoryImageCreateInfo.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT;
+        createInfoChain.Add(&externalMemoryImageCreateInfo,
+                            VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO);
+
+        VkSubresourceLayout planeLayouts[ExternalImageDescriptorDmaBuf::kMaxPlanes];
+        for (uint32_t plane = 0u; plane < planeCount; ++plane) {
+            planeLayouts[plane].offset = dmaBufDescriptor->planeLayouts[plane].offset;
+            planeLayouts[plane].size = 0;  // VK_EXT_image_drm_format_modifier mandates size = 0.
+            planeLayouts[plane].rowPitch = dmaBufDescriptor->planeLayouts[plane].stride;
+            planeLayouts[plane].arrayPitch = 0;  // Not an array texture
+            planeLayouts[plane].depthPitch = 0;  // Not a depth texture
+        }
+
+        VkImageDrmFormatModifierExplicitCreateInfoEXT explicitCreateInfo = {};
+        explicitCreateInfo.drmFormatModifier = dmaBufDescriptor->drmModifier;
+        explicitCreateInfo.drmFormatModifierPlaneCount = planeCount;
+        explicitCreateInfo.pPlaneLayouts = &planeLayouts[0];
+
+        if (planeCount > 1) {
+            // For multi-planar formats, VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT specifies that a
+            // VkImageView can be plane's format which might differ from the image's format.
+            createInfo.flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT;
+        }
+        createInfoChain.Add(&explicitCreateInfo,
+                            VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_EXPLICIT_CREATE_INFO_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;
+    }
+
+    bool Supported() const override { return mSupported; }
+
+  private:
+    bool mSupported = false;
+};
+
+Service::Service(Device* device) {
+    mImpl = std::make_unique<ServiceImplementationDmaBuf>(device);
+}
 
 // static
 bool Service::CheckSupport(const VulkanDeviceInfo& deviceInfo) {
@@ -126,227 +368,4 @@
            deviceInfo.HasExt(DeviceExt::ImageDrmFormatModifier);
 }
 
-bool Service::SupportsImportMemory(VkFormat format,
-                                   VkImageType type,
-                                   VkImageTiling tiling,
-                                   VkImageUsageFlags usage,
-                                   VkImageCreateFlags flags) {
-    return mSupported && (!IsMultiPlanarVkFormat(format) ||
-                          (format == VK_FORMAT_G8_B8R8_2PLANE_420_UNORM &&
-                           mDevice->GetDeviceInfo().HasExt(DeviceExt::ImageFormatList)));
-}
-
-bool Service::SupportsCreateImage(const ExternalImageDescriptor* descriptor,
-                                  VkFormat format,
-                                  VkImageUsageFlags usage,
-                                  bool* supportsDisjoint) {
-    *supportsDisjoint = false;
-    // Early out before we try using extension functions
-    if (!mSupported) {
-        return false;
-    }
-    if (descriptor->GetType() != ExternalImageType::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;
-    }
-    // Only support the NV12 multi-planar format for now.
-    if (planeCount > 1 && format != VK_FORMAT_G8_B8R8_2PLANE_420_UNORM) {
-        return false;
-    }
-    *supportsDisjoint =
-        SupportsDisjoint(mDevice->fn, physicalDevice, format, dmaBufDescriptor->drmModifier);
-
-    // Verify that the format modifier of the external memory and the requested Vulkan format
-    // are actually supported together in a dma-buf import.
-    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;
-    PNextChainBuilder imageFormatInfoChain(&imageFormatInfo);
-
-    VkPhysicalDeviceExternalImageFormatInfo externalImageFormatInfo = {};
-    externalImageFormatInfo.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT;
-    imageFormatInfoChain.Add(&externalImageFormatInfo,
-                             VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO);
-
-    VkPhysicalDeviceImageDrmFormatModifierInfoEXT drmModifierInfo = {};
-    drmModifierInfo.drmFormatModifier = dmaBufDescriptor->drmModifier;
-    drmModifierInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
-    imageFormatInfoChain.Add(&drmModifierInfo,
-                             VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_DRM_FORMAT_MODIFIER_INFO_EXT);
-
-    // For mutable vkimage of multi-planar format, we also need to make sure the each
-    // plane's view format can be supported.
-    std::array<VkFormat, 2> viewFormats;
-    VkImageFormatListCreateInfo imageFormatListInfo = {};
-
-    if (planeCount > 1) {
-        ASSERT(format == VK_FORMAT_G8_B8R8_2PLANE_420_UNORM);
-        viewFormats = {VK_FORMAT_R8_UNORM, VK_FORMAT_R8G8_UNORM};
-        imageFormatListInfo.viewFormatCount = 2;
-        imageFormatListInfo.pViewFormats = viewFormats.data();
-        imageFormatInfo.flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT;
-        imageFormatInfoChain.Add(&imageFormatListInfo,
-                                 VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO);
-    }
-
-    VkImageFormatProperties2 imageFormatProps = {};
-    imageFormatProps.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2;
-    PNextChainBuilder imageFormatPropsChain(&imageFormatProps);
-
-    VkExternalImageFormatProperties externalImageFormatProps = {};
-    imageFormatPropsChain.Add(&externalImageFormatProps,
-                              VK_STRUCTURE_TYPE_EXTERNAL_IMAGE_FORMAT_PROPERTIES);
-
-    VkResult result = VkResult::WrapUnsafe(mDevice->fn.GetPhysicalDeviceImageFormatProperties2(
-        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) {
-    DAWN_INVALID_IF(descriptor->GetType() != ExternalImageType::DmaBuf,
-                    "ExternalImageDescriptor is not a ExternalImageDescriptorDmaBuf.");
-
-    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->GetResourceMemoryAllocator()->FindBestTypeIndex(
-        memoryRequirements, MemoryKind::Opaque);
-    DAWN_INVALID_IF(memoryTypeIndex == -1, "Unable to find an appropriate memory type for import.");
-
-    MemoryImportParams params;
-    params.allocationSize = memoryRequirements.size;
-    params.memoryTypeIndex = static_cast<uint32_t>(memoryTypeIndex);
-    params.dedicatedAllocation = RequiresDedicatedAllocation(dmaBufDescriptor, image);
-    return params;
-}
-
-uint32_t Service::GetQueueFamilyIndex() {
-    return VK_QUEUE_FAMILY_EXTERNAL_KHR;
-}
-
-ResultOrError<VkDeviceMemory> Service::ImportMemory(ExternalMemoryHandle handle,
-                                                    const MemoryImportParams& importParams,
-                                                    VkImage image) {
-    DAWN_INVALID_IF(handle < 0, "Importing memory with an invalid handle.");
-
-    VkMemoryAllocateInfo memoryAllocateInfo = {};
-    memoryAllocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
-    memoryAllocateInfo.allocationSize = importParams.allocationSize;
-    memoryAllocateInfo.memoryTypeIndex = importParams.memoryTypeIndex;
-    PNextChainBuilder memoryAllocateInfoChain(&memoryAllocateInfo);
-
-    VkImportMemoryFdInfoKHR importMemoryFdInfo;
-    importMemoryFdInfo.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT,
-    importMemoryFdInfo.fd = handle;
-    memoryAllocateInfoChain.Add(&importMemoryFdInfo, VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHR);
-
-    VkMemoryDedicatedAllocateInfo memoryDedicatedAllocateInfo;
-    if (importParams.dedicatedAllocation) {
-        memoryDedicatedAllocateInfo.image = image;
-        memoryDedicatedAllocateInfo.buffer = VkBuffer{};
-        memoryAllocateInfoChain.Add(&memoryDedicatedAllocateInfo,
-                                    VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO);
-    }
-
-    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) {
-    DAWN_INVALID_IF(descriptor->GetType() != ExternalImageType::DmaBuf,
-                    "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();
-
-    uint32_t planeCount;
-    DAWN_TRY_ASSIGN(planeCount,
-                    GetModifierPlaneCount(mDevice->fn, physicalDevice, baseCreateInfo.format,
-                                          dmaBufDescriptor->drmModifier));
-
-    VkImageCreateInfo createInfo = baseCreateInfo;
-    createInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
-    createInfo.tiling = VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT;
-
-    PNextChainBuilder createInfoChain(&createInfo);
-
-    VkExternalMemoryImageCreateInfo externalMemoryImageCreateInfo = {};
-    externalMemoryImageCreateInfo.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT;
-    createInfoChain.Add(&externalMemoryImageCreateInfo,
-                        VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO);
-
-    VkSubresourceLayout planeLayouts[ExternalImageDescriptorDmaBuf::kMaxPlanes];
-    for (uint32_t plane = 0u; plane < planeCount; ++plane) {
-        planeLayouts[plane].offset = dmaBufDescriptor->planeLayouts[plane].offset;
-        planeLayouts[plane].size = 0;  // VK_EXT_image_drm_format_modifier mandates size = 0.
-        planeLayouts[plane].rowPitch = dmaBufDescriptor->planeLayouts[plane].stride;
-        planeLayouts[plane].arrayPitch = 0;  // Not an array texture
-        planeLayouts[plane].depthPitch = 0;  // Not a depth texture
-    }
-
-    VkImageDrmFormatModifierExplicitCreateInfoEXT explicitCreateInfo = {};
-    explicitCreateInfo.drmFormatModifier = dmaBufDescriptor->drmModifier;
-    explicitCreateInfo.drmFormatModifierPlaneCount = planeCount;
-    explicitCreateInfo.pPlaneLayouts = &planeLayouts[0];
-
-    if (planeCount > 1) {
-        // For multi-planar formats, VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT specifies that a
-        // VkImageView can be plane's format which might differ from the image's format.
-        createInfo.flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT;
-    }
-    createInfoChain.Add(&explicitCreateInfo,
-                        VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_EXPLICIT_CREATE_INFO_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/MemoryServiceImplementationOpaqueFD.cpp b/src/dawn/native/vulkan/external_memory/MemoryServiceImplementationOpaqueFD.cpp
index 1ad48f1..9eb13eb 100644
--- a/src/dawn/native/vulkan/external_memory/MemoryServiceImplementationOpaqueFD.cpp
+++ b/src/dawn/native/vulkan/external_memory/MemoryServiceImplementationOpaqueFD.cpp
@@ -12,6 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include <memory>
+
 #include "dawn/common/Assert.h"
 #include "dawn/native/vulkan/AdapterVk.h"
 #include "dawn/native/vulkan/BackendVk.h"
@@ -20,152 +22,167 @@
 #include "dawn/native/vulkan/UtilsVulkan.h"
 #include "dawn/native/vulkan/VulkanError.h"
 #include "dawn/native/vulkan/external_memory/MemoryService.h"
+#include "dawn/native/vulkan/external_memory/MemoryServiceImplementation.h"
 
 namespace dawn::native::vulkan::external_memory {
 
-Service::Service(Device* device)
-    : mDevice(device), mSupported(CheckSupport(device->GetDeviceInfo())) {}
+class ServiceImplementationOpaqueFD : public ServiceImplementation {
+  public:
+    explicit ServiceImplementationOpaqueFD(Device* device)
+        : ServiceImplementation(device), mSupported(CheckSupport(device->GetDeviceInfo())) {}
+    ~ServiceImplementationOpaqueFD() override = default;
 
-Service::~Service() = default;
+    static bool CheckSupport(const VulkanDeviceInfo& deviceInfo) {
+        return deviceInfo.HasExt(DeviceExt::ExternalMemoryFD);
+    }
+
+    bool SupportsImportMemory(VkFormat format,
+                              VkImageType type,
+                              VkImageTiling tiling,
+                              VkImageUsageFlags usage,
+                              VkImageCreateFlags flags) override {
+        // Early out before we try using extension functions
+        if (!mSupported) {
+            return false;
+        }
+
+        VkPhysicalDeviceExternalImageFormatInfo externalFormatInfo;
+        externalFormatInfo.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO_KHR;
+        externalFormatInfo.pNext = nullptr;
+        externalFormatInfo.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR;
+
+        VkPhysicalDeviceImageFormatInfo2 formatInfo;
+        formatInfo.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2_KHR;
+        formatInfo.pNext = &externalFormatInfo;
+        formatInfo.format = format;
+        formatInfo.type = type;
+        formatInfo.tiling = tiling;
+        formatInfo.usage = usage;
+        formatInfo.flags = flags;
+
+        VkExternalImageFormatProperties externalFormatProperties;
+        externalFormatProperties.sType = VK_STRUCTURE_TYPE_EXTERNAL_IMAGE_FORMAT_PROPERTIES_KHR;
+        externalFormatProperties.pNext = nullptr;
+
+        VkImageFormatProperties2 formatProperties;
+        formatProperties.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2_KHR;
+        formatProperties.pNext = &externalFormatProperties;
+
+        VkResult result = VkResult::WrapUnsafe(mDevice->fn.GetPhysicalDeviceImageFormatProperties2(
+            ToBackend(mDevice->GetAdapter())->GetPhysicalDevice(), &formatInfo, &formatProperties));
+
+        // If handle not supported, result == VK_ERROR_FORMAT_NOT_SUPPORTED
+        if (result != VK_SUCCESS) {
+            return false;
+        }
+
+        VkFlags memoryFlags =
+            externalFormatProperties.externalMemoryProperties.externalMemoryFeatures;
+        return (memoryFlags & VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT_KHR) != 0;
+    }
+
+    bool SupportsCreateImage(const ExternalImageDescriptor* descriptor,
+                             VkFormat format,
+                             VkImageUsageFlags usage,
+                             bool* supportsDisjoint) override {
+        *supportsDisjoint = false;
+        return mSupported;
+    }
+
+    ResultOrError<MemoryImportParams> GetMemoryImportParams(
+        const ExternalImageDescriptor* descriptor,
+        VkImage image) override {
+        DAWN_INVALID_IF(descriptor->GetType() != ExternalImageType::OpaqueFD,
+                        "ExternalImageDescriptor is not an OpaqueFD descriptor.");
+
+        const ExternalImageDescriptorOpaqueFD* opaqueFDDescriptor =
+            static_cast<const ExternalImageDescriptorOpaqueFD*>(descriptor);
+
+        MemoryImportParams params;
+        params.allocationSize = opaqueFDDescriptor->allocationSize;
+        params.memoryTypeIndex = opaqueFDDescriptor->memoryTypeIndex;
+        params.dedicatedAllocation = RequiresDedicatedAllocation(opaqueFDDescriptor, image);
+        return params;
+    }
+
+    uint32_t GetQueueFamilyIndex() override { return VK_QUEUE_FAMILY_EXTERNAL_KHR; }
+
+    ResultOrError<VkDeviceMemory> ImportMemory(ExternalMemoryHandle handle,
+                                               const MemoryImportParams& importParams,
+                                               VkImage image) override {
+        DAWN_INVALID_IF(handle < 0, "Importing memory with an invalid handle.");
+
+        VkMemoryRequirements requirements;
+        mDevice->fn.GetImageMemoryRequirements(mDevice->GetVkDevice(), image, &requirements);
+        DAWN_INVALID_IF(requirements.size > importParams.allocationSize,
+                        "Requested allocation size (%u) is smaller than the image requires (%u).",
+                        importParams.allocationSize, requirements.size);
+
+        VkMemoryAllocateInfo allocateInfo;
+        allocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
+        allocateInfo.pNext = nullptr;
+        allocateInfo.allocationSize = importParams.allocationSize;
+        allocateInfo.memoryTypeIndex = importParams.memoryTypeIndex;
+        PNextChainBuilder allocateInfoChain(&allocateInfo);
+
+        VkImportMemoryFdInfoKHR importMemoryFdInfo;
+        importMemoryFdInfo.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT;
+        importMemoryFdInfo.fd = handle;
+        allocateInfoChain.Add(&importMemoryFdInfo, VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHR);
+
+        VkMemoryDedicatedAllocateInfo dedicatedAllocateInfo;
+        if (importParams.dedicatedAllocation) {
+            dedicatedAllocateInfo.image = image;
+            dedicatedAllocateInfo.buffer = VkBuffer{};
+            allocateInfoChain.Add(&dedicatedAllocateInfo,
+                                  VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO);
+        }
+
+        VkDeviceMemory allocatedMemory = VK_NULL_HANDLE;
+        DAWN_TRY(CheckVkSuccess(mDevice->fn.AllocateMemory(mDevice->GetVkDevice(), &allocateInfo,
+                                                           nullptr, &*allocatedMemory),
+                                "vkAllocateMemory"));
+        return allocatedMemory;
+    }
+
+    ResultOrError<VkImage> CreateImage(const ExternalImageDescriptor* descriptor,
+                                       const VkImageCreateInfo& baseCreateInfo) override {
+        VkImageCreateInfo createInfo = baseCreateInfo;
+        createInfo.flags |= VK_IMAGE_CREATE_ALIAS_BIT_KHR;
+        createInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
+        createInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+
+        VkExternalMemoryImageCreateInfo externalMemoryImageCreateInfo;
+        externalMemoryImageCreateInfo.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO;
+        externalMemoryImageCreateInfo.pNext = nullptr;
+        externalMemoryImageCreateInfo.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT;
+
+        PNextChainBuilder createInfoChain(&createInfo);
+        createInfoChain.Add(&externalMemoryImageCreateInfo,
+                            VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO);
+
+        ASSERT(IsSampleCountSupported(mDevice, createInfo));
+
+        VkImage image;
+        DAWN_TRY(CheckVkSuccess(
+            mDevice->fn.CreateImage(mDevice->GetVkDevice(), &createInfo, nullptr, &*image),
+            "CreateImage"));
+        return image;
+    }
+
+    bool Supported() const override { return mSupported; }
+
+  private:
+    bool mSupported = false;
+};
+
+Service::Service(Device* device) {
+    mImpl = std::make_unique<ServiceImplementationOpaqueFD>(device);
+}
 
 // static
 bool Service::CheckSupport(const VulkanDeviceInfo& deviceInfo) {
     return deviceInfo.HasExt(DeviceExt::ExternalMemoryFD);
 }
 
-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;
-    }
-
-    VkPhysicalDeviceExternalImageFormatInfo externalFormatInfo;
-    externalFormatInfo.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO_KHR;
-    externalFormatInfo.pNext = nullptr;
-    externalFormatInfo.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR;
-
-    VkPhysicalDeviceImageFormatInfo2 formatInfo;
-    formatInfo.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2_KHR;
-    formatInfo.pNext = &externalFormatInfo;
-    formatInfo.format = format;
-    formatInfo.type = type;
-    formatInfo.tiling = tiling;
-    formatInfo.usage = usage;
-    formatInfo.flags = flags;
-
-    VkExternalImageFormatProperties externalFormatProperties;
-    externalFormatProperties.sType = VK_STRUCTURE_TYPE_EXTERNAL_IMAGE_FORMAT_PROPERTIES_KHR;
-    externalFormatProperties.pNext = nullptr;
-
-    VkImageFormatProperties2 formatProperties;
-    formatProperties.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2_KHR;
-    formatProperties.pNext = &externalFormatProperties;
-
-    VkResult result = VkResult::WrapUnsafe(mDevice->fn.GetPhysicalDeviceImageFormatProperties2(
-        ToBackend(mDevice->GetAdapter())->GetPhysicalDevice(), &formatInfo, &formatProperties));
-
-    // If handle not supported, result == VK_ERROR_FORMAT_NOT_SUPPORTED
-    if (result != VK_SUCCESS) {
-        return false;
-    }
-
-    VkFlags memoryFlags = externalFormatProperties.externalMemoryProperties.externalMemoryFeatures;
-    return (memoryFlags & VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT_KHR) != 0;
-}
-
-bool Service::SupportsCreateImage(const ExternalImageDescriptor* descriptor,
-                                  VkFormat format,
-                                  VkImageUsageFlags usage,
-                                  bool* supportsDisjoint) {
-    *supportsDisjoint = false;
-    return mSupported;
-}
-
-ResultOrError<MemoryImportParams> Service::GetMemoryImportParams(
-    const ExternalImageDescriptor* descriptor,
-    VkImage image) {
-    DAWN_INVALID_IF(descriptor->GetType() != ExternalImageType::OpaqueFD,
-                    "ExternalImageDescriptor is not an OpaqueFD descriptor.");
-
-    const ExternalImageDescriptorOpaqueFD* opaqueFDDescriptor =
-        static_cast<const ExternalImageDescriptorOpaqueFD*>(descriptor);
-
-    MemoryImportParams params;
-    params.allocationSize = opaqueFDDescriptor->allocationSize;
-    params.memoryTypeIndex = opaqueFDDescriptor->memoryTypeIndex;
-    params.dedicatedAllocation = RequiresDedicatedAllocation(opaqueFDDescriptor, image);
-    return params;
-}
-
-uint32_t Service::GetQueueFamilyIndex() {
-    return VK_QUEUE_FAMILY_EXTERNAL_KHR;
-}
-
-ResultOrError<VkDeviceMemory> Service::ImportMemory(ExternalMemoryHandle handle,
-                                                    const MemoryImportParams& importParams,
-                                                    VkImage image) {
-    DAWN_INVALID_IF(handle < 0, "Importing memory with an invalid handle.");
-
-    VkMemoryRequirements requirements;
-    mDevice->fn.GetImageMemoryRequirements(mDevice->GetVkDevice(), image, &requirements);
-    DAWN_INVALID_IF(requirements.size > importParams.allocationSize,
-                    "Requested allocation size (%u) is smaller than the image requires (%u).",
-                    importParams.allocationSize, requirements.size);
-
-    VkMemoryAllocateInfo allocateInfo;
-    allocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
-    allocateInfo.pNext = nullptr;
-    allocateInfo.allocationSize = importParams.allocationSize;
-    allocateInfo.memoryTypeIndex = importParams.memoryTypeIndex;
-    PNextChainBuilder allocateInfoChain(&allocateInfo);
-
-    VkImportMemoryFdInfoKHR importMemoryFdInfo;
-    importMemoryFdInfo.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT;
-    importMemoryFdInfo.fd = handle;
-    allocateInfoChain.Add(&importMemoryFdInfo, VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHR);
-
-    VkMemoryDedicatedAllocateInfo dedicatedAllocateInfo;
-    if (importParams.dedicatedAllocation) {
-        dedicatedAllocateInfo.image = image;
-        dedicatedAllocateInfo.buffer = VkBuffer{};
-        allocateInfoChain.Add(&dedicatedAllocateInfo,
-                              VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO);
-    }
-
-    VkDeviceMemory allocatedMemory = VK_NULL_HANDLE;
-    DAWN_TRY(CheckVkSuccess(mDevice->fn.AllocateMemory(mDevice->GetVkDevice(), &allocateInfo,
-                                                       nullptr, &*allocatedMemory),
-                            "vkAllocateMemory"));
-    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;
-
-    VkExternalMemoryImageCreateInfo externalMemoryImageCreateInfo;
-    externalMemoryImageCreateInfo.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO;
-    externalMemoryImageCreateInfo.pNext = nullptr;
-    externalMemoryImageCreateInfo.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT;
-
-    PNextChainBuilder createInfoChain(&createInfo);
-    createInfoChain.Add(&externalMemoryImageCreateInfo,
-                        VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO);
-
-    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/MemoryServiceImplementationZirconHandle.cpp b/src/dawn/native/vulkan/external_memory/MemoryServiceImplementationZirconHandle.cpp
index 2f0dd3b..383b96a 100644
--- a/src/dawn/native/vulkan/external_memory/MemoryServiceImplementationZirconHandle.cpp
+++ b/src/dawn/native/vulkan/external_memory/MemoryServiceImplementationZirconHandle.cpp
@@ -12,6 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include <memory>
+
 #include "dawn/common/Assert.h"
 #include "dawn/native/vulkan/AdapterVk.h"
 #include "dawn/native/vulkan/BackendVk.h"
@@ -20,155 +22,171 @@
 #include "dawn/native/vulkan/UtilsVulkan.h"
 #include "dawn/native/vulkan/VulkanError.h"
 #include "dawn/native/vulkan/external_memory/MemoryService.h"
+#include "dawn/native/vulkan/external_memory/MemoryServiceImplementation.h"
 
 namespace dawn::native::vulkan::external_memory {
 
-Service::Service(Device* device)
-    : mDevice(device), mSupported(CheckSupport(device->GetDeviceInfo())) {}
+class ServiceImplementationZicronHandle : public ServiceImplementation {
+  public:
+    explicit ServiceImplementationZicronHandle(Device* device)
+        : ServiceImplementation(device), mSupported(CheckSupport(device->GetDeviceInfo())) {}
+    ~ServiceImplementationZicronHandle() override = default;
 
-Service::~Service() = default;
+    static bool CheckSupport(const VulkanDeviceInfo& deviceInfo) {
+        return deviceInfo.HasExt(DeviceExt::ExternalMemoryZirconHandle);
+    }
+
+    bool SupportsImportMemory(VkFormat format,
+                              VkImageType type,
+                              VkImageTiling tiling,
+                              VkImageUsageFlags usage,
+                              VkImageCreateFlags flags) override {
+        // Early out before we try using extension functions
+        if (!mSupported) {
+            return false;
+        }
+
+        VkPhysicalDeviceExternalImageFormatInfo externalFormatInfo;
+        externalFormatInfo.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO_KHR;
+        externalFormatInfo.pNext = nullptr;
+        externalFormatInfo.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_ZIRCON_VMO_BIT_FUCHSIA;
+
+        VkPhysicalDeviceImageFormatInfo2 formatInfo;
+        formatInfo.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2_KHR;
+        formatInfo.pNext = &externalFormatInfo;
+        formatInfo.format = format;
+        formatInfo.type = type;
+        formatInfo.tiling = tiling;
+        formatInfo.usage = usage;
+        formatInfo.flags = flags;
+
+        VkExternalImageFormatProperties externalFormatProperties;
+        externalFormatProperties.sType = VK_STRUCTURE_TYPE_EXTERNAL_IMAGE_FORMAT_PROPERTIES_KHR;
+        externalFormatProperties.pNext = nullptr;
+
+        VkImageFormatProperties2 formatProperties;
+        formatProperties.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2_KHR;
+        formatProperties.pNext = &externalFormatProperties;
+
+        VkResult result = mDevice->fn.GetPhysicalDeviceImageFormatProperties2(
+            ToBackend(mDevice->GetAdapter())->GetPhysicalDevice(), &formatInfo, &formatProperties);
+
+        // If handle not supported, result == VK_ERROR_FORMAT_NOT_SUPPORTED
+        if (result != VK_SUCCESS) {
+            return false;
+        }
+
+        // TODO(http://crbug.com/dawn/206): Investigate dedicated only images
+        VkFlags memoryFlags =
+            externalFormatProperties.externalMemoryProperties.externalMemoryFeatures;
+        return (memoryFlags & VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT_KHR) != 0;
+    }
+
+    bool SupportsCreateImage(const ExternalImageDescriptor* descriptor,
+                             VkFormat format,
+                             VkImageUsageFlags usage,
+                             bool* supportsDisjoint) override {
+        *supportsDisjoint = false;
+        return mSupported;
+    }
+
+    ResultOrError<MemoryImportParams> GetMemoryImportParams(
+        const ExternalImageDescriptor* descriptor,
+        VkImage image) override {
+        DAWN_INVALID_IF(descriptor->type != ExternalImageType::OpaqueFD,
+                        "ExternalImageDescriptor is not an OpaqueFD descriptor.");
+
+        const ExternalImageDescriptorOpaqueFD* opaqueFDDescriptor =
+            static_cast<const ExternalImageDescriptorOpaqueFD*>(descriptor);
+
+        MemoryImportParams params;
+        params.allocationSize = opaqueFDDescriptor->allocationSize;
+        params.memoryTypeIndex = opaqueFDDescriptor->memoryTypeIndex;
+        params.dedicatedAllocation = RequiresDedicatedAllocation(opaqueFDDescriptor, image);
+        return params;
+    }
+
+    uint32_t GetQueueFamilyIndex() override { return VK_QUEUE_FAMILY_EXTERNAL_KHR; }
+
+    ResultOrError<VkDeviceMemory> ImportMemory(ExternalMemoryHandle handle,
+                                               const MemoryImportParams& importParams,
+                                               VkImage image) override {
+        DAWN_INVALID_IF(handle == ZX_HANDLE_INVALID, "Importing memory with an invalid handle.");
+
+        VkMemoryRequirements requirements;
+        mDevice->fn.GetImageMemoryRequirements(mDevice->GetVkDevice(), image, &requirements);
+        DAWN_INVALID_IF(
+            requirements.size > importParams.allocationSize,
+            "Requested allocation size (%u) is smaller than the required image size (%u).",
+            importParams.allocationSize, requirements.size);
+
+        VkMemoryAllocateInfo allocateInfo;
+        allocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
+        allocateInfo.pNext = nullptr;
+        allocateInfo.allocationSize = importParams.allocationSize;
+        allocateInfo.memoryTypeIndex = importParams.memoryTypeIndex;
+        PNextChainBuilder allocateInfoChain(&allocateInfo);
+
+        VkImportMemoryZirconHandleInfoFUCHSIA importMemoryHandleInfo;
+        importMemoryHandleInfo.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_ZIRCON_VMO_BIT_FUCHSIA;
+        importMemoryHandleInfo.handle = handle;
+        allocateInfoChain.Add(&importMemoryHandleInfo,
+                              VK_STRUCTURE_TYPE_IMPORT_MEMORY_ZIRCON_HANDLE_INFO_FUCHSIA);
+
+        VkMemoryDedicatedAllocateInfo dedicatedAllocateInfo;
+        if (importParams.dedicatedAllocation) {
+            dedicatedAllocateInfo.image = image;
+            dedicatedAllocateInfo.buffer = VkBuffer{};
+            allocateInfoChain.Add(&dedicatedAllocateInfo,
+                                  VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO);
+        }
+
+        VkDeviceMemory allocatedMemory = VK_NULL_HANDLE;
+        DAWN_TRY(CheckVkSuccess(mDevice->fn.AllocateMemory(mDevice->GetVkDevice(), &allocateInfo,
+                                                           nullptr, &*allocatedMemory),
+                                "vkAllocateMemory"));
+        return allocatedMemory;
+    }
+
+    ResultOrError<VkImage> CreateImage(const ExternalImageDescriptor* descriptor,
+                                       const VkImageCreateInfo& baseCreateInfo) override {
+        VkImageCreateInfo createInfo = baseCreateInfo;
+        createInfo.flags |= VK_IMAGE_CREATE_ALIAS_BIT_KHR;
+        createInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
+        createInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+
+        VkExternalMemoryImageCreateInfo externalMemoryImageCreateInfo;
+        externalMemoryImageCreateInfo.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO;
+        externalMemoryImageCreateInfo.pNext = nullptr;
+        externalMemoryImageCreateInfo.handleTypes =
+            VK_EXTERNAL_MEMORY_HANDLE_TYPE_ZIRCON_VMO_BIT_FUCHSIA;
+
+        PNextChainBuilder createInfoChain(&createInfo);
+        createInfoChain.Add(&externalMemoryImageCreateInfo,
+                            VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO);
+
+        ASSERT(IsSampleCountSupported(mDevice, createInfo));
+
+        VkImage image;
+        DAWN_TRY(CheckVkSuccess(
+            mDevice->fn.CreateImage(mDevice->GetVkDevice(), &createInfo, nullptr, &*image),
+            "CreateImage"));
+        return image;
+    }
+
+    bool Supported() const override { return mSupported; }
+
+  private:
+    bool mSupported = false;
+};
+
+Service::Service(Device* device) {
+    mImpl = std::make_unique<ServiceImplementationZicronHandle>(device);
+}
 
 // static
 bool Service::CheckSupport(const VulkanDeviceInfo& deviceInfo) {
     return deviceInfo.HasExt(DeviceExt::ExternalMemoryZirconHandle);
 }
 
-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;
-    }
-
-    VkPhysicalDeviceExternalImageFormatInfo externalFormatInfo;
-    externalFormatInfo.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO_KHR;
-    externalFormatInfo.pNext = nullptr;
-    externalFormatInfo.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_ZIRCON_VMO_BIT_FUCHSIA;
-
-    VkPhysicalDeviceImageFormatInfo2 formatInfo;
-    formatInfo.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2_KHR;
-    formatInfo.pNext = &externalFormatInfo;
-    formatInfo.format = format;
-    formatInfo.type = type;
-    formatInfo.tiling = tiling;
-    formatInfo.usage = usage;
-    formatInfo.flags = flags;
-
-    VkExternalImageFormatProperties externalFormatProperties;
-    externalFormatProperties.sType = VK_STRUCTURE_TYPE_EXTERNAL_IMAGE_FORMAT_PROPERTIES_KHR;
-    externalFormatProperties.pNext = nullptr;
-
-    VkImageFormatProperties2 formatProperties;
-    formatProperties.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2_KHR;
-    formatProperties.pNext = &externalFormatProperties;
-
-    VkResult result = mDevice->fn.GetPhysicalDeviceImageFormatProperties2(
-        ToBackend(mDevice->GetAdapter())->GetPhysicalDevice(), &formatInfo, &formatProperties);
-
-    // If handle not supported, result == VK_ERROR_FORMAT_NOT_SUPPORTED
-    if (result != VK_SUCCESS) {
-        return false;
-    }
-
-    // TODO(http://crbug.com/dawn/206): Investigate dedicated only images
-    VkFlags memoryFlags = externalFormatProperties.externalMemoryProperties.externalMemoryFeatures;
-    return (memoryFlags & VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT_KHR) != 0;
-}
-
-bool Service::SupportsCreateImage(const ExternalImageDescriptor* descriptor,
-                                  VkFormat format,
-                                  VkImageUsageFlags usage,
-                                  bool* supportsDisjoint) {
-    *supportsDisjoint = false;
-    return mSupported;
-}
-
-ResultOrError<MemoryImportParams> Service::GetMemoryImportParams(
-    const ExternalImageDescriptor* descriptor,
-    VkImage image) {
-    DAWN_INVALID_IF(descriptor->type != ExternalImageType::OpaqueFD,
-                    "ExternalImageDescriptor is not an OpaqueFD descriptor.");
-
-    const ExternalImageDescriptorOpaqueFD* opaqueFDDescriptor =
-        static_cast<const ExternalImageDescriptorOpaqueFD*>(descriptor);
-
-    MemoryImportParams params;
-    params.allocationSize = opaqueFDDescriptor->allocationSize;
-    params.memoryTypeIndex = opaqueFDDescriptor->memoryTypeIndex;
-    params.dedicatedAllocation = RequiresDedicatedAllocation(opaqueFDDescriptor, image);
-    return params;
-}
-
-uint32_t Service::GetQueueFamilyIndex() {
-    return VK_QUEUE_FAMILY_EXTERNAL_KHR;
-}
-
-ResultOrError<VkDeviceMemory> Service::ImportMemory(ExternalMemoryHandle handle,
-                                                    const MemoryImportParams& importParams,
-                                                    VkImage image) {
-    DAWN_INVALID_IF(handle == ZX_HANDLE_INVALID, "Importing memory with an invalid handle.");
-
-    VkMemoryRequirements requirements;
-    mDevice->fn.GetImageMemoryRequirements(mDevice->GetVkDevice(), image, &requirements);
-    DAWN_INVALID_IF(requirements.size > importParams.allocationSize,
-                    "Requested allocation size (%u) is smaller than the required image size (%u).",
-                    importParams.allocationSize, requirements.size);
-
-    VkMemoryAllocateInfo allocateInfo;
-    allocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
-    allocateInfo.pNext = nullptr;
-    allocateInfo.allocationSize = importParams.allocationSize;
-    allocateInfo.memoryTypeIndex = importParams.memoryTypeIndex;
-    PNextChainBuilder allocateInfoChain(&allocateInfo);
-
-    VkImportMemoryZirconHandleInfoFUCHSIA importMemoryHandleInfo;
-    importMemoryHandleInfo.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_ZIRCON_VMO_BIT_FUCHSIA;
-    importMemoryHandleInfo.handle = handle;
-    allocateInfoChain.Add(&importMemoryHandleInfo,
-                          VK_STRUCTURE_TYPE_IMPORT_MEMORY_ZIRCON_HANDLE_INFO_FUCHSIA);
-
-    VkMemoryDedicatedAllocateInfo dedicatedAllocateInfo;
-    if (importParams.dedicatedAllocation) {
-        dedicatedAllocateInfo.image = image;
-        dedicatedAllocateInfo.buffer = VkBuffer{};
-        allocateInfoChain.Add(&dedicatedAllocateInfo,
-                              VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO);
-    }
-
-    VkDeviceMemory allocatedMemory = VK_NULL_HANDLE;
-    DAWN_TRY(CheckVkSuccess(mDevice->fn.AllocateMemory(mDevice->GetVkDevice(), &allocateInfo,
-                                                       nullptr, &*allocatedMemory),
-                            "vkAllocateMemory"));
-    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;
-
-    VkExternalMemoryImageCreateInfo externalMemoryImageCreateInfo;
-    externalMemoryImageCreateInfo.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO;
-    externalMemoryImageCreateInfo.pNext = nullptr;
-    externalMemoryImageCreateInfo.handleTypes =
-        VK_EXTERNAL_MEMORY_HANDLE_TYPE_ZIRCON_VMO_BIT_FUCHSIA;
-
-    PNextChainBuilder createInfoChain(&createInfo);
-    createInfoChain.Add(&externalMemoryImageCreateInfo,
-                        VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO);
-
-    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/MemoryServiceNull.cpp b/src/dawn/native/vulkan/external_memory/MemoryServiceNull.cpp
index 328265d..5f7de1e 100644
--- a/src/dawn/native/vulkan/external_memory/MemoryServiceNull.cpp
+++ b/src/dawn/native/vulkan/external_memory/MemoryServiceNull.cpp
@@ -12,58 +12,70 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include <memory>
+
 #include "dawn/native/vulkan/DeviceVk.h"
 #include "dawn/native/vulkan/external_memory/MemoryService.h"
+#include "dawn/native/vulkan/external_memory/MemoryServiceImplementation.h"
 
 namespace dawn::native::vulkan::external_memory {
 
-Service::Service(Device* device) : mDevice(device) {
-    DAWN_UNUSED(mDevice);
-    DAWN_UNUSED(mSupported);
-}
+class ServiceImplementationNull : public ServiceImplementation {
+  public:
+    explicit ServiceImplementationNull(Device* device)
+        : ServiceImplementation(device), mSupported(CheckSupport(device->GetDeviceInfo())) {}
+    ~ServiceImplementationNull() override = default;
 
-Service::~Service() = default;
+    static bool CheckSupport(const VulkanDeviceInfo& deviceInfo) { return false; }
+
+    bool SupportsImportMemory(VkFormat format,
+                              VkImageType type,
+                              VkImageTiling tiling,
+                              VkImageUsageFlags usage,
+                              VkImageCreateFlags flags) override {
+        return false;
+    }
+
+    bool SupportsCreateImage(const ExternalImageDescriptor* descriptor,
+                             VkFormat format,
+                             VkImageUsageFlags usage,
+                             bool* supportsDisjoint) override {
+        *supportsDisjoint = false;
+        return false;
+    }
+
+    ResultOrError<MemoryImportParams> GetMemoryImportParams(
+        const ExternalImageDescriptor* descriptor,
+        VkImage image) override {
+        return DAWN_UNIMPLEMENTED_ERROR("Using null memory service to interop inside Vulkan");
+    }
+
+    uint32_t GetQueueFamilyIndex() override { return VK_QUEUE_FAMILY_EXTERNAL_KHR; }
+
+    ResultOrError<VkDeviceMemory> ImportMemory(ExternalMemoryHandle handle,
+                                               const MemoryImportParams& importParams,
+                                               VkImage image) override {
+        return DAWN_UNIMPLEMENTED_ERROR("Using null memory service to interop inside Vulkan");
+    }
+
+    ResultOrError<VkImage> CreateImage(const ExternalImageDescriptor* descriptor,
+                                       const VkImageCreateInfo& baseCreateInfo) override {
+        return DAWN_UNIMPLEMENTED_ERROR("Using null memory service to interop inside Vulkan");
+    }
+
+    bool Supported() const override { return false; }
+
+  private:
+    bool mSupported = false;
+};
+
+Service::Service(Device* device) {
+    mImpl = std::make_unique<ServiceImplementationNull>(device);
+}
 
 // static
 bool Service::CheckSupport(const VulkanDeviceInfo& deviceInfo) {
     return false;
 }
 
-bool Service::SupportsImportMemory(VkFormat format,
-                                   VkImageType type,
-                                   VkImageTiling tiling,
-                                   VkImageUsageFlags usage,
-                                   VkImageCreateFlags flags) {
-    return false;
-}
-
-bool Service::SupportsCreateImage(const ExternalImageDescriptor* descriptor,
-                                  VkFormat format,
-                                  VkImageUsageFlags usage,
-                                  bool* supportsDisjoint) {
-    *supportsDisjoint = false;
-    return false;
-}
-
-ResultOrError<MemoryImportParams> Service::GetMemoryImportParams(
-    const ExternalImageDescriptor* descriptor,
-    VkImage image) {
-    return DAWN_UNIMPLEMENTED_ERROR("Using null memory service to interop inside Vulkan");
-}
-
-uint32_t Service::GetQueueFamilyIndex() {
-    return VK_QUEUE_FAMILY_EXTERNAL_KHR;
-}
-
-ResultOrError<VkDeviceMemory> Service::ImportMemory(ExternalMemoryHandle handle,
-                                                    const MemoryImportParams& importParams,
-                                                    VkImage image) {
-    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