Revert "Remove VK_DEFINE_NON_DISPATCHABLE_HANDLE magic, use explicit VkHandle wrapper"

This reverts commit 4e17d5c2483b63d4863162d692a1a961d1dcb958.

Reason for revert: broken on chromeos

Original change's description:
> Remove VK_DEFINE_NON_DISPATCHABLE_HANDLE magic, use explicit VkHandle wrapper
> 
> Overriding VK_DEFINE_NON_DISPATCHABLE_HANDLE changes the function
> signatures of Vulkan functions, changing their ABI and making us
> incompatible with real drivers. This removes that magic, and replaces it
> with an explicit wrapper, VkHandle, which has much of the same
> functionality as the original VkNonDispatchableHandle.
> 
> It adds definitions for dawn_native::vulkan::VkBuffer et al, which
> shadow the native ::VkBuffer et al. This retains type safety throughout
> the Vulkan backend without changing every single usage.
> 
> Notably, the following things had to change:
> - An explicit conversion from VkBuffer* to ::VkBuffer* is needed for
>   arrays. This is implemented as a reinterpret_cast, which is still
>   safe as the new VkHandle still has the same memory layout properties
>   as VkNonDispatchableHandle did.
> - When pointing to a VkHandle as an output pointer, it's now necessary
>   to explicitly get the native ::VkBuffer (via operator*) and point to it.
> 
> Bug: chromium:1046362
> Change-Id: I9c5691b6e295aca1b46d4e3d0203956e4d570285
> Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/15580
> Reviewed-by: Austin Eng <enga@chromium.org>
> Reviewed-by: Kai Ninomiya <kainino@chromium.org>
> Commit-Queue: Kai Ninomiya <kainino@chromium.org>

TBR=cwallez@chromium.org,kainino@chromium.org,enga@chromium.org

Change-Id: I500df2e34fd0f245ad04c517ff028ddd7bb5a2bf
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Bug: chromium:1046362
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/15620
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
Commit-Queue: Kai Ninomiya <kainino@chromium.org>
diff --git a/src/common/vulkan_platform.h b/src/common/vulkan_platform.h
index 913f011..35742a7 100644
--- a/src/common/vulkan_platform.h
+++ b/src/common/vulkan_platform.h
@@ -18,9 +18,6 @@
 #if !defined(DAWN_ENABLE_BACKEND_VULKAN)
 #    error "vulkan_platform.h included without the Vulkan backend enabled"
 #endif
-#if defined(VULKAN_CORE_H_)
-#    error "vulkan.h included before vulkan_platform.h"
-#endif
 
 #include "common/Platform.h"
 
@@ -36,9 +33,10 @@
 // (like vulkan.h on 64 bit) but makes sure the types are different on 32 bit architectures.
 
 #if defined(DAWN_PLATFORM_64_BIT)
-#    define DAWN_DEFINE_NATIVE_NON_DISPATCHABLE_HANDLE(object) using object = struct object##_T*;
+#    define DAWN_DEFINE_NATIVE_NON_DISPATCHABLE_HANDLE(object) \
+        using object##Native = struct object##_T*;
 #elif defined(DAWN_PLATFORM_32_BIT)
-#    define DAWN_DEFINE_NATIVE_NON_DISPATCHABLE_HANDLE(object) using object = uint64_t;
+#    define DAWN_DEFINE_NATIVE_NON_DISPATCHABLE_HANDLE(object) using object##Native = uint64_t;
 #else
 #    error "Unsupported platform"
 #endif
@@ -55,106 +53,105 @@
 // One way to get the alignment inside structures of a type is to look at the alignment of it
 // wrapped in a structure. Hence VkSameHandleNativeWrappe
 
-namespace dawn_native { namespace vulkan {
+template <typename T>
+struct WrapperStruct {
+    T member;
+};
 
-    namespace detail {
-        template <typename T>
-        struct WrapperStruct {
-            T member;
-        };
+template <typename T>
+static constexpr size_t AlignOfInStruct = alignof(WrapperStruct<T>);
 
-        template <typename T>
-        static constexpr size_t AlignOfInStruct = alignof(WrapperStruct<T>);
+static constexpr size_t kNativeVkHandleAlignment = AlignOfInStruct<VkSomeHandleNative>;
+static constexpr size_t kUint64Alignment = AlignOfInStruct<VkSomeHandleNative>;
 
-        static constexpr size_t kNativeVkHandleAlignment = AlignOfInStruct<VkSomeHandle>;
-        static constexpr size_t kUint64Alignment = AlignOfInStruct<uint64_t>;
-
-        // Simple handle types that supports "nullptr_t" as a 0 value.
-        template <typename Tag, typename HandleType>
-        class alignas(detail::kNativeVkHandleAlignment) VkHandle {
-          public:
-            // Default constructor and assigning of VK_NULL_HANDLE
-            VkHandle() = default;
-            VkHandle(std::nullptr_t) {
-            }
-
-            // Use default copy constructor/assignment
-            VkHandle(const VkHandle<Tag, HandleType>& other) = default;
-            VkHandle& operator=(const VkHandle<Tag, HandleType>&) = default;
-
-            // Comparisons between handles
-            bool operator==(VkHandle<Tag, HandleType> other) const {
-                return mHandle == other.mHandle;
-            }
-            bool operator!=(VkHandle<Tag, HandleType> other) const {
-                return mHandle != other.mHandle;
-            }
-
-            // Comparisons between handles and VK_NULL_HANDLE
-            bool operator==(std::nullptr_t) const {
-                return mHandle == 0;
-            }
-            bool operator!=(std::nullptr_t) const {
-                return mHandle != 0;
-            }
-
-            // Implicit conversion to real Vulkan types.
-            operator HandleType() const {
-                return GetHandle();
-            }
-
-            HandleType GetHandle() const {
-                return mHandle;
-            }
-
-            HandleType& operator*() {
-                return mHandle;
-            }
-
-            static VkHandle<Tag, HandleType> CreateFromHandle(HandleType handle) {
-                return VkHandle{handle};
-            }
-
-          private:
-            explicit VkHandle(HandleType handle) : mHandle(handle) {
-            }
-
-            HandleType mHandle = 0;
-        };
-    }  // namespace detail
-
-    static constexpr std::nullptr_t VK_NULL_HANDLE = nullptr;
-
-    template <typename Tag, typename HandleType>
-    HandleType* AsVkArray(detail::VkHandle<Tag, HandleType>* handle) {
-        return reinterpret_cast<HandleType*>(handle);
+// Simple handle types that supports "nullptr_t" as a 0 value.
+template <typename Tag, typename HandleType>
+class alignas(kNativeVkHandleAlignment) VkNonDispatchableHandle {
+  public:
+    // Default constructor and assigning of VK_NULL_HANDLE
+    VkNonDispatchableHandle() = default;
+    VkNonDispatchableHandle(std::nullptr_t) : mHandle(0) {
     }
 
-}}  // namespace dawn_native::vulkan
+    // Use default copy constructor/assignment
+    VkNonDispatchableHandle(const VkNonDispatchableHandle<Tag, HandleType>& other) = default;
+    VkNonDispatchableHandle& operator=(const VkNonDispatchableHandle<Tag, HandleType>&) = default;
 
-#define VK_DEFINE_NON_DISPATCHABLE_HANDLE(object)                                   \
-    DAWN_DEFINE_NATIVE_NON_DISPATCHABLE_HANDLE(object)                              \
-    namespace dawn_native { namespace vulkan {                                      \
-            using object = detail::VkHandle<struct VkTag##object, ::object>;        \
-            static_assert(sizeof(object) == sizeof(uint64_t), "");                  \
-            static_assert(alignof(object) == detail::kUint64Alignment, "");         \
-            static_assert(sizeof(object) == sizeof(::object), "");                  \
-            static_assert(alignof(object) == detail::kNativeVkHandleAlignment, ""); \
-        }                                                                           \
-    }  // namespace dawn_native::vulkan
+    // Comparisons between handles
+    bool operator==(VkNonDispatchableHandle<Tag, HandleType> other) const {
+        return mHandle == other.mHandle;
+    }
+    bool operator!=(VkNonDispatchableHandle<Tag, HandleType> other) const {
+        return mHandle != other.mHandle;
+    }
 
-#include <vulkan/vulkan.h>
+    // Comparisons between handles and VK_NULL_HANDLE
+    bool operator==(std::nullptr_t) const {
+        return mHandle == 0;
+    }
+    bool operator!=(std::nullptr_t) const {
+        return mHandle != 0;
+    }
 
-// Redefine VK_NULL_HANDLE for better type safety where possible.
-#undef VK_NULL_HANDLE
+    // The regular Vulkan handle type depends on the pointer width but is always 64 bits wide.
+    //  - On 64bit it is an opaque pointer type, probably to help with type safety
+    //  - On 32bit it is a uint64_t because pointers aren't wide enough (and non dispatchable
+    //    handles can be optimized to not be pointer but contain GPU virtual addresses or the
+    //    data in a packed form).
+    // Because of this we need two types of conversions from our handle type: to uint64_t and to
+    // the "native" Vulkan type that may not be an uint64_t
+
+    static VkNonDispatchableHandle<Tag, HandleType> CreateFromU64(uint64_t handle) {
+        return {handle};
+    }
+
+    uint64_t GetU64() const {
+        return mHandle;
+    }
+
 #if defined(DAWN_PLATFORM_64_BIT)
-static constexpr nullptr_t VK_NULL_HANDLE = nullptr;
+    static VkNonDispatchableHandle<Tag, HandleType> CreateFromHandle(HandleType handle) {
+        return CreateFromU64(static_cast<uint64_t>(reinterpret_cast<intptr_t>(handle)));
+    }
+
+    HandleType GetHandle() const {
+        return mHandle;
+    }
 #elif defined(DAWN_PLATFORM_32_BIT)
-static constexpr uint64_t VK_NULL_HANDLE = 0;
+    static VkNonDispatchableHandle<Tag, HandleType> CreateFromHandle(HandleType handle) {
+        return {handle};
+    }
+
+    HandleType GetHandle() const {
+        return mHandle;
+    }
 #else
 #    error "Unsupported platform"
 #endif
 
+  private:
+    VkNonDispatchableHandle(uint64_t handle) : mHandle(handle) {
+    }
+
+    uint64_t mHandle = 0;
+};
+
+#define VK_DEFINE_NON_DISPATCHABLE_HANDLE(object)                          \
+    struct VkTag##object;                                                  \
+    DAWN_DEFINE_NATIVE_NON_DISPATCHABLE_HANDLE(object)                     \
+    using object = VkNonDispatchableHandle<VkTag##object, object##Native>; \
+    static_assert(sizeof(object) == sizeof(uint64_t), "");                 \
+    static_assert(alignof(object) == kUint64Alignment, "");                \
+    static_assert(sizeof(object) == sizeof(object##Native), "");           \
+    static_assert(alignof(object) == kNativeVkHandleAlignment, "");
+
+#    include <vulkan/vulkan.h>
+
+    // VK_NULL_HANDLE is defined to 0 but we don't want our handle type to compare to arbitrary
+    // integers. Redefine VK_NULL_HANDLE to nullptr that has its own type.
+#    undef VK_NULL_HANDLE
+#    define VK_NULL_HANDLE nullptr
+
 // Remove windows.h macros after vulkan_platform's include of windows.h
 #if defined(DAWN_PLATFORM_WINDOWS)
 #    include "common/windows_with_undefs.h"
diff --git a/src/dawn_native/vulkan/BackendVk.cpp b/src/dawn_native/vulkan/BackendVk.cpp
index d2745cb..d2aaa76 100644
--- a/src/dawn_native/vulkan/BackendVk.cpp
+++ b/src/dawn_native/vulkan/BackendVk.cpp
@@ -272,7 +272,7 @@
         createInfo.pUserData = this;
 
         return CheckVkSuccess(mFunctions.CreateDebugReportCallbackEXT(
-                                  mInstance, &createInfo, nullptr, &*mDebugReportCallback),
+                                  mInstance, &createInfo, nullptr, &mDebugReportCallback),
                               "vkCreateDebugReportcallback");
     }
 
diff --git a/src/dawn_native/vulkan/BindGroupLayoutVk.cpp b/src/dawn_native/vulkan/BindGroupLayoutVk.cpp
index 90c8366..89ce53d 100644
--- a/src/dawn_native/vulkan/BindGroupLayoutVk.cpp
+++ b/src/dawn_native/vulkan/BindGroupLayoutVk.cpp
@@ -105,7 +105,7 @@
 
         Device* device = ToBackend(GetDevice());
         DAWN_TRY(CheckVkSuccess(device->fn.CreateDescriptorSetLayout(
-                                    device->GetVkDevice(), &createInfo, nullptr, &*mHandle),
+                                    device->GetVkDevice(), &createInfo, nullptr, &mHandle),
                                 "CreateDescriptorSetLayout"));
 
         // Compute the size of descriptor pools used for this layout.
@@ -171,7 +171,7 @@
 
         VkDescriptorPool descriptorPool;
         DAWN_TRY(CheckVkSuccess(device->fn.CreateDescriptorPool(device->GetVkDevice(), &createInfo,
-                                                                nullptr, &*descriptorPool),
+                                                                nullptr, &descriptorPool),
                                 "CreateDescriptorPool"));
 
         // Allocate our single set.
@@ -180,13 +180,12 @@
         allocateInfo.pNext = nullptr;
         allocateInfo.descriptorPool = descriptorPool;
         allocateInfo.descriptorSetCount = 1;
-        allocateInfo.pSetLayouts = &*mHandle;
+        allocateInfo.pSetLayouts = &mHandle;
 
         VkDescriptorSet descriptorSet;
-        MaybeError result =
-            CheckVkSuccess(device->fn.AllocateDescriptorSets(device->GetVkDevice(), &allocateInfo,
-                                                             &*descriptorSet),
-                           "AllocateDescriptorSets");
+        MaybeError result = CheckVkSuccess(
+            device->fn.AllocateDescriptorSets(device->GetVkDevice(), &allocateInfo, &descriptorSet),
+            "AllocateDescriptorSets");
 
         if (result.IsError()) {
             // On an error we can destroy the pool immediately because no command references it.
diff --git a/src/dawn_native/vulkan/BindGroupVk.h b/src/dawn_native/vulkan/BindGroupVk.h
index 9fa857b..4dd4c21 100644
--- a/src/dawn_native/vulkan/BindGroupVk.h
+++ b/src/dawn_native/vulkan/BindGroupVk.h
@@ -17,7 +17,6 @@
 
 #include "dawn_native/BindGroup.h"
 
-#include "common/vulkan_platform.h"
 #include "dawn_native/vulkan/BindGroupLayoutVk.h"
 
 namespace dawn_native { namespace vulkan {
diff --git a/src/dawn_native/vulkan/BufferVk.cpp b/src/dawn_native/vulkan/BufferVk.cpp
index 4a1af60..8f8b3aa 100644
--- a/src/dawn_native/vulkan/BufferVk.cpp
+++ b/src/dawn_native/vulkan/BufferVk.cpp
@@ -147,7 +147,7 @@
 
         Device* device = ToBackend(GetDevice());
         DAWN_TRY(CheckVkSuccess(
-            device->fn.CreateBuffer(device->GetVkDevice(), &createInfo, nullptr, &*mHandle),
+            device->fn.CreateBuffer(device->GetVkDevice(), &createInfo, nullptr, &mHandle),
             "vkCreateBuffer"));
 
         VkMemoryRequirements requirements;
diff --git a/src/dawn_native/vulkan/CommandBufferVk.cpp b/src/dawn_native/vulkan/CommandBufferVk.cpp
index 7e12806..b845e3b 100644
--- a/src/dawn_native/vulkan/CommandBufferVk.cpp
+++ b/src/dawn_native/vulkan/CommandBufferVk.cpp
@@ -106,7 +106,7 @@
                                                     ? dynamicOffsets[dirtyIndex].data()
                                                     : nullptr;
                 device->fn.CmdBindDescriptorSets(commands, bindPoint, pipelineLayout, dirtyIndex, 1,
-                                                 &*set, dynamicOffsetCounts[dirtyIndex],
+                                                 &set, dynamicOffsetCounts[dirtyIndex],
                                                  dynamicOffset);
             }
         }
@@ -255,14 +255,14 @@
                 createInfo.flags = 0;
                 createInfo.renderPass = renderPassVK;
                 createInfo.attachmentCount = attachmentCount;
-                createInfo.pAttachments = AsVkArray(attachments.data());
+                createInfo.pAttachments = attachments.data();
                 createInfo.width = renderPass->width;
                 createInfo.height = renderPass->height;
                 createInfo.layers = 1;
 
                 DAWN_TRY(
                     CheckVkSuccess(device->fn.CreateFramebuffer(device->GetVkDevice(), &createInfo,
-                                                                nullptr, &*framebuffer),
+                                                                nullptr, &framebuffer),
                                    "CreateFramebuffer"));
 
                 // We don't reuse VkFramebuffers so mark the framebuffer for deletion as soon as the
