• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2019 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 "common/Math.h"
16 #include "tests/DawnTest.h"
17 
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 namespace dawn_native { namespace vulkan {
29 
30     namespace {
31 
32         class VulkanImageWrappingTestBase : public DawnTest {
33           protected:
GetRequiredFeatures()34             std::vector<const char*> GetRequiredFeatures() override {
35                 return {"dawn-internal-usages"};
36             }
37 
38           public:
SetUp()39             void SetUp() override {
40                 DawnTest::SetUp();
41                 DAWN_TEST_UNSUPPORTED_IF(UsesWire());
42 
43                 deviceVk = dawn_native::vulkan::ToBackend(dawn_native::FromAPI(device.Get()));
44             }
45 
46             // Creates a VkImage with external memory
CreateImage(dawn_native::vulkan::Device * deviceVk,uint32_t width,uint32_t height,VkFormat format,VkImage * image)47             ::VkResult CreateImage(dawn_native::vulkan::Device* deviceVk,
48                                    uint32_t width,
49                                    uint32_t height,
50                                    VkFormat format,
51                                    VkImage* image) {
52                 VkExternalMemoryImageCreateInfoKHR externalInfo;
53                 externalInfo.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO_KHR;
54                 externalInfo.pNext = nullptr;
55                 externalInfo.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR;
56 
57                 auto usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
58                              VK_IMAGE_USAGE_TRANSFER_DST_BIT;
59 
60                 VkImageCreateInfo createInfo;
61                 createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
62                 createInfo.pNext = &externalInfo;
63                 createInfo.flags = VK_IMAGE_CREATE_ALIAS_BIT_KHR;
64                 createInfo.imageType = VK_IMAGE_TYPE_2D;
65                 createInfo.format = format;
66                 createInfo.extent = {width, height, 1};
67                 createInfo.mipLevels = 1;
68                 createInfo.arrayLayers = 1;
69                 createInfo.samples = VK_SAMPLE_COUNT_1_BIT;
70                 createInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
71                 createInfo.usage = usage;
72                 createInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
73                 createInfo.queueFamilyIndexCount = 0;
74                 createInfo.pQueueFamilyIndices = nullptr;
75                 createInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
76 
77                 return deviceVk->fn.CreateImage(deviceVk->GetVkDevice(), &createInfo, nullptr,
78                                                 &**image);
79             }
80 
81             // Allocates memory for an image
AllocateMemory(dawn_native::vulkan::Device * deviceVk,VkImage handle,VkDeviceMemory * allocation,VkDeviceSize * allocationSize,uint32_t * memoryTypeIndex)82             ::VkResult AllocateMemory(dawn_native::vulkan::Device* deviceVk,
83                                       VkImage handle,
84                                       VkDeviceMemory* allocation,
85                                       VkDeviceSize* allocationSize,
86                                       uint32_t* memoryTypeIndex) {
87                 // Create the image memory and associate it with the container
88                 VkMemoryRequirements requirements;
89                 deviceVk->fn.GetImageMemoryRequirements(deviceVk->GetVkDevice(), handle,
90                                                         &requirements);
91 
92                 // Import memory from file descriptor
93                 VkExportMemoryAllocateInfoKHR externalInfo;
94                 externalInfo.sType = VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_KHR;
95                 externalInfo.pNext = nullptr;
96                 externalInfo.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR;
97 
98                 int bestType = deviceVk->GetResourceMemoryAllocator()->FindBestTypeIndex(
99                     requirements, MemoryKind::Opaque);
100                 VkMemoryAllocateInfo allocateInfo;
101                 allocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
102                 allocateInfo.pNext = &externalInfo;
103                 allocateInfo.allocationSize = requirements.size;
104                 allocateInfo.memoryTypeIndex = static_cast<uint32_t>(bestType);
105 
106                 *allocationSize = allocateInfo.allocationSize;
107                 *memoryTypeIndex = allocateInfo.memoryTypeIndex;
108 
109                 return deviceVk->fn.AllocateMemory(deviceVk->GetVkDevice(), &allocateInfo, nullptr,
110                                                    &**allocation);
111             }
112 
113             // Binds memory to an image
BindMemory(dawn_native::vulkan::Device * deviceVk,VkImage handle,VkDeviceMemory * memory)114             ::VkResult BindMemory(dawn_native::vulkan::Device* deviceVk,
115                                   VkImage handle,
116                                   VkDeviceMemory* memory) {
117                 return deviceVk->fn.BindImageMemory(deviceVk->GetVkDevice(), handle, *memory, 0);
118             }
119 
120             // Extracts a file descriptor representing memory on a device
GetMemoryFd(dawn_native::vulkan::Device * deviceVk,VkDeviceMemory memory)121             int GetMemoryFd(dawn_native::vulkan::Device* deviceVk, VkDeviceMemory memory) {
122                 VkMemoryGetFdInfoKHR getFdInfo;
123                 getFdInfo.sType = VK_STRUCTURE_TYPE_MEMORY_GET_FD_INFO_KHR;
124                 getFdInfo.pNext = nullptr;
125                 getFdInfo.memory = memory;
126                 getFdInfo.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR;
127 
128                 int memoryFd = -1;
129                 deviceVk->fn.GetMemoryFdKHR(deviceVk->GetVkDevice(), &getFdInfo, &memoryFd);
130 
131                 EXPECT_GE(memoryFd, 0) << "Failed to get file descriptor for external memory";
132                 return memoryFd;
133             }
134 
135             // Prepares and exports memory for an image on a given device
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)136             void CreateBindExportImage(dawn_native::vulkan::Device* deviceVk,
137                                        uint32_t width,
138                                        uint32_t height,
139                                        VkFormat format,
140                                        VkImage* handle,
141                                        VkDeviceMemory* allocation,
142                                        VkDeviceSize* allocationSize,
143                                        uint32_t* memoryTypeIndex,
144                                        int* memoryFd) {
145                 ::VkResult result = CreateImage(deviceVk, width, height, format, handle);
146                 EXPECT_EQ(result, VK_SUCCESS) << "Failed to create external image";
147 
148                 ::VkResult resultBool =
149                     AllocateMemory(deviceVk, *handle, allocation, allocationSize, memoryTypeIndex);
150                 EXPECT_EQ(resultBool, VK_SUCCESS) << "Failed to allocate external memory";
151 
152                 result = BindMemory(deviceVk, *handle, allocation);
153                 EXPECT_EQ(result, VK_SUCCESS) << "Failed to bind image memory";
154 
155                 *memoryFd = GetMemoryFd(deviceVk, *allocation);
156             }
157 
158             // Wraps a vulkan image from external memory
WrapVulkanImage(wgpu::Device dawnDevice,const wgpu::TextureDescriptor * textureDescriptor,int memoryFd,VkDeviceSize allocationSize,uint32_t memoryTypeIndex,std::vector<int> waitFDs,bool isInitialized=true,bool expectValid=true)159             wgpu::Texture WrapVulkanImage(wgpu::Device dawnDevice,
160                                           const wgpu::TextureDescriptor* textureDescriptor,
161                                           int memoryFd,
162                                           VkDeviceSize allocationSize,
163                                           uint32_t memoryTypeIndex,
164                                           std::vector<int> waitFDs,
165                                           bool isInitialized = true,
166                                           bool expectValid = true) {
167                 dawn_native::vulkan::ExternalImageDescriptorOpaqueFD descriptor;
168                 return WrapVulkanImage(dawnDevice, textureDescriptor, memoryFd, allocationSize,
169                                        memoryTypeIndex, waitFDs, descriptor.releasedOldLayout,
170                                        descriptor.releasedNewLayout, isInitialized, expectValid);
171             }
172 
WrapVulkanImage(wgpu::Device dawnDevice,const wgpu::TextureDescriptor * textureDescriptor,int memoryFd,VkDeviceSize allocationSize,uint32_t memoryTypeIndex,std::vector<int> waitFDs,VkImageLayout releasedOldLayout,VkImageLayout releasedNewLayout,bool isInitialized=true,bool expectValid=true)173             wgpu::Texture WrapVulkanImage(wgpu::Device dawnDevice,
174                                           const wgpu::TextureDescriptor* textureDescriptor,
175                                           int memoryFd,
176                                           VkDeviceSize allocationSize,
177                                           uint32_t memoryTypeIndex,
178                                           std::vector<int> waitFDs,
179                                           VkImageLayout releasedOldLayout,
180                                           VkImageLayout releasedNewLayout,
181                                           bool isInitialized = true,
182                                           bool expectValid = true) {
183                 dawn_native::vulkan::ExternalImageDescriptorOpaqueFD descriptor;
184                 descriptor.cTextureDescriptor =
185                     reinterpret_cast<const WGPUTextureDescriptor*>(textureDescriptor);
186                 descriptor.isInitialized = isInitialized;
187                 descriptor.allocationSize = allocationSize;
188                 descriptor.memoryTypeIndex = memoryTypeIndex;
189                 descriptor.memoryFD = memoryFd;
190                 descriptor.waitFDs = waitFDs;
191                 descriptor.releasedOldLayout = releasedOldLayout;
192                 descriptor.releasedNewLayout = releasedNewLayout;
193 
194                 WGPUTexture texture =
195                     dawn_native::vulkan::WrapVulkanImage(dawnDevice.Get(), &descriptor);
196 
197                 if (expectValid) {
198                     EXPECT_NE(texture, nullptr) << "Failed to wrap image, are external memory / "
199                                                    "semaphore extensions supported?";
200                 } else {
201                     EXPECT_EQ(texture, nullptr);
202                 }
203 
204                 return wgpu::Texture::Acquire(texture);
205             }
206 
207             // Exports the signal from a wrapped texture and ignores it
208             // We have to export the signal before destroying the wrapped texture else it's an
209             // assertion failure
IgnoreSignalSemaphore(wgpu::Texture wrappedTexture)210             void IgnoreSignalSemaphore(wgpu::Texture wrappedTexture) {
211                 dawn_native::vulkan::ExternalImageExportInfoOpaqueFD info;
212                 dawn_native::vulkan::ExportVulkanImage(wrappedTexture.Get(),
213                                                        VK_IMAGE_LAYOUT_GENERAL, &info);
214                 for (int handle : info.semaphoreHandles) {
215                     ASSERT_NE(handle, -1);
216                     close(handle);
217                 }
218             }
219 
220           protected:
221             dawn_native::vulkan::Device* deviceVk;
222         };
223 
224     }  // anonymous namespace
225 
226     class VulkanImageWrappingValidationTests : public VulkanImageWrappingTestBase {
227       public:
SetUp()228         void SetUp() override {
229             VulkanImageWrappingTestBase::SetUp();
230             DAWN_TEST_UNSUPPORTED_IF(UsesWire());
231 
232             CreateBindExportImage(deviceVk, 1, 1, VK_FORMAT_R8G8B8A8_UNORM, &defaultImage,
233                                   &defaultAllocation, &defaultAllocationSize,
234                                   &defaultMemoryTypeIndex, &defaultFd);
235             defaultDescriptor.dimension = wgpu::TextureDimension::e2D;
236             defaultDescriptor.format = wgpu::TextureFormat::RGBA8Unorm;
237             defaultDescriptor.size = {1, 1, 1};
238             defaultDescriptor.sampleCount = 1;
239             defaultDescriptor.mipLevelCount = 1;
240             defaultDescriptor.usage = wgpu::TextureUsage::RenderAttachment |
241                                       wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst;
242         }
243 
TearDown()244         void TearDown() override {
245             if (UsesWire()) {
246                 VulkanImageWrappingTestBase::TearDown();
247                 return;
248             }
249 
250             deviceVk->GetFencedDeleter()->DeleteWhenUnused(defaultImage);
251             deviceVk->GetFencedDeleter()->DeleteWhenUnused(defaultAllocation);
252             VulkanImageWrappingTestBase::TearDown();
253         }
254 
255       protected:
256         wgpu::TextureDescriptor defaultDescriptor;
257         VkImage defaultImage;
258         VkDeviceMemory defaultAllocation;
259         VkDeviceSize defaultAllocationSize;
260         uint32_t defaultMemoryTypeIndex;
261         int defaultFd;
262     };
263 
264     // Test no error occurs if the import is valid
TEST_P(VulkanImageWrappingValidationTests,SuccessfulImport)265     TEST_P(VulkanImageWrappingValidationTests, SuccessfulImport) {
266         wgpu::Texture texture =
267             WrapVulkanImage(device, &defaultDescriptor, defaultFd, defaultAllocationSize,
268                             defaultMemoryTypeIndex, {}, true, true);
269         EXPECT_NE(texture.Get(), nullptr);
270         IgnoreSignalSemaphore(texture);
271     }
272 
273     // Test no error occurs if the import is valid with DawnTextureInternalUsageDescriptor
TEST_P(VulkanImageWrappingValidationTests,SuccessfulImportWithInternalUsageDescriptor)274     TEST_P(VulkanImageWrappingValidationTests, SuccessfulImportWithInternalUsageDescriptor) {
275         wgpu::DawnTextureInternalUsageDescriptor internalDesc = {};
276         defaultDescriptor.nextInChain = &internalDesc;
277         internalDesc.internalUsage = wgpu::TextureUsage::CopySrc;
278         internalDesc.sType = wgpu::SType::DawnTextureInternalUsageDescriptor;
279 
280         wgpu::Texture texture =
281             WrapVulkanImage(device, &defaultDescriptor, defaultFd, defaultAllocationSize,
282                             defaultMemoryTypeIndex, {}, true, true);
283         EXPECT_NE(texture.Get(), nullptr);
284         IgnoreSignalSemaphore(texture);
285     }
286 
287     // Test an error occurs if an invalid sType is the nextInChain
TEST_P(VulkanImageWrappingValidationTests,InvalidTextureDescriptor)288     TEST_P(VulkanImageWrappingValidationTests, InvalidTextureDescriptor) {
289         wgpu::ChainedStruct chainedDescriptor;
290         chainedDescriptor.sType = wgpu::SType::SurfaceDescriptorFromWindowsSwapChainPanel;
291         defaultDescriptor.nextInChain = &chainedDescriptor;
292 
293         ASSERT_DEVICE_ERROR(wgpu::Texture texture = WrapVulkanImage(
294                                 device, &defaultDescriptor, defaultFd, defaultAllocationSize,
295                                 defaultMemoryTypeIndex, {}, true, false));
296         EXPECT_EQ(texture.Get(), nullptr);
297     }
298 
299     // Test an error occurs if the descriptor dimension isn't 2D
TEST_P(VulkanImageWrappingValidationTests,InvalidTextureDimension)300     TEST_P(VulkanImageWrappingValidationTests, InvalidTextureDimension) {
301         defaultDescriptor.dimension = wgpu::TextureDimension::e1D;
302 
303         ASSERT_DEVICE_ERROR(wgpu::Texture texture = WrapVulkanImage(
304                                 device, &defaultDescriptor, defaultFd, defaultAllocationSize,
305                                 defaultMemoryTypeIndex, {}, true, false));
306         EXPECT_EQ(texture.Get(), nullptr);
307     }
308 
309     // Test an error occurs if the descriptor mip level count isn't 1
TEST_P(VulkanImageWrappingValidationTests,InvalidMipLevelCount)310     TEST_P(VulkanImageWrappingValidationTests, InvalidMipLevelCount) {
311         defaultDescriptor.mipLevelCount = 2;
312 
313         ASSERT_DEVICE_ERROR(wgpu::Texture texture = WrapVulkanImage(
314                                 device, &defaultDescriptor, defaultFd, defaultAllocationSize,
315                                 defaultMemoryTypeIndex, {}, true, false));
316         EXPECT_EQ(texture.Get(), nullptr);
317     }
318 
319     // Test an error occurs if the descriptor depth isn't 1
TEST_P(VulkanImageWrappingValidationTests,InvalidDepth)320     TEST_P(VulkanImageWrappingValidationTests, InvalidDepth) {
321         defaultDescriptor.size.depthOrArrayLayers = 2;
322 
323         ASSERT_DEVICE_ERROR(wgpu::Texture texture = WrapVulkanImage(
324                                 device, &defaultDescriptor, defaultFd, defaultAllocationSize,
325                                 defaultMemoryTypeIndex, {}, true, false));
326         EXPECT_EQ(texture.Get(), nullptr);
327     }
328 
329     // Test an error occurs if the descriptor sample count isn't 1
TEST_P(VulkanImageWrappingValidationTests,InvalidSampleCount)330     TEST_P(VulkanImageWrappingValidationTests, InvalidSampleCount) {
331         defaultDescriptor.sampleCount = 4;
332 
333         ASSERT_DEVICE_ERROR(wgpu::Texture texture = WrapVulkanImage(
334                                 device, &defaultDescriptor, defaultFd, defaultAllocationSize,
335                                 defaultMemoryTypeIndex, {}, true, false));
336         EXPECT_EQ(texture.Get(), nullptr);
337     }
338 
339     // Test an error occurs if we try to export the signal semaphore twice
TEST_P(VulkanImageWrappingValidationTests,DoubleSignalSemaphoreExport)340     TEST_P(VulkanImageWrappingValidationTests, DoubleSignalSemaphoreExport) {
341         wgpu::Texture texture =
342             WrapVulkanImage(device, &defaultDescriptor, defaultFd, defaultAllocationSize,
343                             defaultMemoryTypeIndex, {}, true, true);
344         ASSERT_NE(texture.Get(), nullptr);
345         IgnoreSignalSemaphore(texture);
346 
347         dawn_native::vulkan::ExternalImageExportInfoOpaqueFD exportInfo;
348         ASSERT_DEVICE_ERROR(bool success = dawn_native::vulkan::ExportVulkanImage(
349                                 texture.Get(), VK_IMAGE_LAYOUT_GENERAL, &exportInfo));
350         ASSERT_FALSE(success);
351     }
352 
353     // Test an error occurs if we try to export the signal semaphore from a normal texture
TEST_P(VulkanImageWrappingValidationTests,NormalTextureSignalSemaphoreExport)354     TEST_P(VulkanImageWrappingValidationTests, NormalTextureSignalSemaphoreExport) {
355         wgpu::Texture texture = device.CreateTexture(&defaultDescriptor);
356         ASSERT_NE(texture.Get(), nullptr);
357 
358         dawn_native::vulkan::ExternalImageExportInfoOpaqueFD exportInfo;
359         ASSERT_DEVICE_ERROR(bool success = dawn_native::vulkan::ExportVulkanImage(
360                                 texture.Get(), VK_IMAGE_LAYOUT_GENERAL, &exportInfo));
361         ASSERT_FALSE(success);
362     }
363 
364     // Test an error occurs if we try to export the signal semaphore from a destroyed texture
TEST_P(VulkanImageWrappingValidationTests,DestroyedTextureSignalSemaphoreExport)365     TEST_P(VulkanImageWrappingValidationTests, DestroyedTextureSignalSemaphoreExport) {
366         wgpu::Texture texture = device.CreateTexture(&defaultDescriptor);
367         ASSERT_NE(texture.Get(), nullptr);
368         texture.Destroy();
369 
370         dawn_native::vulkan::ExternalImageExportInfoOpaqueFD exportInfo;
371         ASSERT_DEVICE_ERROR(bool success = dawn_native::vulkan::ExportVulkanImage(
372                                 texture.Get(), VK_IMAGE_LAYOUT_GENERAL, &exportInfo));
373         ASSERT_FALSE(success);
374     }
375 
376     // Fixture to test using external memory textures through different usages.
377     // These tests are skipped if the harness is using the wire.
378     class VulkanImageWrappingUsageTests : public VulkanImageWrappingTestBase {
379       public:
SetUp()380         void SetUp() override {
381             VulkanImageWrappingTestBase::SetUp();
382             DAWN_TEST_UNSUPPORTED_IF(UsesWire());
383 
384             // Create another device based on the original
385             backendAdapter = dawn_native::vulkan::ToBackend(deviceVk->GetAdapter());
386             deviceDescriptor.forceEnabledToggles = GetParam().forceEnabledWorkarounds;
387             deviceDescriptor.forceDisabledToggles = GetParam().forceDisabledWorkarounds;
388 
389             secondDeviceVk =
390                 dawn_native::vulkan::ToBackend(backendAdapter->CreateDevice(&deviceDescriptor));
391             secondDevice = wgpu::Device::Acquire(dawn_native::ToAPI(secondDeviceVk));
392 
393             CreateBindExportImage(deviceVk, 1, 1, VK_FORMAT_R8G8B8A8_UNORM, &defaultImage,
394                                   &defaultAllocation, &defaultAllocationSize,
395                                   &defaultMemoryTypeIndex, &defaultFd);
396             defaultDescriptor.dimension = wgpu::TextureDimension::e2D;
397             defaultDescriptor.format = wgpu::TextureFormat::RGBA8Unorm;
398             defaultDescriptor.size = {1, 1, 1};
399             defaultDescriptor.sampleCount = 1;
400             defaultDescriptor.mipLevelCount = 1;
401             defaultDescriptor.usage = wgpu::TextureUsage::RenderAttachment |
402                                       wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst;
403         }
404 
TearDown()405         void TearDown() override {
406             if (UsesWire()) {
407                 VulkanImageWrappingTestBase::TearDown();
408                 return;
409             }
410 
411             deviceVk->GetFencedDeleter()->DeleteWhenUnused(defaultImage);
412             deviceVk->GetFencedDeleter()->DeleteWhenUnused(defaultAllocation);
413             VulkanImageWrappingTestBase::TearDown();
414         }
415 
416       protected:
417         wgpu::Device secondDevice;
418         dawn_native::vulkan::Device* secondDeviceVk;
419 
420         dawn_native::vulkan::Adapter* backendAdapter;
421         dawn_native::DawnDeviceDescriptor deviceDescriptor;
422 
423         wgpu::TextureDescriptor defaultDescriptor;
424         VkImage defaultImage;
425         VkDeviceMemory defaultAllocation;
426         VkDeviceSize defaultAllocationSize;
427         uint32_t defaultMemoryTypeIndex;
428         int defaultFd;
429 
430         // Clear a texture on a given device
ClearImage(wgpu::Device dawnDevice,wgpu::Texture wrappedTexture,wgpu::Color clearColor)431         void ClearImage(wgpu::Device dawnDevice,
432                         wgpu::Texture wrappedTexture,
433                         wgpu::Color clearColor) {
434             wgpu::TextureView wrappedView = wrappedTexture.CreateView();
435 
436             // Submit a clear operation
437             utils::ComboRenderPassDescriptor renderPassDescriptor({wrappedView}, {});
438             renderPassDescriptor.cColorAttachments[0].clearColor = clearColor;
439 
440             wgpu::CommandEncoder encoder = dawnDevice.CreateCommandEncoder();
441             wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDescriptor);
442             pass.EndPass();
443 
444             wgpu::CommandBuffer commands = encoder.Finish();
445 
446             wgpu::Queue queue = dawnDevice.GetQueue();
447             queue.Submit(1, &commands);
448         }
449 
450         // Submits a 1x1x1 copy from source to destination
SimpleCopyTextureToTexture(wgpu::Device dawnDevice,wgpu::Queue dawnQueue,wgpu::Texture source,wgpu::Texture destination)451         void SimpleCopyTextureToTexture(wgpu::Device dawnDevice,
452                                         wgpu::Queue dawnQueue,
453                                         wgpu::Texture source,
454                                         wgpu::Texture destination) {
455             wgpu::ImageCopyTexture copySrc;
456             copySrc.texture = source;
457             copySrc.mipLevel = 0;
458             copySrc.origin = {0, 0, 0};
459 
460             wgpu::ImageCopyTexture copyDst;
461             copyDst.texture = destination;
462             copyDst.mipLevel = 0;
463             copyDst.origin = {0, 0, 0};
464 
465             wgpu::Extent3D copySize = {1, 1, 1};
466 
467             wgpu::CommandEncoder encoder = dawnDevice.CreateCommandEncoder();
468             encoder.CopyTextureToTexture(&copySrc, &copyDst, &copySize);
469             wgpu::CommandBuffer commands = encoder.Finish();
470 
471             dawnQueue.Submit(1, &commands);
472         }
473     };
474 
475     // Clear an image in |secondDevice|
476     // Verify clear color is visible in |device|
TEST_P(VulkanImageWrappingUsageTests,ClearImageAcrossDevices)477     TEST_P(VulkanImageWrappingUsageTests, ClearImageAcrossDevices) {
478         // Import the image on |secondDevice|
479         wgpu::Texture wrappedTexture =
480             WrapVulkanImage(secondDevice, &defaultDescriptor, defaultFd, defaultAllocationSize,
481                             defaultMemoryTypeIndex, {}, VK_IMAGE_LAYOUT_UNDEFINED,
482                             VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
483 
484         // Clear |wrappedTexture| on |secondDevice|
485         ClearImage(secondDevice, wrappedTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f});
486 
487         dawn_native::vulkan::ExternalImageExportInfoOpaqueFD exportInfo;
488         dawn_native::vulkan::ExportVulkanImage(wrappedTexture.Get(),
489                                                VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, &exportInfo);
490 
491         // Import the image to |device|, making sure we wait on signalFd
492         int memoryFd = GetMemoryFd(deviceVk, defaultAllocation);
493         wgpu::Texture nextWrappedTexture =
494             WrapVulkanImage(device, &defaultDescriptor, memoryFd, defaultAllocationSize,
495                             defaultMemoryTypeIndex, exportInfo.semaphoreHandles,
496                             exportInfo.releasedOldLayout, exportInfo.releasedNewLayout);
497 
498         // Verify |device| sees the changes from |secondDevice|
499         EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), nextWrappedTexture, 0, 0);
500 
501         IgnoreSignalSemaphore(nextWrappedTexture);
502     }
503 
504     // Clear an image in |secondDevice|
505     // Verify clear color is not visible in |device| if we import the texture as not cleared
TEST_P(VulkanImageWrappingUsageTests,UninitializedTextureIsCleared)506     TEST_P(VulkanImageWrappingUsageTests, UninitializedTextureIsCleared) {
507         // Import the image on |secondDevice|
508         wgpu::Texture wrappedTexture =
509             WrapVulkanImage(secondDevice, &defaultDescriptor, defaultFd, defaultAllocationSize,
510                             defaultMemoryTypeIndex, {}, VK_IMAGE_LAYOUT_UNDEFINED,
511                             VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
512 
513         // Clear |wrappedTexture| on |secondDevice|
514         ClearImage(secondDevice, wrappedTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f});
515 
516         dawn_native::vulkan::ExternalImageExportInfoOpaqueFD exportInfo;
517         dawn_native::vulkan::ExportVulkanImage(wrappedTexture.Get(),
518                                                VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, &exportInfo);
519 
520         // Import the image to |device|, making sure we wait on the semaphore
521         int memoryFd = GetMemoryFd(deviceVk, defaultAllocation);
522         wgpu::Texture nextWrappedTexture =
523             WrapVulkanImage(device, &defaultDescriptor, memoryFd, defaultAllocationSize,
524                             defaultMemoryTypeIndex, exportInfo.semaphoreHandles,
525                             exportInfo.releasedOldLayout, exportInfo.releasedNewLayout, false);
526 
527         // Verify |device| doesn't see the changes from |secondDevice|
528         EXPECT_PIXEL_RGBA8_EQ(RGBA8(0, 0, 0, 0), nextWrappedTexture, 0, 0);
529 
530         IgnoreSignalSemaphore(nextWrappedTexture);
531     }
532 
533     // Import a texture into |secondDevice|
534     // Issue a copy of the imported texture inside |device| to |copyDstTexture|
535     // Verify the clear color from |secondDevice| is visible in |copyDstTexture|
TEST_P(VulkanImageWrappingUsageTests,CopyTextureToTextureSrcSync)536     TEST_P(VulkanImageWrappingUsageTests, CopyTextureToTextureSrcSync) {
537         // Import the image on |secondDevice|
538         wgpu::Texture wrappedTexture =
539             WrapVulkanImage(secondDevice, &defaultDescriptor, defaultFd, defaultAllocationSize,
540                             defaultMemoryTypeIndex, {}, VK_IMAGE_LAYOUT_UNDEFINED,
541                             VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
542 
543         // Clear |wrappedTexture| on |secondDevice|
544         ClearImage(secondDevice, wrappedTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f});
545 
546         dawn_native::vulkan::ExternalImageExportInfoOpaqueFD exportInfo;
547         dawn_native::vulkan::ExportVulkanImage(wrappedTexture.Get(),
548                                                VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, &exportInfo);
549 
550         // Import the image to |device|, making sure we wait on the semaphore
551         int memoryFd = GetMemoryFd(deviceVk, defaultAllocation);
552         wgpu::Texture deviceWrappedTexture =
553             WrapVulkanImage(device, &defaultDescriptor, memoryFd, defaultAllocationSize,
554                             defaultMemoryTypeIndex, exportInfo.semaphoreHandles,
555                             exportInfo.releasedOldLayout, exportInfo.releasedNewLayout);
556 
557         // Create a second texture on |device|
558         wgpu::Texture copyDstTexture = device.CreateTexture(&defaultDescriptor);
559 
560         // Copy |deviceWrappedTexture| into |copyDstTexture|
561         SimpleCopyTextureToTexture(device, queue, deviceWrappedTexture, copyDstTexture);
562 
563         // Verify |copyDstTexture| sees changes from |secondDevice|
564         EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), copyDstTexture, 0, 0);
565 
566         IgnoreSignalSemaphore(deviceWrappedTexture);
567     }
568 
569     // Import a texture into |device|
570     // Copy color A into texture on |device|
571     // Import same texture into |secondDevice|, waiting on the copy signal
572     // Copy color B using Texture to Texture copy on |secondDevice|
573     // Import texture back into |device|, waiting on color B signal
574     // Verify texture contains color B
575     // If texture destination isn't synchronized, |secondDevice| could copy color B
576     // into the texture first, then |device| writes color A
TEST_P(VulkanImageWrappingUsageTests,CopyTextureToTextureDstSync)577     TEST_P(VulkanImageWrappingUsageTests, CopyTextureToTextureDstSync) {
578         // Import the image on |device|
579         wgpu::Texture wrappedTexture = WrapVulkanImage(
580             device, &defaultDescriptor, defaultFd, defaultAllocationSize, defaultMemoryTypeIndex,
581             {}, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
582 
583         // Clear |wrappedTexture| on |device|
584         ClearImage(device, wrappedTexture, {5 / 255.0f, 6 / 255.0f, 7 / 255.0f, 8 / 255.0f});
585 
586         dawn_native::vulkan::ExternalImageExportInfoOpaqueFD exportInfo;
587         dawn_native::vulkan::ExportVulkanImage(wrappedTexture.Get(),
588                                                VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &exportInfo);
589 
590         // Import the image to |secondDevice|, making sure we wait on the semaphore
591         int memoryFd = GetMemoryFd(deviceVk, defaultAllocation);
592         wgpu::Texture secondDeviceWrappedTexture =
593             WrapVulkanImage(secondDevice, &defaultDescriptor, memoryFd, defaultAllocationSize,
594                             defaultMemoryTypeIndex, exportInfo.semaphoreHandles,
595                             exportInfo.releasedOldLayout, exportInfo.releasedNewLayout);
596 
597         // Create a texture with color B on |secondDevice|
598         wgpu::Texture copySrcTexture = secondDevice.CreateTexture(&defaultDescriptor);
599         ClearImage(secondDevice, copySrcTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f});
600 
601         // Copy color B on |secondDevice|
602         wgpu::Queue secondDeviceQueue = secondDevice.GetQueue();
603         SimpleCopyTextureToTexture(secondDevice, secondDeviceQueue, copySrcTexture,
604                                    secondDeviceWrappedTexture);
605 
606         // Re-import back into |device|, waiting on |secondDevice|'s signal
607         dawn_native::vulkan::ExternalImageExportInfoOpaqueFD secondExportInfo;
608         dawn_native::vulkan::ExportVulkanImage(secondDeviceWrappedTexture.Get(),
609                                                VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
610                                                &secondExportInfo);
611         memoryFd = GetMemoryFd(deviceVk, defaultAllocation);
612 
613         wgpu::Texture nextWrappedTexture =
614             WrapVulkanImage(device, &defaultDescriptor, memoryFd, defaultAllocationSize,
615                             defaultMemoryTypeIndex, secondExportInfo.semaphoreHandles,
616                             secondExportInfo.releasedOldLayout, secondExportInfo.releasedNewLayout);
617 
618         // Verify |nextWrappedTexture| contains the color from our copy
619         EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), nextWrappedTexture, 0, 0);
620 
621         IgnoreSignalSemaphore(nextWrappedTexture);
622     }
623 
624     // Import a texture from |secondDevice|
625     // Issue a copy of the imported texture inside |device| to |copyDstBuffer|
626     // Verify the clear color from |secondDevice| is visible in |copyDstBuffer|
TEST_P(VulkanImageWrappingUsageTests,CopyTextureToBufferSrcSync)627     TEST_P(VulkanImageWrappingUsageTests, CopyTextureToBufferSrcSync) {
628         // Import the image on |secondDevice|
629         wgpu::Texture wrappedTexture =
630             WrapVulkanImage(secondDevice, &defaultDescriptor, defaultFd, defaultAllocationSize,
631                             defaultMemoryTypeIndex, {}, VK_IMAGE_LAYOUT_UNDEFINED,
632                             VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
633 
634         // Clear |wrappedTexture| on |secondDevice|
635         ClearImage(secondDevice, wrappedTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f});
636 
637         dawn_native::vulkan::ExternalImageExportInfoOpaqueFD exportInfo;
638         dawn_native::vulkan::ExportVulkanImage(wrappedTexture.Get(),
639                                                VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, &exportInfo);
640 
641         // Import the image to |device|, making sure we wait on the semaphore
642         int memoryFd = GetMemoryFd(deviceVk, defaultAllocation);
643         wgpu::Texture deviceWrappedTexture =
644             WrapVulkanImage(device, &defaultDescriptor, memoryFd, defaultAllocationSize,
645                             defaultMemoryTypeIndex, exportInfo.semaphoreHandles,
646                             exportInfo.releasedOldLayout, exportInfo.releasedNewLayout);
647 
648         // Create a destination buffer on |device|
649         wgpu::BufferDescriptor bufferDesc;
650         bufferDesc.size = 4;
651         bufferDesc.usage = wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::CopySrc;
652         wgpu::Buffer copyDstBuffer = device.CreateBuffer(&bufferDesc);
653 
654         // Copy |deviceWrappedTexture| into |copyDstBuffer|
655         wgpu::ImageCopyTexture copySrc =
656             utils::CreateImageCopyTexture(deviceWrappedTexture, 0, {0, 0, 0});
657         wgpu::ImageCopyBuffer copyDst = utils::CreateImageCopyBuffer(copyDstBuffer, 0, 256);
658 
659         wgpu::Extent3D copySize = {1, 1, 1};
660 
661         wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
662         encoder.CopyTextureToBuffer(&copySrc, &copyDst, &copySize);
663         wgpu::CommandBuffer commands = encoder.Finish();
664         queue.Submit(1, &commands);
665 
666         // Verify |copyDstBuffer| sees changes from |secondDevice|
667         uint32_t expected = 0x04030201;
668         EXPECT_BUFFER_U32_EQ(expected, copyDstBuffer, 0);
669 
670         IgnoreSignalSemaphore(deviceWrappedTexture);
671     }
672 
673     // Import a texture into |device|
674     // Copy color A into texture on |device|
675     // Import same texture into |secondDevice|, waiting on the copy signal
676     // Copy color B using Buffer to Texture copy on |secondDevice|
677     // Import texture back into |device|, waiting on color B signal
678     // Verify texture contains color B
679     // If texture destination isn't synchronized, |secondDevice| could copy color B
680     // into the texture first, then |device| writes color A
TEST_P(VulkanImageWrappingUsageTests,CopyBufferToTextureDstSync)681     TEST_P(VulkanImageWrappingUsageTests, CopyBufferToTextureDstSync) {
682         // Import the image on |device|
683         wgpu::Texture wrappedTexture = WrapVulkanImage(
684             device, &defaultDescriptor, defaultFd, defaultAllocationSize, defaultMemoryTypeIndex,
685             {}, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
686 
687         // Clear |wrappedTexture| on |device|
688         ClearImage(device, wrappedTexture, {5 / 255.0f, 6 / 255.0f, 7 / 255.0f, 8 / 255.0f});
689 
690         dawn_native::vulkan::ExternalImageExportInfoOpaqueFD exportInfo;
691         dawn_native::vulkan::ExportVulkanImage(wrappedTexture.Get(),
692                                                VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, &exportInfo);
693 
694         // Import the image to |secondDevice|, making sure we wait on |signalFd|
695         int memoryFd = GetMemoryFd(deviceVk, defaultAllocation);
696         wgpu::Texture secondDeviceWrappedTexture =
697             WrapVulkanImage(secondDevice, &defaultDescriptor, memoryFd, defaultAllocationSize,
698                             defaultMemoryTypeIndex, exportInfo.semaphoreHandles,
699                             exportInfo.releasedOldLayout, exportInfo.releasedNewLayout);
700 
701         // Copy color B on |secondDevice|
702         wgpu::Queue secondDeviceQueue = secondDevice.GetQueue();
703 
704         // Create a buffer on |secondDevice|
705         wgpu::Buffer copySrcBuffer =
706             utils::CreateBufferFromData(secondDevice, wgpu::BufferUsage::CopySrc, {0x04030201});
707 
708         // Copy |copySrcBuffer| into |secondDeviceWrappedTexture|
709         wgpu::ImageCopyBuffer copySrc = utils::CreateImageCopyBuffer(copySrcBuffer, 0, 256);
710         wgpu::ImageCopyTexture copyDst =
711             utils::CreateImageCopyTexture(secondDeviceWrappedTexture, 0, {0, 0, 0});
712 
713         wgpu::Extent3D copySize = {1, 1, 1};
714 
715         wgpu::CommandEncoder encoder = secondDevice.CreateCommandEncoder();
716         encoder.CopyBufferToTexture(&copySrc, &copyDst, &copySize);
717         wgpu::CommandBuffer commands = encoder.Finish();
718         secondDeviceQueue.Submit(1, &commands);
719 
720         // Re-import back into |device|, waiting on |secondDevice|'s signal
721         dawn_native::vulkan::ExternalImageExportInfoOpaqueFD secondExportInfo;
722         dawn_native::vulkan::ExportVulkanImage(secondDeviceWrappedTexture.Get(),
723                                                VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
724                                                &secondExportInfo);
725         memoryFd = GetMemoryFd(deviceVk, defaultAllocation);
726 
727         wgpu::Texture nextWrappedTexture =
728             WrapVulkanImage(device, &defaultDescriptor, memoryFd, defaultAllocationSize,
729                             defaultMemoryTypeIndex, secondExportInfo.semaphoreHandles,
730                             secondExportInfo.releasedOldLayout, secondExportInfo.releasedNewLayout);
731 
732         // Verify |nextWrappedTexture| contains the color from our copy
733         EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), nextWrappedTexture, 0, 0);
734 
735         IgnoreSignalSemaphore(nextWrappedTexture);
736     }
737 
738     // Import a texture from |secondDevice|
739     // Issue a copy of the imported texture inside |device| to |copyDstTexture|
740     // Issue second copy to |secondCopyDstTexture|
741     // Verify the clear color from |secondDevice| is visible in both copies
TEST_P(VulkanImageWrappingUsageTests,DoubleTextureUsage)742     TEST_P(VulkanImageWrappingUsageTests, DoubleTextureUsage) {
743         // Import the image on |secondDevice|
744         wgpu::Texture wrappedTexture =
745             WrapVulkanImage(secondDevice, &defaultDescriptor, defaultFd, defaultAllocationSize,
746                             defaultMemoryTypeIndex, {}, VK_IMAGE_LAYOUT_UNDEFINED,
747                             VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
748 
749         // Clear |wrappedTexture| on |secondDevice|
750         ClearImage(secondDevice, wrappedTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f});
751 
752         dawn_native::vulkan::ExternalImageExportInfoOpaqueFD exportInfo;
753         dawn_native::vulkan::ExportVulkanImage(wrappedTexture.Get(),
754                                                VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, &exportInfo);
755 
756         // Import the image to |device|, making sure we wait on the semaphore
757         int memoryFd = GetMemoryFd(deviceVk, defaultAllocation);
758         wgpu::Texture deviceWrappedTexture =
759             WrapVulkanImage(device, &defaultDescriptor, memoryFd, defaultAllocationSize,
760                             defaultMemoryTypeIndex, exportInfo.semaphoreHandles,
761                             exportInfo.releasedOldLayout, exportInfo.releasedNewLayout);
762 
763         // Create a second texture on |device|
764         wgpu::Texture copyDstTexture = device.CreateTexture(&defaultDescriptor);
765 
766         // Create a third texture on |device|
767         wgpu::Texture secondCopyDstTexture = device.CreateTexture(&defaultDescriptor);
768 
769         // Copy |deviceWrappedTexture| into |copyDstTexture|
770         SimpleCopyTextureToTexture(device, queue, deviceWrappedTexture, copyDstTexture);
771 
772         // Copy |deviceWrappedTexture| into |secondCopyDstTexture|
773         SimpleCopyTextureToTexture(device, queue, deviceWrappedTexture, secondCopyDstTexture);
774 
775         // Verify |copyDstTexture| sees changes from |secondDevice|
776         EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), copyDstTexture, 0, 0);
777 
778         // Verify |secondCopyDstTexture| sees changes from |secondDevice|
779         EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), secondCopyDstTexture, 0, 0);
780 
781         IgnoreSignalSemaphore(deviceWrappedTexture);
782     }
783 
784     // Tex A on device 3 (external export)
785     // Tex B on device 2 (external export)
786     // Tex C on device 1 (external export)
787     // Clear color for A on device 3
788     // Copy A->B on device 3
789     // Copy B->C on device 2 (wait on B from previous op)
790     // Copy C->D on device 1 (wait on C from previous op)
791     // Verify D has same color as A
TEST_P(VulkanImageWrappingUsageTests,ChainTextureCopy)792     TEST_P(VulkanImageWrappingUsageTests, ChainTextureCopy) {
793         // Close |defaultFd| since this test doesn't import it anywhere
794         close(defaultFd);
795 
796         // device 1 = |device|
797         // device 2 = |secondDevice|
798         // Create device 3
799         dawn_native::vulkan::Device* thirdDeviceVk =
800             dawn_native::vulkan::ToBackend(backendAdapter->CreateDevice(&deviceDescriptor));
801         wgpu::Device thirdDevice = wgpu::Device::Acquire(dawn_native::ToAPI(thirdDeviceVk));
802 
803         // Make queue for device 2 and 3
804         wgpu::Queue secondDeviceQueue = secondDevice.GetQueue();
805         wgpu::Queue thirdDeviceQueue = thirdDevice.GetQueue();
806 
807         // Allocate memory for A, B, C
808         VkImage imageA;
809         VkDeviceMemory allocationA;
810         int memoryFdA;
811         VkDeviceSize allocationSizeA;
812         uint32_t memoryTypeIndexA;
813         CreateBindExportImage(thirdDeviceVk, 1, 1, VK_FORMAT_R8G8B8A8_UNORM, &imageA, &allocationA,
814                               &allocationSizeA, &memoryTypeIndexA, &memoryFdA);
815 
816         VkImage imageB;
817         VkDeviceMemory allocationB;
818         int memoryFdB;
819         VkDeviceSize allocationSizeB;
820         uint32_t memoryTypeIndexB;
821         CreateBindExportImage(secondDeviceVk, 1, 1, VK_FORMAT_R8G8B8A8_UNORM, &imageB, &allocationB,
822                               &allocationSizeB, &memoryTypeIndexB, &memoryFdB);
823 
824         VkImage imageC;
825         VkDeviceMemory allocationC;
826         int memoryFdC;
827         VkDeviceSize allocationSizeC;
828         uint32_t memoryTypeIndexC;
829         CreateBindExportImage(deviceVk, 1, 1, VK_FORMAT_R8G8B8A8_UNORM, &imageC, &allocationC,
830                               &allocationSizeC, &memoryTypeIndexC, &memoryFdC);
831 
832         // Import TexA, TexB on device 3
833         wgpu::Texture wrappedTexADevice3 = WrapVulkanImage(
834             thirdDevice, &defaultDescriptor, memoryFdA, allocationSizeA, memoryTypeIndexA, {},
835             VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
836 
837         wgpu::Texture wrappedTexBDevice3 = WrapVulkanImage(
838             thirdDevice, &defaultDescriptor, memoryFdB, allocationSizeB, memoryTypeIndexB, {},
839             VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
840 
841         // Clear TexA
842         ClearImage(thirdDevice, wrappedTexADevice3,
843                    {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f});
844 
845         // Copy A->B
846         SimpleCopyTextureToTexture(thirdDevice, thirdDeviceQueue, wrappedTexADevice3,
847                                    wrappedTexBDevice3);
848 
849         dawn_native::vulkan::ExternalImageExportInfoOpaqueFD exportInfoTexBDevice3;
850         dawn_native::vulkan::ExportVulkanImage(
851             wrappedTexBDevice3.Get(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, &exportInfoTexBDevice3);
852 
853         IgnoreSignalSemaphore(wrappedTexADevice3);
854 
855         // Import TexB, TexC on device 2
856         memoryFdB = GetMemoryFd(secondDeviceVk, allocationB);
857         wgpu::Texture wrappedTexBDevice2 = WrapVulkanImage(
858             secondDevice, &defaultDescriptor, memoryFdB, allocationSizeB, memoryTypeIndexB,
859             exportInfoTexBDevice3.semaphoreHandles, exportInfoTexBDevice3.releasedOldLayout,
860             exportInfoTexBDevice3.releasedNewLayout);
861 
862         wgpu::Texture wrappedTexCDevice2 = WrapVulkanImage(
863             secondDevice, &defaultDescriptor, memoryFdC, allocationSizeC, memoryTypeIndexC, {},
864             VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
865 
866         // Copy B->C on device 2
867         SimpleCopyTextureToTexture(secondDevice, secondDeviceQueue, wrappedTexBDevice2,
868                                    wrappedTexCDevice2);
869 
870         dawn_native::vulkan::ExternalImageExportInfoOpaqueFD exportInfoTexCDevice2;
871         dawn_native::vulkan::ExportVulkanImage(
872             wrappedTexCDevice2.Get(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, &exportInfoTexCDevice2);
873 
874         IgnoreSignalSemaphore(wrappedTexBDevice2);
875 
876         // Import TexC on device 1
877         memoryFdC = GetMemoryFd(deviceVk, allocationC);
878         wgpu::Texture wrappedTexCDevice1 = WrapVulkanImage(
879             device, &defaultDescriptor, memoryFdC, allocationSizeC, memoryTypeIndexC,
880             exportInfoTexCDevice2.semaphoreHandles, exportInfoTexCDevice2.releasedOldLayout,
881             exportInfoTexCDevice2.releasedNewLayout);
882 
883         // Create TexD on device 1
884         wgpu::Texture texD = device.CreateTexture(&defaultDescriptor);
885 
886         // Copy C->D on device 1
887         SimpleCopyTextureToTexture(device, queue, wrappedTexCDevice1, texD);
888 
889         // Verify D matches clear color
890         EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), texD, 0, 0);
891 
892         thirdDeviceVk->GetFencedDeleter()->DeleteWhenUnused(imageA);
893         thirdDeviceVk->GetFencedDeleter()->DeleteWhenUnused(allocationA);
894         secondDeviceVk->GetFencedDeleter()->DeleteWhenUnused(imageB);
895         secondDeviceVk->GetFencedDeleter()->DeleteWhenUnused(allocationB);
896         deviceVk->GetFencedDeleter()->DeleteWhenUnused(imageC);
897         deviceVk->GetFencedDeleter()->DeleteWhenUnused(allocationC);
898 
899         IgnoreSignalSemaphore(wrappedTexCDevice1);
900     }
901 
902     // Tests a larger image is preserved when importing
TEST_P(VulkanImageWrappingUsageTests,LargerImage)903     TEST_P(VulkanImageWrappingUsageTests, LargerImage) {
904         close(defaultFd);
905 
906         wgpu::TextureDescriptor descriptor;
907         descriptor.dimension = wgpu::TextureDimension::e2D;
908         descriptor.size.width = 640;
909         descriptor.size.height = 480;
910         descriptor.size.depthOrArrayLayers = 1;
911         descriptor.sampleCount = 1;
912         descriptor.format = wgpu::TextureFormat::BGRA8Unorm;
913         descriptor.mipLevelCount = 1;
914         descriptor.usage = wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::CopySrc;
915 
916         // Fill memory with textures to trigger layout issues on AMD
917         std::vector<wgpu::Texture> textures;
918         for (int i = 0; i < 20; i++) {
919             textures.push_back(device.CreateTexture(&descriptor));
920         }
921 
922         wgpu::Queue secondDeviceQueue = secondDevice.GetQueue();
923 
924         // Make an image on |secondDevice|
925         VkImage imageA;
926         VkDeviceMemory allocationA;
927         int memoryFdA;
928         VkDeviceSize allocationSizeA;
929         uint32_t memoryTypeIndexA;
930         CreateBindExportImage(secondDeviceVk, 640, 480, VK_FORMAT_R8G8B8A8_UNORM, &imageA,
931                               &allocationA, &allocationSizeA, &memoryTypeIndexA, &memoryFdA);
932 
933         // Import the image on |secondDevice|
934         wgpu::Texture wrappedTexture =
935             WrapVulkanImage(secondDevice, &descriptor, memoryFdA, allocationSizeA, memoryTypeIndexA,
936                             {}, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
937 
938         // Draw a non-trivial picture
939         uint32_t width = 640, height = 480, pixelSize = 4;
940         uint32_t bytesPerRow = Align(width * pixelSize, kTextureBytesPerRowAlignment);
941         std::vector<unsigned char> data(bytesPerRow * (height - 1) + width * pixelSize);
942 
943         for (uint32_t row = 0; row < height; row++) {
944             for (uint32_t col = 0; col < width; col++) {
945                 float normRow = static_cast<float>(row) / height;
946                 float normCol = static_cast<float>(col) / width;
947                 float dist = sqrt(normRow * normRow + normCol * normCol) * 3;
948                 dist = dist - static_cast<int>(dist);
949                 data[4 * (row * width + col)] = static_cast<unsigned char>(dist * 255);
950                 data[4 * (row * width + col) + 1] = static_cast<unsigned char>(dist * 255);
951                 data[4 * (row * width + col) + 2] = static_cast<unsigned char>(dist * 255);
952                 data[4 * (row * width + col) + 3] = 255;
953             }
954         }
955 
956         // Write the picture
957         {
958             wgpu::Buffer copySrcBuffer = utils::CreateBufferFromData(
959                 secondDevice, data.data(), data.size(), wgpu::BufferUsage::CopySrc);
960             wgpu::ImageCopyBuffer copySrc =
961                 utils::CreateImageCopyBuffer(copySrcBuffer, 0, bytesPerRow);
962             wgpu::ImageCopyTexture copyDst =
963                 utils::CreateImageCopyTexture(wrappedTexture, 0, {0, 0, 0});
964             wgpu::Extent3D copySize = {width, height, 1};
965 
966             wgpu::CommandEncoder encoder = secondDevice.CreateCommandEncoder();
967             encoder.CopyBufferToTexture(&copySrc, &copyDst, &copySize);
968             wgpu::CommandBuffer commands = encoder.Finish();
969             secondDeviceQueue.Submit(1, &commands);
970         }
971 
972         dawn_native::vulkan::ExternalImageExportInfoOpaqueFD exportInfo;
973         dawn_native::vulkan::ExportVulkanImage(wrappedTexture.Get(),
974                                                VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, &exportInfo);
975 
976         int memoryFd = GetMemoryFd(secondDeviceVk, allocationA);
977 
978         // Import the image on |device|
979         wgpu::Texture nextWrappedTexture =
980             WrapVulkanImage(device, &descriptor, memoryFd, allocationSizeA, memoryTypeIndexA,
981                             exportInfo.semaphoreHandles, exportInfo.releasedOldLayout,
982                             exportInfo.releasedNewLayout);
983 
984         // Copy the image into a buffer for comparison
985         wgpu::BufferDescriptor copyDesc;
986         copyDesc.size = data.size();
987         copyDesc.usage = wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst;
988         wgpu::Buffer copyDstBuffer = device.CreateBuffer(&copyDesc);
989         {
990             wgpu::ImageCopyTexture copySrc =
991                 utils::CreateImageCopyTexture(nextWrappedTexture, 0, {0, 0, 0});
992             wgpu::ImageCopyBuffer copyDst =
993                 utils::CreateImageCopyBuffer(copyDstBuffer, 0, bytesPerRow);
994 
995             wgpu::Extent3D copySize = {width, height, 1};
996 
997             wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
998             encoder.CopyTextureToBuffer(&copySrc, &copyDst, &copySize);
999             wgpu::CommandBuffer commands = encoder.Finish();
1000             queue.Submit(1, &commands);
1001         }
1002 
1003         // Check the image is not corrupted on |device|
1004         EXPECT_BUFFER_U32_RANGE_EQ(reinterpret_cast<uint32_t*>(data.data()), copyDstBuffer, 0,
1005                                    data.size() / 4);
1006 
1007         IgnoreSignalSemaphore(nextWrappedTexture);
1008         secondDeviceVk->GetFencedDeleter()->DeleteWhenUnused(imageA);
1009         secondDeviceVk->GetFencedDeleter()->DeleteWhenUnused(allocationA);
1010     }
1011 
1012     DAWN_INSTANTIATE_TEST(VulkanImageWrappingValidationTests, VulkanBackend());
1013     DAWN_INSTANTIATE_TEST(VulkanImageWrappingUsageTests, VulkanBackend());
1014 
1015 }}  // namespace dawn_native::vulkan
1016