Refactor vulkan image wrapping tests on CROS

This implements the test "backend" for DmaBuf.

Bug: chromium:996470
Change-Id: I9a4b8c7d345669e26c1349b87101c6f076e1f613
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/76820
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Commit-Queue: Jie A Chen <jie.a.chen@intel.com>
This commit is contained in:
jchen10 2022-01-25 08:08:09 +00:00 committed by Dawn LUCI CQ
parent 441ebf3bab
commit 8e7b513f95
5 changed files with 201 additions and 896 deletions

View File

@ -492,7 +492,11 @@ source_set("dawn_white_box_tests_sources") {
deps += [ "${dawn_root}/third_party/khronos:vulkan_headers" ]
if (is_chromeos) {
sources += [ "white_box/VulkanImageWrappingTestsDmaBuf.cpp" ]
sources += [
"white_box/VulkanImageWrappingTests.cpp",
"white_box/VulkanImageWrappingTests.h",
"white_box/VulkanImageWrappingTests_DmaBuf.cpp",
]
} else if (is_linux) {
sources += [
"white_box/VulkanImageWrappingTests.cpp",

View File

@ -25,13 +25,6 @@ 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 {

View File

@ -1,888 +0,0 @@
// Copyright 2020 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/DawnTest.h"
#include "common/Math.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"
#include <fcntl.h>
#include <gbm.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());
gbmDevice = CreateGbmDevice();
deviceVk = dawn::native::vulkan::ToBackend(dawn::native::FromAPI(device.Get()));
defaultGbmBo = CreateGbmBo(1, 1, true /* linear */);
defaultStride = gbm_bo_get_stride_for_plane(defaultGbmBo, 0);
defaultModifier = gbm_bo_get_modifier(defaultGbmBo);
defaultFd = gbm_bo_get_fd(defaultGbmBo);
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()) {
DawnTest::TearDown();
return;
}
gbm_bo_destroy(defaultGbmBo);
gbm_device_destroy(gbmDevice);
DawnTest::TearDown();
}
gbm_device* CreateGbmDevice() {
// Render nodes [1] are the primary interface for communicating with the GPU on
// devices that support DRM. The actual filename of the render node is
// implementation-specific, so we must scan through all possible filenames to find
// one that we can use [2].
//
// [1] https://dri.freedesktop.org/docs/drm/gpu/drm-uapi.html#render-nodes
// [2]
// https://cs.chromium.org/chromium/src/ui/ozone/platform/wayland/gpu/drm_render_node_path_finder.cc
const uint32_t kRenderNodeStart = 128;
const uint32_t kRenderNodeEnd = kRenderNodeStart + 16;
const std::string kRenderNodeTemplate = "/dev/dri/renderD";
int renderNodeFd = -1;
for (uint32_t i = kRenderNodeStart; i < kRenderNodeEnd; i++) {
std::string renderNode = kRenderNodeTemplate + std::to_string(i);
renderNodeFd = open(renderNode.c_str(), O_RDWR);
if (renderNodeFd >= 0)
break;
}
EXPECT_GE(renderNodeFd, 0) << "Failed to get file descriptor for render node";
gbm_device* gbmDevice = gbm_create_device(renderNodeFd);
EXPECT_NE(gbmDevice, nullptr) << "Failed to create GBM device";
return gbmDevice;
}
gbm_bo* CreateGbmBo(uint32_t width, uint32_t height, bool linear) {
uint32_t flags = GBM_BO_USE_RENDERING;
if (linear)
flags |= GBM_BO_USE_LINEAR;
gbm_bo* gbmBo = gbm_bo_create(gbmDevice, width, height, GBM_FORMAT_XBGR8888, flags);
EXPECT_NE(gbmBo, nullptr) << "Failed to create GBM buffer object";
return gbmBo;
}
wgpu::Texture WrapVulkanImage(wgpu::Device dawnDevice,
const wgpu::TextureDescriptor* textureDescriptor,
int memoryFd,
uint32_t stride,
uint64_t drmModifier,
std::vector<int> waitFDs,
bool isInitialized = true,
bool expectValid = true) {
dawn::native::vulkan::ExternalImageDescriptorDmaBuf descriptor;
return WrapVulkanImage(dawnDevice, textureDescriptor, memoryFd, stride, drmModifier,
waitFDs, descriptor.releasedOldLayout,
descriptor.releasedNewLayout, isInitialized, expectValid);
}
wgpu::Texture WrapVulkanImage(wgpu::Device dawnDevice,
const wgpu::TextureDescriptor* textureDescriptor,
int memoryFd,
uint32_t stride,
uint64_t drmModifier,
std::vector<int> waitFDs,
VkImageLayout releasedOldLayout,
VkImageLayout releasedNewLayout,
bool isInitialized = true,
bool expectValid = true) {
dawn::native::vulkan::ExternalImageDescriptorDmaBuf descriptor;
descriptor.cTextureDescriptor =
reinterpret_cast<const WGPUTextureDescriptor*>(textureDescriptor);
descriptor.isInitialized = isInitialized;
descriptor.stride = stride;
descriptor.drmModifier = drmModifier;
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::ExternalImageExportInfoDmaBuf exportInfo;
dawn::native::vulkan::ExportVulkanImage(wrappedTexture.Get(),
VK_IMAGE_LAYOUT_GENERAL, &exportInfo);
for (int handle : exportInfo.semaphoreHandles) {
ASSERT_NE(handle, -1);
close(handle);
}
}
protected:
dawn::native::vulkan::Device* deviceVk;
gbm_device* gbmDevice;
wgpu::TextureDescriptor defaultDescriptor;
gbm_bo* defaultGbmBo;
int defaultFd;
uint32_t defaultStride;
uint64_t defaultModifier;
};
} // anonymous namespace
using VulkanImageWrappingValidationTests = VulkanImageWrappingTestBase;
// Test no error occurs if the import is valid
TEST_P(VulkanImageWrappingValidationTests, SuccessfulImport) {
wgpu::Texture texture = WrapVulkanImage(device, &defaultDescriptor, defaultFd,
defaultStride, defaultModifier, {}, 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,
defaultStride, defaultModifier, {}, 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,
defaultStride, defaultModifier, {}, true, false));
EXPECT_EQ(texture.Get(), nullptr);
close(defaultFd);
}
// 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,
defaultStride, defaultModifier, {}, true, false));
EXPECT_EQ(texture.Get(), nullptr);
close(defaultFd);
}
// 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,
defaultStride, defaultModifier, {}, true, false));
EXPECT_EQ(texture.Get(), nullptr);
close(defaultFd);
}
// 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,
defaultStride, defaultModifier, {}, true, false));
EXPECT_EQ(texture.Get(), nullptr);
close(defaultFd);
}
// 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,
defaultStride, defaultModifier, {}, true, false));
EXPECT_EQ(texture.Get(), nullptr);
close(defaultFd);
}
// Test an error occurs if we try to export the signal semaphore twice
TEST_P(VulkanImageWrappingValidationTests, DoubleSignalSemaphoreExport) {
wgpu::Texture texture = WrapVulkanImage(device, &defaultDescriptor, defaultFd,
defaultStride, defaultModifier, {}, true, true);
ASSERT_NE(texture.Get(), nullptr);
IgnoreSignalSemaphore(texture);
dawn::native::vulkan::ExternalImageExportInfoDmaBuf 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) {
close(defaultFd);
wgpu::Texture texture = device.CreateTexture(&defaultDescriptor);
ASSERT_NE(texture.Get(), nullptr);
dawn::native::vulkan::ExternalImageExportInfoDmaBuf 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) {
close(defaultFd);
wgpu::Texture texture = device.CreateTexture(&defaultDescriptor);
ASSERT_NE(texture.Get(), nullptr);
texture.Destroy();
dawn::native::vulkan::ExternalImageExportInfoDmaBuf 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();
if (UsesWire()) {
return;
}
// 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));
}
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(&copySrc, &copyDst, &copySize);
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, defaultStride, defaultModifier, {},
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::ExternalImageExportInfoDmaBuf 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 nextFd = gbm_bo_get_fd(defaultGbmBo);
wgpu::Texture nextWrappedTexture =
WrapVulkanImage(device, &defaultDescriptor, nextFd, defaultStride, defaultModifier,
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, defaultStride, defaultModifier, {},
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::ExternalImageExportInfoDmaBuf 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 nextFd = gbm_bo_get_fd(defaultGbmBo);
wgpu::Texture nextWrappedTexture =
WrapVulkanImage(device, &defaultDescriptor, nextFd, defaultStride, defaultModifier,
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|
// 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, defaultFd, defaultStride, defaultModifier, {},
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::ExternalImageExportInfoDmaBuf 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 nextFd = gbm_bo_get_fd(defaultGbmBo);
wgpu::Texture deviceWrappedTexture =
WrapVulkanImage(device, &defaultDescriptor, nextFd, defaultStride, defaultModifier,
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|
// 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, defaultFd, defaultStride, defaultModifier, {},
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::ExternalImageExportInfoDmaBuf exportInfo;
dawn::native::vulkan::ExportVulkanImage(wrappedTexture.Get(),
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &exportInfo);
// Import the image to |secondDevice|, making sure we wait on |signalFd|
int nextFd = gbm_bo_get_fd(defaultGbmBo);
wgpu::Texture secondDeviceWrappedTexture =
WrapVulkanImage(secondDevice, &defaultDescriptor, nextFd, defaultStride,
defaultModifier, 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::ExternalImageExportInfoDmaBuf secondExportInfo;
dawn::native::vulkan::ExportVulkanImage(secondDeviceWrappedTexture.Get(),
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
&secondExportInfo);
nextFd = gbm_bo_get_fd(defaultGbmBo);
wgpu::Texture nextWrappedTexture =
WrapVulkanImage(device, &defaultDescriptor, nextFd, defaultStride, defaultModifier,
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|
// 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, defaultFd, defaultStride, defaultModifier, {},
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::ExternalImageExportInfoDmaBuf 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 nextFd = gbm_bo_get_fd(defaultGbmBo);
wgpu::Texture deviceWrappedTexture =
WrapVulkanImage(device, &defaultDescriptor, nextFd, defaultStride, defaultModifier,
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(&copySrc, &copyDst, &copySize);
wgpu::CommandBuffer commands = encoder.Finish();
queue.Submit(1, &commands);
// Verify |copyDstBuffer| sees changes from |secondDevice|
uint32_t expected = 0x04030201;
EXPECT_BUFFER_U32_EQ(expected, copyDstBuffer, 0);
IgnoreSignalSemaphore(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, defaultFd, defaultStride, defaultModifier, {},
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::ExternalImageExportInfoDmaBuf 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 nextFd = gbm_bo_get_fd(defaultGbmBo);
wgpu::Texture secondDeviceWrappedTexture =
WrapVulkanImage(secondDevice, &defaultDescriptor, nextFd, defaultStride,
defaultModifier, 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(&copySrc, &copyDst, &copySize);
wgpu::CommandBuffer commands = encoder.Finish();
secondDeviceQueue.Submit(1, &commands);
// Re-import back into |device|, waiting on |secondDevice|'s signal
dawn::native::vulkan::ExternalImageExportInfoDmaBuf secondExportInfo;
dawn::native::vulkan::ExportVulkanImage(secondDeviceWrappedTexture.Get(),
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
&secondExportInfo);
nextFd = gbm_bo_get_fd(defaultGbmBo);
wgpu::Texture nextWrappedTexture =
WrapVulkanImage(device, &defaultDescriptor, nextFd, defaultStride, defaultModifier,
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|
// 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, defaultFd, defaultStride, defaultModifier, {},
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::ExternalImageExportInfoDmaBuf 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 nextFd = gbm_bo_get_fd(defaultGbmBo);
wgpu::Texture deviceWrappedTexture =
WrapVulkanImage(device, &defaultDescriptor, nextFd, defaultStride, defaultModifier,
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();
// Create BOs for A, B, C
gbm_bo* gbmBoA = CreateGbmBo(1, 1, true /* linear */);
uint32_t fdA = gbm_bo_get_fd(gbmBoA);
uint32_t strideA = gbm_bo_get_stride_for_plane(gbmBoA, 0);
uint64_t modifierA = gbm_bo_get_modifier(gbmBoA);
gbm_bo* gbmBoB = CreateGbmBo(1, 1, true /* linear */);
uint32_t fdB = gbm_bo_get_fd(gbmBoB);
uint32_t strideB = gbm_bo_get_stride_for_plane(gbmBoB, 0);
uint64_t modifierB = gbm_bo_get_modifier(gbmBoB);
gbm_bo* gbmBoC = CreateGbmBo(1, 1, true /* linear */);
uint32_t fdC = gbm_bo_get_fd(gbmBoC);
uint32_t strideC = gbm_bo_get_stride_for_plane(gbmBoC, 0);
uint64_t modifierC = gbm_bo_get_modifier(gbmBoC);
// Import TexA, TexB on device 3
wgpu::Texture wrappedTexADevice3 =
WrapVulkanImage(thirdDevice, &defaultDescriptor, fdA, strideA, modifierA, {},
VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
wgpu::Texture wrappedTexBDevice3 =
WrapVulkanImage(thirdDevice, &defaultDescriptor, fdB, strideB, modifierB, {},
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::ExternalImageExportInfoDmaBuf exportInfoTexBDevice3;
dawn::native::vulkan::ExportVulkanImage(
wrappedTexBDevice3.Get(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, &exportInfoTexBDevice3);
IgnoreSignalSemaphore(wrappedTexADevice3);
// Import TexB, TexC on device 2
fdB = gbm_bo_get_fd(gbmBoB);
wgpu::Texture wrappedTexBDevice2 = WrapVulkanImage(
secondDevice, &defaultDescriptor, fdB, strideB, modifierB,
exportInfoTexBDevice3.semaphoreHandles, exportInfoTexBDevice3.releasedOldLayout,
exportInfoTexBDevice3.releasedNewLayout);
wgpu::Texture wrappedTexCDevice2 =
WrapVulkanImage(secondDevice, &defaultDescriptor, fdC, strideC, modifierC, {},
VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
// Copy B->C on device 2
SimpleCopyTextureToTexture(secondDevice, secondDeviceQueue, wrappedTexBDevice2,
wrappedTexCDevice2);
dawn::native::vulkan::ExternalImageExportInfoDmaBuf exportInfoTexCDevice2;
dawn::native::vulkan::ExportVulkanImage(
wrappedTexCDevice2.Get(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, &exportInfoTexCDevice2);
IgnoreSignalSemaphore(wrappedTexBDevice2);
// Import TexC on device 1
fdC = gbm_bo_get_fd(gbmBoC);
wgpu::Texture wrappedTexCDevice1 = WrapVulkanImage(
device, &defaultDescriptor, fdC, strideC, modifierC,
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);
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
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|
gbm_bo* gbmBo = CreateGbmBo(640, 480, false /* linear */);
uint32_t fd = gbm_bo_get_fd(gbmBo);
uint32_t stride = gbm_bo_get_stride_for_plane(gbmBo, 0);
uint64_t modifier = gbm_bo_get_modifier(gbmBo);
// Import the image on |secondDevice|
wgpu::Texture wrappedTexture =
WrapVulkanImage(secondDevice, &descriptor, fd, stride, modifier, {},
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(&copySrc, &copyDst, &copySize);
wgpu::CommandBuffer commands = encoder.Finish();
secondDeviceQueue.Submit(1, &commands);
}
dawn::native::vulkan::ExternalImageExportInfoDmaBuf exportInfo;
dawn::native::vulkan::ExportVulkanImage(wrappedTexture.Get(),
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, &exportInfo);
int nextFd = gbm_bo_get_fd(gbmBo);
// Import the image on |device|
wgpu::Texture nextWrappedTexture = WrapVulkanImage(
device, &descriptor, nextFd, stride, modifier, 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(&copyDesc);
{
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(&copySrc, &copyDst, &copySize);
wgpu::CommandBuffer commands = encoder.Finish();
queue.Submit(1, &commands);
}
// Check the image is not corrupted on |device|
EXPECT_BUFFER_U32_RANGE_EQ(reinterpret_cast<uint32_t*>(data.data()), copyDstBuffer, 0,
data.size() / 4);
IgnoreSignalSemaphore(nextWrappedTexture);
}
DAWN_INSTANTIATE_TEST(VulkanImageWrappingValidationTests, VulkanBackend());
DAWN_INSTANTIATE_TEST(VulkanImageWrappingUsageTests, VulkanBackend());
} // namespace dawn::native::vulkan

View File

@ -0,0 +1,189 @@
// Copyright 2020 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 <fcntl.h>
#include <gbm.h>
#include <gtest/gtest.h>
#include <unistd.h>
namespace dawn::native::vulkan {
ExternalImageDescriptorVkForTesting::ExternalImageDescriptorVkForTesting()
: ExternalImageDescriptorVk(ExternalImageType::DmaBuf) {
}
ExternalImageExportInfoVkForTesting::ExternalImageExportInfoVkForTesting()
: ExternalImageExportInfoVk(ExternalImageType::DmaBuf) {
}
class ExternalSemaphoreDmaBuf : public VulkanImageWrappingTestBackend::ExternalSemaphore {
public:
ExternalSemaphoreDmaBuf(int handle) : mHandle(handle) {
}
~ExternalSemaphoreDmaBuf() override {
if (mHandle != -1) {
close(mHandle);
}
}
int AcquireHandle() {
int handle = mHandle;
mHandle = -1;
return handle;
}
private:
int mHandle = -1;
};
class ExternalTextureDmaBuf : public VulkanImageWrappingTestBackend::ExternalTexture {
public:
ExternalTextureDmaBuf(gbm_bo* bo, int fd, uint32_t stride, uint64_t drmModifier)
: mGbmBo(bo), mFd(fd), stride(stride), drmModifier(drmModifier) {
}
~ExternalTextureDmaBuf() override {
if (mFd != -1) {
close(mFd);
}
if (mGbmBo != nullptr) {
gbm_bo_destroy(mGbmBo);
}
}
int Dup() const {
return dup(mFd);
}
private:
gbm_bo* mGbmBo = nullptr;
int mFd = -1;
public:
const uint32_t stride;
const uint64_t drmModifier;
};
class VulkanImageWrappingTestBackendDmaBuf : public VulkanImageWrappingTestBackend {
public:
VulkanImageWrappingTestBackendDmaBuf(const wgpu::Device& device) {
}
~VulkanImageWrappingTestBackendDmaBuf() {
if (mGbmDevice != nullptr) {
gbm_device_destroy(mGbmDevice);
mGbmDevice = nullptr;
}
}
std::unique_ptr<ExternalTexture> CreateTexture(uint32_t width,
uint32_t height,
wgpu::TextureFormat format,
wgpu::TextureUsage usage) override {
EXPECT_EQ(format, wgpu::TextureFormat::RGBA8Unorm);
gbm_bo* bo = CreateGbmBo(width, height, true);
return std::make_unique<ExternalTextureDmaBuf>(
bo, gbm_bo_get_fd(bo), gbm_bo_get_stride_for_plane(bo, 0), gbm_bo_get_modifier(bo));
}
wgpu::Texture WrapImage(
const wgpu::Device& device,
const ExternalTexture* texture,
const ExternalImageDescriptorVkForTesting& descriptor,
std::vector<std::unique_ptr<ExternalSemaphore>> semaphores) override {
const ExternalTextureDmaBuf* textureDmaBuf =
static_cast<const ExternalTextureDmaBuf*>(texture);
std::vector<int> waitFDs;
for (auto& semaphore : semaphores) {
waitFDs.push_back(
static_cast<ExternalSemaphoreDmaBuf*>(semaphore.get())->AcquireHandle());
}
ExternalImageDescriptorDmaBuf descriptorDmaBuf;
*static_cast<ExternalImageDescriptorVk*>(&descriptorDmaBuf) = descriptor;
descriptorDmaBuf.memoryFD = textureDmaBuf->Dup();
descriptorDmaBuf.waitFDs = std::move(waitFDs);
descriptorDmaBuf.stride = textureDmaBuf->stride;
descriptorDmaBuf.drmModifier = textureDmaBuf->drmModifier;
return dawn::native::vulkan::WrapVulkanImage(device.Get(), &descriptorDmaBuf);
}
bool ExportImage(const wgpu::Texture& texture,
VkImageLayout layout,
ExternalImageExportInfoVkForTesting* exportInfo) override {
ExternalImageExportInfoDmaBuf infoDmaBuf;
bool success = ExportVulkanImage(texture.Get(), layout, &infoDmaBuf);
*static_cast<ExternalImageExportInfoVk*>(exportInfo) = infoDmaBuf;
for (int fd : infoDmaBuf.semaphoreHandles) {
EXPECT_NE(fd, -1);
exportInfo->semaphores.push_back(std::make_unique<ExternalSemaphoreDmaBuf>(fd));
}
return success;
}
void CreateGbmDevice() {
// Render nodes [1] are the primary interface for communicating with the GPU on
// devices that support DRM. The actual filename of the render node is
// implementation-specific, so we must scan through all possible filenames to find
// one that we can use [2].
//
// [1] https://dri.freedesktop.org/docs/drm/gpu/drm-uapi.html#render-nodes
// [2]
// https://cs.chromium.org/chromium/src/ui/ozone/platform/wayland/gpu/drm_render_node_path_finder.cc
const uint32_t kRenderNodeStart = 128;
const uint32_t kRenderNodeEnd = kRenderNodeStart + 16;
const std::string kRenderNodeTemplate = "/dev/dri/renderD";
int renderNodeFd = -1;
for (uint32_t i = kRenderNodeStart; i < kRenderNodeEnd; i++) {
std::string renderNode = kRenderNodeTemplate + std::to_string(i);
renderNodeFd = open(renderNode.c_str(), O_RDWR);
if (renderNodeFd >= 0)
break;
}
EXPECT_GE(renderNodeFd, 0) << "Failed to get file descriptor for render node";
gbm_device* gbmDevice = gbm_create_device(renderNodeFd);
EXPECT_NE(gbmDevice, nullptr) << "Failed to create GBM device";
mGbmDevice = gbmDevice;
}
private:
gbm_bo* CreateGbmBo(uint32_t width, uint32_t height, bool linear) {
uint32_t flags = GBM_BO_USE_RENDERING;
if (linear)
flags |= GBM_BO_USE_LINEAR;
gbm_bo* gbmBo = gbm_bo_create(mGbmDevice, width, height, GBM_FORMAT_XBGR8888, flags);
EXPECT_NE(gbmBo, nullptr) << "Failed to create GBM buffer object";
return gbmBo;
}
gbm_device* mGbmDevice = nullptr;
};
// static
std::unique_ptr<VulkanImageWrappingTestBackend> VulkanImageWrappingTestBackend::Create(
const wgpu::Device& device) {
auto backend = std::make_unique<VulkanImageWrappingTestBackendDmaBuf>(device);
backend->CreateGbmDevice();
return backend;
}
} // namespace dawn::native::vulkan

View File

@ -23,6 +23,13 @@
namespace dawn::native::vulkan {
ExternalImageDescriptorVkForTesting::ExternalImageDescriptorVkForTesting()
: ExternalImageDescriptorVk(ExternalImageType::OpaqueFD) {
}
ExternalImageExportInfoVkForTesting::ExternalImageExportInfoVkForTesting()
: ExternalImageExportInfoVk(ExternalImageType::OpaqueFD) {
}
class ExternalSemaphoreOpaqueFD : public VulkanImageWrappingTestBackend::ExternalSemaphore {
public:
ExternalSemaphoreOpaqueFD(int handle) : mHandle(handle) {