@@ -827,7 +827,7 @@
                     VkBuffer buffer = ToBackend(cmd->buffer)->GetHandle();
                     VkDeviceSize offset = static_cast<VkDeviceSize>(cmd->offset);
 
-                    device->fn.CmdBindVertexBuffers(commands, cmd->slot, 1, &*buffer, &offset);
+                    device->fn.CmdBindVertexBuffers(commands, cmd->slot, 1, &buffer, &offset);
                 } break;
 
                 default:
diff --git a/src/dawn_native/vulkan/ComputePipelineVk.cpp b/src/dawn_native/vulkan/ComputePipelineVk.cpp
index 16dd8e7..2f37620 100644
--- a/src/dawn_native/vulkan/ComputePipelineVk.cpp
+++ b/src/dawn_native/vulkan/ComputePipelineVk.cpp
@@ -38,7 +38,7 @@
         createInfo.pNext = nullptr;
         createInfo.flags = 0;
         createInfo.layout = ToBackend(descriptor->layout)->GetHandle();
-        createInfo.basePipelineHandle = ::VK_NULL_HANDLE;
+        createInfo.basePipelineHandle = VK_NULL_HANDLE;
         createInfo.basePipelineIndex = -1;
 
         createInfo.stage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
@@ -51,8 +51,8 @@
 
         Device* device = ToBackend(GetDevice());
         return CheckVkSuccess(
-            device->fn.CreateComputePipelines(device->GetVkDevice(), ::VK_NULL_HANDLE, 1,
-                                              &createInfo, nullptr, &*mHandle),
+            device->fn.CreateComputePipelines(device->GetVkDevice(), VK_NULL_HANDLE, 1, &createInfo,
+                                              nullptr, &mHandle),
             "CreateComputePipeline");
     }
 
diff --git a/src/dawn_native/vulkan/DeviceVk.cpp b/src/dawn_native/vulkan/DeviceVk.cpp
index e78718e..bc4179a 100644
--- a/src/dawn_native/vulkan/DeviceVk.cpp
+++ b/src/dawn_native/vulkan/DeviceVk.cpp
@@ -272,13 +272,13 @@
         submitInfo.pNext = nullptr;
         submitInfo.waitSemaphoreCount =
             static_cast<uint32_t>(mRecordingContext.waitSemaphores.size());
-        submitInfo.pWaitSemaphores = AsVkArray(mRecordingContext.waitSemaphores.data());
+        submitInfo.pWaitSemaphores = mRecordingContext.waitSemaphores.data();
         submitInfo.pWaitDstStageMask = dstStageMasks.data();
         submitInfo.commandBufferCount = 1;
         submitInfo.pCommandBuffers = &mRecordingContext.commandBuffer;
         submitInfo.signalSemaphoreCount =
             static_cast<uint32_t>(mRecordingContext.signalSemaphores.size());
-        submitInfo.pSignalSemaphores = AsVkArray(mRecordingContext.signalSemaphores.data());
+        submitInfo.pSignalSemaphores = mRecordingContext.signalSemaphores.data();
 
         VkFence fence = VK_NULL_HANDLE;
         DAWN_TRY_ASSIGN(fence, GetUnusedFence());
@@ -474,7 +474,7 @@
     ResultOrError<VkFence> Device::GetUnusedFence() {
         if (!mUnusedFences.empty()) {
             VkFence fence = mUnusedFences.back();
-            DAWN_TRY(CheckVkSuccess(fn.ResetFences(mVkDevice, 1, &*fence), "vkResetFences"));
+            DAWN_TRY(CheckVkSuccess(fn.ResetFences(mVkDevice, 1, &fence), "vkResetFences"));
 
             mUnusedFences.pop_back();
             return fence;
@@ -486,7 +486,7 @@
         createInfo.flags = 0;
 
         VkFence fence = VK_NULL_HANDLE;
-        DAWN_TRY(CheckVkSuccess(fn.CreateFence(mVkDevice, &createInfo, nullptr, &*fence),
+        DAWN_TRY(CheckVkSuccess(fn.CreateFence(mVkDevice, &createInfo, nullptr, &fence),
                                 "vkCreateFence"));
 
         return fence;
@@ -539,7 +539,7 @@
             createInfo.queueFamilyIndex = mQueueFamily;
 
             DAWN_TRY(CheckVkSuccess(fn.CreateCommandPool(mVkDevice, &createInfo, nullptr,
-                                                         &*mRecordingContext.commandPool),
+                                                         &mRecordingContext.commandPool),
                                     "vkCreateCommandPool"));
 
             VkCommandBufferAllocateInfo allocateInfo;
@@ -756,7 +756,7 @@
             VkResult result = VkResult::WrapUnsafe(VK_TIMEOUT);
             do {
                 result = VkResult::WrapUnsafe(
-                    INJECT_ERROR_OR_RUN(fn.WaitForFences(mVkDevice, 1, &*fence, true, UINT64_MAX),
+                    INJECT_ERROR_OR_RUN(fn.WaitForFences(mVkDevice, 1, &fence, true, UINT64_MAX),
                                         VK_ERROR_DEVICE_LOST));
             } while (result == VK_TIMEOUT);
 
diff --git a/src/dawn_native/vulkan/NativeSwapChainImplVk.cpp b/src/dawn_native/vulkan/NativeSwapChainImplVk.cpp
index ada090f..a330d71 100644
--- a/src/dawn_native/vulkan/NativeSwapChainImplVk.cpp
+++ b/src/dawn_native/vulkan/NativeSwapChainImplVk.cpp
@@ -136,7 +136,7 @@
         createInfo.oldSwapchain = oldSwapchain;
 
         if (mDevice->fn.CreateSwapchainKHR(mDevice->GetVkDevice(), &createInfo, nullptr,
-                                           &*mSwapChain) != VK_SUCCESS) {
+                                           &mSwapChain) != VK_SUCCESS) {
             ASSERT(false);
         }
 
@@ -151,7 +151,7 @@
         ASSERT(count >= mConfig.minImageCount);
         mSwapChainImages.resize(count);
         if (mDevice->fn.GetSwapchainImagesKHR(mDevice->GetVkDevice(), mSwapChain, &count,
-                                              AsVkArray(mSwapChainImages.data())) != VK_SUCCESS) {
+                                              mSwapChainImages.data()) != VK_SUCCESS) {
             ASSERT(false);
         }
 
@@ -168,7 +168,7 @@
             barrier.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
             barrier.srcQueueFamilyIndex = 0;
             barrier.dstQueueFamilyIndex = 0;
-            barrier.image = *image;
+            barrier.image = image;
             barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
             barrier.subresourceRange.baseMipLevel = 0;
             barrier.subresourceRange.levelCount = 1;
@@ -197,22 +197,18 @@
             createInfo.pNext = nullptr;
             createInfo.flags = 0;
             if (mDevice->fn.CreateSemaphore(mDevice->GetVkDevice(), &createInfo, nullptr,
-                                            &*semaphore) != VK_SUCCESS) {
+                                            &semaphore) != VK_SUCCESS) {
                 ASSERT(false);
             }
         }
 
         if (mDevice->fn.AcquireNextImageKHR(mDevice->GetVkDevice(), mSwapChain,
                                             std::numeric_limits<uint64_t>::max(), semaphore,
-                                            VkFence{}, &mLastImageIndex) != VK_SUCCESS) {
+                                            VK_NULL_HANDLE, &mLastImageIndex) != VK_SUCCESS) {
             ASSERT(false);
         }
 
-        nextTexture->texture.u64 =
-#if defined(DAWN_PLATFORM_64_BIT)
-            reinterpret_cast<uint64_t>
-#endif
-            (*mSwapChainImages[mLastImageIndex]);
+        nextTexture->texture.u64 = mSwapChainImages[mLastImageIndex].GetU64();
         mDevice->GetPendingRecordingContext()->waitSemaphores.push_back(semaphore);
 
         return DAWN_SWAP_CHAIN_NO_ERROR;
@@ -231,7 +227,7 @@
         presentInfo.waitSemaphoreCount = 0;
         presentInfo.pWaitSemaphores = nullptr;
         presentInfo.swapchainCount = 1;
-        presentInfo.pSwapchains = &*mSwapChain;
+        presentInfo.pSwapchains = &mSwapChain;
         presentInfo.pImageIndices = &mLastImageIndex;
         presentInfo.pResults = nullptr;
 
diff --git a/src/dawn_native/vulkan/PipelineLayoutVk.cpp b/src/dawn_native/vulkan/PipelineLayoutVk.cpp
index 847ba60..dd123af 100644
--- a/src/dawn_native/vulkan/PipelineLayoutVk.cpp
+++ b/src/dawn_native/vulkan/PipelineLayoutVk.cpp
@@ -48,13 +48,13 @@
         createInfo.pNext = nullptr;
         createInfo.flags = 0;
         createInfo.setLayoutCount = numSetLayouts;
-        createInfo.pSetLayouts = AsVkArray(setLayouts.data());
+        createInfo.pSetLayouts = setLayouts.data();
         createInfo.pushConstantRangeCount = 0;
         createInfo.pPushConstantRanges = nullptr;
 
         Device* device = ToBackend(GetDevice());
         return CheckVkSuccess(
-            device->fn.CreatePipelineLayout(device->GetVkDevice(), &createInfo, nullptr, &*mHandle),
+            device->fn.CreatePipelineLayout(device->GetVkDevice(), &createInfo, nullptr, &mHandle),
             "CreatePipelineLayout");
     }
 
diff --git a/src/dawn_native/vulkan/RenderPassCache.cpp b/src/dawn_native/vulkan/RenderPassCache.cpp
index 47330f1..8742458 100644
--- a/src/dawn_native/vulkan/RenderPassCache.cpp
+++ b/src/dawn_native/vulkan/RenderPassCache.cpp
@@ -191,9 +191,9 @@
 
         // Create the render pass from the zillion parameters
         VkRenderPass renderPass;
-        DAWN_TRY(CheckVkSuccess(mDevice->fn.CreateRenderPass(mDevice->GetVkDevice(), &createInfo,
-                                                             nullptr, &*renderPass),
-                                "CreateRenderPass"));
+        DAWN_TRY(CheckVkSuccess(
+            mDevice->fn.CreateRenderPass(mDevice->GetVkDevice(), &createInfo, nullptr, &renderPass),
+            "CreateRenderPass"));
         return renderPass;
     }
 
diff --git a/src/dawn_native/vulkan/RenderPipelineVk.cpp b/src/dawn_native/vulkan/RenderPipelineVk.cpp
index b0ba4fd..4b770a5 100644
--- a/src/dawn_native/vulkan/RenderPipelineVk.cpp
+++ b/src/dawn_native/vulkan/RenderPipelineVk.cpp
@@ -495,12 +495,12 @@
         createInfo.layout = ToBackend(GetLayout())->GetHandle();
         createInfo.renderPass = renderPass;
         createInfo.subpass = 0;
-        createInfo.basePipelineHandle = VkPipeline{};
+        createInfo.basePipelineHandle = VK_NULL_HANDLE;
         createInfo.basePipelineIndex = -1;
 
         return CheckVkSuccess(
-            device->fn.CreateGraphicsPipelines(device->GetVkDevice(), VkPipelineCache{}, 1,
-                                               &createInfo, nullptr, &*mHandle),
+            device->fn.CreateGraphicsPipelines(device->GetVkDevice(), VK_NULL_HANDLE, 1,
+                                               &createInfo, nullptr, &mHandle),
             "CreateGraphicsPipeline");
     }
 
diff --git a/src/dawn_native/vulkan/ResourceMemoryAllocatorVk.cpp b/src/dawn_native/vulkan/ResourceMemoryAllocatorVk.cpp
index 3d8ded5..25fa5ee 100644
--- a/src/dawn_native/vulkan/ResourceMemoryAllocatorVk.cpp
+++ b/src/dawn_native/vulkan/ResourceMemoryAllocatorVk.cpp
@@ -73,7 +73,7 @@
             // First check OOM that we want to surface to the application.
             DAWN_TRY(CheckVkOOMThenSuccess(
                 mDevice->fn.AllocateMemory(mDevice->GetVkDevice(), &allocateInfo, nullptr,
-                                           &*allocatedMemory),
+                                           &allocatedMemory),
                 "vkAllocateMemory"));
 
             ASSERT(allocatedMemory != VK_NULL_HANDLE);
diff --git a/src/dawn_native/vulkan/SamplerVk.cpp b/src/dawn_native/vulkan/SamplerVk.cpp
index 67d70f8..05baf71 100644
--- a/src/dawn_native/vulkan/SamplerVk.cpp
+++ b/src/dawn_native/vulkan/SamplerVk.cpp
@@ -87,7 +87,7 @@
 
         Device* device = ToBackend(GetDevice());
         return CheckVkSuccess(
-            device->fn.CreateSampler(device->GetVkDevice(), &createInfo, nullptr, &*mHandle),
+            device->fn.CreateSampler(device->GetVkDevice(), &createInfo, nullptr, &mHandle),
             "CreateSampler");
     }
 
diff --git a/src/dawn_native/vulkan/ShaderModuleVk.cpp b/src/dawn_native/vulkan/ShaderModuleVk.cpp
index a9e717c..140abba 100644
--- a/src/dawn_native/vulkan/ShaderModuleVk.cpp
+++ b/src/dawn_native/vulkan/ShaderModuleVk.cpp
@@ -74,7 +74,7 @@
 
         Device* device = ToBackend(GetDevice());
         return CheckVkSuccess(
-            device->fn.CreateShaderModule(device->GetVkDevice(), &createInfo, nullptr, &*mHandle),
+            device->fn.CreateShaderModule(device->GetVkDevice(), &createInfo, nullptr, &mHandle),
             "CreateShaderModule");
     }
 
diff --git a/src/dawn_native/vulkan/StagingBufferVk.cpp b/src/dawn_native/vulkan/StagingBufferVk.cpp
index 4fa3b7b..4262318 100644
--- a/src/dawn_native/vulkan/StagingBufferVk.cpp
+++ b/src/dawn_native/vulkan/StagingBufferVk.cpp
@@ -36,7 +36,7 @@
         createInfo.pQueueFamilyIndices = 0;
 
         DAWN_TRY(CheckVkSuccess(
-            mDevice->fn.CreateBuffer(mDevice->GetVkDevice(), &createInfo, nullptr, &*mBuffer),
+            mDevice->fn.CreateBuffer(mDevice->GetVkDevice(), &createInfo, nullptr, &mBuffer),
             "vkCreateBuffer"));
 
         VkMemoryRequirements requirements;
diff --git a/src/dawn_native/vulkan/SwapChainVk.cpp b/src/dawn_native/vulkan/SwapChainVk.cpp
index e64f5e5..876af8e 100644
--- a/src/dawn_native/vulkan/SwapChainVk.cpp
+++ b/src/dawn_native/vulkan/SwapChainVk.cpp
@@ -47,8 +47,7 @@
             return nullptr;
         }
 
-        VkImage nativeTexture =
-            VkImage::CreateFromHandle(reinterpret_cast<::VkImage>(next.texture.u64));
+        VkImage nativeTexture = VkImage::CreateFromU64(next.texture.u64);
         return new Texture(ToBackend(GetDevice()), descriptor, nativeTexture);
     }
 
diff --git a/src/dawn_native/vulkan/TextureVk.cpp b/src/dawn_native/vulkan/TextureVk.cpp
index 6f2fc9e..72a7653 100644
--- a/src/dawn_native/vulkan/TextureVk.cpp
+++ b/src/dawn_native/vulkan/TextureVk.cpp
@@ -460,7 +460,7 @@
         createInfo.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
 
         DAWN_TRY(CheckVkSuccess(
-            device->fn.CreateImage(device->GetVkDevice(), &createInfo, nullptr, &*mHandle),
+            device->fn.CreateImage(device->GetVkDevice(), &createInfo, nullptr, &mHandle),
             "CreateImage"));
 
         // Create the image memory and associate it with the container
@@ -806,7 +806,7 @@
         createInfo.subresourceRange.layerCount = descriptor->arrayLayerCount;
 
         return CheckVkSuccess(
-            device->fn.CreateImageView(device->GetVkDevice(), &createInfo, nullptr, &*mHandle),
+            device->fn.CreateImageView(device->GetVkDevice(), &createInfo, nullptr, &mHandle),
             "CreateImageView");
     }
 
diff --git a/src/dawn_native/vulkan/VulkanBackend.cpp b/src/dawn_native/vulkan/VulkanBackend.cpp
index 2a9361f..ad99a27 100644
--- a/src/dawn_native/vulkan/VulkanBackend.cpp
+++ b/src/dawn_native/vulkan/VulkanBackend.cpp
@@ -42,7 +42,7 @@
     // Explicitly export this function because it uses the "native" type for surfaces while the
     // header as seen in this file uses the wrapped type.
     DAWN_NATIVE_EXPORT DawnSwapChainImplementation
