Add wrapping for using external vulkan images as textures

This change adds platform-dependent services that handle creating
semaphores and importing image memory. Then, we use them to wrap a
texture from an outside source, and release a signal semaphore back
when we're done with it. This will be used to allow chrome to render
dawn on Vulkan platforms.

Bug: chromium:976495
Change-Id: I9f07eaf436e10aa6bd88cffdc74fd23834d62ee0
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/8340
Commit-Queue: Idan Raiter <idanr@google.com>
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
This commit is contained in:
Idan Raiter 2019-08-10 03:17:52 +00:00 committed by Commit Bot service account
parent 8cb23933b1
commit 74e4834d3d
18 changed files with 1716 additions and 14 deletions

View File

@ -362,6 +362,7 @@ source_set("libdawn_native_sources") {
"src/dawn_native/vulkan/ComputePipelineVk.h",
"src/dawn_native/vulkan/DeviceVk.cpp",
"src/dawn_native/vulkan/DeviceVk.h",
"src/dawn_native/vulkan/ExternalHandle.h",
"src/dawn_native/vulkan/FencedDeleter.cpp",
"src/dawn_native/vulkan/FencedDeleter.h",
"src/dawn_native/vulkan/Forward.h",
@ -395,7 +396,21 @@ source_set("libdawn_native_sources") {
"src/dawn_native/vulkan/VulkanFunctions.h",
"src/dawn_native/vulkan/VulkanInfo.cpp",
"src/dawn_native/vulkan/VulkanInfo.h",
"src/dawn_native/vulkan/external_memory/MemoryService.h",
"src/dawn_native/vulkan/external_semaphore/SemaphoreService.h",
]
if (is_linux) {
sources += [
"src/dawn_native/vulkan/external_memory/MemoryServiceOpaqueFD.cpp",
"src/dawn_native/vulkan/external_semaphore/SemaphoreServiceOpaqueFD.cpp",
]
} else {
sources += [
"src/dawn_native/vulkan/external_memory/MemoryServiceNull.cpp",
"src/dawn_native/vulkan/external_semaphore/SemaphoreServiceNull.cpp",
]
}
}
}
@ -667,8 +682,9 @@ test("dawn_unittests") {
}
}
test("dawn_end2end_tests") {
source_set("dawn_end2end_tests_sources") {
configs += [ "${dawn_root}/src/common:dawn_internal" ]
testonly = true
deps = [
":dawn_utils",
@ -681,7 +697,6 @@ test("dawn_end2end_tests") {
]
sources = [
"src/tests/DawnTest.cpp",
"src/tests/DawnTest.h",
"src/tests/end2end/BasicTests.cpp",
"src/tests/end2end/BindGroupTests.cpp",
@ -729,6 +744,59 @@ test("dawn_end2end_tests") {
libs += [ "IOSurface.framework" ]
}
}
source_set("dawn_white_box_tests_sources") {
configs += [ "${dawn_root}/src/common:dawn_internal" ]
testonly = true
deps = [
":dawn_utils",
":libdawn_native",
":libdawn_native_sources",
":libdawn_wire",
"${dawn_root}/src/common",
"${dawn_root}/src/dawn:libdawn",
"third_party:glfw",
"third_party:gmock_and_gtest",
]
sources = [
"src/tests/DawnTest.h",
]
if (dawn_enable_vulkan) {
deps += [ "third_party:vulkan_headers" ]
if (is_linux) {
sources += [ "src/tests/white_box/VulkanImageWrappingTests.cpp" ]
}
}
libs = []
}
test("dawn_end2end_tests") {
configs += [ "${dawn_root}/src/common:dawn_internal" ]
deps = [
":dawn_end2end_tests_sources",
":dawn_utils",
":dawn_white_box_tests_sources",
":libdawn_native",
":libdawn_wire",
"${dawn_root}/src/common",
"${dawn_root}/src/dawn:libdawn",
"third_party:glfw",
"third_party:gmock_and_gtest",
]
sources = [
"src/tests/DawnTest.cpp",
"src/tests/DawnTest.h",
]
libs = []
# When building inside Chromium, use their gtest main function because it is
# needed to run in swarming correctly.

View File

@ -208,6 +208,9 @@ namespace dawn_native {
MaybeError ValidateTextureDescriptor(const DeviceBase* device,
const TextureDescriptor* descriptor) {
if (descriptor == nullptr) {
return DAWN_VALIDATION_ERROR("Texture descriptor is nullptr");
}
if (descriptor->nextInChain != nullptr) {
return DAWN_VALIDATION_ERROR("nextInChain must be nullptr");
}

View File

@ -18,7 +18,9 @@
#include "dawn_native/BackendConnection.h"
#include "dawn_native/Commands.h"
#include "dawn_native/DynamicUploader.h"
#include "dawn_native/Error.h"
#include "dawn_native/ErrorData.h"
#include "dawn_native/VulkanBackend.h"
#include "dawn_native/vulkan/AdapterVk.h"
#include "dawn_native/vulkan/BackendVk.h"
#include "dawn_native/vulkan/BindGroupLayoutVk.h"
@ -68,6 +70,9 @@ namespace dawn_native { namespace vulkan {
mMemoryAllocator = std::make_unique<MemoryAllocator>(this);
mRenderPassCache = std::make_unique<RenderPassCache>(this);
mExternalMemoryService = std::make_unique<external_memory::Service>(this);
mExternalSemaphoreService = std::make_unique<external_semaphore::Service>(this);
return {};
}
@ -568,4 +573,91 @@ namespace dawn_native { namespace vulkan {
return {};
}
MaybeError Device::ImportExternalImage(const ExternalImageDescriptor* descriptor,
ExternalMemoryHandle memoryHandle,
const std::vector<ExternalSemaphoreHandle>& waitHandles,
VkSemaphore* outSignalSemaphore,
VkDeviceMemory* outAllocation,
std::vector<VkSemaphore>* outWaitSemaphores) {
// Check services support this combination of handle type / image info
if (!mExternalSemaphoreService->Supported()) {
return DAWN_VALIDATION_ERROR("External semaphore usage not supported");
}
if (!mExternalMemoryService->Supported()) {
return DAWN_VALIDATION_ERROR("External memory usage not supported");
}
// Create an external semaphore to signal when the texture is done being used
DAWN_TRY_ASSIGN(*outSignalSemaphore,
mExternalSemaphoreService->CreateExportableSemaphore());
// Import the external image's memory
DAWN_TRY_ASSIGN(*outAllocation,
mExternalMemoryService->ImportMemory(
memoryHandle, descriptor->allocationSize, descriptor->memoryTypeIndex));
// Import semaphores we have to wait on before using the texture
for (const ExternalSemaphoreHandle& handle : waitHandles) {
VkSemaphore semaphore = VK_NULL_HANDLE;
DAWN_TRY_ASSIGN(semaphore, mExternalSemaphoreService->ImportSemaphore(handle));
outWaitSemaphores->push_back(semaphore);
}
return {};
}
MaybeError Device::SignalAndExportExternalTexture(Texture* texture,
ExternalSemaphoreHandle* outHandle) {
DAWN_TRY(ValidateObject(texture));
VkSemaphore outSignalSemaphore;
DAWN_TRY(texture->SignalAndDestroy(&outSignalSemaphore));
// This has to happen right after SignalAndDestroy, since the semaphore will be
// deleted when the fenced deleter runs after the queue submission
DAWN_TRY_ASSIGN(*outHandle, mExternalSemaphoreService->ExportSemaphore(outSignalSemaphore));
return {};
}
TextureBase* Device::CreateTextureWrappingVulkanImage(
const ExternalImageDescriptor* descriptor,
ExternalMemoryHandle memoryHandle,
const std::vector<ExternalSemaphoreHandle>& waitHandles) {
const TextureDescriptor* textureDescriptor =
reinterpret_cast<const TextureDescriptor*>(descriptor->cTextureDescriptor);
// Initial validation
if (ConsumedError(ValidateTextureDescriptor(this, textureDescriptor))) {
return nullptr;
}
if (ConsumedError(ValidateVulkanImageCanBeWrapped(this, textureDescriptor))) {
return nullptr;
}
VkSemaphore signalSemaphore = VK_NULL_HANDLE;
VkDeviceMemory allocation = VK_NULL_HANDLE;
std::vector<VkSemaphore> waitSemaphores;
waitSemaphores.reserve(waitHandles.size());
// If failed, cleanup
if (ConsumedError(ImportExternalImage(descriptor, memoryHandle, waitHandles,
&signalSemaphore, &allocation, &waitSemaphores))) {
// Clear the signal semaphore
fn.DestroySemaphore(GetVkDevice(), signalSemaphore, nullptr);
// Clear image memory
fn.FreeMemory(GetVkDevice(), allocation, nullptr);
// Clear any wait semaphores we were able to import
for (VkSemaphore semaphore : waitSemaphores) {
fn.DestroySemaphore(GetVkDevice(), semaphore, nullptr);
}
return nullptr;
}
return new Texture(this, descriptor, textureDescriptor, signalSemaphore, allocation,
waitSemaphores);
}
}} // namespace dawn_native::vulkan

View File

@ -25,6 +25,9 @@
#include "dawn_native/vulkan/VulkanFunctions.h"
#include "dawn_native/vulkan/VulkanInfo.h"
#include "dawn_native/vulkan/external_memory/MemoryService.h"
#include "dawn_native/vulkan/external_semaphore/SemaphoreService.h"
#include <memory>
#include <queue>
@ -32,6 +35,7 @@ namespace dawn_native { namespace vulkan {
class Adapter;
class BufferUploader;
struct ExternalImageDescriptor;
class FencedDeleter;
class MapRequestTracker;
class MemoryAllocator;
@ -64,6 +68,14 @@ namespace dawn_native { namespace vulkan {
Serial GetPendingCommandSerial() const override;
void SubmitPendingCommands();
TextureBase* CreateTextureWrappingVulkanImage(
const ExternalImageDescriptor* descriptor,
ExternalMemoryHandle memoryHandle,
const std::vector<ExternalSemaphoreHandle>& waitHandles);
MaybeError SignalAndExportExternalTexture(Texture* texture,
ExternalSemaphoreHandle* outHandle);
// Dawn API
CommandBufferBase* CreateCommandBuffer(CommandEncoderBase* encoder,
const CommandBufferDescriptor* descriptor) override;
@ -119,6 +131,9 @@ namespace dawn_native { namespace vulkan {
std::unique_ptr<MemoryAllocator> mMemoryAllocator;
std::unique_ptr<RenderPassCache> mRenderPassCache;
std::unique_ptr<external_memory::Service> mExternalMemoryService;
std::unique_ptr<external_semaphore::Service> mExternalSemaphoreService;
VkFence GetUnusedFence();
void CheckPassedFences();
@ -143,8 +158,14 @@ namespace dawn_native { namespace vulkan {
SerialQueue<CommandPoolAndBuffer> mCommandsInFlight;
std::vector<CommandPoolAndBuffer> mUnusedCommands;
CommandPoolAndBuffer mPendingCommands;
CommandRecordingContext mRecordingContext;
MaybeError ImportExternalImage(const ExternalImageDescriptor* descriptor,
ExternalMemoryHandle memoryHandle,
const std::vector<ExternalSemaphoreHandle>& waitHandles,
VkSemaphore* outSignalSemaphore,
VkDeviceMemory* outAllocation,
std::vector<VkSemaphore>* outWaitSemaphores);
};
}} // namespace dawn_native::vulkan

View File

@ -0,0 +1,19 @@
#ifndef DAWNNATIVE_VULKAN_EXTERNALHANDLE_H_
#define DAWNNATIVE_VULKAN_EXTERNALHANDLE_H_
namespace dawn_native { namespace vulkan {
#ifdef DAWN_PLATFORM_LINUX
// File descriptor
using ExternalMemoryHandle = int;
// File descriptor
using ExternalSemaphoreHandle = int;
#else
// Generic types so that the Null service can compile, not used for real handles
using ExternalMemoryHandle = void*;
using ExternalSemaphoreHandle = void*;
#endif
}} // namespace dawn_native::vulkan
#endif // DAWNNATIVE_VULKAN_EXTERNALHANDLE_H_

View File

@ -41,9 +41,7 @@ namespace dawn_native { namespace vulkan {
MemoryAllocator::~MemoryAllocator() {
}
bool MemoryAllocator::Allocate(VkMemoryRequirements requirements,
bool mappable,
DeviceMemoryAllocation* allocation) {
int MemoryAllocator::FindBestTypeIndex(VkMemoryRequirements requirements, bool mappable) {
const VulkanDeviceInfo& info = mDevice->GetDeviceInfo();
// Find a suitable memory type for this allocation
@ -93,6 +91,14 @@ namespace dawn_native { namespace vulkan {
}
}
return bestType;
}
bool MemoryAllocator::Allocate(VkMemoryRequirements requirements,
bool mappable,
DeviceMemoryAllocation* allocation) {
int bestType = FindBestTypeIndex(requirements, mappable);
// TODO(cwallez@chromium.org): I think the Vulkan spec guarantees this should never happen
if (bestType == -1) {
ASSERT(false);

View File

@ -42,6 +42,7 @@ namespace dawn_native { namespace vulkan {
MemoryAllocator(Device* device);
~MemoryAllocator();
int FindBestTypeIndex(VkMemoryRequirements requirements, bool mappable);
bool Allocate(VkMemoryRequirements requirements,
bool mappable,
DeviceMemoryAllocation* allocation);

View File

@ -14,6 +14,7 @@
#include "dawn_native/vulkan/TextureVk.h"
#include "dawn_native/VulkanBackend.h"
#include "dawn_native/vulkan/AdapterVk.h"
#include "dawn_native/vulkan/CommandRecordingContext.h"
#include "dawn_native/vulkan/DeviceVk.h"
@ -379,12 +380,33 @@ namespace dawn_native { namespace vulkan {
}
}
MaybeError ValidateVulkanImageCanBeWrapped(const DeviceBase*,
const TextureDescriptor* descriptor) {
if (descriptor->dimension != dawn::TextureDimension::e2D) {
return DAWN_VALIDATION_ERROR("Texture must be 2D");
}
if (descriptor->mipLevelCount != 1) {
return DAWN_VALIDATION_ERROR("Mip level count must be 1");
}
if (descriptor->arrayLayerCount != 1) {
return DAWN_VALIDATION_ERROR("Array layer count must be 1");
}
if (descriptor->sampleCount != 1) {
return DAWN_VALIDATION_ERROR("Sample count must be 1");
}
return {};
}
Texture::Texture(Device* device, const TextureDescriptor* descriptor)
: TextureBase(device, descriptor, TextureState::OwnedInternal) {
// Create the Vulkan image "container". We don't need to check that the format supports the
// combination of sample, usage etc. because validation should have been done in the Dawn
// frontend already based on the minimum supported formats in the Vulkan spec
VkImageCreateInfo createInfo;
VkImageCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
createInfo.pNext = nullptr;
createInfo.flags = 0;
@ -466,6 +488,95 @@ namespace dawn_native { namespace vulkan {
: TextureBase(device, descriptor, TextureState::OwnedExternal), mHandle(nativeImage) {
}
// Internally managed, but imported from file descriptor
Texture::Texture(Device* device,
const ExternalImageDescriptor* descriptor,
const TextureDescriptor* textureDescriptor,
VkSemaphore signalSemaphore,
VkDeviceMemory externalMemoryAllocation,
std::vector<VkSemaphore> waitSemaphores)
: TextureBase(device, textureDescriptor, TextureState::OwnedInternal),
mExternalAllocation(externalMemoryAllocation),
mExternalState(ExternalState::PendingAcquire),
mSignalSemaphore(signalSemaphore),
mWaitRequirements(std::move(waitSemaphores)) {
VkImageCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
createInfo.pNext = nullptr;
createInfo.flags = VK_IMAGE_CREATE_ALIAS_BIT_KHR;
createInfo.imageType = VulkanImageType(GetDimension());
createInfo.format = VulkanImageFormat(GetFormat().format);
createInfo.extent = VulkanExtent3D(GetSize());
createInfo.mipLevels = GetNumMipLevels();
createInfo.arrayLayers = GetArrayLayers();
createInfo.samples = VulkanSampleCount(GetSampleCount());
createInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
createInfo.usage = VulkanImageUsage(GetUsage(), GetFormat());
createInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
createInfo.queueFamilyIndexCount = 0;
createInfo.pQueueFamilyIndices = nullptr;
createInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
ASSERT(IsSampleCountSupported(device, createInfo));
// We always set VK_IMAGE_USAGE_TRANSFER_DST_BIT unconditionally beause the Vulkan images
// that are used in vkCmdClearColorImage() must have been created with this flag, which is
// also required for the implementation of robust resource initialization.
createInfo.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
if (device->fn.CreateImage(device->GetVkDevice(), &createInfo, nullptr, &mHandle) !=
VK_SUCCESS) {
ASSERT(false);
}
// Create the image memory and associate it with the container
VkMemoryRequirements requirements;
device->fn.GetImageMemoryRequirements(device->GetVkDevice(), mHandle, &requirements);
ASSERT(requirements.size <= descriptor->allocationSize);
if (device->fn.BindImageMemory(device->GetVkDevice(), mHandle, mExternalAllocation, 0) !=
VK_SUCCESS) {
ASSERT(false);
}
// Don't clear imported texture if already cleared
if (descriptor->isCleared) {
SetIsSubresourceContentInitialized(0, 1, 0, 1);
}
}
MaybeError Texture::SignalAndDestroy(VkSemaphore* outSignalSemaphore) {
Device* device = ToBackend(GetDevice());
if (mExternalState == ExternalState::Released) {
return DAWN_VALIDATION_ERROR("Can't export signal semaphore from signaled texture");
}
if (mExternalAllocation == VK_NULL_HANDLE) {
return DAWN_VALIDATION_ERROR(
"Can't export signal semaphore from destroyed / non-external texture");
}
ASSERT(mSignalSemaphore != VK_NULL_HANDLE);
// Release the texture
mExternalState = ExternalState::PendingRelease;
TransitionUsageNow(device->GetPendingRecordingContext(), dawn::TextureUsageBit::None);
// Queue submit to signal we are done with the texture
device->GetPendingRecordingContext()->signalSemaphores.push_back(mSignalSemaphore);
device->SubmitPendingCommands();
// Write out the signal semaphore
*outSignalSemaphore = mSignalSemaphore;
mSignalSemaphore = VK_NULL_HANDLE;
// Destroy the texture so it can't be used again
DestroyInternal();
return {};
}
Texture::~Texture() {
DestroyInternal();
}
@ -479,12 +590,20 @@ namespace dawn_native { namespace vulkan {
// freed after the VkImage is destroyed and this is taken care of by the
// FencedDeleter.
device->GetMemoryAllocator()->Free(&mMemoryAllocation);
if (mHandle != VK_NULL_HANDLE) {
device->GetFencedDeleter()->DeleteWhenUnused(mHandle);
}
}
if (mHandle != VK_NULL_HANDLE) {
device->GetFencedDeleter()->DeleteWhenUnused(mHandle);
}
if (mExternalAllocation != VK_NULL_HANDLE) {
device->GetFencedDeleter()->DeleteWhenUnused(mExternalAllocation);
}
mHandle = VK_NULL_HANDLE;
mExternalAllocation = VK_NULL_HANDLE;
// If a signal semaphore exists it should be requested before we delete the texture
ASSERT(mSignalSemaphore == VK_NULL_HANDLE);
}
VkImage Texture::GetHandle() const {
@ -499,7 +618,7 @@ namespace dawn_native { namespace vulkan {
dawn::TextureUsageBit usage) {
// Avoid encoding barriers when it isn't needed.
bool lastReadOnly = (mLastUsage & kReadOnlyTextureUsages) == mLastUsage;
if (lastReadOnly && mLastUsage == usage) {
if (lastReadOnly && mLastUsage == usage && mLastExternalState == mExternalState) {
return;
}
@ -515,8 +634,6 @@ namespace dawn_native { namespace vulkan {
barrier.dstAccessMask = VulkanAccessFlags(usage, format);
barrier.oldLayout = VulkanImageLayout(mLastUsage, format);
barrier.newLayout = VulkanImageLayout(usage, format);
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.image = mHandle;
// This transitions the whole resource but assumes it is a 2D texture
ASSERT(GetDimension() == dawn::TextureDimension::e2D);
@ -526,11 +643,36 @@ namespace dawn_native { namespace vulkan {
barrier.subresourceRange.baseArrayLayer = 0;
barrier.subresourceRange.layerCount = GetArrayLayers();
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
if (mExternalState == ExternalState::PendingAcquire) {
// Transfer texture from external queue to graphics queue
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_EXTERNAL_KHR;
barrier.dstQueueFamilyIndex = ToBackend(GetDevice())->GetGraphicsQueueFamily();
// Don't override oldLayout to leave it as VK_IMAGE_LAYOUT_UNDEFINED
// TODO(http://crbug.com/dawn/200)
mExternalState = ExternalState::Acquired;
} else if (mExternalState == ExternalState::PendingRelease) {
// Transfer texture from graphics queue to external queue
barrier.srcQueueFamilyIndex = ToBackend(GetDevice())->GetGraphicsQueueFamily();
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_EXTERNAL_KHR;
barrier.newLayout = VK_IMAGE_LAYOUT_GENERAL;
mExternalState = ExternalState::Released;
}
// Move required semaphores into waitSemaphores
recordingContext->waitSemaphores.insert(recordingContext->waitSemaphores.end(),
mWaitRequirements.begin(), mWaitRequirements.end());
mWaitRequirements.clear();
ToBackend(GetDevice())
->fn.CmdPipelineBarrier(recordingContext->commandBuffer, srcStages, dstStages, 0, 0,
nullptr, 0, nullptr, 1, &barrier);
mLastUsage = usage;
mLastExternalState = mExternalState;
}
void Texture::ClearTexture(CommandRecordingContext* recordingContext,

View File

@ -18,20 +18,39 @@
#include "dawn_native/Texture.h"
#include "common/vulkan_platform.h"
#include "dawn_native/vulkan/ExternalHandle.h"
#include "dawn_native/vulkan/MemoryAllocator.h"
namespace dawn_native { namespace vulkan {
struct CommandRecordingContext;
struct ExternalImageDescriptor;
VkFormat VulkanImageFormat(dawn::TextureFormat format);
VkImageUsageFlags VulkanImageUsage(dawn::TextureUsageBit usage, const Format& format);
VkSampleCountFlagBits VulkanSampleCount(uint32_t sampleCount);
MaybeError ValidateVulkanImageCanBeWrapped(const DeviceBase* device,
const TextureDescriptor* descriptor);
class Texture : public TextureBase {
public:
enum class ExternalState {
InternalOnly,
PendingAcquire,
Acquired,
PendingRelease,
Released
};
Texture(Device* device, const TextureDescriptor* descriptor);
Texture(Device* device, const TextureDescriptor* descriptor, VkImage nativeImage);
Texture(Device* device,
const ExternalImageDescriptor* descriptor,
const TextureDescriptor* textureDescriptor,
VkSemaphore signalSemaphore,
VkDeviceMemory externalMemoryAllocation,
std::vector<VkSemaphore> waitSemaphores);
~Texture();
VkImage GetHandle() const;
@ -48,6 +67,8 @@ namespace dawn_native { namespace vulkan {
uint32_t baseArrayLayer,
uint32_t layerCount);
MaybeError SignalAndDestroy(VkSemaphore* outSignalSemaphore);
private:
void DestroyImpl() override;
void ClearTexture(CommandRecordingContext* recordingContext,
@ -58,6 +79,12 @@ namespace dawn_native { namespace vulkan {
VkImage mHandle = VK_NULL_HANDLE;
DeviceMemoryAllocation mMemoryAllocation;
VkDeviceMemory mExternalAllocation = VK_NULL_HANDLE;
ExternalState mExternalState = ExternalState::InternalOnly;
ExternalState mLastExternalState = ExternalState::InternalOnly;
VkSemaphore mSignalSemaphore = VK_NULL_HANDLE;
std::vector<VkSemaphore> mWaitRequirements;
// A usage of none will make sure the texture is transitioned before its first use as
// required by the spec.

View File

@ -24,6 +24,7 @@
#include "common/SwapChainUtils.h"
#include "dawn_native/vulkan/DeviceVk.h"
#include "dawn_native/vulkan/NativeSwapChainImplVk.h"
#include "dawn_native/vulkan/TextureVk.h"
namespace dawn_native { namespace vulkan {
@ -52,4 +53,32 @@ namespace dawn_native { namespace vulkan {
return static_cast<DawnTextureFormat>(impl->GetPreferredFormat());
}
#ifdef DAWN_PLATFORM_LINUX
DawnTexture WrapVulkanImageOpaqueFD(DawnDevice cDevice,
const ExternalImageDescriptorOpaqueFD* descriptor) {
Device* device = reinterpret_cast<Device*>(cDevice);
TextureBase* texture = device->CreateTextureWrappingVulkanImage(
descriptor, descriptor->memoryFD, descriptor->waitFDs);
return reinterpret_cast<DawnTexture>(texture);
}
int ExportSignalSemaphoreOpaqueFD(DawnDevice cDevice, DawnTexture cTexture) {
Device* device = reinterpret_cast<Device*>(cDevice);
Texture* texture = reinterpret_cast<Texture*>(cTexture);
if (!texture) {
return -1;
}
ExternalSemaphoreHandle outHandle;
if (device->ConsumedError(device->SignalAndExportExternalTexture(texture, &outHandle))) {
return -1;
}
return outHandle;
}
#endif
}} // namespace dawn_native::vulkan

View File

@ -0,0 +1,50 @@
// 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.
#ifndef DAWNNATIVE_VULKAN_EXTERNALMEMORY_SERVICE_H_
#define DAWNNATIVE_VULKAN_EXTERNALMEMORY_SERVICE_H_
#include "common/vulkan_platform.h"
#include "dawn_native/Error.h"
#include "dawn_native/vulkan/ExternalHandle.h"
namespace dawn_native { namespace vulkan {
class Device;
}} // namespace dawn_native::vulkan
namespace dawn_native { namespace vulkan { namespace external_memory {
class Service {
public:
explicit Service(Device* device);
~Service();
// True if the device reports it supports this feature
bool Supported();
// Given an external handle pointing to memory, import it into a VkDeviceMemory
ResultOrError<VkDeviceMemory> ImportMemory(ExternalMemoryHandle handle,
VkDeviceSize allocationSize,
uint32_t memoryTypeIndex);
private:
Device* mDevice = nullptr;
// True if early checks pass that determine if the service is supported
bool mSupportedFirstPass = false;
};
}}} // namespace dawn_native::vulkan::external_memory
#endif // DAWNNATIVE_VULKAN_EXTERNALMEMORY_SERVICE_H_

View File

@ -0,0 +1,37 @@
// 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 "dawn_native/vulkan/DeviceVk.h"
#include "dawn_native/vulkan/external_memory/MemoryService.h"
namespace dawn_native { namespace vulkan { namespace external_memory {
Service::Service(Device* device) : mDevice(device) {
DAWN_UNUSED(mDevice);
DAWN_UNUSED(mSupportedFirstPass);
}
Service::~Service() = default;
bool Service::Supported() {
return false;
}
ResultOrError<VkDeviceMemory> Service::ImportMemory(ExternalMemoryHandle handle,
VkDeviceSize allocationSize,
uint32_t memoryTypeIndex) {
return DAWN_UNIMPLEMENTED_ERROR("Using null semaphore service to interop inside Vulkan");
}
}}} // namespace dawn_native::vulkan::external_memory

View File

@ -0,0 +1,61 @@
// 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 "dawn_native/vulkan/DeviceVk.h"
#include "dawn_native/vulkan/VulkanError.h"
#include "dawn_native/vulkan/external_memory/MemoryService.h"
namespace dawn_native { namespace vulkan { namespace external_memory {
Service::Service(Device* device) : mDevice(device) {
const VulkanDeviceInfo& info = mDevice->GetDeviceInfo();
mSupportedFirstPass = info.externalMemory && info.externalMemoryFD;
}
Service::~Service() = default;
bool Service::Supported() {
// TODO(idanr): Query device here for additional support information, decide if supported
// This will likely be done through vkGetPhysicalDeviceImageFormatProperties2KHR, where
// we give it the intended image properties and handle type and see if it's supported
return mSupportedFirstPass;
}
ResultOrError<VkDeviceMemory> Service::ImportMemory(ExternalMemoryHandle handle,
VkDeviceSize allocationSize,
uint32_t memoryTypeIndex) {
if (handle < 0) {
return DAWN_VALIDATION_ERROR("Trying to import memory with invalid handle");
}
VkImportMemoryFdInfoKHR importMemoryFdInfo;
importMemoryFdInfo.sType = VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHR;
importMemoryFdInfo.pNext = nullptr;
importMemoryFdInfo.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR;
importMemoryFdInfo.fd = handle;
VkMemoryAllocateInfo allocateInfo;
allocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
allocateInfo.pNext = &importMemoryFdInfo;
allocateInfo.allocationSize = allocationSize;
allocateInfo.memoryTypeIndex = memoryTypeIndex;
VkDeviceMemory allocatedMemory = VK_NULL_HANDLE;
DAWN_TRY(CheckVkSuccess(mDevice->fn.AllocateMemory(mDevice->GetVkDevice(), &allocateInfo,
nullptr, &allocatedMemory),
"vkAllocateMemory"));
return allocatedMemory;
}
}}} // namespace dawn_native::vulkan::external_memory

View File

@ -0,0 +1,54 @@
// 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.
#ifndef DAWNNATIVE_VULKAN_EXTERNALSEMAPHORE_SERVICE_H_
#define DAWNNATIVE_VULKAN_EXTERNALSEMAPHORE_SERVICE_H_
#include "common/vulkan_platform.h"
#include "dawn_native/Error.h"
#include "dawn_native/vulkan/ExternalHandle.h"
namespace dawn_native { namespace vulkan {
class Device;
}} // namespace dawn_native::vulkan
namespace dawn_native { namespace vulkan { namespace external_semaphore {
class Service {
public:
explicit Service(Device* device);
~Service();
// True if the device reports it supports this feature
bool Supported();
// Given an external handle, import it into a VkSemaphore
ResultOrError<VkSemaphore> ImportSemaphore(ExternalSemaphoreHandle handle);
// Create a VkSemaphore that is exportable into an external handle later
ResultOrError<VkSemaphore> CreateExportableSemaphore();
// Export a VkSemaphore into an external handle
ResultOrError<ExternalSemaphoreHandle> ExportSemaphore(VkSemaphore semaphore);
private:
Device* mDevice = nullptr;
// True if early checks pass that determine if the service is supported
bool mSupportedFirstPass = false;
};
}}} // namespace dawn_native::vulkan::external_semaphore
#endif // DAWNNATIVE_VULKAN_EXTERNALSEMAPHORE_SERVICE_H_

View File

@ -0,0 +1,43 @@
// 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 "dawn_native/vulkan/DeviceVk.h"
#include "dawn_native/vulkan/external_semaphore/SemaphoreService.h"
namespace dawn_native { namespace vulkan { namespace external_semaphore {
Service::Service(Device* device) : mDevice(device) {
DAWN_UNUSED(mDevice);
DAWN_UNUSED(mSupportedFirstPass);
}
Service::~Service() = default;
bool Service::Supported() {
return false;
}
ResultOrError<VkSemaphore> Service::ImportSemaphore(ExternalSemaphoreHandle handle) {
return DAWN_UNIMPLEMENTED_ERROR("Using null semaphore service to interop inside Vulkan");
}
ResultOrError<VkSemaphore> Service::CreateExportableSemaphore() {
return DAWN_UNIMPLEMENTED_ERROR("Using null semaphore service to interop inside Vulkan");
}
ResultOrError<ExternalSemaphoreHandle> Service::ExportSemaphore(VkSemaphore semaphore) {
return DAWN_UNIMPLEMENTED_ERROR("Using null semaphore service to interop inside Vulkan");
}
}}} // namespace dawn_native::vulkan::external_semaphore

View File

@ -0,0 +1,105 @@
// 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 "dawn_native/vulkan/DeviceVk.h"
#include "dawn_native/vulkan/VulkanError.h"
#include "dawn_native/vulkan/external_semaphore/SemaphoreService.h"
namespace dawn_native { namespace vulkan { namespace external_semaphore {
Service::Service(Device* device) : mDevice(device) {
const VulkanDeviceInfo& info = mDevice->GetDeviceInfo();
mSupportedFirstPass = info.externalSemaphore && info.externalSemaphoreFD;
// TODO(idanr): Query device here for additional support information, decide if supported
// This will likely be done through vkGetPhysicalDeviceExternalSemaphorePropertiesKHR, where
// we give it the intended handle type and see if it's supported
}
Service::~Service() = default;
bool Service::Supported() {
return mSupportedFirstPass;
}
ResultOrError<VkSemaphore> Service::ImportSemaphore(ExternalSemaphoreHandle handle) {
if (handle < 0) {
return DAWN_VALIDATION_ERROR("Trying to import semaphore with invalid handle");
}
VkSemaphore semaphore = VK_NULL_HANDLE;
VkSemaphoreCreateInfo info;
info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
info.pNext = nullptr;
info.flags = 0;
DAWN_TRY(CheckVkSuccess(
mDevice->fn.CreateSemaphore(mDevice->GetVkDevice(), &info, nullptr, &semaphore),
"vkCreateSemaphore"));
VkImportSemaphoreFdInfoKHR importSemaphoreFdInfo;
importSemaphoreFdInfo.sType = VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_FD_INFO_KHR;
importSemaphoreFdInfo.pNext = nullptr;
importSemaphoreFdInfo.semaphore = semaphore;
importSemaphoreFdInfo.flags = 0;
importSemaphoreFdInfo.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT_KHR;
importSemaphoreFdInfo.fd = handle;
MaybeError status = CheckVkSuccess(
mDevice->fn.ImportSemaphoreFdKHR(mDevice->GetVkDevice(), &importSemaphoreFdInfo),
"vkImportSemaphoreFdKHR");
if (status.IsError()) {
mDevice->fn.DestroySemaphore(mDevice->GetVkDevice(), semaphore, nullptr);
DAWN_TRY(std::move(status));
}
return semaphore;
}
ResultOrError<VkSemaphore> Service::CreateExportableSemaphore() {
VkExportSemaphoreCreateInfoKHR exportSemaphoreInfo;
exportSemaphoreInfo.sType = VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO_KHR;
exportSemaphoreInfo.pNext = nullptr;
exportSemaphoreInfo.handleTypes = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT_KHR;
VkSemaphoreCreateInfo semaphoreCreateInfo;
semaphoreCreateInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
semaphoreCreateInfo.pNext = &exportSemaphoreInfo;
semaphoreCreateInfo.flags = 0;
VkSemaphore signalSemaphore;
DAWN_TRY(
CheckVkSuccess(mDevice->fn.CreateSemaphore(mDevice->GetVkDevice(), &semaphoreCreateInfo,
nullptr, &signalSemaphore),
"vkCreateSemaphore"));
return signalSemaphore;
}
ResultOrError<ExternalSemaphoreHandle> Service::ExportSemaphore(VkSemaphore semaphore) {
VkSemaphoreGetFdInfoKHR semaphoreGetFdInfo;
semaphoreGetFdInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR;
semaphoreGetFdInfo.pNext = nullptr;
semaphoreGetFdInfo.semaphore = semaphore;
semaphoreGetFdInfo.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT_KHR;
int fd = -1;
DAWN_TRY(CheckVkSuccess(
mDevice->fn.GetSemaphoreFdKHR(mDevice->GetVkDevice(), &semaphoreGetFdInfo, &fd),
"vkGetSemaphoreFdKHR"));
ASSERT(fd >= 0);
return fd;
}
}}} // namespace dawn_native::vulkan::external_semaphore

View File

@ -23,12 +23,44 @@
#include <vector>
namespace dawn_native { namespace vulkan {
// Common properties of external images
struct ExternalImageDescriptor {
const DawnTextureDescriptor* cTextureDescriptor; // Must match image creation params
bool isCleared; // Sets whether the texture will be cleared before use
VkDeviceSize allocationSize; // Must match VkMemoryAllocateInfo from image creation
uint32_t memoryTypeIndex; // Must match VkMemoryAllocateInfo from image creation
};
DAWN_NATIVE_EXPORT VkInstance GetInstance(DawnDevice device);
DAWN_NATIVE_EXPORT DawnSwapChainImplementation CreateNativeSwapChainImpl(DawnDevice device,
VkSurfaceKHR surface);
DAWN_NATIVE_EXPORT DawnTextureFormat
GetNativeSwapChainPreferredFormat(const DawnSwapChainImplementation* swapChain);
// Can't use DAWN_PLATFORM_LINUX since header included in both dawn and chrome
#ifdef __linux__
// Descriptor for opaque file descriptor image import
struct ExternalImageDescriptorOpaqueFD : ExternalImageDescriptor {
int memoryFD; // A file descriptor from an export of the memory of the image
std::vector<int> waitFDs; // File descriptors of semaphores which will be waited on
};
// Imports an external vulkan image from an opaque file descriptor. Internally, this uses
// external memory / semaphore extensions to import the image. Then, waits on the provided
// |descriptor->waitFDs| before the texture can be used. Finally, a signal semaphore
// can be exported, transferring control back to the caller.
// On failure, returns a nullptr
DAWN_NATIVE_EXPORT DawnTexture
WrapVulkanImageOpaqueFD(DawnDevice cDevice,
const ExternalImageDescriptorOpaqueFD* descriptor);
// Exports a signal semaphore from a wrapped texture. This must be called on wrapped
// textures before they are destroyed. On failure, returns -1
DAWN_NATIVE_EXPORT int ExportSignalSemaphoreOpaqueFD(DawnDevice cDevice,
DawnTexture cTexture);
#endif // __linux__
}} // namespace dawn_native::vulkan
#endif // DAWNNATIVE_VULKANBACKEND_H_

View File

@ -0,0 +1,912 @@
// 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 "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/MemoryAllocator.h"
#include "dawn_native/vulkan/TextureVk.h"
#include "utils/DawnHelpers.h"
#include "utils/SystemUtils.h"
// TODO(crbug.com/966500): Intel is disabled until upgrade is finished
namespace {
class VulkanImageWrappingTestBase : public DawnTest {
public:
void SetUp() override {
DawnTest::SetUp();
if (UsesWire() || IsIntel()) {
return;
}
deviceVk = reinterpret_cast<dawn_native::vulkan::Device*>(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->GetMemoryAllocator()->FindBestTypeIndex(requirements, false);
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
dawn::Texture WrapVulkanImage(dawn::Device device,
const dawn::TextureDescriptor* textureDescriptor,
int memoryFd,
VkDeviceSize allocationSize,
uint32_t memoryTypeIndex,
std::vector<int> waitFDs,
bool isCleared = true,
bool expectValid = true) {
dawn_native::vulkan::ExternalImageDescriptorOpaqueFD descriptor;
descriptor.cTextureDescriptor =
reinterpret_cast<const DawnTextureDescriptor*>(textureDescriptor);
descriptor.isCleared = isCleared;
descriptor.allocationSize = allocationSize;
descriptor.memoryTypeIndex = memoryTypeIndex;
descriptor.memoryFD = memoryFd;
descriptor.waitFDs = waitFDs;
DawnTexture texture =
dawn_native::vulkan::WrapVulkanImageOpaqueFD(device.Get(), &descriptor);
if (expectValid) {
EXPECT_NE(texture, nullptr) << "Failed to wrap image, are external memory / "
"semaphore extensions supported?";
} else {
EXPECT_EQ(texture, nullptr);
}
return dawn::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(dawn::Device device, dawn::Texture wrappedTexture) {
int fd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(device.Get(),
wrappedTexture.Get());
ASSERT_NE(fd, -1);
close(fd);
}
protected:
dawn_native::vulkan::Device* deviceVk;
};
} // anonymous namespace
class VulkanImageWrappingValidationTests : public VulkanImageWrappingTestBase {
public:
void SetUp() override {
VulkanImageWrappingTestBase::SetUp();
if (UsesWire() || IsIntel()) {
return;
}
CreateBindExportImage(deviceVk, 1, 1, VK_FORMAT_R8G8B8A8_UNORM, &defaultImage,
&defaultAllocation, &defaultAllocationSize, &defaultMemoryTypeIndex,
&defaultFd);
defaultDescriptor.dimension = dawn::TextureDimension::e2D;
defaultDescriptor.format = dawn::TextureFormat::RGBA8Unorm;
defaultDescriptor.size = {1, 1, 1};
defaultDescriptor.sampleCount = 1;
defaultDescriptor.arrayLayerCount = 1;
defaultDescriptor.mipLevelCount = 1;
defaultDescriptor.usage = dawn::TextureUsageBit::OutputAttachment |
dawn::TextureUsageBit::CopySrc | dawn::TextureUsageBit::CopyDst;
}
void TearDown() override {
if (UsesWire() || IsIntel()) {
VulkanImageWrappingTestBase::TearDown();
return;
}
deviceVk->GetFencedDeleter()->DeleteWhenUnused(defaultImage);
deviceVk->GetFencedDeleter()->DeleteWhenUnused(defaultAllocation);
VulkanImageWrappingTestBase::TearDown();
}
protected:
dawn::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) {
DAWN_SKIP_TEST_IF(UsesWire() || IsIntel());
dawn::Texture texture =
WrapVulkanImage(device, &defaultDescriptor, defaultFd, defaultAllocationSize,
defaultMemoryTypeIndex, {}, true, true);
EXPECT_NE(texture.Get(), nullptr);
IgnoreSignalSemaphore(device, texture);
}
// Test an error occurs if the texture descriptor is missing
TEST_P(VulkanImageWrappingValidationTests, MissingTextureDescriptor) {
DAWN_SKIP_TEST_IF(UsesWire() || IsIntel());
ASSERT_DEVICE_ERROR(dawn::Texture texture =
WrapVulkanImage(device, nullptr, defaultFd, defaultAllocationSize,
defaultMemoryTypeIndex, {}, true, false));
EXPECT_EQ(texture.Get(), nullptr);
}
// Test an error occurs if the texture descriptor is invalid
TEST_P(VulkanImageWrappingValidationTests, InvalidTextureDescriptor) {
DAWN_SKIP_TEST_IF(UsesWire() || IsIntel());
defaultDescriptor.nextInChain = this;
ASSERT_DEVICE_ERROR(dawn::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) {
DAWN_SKIP_TEST_IF(UsesWire() || IsIntel());
defaultDescriptor.dimension = dawn::TextureDimension::e1D;
ASSERT_DEVICE_ERROR(dawn::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) {
DAWN_SKIP_TEST_IF(UsesWire() || IsIntel());
defaultDescriptor.mipLevelCount = 2;
ASSERT_DEVICE_ERROR(dawn::Texture texture = WrapVulkanImage(
device, &defaultDescriptor, defaultFd, defaultAllocationSize,
defaultMemoryTypeIndex, {}, true, false));
EXPECT_EQ(texture.Get(), nullptr);
}
// Test an error occurs if the descriptor array layer count isn't 1
TEST_P(VulkanImageWrappingValidationTests, InvalidArrayLayerCount) {
DAWN_SKIP_TEST_IF(UsesWire() || IsIntel());
defaultDescriptor.arrayLayerCount = 2;
ASSERT_DEVICE_ERROR(dawn::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) {
DAWN_SKIP_TEST_IF(UsesWire() || IsIntel());
defaultDescriptor.sampleCount = 4;
ASSERT_DEVICE_ERROR(dawn::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) {
DAWN_SKIP_TEST_IF(UsesWire() || IsIntel());
dawn::Texture texture =
WrapVulkanImage(device, &defaultDescriptor, defaultFd, defaultAllocationSize,
defaultMemoryTypeIndex, {}, true, true);
ASSERT_NE(texture.Get(), nullptr);
IgnoreSignalSemaphore(device, texture);
ASSERT_DEVICE_ERROR(
int fd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(device.Get(), texture.Get()));
ASSERT_EQ(fd, -1);
}
// Test an error occurs if we try to export the signal semaphore from a normal texture
TEST_P(VulkanImageWrappingValidationTests, NormalTextureSignalSemaphoreExport) {
DAWN_SKIP_TEST_IF(UsesWire() || IsIntel());
dawn::Texture texture = device.CreateTexture(&defaultDescriptor);
ASSERT_NE(texture.Get(), nullptr);
ASSERT_DEVICE_ERROR(
int fd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(device.Get(), texture.Get()));
ASSERT_EQ(fd, -1);
}
// Test an error occurs if we try to export the signal semaphore from a destroyed texture
TEST_P(VulkanImageWrappingValidationTests, DestroyedTextureSignalSemaphoreExport) {
DAWN_SKIP_TEST_IF(UsesWire() || IsIntel());
dawn::Texture texture = device.CreateTexture(&defaultDescriptor);
ASSERT_NE(texture.Get(), nullptr);
texture.Destroy();
ASSERT_DEVICE_ERROR(
int fd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(device.Get(), texture.Get()));
ASSERT_EQ(fd, -1);
}
// 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() || IsIntel()) {
return;
}
// Create another device based on the original
backendAdapter = reinterpret_cast<dawn_native::vulkan::Adapter*>(deviceVk->GetAdapter());
deviceDescriptor.forceEnabledToggles = GetParam().forceEnabledWorkarounds;
deviceDescriptor.forceDisabledToggles = GetParam().forceDisabledWorkarounds;
secondDeviceVk = reinterpret_cast<dawn_native::vulkan::Device*>(
backendAdapter->CreateDevice(&deviceDescriptor));
secondDevice = dawn::Device::Acquire(reinterpret_cast<DawnDevice>(secondDeviceVk));
CreateBindExportImage(deviceVk, 1, 1, VK_FORMAT_R8G8B8A8_UNORM, &defaultImage,
&defaultAllocation, &defaultAllocationSize, &defaultMemoryTypeIndex,
&defaultFd);
defaultDescriptor.dimension = dawn::TextureDimension::e2D;
defaultDescriptor.format = dawn::TextureFormat::RGBA8Unorm;
defaultDescriptor.size = {1, 1, 1};
defaultDescriptor.sampleCount = 1;
defaultDescriptor.arrayLayerCount = 1;
defaultDescriptor.mipLevelCount = 1;
defaultDescriptor.usage = dawn::TextureUsageBit::OutputAttachment |
dawn::TextureUsageBit::CopySrc | dawn::TextureUsageBit::CopyDst;
}
void TearDown() override {
if (UsesWire() || IsIntel()) {
VulkanImageWrappingTestBase::TearDown();
return;
}
deviceVk->GetFencedDeleter()->DeleteWhenUnused(defaultImage);
deviceVk->GetFencedDeleter()->DeleteWhenUnused(defaultAllocation);
VulkanImageWrappingTestBase::TearDown();
}
protected:
dawn::Device secondDevice;
dawn_native::vulkan::Device* secondDeviceVk;
dawn_native::vulkan::Adapter* backendAdapter;
dawn_native::DeviceDescriptor deviceDescriptor;
dawn::TextureDescriptor defaultDescriptor;
VkImage defaultImage;
VkDeviceMemory defaultAllocation;
VkDeviceSize defaultAllocationSize;
uint32_t defaultMemoryTypeIndex;
int defaultFd;
// Clear a texture on a given device
void ClearImage(dawn::Device device, dawn::Texture wrappedTexture, dawn::Color clearColor) {
dawn::TextureView wrappedView = wrappedTexture.CreateDefaultView();
// Submit a clear operation
utils::ComboRenderPassDescriptor renderPassDescriptor({wrappedView}, {});
renderPassDescriptor.cColorAttachmentsInfoPtr[0]->clearColor = clearColor;
dawn::CommandEncoder encoder = device.CreateCommandEncoder();
dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDescriptor);
pass.EndPass();
dawn::CommandBuffer commands = encoder.Finish();
dawn::Queue queue = device.CreateQueue();
queue.Submit(1, &commands);
}
// Submits a 1x1x1 copy from source to destination
void SimpleCopyTextureToTexture(dawn::Device device,
dawn::Queue queue,
dawn::Texture source,
dawn::Texture destination) {
dawn::TextureCopyView copySrc;
copySrc.texture = source;
copySrc.mipLevel = 0;
copySrc.arrayLayer = 0;
copySrc.origin = {0, 0, 0};
dawn::TextureCopyView copyDst;
copyDst.texture = destination;
copyDst.mipLevel = 0;
copyDst.arrayLayer = 0;
copyDst.origin = {0, 0, 0};
dawn::Extent3D copySize = {1, 1, 1};
dawn::CommandEncoder encoder = device.CreateCommandEncoder();
encoder.CopyTextureToTexture(&copySrc, &copyDst, &copySize);
dawn::CommandBuffer commands = encoder.Finish();
queue.Submit(1, &commands);
}
};
// Clear an image in |secondDevice|
// Verify clear color is visible in |device|
TEST_P(VulkanImageWrappingUsageTests, ClearImageAcrossDevices) {
DAWN_SKIP_TEST_IF(UsesWire() || IsIntel());
// Import the image on |secondDevice|
dawn::Texture wrappedTexture =
WrapVulkanImage(secondDevice, &defaultDescriptor, defaultFd, defaultAllocationSize,
defaultMemoryTypeIndex, {});
// Clear |wrappedTexture| on |secondDevice|
ClearImage(secondDevice, wrappedTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f});
int signalFd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(secondDevice.Get(),
wrappedTexture.Get());
// Import the image to |device|, making sure we wait on signalFd
int memoryFd = GetMemoryFd(deviceVk, defaultAllocation);
dawn::Texture nextWrappedTexture =
WrapVulkanImage(device, &defaultDescriptor, memoryFd, defaultAllocationSize,
defaultMemoryTypeIndex, {signalFd});
// Verify |device| sees the changes from |secondDevice|
EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), nextWrappedTexture, 0, 0);
IgnoreSignalSemaphore(device, nextWrappedTexture);
}
// Import texture to |device| and |secondDevice|
// Clear image in |secondDevice|
// Verify clear color is visible in |device|
// Verify the very first import into |device| also sees the change, since it should
// alias the same memory
TEST_P(VulkanImageWrappingUsageTests, ClearImageAcrossDevicesAliased) {
DAWN_SKIP_TEST_IF(UsesWire() || IsIntel());
// Import the image on |device
dawn::Texture wrappedTextureAlias = WrapVulkanImage(
device, &defaultDescriptor, defaultFd, defaultAllocationSize, defaultMemoryTypeIndex, {});
int memoryFd = GetMemoryFd(deviceVk, defaultAllocation);
// Import the image on |secondDevice|
dawn::Texture wrappedTexture =
WrapVulkanImage(secondDevice, &defaultDescriptor, defaultFd, defaultAllocationSize,
defaultMemoryTypeIndex, {});
// Clear |wrappedTexture| on |secondDevice|
ClearImage(secondDevice, wrappedTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f});
int signalFd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(secondDevice.Get(),
wrappedTexture.Get());
// Import the image to |device|, making sure we wait on signalFd
memoryFd = GetMemoryFd(deviceVk, defaultAllocation);
dawn::Texture nextWrappedTexture =
WrapVulkanImage(device, &defaultDescriptor, memoryFd, defaultAllocationSize,
defaultMemoryTypeIndex, {signalFd});
// Verify |device| sees the changes from |secondDevice| (waits)
EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), nextWrappedTexture, 0, 0);
// Verify aliased texture sees changes from |secondDevice| (without waiting!)
EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), wrappedTextureAlias, 0, 0);
IgnoreSignalSemaphore(device, nextWrappedTexture);
IgnoreSignalSemaphore(device, wrappedTextureAlias);
}
// Clear an image in |secondDevice|
// Verify clear color is not visible in |device| if we import the texture as not cleared
TEST_P(VulkanImageWrappingUsageTests, UnclearedTextureIsCleared) {
DAWN_SKIP_TEST_IF(UsesWire() || IsIntel());
// Import the image on |secondDevice|
dawn::Texture wrappedTexture =
WrapVulkanImage(secondDevice, &defaultDescriptor, defaultFd, defaultAllocationSize,
defaultMemoryTypeIndex, {});
// Clear |wrappedTexture| on |secondDevice|
ClearImage(secondDevice, wrappedTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f});
int signalFd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(secondDevice.Get(),
wrappedTexture.Get());
// Import the image to |device|, making sure we wait on signalFd
int memoryFd = GetMemoryFd(deviceVk, defaultAllocation);
dawn::Texture nextWrappedTexture =
WrapVulkanImage(device, &defaultDescriptor, memoryFd, defaultAllocationSize,
defaultMemoryTypeIndex, {signalFd}, false);
// Verify |device| doesn't see the changes from |secondDevice|
EXPECT_PIXEL_RGBA8_EQ(RGBA8(0, 0, 0, 0), nextWrappedTexture, 0, 0);
IgnoreSignalSemaphore(device, 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) {
DAWN_SKIP_TEST_IF(UsesWire() || IsIntel());
// Import the image on |secondDevice|
dawn::Texture wrappedTexture =
WrapVulkanImage(secondDevice, &defaultDescriptor, defaultFd, defaultAllocationSize,
defaultMemoryTypeIndex, {});
// Clear |wrappedTexture| on |secondDevice|
ClearImage(secondDevice, wrappedTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f});
int signalFd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(secondDevice.Get(),
wrappedTexture.Get());
// Import the image to |device|, making sure we wait on |signalFd|
int memoryFd = GetMemoryFd(deviceVk, defaultAllocation);
dawn::Texture deviceWrappedTexture =
WrapVulkanImage(device, &defaultDescriptor, memoryFd, defaultAllocationSize,
defaultMemoryTypeIndex, {signalFd});
// Create a second texture on |device|
dawn::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(device, 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) {
DAWN_SKIP_TEST_IF(UsesWire() || IsIntel());
// Import the image on |device|
dawn::Texture wrappedTexture = WrapVulkanImage(
device, &defaultDescriptor, defaultFd, defaultAllocationSize, defaultMemoryTypeIndex, {});
// Clear |wrappedTexture| on |device|
ClearImage(device, wrappedTexture, {5 / 255.0f, 6 / 255.0f, 7 / 255.0f, 8 / 255.0f});
int signalFd =
dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(device.Get(), wrappedTexture.Get());
// Import the image to |secondDevice|, making sure we wait on |signalFd|
int memoryFd = GetMemoryFd(deviceVk, defaultAllocation);
dawn::Texture secondDeviceWrappedTexture =
WrapVulkanImage(secondDevice, &defaultDescriptor, memoryFd, defaultAllocationSize,
defaultMemoryTypeIndex, {signalFd});
// Create a texture with color B on |secondDevice|
dawn::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|
dawn::Queue secondDeviceQueue = secondDevice.CreateQueue();
SimpleCopyTextureToTexture(secondDevice, secondDeviceQueue, copySrcTexture,
secondDeviceWrappedTexture);
// Re-import back into |device|, waiting on |secondDevice|'s signal
signalFd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(secondDevice.Get(),
secondDeviceWrappedTexture.Get());
memoryFd = GetMemoryFd(deviceVk, defaultAllocation);
dawn::Texture nextWrappedTexture =
WrapVulkanImage(device, &defaultDescriptor, memoryFd, defaultAllocationSize,
defaultMemoryTypeIndex, {signalFd});
// Verify |nextWrappedTexture| contains the color from our copy
EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), nextWrappedTexture, 0, 0);
IgnoreSignalSemaphore(device, 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) {
DAWN_SKIP_TEST_IF(UsesWire() || IsIntel());
// Import the image on |secondDevice|
dawn::Texture wrappedTexture =
WrapVulkanImage(secondDevice, &defaultDescriptor, defaultFd, defaultAllocationSize,
defaultMemoryTypeIndex, {});
// Clear |wrappedTexture| on |secondDevice|
ClearImage(secondDevice, wrappedTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f});
int signalFd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(secondDevice.Get(),
wrappedTexture.Get());
// Import the image to |device|, making sure we wait on |signalFd|
int memoryFd = GetMemoryFd(deviceVk, defaultAllocation);
dawn::Texture deviceWrappedTexture =
WrapVulkanImage(device, &defaultDescriptor, memoryFd, defaultAllocationSize,
defaultMemoryTypeIndex, {signalFd});
// Create a destination buffer on |device|
dawn::BufferDescriptor bufferDesc;
bufferDesc.size = 4;
bufferDesc.usage = dawn::BufferUsageBit::CopyDst | dawn::BufferUsageBit::CopySrc;
dawn::Buffer copyDstBuffer = device.CreateBuffer(&bufferDesc);
// Copy |deviceWrappedTexture| into |copyDstBuffer|
dawn::TextureCopyView copySrc;
copySrc.texture = deviceWrappedTexture;
copySrc.mipLevel = 0;
copySrc.arrayLayer = 0;
copySrc.origin = {0, 0, 0};
dawn::BufferCopyView copyDst;
copyDst.buffer = copyDstBuffer;
copyDst.offset = 0;
copyDst.rowPitch = 256;
copyDst.imageHeight = 0;
dawn::Extent3D copySize = {1, 1, 1};
dawn::CommandEncoder encoder = device.CreateCommandEncoder();
encoder.CopyTextureToBuffer(&copySrc, &copyDst, &copySize);
dawn::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(device, 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) {
DAWN_SKIP_TEST_IF(UsesWire() || IsIntel());
// Import the image on |device|
dawn::Texture wrappedTexture = WrapVulkanImage(
device, &defaultDescriptor, defaultFd, defaultAllocationSize, defaultMemoryTypeIndex, {});
// Clear |wrappedTexture| on |device|
ClearImage(device, wrappedTexture, {5 / 255.0f, 6 / 255.0f, 7 / 255.0f, 8 / 255.0f});
int signalFd =
dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(device.Get(), wrappedTexture.Get());
// Import the image to |secondDevice|, making sure we wait on |signalFd|
int memoryFd = GetMemoryFd(deviceVk, defaultAllocation);
dawn::Texture secondDeviceWrappedTexture =
WrapVulkanImage(secondDevice, &defaultDescriptor, memoryFd, defaultAllocationSize,
defaultMemoryTypeIndex, {signalFd});
// Copy color B on |secondDevice|
dawn::Queue secondDeviceQueue = secondDevice.CreateQueue();
// Create a buffer on |secondDevice|
dawn::Buffer copySrcBuffer =
utils::CreateBufferFromData(secondDevice, dawn::BufferUsageBit::CopySrc, {0x04030201});
// Copy |copySrcBuffer| into |secondDeviceWrappedTexture|
dawn::BufferCopyView copySrc;
copySrc.buffer = copySrcBuffer;
copySrc.offset = 0;
copySrc.rowPitch = 256;
copySrc.imageHeight = 0;
dawn::TextureCopyView copyDst;
copyDst.texture = secondDeviceWrappedTexture;
copyDst.mipLevel = 0;
copyDst.arrayLayer = 0;
copyDst.origin = {0, 0, 0};
dawn::Extent3D copySize = {1, 1, 1};
dawn::CommandEncoder encoder = secondDevice.CreateCommandEncoder();
encoder.CopyBufferToTexture(&copySrc, &copyDst, &copySize);
dawn::CommandBuffer commands = encoder.Finish();
secondDeviceQueue.Submit(1, &commands);
// Re-import back into |device|, waiting on |secondDevice|'s signal
signalFd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(secondDevice.Get(),
secondDeviceWrappedTexture.Get());
memoryFd = GetMemoryFd(deviceVk, defaultAllocation);
dawn::Texture nextWrappedTexture =
WrapVulkanImage(device, &defaultDescriptor, memoryFd, defaultAllocationSize,
defaultMemoryTypeIndex, {signalFd});
// Verify |nextWrappedTexture| contains the color from our copy
EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), nextWrappedTexture, 0, 0);
IgnoreSignalSemaphore(device, 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) {
DAWN_SKIP_TEST_IF(UsesWire() || IsIntel());
// Import the image on |secondDevice|
dawn::Texture wrappedTexture =
WrapVulkanImage(secondDevice, &defaultDescriptor, defaultFd, defaultAllocationSize,
defaultMemoryTypeIndex, {});
// Clear |wrappedTexture| on |secondDevice|
ClearImage(secondDevice, wrappedTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f});
int signalFd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(secondDevice.Get(),
wrappedTexture.Get());
// Import the image to |device|, making sure we wait on |signalFd|
int memoryFd = GetMemoryFd(deviceVk, defaultAllocation);
dawn::Texture deviceWrappedTexture =
WrapVulkanImage(device, &defaultDescriptor, memoryFd, defaultAllocationSize,
defaultMemoryTypeIndex, {signalFd});
// Create a second texture on |device|
dawn::Texture copyDstTexture = device.CreateTexture(&defaultDescriptor);
// Create a third texture on |device|
dawn::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(device, 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) {
DAWN_SKIP_TEST_IF(UsesWire() || IsIntel());
// 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 = reinterpret_cast<dawn_native::vulkan::Device*>(
backendAdapter->CreateDevice(&deviceDescriptor));
dawn::Device thirdDevice = dawn::Device::Acquire(reinterpret_cast<DawnDevice>(thirdDeviceVk));
// Make queue for device 2 and 3
dawn::Queue secondDeviceQueue = secondDevice.CreateQueue();
dawn::Queue thirdDeviceQueue = thirdDevice.CreateQueue();
// 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
dawn::Texture wrappedTexADevice3 = WrapVulkanImage(thirdDevice, &defaultDescriptor, memoryFdA,
allocationSizeA, memoryTypeIndexA, {});
dawn::Texture wrappedTexBDevice3 = WrapVulkanImage(thirdDevice, &defaultDescriptor, memoryFdB,
allocationSizeB, memoryTypeIndexB, {});
// 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);
int signalFdTexBDevice3 = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(
thirdDevice.Get(), wrappedTexBDevice3.Get());
IgnoreSignalSemaphore(thirdDevice, wrappedTexADevice3);
// Import TexB, TexC on device 2
memoryFdB = GetMemoryFd(secondDeviceVk, allocationB);
dawn::Texture wrappedTexBDevice2 =
WrapVulkanImage(secondDevice, &defaultDescriptor, memoryFdB, allocationSizeB,
memoryTypeIndexB, {signalFdTexBDevice3});
dawn::Texture wrappedTexCDevice2 = WrapVulkanImage(secondDevice, &defaultDescriptor, memoryFdC,
allocationSizeC, memoryTypeIndexC, {});
// Copy B->C on device 2
SimpleCopyTextureToTexture(secondDevice, secondDeviceQueue, wrappedTexBDevice2,
wrappedTexCDevice2);
int signalFdTexCDevice2 = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(
secondDevice.Get(), wrappedTexCDevice2.Get());
IgnoreSignalSemaphore(secondDevice, wrappedTexBDevice2);
// Import TexC on device 1
memoryFdC = GetMemoryFd(deviceVk, allocationC);
dawn::Texture wrappedTexCDevice1 =
WrapVulkanImage(device, &defaultDescriptor, memoryFdC, allocationSizeC, memoryTypeIndexC,
{signalFdTexCDevice2});
// Create TexD on device 1
dawn::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(device, wrappedTexCDevice1);
}
DAWN_INSTANTIATE_TEST(VulkanImageWrappingValidationTests, VulkanBackend);
DAWN_INSTANTIATE_TEST(VulkanImageWrappingUsageTests, VulkanBackend);