From 3148b49730b762b130c8e4d90b025d87ebdbf303 Mon Sep 17 00:00:00 2001 From: Corentin Wallez Date: Thu, 13 Jan 2022 13:11:17 +0000 Subject: [PATCH] Start introducing a "backend" for vulkan image wrapping tests Bug: dawn:221 Change-Id: I8077d6a873bbd4f4ed549b386014e10020ffc725 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/75424 Reviewed-by: Loko Kung Commit-Queue: Corentin Wallez --- src/dawn_native/DawnNative.cpp | 12 +- src/dawn_native/vulkan/VulkanBackend.cpp | 4 +- .../external_memory/MemoryServiceDmaBuf.cpp | 6 +- .../external_memory/MemoryServiceOpaqueFD.cpp | 2 +- src/include/dawn_native/DawnNative.h | 10 +- src/tests/BUILD.gn | 6 +- .../white_box/VulkanImageWrappingTests.cpp | 793 +++++++++++++ .../white_box/VulkanImageWrappingTests.h | 76 ++ .../VulkanImageWrappingTestsOpaqueFD.cpp | 1019 ----------------- .../VulkanImageWrappingTests_OpaqueFD.cpp | 270 +++++ 10 files changed, 1168 insertions(+), 1030 deletions(-) create mode 100644 src/tests/white_box/VulkanImageWrappingTests.cpp create mode 100644 src/tests/white_box/VulkanImageWrappingTests.h delete mode 100644 src/tests/white_box/VulkanImageWrappingTestsOpaqueFD.cpp create mode 100644 src/tests/white_box/VulkanImageWrappingTests_OpaqueFD.cpp diff --git a/src/dawn_native/DawnNative.cpp b/src/dawn_native/DawnNative.cpp index b166c44be7..498d9b1f6c 100644 --- a/src/dawn_native/DawnNative.cpp +++ b/src/dawn_native/DawnNative.cpp @@ -278,12 +278,20 @@ namespace dawn::native { // ExternalImageDescriptor - ExternalImageDescriptor::ExternalImageDescriptor(ExternalImageType type) : type(type) { + ExternalImageDescriptor::ExternalImageDescriptor(ExternalImageType type) : mType(type) { + } + + ExternalImageType ExternalImageDescriptor::GetType() const { + return mType; } // ExternalImageExportInfo - ExternalImageExportInfo::ExternalImageExportInfo(ExternalImageType type) : type(type) { + ExternalImageExportInfo::ExternalImageExportInfo(ExternalImageType type) : mType(type) { + } + + ExternalImageType ExternalImageExportInfo::GetType() const { + return mType; } const char* GetObjectLabelForTesting(void* objectHandle) { diff --git a/src/dawn_native/vulkan/VulkanBackend.cpp b/src/dawn_native/vulkan/VulkanBackend.cpp index f15cb8a594..f7e3560cfe 100644 --- a/src/dawn_native/vulkan/VulkanBackend.cpp +++ b/src/dawn_native/vulkan/VulkanBackend.cpp @@ -83,7 +83,7 @@ namespace dawn::native::vulkan { WGPUTexture WrapVulkanImage(WGPUDevice device, const ExternalImageDescriptorVk* descriptor) { #if defined(DAWN_PLATFORM_LINUX) - switch (descriptor->type) { + switch (descriptor->GetType()) { case ExternalImageType::OpaqueFD: case ExternalImageType::DmaBuf: { Device* backendDevice = ToBackend(FromAPI(device)); @@ -108,7 +108,7 @@ namespace dawn::native::vulkan { return false; } #if defined(DAWN_PLATFORM_LINUX) - switch (info->type) { + switch (info->GetType()) { case ExternalImageType::OpaqueFD: case ExternalImageType::DmaBuf: { Texture* backendTexture = ToBackend(FromAPI(texture)); diff --git a/src/dawn_native/vulkan/external_memory/MemoryServiceDmaBuf.cpp b/src/dawn_native/vulkan/external_memory/MemoryServiceDmaBuf.cpp index 2bf8874fd5..8cdd5baee6 100644 --- a/src/dawn_native/vulkan/external_memory/MemoryServiceDmaBuf.cpp +++ b/src/dawn_native/vulkan/external_memory/MemoryServiceDmaBuf.cpp @@ -144,7 +144,7 @@ namespace dawn::native { namespace vulkan::external_memory { if (!mSupported) { return false; } - if (descriptor->type != ExternalImageType::DmaBuf) { + if (descriptor->GetType() != ExternalImageType::DmaBuf) { return false; } const ExternalImageDescriptorDmaBuf* dmaBufDescriptor = @@ -226,7 +226,7 @@ namespace dawn::native { namespace vulkan::external_memory { ResultOrError Service::GetMemoryImportParams( const ExternalImageDescriptor* descriptor, VkImage image) { - DAWN_INVALID_IF(descriptor->type != ExternalImageType::DmaBuf, + DAWN_INVALID_IF(descriptor->GetType() != ExternalImageType::DmaBuf, "ExternalImageDescriptor is not a ExternalImageDescriptorDmaBuf."); const ExternalImageDescriptorDmaBuf* dmaBufDescriptor = @@ -290,7 +290,7 @@ namespace dawn::native { namespace vulkan::external_memory { ResultOrError Service::CreateImage(const ExternalImageDescriptor* descriptor, const VkImageCreateInfo& baseCreateInfo) { - DAWN_INVALID_IF(descriptor->type != ExternalImageType::DmaBuf, + DAWN_INVALID_IF(descriptor->GetType() != ExternalImageType::DmaBuf, "ExternalImageDescriptor is not a dma-buf descriptor."); const ExternalImageDescriptorDmaBuf* dmaBufDescriptor = diff --git a/src/dawn_native/vulkan/external_memory/MemoryServiceOpaqueFD.cpp b/src/dawn_native/vulkan/external_memory/MemoryServiceOpaqueFD.cpp index e8762d2dba..181a17bd4e 100644 --- a/src/dawn_native/vulkan/external_memory/MemoryServiceOpaqueFD.cpp +++ b/src/dawn_native/vulkan/external_memory/MemoryServiceOpaqueFD.cpp @@ -90,7 +90,7 @@ namespace dawn::native { namespace vulkan::external_memory { ResultOrError Service::GetMemoryImportParams( const ExternalImageDescriptor* descriptor, VkImage image) { - DAWN_INVALID_IF(descriptor->type != ExternalImageType::OpaqueFD, + DAWN_INVALID_IF(descriptor->GetType() != ExternalImageType::OpaqueFD, "ExternalImageDescriptor is not an OpaqueFD descriptor."); const ExternalImageDescriptorOpaqueFD* opaqueFDDescriptor = diff --git a/src/include/dawn_native/DawnNative.h b/src/include/dawn_native/DawnNative.h index e8a6e13fdd..4850d8c8a5 100644 --- a/src/include/dawn_native/DawnNative.h +++ b/src/include/dawn_native/DawnNative.h @@ -217,12 +217,15 @@ namespace dawn::native { // Common properties of external images struct DAWN_NATIVE_EXPORT ExternalImageDescriptor { public: - const ExternalImageType type; const WGPUTextureDescriptor* cTextureDescriptor; // Must match image creation params bool isInitialized; // Whether the texture is initialized on import + ExternalImageType GetType() const; protected: ExternalImageDescriptor(ExternalImageType type); + + private: + ExternalImageType mType; }; struct DAWN_NATIVE_EXPORT ExternalImageAccessDescriptor { @@ -233,11 +236,14 @@ namespace dawn::native { struct DAWN_NATIVE_EXPORT ExternalImageExportInfo { public: - const ExternalImageType type; bool isInitialized; // Whether the texture is initialized after export + ExternalImageType GetType() const; protected: ExternalImageExportInfo(ExternalImageType type); + + private: + ExternalImageType mType; }; DAWN_NATIVE_EXPORT const char* GetObjectLabelForTesting(void* objectHandle); diff --git a/src/tests/BUILD.gn b/src/tests/BUILD.gn index 6141875c5f..aab52af377 100644 --- a/src/tests/BUILD.gn +++ b/src/tests/BUILD.gn @@ -494,7 +494,11 @@ source_set("dawn_white_box_tests_sources") { if (is_chromeos) { sources += [ "white_box/VulkanImageWrappingTestsDmaBuf.cpp" ] } else if (is_linux) { - sources += [ "white_box/VulkanImageWrappingTestsOpaqueFD.cpp" ] + sources += [ + "white_box/VulkanImageWrappingTests.cpp", + "white_box/VulkanImageWrappingTests.h", + "white_box/VulkanImageWrappingTests_OpaqueFD.cpp", + ] } if (dawn_enable_error_injection) { diff --git a/src/tests/white_box/VulkanImageWrappingTests.cpp b/src/tests/white_box/VulkanImageWrappingTests.cpp new file mode 100644 index 0000000000..7cf1df0cc7 --- /dev/null +++ b/src/tests/white_box/VulkanImageWrappingTests.cpp @@ -0,0 +1,793 @@ +// Copyright 2021 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "tests/white_box/VulkanImageWrappingTests.h" + +#include "common/Math.h" +#include "dawn_native/vulkan/AdapterVk.h" +#include "dawn_native/vulkan/DeviceVk.h" +#include "tests/DawnTest.h" +#include "utils/WGPUHelpers.h" + +namespace dawn::native { namespace vulkan { + + using ExternalTexture = VulkanImageWrappingTestBackend::ExternalTexture; + using ExternalSemaphore = VulkanImageWrappingTestBackend::ExternalSemaphore; + + ExternalImageDescriptorVkForTesting::ExternalImageDescriptorVkForTesting() + : ExternalImageDescriptorVk(ExternalImageType::OpaqueFD) { + } + ExternalImageExportInfoVkForTesting::ExternalImageExportInfoVkForTesting() + : ExternalImageExportInfoVk(ExternalImageType::OpaqueFD) { + } + + namespace { + + class VulkanImageWrappingTestBase : public DawnTest { + protected: + std::vector GetRequiredFeatures() override { + return {wgpu::FeatureName::DawnInternalUsages}; + } + + public: + void SetUp() override { + DawnTest::SetUp(); + DAWN_TEST_UNSUPPORTED_IF(UsesWire()); + + mBackend = VulkanImageWrappingTestBackend::Create(device); + + defaultDescriptor.dimension = wgpu::TextureDimension::e2D; + defaultDescriptor.format = wgpu::TextureFormat::RGBA8Unorm; + defaultDescriptor.size = {1, 1, 1}; + defaultDescriptor.sampleCount = 1; + defaultDescriptor.mipLevelCount = 1; + defaultDescriptor.usage = wgpu::TextureUsage::RenderAttachment | + wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst; + + defaultTexture = mBackend->CreateTexture(1, 1, defaultDescriptor.format, + defaultDescriptor.usage); + } + + void TearDown() override { + if (UsesWire()) { + DawnTest::TearDown(); + return; + } + + defaultTexture = nullptr; + mBackend = nullptr; + DawnTest::TearDown(); + } + + wgpu::Texture WrapVulkanImage( + wgpu::Device dawnDevice, + const wgpu::TextureDescriptor* textureDescriptor, + const ExternalTexture* externalTexture, + std::vector> semaphores, + bool isInitialized = true, + bool expectValid = true) { + ExternalImageDescriptorVkForTesting descriptor; + return WrapVulkanImage(dawnDevice, textureDescriptor, externalTexture, + std::move(semaphores), descriptor.releasedOldLayout, + descriptor.releasedNewLayout, isInitialized, expectValid); + } + + wgpu::Texture WrapVulkanImage( + wgpu::Device dawnDevice, + const wgpu::TextureDescriptor* textureDescriptor, + const ExternalTexture* externalTexture, + std::vector> semaphores, + VkImageLayout releasedOldLayout, + VkImageLayout releasedNewLayout, + bool isInitialized = true, + bool expectValid = true) { + ExternalImageDescriptorVkForTesting descriptor; + descriptor.cTextureDescriptor = + reinterpret_cast(textureDescriptor); + descriptor.isInitialized = isInitialized; + descriptor.releasedOldLayout = releasedOldLayout; + descriptor.releasedNewLayout = releasedNewLayout; + + wgpu::Texture texture = mBackend->WrapImage(dawnDevice, externalTexture, descriptor, + std::move(semaphores)); + + if (expectValid) { + EXPECT_NE(texture, nullptr) << "Failed to wrap image, are external memory / " + "semaphore extensions supported?"; + } else { + EXPECT_EQ(texture, nullptr); + } + + return texture; + } + + // Exports the signal from a wrapped texture and ignores it + // We have to export the signal before destroying the wrapped texture else it's an + // assertion failure + void IgnoreSignalSemaphore(wgpu::Texture wrappedTexture) { + ExternalImageExportInfoVkForTesting exportInfo; + bool result = + mBackend->ExportImage(wrappedTexture, VK_IMAGE_LAYOUT_GENERAL, &exportInfo); + ASSERT(result); + } + + protected: + std::unique_ptr mBackend; + + wgpu::TextureDescriptor defaultDescriptor; + std::unique_ptr defaultTexture; + }; + + } // anonymous namespace + + using VulkanImageWrappingValidationTests = VulkanImageWrappingTestBase; + + // Test no error occurs if the import is valid + TEST_P(VulkanImageWrappingValidationTests, SuccessfulImport) { + wgpu::Texture texture = + WrapVulkanImage(device, &defaultDescriptor, defaultTexture.get(), {}, true, true); + EXPECT_NE(texture.Get(), nullptr); + IgnoreSignalSemaphore(texture); + } + + // Test no error occurs if the import is valid with DawnTextureInternalUsageDescriptor + TEST_P(VulkanImageWrappingValidationTests, SuccessfulImportWithInternalUsageDescriptor) { + wgpu::DawnTextureInternalUsageDescriptor internalDesc = {}; + defaultDescriptor.nextInChain = &internalDesc; + internalDesc.internalUsage = wgpu::TextureUsage::CopySrc; + internalDesc.sType = wgpu::SType::DawnTextureInternalUsageDescriptor; + + wgpu::Texture texture = + WrapVulkanImage(device, &defaultDescriptor, defaultTexture.get(), {}, true, true); + EXPECT_NE(texture.Get(), nullptr); + IgnoreSignalSemaphore(texture); + } + + // Test an error occurs if an invalid sType is the nextInChain + TEST_P(VulkanImageWrappingValidationTests, InvalidTextureDescriptor) { + wgpu::ChainedStruct chainedDescriptor; + chainedDescriptor.sType = wgpu::SType::SurfaceDescriptorFromWindowsSwapChainPanel; + defaultDescriptor.nextInChain = &chainedDescriptor; + + ASSERT_DEVICE_ERROR(wgpu::Texture texture = WrapVulkanImage( + device, &defaultDescriptor, defaultTexture.get(), {}, true, false)); + EXPECT_EQ(texture.Get(), nullptr); + } + + // Test an error occurs if the descriptor dimension isn't 2D + TEST_P(VulkanImageWrappingValidationTests, InvalidTextureDimension) { + defaultDescriptor.dimension = wgpu::TextureDimension::e1D; + + ASSERT_DEVICE_ERROR(wgpu::Texture texture = WrapVulkanImage( + device, &defaultDescriptor, defaultTexture.get(), {}, true, false)); + EXPECT_EQ(texture.Get(), nullptr); + } + + // Test an error occurs if the descriptor mip level count isn't 1 + TEST_P(VulkanImageWrappingValidationTests, InvalidMipLevelCount) { + defaultDescriptor.mipLevelCount = 2; + + ASSERT_DEVICE_ERROR(wgpu::Texture texture = WrapVulkanImage( + device, &defaultDescriptor, defaultTexture.get(), {}, true, false)); + EXPECT_EQ(texture.Get(), nullptr); + } + + // Test an error occurs if the descriptor depth isn't 1 + TEST_P(VulkanImageWrappingValidationTests, InvalidDepth) { + defaultDescriptor.size.depthOrArrayLayers = 2; + + ASSERT_DEVICE_ERROR(wgpu::Texture texture = WrapVulkanImage( + device, &defaultDescriptor, defaultTexture.get(), {}, true, false)); + EXPECT_EQ(texture.Get(), nullptr); + } + + // Test an error occurs if the descriptor sample count isn't 1 + TEST_P(VulkanImageWrappingValidationTests, InvalidSampleCount) { + defaultDescriptor.sampleCount = 4; + + ASSERT_DEVICE_ERROR(wgpu::Texture texture = WrapVulkanImage( + device, &defaultDescriptor, defaultTexture.get(), {}, true, false)); + EXPECT_EQ(texture.Get(), nullptr); + } + + // Test an error occurs if we try to export the signal semaphore twice + TEST_P(VulkanImageWrappingValidationTests, DoubleSignalSemaphoreExport) { + wgpu::Texture texture = + WrapVulkanImage(device, &defaultDescriptor, defaultTexture.get(), {}, true, true); + ASSERT_NE(texture.Get(), nullptr); + IgnoreSignalSemaphore(texture); + + ExternalImageExportInfoVkForTesting exportInfo; + ASSERT_DEVICE_ERROR( + bool success = mBackend->ExportImage(texture, VK_IMAGE_LAYOUT_GENERAL, &exportInfo)); + ASSERT_FALSE(success); + ASSERT_EQ(exportInfo.semaphores.size(), 0u); + } + + // Test an error occurs if we try to export the signal semaphore from a normal texture + TEST_P(VulkanImageWrappingValidationTests, NormalTextureSignalSemaphoreExport) { + wgpu::Texture texture = device.CreateTexture(&defaultDescriptor); + ASSERT_NE(texture.Get(), nullptr); + + ExternalImageExportInfoVkForTesting exportInfo; + ASSERT_DEVICE_ERROR( + bool success = mBackend->ExportImage(texture, VK_IMAGE_LAYOUT_GENERAL, &exportInfo)); + ASSERT_FALSE(success); + ASSERT_EQ(exportInfo.semaphores.size(), 0u); + } + + // Test an error occurs if we try to export the signal semaphore from a destroyed texture + TEST_P(VulkanImageWrappingValidationTests, DestroyedTextureSignalSemaphoreExport) { + wgpu::Texture texture = device.CreateTexture(&defaultDescriptor); + ASSERT_NE(texture.Get(), nullptr); + texture.Destroy(); + + ExternalImageExportInfoVkForTesting exportInfo; + ASSERT_DEVICE_ERROR( + bool success = mBackend->ExportImage(texture, VK_IMAGE_LAYOUT_GENERAL, &exportInfo)); + ASSERT_FALSE(success); + ASSERT_EQ(exportInfo.semaphores.size(), 0u); + } + + // Fixture to test using external memory textures through different usages. + // These tests are skipped if the harness is using the wire. + class VulkanImageWrappingUsageTests : public VulkanImageWrappingTestBase { + public: + void SetUp() override { + VulkanImageWrappingTestBase::SetUp(); + if (UsesWire()) { + return; + } + + // Create another device based on the original + backendAdapter = + dawn::native::vulkan::ToBackend(dawn::native::FromAPI(device.Get())->GetAdapter()); + deviceDescriptor.nextInChain = &togglesDesc; + togglesDesc.forceEnabledToggles = GetParam().forceEnabledWorkarounds.data(); + togglesDesc.forceEnabledTogglesCount = GetParam().forceEnabledWorkarounds.size(); + togglesDesc.forceDisabledToggles = GetParam().forceDisabledWorkarounds.data(); + togglesDesc.forceDisabledTogglesCount = GetParam().forceDisabledWorkarounds.size(); + + secondDeviceVk = + dawn::native::vulkan::ToBackend(backendAdapter->APICreateDevice(&deviceDescriptor)); + secondDevice = wgpu::Device::Acquire(dawn::native::ToAPI(secondDeviceVk)); + } + + protected: + dawn::native::vulkan::Adapter* backendAdapter; + dawn::native::DeviceDescriptor deviceDescriptor; + dawn::native::DawnTogglesDeviceDescriptor togglesDesc; + + wgpu::Device secondDevice; + dawn::native::vulkan::Device* secondDeviceVk; + + // Clear a texture on a given device + void ClearImage(wgpu::Device dawnDevice, + wgpu::Texture wrappedTexture, + wgpu::Color clearColor) { + wgpu::TextureView wrappedView = wrappedTexture.CreateView(); + + // Submit a clear operation + utils::ComboRenderPassDescriptor renderPassDescriptor({wrappedView}, {}); + renderPassDescriptor.cColorAttachments[0].clearColor = clearColor; + renderPassDescriptor.cColorAttachments[0].loadOp = wgpu::LoadOp::Clear; + + wgpu::CommandEncoder encoder = dawnDevice.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDescriptor); + pass.EndPass(); + + wgpu::CommandBuffer commands = encoder.Finish(); + + wgpu::Queue queue = dawnDevice.GetQueue(); + queue.Submit(1, &commands); + } + + // Submits a 1x1x1 copy from source to destination + void SimpleCopyTextureToTexture(wgpu::Device dawnDevice, + wgpu::Queue dawnQueue, + wgpu::Texture source, + wgpu::Texture destination) { + wgpu::ImageCopyTexture copySrc = utils::CreateImageCopyTexture(source, 0, {0, 0, 0}); + wgpu::ImageCopyTexture copyDst = + utils::CreateImageCopyTexture(destination, 0, {0, 0, 0}); + + wgpu::Extent3D copySize = {1, 1, 1}; + + wgpu::CommandEncoder encoder = dawnDevice.CreateCommandEncoder(); + encoder.CopyTextureToTexture(©Src, ©Dst, ©Size); + wgpu::CommandBuffer commands = encoder.Finish(); + + dawnQueue.Submit(1, &commands); + } + }; + + // Clear an image in |secondDevice| + // Verify clear color is visible in |device| + TEST_P(VulkanImageWrappingUsageTests, ClearImageAcrossDevices) { + // Import the image on |secondDevice| + wgpu::Texture wrappedTexture = + WrapVulkanImage(secondDevice, &defaultDescriptor, defaultTexture.get(), {}, + VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + + // Clear |wrappedTexture| on |secondDevice| + ClearImage(secondDevice, wrappedTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f}); + + ExternalImageExportInfoVkForTesting exportInfo; + ASSERT_TRUE(mBackend->ExportImage(wrappedTexture, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + &exportInfo)); + + // Import the image to |device|, making sure we wait on signalFd + wgpu::Texture nextWrappedTexture = WrapVulkanImage( + device, &defaultDescriptor, defaultTexture.get(), std::move(exportInfo.semaphores), + exportInfo.releasedOldLayout, exportInfo.releasedNewLayout); + + // Verify |device| sees the changes from |secondDevice| + EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), nextWrappedTexture, 0, 0); + + IgnoreSignalSemaphore(nextWrappedTexture); + } + + // Clear an image in |secondDevice| + // Verify clear color is not visible in |device| if we import the texture as not cleared + TEST_P(VulkanImageWrappingUsageTests, UninitializedTextureIsCleared) { + // Import the image on |secondDevice| + wgpu::Texture wrappedTexture = + WrapVulkanImage(secondDevice, &defaultDescriptor, defaultTexture.get(), {}, + VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + + // Clear |wrappedTexture| on |secondDevice| + ClearImage(secondDevice, wrappedTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f}); + + ExternalImageExportInfoVkForTesting exportInfo; + ASSERT_TRUE(mBackend->ExportImage(wrappedTexture, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + &exportInfo)); + + // Import the image to |device|, making sure we wait on signalFd + wgpu::Texture nextWrappedTexture = WrapVulkanImage( + device, &defaultDescriptor, defaultTexture.get(), std::move(exportInfo.semaphores), + exportInfo.releasedOldLayout, exportInfo.releasedNewLayout, false); + + // Verify |device| doesn't see the changes from |secondDevice| + EXPECT_PIXEL_RGBA8_EQ(RGBA8(0, 0, 0, 0), nextWrappedTexture, 0, 0); + + IgnoreSignalSemaphore(nextWrappedTexture); + } + + // Import a texture into |secondDevice| + // Clear the texture on |secondDevice| + // Issue a copy of the imported texture inside |device| to |copyDstTexture| + // Verify the clear color from |secondDevice| is visible in |copyDstTexture| + TEST_P(VulkanImageWrappingUsageTests, CopyTextureToTextureSrcSync) { + // Import the image on |secondDevice| + wgpu::Texture wrappedTexture = + WrapVulkanImage(secondDevice, &defaultDescriptor, defaultTexture.get(), {}, + VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + + // Clear |wrappedTexture| on |secondDevice| + ClearImage(secondDevice, wrappedTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f}); + + ExternalImageExportInfoVkForTesting exportInfo; + ASSERT_TRUE(mBackend->ExportImage(wrappedTexture, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + &exportInfo)); + + // Import the image to |device|, making sure we wait on |signalFd| + wgpu::Texture deviceWrappedTexture = WrapVulkanImage( + device, &defaultDescriptor, defaultTexture.get(), std::move(exportInfo.semaphores), + exportInfo.releasedOldLayout, exportInfo.releasedNewLayout); + + // Create a second texture on |device| + wgpu::Texture copyDstTexture = device.CreateTexture(&defaultDescriptor); + + // Copy |deviceWrappedTexture| into |copyDstTexture| + SimpleCopyTextureToTexture(device, queue, deviceWrappedTexture, copyDstTexture); + + // Verify |copyDstTexture| sees changes from |secondDevice| + EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), copyDstTexture, 0, 0); + + IgnoreSignalSemaphore(deviceWrappedTexture); + } + + // Import a texture into |device| + // Clear texture with color A on |device| + // Import same texture into |secondDevice|, waiting on the copy signal + // Clear the new texture with color B on |secondDevice| + // Copy color B using Texture to Texture copy on |secondDevice| + // Import texture back into |device|, waiting on color B signal + // Verify texture contains color B + // If texture destination isn't synchronized, |secondDevice| could copy color B + // into the texture first, then |device| writes color A + TEST_P(VulkanImageWrappingUsageTests, CopyTextureToTextureDstSync) { + // Import the image on |device| + wgpu::Texture wrappedTexture = + WrapVulkanImage(device, &defaultDescriptor, defaultTexture.get(), {}, + VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + + // Clear |wrappedTexture| on |device| + ClearImage(device, wrappedTexture, {5 / 255.0f, 6 / 255.0f, 7 / 255.0f, 8 / 255.0f}); + + ExternalImageExportInfoVkForTesting exportInfo; + ASSERT_TRUE(mBackend->ExportImage(wrappedTexture, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + &exportInfo)); + + // Import the image to |secondDevice|, making sure we wait on |signalFd| + wgpu::Texture secondDeviceWrappedTexture = + WrapVulkanImage(secondDevice, &defaultDescriptor, defaultTexture.get(), + std::move(exportInfo.semaphores), exportInfo.releasedOldLayout, + exportInfo.releasedNewLayout); + + // Create a texture with color B on |secondDevice| + wgpu::Texture copySrcTexture = secondDevice.CreateTexture(&defaultDescriptor); + ClearImage(secondDevice, copySrcTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f}); + + // Copy color B on |secondDevice| + wgpu::Queue secondDeviceQueue = secondDevice.GetQueue(); + SimpleCopyTextureToTexture(secondDevice, secondDeviceQueue, copySrcTexture, + secondDeviceWrappedTexture); + + // Re-import back into |device|, waiting on |secondDevice|'s signal + ExternalImageExportInfoVkForTesting secondExportInfo; + ASSERT_TRUE(mBackend->ExportImage(secondDeviceWrappedTexture, + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, &secondExportInfo)); + + wgpu::Texture nextWrappedTexture = + WrapVulkanImage(device, &defaultDescriptor, defaultTexture.get(), + std::move(secondExportInfo.semaphores), + secondExportInfo.releasedOldLayout, secondExportInfo.releasedNewLayout); + + // Verify |nextWrappedTexture| contains the color from our copy + EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), nextWrappedTexture, 0, 0); + + IgnoreSignalSemaphore(nextWrappedTexture); + } + + // Import a texture from |secondDevice| + // Clear the texture on |secondDevice| + // Issue a copy of the imported texture inside |device| to |copyDstBuffer| + // Verify the clear color from |secondDevice| is visible in |copyDstBuffer| + TEST_P(VulkanImageWrappingUsageTests, CopyTextureToBufferSrcSync) { + // Import the image on |secondDevice| + wgpu::Texture wrappedTexture = + WrapVulkanImage(secondDevice, &defaultDescriptor, defaultTexture.get(), {}, + VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + + // Clear |wrappedTexture| on |secondDevice| + ClearImage(secondDevice, wrappedTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f}); + + ExternalImageExportInfoVkForTesting exportInfo; + ASSERT_TRUE(mBackend->ExportImage(wrappedTexture, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + &exportInfo)); + + // Import the image to |device|, making sure we wait on |signalFd| + wgpu::Texture deviceWrappedTexture = WrapVulkanImage( + device, &defaultDescriptor, defaultTexture.get(), std::move(exportInfo.semaphores), + exportInfo.releasedOldLayout, exportInfo.releasedNewLayout); + + // Create a destination buffer on |device| + wgpu::BufferDescriptor bufferDesc; + bufferDesc.size = 4; + bufferDesc.usage = wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::CopySrc; + wgpu::Buffer copyDstBuffer = device.CreateBuffer(&bufferDesc); + + // Copy |deviceWrappedTexture| into |copyDstBuffer| + wgpu::ImageCopyTexture copySrc = + utils::CreateImageCopyTexture(deviceWrappedTexture, 0, {0, 0, 0}); + wgpu::ImageCopyBuffer copyDst = utils::CreateImageCopyBuffer(copyDstBuffer, 0, 256); + + wgpu::Extent3D copySize = {1, 1, 1}; + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.CopyTextureToBuffer(©Src, ©Dst, ©Size); + wgpu::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); + + // Verify |copyDstBuffer| sees changes from |secondDevice| + uint32_t expected = 0x04030201; + EXPECT_BUFFER_U32_EQ(expected, copyDstBuffer, 0); + + IgnoreSignalSemaphore(deviceWrappedTexture); + } + + // Import a texture into |device| + // Clear texture with color A on |device| + // Import same texture into |secondDevice|, waiting on the copy signal + // Copy color B using Buffer to Texture copy on |secondDevice| + // Import texture back into |device|, waiting on color B signal + // Verify texture contains color B + // If texture destination isn't synchronized, |secondDevice| could copy color B + // into the texture first, then |device| writes color A + TEST_P(VulkanImageWrappingUsageTests, CopyBufferToTextureDstSync) { + // Import the image on |device| + wgpu::Texture wrappedTexture = + WrapVulkanImage(device, &defaultDescriptor, defaultTexture.get(), {}, + VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + + // Clear |wrappedTexture| on |device| + ClearImage(device, wrappedTexture, {5 / 255.0f, 6 / 255.0f, 7 / 255.0f, 8 / 255.0f}); + + ExternalImageExportInfoVkForTesting exportInfo; + ASSERT_TRUE(mBackend->ExportImage(wrappedTexture, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + &exportInfo)); + + // Import the image to |secondDevice|, making sure we wait on |signalFd| + wgpu::Texture secondDeviceWrappedTexture = + WrapVulkanImage(secondDevice, &defaultDescriptor, defaultTexture.get(), + std::move(exportInfo.semaphores), exportInfo.releasedOldLayout, + exportInfo.releasedNewLayout); + + // Copy color B on |secondDevice| + wgpu::Queue secondDeviceQueue = secondDevice.GetQueue(); + + // Create a buffer on |secondDevice| + wgpu::Buffer copySrcBuffer = + utils::CreateBufferFromData(secondDevice, wgpu::BufferUsage::CopySrc, {0x04030201}); + + // Copy |copySrcBuffer| into |secondDeviceWrappedTexture| + wgpu::ImageCopyBuffer copySrc = utils::CreateImageCopyBuffer(copySrcBuffer, 0, 256); + wgpu::ImageCopyTexture copyDst = + utils::CreateImageCopyTexture(secondDeviceWrappedTexture, 0, {0, 0, 0}); + + wgpu::Extent3D copySize = {1, 1, 1}; + + wgpu::CommandEncoder encoder = secondDevice.CreateCommandEncoder(); + encoder.CopyBufferToTexture(©Src, ©Dst, ©Size); + wgpu::CommandBuffer commands = encoder.Finish(); + secondDeviceQueue.Submit(1, &commands); + + // Re-import back into |device|, waiting on |secondDevice|'s signal + ExternalImageExportInfoVkForTesting secondExportInfo; + ASSERT_TRUE(mBackend->ExportImage(secondDeviceWrappedTexture, + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, &secondExportInfo)); + + wgpu::Texture nextWrappedTexture = + WrapVulkanImage(device, &defaultDescriptor, defaultTexture.get(), + std::move(secondExportInfo.semaphores), + secondExportInfo.releasedOldLayout, secondExportInfo.releasedNewLayout); + + // Verify |nextWrappedTexture| contains the color from our copy + EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), nextWrappedTexture, 0, 0); + + IgnoreSignalSemaphore(nextWrappedTexture); + } + + // Import a texture from |secondDevice| + // Clear the texture on |secondDevice| + // Issue a copy of the imported texture inside |device| to |copyDstTexture| + // Issue second copy to |secondCopyDstTexture| + // Verify the clear color from |secondDevice| is visible in both copies + TEST_P(VulkanImageWrappingUsageTests, DoubleTextureUsage) { + // Import the image on |secondDevice| + wgpu::Texture wrappedTexture = + WrapVulkanImage(secondDevice, &defaultDescriptor, defaultTexture.get(), {}, + VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + + // Clear |wrappedTexture| on |secondDevice| + ClearImage(secondDevice, wrappedTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f}); + + ExternalImageExportInfoVkForTesting exportInfo; + ASSERT_TRUE(mBackend->ExportImage(wrappedTexture, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + &exportInfo)); + + // Import the image to |device|, making sure we wait on |signalFd| + wgpu::Texture deviceWrappedTexture = WrapVulkanImage( + device, &defaultDescriptor, defaultTexture.get(), std::move(exportInfo.semaphores), + exportInfo.releasedOldLayout, exportInfo.releasedNewLayout); + + // Create a second texture on |device| + wgpu::Texture copyDstTexture = device.CreateTexture(&defaultDescriptor); + + // Create a third texture on |device| + wgpu::Texture secondCopyDstTexture = device.CreateTexture(&defaultDescriptor); + + // Copy |deviceWrappedTexture| into |copyDstTexture| + SimpleCopyTextureToTexture(device, queue, deviceWrappedTexture, copyDstTexture); + + // Copy |deviceWrappedTexture| into |secondCopyDstTexture| + SimpleCopyTextureToTexture(device, queue, deviceWrappedTexture, secondCopyDstTexture); + + // Verify |copyDstTexture| sees changes from |secondDevice| + EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), copyDstTexture, 0, 0); + + // Verify |secondCopyDstTexture| sees changes from |secondDevice| + EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), secondCopyDstTexture, 0, 0); + + IgnoreSignalSemaphore(deviceWrappedTexture); + } + + // Tex A on device 3 (external export) + // Tex B on device 2 (external export) + // Tex C on device 1 (external export) + // Clear color for A on device 3 + // Copy A->B on device 3 + // Copy B->C on device 2 (wait on B from previous op) + // Copy C->D on device 1 (wait on C from previous op) + // Verify D has same color as A + TEST_P(VulkanImageWrappingUsageTests, ChainTextureCopy) { + // device 1 = |device| + // device 2 = |secondDevice| + // Create device 3 + dawn::native::vulkan::Device* thirdDeviceVk = + dawn::native::vulkan::ToBackend(backendAdapter->APICreateDevice(&deviceDescriptor)); + wgpu::Device thirdDevice = wgpu::Device::Acquire(dawn::native::ToAPI(thirdDeviceVk)); + + // Make queue for device 2 and 3 + wgpu::Queue secondDeviceQueue = secondDevice.GetQueue(); + wgpu::Queue thirdDeviceQueue = thirdDevice.GetQueue(); + + // Create textures A, B, C + std::unique_ptr textureA = + mBackend->CreateTexture(1, 1, wgpu::TextureFormat::RGBA8Unorm, defaultDescriptor.usage); + std::unique_ptr textureB = + mBackend->CreateTexture(1, 1, wgpu::TextureFormat::RGBA8Unorm, defaultDescriptor.usage); + std::unique_ptr textureC = + mBackend->CreateTexture(1, 1, wgpu::TextureFormat::RGBA8Unorm, defaultDescriptor.usage); + + // Import TexA, TexB on device 3 + wgpu::Texture wrappedTexADevice3 = + WrapVulkanImage(thirdDevice, &defaultDescriptor, textureA.get(), {}, + VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + + wgpu::Texture wrappedTexBDevice3 = + WrapVulkanImage(thirdDevice, &defaultDescriptor, textureB.get(), {}, + VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + + // Clear TexA + ClearImage(thirdDevice, wrappedTexADevice3, + {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f}); + + // Copy A->B + SimpleCopyTextureToTexture(thirdDevice, thirdDeviceQueue, wrappedTexADevice3, + wrappedTexBDevice3); + + ExternalImageExportInfoVkForTesting exportInfoTexBDevice3; + ASSERT_TRUE(mBackend->ExportImage(wrappedTexBDevice3, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + &exportInfoTexBDevice3)); + IgnoreSignalSemaphore(wrappedTexADevice3); + + // Import TexB, TexC on device 2 + wgpu::Texture wrappedTexBDevice2 = WrapVulkanImage( + secondDevice, &defaultDescriptor, textureB.get(), + std::move(exportInfoTexBDevice3.semaphores), exportInfoTexBDevice3.releasedOldLayout, + exportInfoTexBDevice3.releasedNewLayout); + + wgpu::Texture wrappedTexCDevice2 = + WrapVulkanImage(secondDevice, &defaultDescriptor, textureC.get(), {}, + VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + + // Copy B->C on device 2 + SimpleCopyTextureToTexture(secondDevice, secondDeviceQueue, wrappedTexBDevice2, + wrappedTexCDevice2); + + ExternalImageExportInfoVkForTesting exportInfoTexCDevice2; + ASSERT_TRUE(mBackend->ExportImage(wrappedTexCDevice2, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + &exportInfoTexCDevice2)); + IgnoreSignalSemaphore(wrappedTexBDevice2); + + // Import TexC on device 1 + wgpu::Texture wrappedTexCDevice1 = WrapVulkanImage( + device, &defaultDescriptor, textureC.get(), std::move(exportInfoTexCDevice2.semaphores), + exportInfoTexCDevice2.releasedOldLayout, exportInfoTexCDevice2.releasedNewLayout); + + // Create TexD on device 1 + wgpu::Texture texD = device.CreateTexture(&defaultDescriptor); + + // Copy C->D on device 1 + SimpleCopyTextureToTexture(device, queue, wrappedTexCDevice1, texD); + + // Verify D matches clear color + EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), texD, 0, 0); + + IgnoreSignalSemaphore(wrappedTexCDevice1); + } + + // Tests a larger image is preserved when importing + TEST_P(VulkanImageWrappingUsageTests, LargerImage) { + wgpu::TextureDescriptor descriptor; + descriptor.dimension = wgpu::TextureDimension::e2D; + descriptor.size.width = 640; + descriptor.size.height = 480; + descriptor.size.depthOrArrayLayers = 1; + descriptor.sampleCount = 1; + descriptor.format = wgpu::TextureFormat::RGBA8Unorm; + descriptor.mipLevelCount = 1; + descriptor.usage = wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::CopySrc; + + // Fill memory with textures + std::vector textures; + for (int i = 0; i < 20; i++) { + textures.push_back(device.CreateTexture(&descriptor)); + } + + wgpu::Queue secondDeviceQueue = secondDevice.GetQueue(); + + // Make an image on |secondDevice| + std::unique_ptr texture = mBackend->CreateTexture( + descriptor.size.width, descriptor.size.height, descriptor.format, descriptor.usage); + + // Import the image on |secondDevice| + wgpu::Texture wrappedTexture = + WrapVulkanImage(secondDevice, &descriptor, texture.get(), {}, VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + + // Draw a non-trivial picture + uint32_t width = 640, height = 480, pixelSize = 4; + uint32_t bytesPerRow = Align(width * pixelSize, kTextureBytesPerRowAlignment); + std::vector data(bytesPerRow * (height - 1) + width * pixelSize); + + for (uint32_t row = 0; row < height; row++) { + for (uint32_t col = 0; col < width; col++) { + float normRow = static_cast(row) / height; + float normCol = static_cast(col) / width; + float dist = sqrt(normRow * normRow + normCol * normCol) * 3; + dist = dist - static_cast(dist); + data[4 * (row * width + col)] = static_cast(dist * 255); + data[4 * (row * width + col) + 1] = static_cast(dist * 255); + data[4 * (row * width + col) + 2] = static_cast(dist * 255); + data[4 * (row * width + col) + 3] = 255; + } + } + + // Write the picture + { + wgpu::Buffer copySrcBuffer = utils::CreateBufferFromData( + secondDevice, data.data(), data.size(), wgpu::BufferUsage::CopySrc); + wgpu::ImageCopyBuffer copySrc = + utils::CreateImageCopyBuffer(copySrcBuffer, 0, bytesPerRow); + wgpu::ImageCopyTexture copyDst = + utils::CreateImageCopyTexture(wrappedTexture, 0, {0, 0, 0}); + wgpu::Extent3D copySize = {width, height, 1}; + + wgpu::CommandEncoder encoder = secondDevice.CreateCommandEncoder(); + encoder.CopyBufferToTexture(©Src, ©Dst, ©Size); + wgpu::CommandBuffer commands = encoder.Finish(); + secondDeviceQueue.Submit(1, &commands); + } + ExternalImageExportInfoVkForTesting exportInfo; + ASSERT_TRUE(mBackend->ExportImage(wrappedTexture, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + &exportInfo)); + + // Import the image on |device| + wgpu::Texture nextWrappedTexture = + WrapVulkanImage(device, &descriptor, texture.get(), std::move(exportInfo.semaphores), + exportInfo.releasedOldLayout, exportInfo.releasedNewLayout); + + // Copy the image into a buffer for comparison + wgpu::BufferDescriptor copyDesc; + copyDesc.size = data.size(); + copyDesc.usage = wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst; + wgpu::Buffer copyDstBuffer = device.CreateBuffer(©Desc); + { + wgpu::ImageCopyTexture copySrc = + utils::CreateImageCopyTexture(nextWrappedTexture, 0, {0, 0, 0}); + wgpu::ImageCopyBuffer copyDst = + utils::CreateImageCopyBuffer(copyDstBuffer, 0, bytesPerRow); + + wgpu::Extent3D copySize = {width, height, 1}; + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.CopyTextureToBuffer(©Src, ©Dst, ©Size); + wgpu::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); + } + + // Check the image is not corrupted on |device| + EXPECT_BUFFER_U32_RANGE_EQ(reinterpret_cast(data.data()), copyDstBuffer, 0, + data.size() / 4); + + IgnoreSignalSemaphore(nextWrappedTexture); + } + + DAWN_INSTANTIATE_TEST(VulkanImageWrappingValidationTests, VulkanBackend()); + DAWN_INSTANTIATE_TEST(VulkanImageWrappingUsageTests, VulkanBackend()); + +}} // namespace dawn::native::vulkan diff --git a/src/tests/white_box/VulkanImageWrappingTests.h b/src/tests/white_box/VulkanImageWrappingTests.h new file mode 100644 index 0000000000..6c2deb733f --- /dev/null +++ b/src/tests/white_box/VulkanImageWrappingTests.h @@ -0,0 +1,76 @@ +// Copyright 2021 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef TESTS_VULKANIMAGEWRAPPINGTESTS_H_ +#define TESTS_VULKANIMAGEWRAPPINGTESTS_H_ + +// This must be above all other includes otherwise VulkanBackend.h includes vulkan.h before we had +// time to wrap it with vulkan_platform.h +#include "common/vulkan_platform.h" + +#include "common/NonCopyable.h" +#include "dawn/webgpu_cpp.h" +#include "dawn_native/VulkanBackend.h" + +#include +#include + +namespace dawn::native::vulkan { + + struct ExternalImageDescriptorVkForTesting; + struct ExternalImageExportInfoVkForTesting; + + class VulkanImageWrappingTestBackend { + public: + static std::unique_ptr Create(const wgpu::Device& device); + virtual ~VulkanImageWrappingTestBackend() = default; + + class ExternalTexture : NonCopyable { + public: + virtual ~ExternalTexture() = default; + }; + class ExternalSemaphore : NonCopyable { + public: + virtual ~ExternalSemaphore() = default; + }; + + virtual std::unique_ptr CreateTexture(uint32_t width, + uint32_t height, + wgpu::TextureFormat format, + wgpu::TextureUsage usage) = 0; + virtual wgpu::Texture WrapImage( + const wgpu::Device& device, + const ExternalTexture* texture, + const ExternalImageDescriptorVkForTesting& descriptor, + std::vector> semaphores) = 0; + + virtual bool ExportImage(const wgpu::Texture& texture, + VkImageLayout layout, + ExternalImageExportInfoVkForTesting* exportInfo) = 0; + }; + + struct ExternalImageDescriptorVkForTesting : public ExternalImageDescriptorVk { + public: + ExternalImageDescriptorVkForTesting(); + }; + + struct ExternalImageExportInfoVkForTesting : public ExternalImageExportInfoVk { + public: + ExternalImageExportInfoVkForTesting(); + std::vector> semaphores; + }; + +} // namespace dawn::native::vulkan + +#endif // TESTS_VULKANIMAGEWRAPPINGTESTS_H_ diff --git a/src/tests/white_box/VulkanImageWrappingTestsOpaqueFD.cpp b/src/tests/white_box/VulkanImageWrappingTestsOpaqueFD.cpp deleted file mode 100644 index cb045d5629..0000000000 --- a/src/tests/white_box/VulkanImageWrappingTestsOpaqueFD.cpp +++ /dev/null @@ -1,1019 +0,0 @@ -// Copyright 2019 The Dawn Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "common/Math.h" -#include "tests/DawnTest.h" - -#include "common/vulkan_platform.h" -#include "dawn_native/VulkanBackend.h" -#include "dawn_native/vulkan/AdapterVk.h" -#include "dawn_native/vulkan/DeviceVk.h" -#include "dawn_native/vulkan/FencedDeleter.h" -#include "dawn_native/vulkan/ResourceMemoryAllocatorVk.h" -#include "dawn_native/vulkan/TextureVk.h" -#include "utils/SystemUtils.h" -#include "utils/WGPUHelpers.h" - -namespace dawn::native::vulkan { - - namespace { - - class VulkanImageWrappingTestBase : public DawnTest { - protected: - std::vector GetRequiredFeatures() override { - return {wgpu::FeatureName::DawnInternalUsages}; - } - - public: - void SetUp() override { - DawnTest::SetUp(); - DAWN_TEST_UNSUPPORTED_IF(UsesWire()); - - deviceVk = dawn::native::vulkan::ToBackend(dawn::native::FromAPI(device.Get())); - } - - // Creates a VkImage with external memory - ::VkResult CreateImage(dawn::native::vulkan::Device* deviceVk, - uint32_t width, - uint32_t height, - VkFormat format, - VkImage* image) { - VkExternalMemoryImageCreateInfoKHR externalInfo; - externalInfo.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO_KHR; - externalInfo.pNext = nullptr; - externalInfo.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR; - - auto usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | - VK_IMAGE_USAGE_TRANSFER_DST_BIT; - - VkImageCreateInfo createInfo; - createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; - createInfo.pNext = &externalInfo; - createInfo.flags = VK_IMAGE_CREATE_ALIAS_BIT_KHR; - createInfo.imageType = VK_IMAGE_TYPE_2D; - createInfo.format = format; - createInfo.extent = {width, height, 1}; - createInfo.mipLevels = 1; - createInfo.arrayLayers = 1; - createInfo.samples = VK_SAMPLE_COUNT_1_BIT; - createInfo.tiling = VK_IMAGE_TILING_OPTIMAL; - createInfo.usage = usage; - createInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - createInfo.queueFamilyIndexCount = 0; - createInfo.pQueueFamilyIndices = nullptr; - createInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - - return deviceVk->fn.CreateImage(deviceVk->GetVkDevice(), &createInfo, nullptr, - &**image); - } - - // Allocates memory for an image - ::VkResult AllocateMemory(dawn::native::vulkan::Device* deviceVk, - VkImage handle, - VkDeviceMemory* allocation, - VkDeviceSize* allocationSize, - uint32_t* memoryTypeIndex) { - // Create the image memory and associate it with the container - VkMemoryRequirements requirements; - deviceVk->fn.GetImageMemoryRequirements(deviceVk->GetVkDevice(), handle, - &requirements); - - // Import memory from file descriptor - VkExportMemoryAllocateInfoKHR externalInfo; - externalInfo.sType = VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_KHR; - externalInfo.pNext = nullptr; - externalInfo.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR; - - int bestType = deviceVk->GetResourceMemoryAllocator()->FindBestTypeIndex( - requirements, MemoryKind::Opaque); - VkMemoryAllocateInfo allocateInfo; - allocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; - allocateInfo.pNext = &externalInfo; - allocateInfo.allocationSize = requirements.size; - allocateInfo.memoryTypeIndex = static_cast(bestType); - - *allocationSize = allocateInfo.allocationSize; - *memoryTypeIndex = allocateInfo.memoryTypeIndex; - - return deviceVk->fn.AllocateMemory(deviceVk->GetVkDevice(), &allocateInfo, nullptr, - &**allocation); - } - - // Binds memory to an image - ::VkResult BindMemory(dawn::native::vulkan::Device* deviceVk, - VkImage handle, - VkDeviceMemory* memory) { - return deviceVk->fn.BindImageMemory(deviceVk->GetVkDevice(), handle, *memory, 0); - } - - // Extracts a file descriptor representing memory on a device - int GetMemoryFd(dawn::native::vulkan::Device* deviceVk, VkDeviceMemory memory) { - VkMemoryGetFdInfoKHR getFdInfo; - getFdInfo.sType = VK_STRUCTURE_TYPE_MEMORY_GET_FD_INFO_KHR; - getFdInfo.pNext = nullptr; - getFdInfo.memory = memory; - getFdInfo.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR; - - int memoryFd = -1; - deviceVk->fn.GetMemoryFdKHR(deviceVk->GetVkDevice(), &getFdInfo, &memoryFd); - - EXPECT_GE(memoryFd, 0) << "Failed to get file descriptor for external memory"; - return memoryFd; - } - - // Prepares and exports memory for an image on a given device - void CreateBindExportImage(dawn::native::vulkan::Device* deviceVk, - uint32_t width, - uint32_t height, - VkFormat format, - VkImage* handle, - VkDeviceMemory* allocation, - VkDeviceSize* allocationSize, - uint32_t* memoryTypeIndex, - int* memoryFd) { - ::VkResult result = CreateImage(deviceVk, width, height, format, handle); - EXPECT_EQ(result, VK_SUCCESS) << "Failed to create external image"; - - ::VkResult resultBool = - AllocateMemory(deviceVk, *handle, allocation, allocationSize, memoryTypeIndex); - EXPECT_EQ(resultBool, VK_SUCCESS) << "Failed to allocate external memory"; - - result = BindMemory(deviceVk, *handle, allocation); - EXPECT_EQ(result, VK_SUCCESS) << "Failed to bind image memory"; - - *memoryFd = GetMemoryFd(deviceVk, *allocation); - } - - // Wraps a vulkan image from external memory - wgpu::Texture WrapVulkanImage(wgpu::Device dawnDevice, - const wgpu::TextureDescriptor* textureDescriptor, - int memoryFd, - VkDeviceSize allocationSize, - uint32_t memoryTypeIndex, - std::vector waitFDs, - bool isInitialized = true, - bool expectValid = true) { - dawn::native::vulkan::ExternalImageDescriptorOpaqueFD descriptor; - return WrapVulkanImage(dawnDevice, textureDescriptor, memoryFd, allocationSize, - memoryTypeIndex, waitFDs, descriptor.releasedOldLayout, - descriptor.releasedNewLayout, isInitialized, expectValid); - } - - wgpu::Texture WrapVulkanImage(wgpu::Device dawnDevice, - const wgpu::TextureDescriptor* textureDescriptor, - int memoryFd, - VkDeviceSize allocationSize, - uint32_t memoryTypeIndex, - std::vector waitFDs, - VkImageLayout releasedOldLayout, - VkImageLayout releasedNewLayout, - bool isInitialized = true, - bool expectValid = true) { - dawn::native::vulkan::ExternalImageDescriptorOpaqueFD descriptor; - descriptor.cTextureDescriptor = - reinterpret_cast(textureDescriptor); - descriptor.isInitialized = isInitialized; - descriptor.allocationSize = allocationSize; - descriptor.memoryTypeIndex = memoryTypeIndex; - descriptor.memoryFD = memoryFd; - descriptor.waitFDs = waitFDs; - descriptor.releasedOldLayout = releasedOldLayout; - descriptor.releasedNewLayout = releasedNewLayout; - - WGPUTexture texture = - dawn::native::vulkan::WrapVulkanImage(dawnDevice.Get(), &descriptor); - - if (expectValid) { - EXPECT_NE(texture, nullptr) << "Failed to wrap image, are external memory / " - "semaphore extensions supported?"; - } else { - EXPECT_EQ(texture, nullptr); - } - - return wgpu::Texture::Acquire(texture); - } - - // Exports the signal from a wrapped texture and ignores it - // We have to export the signal before destroying the wrapped texture else it's an - // assertion failure - void IgnoreSignalSemaphore(wgpu::Texture wrappedTexture) { - dawn::native::vulkan::ExternalImageExportInfoOpaqueFD info; - dawn::native::vulkan::ExportVulkanImage(wrappedTexture.Get(), - VK_IMAGE_LAYOUT_GENERAL, &info); - for (int handle : info.semaphoreHandles) { - ASSERT_NE(handle, -1); - close(handle); - } - } - - protected: - dawn::native::vulkan::Device* deviceVk; - }; - - } // anonymous namespace - - class VulkanImageWrappingValidationTests : public VulkanImageWrappingTestBase { - public: - void SetUp() override { - VulkanImageWrappingTestBase::SetUp(); - DAWN_TEST_UNSUPPORTED_IF(UsesWire()); - - CreateBindExportImage(deviceVk, 1, 1, VK_FORMAT_R8G8B8A8_UNORM, &defaultImage, - &defaultAllocation, &defaultAllocationSize, - &defaultMemoryTypeIndex, &defaultFd); - defaultDescriptor.dimension = wgpu::TextureDimension::e2D; - defaultDescriptor.format = wgpu::TextureFormat::RGBA8Unorm; - defaultDescriptor.size = {1, 1, 1}; - defaultDescriptor.sampleCount = 1; - defaultDescriptor.mipLevelCount = 1; - defaultDescriptor.usage = wgpu::TextureUsage::RenderAttachment | - wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst; - } - - void TearDown() override { - if (UsesWire()) { - VulkanImageWrappingTestBase::TearDown(); - return; - } - - deviceVk->GetFencedDeleter()->DeleteWhenUnused(defaultImage); - deviceVk->GetFencedDeleter()->DeleteWhenUnused(defaultAllocation); - VulkanImageWrappingTestBase::TearDown(); - } - - protected: - wgpu::TextureDescriptor defaultDescriptor; - VkImage defaultImage; - VkDeviceMemory defaultAllocation; - VkDeviceSize defaultAllocationSize; - uint32_t defaultMemoryTypeIndex; - int defaultFd; - }; - - // Test no error occurs if the import is valid - TEST_P(VulkanImageWrappingValidationTests, SuccessfulImport) { - wgpu::Texture texture = - WrapVulkanImage(device, &defaultDescriptor, defaultFd, defaultAllocationSize, - defaultMemoryTypeIndex, {}, true, true); - EXPECT_NE(texture.Get(), nullptr); - IgnoreSignalSemaphore(texture); - } - - // Test no error occurs if the import is valid with DawnTextureInternalUsageDescriptor - TEST_P(VulkanImageWrappingValidationTests, SuccessfulImportWithInternalUsageDescriptor) { - wgpu::DawnTextureInternalUsageDescriptor internalDesc = {}; - defaultDescriptor.nextInChain = &internalDesc; - internalDesc.internalUsage = wgpu::TextureUsage::CopySrc; - internalDesc.sType = wgpu::SType::DawnTextureInternalUsageDescriptor; - - wgpu::Texture texture = - WrapVulkanImage(device, &defaultDescriptor, defaultFd, defaultAllocationSize, - defaultMemoryTypeIndex, {}, true, true); - EXPECT_NE(texture.Get(), nullptr); - IgnoreSignalSemaphore(texture); - } - - // Test an error occurs if an invalid sType is the nextInChain - TEST_P(VulkanImageWrappingValidationTests, InvalidTextureDescriptor) { - wgpu::ChainedStruct chainedDescriptor; - chainedDescriptor.sType = wgpu::SType::SurfaceDescriptorFromWindowsSwapChainPanel; - defaultDescriptor.nextInChain = &chainedDescriptor; - - ASSERT_DEVICE_ERROR(wgpu::Texture texture = WrapVulkanImage( - device, &defaultDescriptor, defaultFd, defaultAllocationSize, - defaultMemoryTypeIndex, {}, true, false)); - EXPECT_EQ(texture.Get(), nullptr); - } - - // Test an error occurs if the descriptor dimension isn't 2D - TEST_P(VulkanImageWrappingValidationTests, InvalidTextureDimension) { - defaultDescriptor.dimension = wgpu::TextureDimension::e1D; - - ASSERT_DEVICE_ERROR(wgpu::Texture texture = WrapVulkanImage( - device, &defaultDescriptor, defaultFd, defaultAllocationSize, - defaultMemoryTypeIndex, {}, true, false)); - EXPECT_EQ(texture.Get(), nullptr); - } - - // Test an error occurs if the descriptor mip level count isn't 1 - TEST_P(VulkanImageWrappingValidationTests, InvalidMipLevelCount) { - defaultDescriptor.mipLevelCount = 2; - - ASSERT_DEVICE_ERROR(wgpu::Texture texture = WrapVulkanImage( - device, &defaultDescriptor, defaultFd, defaultAllocationSize, - defaultMemoryTypeIndex, {}, true, false)); - EXPECT_EQ(texture.Get(), nullptr); - } - - // Test an error occurs if the descriptor depth isn't 1 - TEST_P(VulkanImageWrappingValidationTests, InvalidDepth) { - defaultDescriptor.size.depthOrArrayLayers = 2; - - ASSERT_DEVICE_ERROR(wgpu::Texture texture = WrapVulkanImage( - device, &defaultDescriptor, defaultFd, defaultAllocationSize, - defaultMemoryTypeIndex, {}, true, false)); - EXPECT_EQ(texture.Get(), nullptr); - } - - // Test an error occurs if the descriptor sample count isn't 1 - TEST_P(VulkanImageWrappingValidationTests, InvalidSampleCount) { - defaultDescriptor.sampleCount = 4; - - ASSERT_DEVICE_ERROR(wgpu::Texture texture = WrapVulkanImage( - device, &defaultDescriptor, defaultFd, defaultAllocationSize, - defaultMemoryTypeIndex, {}, true, false)); - EXPECT_EQ(texture.Get(), nullptr); - } - - // Test an error occurs if we try to export the signal semaphore twice - TEST_P(VulkanImageWrappingValidationTests, DoubleSignalSemaphoreExport) { - wgpu::Texture texture = - WrapVulkanImage(device, &defaultDescriptor, defaultFd, defaultAllocationSize, - defaultMemoryTypeIndex, {}, true, true); - ASSERT_NE(texture.Get(), nullptr); - IgnoreSignalSemaphore(texture); - - dawn::native::vulkan::ExternalImageExportInfoOpaqueFD exportInfo; - ASSERT_DEVICE_ERROR(bool success = dawn::native::vulkan::ExportVulkanImage( - texture.Get(), VK_IMAGE_LAYOUT_GENERAL, &exportInfo)); - ASSERT_FALSE(success); - } - - // Test an error occurs if we try to export the signal semaphore from a normal texture - TEST_P(VulkanImageWrappingValidationTests, NormalTextureSignalSemaphoreExport) { - wgpu::Texture texture = device.CreateTexture(&defaultDescriptor); - ASSERT_NE(texture.Get(), nullptr); - - dawn::native::vulkan::ExternalImageExportInfoOpaqueFD exportInfo; - ASSERT_DEVICE_ERROR(bool success = dawn::native::vulkan::ExportVulkanImage( - texture.Get(), VK_IMAGE_LAYOUT_GENERAL, &exportInfo)); - ASSERT_FALSE(success); - } - - // Test an error occurs if we try to export the signal semaphore from a destroyed texture - TEST_P(VulkanImageWrappingValidationTests, DestroyedTextureSignalSemaphoreExport) { - wgpu::Texture texture = device.CreateTexture(&defaultDescriptor); - ASSERT_NE(texture.Get(), nullptr); - texture.Destroy(); - - dawn::native::vulkan::ExternalImageExportInfoOpaqueFD exportInfo; - ASSERT_DEVICE_ERROR(bool success = dawn::native::vulkan::ExportVulkanImage( - texture.Get(), VK_IMAGE_LAYOUT_GENERAL, &exportInfo)); - ASSERT_FALSE(success); - } - - // Fixture to test using external memory textures through different usages. - // These tests are skipped if the harness is using the wire. - class VulkanImageWrappingUsageTests : public VulkanImageWrappingTestBase { - public: - void SetUp() override { - VulkanImageWrappingTestBase::SetUp(); - DAWN_TEST_UNSUPPORTED_IF(UsesWire()); - - // Create another device based on the original - backendAdapter = dawn::native::vulkan::ToBackend(deviceVk->GetAdapter()); - deviceDescriptor.nextInChain = &togglesDesc; - togglesDesc.forceEnabledToggles = GetParam().forceEnabledWorkarounds.data(); - togglesDesc.forceEnabledTogglesCount = GetParam().forceEnabledWorkarounds.size(); - togglesDesc.forceDisabledToggles = GetParam().forceDisabledWorkarounds.data(); - togglesDesc.forceDisabledTogglesCount = GetParam().forceDisabledWorkarounds.size(); - - secondDeviceVk = - dawn::native::vulkan::ToBackend(backendAdapter->APICreateDevice(&deviceDescriptor)); - secondDevice = wgpu::Device::Acquire(dawn::native::ToAPI(secondDeviceVk)); - - CreateBindExportImage(deviceVk, 1, 1, VK_FORMAT_R8G8B8A8_UNORM, &defaultImage, - &defaultAllocation, &defaultAllocationSize, - &defaultMemoryTypeIndex, &defaultFd); - defaultDescriptor.dimension = wgpu::TextureDimension::e2D; - defaultDescriptor.format = wgpu::TextureFormat::RGBA8Unorm; - defaultDescriptor.size = {1, 1, 1}; - defaultDescriptor.sampleCount = 1; - defaultDescriptor.mipLevelCount = 1; - defaultDescriptor.usage = wgpu::TextureUsage::RenderAttachment | - wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst; - } - - void TearDown() override { - if (UsesWire()) { - VulkanImageWrappingTestBase::TearDown(); - return; - } - - deviceVk->GetFencedDeleter()->DeleteWhenUnused(defaultImage); - deviceVk->GetFencedDeleter()->DeleteWhenUnused(defaultAllocation); - VulkanImageWrappingTestBase::TearDown(); - } - - protected: - wgpu::Device secondDevice; - dawn::native::vulkan::Device* secondDeviceVk; - - dawn::native::vulkan::Adapter* backendAdapter; - dawn::native::DeviceDescriptor deviceDescriptor; - dawn::native::DawnTogglesDeviceDescriptor togglesDesc; - - wgpu::TextureDescriptor defaultDescriptor; - VkImage defaultImage; - VkDeviceMemory defaultAllocation; - VkDeviceSize defaultAllocationSize; - uint32_t defaultMemoryTypeIndex; - int defaultFd; - - // Clear a texture on a given device - void ClearImage(wgpu::Device dawnDevice, - wgpu::Texture wrappedTexture, - wgpu::Color clearColor) { - wgpu::TextureView wrappedView = wrappedTexture.CreateView(); - - // Submit a clear operation - utils::ComboRenderPassDescriptor renderPassDescriptor({wrappedView}, {}); - renderPassDescriptor.cColorAttachments[0].clearColor = clearColor; - - wgpu::CommandEncoder encoder = dawnDevice.CreateCommandEncoder(); - wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDescriptor); - pass.EndPass(); - - wgpu::CommandBuffer commands = encoder.Finish(); - - wgpu::Queue queue = dawnDevice.GetQueue(); - queue.Submit(1, &commands); - } - - // Submits a 1x1x1 copy from source to destination - void SimpleCopyTextureToTexture(wgpu::Device dawnDevice, - wgpu::Queue dawnQueue, - wgpu::Texture source, - wgpu::Texture destination) { - wgpu::ImageCopyTexture copySrc; - copySrc.texture = source; - copySrc.mipLevel = 0; - copySrc.origin = {0, 0, 0}; - - wgpu::ImageCopyTexture copyDst; - copyDst.texture = destination; - copyDst.mipLevel = 0; - copyDst.origin = {0, 0, 0}; - - wgpu::Extent3D copySize = {1, 1, 1}; - - wgpu::CommandEncoder encoder = dawnDevice.CreateCommandEncoder(); - encoder.CopyTextureToTexture(©Src, ©Dst, ©Size); - wgpu::CommandBuffer commands = encoder.Finish(); - - dawnQueue.Submit(1, &commands); - } - }; - - // Clear an image in |secondDevice| - // Verify clear color is visible in |device| - TEST_P(VulkanImageWrappingUsageTests, ClearImageAcrossDevices) { - // Import the image on |secondDevice| - wgpu::Texture wrappedTexture = - WrapVulkanImage(secondDevice, &defaultDescriptor, defaultFd, defaultAllocationSize, - defaultMemoryTypeIndex, {}, VK_IMAGE_LAYOUT_UNDEFINED, - VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); - - // Clear |wrappedTexture| on |secondDevice| - ClearImage(secondDevice, wrappedTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f}); - - dawn::native::vulkan::ExternalImageExportInfoOpaqueFD exportInfo; - dawn::native::vulkan::ExportVulkanImage(wrappedTexture.Get(), - VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, &exportInfo); - - // Import the image to |device|, making sure we wait on signalFd - int memoryFd = GetMemoryFd(deviceVk, defaultAllocation); - wgpu::Texture nextWrappedTexture = - WrapVulkanImage(device, &defaultDescriptor, memoryFd, defaultAllocationSize, - defaultMemoryTypeIndex, exportInfo.semaphoreHandles, - exportInfo.releasedOldLayout, exportInfo.releasedNewLayout); - - // Verify |device| sees the changes from |secondDevice| - EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), nextWrappedTexture, 0, 0); - - IgnoreSignalSemaphore(nextWrappedTexture); - } - - // Clear an image in |secondDevice| - // Verify clear color is not visible in |device| if we import the texture as not cleared - TEST_P(VulkanImageWrappingUsageTests, UninitializedTextureIsCleared) { - // Import the image on |secondDevice| - wgpu::Texture wrappedTexture = - WrapVulkanImage(secondDevice, &defaultDescriptor, defaultFd, defaultAllocationSize, - defaultMemoryTypeIndex, {}, VK_IMAGE_LAYOUT_UNDEFINED, - VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); - - // Clear |wrappedTexture| on |secondDevice| - ClearImage(secondDevice, wrappedTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f}); - - dawn::native::vulkan::ExternalImageExportInfoOpaqueFD exportInfo; - dawn::native::vulkan::ExportVulkanImage(wrappedTexture.Get(), - VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, &exportInfo); - - // Import the image to |device|, making sure we wait on the semaphore - int memoryFd = GetMemoryFd(deviceVk, defaultAllocation); - wgpu::Texture nextWrappedTexture = - WrapVulkanImage(device, &defaultDescriptor, memoryFd, defaultAllocationSize, - defaultMemoryTypeIndex, exportInfo.semaphoreHandles, - exportInfo.releasedOldLayout, exportInfo.releasedNewLayout, false); - - // Verify |device| doesn't see the changes from |secondDevice| - EXPECT_PIXEL_RGBA8_EQ(RGBA8(0, 0, 0, 0), nextWrappedTexture, 0, 0); - - IgnoreSignalSemaphore(nextWrappedTexture); - } - - // Import a texture into |secondDevice| - // Issue a copy of the imported texture inside |device| to |copyDstTexture| - // Verify the clear color from |secondDevice| is visible in |copyDstTexture| - TEST_P(VulkanImageWrappingUsageTests, CopyTextureToTextureSrcSync) { - // Import the image on |secondDevice| - wgpu::Texture wrappedTexture = - WrapVulkanImage(secondDevice, &defaultDescriptor, defaultFd, defaultAllocationSize, - defaultMemoryTypeIndex, {}, VK_IMAGE_LAYOUT_UNDEFINED, - VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); - - // Clear |wrappedTexture| on |secondDevice| - ClearImage(secondDevice, wrappedTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f}); - - dawn::native::vulkan::ExternalImageExportInfoOpaqueFD exportInfo; - dawn::native::vulkan::ExportVulkanImage(wrappedTexture.Get(), - VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, &exportInfo); - - // Import the image to |device|, making sure we wait on the semaphore - int memoryFd = GetMemoryFd(deviceVk, defaultAllocation); - wgpu::Texture deviceWrappedTexture = - WrapVulkanImage(device, &defaultDescriptor, memoryFd, defaultAllocationSize, - defaultMemoryTypeIndex, exportInfo.semaphoreHandles, - exportInfo.releasedOldLayout, exportInfo.releasedNewLayout); - - // Create a second texture on |device| - wgpu::Texture copyDstTexture = device.CreateTexture(&defaultDescriptor); - - // Copy |deviceWrappedTexture| into |copyDstTexture| - SimpleCopyTextureToTexture(device, queue, deviceWrappedTexture, copyDstTexture); - - // Verify |copyDstTexture| sees changes from |secondDevice| - EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), copyDstTexture, 0, 0); - - IgnoreSignalSemaphore(deviceWrappedTexture); - } - - // Import a texture into |device| - // Copy color A into texture on |device| - // Import same texture into |secondDevice|, waiting on the copy signal - // Copy color B using Texture to Texture copy on |secondDevice| - // Import texture back into |device|, waiting on color B signal - // Verify texture contains color B - // If texture destination isn't synchronized, |secondDevice| could copy color B - // into the texture first, then |device| writes color A - TEST_P(VulkanImageWrappingUsageTests, CopyTextureToTextureDstSync) { - // Import the image on |device| - wgpu::Texture wrappedTexture = WrapVulkanImage( - device, &defaultDescriptor, defaultFd, defaultAllocationSize, defaultMemoryTypeIndex, - {}, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); - - // Clear |wrappedTexture| on |device| - ClearImage(device, wrappedTexture, {5 / 255.0f, 6 / 255.0f, 7 / 255.0f, 8 / 255.0f}); - - dawn::native::vulkan::ExternalImageExportInfoOpaqueFD exportInfo; - dawn::native::vulkan::ExportVulkanImage(wrappedTexture.Get(), - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &exportInfo); - - // Import the image to |secondDevice|, making sure we wait on the semaphore - int memoryFd = GetMemoryFd(deviceVk, defaultAllocation); - wgpu::Texture secondDeviceWrappedTexture = - WrapVulkanImage(secondDevice, &defaultDescriptor, memoryFd, defaultAllocationSize, - defaultMemoryTypeIndex, exportInfo.semaphoreHandles, - exportInfo.releasedOldLayout, exportInfo.releasedNewLayout); - - // Create a texture with color B on |secondDevice| - wgpu::Texture copySrcTexture = secondDevice.CreateTexture(&defaultDescriptor); - ClearImage(secondDevice, copySrcTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f}); - - // Copy color B on |secondDevice| - wgpu::Queue secondDeviceQueue = secondDevice.GetQueue(); - SimpleCopyTextureToTexture(secondDevice, secondDeviceQueue, copySrcTexture, - secondDeviceWrappedTexture); - - // Re-import back into |device|, waiting on |secondDevice|'s signal - dawn::native::vulkan::ExternalImageExportInfoOpaqueFD secondExportInfo; - dawn::native::vulkan::ExportVulkanImage(secondDeviceWrappedTexture.Get(), - VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, - &secondExportInfo); - memoryFd = GetMemoryFd(deviceVk, defaultAllocation); - - wgpu::Texture nextWrappedTexture = - WrapVulkanImage(device, &defaultDescriptor, memoryFd, defaultAllocationSize, - defaultMemoryTypeIndex, secondExportInfo.semaphoreHandles, - secondExportInfo.releasedOldLayout, secondExportInfo.releasedNewLayout); - - // Verify |nextWrappedTexture| contains the color from our copy - EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), nextWrappedTexture, 0, 0); - - IgnoreSignalSemaphore(nextWrappedTexture); - } - - // Import a texture from |secondDevice| - // Issue a copy of the imported texture inside |device| to |copyDstBuffer| - // Verify the clear color from |secondDevice| is visible in |copyDstBuffer| - TEST_P(VulkanImageWrappingUsageTests, CopyTextureToBufferSrcSync) { - // Import the image on |secondDevice| - wgpu::Texture wrappedTexture = - WrapVulkanImage(secondDevice, &defaultDescriptor, defaultFd, defaultAllocationSize, - defaultMemoryTypeIndex, {}, VK_IMAGE_LAYOUT_UNDEFINED, - VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); - - // Clear |wrappedTexture| on |secondDevice| - ClearImage(secondDevice, wrappedTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f}); - - dawn::native::vulkan::ExternalImageExportInfoOpaqueFD exportInfo; - dawn::native::vulkan::ExportVulkanImage(wrappedTexture.Get(), - VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, &exportInfo); - - // Import the image to |device|, making sure we wait on the semaphore - int memoryFd = GetMemoryFd(deviceVk, defaultAllocation); - wgpu::Texture deviceWrappedTexture = - WrapVulkanImage(device, &defaultDescriptor, memoryFd, defaultAllocationSize, - defaultMemoryTypeIndex, exportInfo.semaphoreHandles, - exportInfo.releasedOldLayout, exportInfo.releasedNewLayout); - - // Create a destination buffer on |device| - wgpu::BufferDescriptor bufferDesc; - bufferDesc.size = 4; - bufferDesc.usage = wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::CopySrc; - wgpu::Buffer copyDstBuffer = device.CreateBuffer(&bufferDesc); - - // Copy |deviceWrappedTexture| into |copyDstBuffer| - wgpu::ImageCopyTexture copySrc = - utils::CreateImageCopyTexture(deviceWrappedTexture, 0, {0, 0, 0}); - wgpu::ImageCopyBuffer copyDst = utils::CreateImageCopyBuffer(copyDstBuffer, 0, 256); - - wgpu::Extent3D copySize = {1, 1, 1}; - - wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); - encoder.CopyTextureToBuffer(©Src, ©Dst, ©Size); - wgpu::CommandBuffer commands = encoder.Finish(); - queue.Submit(1, &commands); - - // Verify |copyDstBuffer| sees changes from |secondDevice| - uint32_t expected = 0x04030201; - EXPECT_BUFFER_U32_EQ(expected, copyDstBuffer, 0); - - IgnoreSignalSemaphore(deviceWrappedTexture); - } - - // Import a texture into |device| - // Copy color A into texture on |device| - // Import same texture into |secondDevice|, waiting on the copy signal - // Copy color B using Buffer to Texture copy on |secondDevice| - // Import texture back into |device|, waiting on color B signal - // Verify texture contains color B - // If texture destination isn't synchronized, |secondDevice| could copy color B - // into the texture first, then |device| writes color A - TEST_P(VulkanImageWrappingUsageTests, CopyBufferToTextureDstSync) { - // Import the image on |device| - wgpu::Texture wrappedTexture = WrapVulkanImage( - device, &defaultDescriptor, defaultFd, defaultAllocationSize, defaultMemoryTypeIndex, - {}, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); - - // Clear |wrappedTexture| on |device| - ClearImage(device, wrappedTexture, {5 / 255.0f, 6 / 255.0f, 7 / 255.0f, 8 / 255.0f}); - - dawn::native::vulkan::ExternalImageExportInfoOpaqueFD exportInfo; - dawn::native::vulkan::ExportVulkanImage(wrappedTexture.Get(), - VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, &exportInfo); - - // Import the image to |secondDevice|, making sure we wait on |signalFd| - int memoryFd = GetMemoryFd(deviceVk, defaultAllocation); - wgpu::Texture secondDeviceWrappedTexture = - WrapVulkanImage(secondDevice, &defaultDescriptor, memoryFd, defaultAllocationSize, - defaultMemoryTypeIndex, exportInfo.semaphoreHandles, - exportInfo.releasedOldLayout, exportInfo.releasedNewLayout); - - // Copy color B on |secondDevice| - wgpu::Queue secondDeviceQueue = secondDevice.GetQueue(); - - // Create a buffer on |secondDevice| - wgpu::Buffer copySrcBuffer = - utils::CreateBufferFromData(secondDevice, wgpu::BufferUsage::CopySrc, {0x04030201}); - - // Copy |copySrcBuffer| into |secondDeviceWrappedTexture| - wgpu::ImageCopyBuffer copySrc = utils::CreateImageCopyBuffer(copySrcBuffer, 0, 256); - wgpu::ImageCopyTexture copyDst = - utils::CreateImageCopyTexture(secondDeviceWrappedTexture, 0, {0, 0, 0}); - - wgpu::Extent3D copySize = {1, 1, 1}; - - wgpu::CommandEncoder encoder = secondDevice.CreateCommandEncoder(); - encoder.CopyBufferToTexture(©Src, ©Dst, ©Size); - wgpu::CommandBuffer commands = encoder.Finish(); - secondDeviceQueue.Submit(1, &commands); - - // Re-import back into |device|, waiting on |secondDevice|'s signal - dawn::native::vulkan::ExternalImageExportInfoOpaqueFD secondExportInfo; - dawn::native::vulkan::ExportVulkanImage(secondDeviceWrappedTexture.Get(), - VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, - &secondExportInfo); - memoryFd = GetMemoryFd(deviceVk, defaultAllocation); - - wgpu::Texture nextWrappedTexture = - WrapVulkanImage(device, &defaultDescriptor, memoryFd, defaultAllocationSize, - defaultMemoryTypeIndex, secondExportInfo.semaphoreHandles, - secondExportInfo.releasedOldLayout, secondExportInfo.releasedNewLayout); - - // Verify |nextWrappedTexture| contains the color from our copy - EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), nextWrappedTexture, 0, 0); - - IgnoreSignalSemaphore(nextWrappedTexture); - } - - // Import a texture from |secondDevice| - // Issue a copy of the imported texture inside |device| to |copyDstTexture| - // Issue second copy to |secondCopyDstTexture| - // Verify the clear color from |secondDevice| is visible in both copies - TEST_P(VulkanImageWrappingUsageTests, DoubleTextureUsage) { - // Import the image on |secondDevice| - wgpu::Texture wrappedTexture = - WrapVulkanImage(secondDevice, &defaultDescriptor, defaultFd, defaultAllocationSize, - defaultMemoryTypeIndex, {}, VK_IMAGE_LAYOUT_UNDEFINED, - VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); - - // Clear |wrappedTexture| on |secondDevice| - ClearImage(secondDevice, wrappedTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f}); - - dawn::native::vulkan::ExternalImageExportInfoOpaqueFD exportInfo; - dawn::native::vulkan::ExportVulkanImage(wrappedTexture.Get(), - VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, &exportInfo); - - // Import the image to |device|, making sure we wait on the semaphore - int memoryFd = GetMemoryFd(deviceVk, defaultAllocation); - wgpu::Texture deviceWrappedTexture = - WrapVulkanImage(device, &defaultDescriptor, memoryFd, defaultAllocationSize, - defaultMemoryTypeIndex, exportInfo.semaphoreHandles, - exportInfo.releasedOldLayout, exportInfo.releasedNewLayout); - - // Create a second texture on |device| - wgpu::Texture copyDstTexture = device.CreateTexture(&defaultDescriptor); - - // Create a third texture on |device| - wgpu::Texture secondCopyDstTexture = device.CreateTexture(&defaultDescriptor); - - // Copy |deviceWrappedTexture| into |copyDstTexture| - SimpleCopyTextureToTexture(device, queue, deviceWrappedTexture, copyDstTexture); - - // Copy |deviceWrappedTexture| into |secondCopyDstTexture| - SimpleCopyTextureToTexture(device, queue, deviceWrappedTexture, secondCopyDstTexture); - - // Verify |copyDstTexture| sees changes from |secondDevice| - EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), copyDstTexture, 0, 0); - - // Verify |secondCopyDstTexture| sees changes from |secondDevice| - EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), secondCopyDstTexture, 0, 0); - - IgnoreSignalSemaphore(deviceWrappedTexture); - } - - // Tex A on device 3 (external export) - // Tex B on device 2 (external export) - // Tex C on device 1 (external export) - // Clear color for A on device 3 - // Copy A->B on device 3 - // Copy B->C on device 2 (wait on B from previous op) - // Copy C->D on device 1 (wait on C from previous op) - // Verify D has same color as A - TEST_P(VulkanImageWrappingUsageTests, ChainTextureCopy) { - // Close |defaultFd| since this test doesn't import it anywhere - close(defaultFd); - - // device 1 = |device| - // device 2 = |secondDevice| - // Create device 3 - dawn::native::vulkan::Device* thirdDeviceVk = - dawn::native::vulkan::ToBackend(backendAdapter->APICreateDevice(&deviceDescriptor)); - wgpu::Device thirdDevice = wgpu::Device::Acquire(dawn::native::ToAPI(thirdDeviceVk)); - - // Make queue for device 2 and 3 - wgpu::Queue secondDeviceQueue = secondDevice.GetQueue(); - wgpu::Queue thirdDeviceQueue = thirdDevice.GetQueue(); - - // Allocate memory for A, B, C - VkImage imageA; - VkDeviceMemory allocationA; - int memoryFdA; - VkDeviceSize allocationSizeA; - uint32_t memoryTypeIndexA; - CreateBindExportImage(thirdDeviceVk, 1, 1, VK_FORMAT_R8G8B8A8_UNORM, &imageA, &allocationA, - &allocationSizeA, &memoryTypeIndexA, &memoryFdA); - - VkImage imageB; - VkDeviceMemory allocationB; - int memoryFdB; - VkDeviceSize allocationSizeB; - uint32_t memoryTypeIndexB; - CreateBindExportImage(secondDeviceVk, 1, 1, VK_FORMAT_R8G8B8A8_UNORM, &imageB, &allocationB, - &allocationSizeB, &memoryTypeIndexB, &memoryFdB); - - VkImage imageC; - VkDeviceMemory allocationC; - int memoryFdC; - VkDeviceSize allocationSizeC; - uint32_t memoryTypeIndexC; - CreateBindExportImage(deviceVk, 1, 1, VK_FORMAT_R8G8B8A8_UNORM, &imageC, &allocationC, - &allocationSizeC, &memoryTypeIndexC, &memoryFdC); - - // Import TexA, TexB on device 3 - wgpu::Texture wrappedTexADevice3 = WrapVulkanImage( - thirdDevice, &defaultDescriptor, memoryFdA, allocationSizeA, memoryTypeIndexA, {}, - VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); - - wgpu::Texture wrappedTexBDevice3 = WrapVulkanImage( - thirdDevice, &defaultDescriptor, memoryFdB, allocationSizeB, memoryTypeIndexB, {}, - VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); - - // Clear TexA - ClearImage(thirdDevice, wrappedTexADevice3, - {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f}); - - // Copy A->B - SimpleCopyTextureToTexture(thirdDevice, thirdDeviceQueue, wrappedTexADevice3, - wrappedTexBDevice3); - - dawn::native::vulkan::ExternalImageExportInfoOpaqueFD exportInfoTexBDevice3; - dawn::native::vulkan::ExportVulkanImage( - wrappedTexBDevice3.Get(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, &exportInfoTexBDevice3); - - IgnoreSignalSemaphore(wrappedTexADevice3); - - // Import TexB, TexC on device 2 - memoryFdB = GetMemoryFd(secondDeviceVk, allocationB); - wgpu::Texture wrappedTexBDevice2 = WrapVulkanImage( - secondDevice, &defaultDescriptor, memoryFdB, allocationSizeB, memoryTypeIndexB, - exportInfoTexBDevice3.semaphoreHandles, exportInfoTexBDevice3.releasedOldLayout, - exportInfoTexBDevice3.releasedNewLayout); - - wgpu::Texture wrappedTexCDevice2 = WrapVulkanImage( - secondDevice, &defaultDescriptor, memoryFdC, allocationSizeC, memoryTypeIndexC, {}, - VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); - - // Copy B->C on device 2 - SimpleCopyTextureToTexture(secondDevice, secondDeviceQueue, wrappedTexBDevice2, - wrappedTexCDevice2); - - dawn::native::vulkan::ExternalImageExportInfoOpaqueFD exportInfoTexCDevice2; - dawn::native::vulkan::ExportVulkanImage( - wrappedTexCDevice2.Get(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, &exportInfoTexCDevice2); - - IgnoreSignalSemaphore(wrappedTexBDevice2); - - // Import TexC on device 1 - memoryFdC = GetMemoryFd(deviceVk, allocationC); - wgpu::Texture wrappedTexCDevice1 = WrapVulkanImage( - device, &defaultDescriptor, memoryFdC, allocationSizeC, memoryTypeIndexC, - exportInfoTexCDevice2.semaphoreHandles, exportInfoTexCDevice2.releasedOldLayout, - exportInfoTexCDevice2.releasedNewLayout); - - // Create TexD on device 1 - wgpu::Texture texD = device.CreateTexture(&defaultDescriptor); - - // Copy C->D on device 1 - SimpleCopyTextureToTexture(device, queue, wrappedTexCDevice1, texD); - - // Verify D matches clear color - EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), texD, 0, 0); - - thirdDeviceVk->GetFencedDeleter()->DeleteWhenUnused(imageA); - thirdDeviceVk->GetFencedDeleter()->DeleteWhenUnused(allocationA); - secondDeviceVk->GetFencedDeleter()->DeleteWhenUnused(imageB); - secondDeviceVk->GetFencedDeleter()->DeleteWhenUnused(allocationB); - deviceVk->GetFencedDeleter()->DeleteWhenUnused(imageC); - deviceVk->GetFencedDeleter()->DeleteWhenUnused(allocationC); - - IgnoreSignalSemaphore(wrappedTexCDevice1); - } - - // Tests a larger image is preserved when importing - TEST_P(VulkanImageWrappingUsageTests, LargerImage) { - close(defaultFd); - - wgpu::TextureDescriptor descriptor; - descriptor.dimension = wgpu::TextureDimension::e2D; - descriptor.size.width = 640; - descriptor.size.height = 480; - descriptor.size.depthOrArrayLayers = 1; - descriptor.sampleCount = 1; - descriptor.format = wgpu::TextureFormat::BGRA8Unorm; - descriptor.mipLevelCount = 1; - descriptor.usage = wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::CopySrc; - - // Fill memory with textures to trigger layout issues on AMD - std::vector textures; - for (int i = 0; i < 20; i++) { - textures.push_back(device.CreateTexture(&descriptor)); - } - - wgpu::Queue secondDeviceQueue = secondDevice.GetQueue(); - - // Make an image on |secondDevice| - VkImage imageA; - VkDeviceMemory allocationA; - int memoryFdA; - VkDeviceSize allocationSizeA; - uint32_t memoryTypeIndexA; - CreateBindExportImage(secondDeviceVk, 640, 480, VK_FORMAT_R8G8B8A8_UNORM, &imageA, - &allocationA, &allocationSizeA, &memoryTypeIndexA, &memoryFdA); - - // Import the image on |secondDevice| - wgpu::Texture wrappedTexture = - WrapVulkanImage(secondDevice, &descriptor, memoryFdA, allocationSizeA, memoryTypeIndexA, - {}, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); - - // Draw a non-trivial picture - uint32_t width = 640, height = 480, pixelSize = 4; - uint32_t bytesPerRow = Align(width * pixelSize, kTextureBytesPerRowAlignment); - std::vector data(bytesPerRow * (height - 1) + width * pixelSize); - - for (uint32_t row = 0; row < height; row++) { - for (uint32_t col = 0; col < width; col++) { - float normRow = static_cast(row) / height; - float normCol = static_cast(col) / width; - float dist = sqrt(normRow * normRow + normCol * normCol) * 3; - dist = dist - static_cast(dist); - data[4 * (row * width + col)] = static_cast(dist * 255); - data[4 * (row * width + col) + 1] = static_cast(dist * 255); - data[4 * (row * width + col) + 2] = static_cast(dist * 255); - data[4 * (row * width + col) + 3] = 255; - } - } - - // Write the picture - { - wgpu::Buffer copySrcBuffer = utils::CreateBufferFromData( - secondDevice, data.data(), data.size(), wgpu::BufferUsage::CopySrc); - wgpu::ImageCopyBuffer copySrc = - utils::CreateImageCopyBuffer(copySrcBuffer, 0, bytesPerRow); - wgpu::ImageCopyTexture copyDst = - utils::CreateImageCopyTexture(wrappedTexture, 0, {0, 0, 0}); - wgpu::Extent3D copySize = {width, height, 1}; - - wgpu::CommandEncoder encoder = secondDevice.CreateCommandEncoder(); - encoder.CopyBufferToTexture(©Src, ©Dst, ©Size); - wgpu::CommandBuffer commands = encoder.Finish(); - secondDeviceQueue.Submit(1, &commands); - } - - dawn::native::vulkan::ExternalImageExportInfoOpaqueFD exportInfo; - dawn::native::vulkan::ExportVulkanImage(wrappedTexture.Get(), - VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, &exportInfo); - - int memoryFd = GetMemoryFd(secondDeviceVk, allocationA); - - // Import the image on |device| - wgpu::Texture nextWrappedTexture = - WrapVulkanImage(device, &descriptor, memoryFd, allocationSizeA, memoryTypeIndexA, - exportInfo.semaphoreHandles, exportInfo.releasedOldLayout, - exportInfo.releasedNewLayout); - - // Copy the image into a buffer for comparison - wgpu::BufferDescriptor copyDesc; - copyDesc.size = data.size(); - copyDesc.usage = wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst; - wgpu::Buffer copyDstBuffer = device.CreateBuffer(©Desc); - { - wgpu::ImageCopyTexture copySrc = - utils::CreateImageCopyTexture(nextWrappedTexture, 0, {0, 0, 0}); - wgpu::ImageCopyBuffer copyDst = - utils::CreateImageCopyBuffer(copyDstBuffer, 0, bytesPerRow); - - wgpu::Extent3D copySize = {width, height, 1}; - - wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); - encoder.CopyTextureToBuffer(©Src, ©Dst, ©Size); - wgpu::CommandBuffer commands = encoder.Finish(); - queue.Submit(1, &commands); - } - - // Check the image is not corrupted on |device| - EXPECT_BUFFER_U32_RANGE_EQ(reinterpret_cast(data.data()), copyDstBuffer, 0, - data.size() / 4); - - IgnoreSignalSemaphore(nextWrappedTexture); - secondDeviceVk->GetFencedDeleter()->DeleteWhenUnused(imageA); - secondDeviceVk->GetFencedDeleter()->DeleteWhenUnused(allocationA); - } - - DAWN_INSTANTIATE_TEST(VulkanImageWrappingValidationTests, VulkanBackend()); - DAWN_INSTANTIATE_TEST(VulkanImageWrappingUsageTests, VulkanBackend()); - -} // namespace dawn::native::vulkan diff --git a/src/tests/white_box/VulkanImageWrappingTests_OpaqueFD.cpp b/src/tests/white_box/VulkanImageWrappingTests_OpaqueFD.cpp new file mode 100644 index 0000000000..23275dcd64 --- /dev/null +++ b/src/tests/white_box/VulkanImageWrappingTests_OpaqueFD.cpp @@ -0,0 +1,270 @@ +// Copyright 2021 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "tests/white_box/VulkanImageWrappingTests.h" + +#include "dawn_native/vulkan/DeviceVk.h" +#include "dawn_native/vulkan/FencedDeleter.h" +#include "dawn_native/vulkan/ResourceMemoryAllocatorVk.h" + +#include +#include + +namespace dawn::native::vulkan { + + class ExternalSemaphoreOpaqueFD : public VulkanImageWrappingTestBackend::ExternalSemaphore { + public: + ExternalSemaphoreOpaqueFD(int handle) : mHandle(handle) { + } + ~ExternalSemaphoreOpaqueFD() override { + if (mHandle != -1) { + close(mHandle); + } + } + int AcquireHandle() { + int handle = mHandle; + mHandle = -1; + return handle; + } + + private: + int mHandle = -1; + }; + + class ExternalTextureOpaqueFD : public VulkanImageWrappingTestBackend::ExternalTexture { + public: + ExternalTextureOpaqueFD(dawn::native::vulkan::Device* device, + int fd, + VkDeviceMemory allocation, + VkImage handle, + VkDeviceSize allocationSize, + uint32_t memoryTypeIndex) + : mDevice(device), + mFd(fd), + mAllocation(allocation), + mHandle(handle), + allocationSize(allocationSize), + memoryTypeIndex(memoryTypeIndex) { + } + + ~ExternalTextureOpaqueFD() override { + if (mFd != -1) { + close(mFd); + } + if (mAllocation != VK_NULL_HANDLE) { + mDevice->GetFencedDeleter()->DeleteWhenUnused(mAllocation); + } + if (mHandle != VK_NULL_HANDLE) { + mDevice->GetFencedDeleter()->DeleteWhenUnused(mHandle); + } + } + + int Dup() const { + return dup(mFd); + } + + private: + dawn::native::vulkan::Device* mDevice; + int mFd = -1; + VkDeviceMemory mAllocation = VK_NULL_HANDLE; + VkImage mHandle = VK_NULL_HANDLE; + + public: + const VkDeviceSize allocationSize; + const uint32_t memoryTypeIndex; + }; + + class VulkanImageWrappingTestBackendOpaqueFD : public VulkanImageWrappingTestBackend { + public: + VulkanImageWrappingTestBackendOpaqueFD(const wgpu::Device& device) : mDevice(device) { + mDeviceVk = dawn::native::vulkan::ToBackend(dawn::native::FromAPI(device.Get())); + } + + std::unique_ptr CreateTexture(uint32_t width, + uint32_t height, + wgpu::TextureFormat format, + wgpu::TextureUsage usage) override { + EXPECT_EQ(format, wgpu::TextureFormat::RGBA8Unorm); + VkFormat vulkanFormat = VK_FORMAT_R8G8B8A8_UNORM; + + VkImage handle; + ::VkResult result = CreateImage(mDeviceVk, width, height, vulkanFormat, &handle); + EXPECT_EQ(result, VK_SUCCESS) << "Failed to create external image"; + + VkDeviceMemory allocation; + VkDeviceSize allocationSize; + uint32_t memoryTypeIndex; + ::VkResult resultBool = + AllocateMemory(mDeviceVk, handle, &allocation, &allocationSize, &memoryTypeIndex); + EXPECT_EQ(resultBool, VK_SUCCESS) << "Failed to allocate external memory"; + + result = BindMemory(mDeviceVk, handle, allocation); + EXPECT_EQ(result, VK_SUCCESS) << "Failed to bind image memory"; + + int fd = GetMemoryFd(mDeviceVk, allocation); + + return std::make_unique(mDeviceVk, fd, allocation, handle, + allocationSize, memoryTypeIndex); + } + + wgpu::Texture WrapImage( + const wgpu::Device& device, + const ExternalTexture* texture, + const ExternalImageDescriptorVkForTesting& descriptor, + std::vector> semaphores) override { + const ExternalTextureOpaqueFD* textureOpaqueFD = + static_cast(texture); + std::vector waitFDs; + for (auto& semaphore : semaphores) { + waitFDs.push_back( + static_cast(semaphore.get())->AcquireHandle()); + } + + ExternalImageDescriptorOpaqueFD descriptorOpaqueFD; + *static_cast(&descriptorOpaqueFD) = descriptor; + descriptorOpaqueFD.memoryFD = textureOpaqueFD->Dup(); + descriptorOpaqueFD.allocationSize = textureOpaqueFD->allocationSize; + descriptorOpaqueFD.memoryTypeIndex = textureOpaqueFD->memoryTypeIndex; + descriptorOpaqueFD.waitFDs = std::move(waitFDs); + + return dawn::native::vulkan::WrapVulkanImage(device.Get(), &descriptorOpaqueFD); + } + + bool ExportImage(const wgpu::Texture& texture, + VkImageLayout layout, + ExternalImageExportInfoVkForTesting* exportInfo) override { + ExternalImageExportInfoOpaqueFD infoOpaqueFD; + bool success = ExportVulkanImage(texture.Get(), layout, &infoOpaqueFD); + + *static_cast(exportInfo) = infoOpaqueFD; + for (int fd : infoOpaqueFD.semaphoreHandles) { + EXPECT_NE(fd, -1); + exportInfo->semaphores.push_back(std::make_unique(fd)); + } + + return success; + } + + private: + // Creates a VkImage with external memory + ::VkResult CreateImage(dawn::native::vulkan::Device* deviceVk, + uint32_t width, + uint32_t height, + VkFormat format, + VkImage* image) { + VkExternalMemoryImageCreateInfoKHR externalInfo; + externalInfo.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO_KHR; + externalInfo.pNext = nullptr; + externalInfo.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR; + + auto usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | + VK_IMAGE_USAGE_TRANSFER_DST_BIT; + + VkImageCreateInfo createInfo; + createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + createInfo.pNext = &externalInfo; + createInfo.flags = VK_IMAGE_CREATE_ALIAS_BIT_KHR; + createInfo.imageType = VK_IMAGE_TYPE_2D; + createInfo.format = format; + createInfo.extent = {width, height, 1}; + createInfo.mipLevels = 1; + createInfo.arrayLayers = 1; + createInfo.samples = VK_SAMPLE_COUNT_1_BIT; + createInfo.tiling = VK_IMAGE_TILING_OPTIMAL; + createInfo.usage = usage; + createInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + createInfo.queueFamilyIndexCount = 0; + createInfo.pQueueFamilyIndices = nullptr; + createInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + + return deviceVk->fn.CreateImage(deviceVk->GetVkDevice(), &createInfo, nullptr, + &**image); + } + + // Allocates memory for an image + ::VkResult AllocateMemory(dawn::native::vulkan::Device* deviceVk, + VkImage handle, + VkDeviceMemory* allocation, + VkDeviceSize* allocationSize, + uint32_t* memoryTypeIndex) { + // Create the image memory and associate it with the container + VkMemoryRequirements requirements; + deviceVk->fn.GetImageMemoryRequirements(deviceVk->GetVkDevice(), handle, &requirements); + + // Import memory from file descriptor + VkExportMemoryAllocateInfoKHR externalInfo; + externalInfo.sType = VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_KHR; + externalInfo.pNext = nullptr; + externalInfo.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR; + + int bestType = deviceVk->GetResourceMemoryAllocator()->FindBestTypeIndex( + requirements, MemoryKind::Opaque); + VkMemoryAllocateInfo allocateInfo; + allocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + allocateInfo.pNext = &externalInfo; + allocateInfo.allocationSize = requirements.size; + allocateInfo.memoryTypeIndex = static_cast(bestType); + + *allocationSize = allocateInfo.allocationSize; + *memoryTypeIndex = allocateInfo.memoryTypeIndex; + + return deviceVk->fn.AllocateMemory(deviceVk->GetVkDevice(), &allocateInfo, nullptr, + &**allocation); + } + + // Binds memory to an image + ::VkResult BindMemory(dawn::native::vulkan::Device* deviceVk, + VkImage handle, + VkDeviceMemory memory) { + return deviceVk->fn.BindImageMemory(deviceVk->GetVkDevice(), handle, memory, 0); + } + + // Extracts a file descriptor representing memory on a device + int GetMemoryFd(dawn::native::vulkan::Device* deviceVk, VkDeviceMemory memory) { + VkMemoryGetFdInfoKHR getFdInfo; + getFdInfo.sType = VK_STRUCTURE_TYPE_MEMORY_GET_FD_INFO_KHR; + getFdInfo.pNext = nullptr; + getFdInfo.memory = memory; + getFdInfo.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR; + + int memoryFd = -1; + deviceVk->fn.GetMemoryFdKHR(deviceVk->GetVkDevice(), &getFdInfo, &memoryFd); + + EXPECT_GE(memoryFd, 0) << "Failed to get file descriptor for external memory"; + return memoryFd; + } + + // Prepares and exports memory for an image on a given device + void CreateBindExportImage(dawn::native::vulkan::Device* deviceVk, + uint32_t width, + uint32_t height, + VkFormat format, + VkImage* handle, + VkDeviceMemory* allocation, + VkDeviceSize* allocationSize, + uint32_t* memoryTypeIndex, + int* memoryFd) { + } + + wgpu::Device mDevice; + dawn::native::vulkan::Device* mDeviceVk; + }; + + // static + std::unique_ptr VulkanImageWrappingTestBackend::Create( + const wgpu::Device& device) { + return std::make_unique(device); + } + +} // namespace dawn::native::vulkan