-    CreateNativeSwapChainImpl(WGPUDevice device, ::VkSurfaceKHR surfaceNative) {
+    CreateNativeSwapChainImpl(WGPUDevice device, VkSurfaceKHRNative surfaceNative) {
         Device* backendDevice = reinterpret_cast<Device*>(device);
         VkSurfaceKHR surface = VkSurfaceKHR::CreateFromHandle(surfaceNative);
 
diff --git a/src/dawn_native/vulkan/external_memory/MemoryServiceOpaqueFD.cpp b/src/dawn_native/vulkan/external_memory/MemoryServiceOpaqueFD.cpp
index a8bc181..a6bb2fa 100644
--- a/src/dawn_native/vulkan/external_memory/MemoryServiceOpaqueFD.cpp
+++ b/src/dawn_native/vulkan/external_memory/MemoryServiceOpaqueFD.cpp
@@ -130,7 +130,7 @@
 
         VkDeviceMemory allocatedMemory = VK_NULL_HANDLE;
         DAWN_TRY(CheckVkSuccess(mDevice->fn.AllocateMemory(mDevice->GetVkDevice(), &allocateInfo,
-                                                           nullptr, &*allocatedMemory),
+                                                           nullptr, &allocatedMemory),
                                 "vkAllocateMemory"));
         return allocatedMemory;
     }
@@ -146,7 +146,7 @@
 
         VkImage image;
         DAWN_TRY(CheckVkSuccess(
-            mDevice->fn.CreateImage(mDevice->GetVkDevice(), &createInfo, nullptr, &*image),
+            mDevice->fn.CreateImage(mDevice->GetVkDevice(), &createInfo, nullptr, &image),
             "CreateImage"));
         return image;
     }
diff --git a/src/dawn_native/vulkan/external_semaphore/SemaphoreServiceOpaqueFD.cpp b/src/dawn_native/vulkan/external_semaphore/SemaphoreServiceOpaqueFD.cpp
index 3e1d3f0..ea7bf47 100644
--- a/src/dawn_native/vulkan/external_semaphore/SemaphoreServiceOpaqueFD.cpp
+++ b/src/dawn_native/vulkan/external_semaphore/SemaphoreServiceOpaqueFD.cpp
@@ -72,7 +72,7 @@
         info.flags = 0;
 
         DAWN_TRY(CheckVkSuccess(
-            mDevice->fn.CreateSemaphore(mDevice->GetVkDevice(), &info, nullptr, &*semaphore),
+            mDevice->fn.CreateSemaphore(mDevice->GetVkDevice(), &info, nullptr, &semaphore),
             "vkCreateSemaphore"));
 
         VkImportSemaphoreFdInfoKHR importSemaphoreFdInfo;
@@ -109,7 +109,7 @@
         VkSemaphore signalSemaphore;
         DAWN_TRY(
             CheckVkSuccess(mDevice->fn.CreateSemaphore(mDevice->GetVkDevice(), &semaphoreCreateInfo,
-                                                       nullptr, &*signalSemaphore),
+                                                       nullptr, &signalSemaphore),
                            "vkCreateSemaphore"));
         return signalSemaphore;
     }
diff --git a/src/include/dawn_native/VulkanBackend.h b/src/include/dawn_native/VulkanBackend.h
index 005a655..30dbb05 100644
--- a/src/include/dawn_native/VulkanBackend.h
+++ b/src/include/dawn_native/VulkanBackend.h
@@ -47,8 +47,8 @@
 
     DAWN_NATIVE_EXPORT PFN_vkVoidFunction GetInstanceProcAddr(WGPUDevice device, const char* pName);
 
-    DAWN_NATIVE_EXPORT DawnSwapChainImplementation
-    CreateNativeSwapChainImpl(WGPUDevice device, ::VkSurfaceKHR surface);
+    DAWN_NATIVE_EXPORT DawnSwapChainImplementation CreateNativeSwapChainImpl(WGPUDevice device,
+                                                                             VkSurfaceKHR surface);
     DAWN_NATIVE_EXPORT WGPUTextureFormat
     GetNativeSwapChainPreferredFormat(const DawnSwapChainImplementation* swapChain);
 
diff --git a/src/tests/DawnTest.h b/src/tests/DawnTest.h
index 9cdfd3e..677f670 100644
--- a/src/tests/DawnTest.h
+++ b/src/tests/DawnTest.h
@@ -32,30 +32,30 @@
 // them.
 #define EXPECT_BUFFER_U32_EQ(expected, buffer, offset)                         \
     AddBufferExpectation(__FILE__, __LINE__, buffer, offset, sizeof(uint32_t), \
-                         new ::detail::ExpectEq<uint32_t>(expected))
+                         new detail::ExpectEq<uint32_t>(expected))
 
 #define EXPECT_BUFFER_U32_RANGE_EQ(expected, buffer, offset, count)                    \
     AddBufferExpectation(__FILE__, __LINE__, buffer, offset, sizeof(uint32_t) * count, \
-                         new ::detail::ExpectEq<uint32_t>(expected, count))
+                         new detail::ExpectEq<uint32_t>(expected, count))
 
 // Test a pixel of the mip level 0 of a 2D texture.
 #define EXPECT_PIXEL_RGBA8_EQ(expected, texture, x, y)                                  \
     AddTextureExpectation(__FILE__, __LINE__, texture, x, y, 1, 1, 0, 0, sizeof(RGBA8), \
-                          new ::detail::ExpectEq<RGBA8>(expected))
+                          new detail::ExpectEq<RGBA8>(expected))
 
 #define EXPECT_TEXTURE_RGBA8_EQ(expected, texture, x, y, width, height, level, slice)     \
     AddTextureExpectation(__FILE__, __LINE__, texture, x, y, width, height, level, slice, \
                           sizeof(RGBA8),                                                  \
-                          new ::detail::ExpectEq<RGBA8>(expected, (width) * (height)))
+                          new detail::ExpectEq<RGBA8>(expected, (width) * (height)))
 
 #define EXPECT_PIXEL_FLOAT_EQ(expected, texture, x, y)                                  \
     AddTextureExpectation(__FILE__, __LINE__, texture, x, y, 1, 1, 0, 0, sizeof(float), \
-                          new ::detail::ExpectEq<float>(expected))
+                          new detail::ExpectEq<float>(expected))
 
 #define EXPECT_TEXTURE_FLOAT_EQ(expected, texture, x, y, width, height, level, slice)     \
     AddTextureExpectation(__FILE__, __LINE__, texture, x, y, width, height, level, slice, \
                           sizeof(float),                                                  \
-                          new ::detail::ExpectEq<float>(expected, (width) * (height)))
+                          new detail::ExpectEq<float>(expected, (width) * (height)))
 
 // Should only be used to test validation of function that can't be tested by regular validation
 // tests;
diff --git a/src/tests/white_box/VulkanImageWrappingTests.cpp b/src/tests/white_box/VulkanImageWrappingTests.cpp
index 1db96439..de7d8fe 100644
--- a/src/tests/white_box/VulkanImageWrappingTests.cpp
+++ b/src/tests/white_box/VulkanImageWrappingTests.cpp
@@ -25,1008 +25,993 @@
 #include "utils/SystemUtils.h"
 #include "utils/WGPUHelpers.h"
 
