• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2020 The Dawn Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "tests/DawnTest.h"
16 
17 #include "common/Math.h"
18 #include "common/vulkan_platform.h"
19 #include "dawn_native/VulkanBackend.h"
20 #include "dawn_native/vulkan/AdapterVk.h"
21 #include "dawn_native/vulkan/DeviceVk.h"
22 #include "dawn_native/vulkan/FencedDeleter.h"
23 #include "dawn_native/vulkan/ResourceMemoryAllocatorVk.h"
24 #include "dawn_native/vulkan/TextureVk.h"
25 #include "utils/SystemUtils.h"
26 #include "utils/WGPUHelpers.h"
27 
28 #include <fcntl.h>
29 #include <gbm.h>
30 
31 namespace dawn_native { namespace vulkan {
32 
33     namespace {
34 
35         class VulkanImageWrappingTestBase : public DawnTest {
36           protected:
GetRequiredFeatures()37             std::vector<const char*> GetRequiredFeatures() override {
38                 return {"dawn-internal-usages"};
39             }
40 
41           public:
SetUp()42             void SetUp() override {
43                 DawnTest::SetUp();
44                 DAWN_TEST_UNSUPPORTED_IF(UsesWire());
45 
46                 gbmDevice = CreateGbmDevice();
47                 deviceVk = dawn_native::vulkan::ToBackend(dawn_native::FromAPI(device.Get()));
48 
49                 defaultGbmBo = CreateGbmBo(1, 1, true /* linear */);
50                 defaultStride = gbm_bo_get_stride_for_plane(defaultGbmBo, 0);
51                 defaultModifier = gbm_bo_get_modifier(defaultGbmBo);
52                 defaultFd = gbm_bo_get_fd(defaultGbmBo);
53 
54                 defaultDescriptor.dimension = wgpu::TextureDimension::e2D;
55                 defaultDescriptor.format = wgpu::TextureFormat::RGBA8Unorm;
56                 defaultDescriptor.size = {1, 1, 1};
57                 defaultDescriptor.sampleCount = 1;
58                 defaultDescriptor.mipLevelCount = 1;
59                 defaultDescriptor.usage = wgpu::TextureUsage::RenderAttachment |
60                                           wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst;
61             }
62 
TearDown()63             void TearDown() override {
64                 if (UsesWire()) {
65                     DawnTest::TearDown();
66                     return;
67                 }
68 
69                 gbm_bo_destroy(defaultGbmBo);
70                 gbm_device_destroy(gbmDevice);
71 
72                 DawnTest::TearDown();
73             }
74 
CreateGbmDevice()75             gbm_device* CreateGbmDevice() {
76                 // Render nodes [1] are the primary interface for communicating with the GPU on
77                 // devices that support DRM. The actual filename of the render node is
78                 // implementation-specific, so we must scan through all possible filenames to find
79                 // one that we can use [2].
80                 //
81                 // [1] https://dri.freedesktop.org/docs/drm/gpu/drm-uapi.html#render-nodes
82                 // [2]
83                 // https://cs.chromium.org/chromium/src/ui/ozone/platform/wayland/gpu/drm_render_node_path_finder.cc
84                 const uint32_t kRenderNodeStart = 128;
85                 const uint32_t kRenderNodeEnd = kRenderNodeStart + 16;
86                 const std::string kRenderNodeTemplate = "/dev/dri/renderD";
87 
88                 int renderNodeFd = -1;
89                 for (uint32_t i = kRenderNodeStart; i < kRenderNodeEnd; i++) {
90                     std::string renderNode = kRenderNodeTemplate + std::to_string(i);
91                     renderNodeFd = open(renderNode.c_str(), O_RDWR);
92                     if (renderNodeFd >= 0)
93                         break;
94                 }
95                 EXPECT_GE(renderNodeFd, 0) << "Failed to get file descriptor for render node";
96 
97                 gbm_device* gbmDevice = gbm_create_device(renderNodeFd);
98                 EXPECT_NE(gbmDevice, nullptr) << "Failed to create GBM device";
99                 return gbmDevice;
100             }
101 
CreateGbmBo(uint32_t width,uint32_t height,bool linear)102             gbm_bo* CreateGbmBo(uint32_t width, uint32_t height, bool linear) {
103                 uint32_t flags = GBM_BO_USE_RENDERING;
104                 if (linear)
105                     flags |= GBM_BO_USE_LINEAR;
106                 gbm_bo* gbmBo = gbm_bo_create(gbmDevice, width, height, GBM_FORMAT_XBGR8888, flags);
107                 EXPECT_NE(gbmBo, nullptr) << "Failed to create GBM buffer object";
108                 return gbmBo;
109             }
110 
WrapVulkanImage(wgpu::Device dawnDevice,const wgpu::TextureDescriptor * textureDescriptor,int memoryFd,uint32_t stride,uint64_t drmModifier,std::vector<int> waitFDs,bool isInitialized=true,bool expectValid=true)111             wgpu::Texture WrapVulkanImage(wgpu::Device dawnDevice,
112                                           const wgpu::TextureDescriptor* textureDescriptor,
113                                           int memoryFd,
114                                           uint32_t stride,
115                                           uint64_t drmModifier,
116                                           std::vector<int> waitFDs,
117                                           bool isInitialized = true,
118                                           bool expectValid = true) {
119                 dawn_native::vulkan::ExternalImageDescriptorDmaBuf descriptor;
120                 return WrapVulkanImage(dawnDevice, textureDescriptor, memoryFd, stride, drmModifier,
121                                        waitFDs, descriptor.releasedOldLayout,
122                                        descriptor.releasedNewLayout, isInitialized, expectValid);
123             }
124 
WrapVulkanImage(wgpu::Device dawnDevice,const wgpu::TextureDescriptor * textureDescriptor,int memoryFd,uint32_t stride,uint64_t drmModifier,std::vector<int> waitFDs,VkImageLayout releasedOldLayout,VkImageLayout releasedNewLayout,bool isInitialized=true,bool expectValid=true)125             wgpu::Texture WrapVulkanImage(wgpu::Device dawnDevice,
126                                           const wgpu::TextureDescriptor* textureDescriptor,
127                                           int memoryFd,
128                                           uint32_t stride,
129                                           uint64_t drmModifier,
130                                           std::vector<int> waitFDs,
131                                           VkImageLayout releasedOldLayout,
132                                           VkImageLayout releasedNewLayout,
133                                           bool isInitialized = true,
134                                           bool expectValid = true) {
135                 dawn_native::vulkan::ExternalImageDescriptorDmaBuf descriptor;
136                 descriptor.cTextureDescriptor =
137                     reinterpret_cast<const WGPUTextureDescriptor*>(textureDescriptor);
138                 descriptor.isInitialized = isInitialized;
139                 descriptor.stride = stride;
140                 descriptor.drmModifier = drmModifier;
141                 descriptor.memoryFD = memoryFd;
142                 descriptor.waitFDs = waitFDs;
143                 descriptor.releasedOldLayout = releasedOldLayout;
144                 descriptor.releasedNewLayout = releasedNewLayout;
145 
146                 WGPUTexture texture =
147                     dawn_native::vulkan::WrapVulkanImage(dawnDevice.Get(), &descriptor);
148 
149                 if (expectValid) {
150                     EXPECT_NE(texture, nullptr) << "Failed to wrap image, are external memory / "
151                                                    "semaphore extensions supported?";
152                 } else {
153                     EXPECT_EQ(texture, nullptr);
154                 }
155 
156                 return wgpu::Texture::Acquire(texture);
157             }
158 
159             // Exports the signal from a wrapped texture and ignores it
160             // We have to export the signal before destroying the wrapped texture else it's an
161             // assertion failure
IgnoreSignalSemaphore(wgpu::Texture wrappedTexture)162             void IgnoreSignalSemaphore(wgpu::Texture wrappedTexture) {
163                 dawn_native::vulkan::ExternalImageExportInfoDmaBuf exportInfo;
164                 dawn_native::vulkan::ExportVulkanImage(wrappedTexture.Get(), VK_IMAGE_LAYOUT_GENERAL, &exportInfo);
165                 for (int handle : exportInfo.semaphoreHandles) {
166                     ASSERT_NE(handle, -1);
167                     close(handle);
168                 }
169             }
170 
171           protected:
172             dawn_native::vulkan::Device* deviceVk;
173             gbm_device* gbmDevice;
174             wgpu::TextureDescriptor defaultDescriptor;
175             gbm_bo* defaultGbmBo;
176             int defaultFd;
177             uint32_t defaultStride;
178             uint64_t defaultModifier;
179         };
180 
181     }  // anonymous namespace
182 
183     using VulkanImageWrappingValidationTests = VulkanImageWrappingTestBase;
184 
185     // Test no error occurs if the import is valid
TEST_P(VulkanImageWrappingValidationTests,SuccessfulImport)186     TEST_P(VulkanImageWrappingValidationTests, SuccessfulImport) {
187         wgpu::Texture texture = WrapVulkanImage(device, &defaultDescriptor, defaultFd,
188                                                 defaultStride, defaultModifier, {}, true, true);
189         EXPECT_NE(texture.Get(), nullptr);
190         IgnoreSignalSemaphore(texture);
191     }
192 
193     // Test no error occurs if the import is valid with DawnTextureInternalUsageDescriptor
TEST_P(VulkanImageWrappingValidationTests,SuccessfulImportWithInternalUsageDescriptor)194     TEST_P(VulkanImageWrappingValidationTests, SuccessfulImportWithInternalUsageDescriptor) {
195         wgpu::DawnTextureInternalUsageDescriptor internalDesc = {};
196         defaultDescriptor.nextInChain = &internalDesc;
197         internalDesc.internalUsage = wgpu::TextureUsage::CopySrc;
198         internalDesc.sType = wgpu::SType::DawnTextureInternalUsageDescriptor;
199 
200         wgpu::Texture texture = WrapVulkanImage(device, &defaultDescriptor, defaultFd,
201                                                 defaultStride, defaultModifier, {}, true, true);
202         EXPECT_NE(texture.Get(), nullptr);
203         IgnoreSignalSemaphore(texture);
204     }
205 
206     // Test an error occurs if an invalid sType is the nextInChain
TEST_P(VulkanImageWrappingValidationTests,InvalidTextureDescriptor)207     TEST_P(VulkanImageWrappingValidationTests, InvalidTextureDescriptor) {
208         wgpu::ChainedStruct chainedDescriptor;
209         chainedDescriptor.sType = wgpu::SType::SurfaceDescriptorFromWindowsSwapChainPanel;
210         defaultDescriptor.nextInChain = &chainedDescriptor;
211 
212         ASSERT_DEVICE_ERROR(wgpu::Texture texture =
213                                 WrapVulkanImage(device, &defaultDescriptor, defaultFd,
214                                                 defaultStride, defaultModifier, {}, true, false));
215         EXPECT_EQ(texture.Get(), nullptr);
216         close(defaultFd);
217     }
218 
219     // Test an error occurs if the descriptor dimension isn't 2D
TEST_P(VulkanImageWrappingValidationTests,InvalidTextureDimension)220     TEST_P(VulkanImageWrappingValidationTests, InvalidTextureDimension) {
221         defaultDescriptor.dimension = wgpu::TextureDimension::e1D;
222 
223         ASSERT_DEVICE_ERROR(wgpu::Texture texture =
224                                 WrapVulkanImage(device, &defaultDescriptor, defaultFd,
225                                                 defaultStride, defaultModifier, {}, true, false));
226         EXPECT_EQ(texture.Get(), nullptr);
227         close(defaultFd);
228     }
229 
230     // Test an error occurs if the descriptor mip level count isn't 1
TEST_P(VulkanImageWrappingValidationTests,InvalidMipLevelCount)231     TEST_P(VulkanImageWrappingValidationTests, InvalidMipLevelCount) {
232         defaultDescriptor.mipLevelCount = 2;
233 
234         ASSERT_DEVICE_ERROR(wgpu::Texture texture =
235                                 WrapVulkanImage(device, &defaultDescriptor, defaultFd,
236                                                 defaultStride, defaultModifier, {}, true, false));
237         EXPECT_EQ(texture.Get(), nullptr);
238         close(defaultFd);
239     }
240 
241     // Test an error occurs if the descriptor depth isn't 1
TEST_P(VulkanImageWrappingValidationTests,InvalidDepth)242     TEST_P(VulkanImageWrappingValidationTests, InvalidDepth) {
243         defaultDescriptor.size.depthOrArrayLayers = 2;
244 
245         ASSERT_DEVICE_ERROR(wgpu::Texture texture =
246                                 WrapVulkanImage(device, &defaultDescriptor, defaultFd,
247                                                 defaultStride, defaultModifier, {}, true, false));
248         EXPECT_EQ(texture.Get(), nullptr);
249         close(defaultFd);
250     }
251 
252     // Test an error occurs if the descriptor sample count isn't 1
TEST_P(VulkanImageWrappingValidationTests,InvalidSampleCount)253     TEST_P(VulkanImageWrappingValidationTests, InvalidSampleCount) {
254         defaultDescriptor.sampleCount = 4;
255 
256         ASSERT_DEVICE_ERROR(wgpu::Texture texture =
257                                 WrapVulkanImage(device, &defaultDescriptor, defaultFd,
258                                                 defaultStride, defaultModifier, {}, true, false));
259         EXPECT_EQ(texture.Get(), nullptr);
260         close(defaultFd);
261     }
262 
263     // Test an error occurs if we try to export the signal semaphore twice
TEST_P(VulkanImageWrappingValidationTests,DoubleSignalSemaphoreExport)264     TEST_P(VulkanImageWrappingValidationTests, DoubleSignalSemaphoreExport) {
265         wgpu::Texture texture = WrapVulkanImage(device, &defaultDescriptor, defaultFd,
266                                                 defaultStride, defaultModifier, {}, true, true);
267         ASSERT_NE(texture.Get(), nullptr);
268         IgnoreSignalSemaphore(texture);
269 
270         dawn_native::vulkan::ExternalImageExportInfoDmaBuf exportInfo;
271         ASSERT_DEVICE_ERROR(bool success = dawn_native::vulkan::ExportVulkanImage(
272                                 texture.Get(), VK_IMAGE_LAYOUT_GENERAL, &exportInfo));
273         ASSERT_FALSE(success);
274     }
275 
276     // Test an error occurs if we try to export the signal semaphore from a normal texture
TEST_P(VulkanImageWrappingValidationTests,NormalTextureSignalSemaphoreExport)277     TEST_P(VulkanImageWrappingValidationTests, NormalTextureSignalSemaphoreExport) {
278         close(defaultFd);
279 
280         wgpu::Texture texture = device.CreateTexture(&defaultDescriptor);
281         ASSERT_NE(texture.Get(), nullptr);
282 
283         dawn_native::vulkan::ExternalImageExportInfoDmaBuf exportInfo;
284         ASSERT_DEVICE_ERROR(bool success = dawn_native::vulkan::ExportVulkanImage(
285                                 texture.Get(), VK_IMAGE_LAYOUT_GENERAL, &exportInfo));
286         ASSERT_FALSE(success);
287     }
288 
289     // Test an error occurs if we try to export the signal semaphore from a destroyed texture
TEST_P(VulkanImageWrappingValidationTests,DestroyedTextureSignalSemaphoreExport)290     TEST_P(VulkanImageWrappingValidationTests, DestroyedTextureSignalSemaphoreExport) {
291         close(defaultFd);
292 
293         wgpu::Texture texture = device.CreateTexture(&defaultDescriptor);
294         ASSERT_NE(texture.Get(), nullptr);
295         texture.Destroy();
296 
297         dawn_native::vulkan::ExternalImageExportInfoDmaBuf exportInfo;
298         ASSERT_DEVICE_ERROR(bool success = dawn_native::vulkan::ExportVulkanImage(
299                                 texture.Get(), VK_IMAGE_LAYOUT_GENERAL, &exportInfo));
300         ASSERT_FALSE(success);
301     }
302 
303     // Fixture to test using external memory textures through different usages.
304     // These tests are skipped if the harness is using the wire.
305     class VulkanImageWrappingUsageTests : public VulkanImageWrappingTestBase {
306       public:
SetUp()307         void SetUp() override {
308             VulkanImageWrappingTestBase::SetUp();
309             if (UsesWire()) {
310                 return;
311             }
312 
313             // Create another device based on the original
314             backendAdapter = dawn_native::vulkan::ToBackend(deviceVk->GetAdapter());
315             deviceDescriptor.forceEnabledToggles = GetParam().forceEnabledWorkarounds;
316             deviceDescriptor.forceDisabledToggles = GetParam().forceDisabledWorkarounds;
317 
318             secondDeviceVk =
319                 dawn_native::vulkan::ToBackend(backendAdapter->CreateDevice(&deviceDescriptor));
320             secondDevice = wgpu::Device::Acquire(dawn_native::ToAPI(secondDeviceVk));
321         }
322 
323       protected:
324         dawn_native::vulkan::Adapter* backendAdapter;
325         dawn_native::DawnDeviceDescriptor deviceDescriptor;
326 
327         wgpu::Device secondDevice;
328         dawn_native::vulkan::Device* secondDeviceVk;
329 
330         // Clear a texture on a given device
ClearImage(wgpu::Device dawnDevice,wgpu::Texture wrappedTexture,wgpu::Color clearColor)331         void ClearImage(wgpu::Device dawnDevice,
332                         wgpu::Texture wrappedTexture,
333                         wgpu::Color clearColor) {
334             wgpu::TextureView wrappedView = wrappedTexture.CreateView();
335 
336             // Submit a clear operation
337             utils::ComboRenderPassDescriptor renderPassDescriptor({wrappedView}, {});
338             renderPassDescriptor.cColorAttachments[0].clearColor = clearColor;
339             renderPassDescriptor.cColorAttachments[0].loadOp = wgpu::LoadOp::Clear;
340 
341             wgpu::CommandEncoder encoder = dawnDevice.CreateCommandEncoder();
342             wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDescriptor);
343             pass.EndPass();
344 
345             wgpu::CommandBuffer commands = encoder.Finish();
346 
347             wgpu::Queue queue = dawnDevice.GetQueue();
348             queue.Submit(1, &commands);
349         }
350 
351         // Submits a 1x1x1 copy from source to destination
SimpleCopyTextureToTexture(wgpu::Device dawnDevice,wgpu::Queue dawnQueue,wgpu::Texture source,wgpu::Texture destination)352         void SimpleCopyTextureToTexture(wgpu::Device dawnDevice,
353                                         wgpu::Queue dawnQueue,
354                                         wgpu::Texture source,
355                                         wgpu::Texture destination) {
356             wgpu::ImageCopyTexture copySrc = utils::CreateImageCopyTexture(source, 0, {0, 0, 0});
357             wgpu::ImageCopyTexture copyDst =
358                 utils::CreateImageCopyTexture(destination, 0, {0, 0, 0});
359 
360             wgpu::Extent3D copySize = {1, 1, 1};
361 
362             wgpu::CommandEncoder encoder = dawnDevice.CreateCommandEncoder();
363             encoder.CopyTextureToTexture(&copySrc, &copyDst, &copySize);
364             wgpu::CommandBuffer commands = encoder.Finish();
365 
366             dawnQueue.Submit(1, &commands);
367         }
368     };
369 
370     // Clear an image in |secondDevice|
371     // Verify clear color is visible in |device|
TEST_P(VulkanImageWrappingUsageTests,ClearImageAcrossDevices)372     TEST_P(VulkanImageWrappingUsageTests, ClearImageAcrossDevices) {
373         // Import the image on |secondDevice|
374         wgpu::Texture wrappedTexture = WrapVulkanImage(
375             secondDevice, &defaultDescriptor, defaultFd, defaultStride, defaultModifier, {},
376             VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
377 
378         // Clear |wrappedTexture| on |secondDevice|
379         ClearImage(secondDevice, wrappedTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f});
380 
381         dawn_native::vulkan::ExternalImageExportInfoDmaBuf exportInfo;
382         dawn_native::vulkan::ExportVulkanImage(wrappedTexture.Get(),
383                                                VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, &exportInfo);
384 
385         // Import the image to |device|, making sure we wait on signalFd
386         int nextFd = gbm_bo_get_fd(defaultGbmBo);
387         wgpu::Texture nextWrappedTexture =
388             WrapVulkanImage(device, &defaultDescriptor, nextFd, defaultStride, defaultModifier,
389                             exportInfo.semaphoreHandles, exportInfo.releasedOldLayout,
390                             exportInfo.releasedNewLayout);
391 
392         // Verify |device| sees the changes from |secondDevice|
393         EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), nextWrappedTexture, 0, 0);
394 
395         IgnoreSignalSemaphore(nextWrappedTexture);
396     }
397 
398     // Clear an image in |secondDevice|
399     // Verify clear color is not visible in |device| if we import the texture as not cleared
TEST_P(VulkanImageWrappingUsageTests,UninitializedTextureIsCleared)400     TEST_P(VulkanImageWrappingUsageTests, UninitializedTextureIsCleared) {
401         // Import the image on |secondDevice|
402         wgpu::Texture wrappedTexture = WrapVulkanImage(
403             secondDevice, &defaultDescriptor, defaultFd, defaultStride, defaultModifier, {},
404             VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
405 
406         // Clear |wrappedTexture| on |secondDevice|
407         ClearImage(secondDevice, wrappedTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f});
408 
409         dawn_native::vulkan::ExternalImageExportInfoDmaBuf exportInfo;
410         dawn_native::vulkan::ExportVulkanImage(wrappedTexture.Get(),
411                                                VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, &exportInfo);
412 
413         // Import the image to |device|, making sure we wait on signalFd
414         int nextFd = gbm_bo_get_fd(defaultGbmBo);
415         wgpu::Texture nextWrappedTexture =
416             WrapVulkanImage(device, &defaultDescriptor, nextFd, defaultStride, defaultModifier,
417                             exportInfo.semaphoreHandles, exportInfo.releasedOldLayout,
418                             exportInfo.releasedNewLayout, false);
419 
420         // Verify |device| doesn't see the changes from |secondDevice|
421         EXPECT_PIXEL_RGBA8_EQ(RGBA8(0, 0, 0, 0), nextWrappedTexture, 0, 0);
422 
423         IgnoreSignalSemaphore(nextWrappedTexture);
424     }
425 
426     // Import a texture into |secondDevice|
427     // Clear the texture on |secondDevice|
428     // Issue a copy of the imported texture inside |device| to |copyDstTexture|
429     // Verify the clear color from |secondDevice| is visible in |copyDstTexture|
TEST_P(VulkanImageWrappingUsageTests,CopyTextureToTextureSrcSync)430     TEST_P(VulkanImageWrappingUsageTests, CopyTextureToTextureSrcSync) {
431         // Import the image on |secondDevice|
432         wgpu::Texture wrappedTexture = WrapVulkanImage(
433             secondDevice, &defaultDescriptor, defaultFd, defaultStride, defaultModifier, {},
434             VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
435 
436         // Clear |wrappedTexture| on |secondDevice|
437         ClearImage(secondDevice, wrappedTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f});
438 
439         dawn_native::vulkan::ExternalImageExportInfoDmaBuf exportInfo;
440         dawn_native::vulkan::ExportVulkanImage(wrappedTexture.Get(),
441                                                VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, &exportInfo);
442 
443         // Import the image to |device|, making sure we wait on |signalFd|
444         int nextFd = gbm_bo_get_fd(defaultGbmBo);
445         wgpu::Texture deviceWrappedTexture =
446             WrapVulkanImage(device, &defaultDescriptor, nextFd, defaultStride, defaultModifier,
447                             exportInfo.semaphoreHandles, exportInfo.releasedOldLayout,
448                             exportInfo.releasedNewLayout);
449 
450         // Create a second texture on |device|
451         wgpu::Texture copyDstTexture = device.CreateTexture(&defaultDescriptor);
452 
453         // Copy |deviceWrappedTexture| into |copyDstTexture|
454         SimpleCopyTextureToTexture(device, queue, deviceWrappedTexture, copyDstTexture);
455 
456         // Verify |copyDstTexture| sees changes from |secondDevice|
457         EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), copyDstTexture, 0, 0);
458 
459         IgnoreSignalSemaphore(deviceWrappedTexture);
460     }
461 
462     // Import a texture into |device|
463     // Clear texture with color A on |device|
464     // Import same texture into |secondDevice|, waiting on the copy signal
465     // Clear the new texture with color B on |secondDevice|
466     // Copy color B using Texture to Texture copy on |secondDevice|
467     // Import texture back into |device|, waiting on color B signal
468     // Verify texture contains color B
469     // If texture destination isn't synchronized, |secondDevice| could copy color B
470     // into the texture first, then |device| writes color A
TEST_P(VulkanImageWrappingUsageTests,CopyTextureToTextureDstSync)471     TEST_P(VulkanImageWrappingUsageTests, CopyTextureToTextureDstSync) {
472         // Import the image on |device|
473         wgpu::Texture wrappedTexture = WrapVulkanImage(
474             device, &defaultDescriptor, defaultFd, defaultStride, defaultModifier, {},
475             VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
476 
477         // Clear |wrappedTexture| on |device|
478         ClearImage(device, wrappedTexture, {5 / 255.0f, 6 / 255.0f, 7 / 255.0f, 8 / 255.0f});
479 
480         dawn_native::vulkan::ExternalImageExportInfoDmaBuf exportInfo;
481         dawn_native::vulkan::ExportVulkanImage(wrappedTexture.Get(),
482                                                VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &exportInfo);
483 
484         // Import the image to |secondDevice|, making sure we wait on |signalFd|
485         int nextFd = gbm_bo_get_fd(defaultGbmBo);
486         wgpu::Texture secondDeviceWrappedTexture =
487             WrapVulkanImage(secondDevice, &defaultDescriptor, nextFd, defaultStride,
488                             defaultModifier, exportInfo.semaphoreHandles,
489                             exportInfo.releasedOldLayout, exportInfo.releasedNewLayout);
490 
491         // Create a texture with color B on |secondDevice|
492         wgpu::Texture copySrcTexture = secondDevice.CreateTexture(&defaultDescriptor);
493         ClearImage(secondDevice, copySrcTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f});
494 
495         // Copy color B on |secondDevice|
496         wgpu::Queue secondDeviceQueue = secondDevice.GetQueue();
497         SimpleCopyTextureToTexture(secondDevice, secondDeviceQueue, copySrcTexture,
498                                    secondDeviceWrappedTexture);
499 
500         // Re-import back into |device|, waiting on |secondDevice|'s signal
501         dawn_native::vulkan::ExternalImageExportInfoDmaBuf secondExportInfo;
502         dawn_native::vulkan::ExportVulkanImage(secondDeviceWrappedTexture.Get(),
503                                                VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
504                                                &secondExportInfo);
505         nextFd = gbm_bo_get_fd(defaultGbmBo);
506 
507         wgpu::Texture nextWrappedTexture =
508             WrapVulkanImage(device, &defaultDescriptor, nextFd, defaultStride, defaultModifier,
509                             secondExportInfo.semaphoreHandles, secondExportInfo.releasedOldLayout,
510                             secondExportInfo.releasedNewLayout);
511 
512         // Verify |nextWrappedTexture| contains the color from our copy
513         EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), nextWrappedTexture, 0, 0);
514 
515         IgnoreSignalSemaphore(nextWrappedTexture);
516     }
517 
518     // Import a texture from |secondDevice|
519     // Clear the texture on |secondDevice|
520     // Issue a copy of the imported texture inside |device| to |copyDstBuffer|
521     // Verify the clear color from |secondDevice| is visible in |copyDstBuffer|
TEST_P(VulkanImageWrappingUsageTests,CopyTextureToBufferSrcSync)522     TEST_P(VulkanImageWrappingUsageTests, CopyTextureToBufferSrcSync) {
523         // Import the image on |secondDevice|
524         wgpu::Texture wrappedTexture = WrapVulkanImage(
525             secondDevice, &defaultDescriptor, defaultFd, defaultStride, defaultModifier, {},
526             VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
527 
528         // Clear |wrappedTexture| on |secondDevice|
529         ClearImage(secondDevice, wrappedTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f});
530 
531         dawn_native::vulkan::ExternalImageExportInfoDmaBuf exportInfo;
532         dawn_native::vulkan::ExportVulkanImage(wrappedTexture.Get(),
533                                                VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, &exportInfo);
534 
535         // Import the image to |device|, making sure we wait on |signalFd|
536         int nextFd = gbm_bo_get_fd(defaultGbmBo);
537         wgpu::Texture deviceWrappedTexture =
538             WrapVulkanImage(device, &defaultDescriptor, nextFd, defaultStride, defaultModifier,
539                             exportInfo.semaphoreHandles, exportInfo.releasedOldLayout,
540                             exportInfo.releasedNewLayout);
541 
542         // Create a destination buffer on |device|
543         wgpu::BufferDescriptor bufferDesc;
544         bufferDesc.size = 4;
545         bufferDesc.usage = wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::CopySrc;
546         wgpu::Buffer copyDstBuffer = device.CreateBuffer(&bufferDesc);
547 
548         // Copy |deviceWrappedTexture| into |copyDstBuffer|
549         wgpu::ImageCopyTexture copySrc =
550             utils::CreateImageCopyTexture(deviceWrappedTexture, 0, {0, 0, 0});
551         wgpu::ImageCopyBuffer copyDst = utils::CreateImageCopyBuffer(copyDstBuffer, 0, 256);
552 
553         wgpu::Extent3D copySize = {1, 1, 1};
554 
555         wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
556         encoder.CopyTextureToBuffer(&copySrc, &copyDst, &copySize);
557         wgpu::CommandBuffer commands = encoder.Finish();
558         queue.Submit(1, &commands);
559 
560         // Verify |copyDstBuffer| sees changes from |secondDevice|
561         uint32_t expected = 0x04030201;
562         EXPECT_BUFFER_U32_EQ(expected, copyDstBuffer, 0);
563 
564         IgnoreSignalSemaphore(deviceWrappedTexture);
565     }
566 
567     // Import a texture into |device|
568     // Clear texture with color A on |device|
569     // Import same texture into |secondDevice|, waiting on the copy signal
570     // Copy color B using Buffer to Texture copy on |secondDevice|
571     // Import texture back into |device|, waiting on color B signal
572     // Verify texture contains color B
573     // If texture destination isn't synchronized, |secondDevice| could copy color B
574     // into the texture first, then |device| writes color A
TEST_P(VulkanImageWrappingUsageTests,CopyBufferToTextureDstSync)575     TEST_P(VulkanImageWrappingUsageTests, CopyBufferToTextureDstSync) {
576         // Import the image on |device|
577         wgpu::Texture wrappedTexture = WrapVulkanImage(
578             device, &defaultDescriptor, defaultFd, defaultStride, defaultModifier, {},
579             VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
580 
581         // Clear |wrappedTexture| on |device|
582         ClearImage(device, wrappedTexture, {5 / 255.0f, 6 / 255.0f, 7 / 255.0f, 8 / 255.0f});
583 
584         dawn_native::vulkan::ExternalImageExportInfoDmaBuf exportInfo;
585         dawn_native::vulkan::ExportVulkanImage(wrappedTexture.Get(),
586                                                VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, &exportInfo);
587 
588         // Import the image to |secondDevice|, making sure we wait on |signalFd|
589         int nextFd = gbm_bo_get_fd(defaultGbmBo);
590         wgpu::Texture secondDeviceWrappedTexture =
591             WrapVulkanImage(secondDevice, &defaultDescriptor, nextFd, defaultStride,
592                             defaultModifier, exportInfo.semaphoreHandles,
593                             exportInfo.releasedOldLayout, exportInfo.releasedNewLayout);
594 
595         // Copy color B on |secondDevice|
596         wgpu::Queue secondDeviceQueue = secondDevice.GetQueue();
597 
598         // Create a buffer on |secondDevice|
599         wgpu::Buffer copySrcBuffer =
600             utils::CreateBufferFromData(secondDevice, wgpu::BufferUsage::CopySrc, {0x04030201});
601 
602         // Copy |copySrcBuffer| into |secondDeviceWrappedTexture|
603         wgpu::ImageCopyBuffer copySrc = utils::CreateImageCopyBuffer(copySrcBuffer, 0, 256);
604         wgpu::ImageCopyTexture copyDst =
605             utils::CreateImageCopyTexture(secondDeviceWrappedTexture, 0, {0, 0, 0});
606 
607         wgpu::Extent3D copySize = {1, 1, 1};
608 
609         wgpu::CommandEncoder encoder = secondDevice.CreateCommandEncoder();
610         encoder.CopyBufferToTexture(&copySrc, &copyDst, &copySize);
611         wgpu::CommandBuffer commands = encoder.Finish();
612         secondDeviceQueue.Submit(1, &commands);
613 
614         // Re-import back into |device|, waiting on |secondDevice|'s signal
615         dawn_native::vulkan::ExternalImageExportInfoDmaBuf secondExportInfo;
616         dawn_native::vulkan::ExportVulkanImage(secondDeviceWrappedTexture.Get(),
617                                                VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
618                                                &secondExportInfo);
619         nextFd = gbm_bo_get_fd(defaultGbmBo);
620 
621         wgpu::Texture nextWrappedTexture =
622             WrapVulkanImage(device, &defaultDescriptor, nextFd, defaultStride, defaultModifier,
623                             secondExportInfo.semaphoreHandles, secondExportInfo.releasedOldLayout,
624                             secondExportInfo.releasedNewLayout);
625 
626         // Verify |nextWrappedTexture| contains the color from our copy
627         EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), nextWrappedTexture, 0, 0);
628 
629         IgnoreSignalSemaphore(nextWrappedTexture);
630     }
631 
632     // Import a texture from |secondDevice|
633     // Clear the texture on |secondDevice|
634     // Issue a copy of the imported texture inside |device| to |copyDstTexture|
635     // Issue second copy to |secondCopyDstTexture|
636     // Verify the clear color from |secondDevice| is visible in both copies
TEST_P(VulkanImageWrappingUsageTests,DoubleTextureUsage)637     TEST_P(VulkanImageWrappingUsageTests, DoubleTextureUsage) {
638         // Import the image on |secondDevice|
639         wgpu::Texture wrappedTexture = WrapVulkanImage(
640             secondDevice, &defaultDescriptor, defaultFd, defaultStride, defaultModifier, {},
641             VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
642 
643         // Clear |wrappedTexture| on |secondDevice|
644         ClearImage(secondDevice, wrappedTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f});
645 
646         dawn_native::vulkan::ExternalImageExportInfoDmaBuf exportInfo;
647         dawn_native::vulkan::ExportVulkanImage(wrappedTexture.Get(),
648                                                VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, &exportInfo);
649 
650         // Import the image to |device|, making sure we wait on |signalFd|
651         int nextFd = gbm_bo_get_fd(defaultGbmBo);
652         wgpu::Texture deviceWrappedTexture =
653             WrapVulkanImage(device, &defaultDescriptor, nextFd, defaultStride, defaultModifier,
654                             exportInfo.semaphoreHandles, exportInfo.releasedOldLayout,
655                             exportInfo.releasedNewLayout);
656 
657         // Create a second texture on |device|
658         wgpu::Texture copyDstTexture = device.CreateTexture(&defaultDescriptor);
659 
660         // Create a third texture on |device|
661         wgpu::Texture secondCopyDstTexture = device.CreateTexture(&defaultDescriptor);
662 
663         // Copy |deviceWrappedTexture| into |copyDstTexture|
664         SimpleCopyTextureToTexture(device, queue, deviceWrappedTexture, copyDstTexture);
665 
666         // Copy |deviceWrappedTexture| into |secondCopyDstTexture|
667         SimpleCopyTextureToTexture(device, queue, deviceWrappedTexture, secondCopyDstTexture);
668 
669         // Verify |copyDstTexture| sees changes from |secondDevice|
670         EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), copyDstTexture, 0, 0);
671 
672         // Verify |secondCopyDstTexture| sees changes from |secondDevice|
673         EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), secondCopyDstTexture, 0, 0);
674 
675         IgnoreSignalSemaphore(deviceWrappedTexture);
676     }
677 
678     // Tex A on device 3 (external export)
679     // Tex B on device 2 (external export)
680     // Tex C on device 1 (external export)
681     // Clear color for A on device 3
682     // Copy A->B on device 3
683     // Copy B->C on device 2 (wait on B from previous op)
684     // Copy C->D on device 1 (wait on C from previous op)
685     // Verify D has same color as A
TEST_P(VulkanImageWrappingUsageTests,ChainTextureCopy)686     TEST_P(VulkanImageWrappingUsageTests, ChainTextureCopy) {
687         // Close |defaultFd| since this test doesn't import it anywhere
688         close(defaultFd);
689 
690         // device 1 = |device|
691         // device 2 = |secondDevice|
692         // Create device 3
693         dawn_native::vulkan::Device* thirdDeviceVk =
694             dawn_native::vulkan::ToBackend(backendAdapter->CreateDevice(&deviceDescriptor));
695         wgpu::Device thirdDevice = wgpu::Device::Acquire(dawn_native::ToAPI(thirdDeviceVk));
696 
697         // Make queue for device 2 and 3
698         wgpu::Queue secondDeviceQueue = secondDevice.GetQueue();
699         wgpu::Queue thirdDeviceQueue = thirdDevice.GetQueue();
700 
701         // Create BOs for A, B, C
702         gbm_bo* gbmBoA = CreateGbmBo(1, 1, true /* linear */);
703         uint32_t fdA = gbm_bo_get_fd(gbmBoA);
704         uint32_t strideA = gbm_bo_get_stride_for_plane(gbmBoA, 0);
705         uint64_t modifierA = gbm_bo_get_modifier(gbmBoA);
706 
707         gbm_bo* gbmBoB = CreateGbmBo(1, 1, true /* linear */);
708         uint32_t fdB = gbm_bo_get_fd(gbmBoB);
709         uint32_t strideB = gbm_bo_get_stride_for_plane(gbmBoB, 0);
710         uint64_t modifierB = gbm_bo_get_modifier(gbmBoB);
711 
712         gbm_bo* gbmBoC = CreateGbmBo(1, 1, true /* linear */);
713         uint32_t fdC = gbm_bo_get_fd(gbmBoC);
714         uint32_t strideC = gbm_bo_get_stride_for_plane(gbmBoC, 0);
715         uint64_t modifierC = gbm_bo_get_modifier(gbmBoC);
716 
717         // Import TexA, TexB on device 3
718         wgpu::Texture wrappedTexADevice3 =
719             WrapVulkanImage(thirdDevice, &defaultDescriptor, fdA, strideA, modifierA, {},
720                             VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
721 
722         wgpu::Texture wrappedTexBDevice3 =
723             WrapVulkanImage(thirdDevice, &defaultDescriptor, fdB, strideB, modifierB, {},
724                             VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
725 
726         // Clear TexA
727         ClearImage(thirdDevice, wrappedTexADevice3,
728                    {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f});
729 
730         // Copy A->B
731         SimpleCopyTextureToTexture(thirdDevice, thirdDeviceQueue, wrappedTexADevice3,
732                                    wrappedTexBDevice3);
733 
734         dawn_native::vulkan::ExternalImageExportInfoDmaBuf exportInfoTexBDevice3;
735         dawn_native::vulkan::ExportVulkanImage(
736             wrappedTexBDevice3.Get(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, &exportInfoTexBDevice3);
737         IgnoreSignalSemaphore(wrappedTexADevice3);
738 
739         // Import TexB, TexC on device 2
740         fdB = gbm_bo_get_fd(gbmBoB);
741         wgpu::Texture wrappedTexBDevice2 = WrapVulkanImage(
742             secondDevice, &defaultDescriptor, fdB, strideB, modifierB,
743             exportInfoTexBDevice3.semaphoreHandles, exportInfoTexBDevice3.releasedOldLayout,
744             exportInfoTexBDevice3.releasedNewLayout);
745 
746         wgpu::Texture wrappedTexCDevice2 =
747             WrapVulkanImage(secondDevice, &defaultDescriptor, fdC, strideC, modifierC, {},
748                             VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
749 
750         // Copy B->C on device 2
751         SimpleCopyTextureToTexture(secondDevice, secondDeviceQueue, wrappedTexBDevice2,
752                                    wrappedTexCDevice2);
753 
754         dawn_native::vulkan::ExternalImageExportInfoDmaBuf exportInfoTexCDevice2;
755         dawn_native::vulkan::ExportVulkanImage(
756             wrappedTexCDevice2.Get(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, &exportInfoTexCDevice2);
757         IgnoreSignalSemaphore(wrappedTexBDevice2);
758 
759         // Import TexC on device 1
760         fdC = gbm_bo_get_fd(gbmBoC);
761         wgpu::Texture wrappedTexCDevice1 = WrapVulkanImage(
762             device, &defaultDescriptor, fdC, strideC, modifierC,
763             exportInfoTexCDevice2.semaphoreHandles, exportInfoTexCDevice2.releasedOldLayout,
764             exportInfoTexCDevice2.releasedNewLayout);
765 
766         // Create TexD on device 1
767         wgpu::Texture texD = device.CreateTexture(&defaultDescriptor);
768 
769         // Copy C->D on device 1
770         SimpleCopyTextureToTexture(device, queue, wrappedTexCDevice1, texD);
771 
772         // Verify D matches clear color
773         EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), texD, 0, 0);
774 
775         IgnoreSignalSemaphore(wrappedTexCDevice1);
776     }
777 
778     // Tests a larger image is preserved when importing
TEST_P(VulkanImageWrappingUsageTests,LargerImage)779     TEST_P(VulkanImageWrappingUsageTests, LargerImage) {
780         close(defaultFd);
781 
782         wgpu::TextureDescriptor descriptor;
783         descriptor.dimension = wgpu::TextureDimension::e2D;
784         descriptor.size.width = 640;
785         descriptor.size.height = 480;
786         descriptor.size.depthOrArrayLayers = 1;
787         descriptor.sampleCount = 1;
788         descriptor.format = wgpu::TextureFormat::BGRA8Unorm;
789         descriptor.mipLevelCount = 1;
790         descriptor.usage = wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::CopySrc;
791 
792         // Fill memory with textures
793         std::vector<wgpu::Texture> textures;
794         for (int i = 0; i < 20; i++) {
795             textures.push_back(device.CreateTexture(&descriptor));
796         }
797 
798         wgpu::Queue secondDeviceQueue = secondDevice.GetQueue();
799 
800         // Make an image on |secondDevice|
801         gbm_bo* gbmBo = CreateGbmBo(640, 480, false /* linear */);
802         uint32_t fd = gbm_bo_get_fd(gbmBo);
803         uint32_t stride = gbm_bo_get_stride_for_plane(gbmBo, 0);
804         uint64_t modifier = gbm_bo_get_modifier(gbmBo);
805 
806         // Import the image on |secondDevice|
807         wgpu::Texture wrappedTexture =
808             WrapVulkanImage(secondDevice, &descriptor, fd, stride, modifier, {},
809                             VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
810 
811         // Draw a non-trivial picture
812         uint32_t width = 640, height = 480, pixelSize = 4;
813         uint32_t bytesPerRow = Align(width * pixelSize, kTextureBytesPerRowAlignment);
814         std::vector<unsigned char> data(bytesPerRow * (height - 1) + width * pixelSize);
815 
816         for (uint32_t row = 0; row < height; row++) {
817             for (uint32_t col = 0; col < width; col++) {
818                 float normRow = static_cast<float>(row) / height;
819                 float normCol = static_cast<float>(col) / width;
820                 float dist = sqrt(normRow * normRow + normCol * normCol) * 3;
821                 dist = dist - static_cast<int>(dist);
822                 data[4 * (row * width + col)] = static_cast<unsigned char>(dist * 255);
823                 data[4 * (row * width + col) + 1] = static_cast<unsigned char>(dist * 255);
824                 data[4 * (row * width + col) + 2] = static_cast<unsigned char>(dist * 255);
825                 data[4 * (row * width + col) + 3] = 255;
826             }
827         }
828 
829         // Write the picture
830         {
831             wgpu::Buffer copySrcBuffer = utils::CreateBufferFromData(
832                 secondDevice, data.data(), data.size(), wgpu::BufferUsage::CopySrc);
833             wgpu::ImageCopyBuffer copySrc =
834                 utils::CreateImageCopyBuffer(copySrcBuffer, 0, bytesPerRow);
835             wgpu::ImageCopyTexture copyDst =
836                 utils::CreateImageCopyTexture(wrappedTexture, 0, {0, 0, 0});
837             wgpu::Extent3D copySize = {width, height, 1};
838 
839             wgpu::CommandEncoder encoder = secondDevice.CreateCommandEncoder();
840             encoder.CopyBufferToTexture(&copySrc, &copyDst, &copySize);
841             wgpu::CommandBuffer commands = encoder.Finish();
842             secondDeviceQueue.Submit(1, &commands);
843         }
844         dawn_native::vulkan::ExternalImageExportInfoDmaBuf exportInfo;
845         dawn_native::vulkan::ExportVulkanImage(wrappedTexture.Get(),
846                                                VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, &exportInfo);
847         int nextFd = gbm_bo_get_fd(gbmBo);
848 
849         // Import the image on |device|
850         wgpu::Texture nextWrappedTexture = WrapVulkanImage(
851             device, &descriptor, nextFd, stride, modifier, exportInfo.semaphoreHandles,
852             exportInfo.releasedOldLayout, exportInfo.releasedNewLayout);
853 
854         // Copy the image into a buffer for comparison
855         wgpu::BufferDescriptor copyDesc;
856         copyDesc.size = data.size();
857         copyDesc.usage = wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst;
858         wgpu::Buffer copyDstBuffer = device.CreateBuffer(&copyDesc);
859         {
860             wgpu::ImageCopyTexture copySrc =
861                 utils::CreateImageCopyTexture(nextWrappedTexture, 0, {0, 0, 0});
862             wgpu::ImageCopyBuffer copyDst =
863                 utils::CreateImageCopyBuffer(copyDstBuffer, 0, bytesPerRow);
864 
865             wgpu::Extent3D copySize = {width, height, 1};
866 
867             wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
868             encoder.CopyTextureToBuffer(&copySrc, &copyDst, &copySize);
869             wgpu::CommandBuffer commands = encoder.Finish();
870             queue.Submit(1, &commands);
871         }
872 
873         // Check the image is not corrupted on |device|
874         EXPECT_BUFFER_U32_RANGE_EQ(reinterpret_cast<uint32_t*>(data.data()), copyDstBuffer, 0,
875                                    data.size() / 4);
876 
877         IgnoreSignalSemaphore(nextWrappedTexture);
878     }
879 
880     DAWN_INSTANTIATE_TEST(VulkanImageWrappingValidationTests, VulkanBackend());
881     DAWN_INSTANTIATE_TEST(VulkanImageWrappingUsageTests, VulkanBackend());
882 
883 }}  // namespace dawn_native::vulkan
884