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 <lokokung@google.com>
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
diff --git a/src/dawn_native/DawnNative.cpp b/src/dawn_native/DawnNative.cpp
index b166c44..498d9b1 100644
--- a/src/dawn_native/DawnNative.cpp
+++ b/src/dawn_native/DawnNative.cpp
@@ -278,12 +278,20 @@
// 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 f15cb8a..f7e3560 100644
--- a/src/dawn_native/vulkan/VulkanBackend.cpp
+++ b/src/dawn_native/vulkan/VulkanBackend.cpp
@@ -83,7 +83,7 @@
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 @@
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 2bf8874..8cdd5ba 100644
--- a/src/dawn_native/vulkan/external_memory/MemoryServiceDmaBuf.cpp
+++ b/src/dawn_native/vulkan/external_memory/MemoryServiceDmaBuf.cpp
@@ -144,7 +144,7 @@
if (!mSupported) {
return false;
}
- if (descriptor->type != ExternalImageType::DmaBuf) {
+ if (descriptor->GetType() != ExternalImageType::DmaBuf) {
return false;
}
const ExternalImageDescriptorDmaBuf* dmaBufDescriptor =
@@ -226,7 +226,7 @@
ResultOrError<MemoryImportParams> 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 @@
ResultOrError<VkImage> 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 e8762d2..181a17b 100644
--- a/src/dawn_native/vulkan/external_memory/MemoryServiceOpaqueFD.cpp
+++ b/src/dawn_native/vulkan/external_memory/MemoryServiceOpaqueFD.cpp
@@ -90,7 +90,7 @@
ResultOrError<MemoryImportParams> 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 e8a6e13..4850d8c 100644
--- a/src/include/dawn_native/DawnNative.h
+++ b/src/include/dawn_native/DawnNative.h
@@ -217,12 +217,15 @@
// 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 @@
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 6141875..aab52af 100644
--- a/src/tests/BUILD.gn
+++ b/src/tests/BUILD.gn
@@ -494,7 +494,11 @@
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 0000000..7cf1df0
--- /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<wgpu::FeatureName> 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<std::unique_ptr<ExternalSemaphore>> 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<std::unique_ptr<ExternalSemaphore>> semaphores,
+ VkImageLayout releasedOldLayout,
+ VkImageLayout releasedNewLayout,
+ bool isInitialized = true,
+ bool expectValid = true) {
+ ExternalImageDescriptorVkForTesting descriptor;
+ descriptor.cTextureDescriptor =
+ reinterpret_cast<const WGPUTextureDescriptor*>(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<VulkanImageWrappingTestBackend> mBackend;
+
+ wgpu::TextureDescriptor defaultDescriptor;
+ std::unique_ptr<ExternalTexture> 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<ExternalTexture> textureA =
+ mBackend->CreateTexture(1, 1, wgpu::TextureFormat::RGBA8Unorm, defaultDescriptor.usage);
+ std::unique_ptr<ExternalTexture> textureB =
+ mBackend->CreateTexture(1, 1, wgpu::TextureFormat::RGBA8Unorm, defaultDescriptor.usage);
+ std::unique_ptr<ExternalTexture> 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<wgpu::Texture> 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<ExternalTexture> 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<unsigned char> 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<float>(row) / height;
+ float normCol = static_cast<float>(col) / width;
+ float dist = sqrt(normRow * normRow + normCol * normCol) * 3;
+ dist = dist - static_cast<int>(dist);
+ data[4 * (row * width + col)] = static_cast<unsigned char>(dist * 255);
+ data[4 * (row * width + col) + 1] = static_cast<unsigned char>(dist * 255);
+ data[4 * (row * width + col) + 2] = static_cast<unsigned char>(dist * 255);
+ data[4 * (row * width + col) + 3] = 255;
+ }
+ }
+
+ // Write the picture
+ {
+ wgpu::Buffer copySrcBuffer = utils::CreateBufferFromData(
+ secondDevice, data.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<uint32_t*>(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 0000000..6c2deb7
--- /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 <memory>
+#include <vector>
+
+namespace dawn::native::vulkan {
+
+ struct ExternalImageDescriptorVkForTesting;
+ struct ExternalImageExportInfoVkForTesting;
+
+ class VulkanImageWrappingTestBackend {
+ public:
+ static std::unique_ptr<VulkanImageWrappingTestBackend> 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<ExternalTexture> 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<std::unique_ptr<ExternalSemaphore>> 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<std::unique_ptr<VulkanImageWrappingTestBackend::ExternalSemaphore>> 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 cb045d5..0000000
--- 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<wgpu::FeatureName> 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<uint32_t>(bestType);
-
- *allocationSize = allocateInfo.allocationSize;
- *memoryTypeIndex = allocateInfo.memoryTypeIndex;
-
- return deviceVk->fn.AllocateMemory(deviceVk->GetVkDevice(), &allocateInfo, nullptr,
- &**allocation);
- }
-
- // Binds memory to an image
- ::VkResult BindMemory(dawn::native::vulkan::Device* deviceVk,
- VkImage handle,
- VkDeviceMemory* memory) {
- return deviceVk->fn.BindImageMemory(deviceVk->GetVkDevice(), handle, *memory, 0);
- }
-
- // Extracts a file descriptor representing memory on a device
- int GetMemoryFd(dawn::native::vulkan::Device* deviceVk, VkDeviceMemory memory) {
- VkMemoryGetFdInfoKHR getFdInfo;
- getFdInfo.sType = VK_STRUCTURE_TYPE_MEMORY_GET_FD_INFO_KHR;
- getFdInfo.pNext = nullptr;
- getFdInfo.memory = memory;
- getFdInfo.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR;
-
- int memoryFd = -1;
- deviceVk->fn.GetMemoryFdKHR(deviceVk->GetVkDevice(), &getFdInfo, &memoryFd);
-
- EXPECT_GE(memoryFd, 0) << "Failed to get file descriptor for external memory";
- return memoryFd;
- }
-
- // Prepares and exports memory for an image on a given device
- void CreateBindExportImage(dawn::native::vulkan::Device* deviceVk,
- uint32_t width,
- uint32_t height,
- VkFormat format,
- VkImage* handle,
- VkDeviceMemory* allocation,
- VkDeviceSize* allocationSize,
- uint32_t* memoryTypeIndex,
- int* memoryFd) {
- ::VkResult result = CreateImage(deviceVk, width, height, format, handle);
- EXPECT_EQ(result, VK_SUCCESS) << "Failed to create external image";
-
- ::VkResult resultBool =
- AllocateMemory(deviceVk, *handle, allocation, allocationSize, memoryTypeIndex);
- EXPECT_EQ(resultBool, VK_SUCCESS) << "Failed to allocate external memory";
-
- result = BindMemory(deviceVk, *handle, allocation);
- EXPECT_EQ(result, VK_SUCCESS) << "Failed to bind image memory";
-
- *memoryFd = GetMemoryFd(deviceVk, *allocation);
- }
-
- // Wraps a vulkan image from external memory
- wgpu::Texture WrapVulkanImage(wgpu::Device dawnDevice,
- const wgpu::TextureDescriptor* textureDescriptor,
- int memoryFd,
- VkDeviceSize allocationSize,
- uint32_t memoryTypeIndex,
- std::vector<int> 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<int> waitFDs,
- VkImageLayout releasedOldLayout,
- VkImageLayout releasedNewLayout,
- bool isInitialized = true,
- bool expectValid = true) {
- dawn::native::vulkan::ExternalImageDescriptorOpaqueFD descriptor;
- descriptor.cTextureDescriptor =
- reinterpret_cast<const WGPUTextureDescriptor*>(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<wgpu::Texture> 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<unsigned char> 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<float>(row) / height;
- float normCol = static_cast<float>(col) / width;
- float dist = sqrt(normRow * normRow + normCol * normCol) * 3;
- dist = dist - static_cast<int>(dist);
- data[4 * (row * width + col)] = static_cast<unsigned char>(dist * 255);
- data[4 * (row * width + col) + 1] = static_cast<unsigned char>(dist * 255);
- data[4 * (row * width + col) + 2] = static_cast<unsigned char>(dist * 255);
- data[4 * (row * width + col) + 3] = 255;
- }
- }
-
- // Write the picture
- {
- wgpu::Buffer copySrcBuffer = utils::CreateBufferFromData(
- secondDevice, data.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<uint32_t*>(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 0000000..23275dc
--- /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 <gtest/gtest.h>
+#include <unistd.h>
+
+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<ExternalTexture> 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<ExternalTextureOpaqueFD>(mDeviceVk, fd, allocation, handle,
+ allocationSize, memoryTypeIndex);
+ }
+
+ wgpu::Texture WrapImage(
+ const wgpu::Device& device,
+ const ExternalTexture* texture,
+ const ExternalImageDescriptorVkForTesting& descriptor,
+ std::vector<std::unique_ptr<ExternalSemaphore>> semaphores) override {
+ const ExternalTextureOpaqueFD* textureOpaqueFD =
+ static_cast<const ExternalTextureOpaqueFD*>(texture);
+ std::vector<int> waitFDs;
+ for (auto& semaphore : semaphores) {
+ waitFDs.push_back(
+ static_cast<ExternalSemaphoreOpaqueFD*>(semaphore.get())->AcquireHandle());
+ }
+
+ ExternalImageDescriptorOpaqueFD descriptorOpaqueFD;
+ *static_cast<ExternalImageDescriptorVk*>(&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<ExternalImageExportInfoVk*>(exportInfo) = infoOpaqueFD;
+ for (int fd : infoOpaqueFD.semaphoreHandles) {
+ EXPECT_NE(fd, -1);
+ exportInfo->semaphores.push_back(std::make_unique<ExternalSemaphoreOpaqueFD>(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<uint32_t>(bestType);
+
+ *allocationSize = allocateInfo.allocationSize;
+ *memoryTypeIndex = allocateInfo.memoryTypeIndex;
+
+ return deviceVk->fn.AllocateMemory(deviceVk->GetVkDevice(), &allocateInfo, nullptr,
+ &**allocation);
+ }
+
+ // Binds memory to an image
+ ::VkResult BindMemory(dawn::native::vulkan::Device* deviceVk,
+ VkImage handle,
+ VkDeviceMemory memory) {
+ return deviceVk->fn.BindImageMemory(deviceVk->GetVkDevice(), handle, memory, 0);
+ }
+
+ // Extracts a file descriptor representing memory on a device
+ int GetMemoryFd(dawn::native::vulkan::Device* deviceVk, VkDeviceMemory memory) {
+ VkMemoryGetFdInfoKHR getFdInfo;
+ getFdInfo.sType = VK_STRUCTURE_TYPE_MEMORY_GET_FD_INFO_KHR;
+ getFdInfo.pNext = nullptr;
+ getFdInfo.memory = memory;
+ getFdInfo.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR;
+
+ int memoryFd = -1;
+ deviceVk->fn.GetMemoryFdKHR(deviceVk->GetVkDevice(), &getFdInfo, &memoryFd);
+
+ EXPECT_GE(memoryFd, 0) << "Failed to get file descriptor for external memory";
+ return memoryFd;
+ }
+
+ // Prepares and exports memory for an image on a given device
+ void CreateBindExportImage(dawn::native::vulkan::Device* deviceVk,
+ uint32_t width,
+ uint32_t height,
+ VkFormat format,
+ VkImage* handle,
+ VkDeviceMemory* allocation,
+ VkDeviceSize* allocationSize,
+ uint32_t* memoryTypeIndex,
+ int* memoryFd) {
+ }
+
+ wgpu::Device mDevice;
+ dawn::native::vulkan::Device* mDeviceVk;
+ };
+
+ // static
+ std::unique_ptr<VulkanImageWrappingTestBackend> VulkanImageWrappingTestBackend::Create(
+ const wgpu::Device& device) {
+ return std::make_unique<VulkanImageWrappingTestBackendOpaqueFD>(device);
+ }
+
+} // namespace dawn::native::vulkan