-namespace dawn_native { namespace vulkan {
+namespace {
 
-    namespace {
-
-        class VulkanImageWrappingTestBase : public DawnTest {
-          public:
-            void TestSetUp() override {
-                if (UsesWire()) {
-                    return;
-                }
-
-                deviceVk = reinterpret_cast<dawn_native::vulkan::Device*>(device.Get());
+    class VulkanImageWrappingTestBase : public DawnTest {
+      public:
+        void TestSetUp() override {
+            if (UsesWire()) {
+                return;
             }
 
-            // Creates a VkImage with external memory
-            ::VkResult CreateImage(dawn_native::vulkan::Device* deviceVk,
+            deviceVk = reinterpret_cast<dawn_native::vulkan::Device*>(device.Get());
+        }
+
+        // Creates a VkImage with external memory
+        VkResult CreateImage(dawn_native::vulkan::Device* deviceVk,
+                             uint32_t width,
+                             uint32_t height,
+                             VkFormat format,
+                             VkImage* image) {
+            VkExternalMemoryImageCreateInfoKHR externalInfo;
+            externalInfo.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO_KHR;
+            externalInfo.pNext = nullptr;
+            externalInfo.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR;
+
+            auto usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
+                         VK_IMAGE_USAGE_TRANSFER_DST_BIT;
+
+            VkImageCreateInfo createInfo;
+            createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
+            createInfo.pNext = &externalInfo;
+            createInfo.flags = VK_IMAGE_CREATE_ALIAS_BIT_KHR;
+            createInfo.imageType = VK_IMAGE_TYPE_2D;
+            createInfo.format = format;
+            createInfo.extent = {width, height, 1};
+            createInfo.mipLevels = 1;
+            createInfo.arrayLayers = 1;
+            createInfo.samples = VK_SAMPLE_COUNT_1_BIT;
+            createInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
+            createInfo.usage = usage;
+            createInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
+            createInfo.queueFamilyIndexCount = 0;
+            createInfo.pQueueFamilyIndices = nullptr;
+            createInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+
+            return deviceVk->fn.CreateImage(deviceVk->GetVkDevice(), &createInfo, nullptr, image);
+        }
+
+        // Allocates memory for an image
+        VkResult AllocateMemory(dawn_native::vulkan::Device* deviceVk,
+                                VkImage handle,
+                                VkDeviceMemory* allocation,
+                                VkDeviceSize* allocationSize,
+                                uint32_t* memoryTypeIndex) {
+            // Create the image memory and associate it with the container
+            VkMemoryRequirements requirements;
+            deviceVk->fn.GetImageMemoryRequirements(deviceVk->GetVkDevice(), handle, &requirements);
+
+            // Import memory from file descriptor
+            VkExportMemoryAllocateInfoKHR externalInfo;
+            externalInfo.sType = VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_KHR;
+            externalInfo.pNext = nullptr;
+            externalInfo.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR;
+
+            int bestType = deviceVk->GetResourceMemoryAllocatorForTesting()->FindBestTypeIndex(
+                requirements, false);
+            VkMemoryAllocateInfo allocateInfo;
+            allocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
+            allocateInfo.pNext = &externalInfo;
+            allocateInfo.allocationSize = requirements.size;
+            allocateInfo.memoryTypeIndex = static_cast<uint32_t>(bestType);
+
+            *allocationSize = allocateInfo.allocationSize;
+            *memoryTypeIndex = allocateInfo.memoryTypeIndex;
+
+            return deviceVk->fn.AllocateMemory(deviceVk->GetVkDevice(), &allocateInfo, nullptr,
+                                               allocation);
+        }
+
+        // Binds memory to an image
+        VkResult BindMemory(dawn_native::vulkan::Device* deviceVk,
+                            VkImage handle,
+                            VkDeviceMemory* memory) {
+            return deviceVk->fn.BindImageMemory(deviceVk->GetVkDevice(), handle, *memory, 0);
+        }
+
+        // Extracts a file descriptor representing memory on a device
+        int GetMemoryFd(dawn_native::vulkan::Device* deviceVk, VkDeviceMemory memory) {
+            VkMemoryGetFdInfoKHR getFdInfo;
+            getFdInfo.sType = VK_STRUCTURE_TYPE_MEMORY_GET_FD_INFO_KHR;
+            getFdInfo.pNext = nullptr;
+            getFdInfo.memory = memory;
+            getFdInfo.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR;
+
+            int memoryFd = -1;
+            deviceVk->fn.GetMemoryFdKHR(deviceVk->GetVkDevice(), &getFdInfo, &memoryFd);
+
+            EXPECT_GE(memoryFd, 0) << "Failed to get file descriptor for external memory";
+            return memoryFd;
+        }
+
+        // Prepares and exports memory for an image on a given device
+        void CreateBindExportImage(dawn_native::vulkan::Device* deviceVk,
                                    uint32_t width,
                                    uint32_t height,
                                    VkFormat format,
-                                   VkImage* image) {
-                VkExternalMemoryImageCreateInfoKHR externalInfo;
-                externalInfo.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO_KHR;
-                externalInfo.pNext = nullptr;
-                externalInfo.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR;
+                                   VkImage* handle,
+                                   VkDeviceMemory* allocation,
+                                   VkDeviceSize* allocationSize,
+                                   uint32_t* memoryTypeIndex,
+                                   int* memoryFd) {
+            VkResult result = CreateImage(deviceVk, width, height, format, handle);
+            EXPECT_EQ(result, VK_SUCCESS) << "Failed to create external image";
 
-                auto usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
-                             VK_IMAGE_USAGE_TRANSFER_DST_BIT;
+            VkResult resultBool =
+                AllocateMemory(deviceVk, *handle, allocation, allocationSize, memoryTypeIndex);
+            EXPECT_EQ(resultBool, VK_SUCCESS) << "Failed to allocate external memory";
 
-                VkImageCreateInfo createInfo;
-                createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
-                createInfo.pNext = &externalInfo;
-                createInfo.flags = VK_IMAGE_CREATE_ALIAS_BIT_KHR;
-                createInfo.imageType = VK_IMAGE_TYPE_2D;
-                createInfo.format = format;
-                createInfo.extent = {width, height, 1};
-                createInfo.mipLevels = 1;
-                createInfo.arrayLayers = 1;
-                createInfo.samples = VK_SAMPLE_COUNT_1_BIT;
-                createInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
-                createInfo.usage = usage;
-                createInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
-                createInfo.queueFamilyIndexCount = 0;
-                createInfo.pQueueFamilyIndices = nullptr;
-                createInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+            result = BindMemory(deviceVk, *handle, allocation);
+            EXPECT_EQ(result, VK_SUCCESS) << "Failed to bind image memory";
 
-                return deviceVk->fn.CreateImage(deviceVk->GetVkDevice(), &createInfo, nullptr,
-                                                &**image);
-            }
-
-            // Allocates memory for an image
-            ::VkResult AllocateMemory(dawn_native::vulkan::Device* deviceVk,
-                                      VkImage handle,
-                                      VkDeviceMemory* allocation,
-                                      VkDeviceSize* allocationSize,
-                                      uint32_t* memoryTypeIndex) {
-                // Create the image memory and associate it with the container
-                VkMemoryRequirements requirements;
-                deviceVk->fn.GetImageMemoryRequirements(deviceVk->GetVkDevice(), handle,
-                                                        &requirements);
-
-                // Import memory from file descriptor
-                VkExportMemoryAllocateInfoKHR externalInfo;
-                externalInfo.sType = VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_KHR;
-                externalInfo.pNext = nullptr;
-                externalInfo.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR;
-
-                int bestType = deviceVk->GetResourceMemoryAllocatorForTesting()->FindBestTypeIndex(
-                    requirements, false);
-                VkMemoryAllocateInfo allocateInfo;
-                allocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
-                allocateInfo.pNext = &externalInfo;
-                allocateInfo.allocationSize = requirements.size;
-                allocateInfo.memoryTypeIndex = static_cast<uint32_t>(bestType);
-
-                *allocationSize = allocateInfo.allocationSize;
-                *memoryTypeIndex = allocateInfo.memoryTypeIndex;
-
-                return deviceVk->fn.AllocateMemory(deviceVk->GetVkDevice(), &allocateInfo, nullptr,
-                                                   &**allocation);
-            }
-
-            // Binds memory to an image
-            ::VkResult BindMemory(dawn_native::vulkan::Device* deviceVk,
-                                  VkImage handle,
-                                  VkDeviceMemory* memory) {
-                return deviceVk->fn.BindImageMemory(deviceVk->GetVkDevice(), handle, *memory, 0);
-            }
-
-            // Extracts a file descriptor representing memory on a device
-            int GetMemoryFd(dawn_native::vulkan::Device* deviceVk, VkDeviceMemory memory) {
-                VkMemoryGetFdInfoKHR getFdInfo;
-                getFdInfo.sType = VK_STRUCTURE_TYPE_MEMORY_GET_FD_INFO_KHR;
-                getFdInfo.pNext = nullptr;
-                getFdInfo.memory = memory;
-                getFdInfo.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR;
-
-                int memoryFd = -1;
-                deviceVk->fn.GetMemoryFdKHR(deviceVk->GetVkDevice(), &getFdInfo, &memoryFd);
-
-                EXPECT_GE(memoryFd, 0) << "Failed to get file descriptor for external memory";
-                return memoryFd;
-            }
-
-            // Prepares and exports memory for an image on a given device
-            void CreateBindExportImage(dawn_native::vulkan::Device* deviceVk,
-                                       uint32_t width,
-                                       uint32_t height,
-                                       VkFormat format,
-                                       VkImage* handle,
-                                       VkDeviceMemory* allocation,
-                                       VkDeviceSize* allocationSize,
-                                       uint32_t* memoryTypeIndex,
-                                       int* memoryFd) {
-                ::VkResult result = CreateImage(deviceVk, width, height, format, handle);
-                EXPECT_EQ(result, VK_SUCCESS) << "Failed to create external image";
-
-                ::VkResult resultBool =
-                    AllocateMemory(deviceVk, *handle, allocation, allocationSize, memoryTypeIndex);
-                EXPECT_EQ(resultBool, VK_SUCCESS) << "Failed to allocate external memory";
-
-                result = BindMemory(deviceVk, *handle, allocation);
-                EXPECT_EQ(result, VK_SUCCESS) << "Failed to bind image memory";
-
-                *memoryFd = GetMemoryFd(deviceVk, *allocation);
-            }
-
-            // Wraps a vulkan image from external memory
-            wgpu::Texture WrapVulkanImage(wgpu::Device device,
-                                          const wgpu::TextureDescriptor* textureDescriptor,
-                                          int memoryFd,
-                                          VkDeviceSize allocationSize,
-                                          uint32_t memoryTypeIndex,
-                                          std::vector<int> waitFDs,
-                                          bool isCleared = true,
-                                          bool expectValid = true) {
-                dawn_native::vulkan::ExternalImageDescriptorOpaqueFD descriptor;
-                descriptor.cTextureDescriptor =
-                    reinterpret_cast<const WGPUTextureDescriptor*>(textureDescriptor);
-                descriptor.isCleared = isCleared;
-                descriptor.allocationSize = allocationSize;
-                descriptor.memoryTypeIndex = memoryTypeIndex;
-                descriptor.memoryFD = memoryFd;
-                descriptor.waitFDs = waitFDs;
-
-                WGPUTexture texture =
-                    dawn_native::vulkan::WrapVulkanImage(device.Get(), &descriptor);
-
-                if (expectValid) {
-                    EXPECT_NE(texture, nullptr) << "Failed to wrap image, are external memory / "
-                                                   "semaphore extensions supported?";
-                } else {
-                    EXPECT_EQ(texture, nullptr);
-                }
-
-                return wgpu::Texture::Acquire(texture);
-            }
-
-            // Exports the signal from a wrapped texture and ignores it
-            // We have to export the signal before destroying the wrapped texture else it's an
-            // assertion failure
-            void IgnoreSignalSemaphore(wgpu::Device device, wgpu::Texture wrappedTexture) {
-                int fd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(device.Get(),
-                                                                            wrappedTexture.Get());
-                ASSERT_NE(fd, -1);
-                close(fd);
-            }
-
-          protected:
-            dawn_native::vulkan::Device* deviceVk;
-        };
-
-    }  // anonymous namespace
-
-    class VulkanImageWrappingValidationTests : public VulkanImageWrappingTestBase {
-      public:
-        void TestSetUp() override {
-            VulkanImageWrappingTestBase::TestSetUp();
-            if (UsesWire()) {
-                return;
-            }
-
-            CreateBindExportImage(deviceVk, 1, 1, VK_FORMAT_R8G8B8A8_UNORM, &defaultImage,
-                                  &defaultAllocation, &defaultAllocationSize,
-                                  &defaultMemoryTypeIndex, &defaultFd);
-            defaultDescriptor.dimension = wgpu::TextureDimension::e2D;
-            defaultDescriptor.format = wgpu::TextureFormat::RGBA8Unorm;
-            defaultDescriptor.size = {1, 1, 1};
-            defaultDescriptor.sampleCount = 1;
-            defaultDescriptor.arrayLayerCount = 1;
-            defaultDescriptor.mipLevelCount = 1;
-            defaultDescriptor.usage = wgpu::TextureUsage::OutputAttachment |
-                                      wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst;
+            *memoryFd = GetMemoryFd(deviceVk, *allocation);
         }
 
-        void TearDown() override {
-            if (UsesWire()) {
-                VulkanImageWrappingTestBase::TearDown();
-                return;
+        // Wraps a vulkan image from external memory
+        wgpu::Texture WrapVulkanImage(wgpu::Device device,
+                                      const wgpu::TextureDescriptor* textureDescriptor,
+                                      int memoryFd,
+                                      VkDeviceSize allocationSize,
+                                      uint32_t memoryTypeIndex,
+                                      std::vector<int> waitFDs,
+                                      bool isCleared = true,
+                                      bool expectValid = true) {
+            dawn_native::vulkan::ExternalImageDescriptorOpaqueFD descriptor;
+            descriptor.cTextureDescriptor =
+                reinterpret_cast<const WGPUTextureDescriptor*>(textureDescriptor);
+            descriptor.isCleared = isCleared;
+            descriptor.allocationSize = allocationSize;
+            descriptor.memoryTypeIndex = memoryTypeIndex;
+            descriptor.memoryFD = memoryFd;
+            descriptor.waitFDs = waitFDs;
+
+            WGPUTexture texture = dawn_native::vulkan::WrapVulkanImage(device.Get(), &descriptor);
+
+            if (expectValid) {
+                EXPECT_NE(texture, nullptr) << "Failed to wrap image, are external memory / "
+                                               "semaphore extensions supported?";
+            } else {
+                EXPECT_EQ(texture, nullptr);
             }
 
-            deviceVk->GetFencedDeleter()->DeleteWhenUnused(defaultImage);
-            deviceVk->GetFencedDeleter()->DeleteWhenUnused(defaultAllocation);
-            VulkanImageWrappingTestBase::TearDown();
+            return wgpu::Texture::Acquire(texture);
+        }
+
+        // Exports the signal from a wrapped texture and ignores it
+        // We have to export the signal before destroying the wrapped texture else it's an assertion
+        // failure
+        void IgnoreSignalSemaphore(wgpu::Device device, wgpu::Texture wrappedTexture) {
+            int fd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(device.Get(),
+                                                                        wrappedTexture.Get());
+            ASSERT_NE(fd, -1);
+            close(fd);
         }
 
       protected:
-        wgpu::TextureDescriptor defaultDescriptor;
-        VkImage defaultImage;
-        VkDeviceMemory defaultAllocation;
-        VkDeviceSize defaultAllocationSize;
-        uint32_t defaultMemoryTypeIndex;
-        int defaultFd;
+        dawn_native::vulkan::Device* deviceVk;
     };
 
-    // Test no error occurs if the import is valid
-    TEST_P(VulkanImageWrappingValidationTests, SuccessfulImport) {
-        DAWN_SKIP_TEST_IF(UsesWire());
-        wgpu::Texture texture =
-            WrapVulkanImage(device, &defaultDescriptor, defaultFd, defaultAllocationSize,
-                            defaultMemoryTypeIndex, {}, true, true);
-        EXPECT_NE(texture.Get(), nullptr);
-        IgnoreSignalSemaphore(device, texture);
-    }
+}  // anonymous namespace
 
-    // Test an error occurs if the texture descriptor is missing
-    TEST_P(VulkanImageWrappingValidationTests, MissingTextureDescriptor) {
-        DAWN_SKIP_TEST_IF(UsesWire());
-        ASSERT_DEVICE_ERROR(wgpu::Texture texture =
-                                WrapVulkanImage(device, nullptr, defaultFd, defaultAllocationSize,
-                                                defaultMemoryTypeIndex, {}, true, false));
-        EXPECT_EQ(texture.Get(), nullptr);
-    }
-
-    // Test an error occurs if the texture descriptor is invalid
-    TEST_P(VulkanImageWrappingValidationTests, InvalidTextureDescriptor) {
-        DAWN_SKIP_TEST_IF(UsesWire());
-        wgpu::ChainedStruct chainedDescriptor;
-        defaultDescriptor.nextInChain = &chainedDescriptor;
-
-        ASSERT_DEVICE_ERROR(wgpu::Texture texture = WrapVulkanImage(
-                                device, &defaultDescriptor, defaultFd, defaultAllocationSize,
-                                defaultMemoryTypeIndex, {}, true, false));
-        EXPECT_EQ(texture.Get(), nullptr);
-    }
-
-    // Test an error occurs if the descriptor dimension isn't 2D
-    TEST_P(VulkanImageWrappingValidationTests, InvalidTextureDimension) {
-        DAWN_SKIP_TEST_IF(UsesWire());
-        defaultDescriptor.dimension = wgpu::TextureDimension::e1D;
-
-        ASSERT_DEVICE_ERROR(wgpu::Texture texture = WrapVulkanImage(
-                                device, &defaultDescriptor, defaultFd, defaultAllocationSize,
-                                defaultMemoryTypeIndex, {}, true, false));
-        EXPECT_EQ(texture.Get(), nullptr);
-    }
-
-    // Test an error occurs if the descriptor mip level count isn't 1
-    TEST_P(VulkanImageWrappingValidationTests, InvalidMipLevelCount) {
-        DAWN_SKIP_TEST_IF(UsesWire());
-        defaultDescriptor.mipLevelCount = 2;
-
-        ASSERT_DEVICE_ERROR(wgpu::Texture texture = WrapVulkanImage(
-                                device, &defaultDescriptor, defaultFd, defaultAllocationSize,
-                                defaultMemoryTypeIndex, {}, true, false));
-        EXPECT_EQ(texture.Get(), nullptr);
-    }
-
-    // Test an error occurs if the descriptor array layer count isn't 1
-    TEST_P(VulkanImageWrappingValidationTests, InvalidArrayLayerCount) {
-        DAWN_SKIP_TEST_IF(UsesWire());
-        defaultDescriptor.arrayLayerCount = 2;
-
-        ASSERT_DEVICE_ERROR(wgpu::Texture texture = WrapVulkanImage(
-                                device, &defaultDescriptor, defaultFd, defaultAllocationSize,
-                                defaultMemoryTypeIndex, {}, true, false));
-        EXPECT_EQ(texture.Get(), nullptr);
-    }
-
-    // Test an error occurs if the descriptor sample count isn't 1
-    TEST_P(VulkanImageWrappingValidationTests, InvalidSampleCount) {
-        DAWN_SKIP_TEST_IF(UsesWire());
-        defaultDescriptor.sampleCount = 4;
-
-        ASSERT_DEVICE_ERROR(wgpu::Texture texture = WrapVulkanImage(
-                                device, &defaultDescriptor, defaultFd, defaultAllocationSize,
-                                defaultMemoryTypeIndex, {}, true, false));
-        EXPECT_EQ(texture.Get(), nullptr);
-    }
-
-    // Test an error occurs if we try to export the signal semaphore twice
-    TEST_P(VulkanImageWrappingValidationTests, DoubleSignalSemaphoreExport) {
-        DAWN_SKIP_TEST_IF(UsesWire());
-        wgpu::Texture texture =
-            WrapVulkanImage(device, &defaultDescriptor, defaultFd, defaultAllocationSize,
-                            defaultMemoryTypeIndex, {}, true, true);
-        ASSERT_NE(texture.Get(), nullptr);
-        IgnoreSignalSemaphore(device, texture);
-        ASSERT_DEVICE_ERROR(int fd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(
-                                device.Get(), texture.Get()));
-        ASSERT_EQ(fd, -1);
-    }
-
-    // Test an error occurs if we try to export the signal semaphore from a normal texture
-    TEST_P(VulkanImageWrappingValidationTests, NormalTextureSignalSemaphoreExport) {
-        DAWN_SKIP_TEST_IF(UsesWire());
-        wgpu::Texture texture = device.CreateTexture(&defaultDescriptor);
-        ASSERT_NE(texture.Get(), nullptr);
-        ASSERT_DEVICE_ERROR(int fd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(
-                                device.Get(), texture.Get()));
-        ASSERT_EQ(fd, -1);
-    }
-
-    // Test an error occurs if we try to export the signal semaphore from a destroyed texture
-    TEST_P(VulkanImageWrappingValidationTests, DestroyedTextureSignalSemaphoreExport) {
-        DAWN_SKIP_TEST_IF(UsesWire());
-        wgpu::Texture texture = device.CreateTexture(&defaultDescriptor);
-        ASSERT_NE(texture.Get(), nullptr);
-        texture.Destroy();
-        ASSERT_DEVICE_ERROR(int fd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(
-                                device.Get(), texture.Get()));
-        ASSERT_EQ(fd, -1);
-    }
-
-    // Fixture to test using external memory textures through different usages.
-    // These tests are skipped if the harness is using the wire.
-    class VulkanImageWrappingUsageTests : public VulkanImageWrappingTestBase {
-      public:
-        void TestSetUp() override {
-            VulkanImageWrappingTestBase::TestSetUp();
-            if (UsesWire()) {
-                return;
-            }
-
-            // Create another device based on the original
-            backendAdapter =
-                reinterpret_cast<dawn_native::vulkan::Adapter*>(deviceVk->GetAdapter());
-            deviceDescriptor.forceEnabledToggles = GetParam().forceEnabledWorkarounds;
-            deviceDescriptor.forceDisabledToggles = GetParam().forceDisabledWorkarounds;
-
-            secondDeviceVk = reinterpret_cast<dawn_native::vulkan::Device*>(
-                backendAdapter->CreateDevice(&deviceDescriptor));
-            secondDevice = wgpu::Device::Acquire(reinterpret_cast<WGPUDevice>(secondDeviceVk));
-
-            CreateBindExportImage(deviceVk, 1, 1, VK_FORMAT_R8G8B8A8_UNORM, &defaultImage,
-                                  &defaultAllocation, &defaultAllocationSize,
-                                  &defaultMemoryTypeIndex, &defaultFd);
-            defaultDescriptor.dimension = wgpu::TextureDimension::e2D;
-            defaultDescriptor.format = wgpu::TextureFormat::RGBA8Unorm;
-            defaultDescriptor.size = {1, 1, 1};
-            defaultDescriptor.sampleCount = 1;
-            defaultDescriptor.arrayLayerCount = 1;
-            defaultDescriptor.mipLevelCount = 1;
-            defaultDescriptor.usage = wgpu::TextureUsage::OutputAttachment |
-                                      wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst;
+class VulkanImageWrappingValidationTests : public VulkanImageWrappingTestBase {
+  public:
+    void TestSetUp() override {
+        VulkanImageWrappingTestBase::TestSetUp();
+        if (UsesWire()) {
+            return;
         }
 
-        void TearDown() override {
-            if (UsesWire()) {
-                VulkanImageWrappingTestBase::TearDown();
-                return;
-            }
+        CreateBindExportImage(deviceVk, 1, 1, VK_FORMAT_R8G8B8A8_UNORM, &defaultImage,
+                              &defaultAllocation, &defaultAllocationSize, &defaultMemoryTypeIndex,
+                              &defaultFd);
+        defaultDescriptor.dimension = wgpu::TextureDimension::e2D;
+        defaultDescriptor.format = wgpu::TextureFormat::RGBA8Unorm;
+        defaultDescriptor.size = {1, 1, 1};
+        defaultDescriptor.sampleCount = 1;
+        defaultDescriptor.arrayLayerCount = 1;
+        defaultDescriptor.mipLevelCount = 1;
+        defaultDescriptor.usage = wgpu::TextureUsage::OutputAttachment |
+                                  wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst;
+    }
 
-            deviceVk->GetFencedDeleter()->DeleteWhenUnused(defaultImage);
-            deviceVk->GetFencedDeleter()->DeleteWhenUnused(defaultAllocation);
+    void TearDown() override {
+        if (UsesWire()) {
             VulkanImageWrappingTestBase::TearDown();
+            return;
         }
 
-      protected:
-        wgpu::Device secondDevice;
-        dawn_native::vulkan::Device* secondDeviceVk;
+        deviceVk->GetFencedDeleter()->DeleteWhenUnused(defaultImage);
+        deviceVk->GetFencedDeleter()->DeleteWhenUnused(defaultAllocation);
+        VulkanImageWrappingTestBase::TearDown();
+    }
 
-        dawn_native::vulkan::Adapter* backendAdapter;
-        dawn_native::DeviceDescriptor deviceDescriptor;
+  protected:
+    wgpu::TextureDescriptor defaultDescriptor;
+    VkImage defaultImage;
+    VkDeviceMemory defaultAllocation;
+    VkDeviceSize defaultAllocationSize;
+    uint32_t defaultMemoryTypeIndex;
+    int defaultFd;
+};
 
-        wgpu::TextureDescriptor defaultDescriptor;
-        VkImage defaultImage;
-        VkDeviceMemory defaultAllocation;
-        VkDeviceSize defaultAllocationSize;
-        uint32_t defaultMemoryTypeIndex;
-        int defaultFd;
+// Test no error occurs if the import is valid
+TEST_P(VulkanImageWrappingValidationTests, SuccessfulImport) {
+    DAWN_SKIP_TEST_IF(UsesWire());
+    wgpu::Texture texture =
+        WrapVulkanImage(device, &defaultDescriptor, defaultFd, defaultAllocationSize,
+                        defaultMemoryTypeIndex, {}, true, true);
+    EXPECT_NE(texture.Get(), nullptr);
+    IgnoreSignalSemaphore(device, texture);
+}
 
-        // Clear a texture on a given device
-        void ClearImage(wgpu::Device device, wgpu::Texture wrappedTexture, wgpu::Color clearColor) {
-            wgpu::TextureView wrappedView = wrappedTexture.CreateView();
+// Test an error occurs if the texture descriptor is missing
+TEST_P(VulkanImageWrappingValidationTests, MissingTextureDescriptor) {
+    DAWN_SKIP_TEST_IF(UsesWire());
+    ASSERT_DEVICE_ERROR(wgpu::Texture texture =
+                            WrapVulkanImage(device, nullptr, defaultFd, defaultAllocationSize,
+                                            defaultMemoryTypeIndex, {}, true, false));
+    EXPECT_EQ(texture.Get(), nullptr);
+}
 
-            // Submit a clear operation
-            utils::ComboRenderPassDescriptor renderPassDescriptor({wrappedView}, {});
-            renderPassDescriptor.cColorAttachments[0].clearColor = clearColor;
+// Test an error occurs if the texture descriptor is invalid
+TEST_P(VulkanImageWrappingValidationTests, InvalidTextureDescriptor) {
+    DAWN_SKIP_TEST_IF(UsesWire());
+    wgpu::ChainedStruct chainedDescriptor;
+    defaultDescriptor.nextInChain = &chainedDescriptor;
 
-            wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
-            wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDescriptor);
-            pass.EndPass();
+    ASSERT_DEVICE_ERROR(wgpu::Texture texture = WrapVulkanImage(
+                            device, &defaultDescriptor, defaultFd, defaultAllocationSize,
+                            defaultMemoryTypeIndex, {}, true, false));
+    EXPECT_EQ(texture.Get(), nullptr);
+}
 
-            wgpu::CommandBuffer commands = encoder.Finish();
+// Test an error occurs if the descriptor dimension isn't 2D
+TEST_P(VulkanImageWrappingValidationTests, InvalidTextureDimension) {
+    DAWN_SKIP_TEST_IF(UsesWire());
+    defaultDescriptor.dimension = wgpu::TextureDimension::e1D;
 
-            wgpu::Queue queue = device.CreateQueue();
-            queue.Submit(1, &commands);
+    ASSERT_DEVICE_ERROR(wgpu::Texture texture = WrapVulkanImage(
+                            device, &defaultDescriptor, defaultFd, defaultAllocationSize,
+                            defaultMemoryTypeIndex, {}, true, false));
+    EXPECT_EQ(texture.Get(), nullptr);
+}
+
+// Test an error occurs if the descriptor mip level count isn't 1
+TEST_P(VulkanImageWrappingValidationTests, InvalidMipLevelCount) {
+    DAWN_SKIP_TEST_IF(UsesWire());
+    defaultDescriptor.mipLevelCount = 2;
+
+    ASSERT_DEVICE_ERROR(wgpu::Texture texture = WrapVulkanImage(
+                            device, &defaultDescriptor, defaultFd, defaultAllocationSize,
+                            defaultMemoryTypeIndex, {}, true, false));
+    EXPECT_EQ(texture.Get(), nullptr);
+}
+
+// Test an error occurs if the descriptor array layer count isn't 1
+TEST_P(VulkanImageWrappingValidationTests, InvalidArrayLayerCount) {
+    DAWN_SKIP_TEST_IF(UsesWire());
+    defaultDescriptor.arrayLayerCount = 2;
+
+    ASSERT_DEVICE_ERROR(wgpu::Texture texture = WrapVulkanImage(
+                            device, &defaultDescriptor, defaultFd, defaultAllocationSize,
+                            defaultMemoryTypeIndex, {}, true, false));
+    EXPECT_EQ(texture.Get(), nullptr);
+}
+
+// Test an error occurs if the descriptor sample count isn't 1
+TEST_P(VulkanImageWrappingValidationTests, InvalidSampleCount) {
+    DAWN_SKIP_TEST_IF(UsesWire());
+    defaultDescriptor.sampleCount = 4;
+
+    ASSERT_DEVICE_ERROR(wgpu::Texture texture = WrapVulkanImage(
+                            device, &defaultDescriptor, defaultFd, defaultAllocationSize,
+                            defaultMemoryTypeIndex, {}, true, false));
+    EXPECT_EQ(texture.Get(), nullptr);
+}
+
+// Test an error occurs if we try to export the signal semaphore twice
+TEST_P(VulkanImageWrappingValidationTests, DoubleSignalSemaphoreExport) {
+    DAWN_SKIP_TEST_IF(UsesWire());
+    wgpu::Texture texture =
+        WrapVulkanImage(device, &defaultDescriptor, defaultFd, defaultAllocationSize,
+                        defaultMemoryTypeIndex, {}, true, true);
+    ASSERT_NE(texture.Get(), nullptr);
+    IgnoreSignalSemaphore(device, texture);
+    ASSERT_DEVICE_ERROR(
+        int fd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(device.Get(), texture.Get()));
+    ASSERT_EQ(fd, -1);
+}
+
+// Test an error occurs if we try to export the signal semaphore from a normal texture
+TEST_P(VulkanImageWrappingValidationTests, NormalTextureSignalSemaphoreExport) {
+    DAWN_SKIP_TEST_IF(UsesWire());
+    wgpu::Texture texture = device.CreateTexture(&defaultDescriptor);
+    ASSERT_NE(texture.Get(), nullptr);
+    ASSERT_DEVICE_ERROR(
+        int fd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(device.Get(), texture.Get()));
+    ASSERT_EQ(fd, -1);
+}
+
+// Test an error occurs if we try to export the signal semaphore from a destroyed texture
+TEST_P(VulkanImageWrappingValidationTests, DestroyedTextureSignalSemaphoreExport) {
+    DAWN_SKIP_TEST_IF(UsesWire());
+    wgpu::Texture texture = device.CreateTexture(&defaultDescriptor);
+    ASSERT_NE(texture.Get(), nullptr);
+    texture.Destroy();
+    ASSERT_DEVICE_ERROR(
+        int fd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(device.Get(), texture.Get()));
+    ASSERT_EQ(fd, -1);
+}
+
+// Fixture to test using external memory textures through different usages.
+// These tests are skipped if the harness is using the wire.
+class VulkanImageWrappingUsageTests : public VulkanImageWrappingTestBase {
+  public:
+    void TestSetUp() override {
+        VulkanImageWrappingTestBase::TestSetUp();
+        if (UsesWire()) {
+            return;
         }
 
-        // Submits a 1x1x1 copy from source to destination
-        void SimpleCopyTextureToTexture(wgpu::Device device,
-                                        wgpu::Queue queue,
-                                        wgpu::Texture source,
-                                        wgpu::Texture destination) {
-            wgpu::TextureCopyView copySrc;
-            copySrc.texture = source;
-            copySrc.mipLevel = 0;
-            copySrc.arrayLayer = 0;
-            copySrc.origin = {0, 0, 0};
+        // Create another device based on the original
+        backendAdapter = reinterpret_cast<dawn_native::vulkan::Adapter*>(deviceVk->GetAdapter());
+        deviceDescriptor.forceEnabledToggles = GetParam().forceEnabledWorkarounds;
+        deviceDescriptor.forceDisabledToggles = GetParam().forceDisabledWorkarounds;
 
-            wgpu::TextureCopyView copyDst;
-            copyDst.texture = destination;
-            copyDst.mipLevel = 0;
-            copyDst.arrayLayer = 0;
-            copyDst.origin = {0, 0, 0};
+        secondDeviceVk = reinterpret_cast<dawn_native::vulkan::Device*>(
+            backendAdapter->CreateDevice(&deviceDescriptor));
+        secondDevice = wgpu::Device::Acquire(reinterpret_cast<WGPUDevice>(secondDeviceVk));
 
-            wgpu::Extent3D copySize = {1, 1, 1};
+        CreateBindExportImage(deviceVk, 1, 1, VK_FORMAT_R8G8B8A8_UNORM, &defaultImage,
+                              &defaultAllocation, &defaultAllocationSize, &defaultMemoryTypeIndex,
+                              &defaultFd);
+        defaultDescriptor.dimension = wgpu::TextureDimension::e2D;
+        defaultDescriptor.format = wgpu::TextureFormat::RGBA8Unorm;
+        defaultDescriptor.size = {1, 1, 1};
+        defaultDescriptor.sampleCount = 1;
+        defaultDescriptor.arrayLayerCount = 1;
+        defaultDescriptor.mipLevelCount = 1;
+        defaultDescriptor.usage = wgpu::TextureUsage::OutputAttachment |
+                                  wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst;
+    }
 
-            wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
-            encoder.CopyTextureToTexture(&copySrc, &copyDst, &copySize);
-            wgpu::CommandBuffer commands = encoder.Finish();
-
-            queue.Submit(1, &commands);
+    void TearDown() override {
+        if (UsesWire()) {
+            VulkanImageWrappingTestBase::TearDown();
+            return;
         }
-    };
 
-    // Clear an image in |secondDevice|
-    // Verify clear color is visible in |device|
-    TEST_P(VulkanImageWrappingUsageTests, ClearImageAcrossDevices) {
-        DAWN_SKIP_TEST_IF(UsesWire());
-
-        // Import the image on |secondDevice|
-        wgpu::Texture wrappedTexture =
-            WrapVulkanImage(secondDevice, &defaultDescriptor, defaultFd, defaultAllocationSize,
-                            defaultMemoryTypeIndex, {});
-
-        // Clear |wrappedTexture| on |secondDevice|
-        ClearImage(secondDevice, wrappedTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f});
-
-        int signalFd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(secondDevice.Get(),
-                                                                          wrappedTexture.Get());
-
-        // Import the image to |device|, making sure we wait on signalFd
-        int memoryFd = GetMemoryFd(deviceVk, defaultAllocation);
-        wgpu::Texture nextWrappedTexture =
-            WrapVulkanImage(device, &defaultDescriptor, memoryFd, defaultAllocationSize,
-                            defaultMemoryTypeIndex, {signalFd});
-
-        // Verify |device| sees the changes from |secondDevice|
-        EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), nextWrappedTexture, 0, 0);
-
-        IgnoreSignalSemaphore(device, nextWrappedTexture);
+        deviceVk->GetFencedDeleter()->DeleteWhenUnused(defaultImage);
+        deviceVk->GetFencedDeleter()->DeleteWhenUnused(defaultAllocation);
+        VulkanImageWrappingTestBase::TearDown();
     }
 
-    // Import texture to |device| and |secondDevice|
-    // Clear image in |secondDevice|
-    // Verify clear color is visible in |device|
-    // Verify the very first import into |device| also sees the change, since it should
-    // alias the same memory
-    TEST_P(VulkanImageWrappingUsageTests, ClearImageAcrossDevicesAliased) {
-        DAWN_SKIP_TEST_IF(UsesWire());
-        // Import the image on |device
-        wgpu::Texture wrappedTextureAlias =
-            WrapVulkanImage(device, &defaultDescriptor, defaultFd, defaultAllocationSize,
-                            defaultMemoryTypeIndex, {});
+  protected:
+    wgpu::Device secondDevice;
+    dawn_native::vulkan::Device* secondDeviceVk;
 
-        int memoryFd = GetMemoryFd(deviceVk, defaultAllocation);
+    dawn_native::vulkan::Adapter* backendAdapter;
+    dawn_native::DeviceDescriptor deviceDescriptor;
 
-        // Import the image on |secondDevice|
-        wgpu::Texture wrappedTexture =
-            WrapVulkanImage(secondDevice, &defaultDescriptor, defaultFd, defaultAllocationSize,
-                            defaultMemoryTypeIndex, {});
+    wgpu::TextureDescriptor defaultDescriptor;
+    VkImage defaultImage;
+    VkDeviceMemory defaultAllocation;
+    VkDeviceSize defaultAllocationSize;
+    uint32_t defaultMemoryTypeIndex;
+    int defaultFd;
 
-        // Clear |wrappedTexture| on |secondDevice|
-        ClearImage(secondDevice, wrappedTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f});
+    // Clear a texture on a given device
+    void ClearImage(wgpu::Device device, wgpu::Texture wrappedTexture, wgpu::Color clearColor) {
+        wgpu::TextureView wrappedView = wrappedTexture.CreateView();
 
-        int signalFd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(secondDevice.Get(),
-                                                                          wrappedTexture.Get());
+        // Submit a clear operation
+        utils::ComboRenderPassDescriptor renderPassDescriptor({wrappedView}, {});
+        renderPassDescriptor.cColorAttachments[0].clearColor = clearColor;
 
-        // Import the image to |device|, making sure we wait on signalFd
-        memoryFd = GetMemoryFd(deviceVk, defaultAllocation);
-        wgpu::Texture nextWrappedTexture =
-            WrapVulkanImage(device, &defaultDescriptor, memoryFd, defaultAllocationSize,
-                            defaultMemoryTypeIndex, {signalFd});
+        wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
+        wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDescriptor);
+        pass.EndPass();
 
-        // Verify |device| sees the changes from |secondDevice| (waits)
-        EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), nextWrappedTexture, 0, 0);
+        wgpu::CommandBuffer commands = encoder.Finish();
 
-        // Verify aliased texture sees changes from |secondDevice| (without waiting!)
-        EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), wrappedTextureAlias, 0, 0);
-
-        IgnoreSignalSemaphore(device, nextWrappedTexture);
-        IgnoreSignalSemaphore(device, wrappedTextureAlias);
+        wgpu::Queue queue = device.CreateQueue();
+        queue.Submit(1, &commands);
     }
 
-    // Clear an image in |secondDevice|
-    // Verify clear color is not visible in |device| if we import the texture as not cleared
-    TEST_P(VulkanImageWrappingUsageTests, UnclearedTextureIsCleared) {
-        DAWN_SKIP_TEST_IF(UsesWire());
-
-        // Import the image on |secondDevice|
-        wgpu::Texture wrappedTexture =
-            WrapVulkanImage(secondDevice, &defaultDescriptor, defaultFd, defaultAllocationSize,
-                            defaultMemoryTypeIndex, {});
-
-        // Clear |wrappedTexture| on |secondDevice|
-        ClearImage(secondDevice, wrappedTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f});
-
-        int signalFd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(secondDevice.Get(),
-                                                                          wrappedTexture.Get());
-
-        // Import the image to |device|, making sure we wait on signalFd
-        int memoryFd = GetMemoryFd(deviceVk, defaultAllocation);
-        wgpu::Texture nextWrappedTexture =
-            WrapVulkanImage(device, &defaultDescriptor, memoryFd, defaultAllocationSize,
-                            defaultMemoryTypeIndex, {signalFd}, false);
-
-        // Verify |device| doesn't see the changes from |secondDevice|
-        EXPECT_PIXEL_RGBA8_EQ(RGBA8(0, 0, 0, 0), nextWrappedTexture, 0, 0);
-
-        IgnoreSignalSemaphore(device, nextWrappedTexture);
-    }
-
-    // Import a texture into |secondDevice|
-    // Issue a copy of the imported texture inside |device| to |copyDstTexture|
-    // Verify the clear color from |secondDevice| is visible in |copyDstTexture|
-    TEST_P(VulkanImageWrappingUsageTests, CopyTextureToTextureSrcSync) {
-        DAWN_SKIP_TEST_IF(UsesWire());
-
-        // Import the image on |secondDevice|
-        wgpu::Texture wrappedTexture =
-            WrapVulkanImage(secondDevice, &defaultDescriptor, defaultFd, defaultAllocationSize,
-                            defaultMemoryTypeIndex, {});
-
-        // Clear |wrappedTexture| on |secondDevice|
-        ClearImage(secondDevice, wrappedTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f});
-
-        int signalFd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(secondDevice.Get(),
-                                                                          wrappedTexture.Get());
-
-        // Import the image to |device|, making sure we wait on |signalFd|
-        int memoryFd = GetMemoryFd(deviceVk, defaultAllocation);
-        wgpu::Texture deviceWrappedTexture =
-            WrapVulkanImage(device, &defaultDescriptor, memoryFd, defaultAllocationSize,
-                            defaultMemoryTypeIndex, {signalFd});
-
-        // Create a second texture on |device|
-        wgpu::Texture copyDstTexture = device.CreateTexture(&defaultDescriptor);
-
-        // Copy |deviceWrappedTexture| into |copyDstTexture|
-        SimpleCopyTextureToTexture(device, queue, deviceWrappedTexture, copyDstTexture);
-
-        // Verify |copyDstTexture| sees changes from |secondDevice|
-        EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), copyDstTexture, 0, 0);
-
-        IgnoreSignalSemaphore(device, deviceWrappedTexture);
-    }
-
-    // Import a texture into |device|
-    // Copy color A into texture on |device|
-    // Import same texture into |secondDevice|, waiting on the copy signal
-    // Copy color B using Texture to Texture copy on |secondDevice|
-    // Import texture back into |device|, waiting on color B signal
-    // Verify texture contains color B
-    // If texture destination isn't synchronized, |secondDevice| could copy color B
-    // into the texture first, then |device| writes color A
-    TEST_P(VulkanImageWrappingUsageTests, CopyTextureToTextureDstSync) {
-        DAWN_SKIP_TEST_IF(UsesWire());
-
-        // Import the image on |device|
-        wgpu::Texture wrappedTexture =
-            WrapVulkanImage(device, &defaultDescriptor, defaultFd, defaultAllocationSize,
-                            defaultMemoryTypeIndex, {});
-
-        // Clear |wrappedTexture| on |device|
-        ClearImage(device, wrappedTexture, {5 / 255.0f, 6 / 255.0f, 7 / 255.0f, 8 / 255.0f});
-
-        int signalFd =
-            dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(device.Get(), wrappedTexture.Get());
-
-        // Import the image to |secondDevice|, making sure we wait on |signalFd|
-        int memoryFd = GetMemoryFd(deviceVk, defaultAllocation);
-        wgpu::Texture secondDeviceWrappedTexture =
-            WrapVulkanImage(secondDevice, &defaultDescriptor, memoryFd, defaultAllocationSize,
-                            defaultMemoryTypeIndex, {signalFd});
-
-        // Create a texture with color B on |secondDevice|
-        wgpu::Texture copySrcTexture = secondDevice.CreateTexture(&defaultDescriptor);
-        ClearImage(secondDevice, copySrcTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f});
-
-        // Copy color B on |secondDevice|
-        wgpu::Queue secondDeviceQueue = secondDevice.CreateQueue();
-        SimpleCopyTextureToTexture(secondDevice, secondDeviceQueue, copySrcTexture,
-                                   secondDeviceWrappedTexture);
-
-        // Re-import back into |device|, waiting on |secondDevice|'s signal
-        signalFd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(
-            secondDevice.Get(), secondDeviceWrappedTexture.Get());
-        memoryFd = GetMemoryFd(deviceVk, defaultAllocation);
-
-        wgpu::Texture nextWrappedTexture =
-            WrapVulkanImage(device, &defaultDescriptor, memoryFd, defaultAllocationSize,
-                            defaultMemoryTypeIndex, {signalFd});
-
-        // Verify |nextWrappedTexture| contains the color from our copy
-        EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), nextWrappedTexture, 0, 0);
-
-        IgnoreSignalSemaphore(device, nextWrappedTexture);
-    }
-
-    // Import a texture from |secondDevice|
-    // Issue a copy of the imported texture inside |device| to |copyDstBuffer|
-    // Verify the clear color from |secondDevice| is visible in |copyDstBuffer|
-    TEST_P(VulkanImageWrappingUsageTests, CopyTextureToBufferSrcSync) {
-        DAWN_SKIP_TEST_IF(UsesWire());
-
-        // Import the image on |secondDevice|
-        wgpu::Texture wrappedTexture =
-            WrapVulkanImage(secondDevice, &defaultDescriptor, defaultFd, defaultAllocationSize,
-                            defaultMemoryTypeIndex, {});
-
-        // Clear |wrappedTexture| on |secondDevice|
-        ClearImage(secondDevice, wrappedTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f});
-
-        int signalFd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(secondDevice.Get(),
-                                                                          wrappedTexture.Get());
-
-        // Import the image to |device|, making sure we wait on |signalFd|
-        int memoryFd = GetMemoryFd(deviceVk, defaultAllocation);
-        wgpu::Texture deviceWrappedTexture =
-            WrapVulkanImage(device, &defaultDescriptor, memoryFd, defaultAllocationSize,
-                            defaultMemoryTypeIndex, {signalFd});
-
-        // Create a destination buffer on |device|
-        wgpu::BufferDescriptor bufferDesc;
-        bufferDesc.size = 4;
-        bufferDesc.usage = wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::CopySrc;
-        wgpu::Buffer copyDstBuffer = device.CreateBuffer(&bufferDesc);
-
-        // Copy |deviceWrappedTexture| into |copyDstBuffer|
+    // Submits a 1x1x1 copy from source to destination
+    void SimpleCopyTextureToTexture(wgpu::Device device,
+                                    wgpu::Queue queue,
+                                    wgpu::Texture source,
+                                    wgpu::Texture destination) {
         wgpu::TextureCopyView copySrc;
-        copySrc.texture = deviceWrappedTexture;
+        copySrc.texture = source;
         copySrc.mipLevel = 0;
         copySrc.arrayLayer = 0;
         copySrc.origin = {0, 0, 0};
 
-        wgpu::BufferCopyView copyDst;
-        copyDst.buffer = copyDstBuffer;
-        copyDst.offset = 0;
-        copyDst.rowPitch = 256;
-        copyDst.imageHeight = 0;
-
-        wgpu::Extent3D copySize = {1, 1, 1};
-
-        wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
-        encoder.CopyTextureToBuffer(&copySrc, &copyDst, &copySize);
-        wgpu::CommandBuffer commands = encoder.Finish();
-        queue.Submit(1, &commands);
-
-        // Verify |copyDstBuffer| sees changes from |secondDevice|
-        uint32_t expected = 0x04030201;
-        EXPECT_BUFFER_U32_EQ(expected, copyDstBuffer, 0);
-
-        IgnoreSignalSemaphore(device, deviceWrappedTexture);
-    }
-
-    // Import a texture into |device|
-    // Copy color A into texture on |device|
-    // Import same texture into |secondDevice|, waiting on the copy signal
-    // Copy color B using Buffer to Texture copy on |secondDevice|
-    // Import texture back into |device|, waiting on color B signal
-    // Verify texture contains color B
-    // If texture destination isn't synchronized, |secondDevice| could copy color B
-    // into the texture first, then |device| writes color A
-    TEST_P(VulkanImageWrappingUsageTests, CopyBufferToTextureDstSync) {
-        DAWN_SKIP_TEST_IF(UsesWire());
-
-        // Import the image on |device|
-        wgpu::Texture wrappedTexture =
-            WrapVulkanImage(device, &defaultDescriptor, defaultFd, defaultAllocationSize,
-                            defaultMemoryTypeIndex, {});
-
-        // Clear |wrappedTexture| on |device|
-        ClearImage(device, wrappedTexture, {5 / 255.0f, 6 / 255.0f, 7 / 255.0f, 8 / 255.0f});
-
-        int signalFd =
-            dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(device.Get(), wrappedTexture.Get());
-
-        // Import the image to |secondDevice|, making sure we wait on |signalFd|
-        int memoryFd = GetMemoryFd(deviceVk, defaultAllocation);
-        wgpu::Texture secondDeviceWrappedTexture =
-            WrapVulkanImage(secondDevice, &defaultDescriptor, memoryFd, defaultAllocationSize,
-                            defaultMemoryTypeIndex, {signalFd});
-
-        // Copy color B on |secondDevice|
-        wgpu::Queue secondDeviceQueue = secondDevice.CreateQueue();
-
-        // Create a buffer on |secondDevice|
-        wgpu::Buffer copySrcBuffer =
-            utils::CreateBufferFromData(secondDevice, wgpu::BufferUsage::CopySrc, {0x04030201});
-
-        // Copy |copySrcBuffer| into |secondDeviceWrappedTexture|
-        wgpu::BufferCopyView copySrc;
-        copySrc.buffer = copySrcBuffer;
-        copySrc.offset = 0;
-        copySrc.rowPitch = 256;
-        copySrc.imageHeight = 0;
-
         wgpu::TextureCopyView copyDst;
-        copyDst.texture = secondDeviceWrappedTexture;
+        copyDst.texture = destination;
         copyDst.mipLevel = 0;
         copyDst.arrayLayer = 0;
         copyDst.origin = {0, 0, 0};
 
         wgpu::Extent3D copySize = {1, 1, 1};
 
+        wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
+        encoder.CopyTextureToTexture(&copySrc, &copyDst, &copySize);
+        wgpu::CommandBuffer commands = encoder.Finish();
+
+        queue.Submit(1, &commands);
+    }
+};
+
+// Clear an image in |secondDevice|
+// Verify clear color is visible in |device|
+TEST_P(VulkanImageWrappingUsageTests, ClearImageAcrossDevices) {
+    DAWN_SKIP_TEST_IF(UsesWire());
+
+    // Import the image on |secondDevice|
+    wgpu::Texture wrappedTexture =
+        WrapVulkanImage(secondDevice, &defaultDescriptor, defaultFd, defaultAllocationSize,
+                        defaultMemoryTypeIndex, {});
+
+    // Clear |wrappedTexture| on |secondDevice|
+    ClearImage(secondDevice, wrappedTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f});
+
+    int signalFd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(secondDevice.Get(),
+                                                                      wrappedTexture.Get());
+
+    // Import the image to |device|, making sure we wait on signalFd
+    int memoryFd = GetMemoryFd(deviceVk, defaultAllocation);
+    wgpu::Texture nextWrappedTexture =
+        WrapVulkanImage(device, &defaultDescriptor, memoryFd, defaultAllocationSize,
+                        defaultMemoryTypeIndex, {signalFd});
+
+    // Verify |device| sees the changes from |secondDevice|
+    EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), nextWrappedTexture, 0, 0);
+
+    IgnoreSignalSemaphore(device, nextWrappedTexture);
+}
+
+// Import texture to |device| and |secondDevice|
+// Clear image in |secondDevice|
+// Verify clear color is visible in |device|
+// Verify the very first import into |device| also sees the change, since it should
+// alias the same memory
+TEST_P(VulkanImageWrappingUsageTests, ClearImageAcrossDevicesAliased) {
+    DAWN_SKIP_TEST_IF(UsesWire());
+    // Import the image on |device
+    wgpu::Texture wrappedTextureAlias = WrapVulkanImage(
+        device, &defaultDescriptor, defaultFd, defaultAllocationSize, defaultMemoryTypeIndex, {});
+
+    int memoryFd = GetMemoryFd(deviceVk, defaultAllocation);
+
+    // Import the image on |secondDevice|
+    wgpu::Texture wrappedTexture =
+        WrapVulkanImage(secondDevice, &defaultDescriptor, defaultFd, defaultAllocationSize,
+                        defaultMemoryTypeIndex, {});
+
+    // Clear |wrappedTexture| on |secondDevice|
+    ClearImage(secondDevice, wrappedTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f});
+
+    int signalFd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(secondDevice.Get(),
+                                                                      wrappedTexture.Get());
+
+    // Import the image to |device|, making sure we wait on signalFd
+    memoryFd = GetMemoryFd(deviceVk, defaultAllocation);
+    wgpu::Texture nextWrappedTexture =
+        WrapVulkanImage(device, &defaultDescriptor, memoryFd, defaultAllocationSize,
+                        defaultMemoryTypeIndex, {signalFd});
+
+    // Verify |device| sees the changes from |secondDevice| (waits)
+    EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), nextWrappedTexture, 0, 0);
+
+    // Verify aliased texture sees changes from |secondDevice| (without waiting!)
+    EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), wrappedTextureAlias, 0, 0);
+
+    IgnoreSignalSemaphore(device, nextWrappedTexture);
+    IgnoreSignalSemaphore(device, wrappedTextureAlias);
+}
+
+// Clear an image in |secondDevice|
+// Verify clear color is not visible in |device| if we import the texture as not cleared
+TEST_P(VulkanImageWrappingUsageTests, UnclearedTextureIsCleared) {
+    DAWN_SKIP_TEST_IF(UsesWire());
+
+    // Import the image on |secondDevice|
+    wgpu::Texture wrappedTexture =
+        WrapVulkanImage(secondDevice, &defaultDescriptor, defaultFd, defaultAllocationSize,
+                        defaultMemoryTypeIndex, {});
+
+    // Clear |wrappedTexture| on |secondDevice|
+    ClearImage(secondDevice, wrappedTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f});
+
+    int signalFd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(secondDevice.Get(),
+                                                                      wrappedTexture.Get());
+
+    // Import the image to |device|, making sure we wait on signalFd
+    int memoryFd = GetMemoryFd(deviceVk, defaultAllocation);
+    wgpu::Texture nextWrappedTexture =
+        WrapVulkanImage(device, &defaultDescriptor, memoryFd, defaultAllocationSize,
+                        defaultMemoryTypeIndex, {signalFd}, false);
+
+    // Verify |device| doesn't see the changes from |secondDevice|
+    EXPECT_PIXEL_RGBA8_EQ(RGBA8(0, 0, 0, 0), nextWrappedTexture, 0, 0);
+
+    IgnoreSignalSemaphore(device, nextWrappedTexture);
+}
+
+// Import a texture into |secondDevice|
+// Issue a copy of the imported texture inside |device| to |copyDstTexture|
+// Verify the clear color from |secondDevice| is visible in |copyDstTexture|
+TEST_P(VulkanImageWrappingUsageTests, CopyTextureToTextureSrcSync) {
+    DAWN_SKIP_TEST_IF(UsesWire());
+
+    // Import the image on |secondDevice|
+    wgpu::Texture wrappedTexture =
+        WrapVulkanImage(secondDevice, &defaultDescriptor, defaultFd, defaultAllocationSize,
+                        defaultMemoryTypeIndex, {});
+
+    // Clear |wrappedTexture| on |secondDevice|
+    ClearImage(secondDevice, wrappedTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f});
+
+    int signalFd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(secondDevice.Get(),
+                                                                      wrappedTexture.Get());
+
+    // Import the image to |device|, making sure we wait on |signalFd|
+    int memoryFd = GetMemoryFd(deviceVk, defaultAllocation);
+    wgpu::Texture deviceWrappedTexture =
+        WrapVulkanImage(device, &defaultDescriptor, memoryFd, defaultAllocationSize,
+                        defaultMemoryTypeIndex, {signalFd});
+
+    // Create a second texture on |device|
+    wgpu::Texture copyDstTexture = device.CreateTexture(&defaultDescriptor);
+
+    // Copy |deviceWrappedTexture| into |copyDstTexture|
+    SimpleCopyTextureToTexture(device, queue, deviceWrappedTexture, copyDstTexture);
+
+    // Verify |copyDstTexture| sees changes from |secondDevice|
+    EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), copyDstTexture, 0, 0);
+
+    IgnoreSignalSemaphore(device, deviceWrappedTexture);
+}
+
+// Import a texture into |device|
+// Copy color A into texture on |device|
+// Import same texture into |secondDevice|, waiting on the copy signal
+// Copy color B using Texture to Texture copy on |secondDevice|
+// Import texture back into |device|, waiting on color B signal
+// Verify texture contains color B
+// If texture destination isn't synchronized, |secondDevice| could copy color B
+// into the texture first, then |device| writes color A
+TEST_P(VulkanImageWrappingUsageTests, CopyTextureToTextureDstSync) {
+    DAWN_SKIP_TEST_IF(UsesWire());
+
+    // Import the image on |device|
+    wgpu::Texture wrappedTexture = WrapVulkanImage(
+        device, &defaultDescriptor, defaultFd, defaultAllocationSize, defaultMemoryTypeIndex, {});
+
+    // Clear |wrappedTexture| on |device|
+    ClearImage(device, wrappedTexture, {5 / 255.0f, 6 / 255.0f, 7 / 255.0f, 8 / 255.0f});
+
+    int signalFd =
+        dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(device.Get(), wrappedTexture.Get());
+
+    // Import the image to |secondDevice|, making sure we wait on |signalFd|
+    int memoryFd = GetMemoryFd(deviceVk, defaultAllocation);
+    wgpu::Texture secondDeviceWrappedTexture =
+        WrapVulkanImage(secondDevice, &defaultDescriptor, memoryFd, defaultAllocationSize,
+                        defaultMemoryTypeIndex, {signalFd});
+
+    // Create a texture with color B on |secondDevice|
+    wgpu::Texture copySrcTexture = secondDevice.CreateTexture(&defaultDescriptor);
+    ClearImage(secondDevice, copySrcTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f});
+
+    // Copy color B on |secondDevice|
+    wgpu::Queue secondDeviceQueue = secondDevice.CreateQueue();
+    SimpleCopyTextureToTexture(secondDevice, secondDeviceQueue, copySrcTexture,
+                               secondDeviceWrappedTexture);
+
+    // Re-import back into |device|, waiting on |secondDevice|'s signal
+    signalFd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(secondDevice.Get(),
+                                                                  secondDeviceWrappedTexture.Get());
+    memoryFd = GetMemoryFd(deviceVk, defaultAllocation);
+
+    wgpu::Texture nextWrappedTexture =
+        WrapVulkanImage(device, &defaultDescriptor, memoryFd, defaultAllocationSize,
+                        defaultMemoryTypeIndex, {signalFd});
+
+    // Verify |nextWrappedTexture| contains the color from our copy
+    EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), nextWrappedTexture, 0, 0);
+
+    IgnoreSignalSemaphore(device, nextWrappedTexture);
+}
+
+// Import a texture from |secondDevice|
+// Issue a copy of the imported texture inside |device| to |copyDstBuffer|
+// Verify the clear color from |secondDevice| is visible in |copyDstBuffer|
+TEST_P(VulkanImageWrappingUsageTests, CopyTextureToBufferSrcSync) {
+    DAWN_SKIP_TEST_IF(UsesWire());
+
+    // Import the image on |secondDevice|
+    wgpu::Texture wrappedTexture =
+        WrapVulkanImage(secondDevice, &defaultDescriptor, defaultFd, defaultAllocationSize,
+                        defaultMemoryTypeIndex, {});
+
+    // Clear |wrappedTexture| on |secondDevice|
+    ClearImage(secondDevice, wrappedTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f});
+
+    int signalFd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(secondDevice.Get(),
+                                                                      wrappedTexture.Get());
+
+    // Import the image to |device|, making sure we wait on |signalFd|
+    int memoryFd = GetMemoryFd(deviceVk, defaultAllocation);
+    wgpu::Texture deviceWrappedTexture =
+        WrapVulkanImage(device, &defaultDescriptor, memoryFd, defaultAllocationSize,
+                        defaultMemoryTypeIndex, {signalFd});
+
+    // Create a destination buffer on |device|
+    wgpu::BufferDescriptor bufferDesc;
+    bufferDesc.size = 4;
+    bufferDesc.usage = wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::CopySrc;
+    wgpu::Buffer copyDstBuffer = device.CreateBuffer(&bufferDesc);
+
+    // Copy |deviceWrappedTexture| into |copyDstBuffer|
+    wgpu::TextureCopyView copySrc;
+    copySrc.texture = deviceWrappedTexture;
+    copySrc.mipLevel = 0;
+    copySrc.arrayLayer = 0;
+    copySrc.origin = {0, 0, 0};
+
+    wgpu::BufferCopyView copyDst;
+    copyDst.buffer = copyDstBuffer;
+    copyDst.offset = 0;
+    copyDst.rowPitch = 256;
+    copyDst.imageHeight = 0;
+
+    wgpu::Extent3D copySize = {1, 1, 1};
+
+    wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
+    encoder.CopyTextureToBuffer(&copySrc, &copyDst, &copySize);
+    wgpu::CommandBuffer commands = encoder.Finish();
+    queue.Submit(1, &commands);
+
+    // Verify |copyDstBuffer| sees changes from |secondDevice|
+    uint32_t expected = 0x04030201;
+    EXPECT_BUFFER_U32_EQ(expected, copyDstBuffer, 0);
+
+    IgnoreSignalSemaphore(device, deviceWrappedTexture);
+}
+
+// Import a texture into |device|
+// Copy color A into texture on |device|
+// Import same texture into |secondDevice|, waiting on the copy signal
+// Copy color B using Buffer to Texture copy on |secondDevice|
+// Import texture back into |device|, waiting on color B signal
+// Verify texture contains color B
+// If texture destination isn't synchronized, |secondDevice| could copy color B
+// into the texture first, then |device| writes color A
+TEST_P(VulkanImageWrappingUsageTests, CopyBufferToTextureDstSync) {
+    DAWN_SKIP_TEST_IF(UsesWire());
+
+    // Import the image on |device|
+    wgpu::Texture wrappedTexture = WrapVulkanImage(
+        device, &defaultDescriptor, defaultFd, defaultAllocationSize, defaultMemoryTypeIndex, {});
+
+    // Clear |wrappedTexture| on |device|
+    ClearImage(device, wrappedTexture, {5 / 255.0f, 6 / 255.0f, 7 / 255.0f, 8 / 255.0f});
+
+    int signalFd =
+        dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(device.Get(), wrappedTexture.Get());
+
+    // Import the image to |secondDevice|, making sure we wait on |signalFd|
+    int memoryFd = GetMemoryFd(deviceVk, defaultAllocation);
+    wgpu::Texture secondDeviceWrappedTexture =
+        WrapVulkanImage(secondDevice, &defaultDescriptor, memoryFd, defaultAllocationSize,
+                        defaultMemoryTypeIndex, {signalFd});
+
+    // Copy color B on |secondDevice|
+    wgpu::Queue secondDeviceQueue = secondDevice.CreateQueue();
+
+    // Create a buffer on |secondDevice|
+    wgpu::Buffer copySrcBuffer =
+        utils::CreateBufferFromData(secondDevice, wgpu::BufferUsage::CopySrc, {0x04030201});
+
+    // Copy |copySrcBuffer| into |secondDeviceWrappedTexture|
+    wgpu::BufferCopyView copySrc;
+    copySrc.buffer = copySrcBuffer;
+    copySrc.offset = 0;
+    copySrc.rowPitch = 256;
+    copySrc.imageHeight = 0;
+
+    wgpu::TextureCopyView copyDst;
+    copyDst.texture = secondDeviceWrappedTexture;
+    copyDst.mipLevel = 0;
+    copyDst.arrayLayer = 0;
+    copyDst.origin = {0, 0, 0};
+
+    wgpu::Extent3D copySize = {1, 1, 1};
+
+    wgpu::CommandEncoder encoder = secondDevice.CreateCommandEncoder();
+    encoder.CopyBufferToTexture(&copySrc, &copyDst, &copySize);
+    wgpu::CommandBuffer commands = encoder.Finish();
+    secondDeviceQueue.Submit(1, &commands);
+
+    // Re-import back into |device|, waiting on |secondDevice|'s signal
+    signalFd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(secondDevice.Get(),
+                                                                  secondDeviceWrappedTexture.Get());
+    memoryFd = GetMemoryFd(deviceVk, defaultAllocation);
+
+    wgpu::Texture nextWrappedTexture =
+        WrapVulkanImage(device, &defaultDescriptor, memoryFd, defaultAllocationSize,
+                        defaultMemoryTypeIndex, {signalFd});
+
+    // Verify |nextWrappedTexture| contains the color from our copy
+    EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), nextWrappedTexture, 0, 0);
+
+    IgnoreSignalSemaphore(device, nextWrappedTexture);
+}
+
+// Import a texture from |secondDevice|
+// Issue a copy of the imported texture inside |device| to |copyDstTexture|
+// Issue second copy to |secondCopyDstTexture|
+// Verify the clear color from |secondDevice| is visible in both copies
+TEST_P(VulkanImageWrappingUsageTests, DoubleTextureUsage) {
+    DAWN_SKIP_TEST_IF(UsesWire());
+
+    // Import the image on |secondDevice|
+    wgpu::Texture wrappedTexture =
+        WrapVulkanImage(secondDevice, &defaultDescriptor, defaultFd, defaultAllocationSize,
+                        defaultMemoryTypeIndex, {});
+
+    // Clear |wrappedTexture| on |secondDevice|
+    ClearImage(secondDevice, wrappedTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f});
+
+    int signalFd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(secondDevice.Get(),
+                                                                      wrappedTexture.Get());
+
+    // Import the image to |device|, making sure we wait on |signalFd|
+    int memoryFd = GetMemoryFd(deviceVk, defaultAllocation);
+    wgpu::Texture deviceWrappedTexture =
+        WrapVulkanImage(device, &defaultDescriptor, memoryFd, defaultAllocationSize,
+                        defaultMemoryTypeIndex, {signalFd});
+
+    // Create a second texture on |device|
+    wgpu::Texture copyDstTexture = device.CreateTexture(&defaultDescriptor);
+
+    // Create a third texture on |device|
+    wgpu::Texture secondCopyDstTexture = device.CreateTexture(&defaultDescriptor);
+
+    // Copy |deviceWrappedTexture| into |copyDstTexture|
+    SimpleCopyTextureToTexture(device, queue, deviceWrappedTexture, copyDstTexture);
+
+    // Copy |deviceWrappedTexture| into |secondCopyDstTexture|
+    SimpleCopyTextureToTexture(device, queue, deviceWrappedTexture, secondCopyDstTexture);
+
+    // Verify |copyDstTexture| sees changes from |secondDevice|
+    EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), copyDstTexture, 0, 0);
+
+    // Verify |secondCopyDstTexture| sees changes from |secondDevice|
+    EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), secondCopyDstTexture, 0, 0);
+
+    IgnoreSignalSemaphore(device, deviceWrappedTexture);
+}
+
+// Tex A on device 3 (external export)
+// Tex B on device 2 (external export)
+// Tex C on device 1 (external export)
+// Clear color for A on device 3
+// Copy A->B on device 3
+// Copy B->C on device 2 (wait on B from previous op)
+// Copy C->D on device 1 (wait on C from previous op)
+// Verify D has same color as A
+TEST_P(VulkanImageWrappingUsageTests, ChainTextureCopy) {
+    DAWN_SKIP_TEST_IF(UsesWire());
+
+    // Close |defaultFd| since this test doesn't import it anywhere
+    close(defaultFd);
+
+    // device 1 = |device|
+    // device 2 = |secondDevice|
+    // Create device 3
+    dawn_native::vulkan::Device* thirdDeviceVk = reinterpret_cast<dawn_native::vulkan::Device*>(
+        backendAdapter->CreateDevice(&deviceDescriptor));
+    wgpu::Device thirdDevice = wgpu::Device::Acquire(reinterpret_cast<WGPUDevice>(thirdDeviceVk));
+
+    // Make queue for device 2 and 3
+    wgpu::Queue secondDeviceQueue = secondDevice.CreateQueue();
+    wgpu::Queue thirdDeviceQueue = thirdDevice.CreateQueue();
+
+    // Allocate memory for A, B, C
+    VkImage imageA;
+    VkDeviceMemory allocationA;
+    int memoryFdA;
+    VkDeviceSize allocationSizeA;
+    uint32_t memoryTypeIndexA;
+    CreateBindExportImage(thirdDeviceVk, 1, 1, VK_FORMAT_R8G8B8A8_UNORM, &imageA, &allocationA,
+                          &allocationSizeA, &memoryTypeIndexA, &memoryFdA);
+
+    VkImage imageB;
+    VkDeviceMemory allocationB;
+    int memoryFdB;
+    VkDeviceSize allocationSizeB;
+    uint32_t memoryTypeIndexB;
+    CreateBindExportImage(secondDeviceVk, 1, 1, VK_FORMAT_R8G8B8A8_UNORM, &imageB, &allocationB,
+                          &allocationSizeB, &memoryTypeIndexB, &memoryFdB);
+
+    VkImage imageC;
+    VkDeviceMemory allocationC;
+    int memoryFdC;
+    VkDeviceSize allocationSizeC;
+    uint32_t memoryTypeIndexC;
+    CreateBindExportImage(deviceVk, 1, 1, VK_FORMAT_R8G8B8A8_UNORM, &imageC, &allocationC,
+                          &allocationSizeC, &memoryTypeIndexC, &memoryFdC);
+
+    // Import TexA, TexB on device 3
+    wgpu::Texture wrappedTexADevice3 = WrapVulkanImage(thirdDevice, &defaultDescriptor, memoryFdA,
+                                                       allocationSizeA, memoryTypeIndexA, {});
+
+    wgpu::Texture wrappedTexBDevice3 = WrapVulkanImage(thirdDevice, &defaultDescriptor, memoryFdB,
+                                                       allocationSizeB, memoryTypeIndexB, {});
+
+    // Clear TexA
+    ClearImage(thirdDevice, wrappedTexADevice3, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f});
+
+    // Copy A->B
+    SimpleCopyTextureToTexture(thirdDevice, thirdDeviceQueue, wrappedTexADevice3,
+                               wrappedTexBDevice3);
+
+    int signalFdTexBDevice3 = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(
+        thirdDevice.Get(), wrappedTexBDevice3.Get());
+    IgnoreSignalSemaphore(thirdDevice, wrappedTexADevice3);
+
+    // Import TexB, TexC on device 2
+    memoryFdB = GetMemoryFd(secondDeviceVk, allocationB);
+    wgpu::Texture wrappedTexBDevice2 =
+        WrapVulkanImage(secondDevice, &defaultDescriptor, memoryFdB, allocationSizeB,
+                        memoryTypeIndexB, {signalFdTexBDevice3});
+
+    wgpu::Texture wrappedTexCDevice2 = WrapVulkanImage(secondDevice, &defaultDescriptor, memoryFdC,
+                                                       allocationSizeC, memoryTypeIndexC, {});
+
+    // Copy B->C on device 2
+    SimpleCopyTextureToTexture(secondDevice, secondDeviceQueue, wrappedTexBDevice2,
+                               wrappedTexCDevice2);
+
+    int signalFdTexCDevice2 = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(
+        secondDevice.Get(), wrappedTexCDevice2.Get());
+    IgnoreSignalSemaphore(secondDevice, wrappedTexBDevice2);
+
+    // Import TexC on device 1
+    memoryFdC = GetMemoryFd(deviceVk, allocationC);
+    wgpu::Texture wrappedTexCDevice1 =
+        WrapVulkanImage(device, &defaultDescriptor, memoryFdC, allocationSizeC, memoryTypeIndexC,
+                        {signalFdTexCDevice2});
+
+    // Create TexD on device 1
+    wgpu::Texture texD = device.CreateTexture(&defaultDescriptor);
+
+    // Copy C->D on device 1
+    SimpleCopyTextureToTexture(device, queue, wrappedTexCDevice1, texD);
+
+    // Verify D matches clear color
+    EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), texD, 0, 0);
+
+    thirdDeviceVk->GetFencedDeleter()->DeleteWhenUnused(imageA);
+    thirdDeviceVk->GetFencedDeleter()->DeleteWhenUnused(allocationA);
+    secondDeviceVk->GetFencedDeleter()->DeleteWhenUnused(imageB);
+    secondDeviceVk->GetFencedDeleter()->DeleteWhenUnused(allocationB);
+    deviceVk->GetFencedDeleter()->DeleteWhenUnused(imageC);
+    deviceVk->GetFencedDeleter()->DeleteWhenUnused(allocationC);
+
+    IgnoreSignalSemaphore(device, wrappedTexCDevice1);
+}
+
+// Tests a larger image is preserved when importing
+// TODO(http://crbug.com/dawn/206): This fails on AMD
+TEST_P(VulkanImageWrappingUsageTests, LargerImage) {
+    DAWN_SKIP_TEST_IF(UsesWire() || IsAMD());
+
+    close(defaultFd);
+
+    wgpu::TextureDescriptor descriptor;
+    descriptor.dimension = wgpu::TextureDimension::e2D;
+    descriptor.size.width = 640;
+    descriptor.size.height = 480;
+    descriptor.size.depth = 1;
+    descriptor.arrayLayerCount = 1;
+    descriptor.sampleCount = 1;
+    descriptor.format = wgpu::TextureFormat::BGRA8Unorm;
+    descriptor.mipLevelCount = 1;
+    descriptor.usage = wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::CopySrc;
+
+    // Fill memory with textures to trigger layout issues on AMD
+    std::vector<wgpu::Texture> textures;
+    for (int i = 0; i < 20; i++) {
+        textures.push_back(device.CreateTexture(&descriptor));
+    }
+
+    wgpu::Queue secondDeviceQueue = secondDevice.CreateQueue();
+
+    // Make an image on |secondDevice|
+    VkImage imageA;
+    VkDeviceMemory allocationA;
+    int memoryFdA;
+    VkDeviceSize allocationSizeA;
+    uint32_t memoryTypeIndexA;
+    CreateBindExportImage(secondDeviceVk, 640, 480, VK_FORMAT_R8G8B8A8_UNORM, &imageA, &allocationA,
+                          &allocationSizeA, &memoryTypeIndexA, &memoryFdA);
+
+    // Import the image on |secondDevice|
+    wgpu::Texture wrappedTexture = WrapVulkanImage(secondDevice, &descriptor, memoryFdA,
+                                                   allocationSizeA, memoryTypeIndexA, {});
+
+    // Draw a non-trivial picture
+    int width = 640, height = 480, pixelSize = 4;
+    uint32_t rowPitch = Align(width * pixelSize, kTextureRowPitchAlignment);
+    uint32_t size = rowPitch * (height - 1) + width * pixelSize;
+    unsigned char data[size];
+    for (int row = 0; row < height; row++) {
+        for (int col = 0; col < width; col++) {
+            float normRow = static_cast<float>(row) / height;
+            float normCol = static_cast<float>(col) / width;
+            float dist = sqrt(normRow * normRow + normCol * normCol) * 3;
+            dist = dist - static_cast<int>(dist);
+            data[4 * (row * width + col)] = static_cast<unsigned char>(dist * 255);
+            data[4 * (row * width + col) + 1] = static_cast<unsigned char>(dist * 255);
+            data[4 * (row * width + col) + 2] = static_cast<unsigned char>(dist * 255);
+            data[4 * (row * width + col) + 3] = 255;
+        }
+    }
+
+    // Write the picture
+    {
+        wgpu::Buffer copySrcBuffer =
+            utils::CreateBufferFromData(secondDevice, data, size, wgpu::BufferUsage::CopySrc);
+        wgpu::BufferCopyView copySrc = utils::CreateBufferCopyView(copySrcBuffer, 0, rowPitch, 0);
+        wgpu::TextureCopyView copyDst =
+            utils::CreateTextureCopyView(wrappedTexture, 0, 0, {0, 0, 0});
+        wgpu::Extent3D copySize = {width, height, 1};
+
         wgpu::CommandEncoder encoder = secondDevice.CreateCommandEncoder();
         encoder.CopyBufferToTexture(&copySrc, &copyDst, &copySize);
         wgpu::CommandBuffer commands = encoder.Finish();
         secondDeviceQueue.Submit(1, &commands);
-
-        // Re-import back into |device|, waiting on |secondDevice|'s signal
-        signalFd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(
-            secondDevice.Get(), secondDeviceWrappedTexture.Get());
-        memoryFd = GetMemoryFd(deviceVk, defaultAllocation);
-
-        wgpu::Texture nextWrappedTexture =
-            WrapVulkanImage(device, &defaultDescriptor, memoryFd, defaultAllocationSize,
-                            defaultMemoryTypeIndex, {signalFd});
-
-        // Verify |nextWrappedTexture| contains the color from our copy
-        EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), nextWrappedTexture, 0, 0);
-
-        IgnoreSignalSemaphore(device, nextWrappedTexture);
     }
 
-    // Import a texture from |secondDevice|
-    // Issue a copy of the imported texture inside |device| to |copyDstTexture|
-    // Issue second copy to |secondCopyDstTexture|
-    // Verify the clear color from |secondDevice| is visible in both copies
-    TEST_P(VulkanImageWrappingUsageTests, DoubleTextureUsage) {
-        DAWN_SKIP_TEST_IF(UsesWire());
+    int signalFd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(secondDevice.Get(),
+                                                                      wrappedTexture.Get());
+    int memoryFd = GetMemoryFd(secondDeviceVk, allocationA);
 
-        // Import the image on |secondDevice|
-        wgpu::Texture wrappedTexture =
-            WrapVulkanImage(secondDevice, &defaultDescriptor, defaultFd, defaultAllocationSize,
-                            defaultMemoryTypeIndex, {});
+    // Import the image on |device|
+    wgpu::Texture nextWrappedTexture = WrapVulkanImage(
+        device, &descriptor, memoryFd, allocationSizeA, memoryTypeIndexA, {signalFd});
 
-        // Clear |wrappedTexture| on |secondDevice|
-        ClearImage(secondDevice, wrappedTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f});
+    // Copy the image into a buffer for comparison
+    wgpu::BufferDescriptor copyDesc;
+    copyDesc.size = size;
+    copyDesc.usage = wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst;
+    wgpu::Buffer copyDstBuffer = device.CreateBuffer(&copyDesc);
+    {
+        wgpu::TextureCopyView copySrc =
+            utils::CreateTextureCopyView(nextWrappedTexture, 0, 0, {0, 0, 0});
+        wgpu::BufferCopyView copyDst = utils::CreateBufferCopyView(copyDstBuffer, 0, rowPitch, 0);
 
-        int signalFd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(secondDevice.Get(),
-                                                                          wrappedTexture.Get());
+        wgpu::Extent3D copySize = {width, height, 1};
 
-        // Import the image to |device|, making sure we wait on |signalFd|
-        int memoryFd = GetMemoryFd(deviceVk, defaultAllocation);
-        wgpu::Texture deviceWrappedTexture =
-            WrapVulkanImage(device, &defaultDescriptor, memoryFd, defaultAllocationSize,
-                            defaultMemoryTypeIndex, {signalFd});
-
-        // Create a second texture on |device|
-        wgpu::Texture copyDstTexture = device.CreateTexture(&defaultDescriptor);
-
-        // Create a third texture on |device|
-        wgpu::Texture secondCopyDstTexture = device.CreateTexture(&defaultDescriptor);
-
-        // Copy |deviceWrappedTexture| into |copyDstTexture|
-        SimpleCopyTextureToTexture(device, queue, deviceWrappedTexture, copyDstTexture);
-
-        // Copy |deviceWrappedTexture| into |secondCopyDstTexture|
-        SimpleCopyTextureToTexture(device, queue, deviceWrappedTexture, secondCopyDstTexture);
-
-        // Verify |copyDstTexture| sees changes from |secondDevice|
-        EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), copyDstTexture, 0, 0);
-
-        // Verify |secondCopyDstTexture| sees changes from |secondDevice|
-        EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), secondCopyDstTexture, 0, 0);
-
-        IgnoreSignalSemaphore(device, deviceWrappedTexture);
+        wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
+        encoder.CopyTextureToBuffer(&copySrc, &copyDst, &copySize);
+        wgpu::CommandBuffer commands = encoder.Finish();
+        queue.Submit(1, &commands);
     }
 
-    // Tex A on device 3 (external export)
-    // Tex B on device 2 (external export)
-    // Tex C on device 1 (external export)
-    // Clear color for A on device 3
-    // Copy A->B on device 3
-    // Copy B->C on device 2 (wait on B from previous op)
-    // Copy C->D on device 1 (wait on C from previous op)
-    // Verify D has same color as A
-    TEST_P(VulkanImageWrappingUsageTests, ChainTextureCopy) {
-        DAWN_SKIP_TEST_IF(UsesWire());
+    // Check the image is not corrupted on |device|
+    EXPECT_BUFFER_U32_RANGE_EQ(reinterpret_cast<uint32_t*>(data), copyDstBuffer, 0, size / 4);
 
-        // Close |defaultFd| since this test doesn't import it anywhere
-        close(defaultFd);
+    IgnoreSignalSemaphore(device, nextWrappedTexture);
+    secondDeviceVk->GetFencedDeleter()->DeleteWhenUnused(imageA);
+    secondDeviceVk->GetFencedDeleter()->DeleteWhenUnused(allocationA);
+}
 
-        // device 1 = |device|
-        // device 2 = |secondDevice|
-        // Create device 3
-        dawn_native::vulkan::Device* thirdDeviceVk = reinterpret_cast<dawn_native::vulkan::Device*>(
-            backendAdapter->CreateDevice(&deviceDescriptor));
-        wgpu::Device thirdDevice =
-            wgpu::Device::Acquire(reinterpret_cast<WGPUDevice>(thirdDeviceVk));
-
-        // Make queue for device 2 and 3
-        wgpu::Queue secondDeviceQueue = secondDevice.CreateQueue();
-        wgpu::Queue thirdDeviceQueue = thirdDevice.CreateQueue();
-
-        // Allocate memory for A, B, C
-        VkImage imageA;
-        VkDeviceMemory allocationA;
-        int memoryFdA;
-        VkDeviceSize allocationSizeA;
-        uint32_t memoryTypeIndexA;
-        CreateBindExportImage(thirdDeviceVk, 1, 1, VK_FORMAT_R8G8B8A8_UNORM, &imageA, &allocationA,
-                              &allocationSizeA, &memoryTypeIndexA, &memoryFdA);
-
-        VkImage imageB;
-        VkDeviceMemory allocationB;
-        int memoryFdB;
-        VkDeviceSize allocationSizeB;
-        uint32_t memoryTypeIndexB;
-        CreateBindExportImage(secondDeviceVk, 1, 1, VK_FORMAT_R8G8B8A8_UNORM, &imageB, &allocationB,
-                              &allocationSizeB, &memoryTypeIndexB, &memoryFdB);
-
-        VkImage imageC;
-        VkDeviceMemory allocationC;
-        int memoryFdC;
-        VkDeviceSize allocationSizeC;
-        uint32_t memoryTypeIndexC;
-        CreateBindExportImage(deviceVk, 1, 1, VK_FORMAT_R8G8B8A8_UNORM, &imageC, &allocationC,
-                              &allocationSizeC, &memoryTypeIndexC, &memoryFdC);
-
-        // Import TexA, TexB on device 3
-        wgpu::Texture wrappedTexADevice3 = WrapVulkanImage(
-            thirdDevice, &defaultDescriptor, memoryFdA, allocationSizeA, memoryTypeIndexA, {});
-
-        wgpu::Texture wrappedTexBDevice3 = WrapVulkanImage(
-            thirdDevice, &defaultDescriptor, memoryFdB, allocationSizeB, memoryTypeIndexB, {});
-
-        // Clear TexA
-        ClearImage(thirdDevice, wrappedTexADevice3,
-                   {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f});
-
-        // Copy A->B
-        SimpleCopyTextureToTexture(thirdDevice, thirdDeviceQueue, wrappedTexADevice3,
-                                   wrappedTexBDevice3);
-
-        int signalFdTexBDevice3 = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(
-            thirdDevice.Get(), wrappedTexBDevice3.Get());
-        IgnoreSignalSemaphore(thirdDevice, wrappedTexADevice3);
-
-        // Import TexB, TexC on device 2
-        memoryFdB = GetMemoryFd(secondDeviceVk, allocationB);
-        wgpu::Texture wrappedTexBDevice2 =
-            WrapVulkanImage(secondDevice, &defaultDescriptor, memoryFdB, allocationSizeB,
-                            memoryTypeIndexB, {signalFdTexBDevice3});
-
-        wgpu::Texture wrappedTexCDevice2 = WrapVulkanImage(
-            secondDevice, &defaultDescriptor, memoryFdC, allocationSizeC, memoryTypeIndexC, {});
-
-        // Copy B->C on device 2
-        SimpleCopyTextureToTexture(secondDevice, secondDeviceQueue, wrappedTexBDevice2,
-                                   wrappedTexCDevice2);
-
-        int signalFdTexCDevice2 = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(
-            secondDevice.Get(), wrappedTexCDevice2.Get());
-        IgnoreSignalSemaphore(secondDevice, wrappedTexBDevice2);
-
-        // Import TexC on device 1
-        memoryFdC = GetMemoryFd(deviceVk, allocationC);
-        wgpu::Texture wrappedTexCDevice1 =
-            WrapVulkanImage(device, &defaultDescriptor, memoryFdC, allocationSizeC,
-                            memoryTypeIndexC, {signalFdTexCDevice2});
-
-        // Create TexD on device 1
-        wgpu::Texture texD = device.CreateTexture(&defaultDescriptor);
-
-        // Copy C->D on device 1
-        SimpleCopyTextureToTexture(device, queue, wrappedTexCDevice1, texD);
-
-        // Verify D matches clear color
-        EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), texD, 0, 0);
-
-        thirdDeviceVk->GetFencedDeleter()->DeleteWhenUnused(imageA);
-        thirdDeviceVk->GetFencedDeleter()->DeleteWhenUnused(allocationA);
-        secondDeviceVk->GetFencedDeleter()->DeleteWhenUnused(imageB);
-        secondDeviceVk->GetFencedDeleter()->DeleteWhenUnused(allocationB);
-        deviceVk->GetFencedDeleter()->DeleteWhenUnused(imageC);
-        deviceVk->GetFencedDeleter()->DeleteWhenUnused(allocationC);
-
-        IgnoreSignalSemaphore(device, wrappedTexCDevice1);
-    }
-
-    // Tests a larger image is preserved when importing
-    // TODO(http://crbug.com/dawn/206): This fails on AMD
-    TEST_P(VulkanImageWrappingUsageTests, LargerImage) {
-        DAWN_SKIP_TEST_IF(UsesWire() || IsAMD());
-
-        close(defaultFd);
-
-        wgpu::TextureDescriptor descriptor;
-        descriptor.dimension = wgpu::TextureDimension::e2D;
-        descriptor.size.width = 640;
-        descriptor.size.height = 480;
-        descriptor.size.depth = 1;
-        descriptor.arrayLayerCount = 1;
-        descriptor.sampleCount = 1;
-        descriptor.format = wgpu::TextureFormat::BGRA8Unorm;
-        descriptor.mipLevelCount = 1;
-        descriptor.usage = wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::CopySrc;
-
-        // Fill memory with textures to trigger layout issues on AMD
-        std::vector<wgpu::Texture> textures;
-        for (int i = 0; i < 20; i++) {
-            textures.push_back(device.CreateTexture(&descriptor));
-        }
-
-        wgpu::Queue secondDeviceQueue = secondDevice.CreateQueue();
-
-        // Make an image on |secondDevice|
-        VkImage imageA;
-        VkDeviceMemory allocationA;
-        int memoryFdA;
-        VkDeviceSize allocationSizeA;
-        uint32_t memoryTypeIndexA;
-        CreateBindExportImage(secondDeviceVk, 640, 480, VK_FORMAT_R8G8B8A8_UNORM, &imageA,
-                              &allocationA, &allocationSizeA, &memoryTypeIndexA, &memoryFdA);
-
-        // Import the image on |secondDevice|
-        wgpu::Texture wrappedTexture = WrapVulkanImage(secondDevice, &descriptor, memoryFdA,
-                                                       allocationSizeA, memoryTypeIndexA, {});
-
-        // Draw a non-trivial picture
-        int width = 640, height = 480, pixelSize = 4;
-        uint32_t rowPitch = Align(width * pixelSize, kTextureRowPitchAlignment);
-        uint32_t size = rowPitch * (height - 1) + width * pixelSize;
-        unsigned char data[size];
-        for (int row = 0; row < height; row++) {
-            for (int col = 0; col < width; col++) {
-                float normRow = static_cast<float>(row) / height;
-                float normCol = static_cast<float>(col) / width;
-                float dist = sqrt(normRow * normRow + normCol * normCol) * 3;
-                dist = dist - static_cast<int>(dist);
-                data[4 * (row * width + col)] = static_cast<unsigned char>(dist * 255);
-                data[4 * (row * width + col) + 1] = static_cast<unsigned char>(dist * 255);
-                data[4 * (row * width + col) + 2] = static_cast<unsigned char>(dist * 255);
-                data[4 * (row * width + col) + 3] = 255;
-            }
-        }
-
-        // Write the picture
-        {
-            wgpu::Buffer copySrcBuffer =
-                utils::CreateBufferFromData(secondDevice, data, size, wgpu::BufferUsage::CopySrc);
-            wgpu::BufferCopyView copySrc =
-                utils::CreateBufferCopyView(copySrcBuffer, 0, rowPitch, 0);
-            wgpu::TextureCopyView copyDst =
-                utils::CreateTextureCopyView(wrappedTexture, 0, 0, {0, 0, 0});
-            wgpu::Extent3D copySize = {width, height, 1};
-
-            wgpu::CommandEncoder encoder = secondDevice.CreateCommandEncoder();
-            encoder.CopyBufferToTexture(&copySrc, &copyDst, &copySize);
-            wgpu::CommandBuffer commands = encoder.Finish();
-            secondDeviceQueue.Submit(1, &commands);
-        }
-
-        int signalFd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(secondDevice.Get(),
-                                                                          wrappedTexture.Get());
-        int memoryFd = GetMemoryFd(secondDeviceVk, allocationA);
-
-        // Import the image on |device|
-        wgpu::Texture nextWrappedTexture = WrapVulkanImage(
-            device, &descriptor, memoryFd, allocationSizeA, memoryTypeIndexA, {signalFd});
-
-        // Copy the image into a buffer for comparison
-        wgpu::BufferDescriptor copyDesc;
-        copyDesc.size = size;
-        copyDesc.usage = wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst;
-        wgpu::Buffer copyDstBuffer = device.CreateBuffer(&copyDesc);
-        {
-            wgpu::TextureCopyView copySrc =
-                utils::CreateTextureCopyView(nextWrappedTexture, 0, 0, {0, 0, 0});
-            wgpu::BufferCopyView copyDst =
-                utils::CreateBufferCopyView(copyDstBuffer, 0, rowPitch, 0);
-
-            wgpu::Extent3D copySize = {width, height, 1};
-
-            wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
-            encoder.CopyTextureToBuffer(&copySrc, &copyDst, &copySize);
-            wgpu::CommandBuffer commands = encoder.Finish();
-            queue.Submit(1, &commands);
-        }
-
-        // Check the image is not corrupted on |device|
-        EXPECT_BUFFER_U32_RANGE_EQ(reinterpret_cast<uint32_t*>(data), copyDstBuffer, 0, size / 4);
-
-        IgnoreSignalSemaphore(device, nextWrappedTexture);
-        secondDeviceVk->GetFencedDeleter()->DeleteWhenUnused(imageA);
-        secondDeviceVk->GetFencedDeleter()->DeleteWhenUnused(allocationA);
-    }
-
-    DAWN_INSTANTIATE_TEST(VulkanImageWrappingValidationTests, VulkanBackend);
-    DAWN_INSTANTIATE_TEST(VulkanImageWrappingUsageTests, VulkanBackend);
-
-}}  // namespace dawn_native::vulkan
+DAWN_INSTANTIATE_TEST(VulkanImageWrappingValidationTests, VulkanBackend);
+DAWN_INSTANTIATE_TEST(VulkanImageWrappingUsageTests, VulkanBackend);