Pass the old/new VkImageLayouts to Vulkan image import/export

Returning the layouts from an export operation and then using
them in a subsequent import operation allows the import to preserve
the texture contents.

This fixes Vukan image wrapping on some AMD/NVIDIA devices.

Bug: dawn:200
Change-Id: Icbb6e759856d410bb69724b9f439bc3088756d19
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/28380
Commit-Queue: Austin Eng <enga@chromium.org>
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
This commit is contained in:
Austin Eng 2020-09-22 20:10:46 +00:00 committed by Commit Bot service account
parent 670858da9b
commit 0b29732cd8
19 changed files with 635 additions and 377 deletions

View File

@ -195,8 +195,14 @@ namespace dawn_native {
return GetProcMapNamesForTestingInternal(); return GetProcMapNamesForTestingInternal();
} }
ExternalImageDescriptor::ExternalImageDescriptor(ExternalImageDescriptorType type) // ExternalImageDescriptor
: type(type) {
ExternalImageDescriptor::ExternalImageDescriptor(ExternalImageType type) : type(type) {
}
// ExternalImageExportInfo
ExternalImageExportInfo::ExternalImageExportInfo(ExternalImageType type) : type(type) {
} }
} // namespace dawn_native } // namespace dawn_native

View File

@ -48,7 +48,7 @@ namespace dawn_native { namespace d3d12 {
} }
ExternalImageDescriptorDXGISharedHandle::ExternalImageDescriptorDXGISharedHandle() ExternalImageDescriptorDXGISharedHandle::ExternalImageDescriptorDXGISharedHandle()
: ExternalImageDescriptor(ExternalImageDescriptorType::DXGISharedHandle) { : ExternalImageDescriptor(ExternalImageType::DXGISharedHandle) {
} }
uint64_t SetExternalMemoryReservation(WGPUDevice device, uint64_t SetExternalMemoryReservation(WGPUDevice device,

View File

@ -396,7 +396,7 @@ namespace dawn_native { namespace d3d12 {
AcquireRef(new Texture(device, textureDescriptor, TextureState::OwnedExternal)); AcquireRef(new Texture(device, textureDescriptor, TextureState::OwnedExternal));
DAWN_TRY(dawnTexture->InitializeAsExternalTexture(textureDescriptor, sharedHandle, DAWN_TRY(dawnTexture->InitializeAsExternalTexture(textureDescriptor, sharedHandle,
acquireMutexKey, isSwapChainTexture)); acquireMutexKey, isSwapChainTexture));
dawnTexture->SetIsSubresourceContentInitialized(descriptor->isCleared, dawnTexture->SetIsSubresourceContentInitialized(descriptor->isInitialized,
dawnTexture->GetAllSubresources()); dawnTexture->GetAllSubresources());
return std::move(dawnTexture); return std::move(dawnTexture);
} }

View File

@ -28,7 +28,7 @@ namespace dawn_native { namespace metal {
} }
ExternalImageDescriptorIOSurface::ExternalImageDescriptorIOSurface() ExternalImageDescriptorIOSurface::ExternalImageDescriptorIOSurface()
: ExternalImageDescriptor(ExternalImageDescriptorType::IOSurface) { : ExternalImageDescriptor(ExternalImageType::IOSurface) {
} }
WGPUTexture WrapIOSurface(WGPUDevice cDevice, WGPUTexture WrapIOSurface(WGPUDevice cDevice,

View File

@ -355,7 +355,7 @@ namespace dawn_native { namespace metal {
plane:plane]; plane:plane];
[mtlDesc release]; [mtlDesc release];
SetIsSubresourceContentInitialized(descriptor->isCleared, GetAllSubresources()); SetIsSubresourceContentInitialized(descriptor->isInitialized, GetAllSubresources());
} }
Texture::~Texture() { Texture::~Texture() {

View File

@ -658,7 +658,7 @@ namespace dawn_native { namespace vulkan {
return {}; return {};
} }
MaybeError Device::ImportExternalImage(const ExternalImageDescriptor* descriptor, MaybeError Device::ImportExternalImage(const ExternalImageDescriptorVk* descriptor,
ExternalMemoryHandle memoryHandle, ExternalMemoryHandle memoryHandle,
VkImage image, VkImage image,
const std::vector<ExternalSemaphoreHandle>& waitHandles, const std::vector<ExternalSemaphoreHandle>& waitHandles,
@ -702,22 +702,35 @@ namespace dawn_native { namespace vulkan {
return {}; return {};
} }
MaybeError Device::SignalAndExportExternalTexture(Texture* texture, bool Device::SignalAndExportExternalTexture(
ExternalSemaphoreHandle* outHandle) { Texture* texture,
DAWN_TRY(ValidateObject(texture)); VkImageLayout desiredLayout,
ExternalImageExportInfoVk* info,
std::vector<ExternalSemaphoreHandle>* semaphoreHandles) {
return !ConsumedError([&]() -> MaybeError {
DAWN_TRY(ValidateObject(texture));
VkSemaphore outSignalSemaphore; VkSemaphore signalSemaphore;
DAWN_TRY(texture->SignalAndDestroy(&outSignalSemaphore)); VkImageLayout releasedOldLayout;
VkImageLayout releasedNewLayout;
DAWN_TRY(texture->ExportExternalTexture(desiredLayout, &signalSemaphore,
&releasedOldLayout, &releasedNewLayout));
// This has to happen right after SignalAndDestroy, since the semaphore will be ExternalSemaphoreHandle semaphoreHandle;
// deleted when the fenced deleter runs after the queue submission DAWN_TRY_ASSIGN(semaphoreHandle,
DAWN_TRY_ASSIGN(*outHandle, mExternalSemaphoreService->ExportSemaphore(outSignalSemaphore)); mExternalSemaphoreService->ExportSemaphore(signalSemaphore));
semaphoreHandles->push_back(semaphoreHandle);
info->releasedOldLayout = releasedOldLayout;
info->releasedNewLayout = releasedNewLayout;
info->isInitialized =
texture->IsSubresourceContentInitialized(texture->GetAllSubresources());
return {}; return {};
}());
} }
TextureBase* Device::CreateTextureWrappingVulkanImage( TextureBase* Device::CreateTextureWrappingVulkanImage(
const ExternalImageDescriptor* descriptor, const ExternalImageDescriptorVk* descriptor,
ExternalMemoryHandle memoryHandle, ExternalMemoryHandle memoryHandle,
const std::vector<ExternalSemaphoreHandle>& waitHandles) { const std::vector<ExternalSemaphoreHandle>& waitHandles) {
const TextureDescriptor* textureDescriptor = const TextureDescriptor* textureDescriptor =

View File

@ -69,12 +69,13 @@ namespace dawn_native { namespace vulkan {
// Dawn Native API // Dawn Native API
TextureBase* CreateTextureWrappingVulkanImage( TextureBase* CreateTextureWrappingVulkanImage(
const ExternalImageDescriptor* descriptor, const ExternalImageDescriptorVk* descriptor,
ExternalMemoryHandle memoryHandle, ExternalMemoryHandle memoryHandle,
const std::vector<ExternalSemaphoreHandle>& waitHandles); const std::vector<ExternalSemaphoreHandle>& waitHandles);
bool SignalAndExportExternalTexture(Texture* texture,
MaybeError SignalAndExportExternalTexture(Texture* texture, VkImageLayout desiredLayout,
ExternalSemaphoreHandle* outHandle); ExternalImageExportInfoVk* info,
std::vector<ExternalSemaphoreHandle>* semaphoreHandle);
// Dawn API // Dawn API
CommandBufferBase* CreateCommandBuffer(CommandEncoder* encoder, CommandBufferBase* CreateCommandBuffer(CommandEncoder* encoder,
@ -192,7 +193,7 @@ namespace dawn_native { namespace vulkan {
// There is always a valid recording context stored in mRecordingContext // There is always a valid recording context stored in mRecordingContext
CommandRecordingContext mRecordingContext; CommandRecordingContext mRecordingContext;
MaybeError ImportExternalImage(const ExternalImageDescriptor* descriptor, MaybeError ImportExternalImage(const ExternalImageDescriptorVk* descriptor,
ExternalMemoryHandle memoryHandle, ExternalMemoryHandle memoryHandle,
VkImage image, VkImage image,
const std::vector<ExternalSemaphoreHandle>& waitHandles, const std::vector<ExternalSemaphoreHandle>& waitHandles,
@ -203,4 +204,4 @@ namespace dawn_native { namespace vulkan {
}} // namespace dawn_native::vulkan }} // namespace dawn_native::vulkan
#endif // DAWNNATIVE_VULKAN_DEVICEVK_H_ #endif // DAWNNATIVE_VULKAN_DEVICEVK_H_

View File

@ -462,7 +462,7 @@ namespace dawn_native { namespace vulkan {
// static // static
ResultOrError<Texture*> Texture::CreateFromExternal( ResultOrError<Texture*> Texture::CreateFromExternal(
Device* device, Device* device,
const ExternalImageDescriptor* descriptor, const ExternalImageDescriptorVk* descriptor,
const TextureDescriptor* textureDescriptor, const TextureDescriptor* textureDescriptor,
external_memory::Service* externalMemoryService) { external_memory::Service* externalMemoryService) {
Ref<Texture> texture = Ref<Texture> texture =
@ -537,7 +537,7 @@ namespace dawn_native { namespace vulkan {
} }
// Internally managed, but imported from external handle // Internally managed, but imported from external handle
MaybeError Texture::InitializeFromExternal(const ExternalImageDescriptor* descriptor, MaybeError Texture::InitializeFromExternal(const ExternalImageDescriptorVk* descriptor,
external_memory::Service* externalMemoryService) { external_memory::Service* externalMemoryService) {
VkFormat format = VulkanImageFormat(ToBackend(GetDevice()), GetFormat().format); VkFormat format = VulkanImageFormat(ToBackend(GetDevice()), GetFormat().format);
VkImageUsageFlags usage = VulkanImageUsage(GetUsage(), GetFormat()); VkImageUsageFlags usage = VulkanImageUsage(GetUsage(), GetFormat());
@ -547,6 +547,9 @@ namespace dawn_native { namespace vulkan {
mExternalState = ExternalState::PendingAcquire; mExternalState = ExternalState::PendingAcquire;
mPendingAcquireOldLayout = descriptor->releasedOldLayout;
mPendingAcquireNewLayout = descriptor->releasedNewLayout;
VkImageCreateInfo baseCreateInfo = {}; VkImageCreateInfo baseCreateInfo = {};
FillVulkanCreateInfoSizesAndType(*this, &baseCreateInfo); FillVulkanCreateInfoSizesAndType(*this, &baseCreateInfo);
@ -571,7 +574,7 @@ namespace dawn_native { namespace vulkan {
mHandle = nativeImage; mHandle = nativeImage;
} }
MaybeError Texture::BindExternalMemory(const ExternalImageDescriptor* descriptor, MaybeError Texture::BindExternalMemory(const ExternalImageDescriptorVk* descriptor,
VkSemaphore signalSemaphore, VkSemaphore signalSemaphore,
VkDeviceMemory externalMemoryAllocation, VkDeviceMemory externalMemoryAllocation,
std::vector<VkSemaphore> waitSemaphores) { std::vector<VkSemaphore> waitSemaphores) {
@ -580,8 +583,8 @@ namespace dawn_native { namespace vulkan {
device->fn.BindImageMemory(device->GetVkDevice(), mHandle, externalMemoryAllocation, 0), device->fn.BindImageMemory(device->GetVkDevice(), mHandle, externalMemoryAllocation, 0),
"BindImageMemory (external)")); "BindImageMemory (external)"));
// Don't clear imported texture if already cleared // Don't clear imported texture if already initialized
if (descriptor->isCleared) { if (descriptor->isInitialized) {
SetIsSubresourceContentInitialized(true, GetAllSubresources()); SetIsSubresourceContentInitialized(true, GetAllSubresources());
} }
@ -592,7 +595,10 @@ namespace dawn_native { namespace vulkan {
return {}; return {};
} }
MaybeError Texture::SignalAndDestroy(VkSemaphore* outSignalSemaphore) { MaybeError Texture::ExportExternalTexture(VkImageLayout desiredLayout,
VkSemaphore* signalSemaphore,
VkImageLayout* releasedOldLayout,
VkImageLayout* releasedNewLayout) {
Device* device = ToBackend(GetDevice()); Device* device = ToBackend(GetDevice());
if (mExternalState == ExternalState::Released) { if (mExternalState == ExternalState::Released) {
@ -605,17 +611,60 @@ namespace dawn_native { namespace vulkan {
} }
ASSERT(mSignalSemaphore != VK_NULL_HANDLE); ASSERT(mSignalSemaphore != VK_NULL_HANDLE);
ASSERT(GetNumMipLevels() == 1 && GetArrayLayers() == 1);
// Release the texture // Release the texture
mExternalState = ExternalState::PendingRelease; mExternalState = ExternalState::Released;
TransitionFullUsage(device->GetPendingRecordingContext(), wgpu::TextureUsage::None);
wgpu::TextureUsage usage = mSubresourceLastUsages[0];
VkImageMemoryBarrier barrier;
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
barrier.pNext = nullptr;
barrier.image = GetHandle();
barrier.subresourceRange.aspectMask = VulkanAspectMask(GetFormat().aspects);
barrier.subresourceRange.baseMipLevel = 0;
barrier.subresourceRange.levelCount = 1;
barrier.subresourceRange.baseArrayLayer = 0;
barrier.subresourceRange.layerCount = 1;
barrier.srcAccessMask = VulkanAccessFlags(usage, GetFormat());
barrier.dstAccessMask = 0; // The barrier must be paired with another barrier that will
// specify the dst access mask on the importing queue.
barrier.oldLayout = VulkanImageLayout(usage, GetFormat());
if (desiredLayout == VK_IMAGE_LAYOUT_UNDEFINED) {
// VK_IMAGE_LAYOUT_UNDEFINED is invalid here. We use it as a
// special value to indicate no layout transition should be done.
barrier.newLayout = barrier.oldLayout;
} else {
barrier.newLayout = desiredLayout;
}
barrier.srcQueueFamilyIndex = device->GetGraphicsQueueFamily();
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_EXTERNAL_KHR;
VkPipelineStageFlags srcStages = VulkanPipelineStage(usage, GetFormat());
VkPipelineStageFlags dstStages =
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; // We don't know when the importing queue will need
// the texture, so pass
// VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT to ensure
// the barrier happens-before any usage in the
// importing queue.
CommandRecordingContext* recordingContext = device->GetPendingRecordingContext();
device->fn.CmdPipelineBarrier(recordingContext->commandBuffer, srcStages, dstStages, 0, 0,
nullptr, 0, nullptr, 1, &barrier);
// Queue submit to signal we are done with the texture // Queue submit to signal we are done with the texture
device->GetPendingRecordingContext()->signalSemaphores.push_back(mSignalSemaphore); recordingContext->signalSemaphores.push_back(mSignalSemaphore);
DAWN_TRY(device->SubmitPendingCommands()); DAWN_TRY(device->SubmitPendingCommands());
// Write out the signal semaphore // Write out the layouts and signal semaphore
*outSignalSemaphore = mSignalSemaphore; *releasedOldLayout = barrier.oldLayout;
*releasedNewLayout = barrier.newLayout;
*signalSemaphore = mSignalSemaphore;
mSignalSemaphore = VK_NULL_HANDLE; mSignalSemaphore = VK_NULL_HANDLE;
// Destroy the texture so it can't be used again // Destroy the texture so it can't be used again
@ -688,26 +737,58 @@ namespace dawn_native { namespace vulkan {
SubresourceRange::SingleMipAndLayer(0, 0, GetFormat().aspects))); SubresourceRange::SingleMipAndLayer(0, 0, GetFormat().aspects)));
} }
VkImageMemoryBarrier* barrier = &(*barriers)[transitionBarrierStart];
// Transfer texture from external queue to graphics queue // Transfer texture from external queue to graphics queue
(*barriers)[transitionBarrierStart].srcQueueFamilyIndex = VK_QUEUE_FAMILY_EXTERNAL_KHR; barrier->srcQueueFamilyIndex = VK_QUEUE_FAMILY_EXTERNAL_KHR;
(*barriers)[transitionBarrierStart].dstQueueFamilyIndex = barrier->dstQueueFamilyIndex = ToBackend(GetDevice())->GetGraphicsQueueFamily();
ToBackend(GetDevice())->GetGraphicsQueueFamily();
// Don't override oldLayout to leave it as VK_IMAGE_LAYOUT_UNDEFINED // srcAccessMask means nothing when importing. Queue transfers require a barrier on
// TODO(http://crbug.com/dawn/200) // both the importing and exporting queues. The exporting queue should have specified
mExternalState = ExternalState::Acquired; // this.
} else if (mExternalState == ExternalState::PendingRelease) { barrier->srcAccessMask = 0;
if (barriers->size() == transitionBarrierStart) {
barriers->push_back(BuildMemoryBarrier( // This should be the first barrier after import.
GetFormat(), mHandle, wgpu::TextureUsage::None, wgpu::TextureUsage::None, ASSERT(barrier->oldLayout == VK_IMAGE_LAYOUT_UNDEFINED);
SubresourceRange::SingleMipAndLayer(0, 0, GetFormat().aspects)));
// Save the desired layout. We may need to transition through an intermediate
// |mPendingAcquireLayout| first.
VkImageLayout desiredLayout = barrier->newLayout;
bool isInitialized = IsSubresourceContentInitialized(GetAllSubresources());
// We don't care about the pending old layout if the texture is uninitialized. The
// driver is free to discard it. Likewise, we don't care about the pending new layout if
// the texture is uninitialized. We can skip the layout transition.
if (!isInitialized) {
barrier->oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
barrier->newLayout = desiredLayout;
} else {
barrier->oldLayout = mPendingAcquireOldLayout;
barrier->newLayout = mPendingAcquireNewLayout;
} }
// Transfer texture from graphics queue to external queue // If these are unequal, we need an another barrier to transition the layout.
(*barriers)[transitionBarrierStart].srcQueueFamilyIndex = if (barrier->newLayout != desiredLayout) {
ToBackend(GetDevice())->GetGraphicsQueueFamily(); VkImageMemoryBarrier layoutBarrier;
(*barriers)[transitionBarrierStart].dstQueueFamilyIndex = VK_QUEUE_FAMILY_EXTERNAL_KHR; layoutBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
(*barriers)[transitionBarrierStart].newLayout = VK_IMAGE_LAYOUT_GENERAL; layoutBarrier.pNext = nullptr;
mExternalState = ExternalState::Released; layoutBarrier.image = GetHandle();
layoutBarrier.subresourceRange = barrier->subresourceRange;
// Transition from the acquired new layout to the desired layout.
layoutBarrier.oldLayout = barrier->newLayout;
layoutBarrier.newLayout = desiredLayout;
// We already transitioned these.
layoutBarrier.srcAccessMask = 0;
layoutBarrier.dstAccessMask = 0;
layoutBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
layoutBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barriers->push_back(layoutBarrier);
}
mExternalState = ExternalState::Acquired;
} }
mLastExternalState = mExternalState; mLastExternalState = mExternalState;

View File

@ -49,7 +49,7 @@ namespace dawn_native { namespace vulkan {
// image must be bound via Texture::BindExternalMemory. // image must be bound via Texture::BindExternalMemory.
static ResultOrError<Texture*> CreateFromExternal( static ResultOrError<Texture*> CreateFromExternal(
Device* device, Device* device,
const ExternalImageDescriptor* descriptor, const ExternalImageDescriptorVk* descriptor,
const TextureDescriptor* textureDescriptor, const TextureDescriptor* textureDescriptor,
external_memory::Service* externalMemoryService); external_memory::Service* externalMemoryService);
@ -84,20 +84,24 @@ namespace dawn_native { namespace vulkan {
void EnsureSubresourceContentInitialized(CommandRecordingContext* recordingContext, void EnsureSubresourceContentInitialized(CommandRecordingContext* recordingContext,
const SubresourceRange& range); const SubresourceRange& range);
MaybeError SignalAndDestroy(VkSemaphore* outSignalSemaphore);
// Binds externally allocated memory to the VkImage and on success, takes ownership of // Binds externally allocated memory to the VkImage and on success, takes ownership of
// semaphores. // semaphores.
MaybeError BindExternalMemory(const ExternalImageDescriptor* descriptor, MaybeError BindExternalMemory(const ExternalImageDescriptorVk* descriptor,
VkSemaphore signalSemaphore, VkSemaphore signalSemaphore,
VkDeviceMemory externalMemoryAllocation, VkDeviceMemory externalMemoryAllocation,
std::vector<VkSemaphore> waitSemaphores); std::vector<VkSemaphore> waitSemaphores);
MaybeError ExportExternalTexture(VkImageLayout desiredLayout,
VkSemaphore* signalSemaphore,
VkImageLayout* releasedOldLayout,
VkImageLayout* releasedNewLayout);
private: private:
~Texture() override; ~Texture() override;
using TextureBase::TextureBase; using TextureBase::TextureBase;
MaybeError InitializeAsInternalTexture(); MaybeError InitializeAsInternalTexture();
MaybeError InitializeFromExternal(const ExternalImageDescriptor* descriptor, MaybeError InitializeFromExternal(const ExternalImageDescriptorVk* descriptor,
external_memory::Service* externalMemoryService); external_memory::Service* externalMemoryService);
void InitializeForSwapChain(VkImage nativeImage); void InitializeForSwapChain(VkImage nativeImage);
@ -119,12 +123,14 @@ namespace dawn_native { namespace vulkan {
InternalOnly, InternalOnly,
PendingAcquire, PendingAcquire,
Acquired, Acquired,
PendingRelease,
Released Released
}; };
ExternalState mExternalState = ExternalState::InternalOnly; ExternalState mExternalState = ExternalState::InternalOnly;
ExternalState mLastExternalState = ExternalState::InternalOnly; ExternalState mLastExternalState = ExternalState::InternalOnly;
VkImageLayout mPendingAcquireOldLayout;
VkImageLayout mPendingAcquireNewLayout;
VkSemaphore mSignalSemaphore = VK_NULL_HANDLE; VkSemaphore mSignalSemaphore = VK_NULL_HANDLE;
std::vector<VkSemaphore> mWaitRequirements; std::vector<VkSemaphore> mWaitRequirements;

View File

@ -59,51 +59,75 @@ namespace dawn_native { namespace vulkan {
return static_cast<WGPUTextureFormat>(impl->GetPreferredFormat()); return static_cast<WGPUTextureFormat>(impl->GetPreferredFormat());
} }
#ifdef DAWN_PLATFORM_LINUX #if defined(DAWN_PLATFORM_LINUX)
ExternalImageDescriptorFD::ExternalImageDescriptorFD(ExternalImageDescriptorType descType)
: ExternalImageDescriptor(descType) {
}
ExternalImageDescriptorOpaqueFD::ExternalImageDescriptorOpaqueFD() ExternalImageDescriptorOpaqueFD::ExternalImageDescriptorOpaqueFD()
: ExternalImageDescriptorFD(ExternalImageDescriptorType::OpaqueFD) { : ExternalImageDescriptorFD(ExternalImageType::OpaqueFD) {
} }
ExternalImageDescriptorDmaBuf::ExternalImageDescriptorDmaBuf() ExternalImageDescriptorDmaBuf::ExternalImageDescriptorDmaBuf()
: ExternalImageDescriptorFD(ExternalImageDescriptorType::DmaBuf) { : ExternalImageDescriptorFD(ExternalImageType::DmaBuf) {
}
ExternalImageExportInfoOpaqueFD::ExternalImageExportInfoOpaqueFD()
: ExternalImageExportInfoFD(ExternalImageType::OpaqueFD) {
}
ExternalImageExportInfoDmaBuf::ExternalImageExportInfoDmaBuf()
: ExternalImageExportInfoFD(ExternalImageType::DmaBuf) {
} }
int ExportSignalSemaphoreOpaqueFD(WGPUDevice cDevice, WGPUTexture cTexture) { int ExportSignalSemaphoreOpaqueFD(WGPUDevice cDevice, WGPUTexture cTexture) {
// Doesn't actually matter if we use OpaqueFD or DmaBuf since these paths are the same right
// now. This function will be removed.
Device* device = reinterpret_cast<Device*>(cDevice); Device* device = reinterpret_cast<Device*>(cDevice);
Texture* texture = reinterpret_cast<Texture*>(cTexture); device->EmitDeprecationWarning(
"ExportSignalSemaphoreOpaqueFD is deprecated. Please use ExportVulkanImage instead.");
if (!texture) { ExternalImageExportInfoOpaqueFD info;
if (!ExportVulkanImage(cTexture, VK_IMAGE_LAYOUT_GENERAL, &info)) {
return -1; return -1;
} }
return info.semaphoreHandles[0];
ExternalSemaphoreHandle outHandle;
if (device->ConsumedError(device->SignalAndExportExternalTexture(texture, &outHandle))) {
return -1;
}
return outHandle;
} }
#endif // DAWN_PLATFORM_LINUX
WGPUTexture WrapVulkanImage(WGPUDevice cDevice, const ExternalImageDescriptor* descriptor) { WGPUTexture WrapVulkanImage(WGPUDevice cDevice, const ExternalImageDescriptorVk* descriptor) {
Device* device = reinterpret_cast<Device*>(cDevice);
switch (descriptor->type) { switch (descriptor->type) {
case ExternalImageDescriptorType::OpaqueFD: #if defined(DAWN_PLATFORM_LINUX)
case ExternalImageDescriptorType::DmaBuf: { case ExternalImageType::OpaqueFD:
case ExternalImageType::DmaBuf: {
const ExternalImageDescriptorFD* fdDescriptor = const ExternalImageDescriptorFD* fdDescriptor =
static_cast<const ExternalImageDescriptorFD*>(descriptor); static_cast<const ExternalImageDescriptorFD*>(descriptor);
Device* device = reinterpret_cast<Device*>(cDevice);
TextureBase* texture = device->CreateTextureWrappingVulkanImage( TextureBase* texture = device->CreateTextureWrappingVulkanImage(
descriptor, fdDescriptor->memoryFD, fdDescriptor->waitFDs); fdDescriptor, fdDescriptor->memoryFD, fdDescriptor->waitFDs);
return reinterpret_cast<WGPUTexture>(texture); return reinterpret_cast<WGPUTexture>(texture);
} }
#endif // DAWN_PLATFORM_LINUX
default: default:
return nullptr; return nullptr;
} }
} }
#endif
bool ExportVulkanImage(WGPUTexture cTexture,
VkImageLayout desiredLayout,
ExternalImageExportInfoVk* info) {
if (cTexture == nullptr) {
return false;
}
switch (info->type) {
#if defined(DAWN_PLATFORM_LINUX)
case ExternalImageType::OpaqueFD:
case ExternalImageType::DmaBuf: {
Texture* texture = reinterpret_cast<Texture*>(cTexture);
Device* device = ToBackend(texture->GetDevice());
ExternalImageExportInfoFD* fdInfo = static_cast<ExternalImageExportInfoFD*>(info);
return device->SignalAndExportExternalTexture(texture, desiredLayout, fdInfo,
&fdInfo->semaphoreHandles);
}
#endif // DAWN_PLATFORM_LINUX
default:
return false;
}
}
}} // namespace dawn_native::vulkan }} // namespace dawn_native::vulkan

View File

@ -83,7 +83,7 @@ namespace dawn_native { namespace vulkan { namespace external_memory {
if (!mSupported) { if (!mSupported) {
return false; return false;
} }
if (descriptor->type != ExternalImageDescriptorType::DmaBuf) { if (descriptor->type != ExternalImageType::DmaBuf) {
return false; return false;
} }
const ExternalImageDescriptorDmaBuf* dmaBufDescriptor = const ExternalImageDescriptorDmaBuf* dmaBufDescriptor =
@ -150,7 +150,7 @@ namespace dawn_native { namespace vulkan { namespace external_memory {
ResultOrError<MemoryImportParams> Service::GetMemoryImportParams( ResultOrError<MemoryImportParams> Service::GetMemoryImportParams(
const ExternalImageDescriptor* descriptor, const ExternalImageDescriptor* descriptor,
VkImage image) { VkImage image) {
if (descriptor->type != ExternalImageDescriptorType::DmaBuf) { if (descriptor->type != ExternalImageType::DmaBuf) {
return DAWN_VALIDATION_ERROR("ExternalImageDescriptor is not a dma-buf descriptor"); return DAWN_VALIDATION_ERROR("ExternalImageDescriptor is not a dma-buf descriptor");
} }
const ExternalImageDescriptorDmaBuf* dmaBufDescriptor = const ExternalImageDescriptorDmaBuf* dmaBufDescriptor =
@ -216,7 +216,7 @@ namespace dawn_native { namespace vulkan { namespace external_memory {
ResultOrError<VkImage> Service::CreateImage(const ExternalImageDescriptor* descriptor, ResultOrError<VkImage> Service::CreateImage(const ExternalImageDescriptor* descriptor,
const VkImageCreateInfo& baseCreateInfo) { const VkImageCreateInfo& baseCreateInfo) {
if (descriptor->type != ExternalImageDescriptorType::DmaBuf) { if (descriptor->type != ExternalImageType::DmaBuf) {
return DAWN_VALIDATION_ERROR("ExternalImageDescriptor is not a dma-buf descriptor"); return DAWN_VALIDATION_ERROR("ExternalImageDescriptor is not a dma-buf descriptor");
} }
const ExternalImageDescriptorDmaBuf* dmaBufDescriptor = const ExternalImageDescriptorDmaBuf* dmaBufDescriptor =

View File

@ -71,8 +71,7 @@ namespace dawn_native { namespace vulkan { namespace external_memory {
// TODO(http://crbug.com/dawn/206): Investigate dedicated only images // TODO(http://crbug.com/dawn/206): Investigate dedicated only images
VkFlags memoryFlags = VkFlags memoryFlags =
externalFormatProperties.externalMemoryProperties.externalMemoryFeatures; externalFormatProperties.externalMemoryProperties.externalMemoryFeatures;
return (memoryFlags & VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT_KHR) && return (memoryFlags & VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT_KHR) != 0;
!(memoryFlags & VK_EXTERNAL_MEMORY_FEATURE_DEDICATED_ONLY_BIT_KHR);
} }
bool Service::SupportsCreateImage(const ExternalImageDescriptor* descriptor, bool Service::SupportsCreateImage(const ExternalImageDescriptor* descriptor,
@ -84,7 +83,7 @@ namespace dawn_native { namespace vulkan { namespace external_memory {
ResultOrError<MemoryImportParams> Service::GetMemoryImportParams( ResultOrError<MemoryImportParams> Service::GetMemoryImportParams(
const ExternalImageDescriptor* descriptor, const ExternalImageDescriptor* descriptor,
VkImage image) { VkImage image) {
if (descriptor->type != ExternalImageDescriptorType::OpaqueFD) { if (descriptor->type != ExternalImageType::OpaqueFD) {
return DAWN_VALIDATION_ERROR("ExternalImageDescriptor is not an OpaqueFD descriptor"); return DAWN_VALIDATION_ERROR("ExternalImageDescriptor is not an OpaqueFD descriptor");
} }
const ExternalImageDescriptorOpaqueFD* opaqueFDDescriptor = const ExternalImageDescriptorOpaqueFD* opaqueFDDescriptor =

View File

@ -71,8 +71,7 @@ namespace dawn_native { namespace vulkan { namespace external_memory {
// TODO(http://crbug.com/dawn/206): Investigate dedicated only images // TODO(http://crbug.com/dawn/206): Investigate dedicated only images
VkFlags memoryFlags = VkFlags memoryFlags =
externalFormatProperties.externalMemoryProperties.externalMemoryFeatures; externalFormatProperties.externalMemoryProperties.externalMemoryFeatures;
return (memoryFlags & VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT_KHR) && return (memoryFlags & VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT_KHR) != 0;
!(memoryFlags & VK_EXTERNAL_MEMORY_FEATURE_DEDICATED_ONLY_BIT_KHR);
} }
bool Service::SupportsCreateImage(const ExternalImageDescriptor* descriptor, bool Service::SupportsCreateImage(const ExternalImageDescriptor* descriptor,
@ -84,7 +83,7 @@ namespace dawn_native { namespace vulkan { namespace external_memory {
ResultOrError<MemoryImportParams> Service::GetMemoryImportParams( ResultOrError<MemoryImportParams> Service::GetMemoryImportParams(
const ExternalImageDescriptor* descriptor, const ExternalImageDescriptor* descriptor,
VkImage image) { VkImage image) {
if (descriptor->type != ExternalImageDescriptorType::OpaqueFD) { if (descriptor->type != ExternalImageType::OpaqueFD) {
return DAWN_VALIDATION_ERROR("ExternalImageDescriptor is not an OpaqueFD descriptor"); return DAWN_VALIDATION_ERROR("ExternalImageDescriptor is not an OpaqueFD descriptor");
} }
const ExternalImageDescriptorOpaqueFD* opaqueFDDescriptor = const ExternalImageDescriptorOpaqueFD* opaqueFDDescriptor =

View File

@ -200,8 +200,8 @@ namespace dawn_native {
DAWN_NATIVE_EXPORT uint64_t AcquireErrorInjectorCallCount(); DAWN_NATIVE_EXPORT uint64_t AcquireErrorInjectorCallCount();
DAWN_NATIVE_EXPORT void InjectErrorAt(uint64_t index); DAWN_NATIVE_EXPORT void InjectErrorAt(uint64_t index);
// The different types of ExternalImageDescriptors // The different types of external images
enum ExternalImageDescriptorType { enum ExternalImageType {
OpaqueFD, OpaqueFD,
DmaBuf, DmaBuf,
IOSurface, IOSurface,
@ -211,13 +211,26 @@ namespace dawn_native {
// Common properties of external images // Common properties of external images
struct DAWN_NATIVE_EXPORT ExternalImageDescriptor { struct DAWN_NATIVE_EXPORT ExternalImageDescriptor {
public: public:
const ExternalImageDescriptorType type; const ExternalImageType type;
const WGPUTextureDescriptor* cTextureDescriptor; // Must match image creation params const WGPUTextureDescriptor* cTextureDescriptor; // Must match image creation params
bool isCleared; // Sets whether the texture will be cleared before use union {
bool isInitialized; // Whether the texture is initialized on import
bool isCleared; // DEPRECATED: Sets whether the texture will be cleared before use
};
protected: protected:
ExternalImageDescriptor(ExternalImageDescriptorType type); ExternalImageDescriptor(ExternalImageType type);
}; };
struct DAWN_NATIVE_EXPORT ExternalImageExportInfo {
public:
const ExternalImageType type;
bool isInitialized; // Whether the texture is initialized after export
protected:
ExternalImageExportInfo(ExternalImageType type);
};
} // namespace dawn_native } // namespace dawn_native
#endif // DAWNNATIVE_DAWNNATIVE_H_ #endif // DAWNNATIVE_DAWNNATIVE_H_

View File

@ -33,19 +33,49 @@ namespace dawn_native { namespace vulkan {
DAWN_NATIVE_EXPORT WGPUTextureFormat DAWN_NATIVE_EXPORT WGPUTextureFormat
GetNativeSwapChainPreferredFormat(const DawnSwapChainImplementation* swapChain); GetNativeSwapChainPreferredFormat(const DawnSwapChainImplementation* swapChain);
// Can't use DAWN_PLATFORM_LINUX since header included in both dawn and chrome struct DAWN_NATIVE_EXPORT ExternalImageDescriptorVk : ExternalImageDescriptor {
public:
// The following members may be ignored if |ExternalImageDescriptor::isInitialized| is false
// since the import does not need to preserve texture contents.
// See https://www.khronos.org/registry/vulkan/specs/1.1/html/chap7.html. The acquire
// operation old/new layouts must match exactly the layouts in the release operation. So
// we may need to issue two barriers releasedOldLayout -> releasedNewLayout ->
// cTextureDescriptor.usage if the new layout is not compatible with the desired usage.
// The first barrier is the queue transfer, the second is the layout transition to our
// desired usage.
VkImageLayout releasedOldLayout = VK_IMAGE_LAYOUT_GENERAL;
VkImageLayout releasedNewLayout = VK_IMAGE_LAYOUT_GENERAL;
protected:
using ExternalImageDescriptor::ExternalImageDescriptor;
};
struct ExternalImageExportInfoVk : ExternalImageExportInfo {
public:
// See comments in |ExternalImageDescriptorVk|
// Contains the old/new layouts used in the queue release operation.
VkImageLayout releasedOldLayout;
VkImageLayout releasedNewLayout;
protected:
using ExternalImageExportInfo::ExternalImageExportInfo;
};
// Can't use DAWN_PLATFORM_LINUX since header included in both Dawn and Chrome
#ifdef __linux__ #ifdef __linux__
// Common properties of external images represented by FDs. On successful import the file // Common properties of external images represented by FDs. On successful import the file
// descriptor's ownership is transferred to the Dawn implementation and they shouldn't be // descriptor's ownership is transferred to the Dawn implementation and they shouldn't be
// used outside of Dawn again. TODO(enga): Also transfer ownership in the error case so the // used outside of Dawn again. TODO(enga): Also transfer ownership in the error case so the
// caller can assume the FD is always consumed. // caller can assume the FD is always consumed.
struct DAWN_NATIVE_EXPORT ExternalImageDescriptorFD : ExternalImageDescriptor { struct DAWN_NATIVE_EXPORT ExternalImageDescriptorFD : ExternalImageDescriptorVk {
public: public:
int memoryFD; // A file descriptor from an export of the memory of the image 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 std::vector<int> waitFDs; // File descriptors of semaphores which will be waited on
protected: protected:
ExternalImageDescriptorFD(ExternalImageDescriptorType type); using ExternalImageDescriptorVk::ExternalImageDescriptorVk;
}; };
// Descriptor for opaque file descriptor image import // Descriptor for opaque file descriptor image import
@ -64,8 +94,29 @@ namespace dawn_native { namespace vulkan {
uint64_t drmModifier; // DRM modifier of the buffer uint64_t drmModifier; // DRM modifier of the buffer
}; };
// Info struct that is written to in |ExportVulkanImage|.
struct DAWN_NATIVE_EXPORT ExternalImageExportInfoFD : ExternalImageExportInfoVk {
public:
// Contains the exported semaphore handles.
std::vector<int> semaphoreHandles;
protected:
using ExternalImageExportInfoVk::ExternalImageExportInfoVk;
};
struct DAWN_NATIVE_EXPORT ExternalImageExportInfoOpaqueFD : ExternalImageExportInfoFD {
ExternalImageExportInfoOpaqueFD();
};
struct DAWN_NATIVE_EXPORT ExternalImageExportInfoDmaBuf : ExternalImageExportInfoFD {
ExternalImageExportInfoDmaBuf();
};
#endif // __linux__
// Exports a signal semaphore from a wrapped texture. This must be called on wrapped // Exports a signal semaphore from a wrapped texture. This must be called on wrapped
// textures before they are destroyed. On failure, returns -1 // textures before they are destroyed. On failure, returns -1
// TODO(enga): Remove after updating Chromium to use ExportVulkanImage.
DAWN_NATIVE_EXPORT int ExportSignalSemaphoreOpaqueFD(WGPUDevice cDevice, DAWN_NATIVE_EXPORT int ExportSignalSemaphoreOpaqueFD(WGPUDevice cDevice,
WGPUTexture cTexture); WGPUTexture cTexture);
@ -74,8 +125,15 @@ namespace dawn_native { namespace vulkan {
// primitives before the texture can be used. // primitives before the texture can be used.
// On failure, returns a nullptr. // On failure, returns a nullptr.
DAWN_NATIVE_EXPORT WGPUTexture WrapVulkanImage(WGPUDevice cDevice, DAWN_NATIVE_EXPORT WGPUTexture WrapVulkanImage(WGPUDevice cDevice,
const ExternalImageDescriptor* descriptor); const ExternalImageDescriptorVk* descriptor);
#endif // __linux__
// Exports external memory from a Vulkan image. This must be called on wrapped textures
// before they are destroyed. It writes the semaphore to wait on and the old/new image
// layouts to |info|. Pass VK_IMAGE_LAYOUT_UNDEFINED as |desiredLayout| if you don't want to
// perform a layout transition.
DAWN_NATIVE_EXPORT bool ExportVulkanImage(WGPUTexture cTexture,
VkImageLayout desiredLayout,
ExternalImageExportInfoVk* info);
}} // namespace dawn_native::vulkan }} // namespace dawn_native::vulkan

View File

@ -300,7 +300,7 @@ class D3D12SharedHandleUsageTests : public D3D12ResourceTestBase {
const wgpu::Color& clearColor, const wgpu::Color& clearColor,
ID3D11Texture2D** d3d11TextureOut, ID3D11Texture2D** d3d11TextureOut,
IDXGIKeyedMutex** dxgiKeyedMutexOut, IDXGIKeyedMutex** dxgiKeyedMutexOut,
bool isCleared = true) const { bool isInitialized = true) const {
ComPtr<ID3D11Texture2D> d3d11Texture; ComPtr<ID3D11Texture2D> d3d11Texture;
HRESULT hr = mD3d11Device->CreateTexture2D(d3dDescriptor, nullptr, &d3d11Texture); HRESULT hr = mD3d11Device->CreateTexture2D(d3dDescriptor, nullptr, &d3d11Texture);
ASSERT_EQ(hr, S_OK); ASSERT_EQ(hr, S_OK);
@ -339,7 +339,7 @@ class D3D12SharedHandleUsageTests : public D3D12ResourceTestBase {
reinterpret_cast<const WGPUTextureDescriptor*>(dawnDescriptor); reinterpret_cast<const WGPUTextureDescriptor*>(dawnDescriptor);
externDesc.sharedHandle = sharedHandle; externDesc.sharedHandle = sharedHandle;
externDesc.acquireMutexKey = 1; externDesc.acquireMutexKey = 1;
externDesc.isCleared = isCleared; externDesc.isInitialized = isInitialized;
WGPUTexture dawnTexture = dawn_native::d3d12::WrapSharedHandle(device.Get(), &externDesc); WGPUTexture dawnTexture = dawn_native::d3d12::WrapSharedHandle(device.Get(), &externDesc);
*dawnTextureOut = wgpu::Texture::Acquire(dawnTexture); *dawnTextureOut = wgpu::Texture::Acquire(dawnTexture);
@ -502,9 +502,9 @@ TEST_P(D3D12SharedHandleUsageTests, ClearTwiceInD3D12ReadbackInD3D11) {
} }
// 1. Create and clear a D3D11 texture with clearColor // 1. Create and clear a D3D11 texture with clearColor
// 2. Import the texture with isCleared = false // 2. Import the texture with isInitialized = false
// 3. Verify clearColor is not visible in wrapped texture // 3. Verify clearColor is not visible in wrapped texture
TEST_P(D3D12SharedHandleUsageTests, UnclearedTextureIsCleared) { TEST_P(D3D12SharedHandleUsageTests, UninitializedTextureIsCleared) {
DAWN_SKIP_TEST_IF(UsesWire()); DAWN_SKIP_TEST_IF(UsesWire());
const wgpu::Color clearColor{1.0f, 0.0f, 0.0f, 1.0f}; const wgpu::Color clearColor{1.0f, 0.0f, 0.0f, 1.0f};

View File

@ -97,13 +97,13 @@ namespace {
wgpu::Texture WrapIOSurface(const wgpu::TextureDescriptor* descriptor, wgpu::Texture WrapIOSurface(const wgpu::TextureDescriptor* descriptor,
IOSurfaceRef ioSurface, IOSurfaceRef ioSurface,
uint32_t plane, uint32_t plane,
bool isCleared = true) { bool isInitialized = true) {
dawn_native::metal::ExternalImageDescriptorIOSurface externDesc; dawn_native::metal::ExternalImageDescriptorIOSurface externDesc;
externDesc.cTextureDescriptor = externDesc.cTextureDescriptor =
reinterpret_cast<const WGPUTextureDescriptor*>(descriptor); reinterpret_cast<const WGPUTextureDescriptor*>(descriptor);
externDesc.ioSurface = ioSurface; externDesc.ioSurface = ioSurface;
externDesc.plane = plane; externDesc.plane = plane;
externDesc.isCleared = isCleared; externDesc.isInitialized = isInitialized;
WGPUTexture texture = dawn_native::metal::WrapIOSurface(device.Get(), &externDesc); WGPUTexture texture = dawn_native::metal::WrapIOSurface(device.Get(), &externDesc);
return wgpu::Texture::Acquire(texture); return wgpu::Texture::Acquire(texture);
} }
@ -446,8 +446,8 @@ TEST_P(IOSurfaceUsageTests, ClearRGBA8IOSurface) {
DoClearTest(ioSurface.get(), wgpu::TextureFormat::RGBA8Unorm, &data, sizeof(data)); DoClearTest(ioSurface.get(), wgpu::TextureFormat::RGBA8Unorm, &data, sizeof(data));
} }
// Test that texture with color is cleared when isCleared = false // Test that texture with color is cleared when isInitialized = false
TEST_P(IOSurfaceUsageTests, UnclearedTextureIsCleared) { TEST_P(IOSurfaceUsageTests, UninitializedTextureIsCleared) {
DAWN_SKIP_TEST_IF(UsesWire()); DAWN_SKIP_TEST_IF(UsesWire());
ScopedIOSurfaceRef ioSurface = CreateSinglePlaneIOSurface(1, 1, kCVPixelFormatType_32RGBA, 4); ScopedIOSurfaceRef ioSurface = CreateSinglePlaneIOSurface(1, 1, kCVPixelFormatType_32RGBA, 4);
@ -465,7 +465,7 @@ TEST_P(IOSurfaceUsageTests, UnclearedTextureIsCleared) {
textureDescriptor.mipLevelCount = 1; textureDescriptor.mipLevelCount = 1;
textureDescriptor.usage = wgpu::TextureUsage::OutputAttachment | wgpu::TextureUsage::CopySrc; textureDescriptor.usage = wgpu::TextureUsage::OutputAttachment | wgpu::TextureUsage::CopySrc;
// wrap ioSurface and ensure color is not visible when isCleared set to false // wrap ioSurface and ensure color is not visible when isInitialized set to false
wgpu::Texture ioSurfaceTexture = WrapIOSurface(&textureDescriptor, ioSurface.get(), 0, false); wgpu::Texture ioSurfaceTexture = WrapIOSurface(&textureDescriptor, ioSurface.get(), 0, false);
EXPECT_PIXEL_RGBA8_EQ(RGBA8(0, 0, 0, 0), ioSurfaceTexture, 0, 0); EXPECT_PIXEL_RGBA8_EQ(RGBA8(0, 0, 0, 0), ioSurfaceTexture, 0, 0);
} }

View File

@ -104,16 +104,34 @@ namespace dawn_native { namespace vulkan {
uint32_t stride, uint32_t stride,
uint64_t drmModifier, uint64_t drmModifier,
std::vector<int> waitFDs, std::vector<int> waitFDs,
bool isCleared = true, 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) { bool expectValid = true) {
dawn_native::vulkan::ExternalImageDescriptorDmaBuf descriptor; dawn_native::vulkan::ExternalImageDescriptorDmaBuf descriptor;
descriptor.cTextureDescriptor = descriptor.cTextureDescriptor =
reinterpret_cast<const WGPUTextureDescriptor*>(textureDescriptor); reinterpret_cast<const WGPUTextureDescriptor*>(textureDescriptor);
descriptor.isCleared = isCleared; descriptor.isInitialized = isInitialized;
descriptor.stride = stride; descriptor.stride = stride;
descriptor.drmModifier = drmModifier; descriptor.drmModifier = drmModifier;
descriptor.memoryFD = memoryFd; descriptor.memoryFD = memoryFd;
descriptor.waitFDs = waitFDs; descriptor.waitFDs = waitFDs;
descriptor.releasedOldLayout = releasedOldLayout;
descriptor.releasedNewLayout = releasedNewLayout;
WGPUTexture texture = WGPUTexture texture =
dawn_native::vulkan::WrapVulkanImage(dawnDevice.Get(), &descriptor); dawn_native::vulkan::WrapVulkanImage(dawnDevice.Get(), &descriptor);
@ -131,11 +149,13 @@ namespace dawn_native { namespace vulkan {
// Exports the signal from a wrapped texture and ignores it // 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 // We have to export the signal before destroying the wrapped texture else it's an
// assertion failure // assertion failure
void IgnoreSignalSemaphore(wgpu::Device dawnDevice, wgpu::Texture wrappedTexture) { void IgnoreSignalSemaphore(wgpu::Texture wrappedTexture) {
int fd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(dawnDevice.Get(), dawn_native::vulkan::ExternalImageExportInfoDmaBuf exportInfo;
wrappedTexture.Get()); dawn_native::vulkan::ExportVulkanImage(wrappedTexture.Get(), VK_IMAGE_LAYOUT_GENERAL, &exportInfo));
ASSERT_NE(fd, -1); for (int handle : info.semaphoreHandles) {
close(fd); ASSERT_NE(handle, -1);
close(handle);
}
} }
protected: protected:
@ -157,7 +177,7 @@ namespace dawn_native { namespace vulkan {
wgpu::Texture texture = WrapVulkanImage(device, &defaultDescriptor, defaultFd, wgpu::Texture texture = WrapVulkanImage(device, &defaultDescriptor, defaultFd,
defaultStride, defaultModifier, {}, true, true); defaultStride, defaultModifier, {}, true, true);
EXPECT_NE(texture.Get(), nullptr); EXPECT_NE(texture.Get(), nullptr);
IgnoreSignalSemaphore(device, texture); IgnoreSignalSemaphore(texture);
} }
// Test an error occurs if the texture descriptor is missing // Test an error occurs if the texture descriptor is missing
@ -230,10 +250,12 @@ namespace dawn_native { namespace vulkan {
wgpu::Texture texture = WrapVulkanImage(device, &defaultDescriptor, defaultFd, wgpu::Texture texture = WrapVulkanImage(device, &defaultDescriptor, defaultFd,
defaultStride, defaultModifier, {}, true, true); defaultStride, defaultModifier, {}, true, true);
ASSERT_NE(texture.Get(), nullptr); ASSERT_NE(texture.Get(), nullptr);
IgnoreSignalSemaphore(device, texture); IgnoreSignalSemaphore(texture);
ASSERT_DEVICE_ERROR(int fd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(
device.Get(), texture.Get())); dawn_native::vulkan::ExternalImageExportInfoDmaBuf exportInfo;
ASSERT_EQ(fd, -1); 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 an error occurs if we try to export the signal semaphore from a normal texture
@ -242,9 +264,11 @@ namespace dawn_native { namespace vulkan {
wgpu::Texture texture = device.CreateTexture(&defaultDescriptor); wgpu::Texture texture = device.CreateTexture(&defaultDescriptor);
ASSERT_NE(texture.Get(), nullptr); ASSERT_NE(texture.Get(), nullptr);
ASSERT_DEVICE_ERROR(int fd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(
device.Get(), texture.Get())); dawn_native::vulkan::ExternalImageExportInfoDmaBuf exportInfo;
ASSERT_EQ(fd, -1); 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 an error occurs if we try to export the signal semaphore from a destroyed texture
@ -254,9 +278,11 @@ namespace dawn_native { namespace vulkan {
wgpu::Texture texture = device.CreateTexture(&defaultDescriptor); wgpu::Texture texture = device.CreateTexture(&defaultDescriptor);
ASSERT_NE(texture.Get(), nullptr); ASSERT_NE(texture.Get(), nullptr);
texture.Destroy(); texture.Destroy();
ASSERT_DEVICE_ERROR(int fd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(
device.Get(), texture.Get())); dawn_native::vulkan::ExternalImageExportInfoDmaBuf exportInfo;
ASSERT_EQ(fd, -1); 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. // Fixture to test using external memory textures through different usages.
@ -330,84 +356,56 @@ namespace dawn_native { namespace vulkan {
// Verify clear color is visible in |device| // Verify clear color is visible in |device|
TEST_P(VulkanImageWrappingUsageTests, ClearImageAcrossDevices) { TEST_P(VulkanImageWrappingUsageTests, ClearImageAcrossDevices) {
// Import the image on |secondDevice| // Import the image on |secondDevice|
wgpu::Texture wrappedTexture = WrapVulkanImage(secondDevice, &defaultDescriptor, defaultFd, wgpu::Texture wrappedTexture = WrapVulkanImage(
defaultStride, defaultModifier, {}); secondDevice, &defaultDescriptor, defaultFd, defaultStride, defaultModifier, {},
VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
// Clear |wrappedTexture| on |secondDevice| // Clear |wrappedTexture| on |secondDevice|
ClearImage(secondDevice, wrappedTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f}); ClearImage(secondDevice, wrappedTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f});
int signalFd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(secondDevice.Get(), dawn_native::vulkan::ExternalImageExportInfoDmaBuf exportInfo;
wrappedTexture.Get()); dawn_native::vulkan::ExportVulkanImage(wrappedTexture.Get(),
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, &exportInfo);
// Import the image to |device|, making sure we wait on signalFd // Import the image to |device|, making sure we wait on signalFd
int nextFd = gbm_bo_get_fd(defaultGbmBo); int nextFd = gbm_bo_get_fd(defaultGbmBo);
wgpu::Texture nextWrappedTexture = WrapVulkanImage( wgpu::Texture nextWrappedTexture =
device, &defaultDescriptor, nextFd, defaultStride, defaultModifier, {signalFd}); WrapVulkanImage(device, &defaultDescriptor, nextFd, defaultStride, defaultModifier,
exportInfo.semaphoreHandles, exportInfo.releasedOldLayout,
exportInfo.releasedNewLayout);
// Verify |device| sees the changes from |secondDevice| // Verify |device| sees the changes from |secondDevice|
EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), nextWrappedTexture, 0, 0); EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), nextWrappedTexture, 0, 0);
IgnoreSignalSemaphore(device, nextWrappedTexture); IgnoreSignalSemaphore(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) {
// Import the image on |device|
wgpu::Texture wrappedTextureAlias = WrapVulkanImage(device, &defaultDescriptor, defaultFd,
defaultStride, defaultModifier, {});
// Import the image on |secondDevice|
int nextFd = gbm_bo_get_fd(defaultGbmBo);
wgpu::Texture wrappedTexture = WrapVulkanImage(secondDevice, &defaultDescriptor, nextFd,
defaultStride, defaultModifier, {});
// 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
nextFd = gbm_bo_get_fd(defaultGbmBo);
wgpu::Texture nextWrappedTexture = WrapVulkanImage(
device, &defaultDescriptor, nextFd, defaultStride, defaultModifier, {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| // Clear an image in |secondDevice|
// Verify clear color is not visible in |device| if we import the texture as not cleared // Verify clear color is not visible in |device| if we import the texture as not cleared
TEST_P(VulkanImageWrappingUsageTests, UnclearedTextureIsCleared) { TEST_P(VulkanImageWrappingUsageTests, UninitializedTextureIsCleared) {
// Import the image on |secondDevice| // Import the image on |secondDevice|
wgpu::Texture wrappedTexture = WrapVulkanImage(secondDevice, &defaultDescriptor, defaultFd, wgpu::Texture wrappedTexture = WrapVulkanImage(
defaultStride, defaultModifier, {}); secondDevice, &defaultDescriptor, defaultFd, defaultStride, defaultModifier, {},
VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
// Clear |wrappedTexture| on |secondDevice| // Clear |wrappedTexture| on |secondDevice|
ClearImage(secondDevice, wrappedTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f}); ClearImage(secondDevice, wrappedTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f});
int signalFd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(secondDevice.Get(), dawn_native::vulkan::ExternalImageExportInfoDmaBuf exportInfo;
wrappedTexture.Get()); dawn_native::vulkan::ExportVulkanImage(wrappedTexture.Get(),
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, &exportInfo);
// Import the image to |device|, making sure we wait on signalFd // Import the image to |device|, making sure we wait on signalFd
int nextFd = gbm_bo_get_fd(defaultGbmBo); int nextFd = gbm_bo_get_fd(defaultGbmBo);
wgpu::Texture nextWrappedTexture = WrapVulkanImage( wgpu::Texture nextWrappedTexture =
device, &defaultDescriptor, nextFd, defaultStride, defaultModifier, {signalFd}, false); WrapVulkanImage(device, &defaultDescriptor, nextFd, defaultStride, defaultModifier,
exportInfo.semaphoreHandles, exportInfo.releasedOldLayout,
exportInfo.releasedNewLayout, false);
// Verify |device| doesn't see the changes from |secondDevice| // Verify |device| doesn't see the changes from |secondDevice|
EXPECT_PIXEL_RGBA8_EQ(RGBA8(0, 0, 0, 0), nextWrappedTexture, 0, 0); EXPECT_PIXEL_RGBA8_EQ(RGBA8(0, 0, 0, 0), nextWrappedTexture, 0, 0);
IgnoreSignalSemaphore(device, nextWrappedTexture); IgnoreSignalSemaphore(nextWrappedTexture);
} }
// Import a texture into |secondDevice| // Import a texture into |secondDevice|
@ -416,19 +414,23 @@ namespace dawn_native { namespace vulkan {
// Verify the clear color from |secondDevice| is visible in |copyDstTexture| // Verify the clear color from |secondDevice| is visible in |copyDstTexture|
TEST_P(VulkanImageWrappingUsageTests, CopyTextureToTextureSrcSync) { TEST_P(VulkanImageWrappingUsageTests, CopyTextureToTextureSrcSync) {
// Import the image on |secondDevice| // Import the image on |secondDevice|
wgpu::Texture wrappedTexture = WrapVulkanImage(secondDevice, &defaultDescriptor, defaultFd, wgpu::Texture wrappedTexture = WrapVulkanImage(
defaultStride, defaultModifier, {}); secondDevice, &defaultDescriptor, defaultFd, defaultStride, defaultModifier, {},
VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
// Clear |wrappedTexture| on |secondDevice| // Clear |wrappedTexture| on |secondDevice|
ClearImage(secondDevice, wrappedTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f}); ClearImage(secondDevice, wrappedTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f});
int signalFd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(secondDevice.Get(), dawn_native::vulkan::ExternalImageExportInfoDmaBuf exportInfo;
wrappedTexture.Get()); dawn_native::vulkan::ExportVulkanImage(wrappedTexture.Get(),
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, &exportInfo);
// Import the image to |device|, making sure we wait on |signalFd| // Import the image to |device|, making sure we wait on |signalFd|
int nextFd = gbm_bo_get_fd(defaultGbmBo); int nextFd = gbm_bo_get_fd(defaultGbmBo);
wgpu::Texture deviceWrappedTexture = WrapVulkanImage( wgpu::Texture deviceWrappedTexture =
device, &defaultDescriptor, nextFd, defaultStride, defaultModifier, {signalFd}); WrapVulkanImage(device, &defaultDescriptor, nextFd, defaultStride, defaultModifier,
exportInfo.semaphoreHandles, exportInfo.releasedOldLayout,
exportInfo.releasedNewLayout);
// Create a second texture on |device| // Create a second texture on |device|
wgpu::Texture copyDstTexture = device.CreateTexture(&defaultDescriptor); wgpu::Texture copyDstTexture = device.CreateTexture(&defaultDescriptor);
@ -439,7 +441,7 @@ namespace dawn_native { namespace vulkan {
// Verify |copyDstTexture| sees changes from |secondDevice| // Verify |copyDstTexture| sees changes from |secondDevice|
EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), copyDstTexture, 0, 0); EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), copyDstTexture, 0, 0);
IgnoreSignalSemaphore(device, deviceWrappedTexture); IgnoreSignalSemaphore(deviceWrappedTexture);
} }
// Import a texture into |device| // Import a texture into |device|
@ -453,19 +455,23 @@ namespace dawn_native { namespace vulkan {
// into the texture first, then |device| writes color A // into the texture first, then |device| writes color A
TEST_P(VulkanImageWrappingUsageTests, CopyTextureToTextureDstSync) { TEST_P(VulkanImageWrappingUsageTests, CopyTextureToTextureDstSync) {
// Import the image on |device| // Import the image on |device|
wgpu::Texture wrappedTexture = WrapVulkanImage(device, &defaultDescriptor, defaultFd, wgpu::Texture wrappedTexture = WrapVulkanImage(
defaultStride, defaultModifier, {}); device, &defaultDescriptor, defaultFd, defaultStride, defaultModifier, {},
VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
// Clear |wrappedTexture| on |device| // Clear |wrappedTexture| on |device|
ClearImage(device, wrappedTexture, {5 / 255.0f, 6 / 255.0f, 7 / 255.0f, 8 / 255.0f}); ClearImage(device, wrappedTexture, {5 / 255.0f, 6 / 255.0f, 7 / 255.0f, 8 / 255.0f});
int signalFd = dawn_native::vulkan::ExternalImageExportInfoDmaBuf exportInfo;
dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(device.Get(), wrappedTexture.Get()); dawn_native::vulkan::ExportVulkanImage(wrappedTexture.Get(),
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &exportInfo);
// Import the image to |secondDevice|, making sure we wait on |signalFd| // Import the image to |secondDevice|, making sure we wait on |signalFd|
int nextFd = gbm_bo_get_fd(defaultGbmBo); int nextFd = gbm_bo_get_fd(defaultGbmBo);
wgpu::Texture secondDeviceWrappedTexture = WrapVulkanImage( wgpu::Texture secondDeviceWrappedTexture =
secondDevice, &defaultDescriptor, nextFd, defaultStride, defaultModifier, {signalFd}); WrapVulkanImage(secondDevice, &defaultDescriptor, nextFd, defaultStride,
defaultModifier, exportInfo.semaphoreHandles,
exportInfo.releasedOldLayout, exportInfo.releasedNewLayout);
// Create a texture with color B on |secondDevice| // Create a texture with color B on |secondDevice|
wgpu::Texture copySrcTexture = secondDevice.CreateTexture(&defaultDescriptor); wgpu::Texture copySrcTexture = secondDevice.CreateTexture(&defaultDescriptor);
@ -477,17 +483,21 @@ namespace dawn_native { namespace vulkan {
secondDeviceWrappedTexture); secondDeviceWrappedTexture);
// Re-import back into |device|, waiting on |secondDevice|'s signal // Re-import back into |device|, waiting on |secondDevice|'s signal
signalFd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD( dawn_native::vulkan::ExternalImageExportInfoDmaBuf secondExportInfo;
secondDevice.Get(), secondDeviceWrappedTexture.Get()); dawn_native::vulkan::ExportVulkanImage(secondDeviceWrappedTexture.Get(),
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
&secondExportInfo);
nextFd = gbm_bo_get_fd(defaultGbmBo); nextFd = gbm_bo_get_fd(defaultGbmBo);
wgpu::Texture nextWrappedTexture = WrapVulkanImage( wgpu::Texture nextWrappedTexture =
device, &defaultDescriptor, nextFd, defaultStride, defaultModifier, {signalFd}); WrapVulkanImage(device, &defaultDescriptor, nextFd, defaultStride, defaultModifier,
secondExportInfo.semaphoreHandles, secondExportInfo.releasedOldLayout,
secondExportInfo.releasedNewLayout);
// Verify |nextWrappedTexture| contains the color from our copy // Verify |nextWrappedTexture| contains the color from our copy
EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), nextWrappedTexture, 0, 0); EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), nextWrappedTexture, 0, 0);
IgnoreSignalSemaphore(device, nextWrappedTexture); IgnoreSignalSemaphore(nextWrappedTexture);
} }
// Import a texture from |secondDevice| // Import a texture from |secondDevice|
@ -496,19 +506,23 @@ namespace dawn_native { namespace vulkan {
// Verify the clear color from |secondDevice| is visible in |copyDstBuffer| // Verify the clear color from |secondDevice| is visible in |copyDstBuffer|
TEST_P(VulkanImageWrappingUsageTests, CopyTextureToBufferSrcSync) { TEST_P(VulkanImageWrappingUsageTests, CopyTextureToBufferSrcSync) {
// Import the image on |secondDevice| // Import the image on |secondDevice|
wgpu::Texture wrappedTexture = WrapVulkanImage(secondDevice, &defaultDescriptor, defaultFd, wgpu::Texture wrappedTexture = WrapVulkanImage(
defaultStride, defaultModifier, {}); secondDevice, &defaultDescriptor, defaultFd, defaultStride, defaultModifier, {},
VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
// Clear |wrappedTexture| on |secondDevice| // Clear |wrappedTexture| on |secondDevice|
ClearImage(secondDevice, wrappedTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f}); ClearImage(secondDevice, wrappedTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f});
int signalFd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(secondDevice.Get(), dawn_native::vulkan::ExternalImageExportInfoDmaBuf exportInfo;
wrappedTexture.Get()); dawn_native::vulkan::ExportVulkanImage(wrappedTexture.Get(),
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, &exportInfo);
// Import the image to |device|, making sure we wait on |signalFd| // Import the image to |device|, making sure we wait on |signalFd|
int nextFd = gbm_bo_get_fd(defaultGbmBo); int nextFd = gbm_bo_get_fd(defaultGbmBo);
wgpu::Texture deviceWrappedTexture = WrapVulkanImage( wgpu::Texture deviceWrappedTexture =
device, &defaultDescriptor, nextFd, defaultStride, defaultModifier, {signalFd}); WrapVulkanImage(device, &defaultDescriptor, nextFd, defaultStride, defaultModifier,
exportInfo.semaphoreHandles, exportInfo.releasedOldLayout,
exportInfo.releasedNewLayout);
// Create a destination buffer on |device| // Create a destination buffer on |device|
wgpu::BufferDescriptor bufferDesc; wgpu::BufferDescriptor bufferDesc;
@ -532,7 +546,7 @@ namespace dawn_native { namespace vulkan {
uint32_t expected = 1; uint32_t expected = 1;
EXPECT_BUFFER_U32_EQ(expected, copyDstBuffer, 0); EXPECT_BUFFER_U32_EQ(expected, copyDstBuffer, 0);
IgnoreSignalSemaphore(device, deviceWrappedTexture); IgnoreSignalSemaphore(deviceWrappedTexture);
} }
// Import a texture into |device| // Import a texture into |device|
@ -545,19 +559,23 @@ namespace dawn_native { namespace vulkan {
// into the texture first, then |device| writes color A // into the texture first, then |device| writes color A
TEST_P(VulkanImageWrappingUsageTests, CopyBufferToTextureDstSync) { TEST_P(VulkanImageWrappingUsageTests, CopyBufferToTextureDstSync) {
// Import the image on |device| // Import the image on |device|
wgpu::Texture wrappedTexture = WrapVulkanImage(device, &defaultDescriptor, defaultFd, wgpu::Texture wrappedTexture = WrapVulkanImage(
defaultStride, defaultModifier, {}); device, &defaultDescriptor, defaultFd, defaultStride, defaultModifier, {},
VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
// Clear |wrappedTexture| on |device| // Clear |wrappedTexture| on |device|
ClearImage(device, wrappedTexture, {5 / 255.0f, 6 / 255.0f, 7 / 255.0f, 8 / 255.0f}); ClearImage(device, wrappedTexture, {5 / 255.0f, 6 / 255.0f, 7 / 255.0f, 8 / 255.0f});
int signalFd = dawn_native::vulkan::ExternalImageExportInfoDmaBuf exportInfo;
dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(device.Get(), wrappedTexture.Get()); dawn_native::vulkan::ExportVulkanImage(wrappedTexture.Get(),
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, &exportInfo);
// Import the image to |secondDevice|, making sure we wait on |signalFd| // Import the image to |secondDevice|, making sure we wait on |signalFd|
int nextFd = gbm_bo_get_fd(defaultGbmBo); int nextFd = gbm_bo_get_fd(defaultGbmBo);
wgpu::Texture secondDeviceWrappedTexture = WrapVulkanImage( wgpu::Texture secondDeviceWrappedTexture =
secondDevice, &defaultDescriptor, nextFd, defaultStride, defaultModifier, {signalFd}); WrapVulkanImage(secondDevice, &defaultDescriptor, nextFd, defaultStride,
defaultModifier, exportInfo.semaphoreHandles,
exportInfo.releasedOldLayout, exportInfo.releasedNewLayout);
// Copy color B on |secondDevice| // Copy color B on |secondDevice|
wgpu::Queue secondDeviceQueue = secondDevice.GetDefaultQueue(); wgpu::Queue secondDeviceQueue = secondDevice.GetDefaultQueue();
@ -579,17 +597,21 @@ namespace dawn_native { namespace vulkan {
secondDeviceQueue.Submit(1, &commands); secondDeviceQueue.Submit(1, &commands);
// Re-import back into |device|, waiting on |secondDevice|'s signal // Re-import back into |device|, waiting on |secondDevice|'s signal
signalFd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD( dawn_native::vulkan::ExternalImageExportInfoDmaBuf secondExportInfo;
secondDevice.Get(), secondDeviceWrappedTexture.Get()); dawn_native::vulkan::ExportVulkanImage(secondDeviceWrappedTexture.Get(),
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
&secondExportInfo);
nextFd = gbm_bo_get_fd(defaultGbmBo); nextFd = gbm_bo_get_fd(defaultGbmBo);
wgpu::Texture nextWrappedTexture = WrapVulkanImage( wgpu::Texture nextWrappedTexture =
device, &defaultDescriptor, nextFd, defaultStride, defaultModifier, {signalFd}); WrapVulkanImage(device, &defaultDescriptor, nextFd, defaultStride, defaultModifier,
secondExportInfo.semaphoreHandles, secondExportInfo.releasedOldLayout,
secondExportInfo.releasedNewLayout);
// Verify |nextWrappedTexture| contains the color from our copy // Verify |nextWrappedTexture| contains the color from our copy
EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), nextWrappedTexture, 0, 0); EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), nextWrappedTexture, 0, 0);
IgnoreSignalSemaphore(device, nextWrappedTexture); IgnoreSignalSemaphore(nextWrappedTexture);
} }
// Import a texture from |secondDevice| // Import a texture from |secondDevice|
@ -599,19 +621,23 @@ namespace dawn_native { namespace vulkan {
// Verify the clear color from |secondDevice| is visible in both copies // Verify the clear color from |secondDevice| is visible in both copies
TEST_P(VulkanImageWrappingUsageTests, DoubleTextureUsage) { TEST_P(VulkanImageWrappingUsageTests, DoubleTextureUsage) {
// Import the image on |secondDevice| // Import the image on |secondDevice|
wgpu::Texture wrappedTexture = WrapVulkanImage(secondDevice, &defaultDescriptor, defaultFd, wgpu::Texture wrappedTexture = WrapVulkanImage(
defaultStride, defaultModifier, {}); secondDevice, &defaultDescriptor, defaultFd, defaultStride, defaultModifier, {},
VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
// Clear |wrappedTexture| on |secondDevice| // Clear |wrappedTexture| on |secondDevice|
ClearImage(secondDevice, wrappedTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f}); ClearImage(secondDevice, wrappedTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f});
int signalFd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(secondDevice.Get(), dawn_native::vulkan::ExternalImageExportInfoDmaBuf exportInfo;
wrappedTexture.Get()); dawn_native::vulkan::ExportVulkanImage(wrappedTexture.Get(),
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, &exportInfo);
// Import the image to |device|, making sure we wait on |signalFd| // Import the image to |device|, making sure we wait on |signalFd|
int nextFd = gbm_bo_get_fd(defaultGbmBo); int nextFd = gbm_bo_get_fd(defaultGbmBo);
wgpu::Texture deviceWrappedTexture = WrapVulkanImage( wgpu::Texture deviceWrappedTexture =
device, &defaultDescriptor, nextFd, defaultStride, defaultModifier, {signalFd}); WrapVulkanImage(device, &defaultDescriptor, nextFd, defaultStride, defaultModifier,
exportInfo.semaphoreHandles, exportInfo.releasedOldLayout,
exportInfo.releasedNewLayout);
// Create a second texture on |device| // Create a second texture on |device|
wgpu::Texture copyDstTexture = device.CreateTexture(&defaultDescriptor); wgpu::Texture copyDstTexture = device.CreateTexture(&defaultDescriptor);
@ -631,7 +657,7 @@ namespace dawn_native { namespace vulkan {
// Verify |secondCopyDstTexture| sees changes from |secondDevice| // Verify |secondCopyDstTexture| sees changes from |secondDevice|
EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), secondCopyDstTexture, 0, 0); EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), secondCopyDstTexture, 0, 0);
IgnoreSignalSemaphore(device, deviceWrappedTexture); IgnoreSignalSemaphore(deviceWrappedTexture);
} }
// Tex A on device 3 (external export) // Tex A on device 3 (external export)
@ -676,10 +702,12 @@ namespace dawn_native { namespace vulkan {
// Import TexA, TexB on device 3 // Import TexA, TexB on device 3
wgpu::Texture wrappedTexADevice3 = wgpu::Texture wrappedTexADevice3 =
WrapVulkanImage(thirdDevice, &defaultDescriptor, fdA, strideA, modifierA, {}); WrapVulkanImage(thirdDevice, &defaultDescriptor, fdA, strideA, modifierA, {},
VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
wgpu::Texture wrappedTexBDevice3 = wgpu::Texture wrappedTexBDevice3 =
WrapVulkanImage(thirdDevice, &defaultDescriptor, fdB, strideB, modifierB, {}); WrapVulkanImage(thirdDevice, &defaultDescriptor, fdB, strideB, modifierB, {},
VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
// Clear TexA // Clear TexA
ClearImage(thirdDevice, wrappedTexADevice3, ClearImage(thirdDevice, wrappedTexADevice3,
@ -689,30 +717,37 @@ namespace dawn_native { namespace vulkan {
SimpleCopyTextureToTexture(thirdDevice, thirdDeviceQueue, wrappedTexADevice3, SimpleCopyTextureToTexture(thirdDevice, thirdDeviceQueue, wrappedTexADevice3,
wrappedTexBDevice3); wrappedTexBDevice3);
int signalFdTexBDevice3 = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD( dawn_native::vulkan::ExternalImageExportInfoDmaBuf exportInfoTexBDevice3;
thirdDevice.Get(), wrappedTexBDevice3.Get()); dawn_native::vulkan::ExportVulkanImage(
IgnoreSignalSemaphore(thirdDevice, wrappedTexADevice3); wrappedTexBDevice3.Get(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, &exportInfoTexBDevice3);
IgnoreSignalSemaphore(wrappedTexADevice3);
// Import TexB, TexC on device 2 // Import TexB, TexC on device 2
fdB = gbm_bo_get_fd(gbmBoB); fdB = gbm_bo_get_fd(gbmBoB);
wgpu::Texture wrappedTexBDevice2 = WrapVulkanImage( wgpu::Texture wrappedTexBDevice2 = WrapVulkanImage(
secondDevice, &defaultDescriptor, fdB, strideB, modifierB, {signalFdTexBDevice3}); secondDevice, &defaultDescriptor, fdB, strideB, modifierB,
exportInfoTexBDevice3.semaphoreHandles, exportInfoTexBDevice3.releasedOldLayout,
exportInfoTexBDevice3.releasedNewLayout);
wgpu::Texture wrappedTexCDevice2 = wgpu::Texture wrappedTexCDevice2 =
WrapVulkanImage(secondDevice, &defaultDescriptor, fdC, strideC, modifierC, {}); WrapVulkanImage(secondDevice, &defaultDescriptor, fdC, strideC, modifierC, {},
VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
// Copy B->C on device 2 // Copy B->C on device 2
SimpleCopyTextureToTexture(secondDevice, secondDeviceQueue, wrappedTexBDevice2, SimpleCopyTextureToTexture(secondDevice, secondDeviceQueue, wrappedTexBDevice2,
wrappedTexCDevice2); wrappedTexCDevice2);
int signalFdTexCDevice2 = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD( dawn_native::vulkan::ExternalImageExportInfoDmaBuf exportInfoTexCDevice2;
secondDevice.Get(), wrappedTexCDevice2.Get()); dawn_native::vulkan::ExportVulkanImage(
IgnoreSignalSemaphore(secondDevice, wrappedTexBDevice2); wrappedTexCDevice2.Get(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, &exportInfoTexCDevice2);
IgnoreSignalSemaphore(wrappedTexBDevice2);
// Import TexC on device 1 // Import TexC on device 1
fdC = gbm_bo_get_fd(gbmBoC); fdC = gbm_bo_get_fd(gbmBoC);
wgpu::Texture wrappedTexCDevice1 = WrapVulkanImage(device, &defaultDescriptor, fdC, strideC, wgpu::Texture wrappedTexCDevice1 = WrapVulkanImage(
modifierC, {signalFdTexCDevice2}); device, &defaultDescriptor, fdC, strideC, modifierC,
exportInfoTexCDevice2.semaphoreHandles, exportInfoTexCDevice2.releasedOldLayout,
exportInfoTexCDevice2.releasedNewLayout);
// Create TexD on device 1 // Create TexD on device 1
wgpu::Texture texD = device.CreateTexture(&defaultDescriptor); wgpu::Texture texD = device.CreateTexture(&defaultDescriptor);
@ -723,7 +758,7 @@ namespace dawn_native { namespace vulkan {
// Verify D matches clear color // Verify D matches clear color
EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), texD, 0, 0); EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), texD, 0, 0);
IgnoreSignalSemaphore(device, wrappedTexCDevice1); IgnoreSignalSemaphore(wrappedTexCDevice1);
} }
// Tests a larger image is preserved when importing // Tests a larger image is preserved when importing
@ -756,7 +791,8 @@ namespace dawn_native { namespace vulkan {
// Import the image on |secondDevice| // Import the image on |secondDevice|
wgpu::Texture wrappedTexture = wgpu::Texture wrappedTexture =
WrapVulkanImage(secondDevice, &descriptor, fd, stride, modifier, {}); WrapVulkanImage(secondDevice, &descriptor, fd, stride, modifier, {},
VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
// Draw a non-trivial picture // Draw a non-trivial picture
uint32_t width = 640, height = 480, pixelSize = 4; uint32_t width = 640, height = 480, pixelSize = 4;
@ -791,14 +827,15 @@ namespace dawn_native { namespace vulkan {
wgpu::CommandBuffer commands = encoder.Finish(); wgpu::CommandBuffer commands = encoder.Finish();
secondDeviceQueue.Submit(1, &commands); secondDeviceQueue.Submit(1, &commands);
} }
dawn_native::vulkan::ExternalImageExportInfoDmaBuf exportInfo;
int signalFd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(secondDevice.Get(), dawn_native::vulkan::ExportVulkanImage(wrappedTexture.Get(),
wrappedTexture.Get()); VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, &exportInfo);
int nextFd = gbm_bo_get_fd(gbmBo); int nextFd = gbm_bo_get_fd(gbmBo);
// Import the image on |device| // Import the image on |device|
wgpu::Texture nextWrappedTexture = wgpu::Texture nextWrappedTexture = WrapVulkanImage(
WrapVulkanImage(device, &descriptor, nextFd, stride, modifier, {signalFd}); device, &descriptor, nextFd, stride, modifier, exportInfo.semaphoreHandles,
exportInfo.releasedOldLayout, exportInfo.releasedNewLayout);
// Copy the image into a buffer for comparison // Copy the image into a buffer for comparison
wgpu::BufferDescriptor copyDesc; wgpu::BufferDescriptor copyDesc;
@ -823,7 +860,7 @@ namespace dawn_native { namespace vulkan {
EXPECT_BUFFER_U32_RANGE_EQ(reinterpret_cast<uint32_t*>(data.data()), copyDstBuffer, 0, EXPECT_BUFFER_U32_RANGE_EQ(reinterpret_cast<uint32_t*>(data.data()), copyDstBuffer, 0,
data.size() / 4); data.size() / 4);
IgnoreSignalSemaphore(device, nextWrappedTexture); IgnoreSignalSemaphore(nextWrappedTexture);
} }
DAWN_INSTANTIATE_TEST(VulkanImageWrappingValidationTests, VulkanBackend()); DAWN_INSTANTIATE_TEST(VulkanImageWrappingValidationTests, VulkanBackend());

View File

@ -157,16 +157,34 @@ namespace dawn_native { namespace vulkan {
VkDeviceSize allocationSize, VkDeviceSize allocationSize,
uint32_t memoryTypeIndex, uint32_t memoryTypeIndex,
std::vector<int> waitFDs, std::vector<int> waitFDs,
bool isCleared = true, bool isInitialized = true,
bool expectValid = true) {
dawn_native::vulkan::ExternalImageDescriptorOpaqueFD descriptor;
return WrapVulkanImage(dawnDevice, textureDescriptor, memoryFd, allocationSize,
memoryTypeIndex, waitFDs, descriptor.releasedOldLayout,
descriptor.releasedNewLayout, isInitialized, expectValid);
}
wgpu::Texture WrapVulkanImage(wgpu::Device dawnDevice,
const wgpu::TextureDescriptor* textureDescriptor,
int memoryFd,
VkDeviceSize allocationSize,
uint32_t memoryTypeIndex,
std::vector<int> waitFDs,
VkImageLayout releasedOldLayout,
VkImageLayout releasedNewLayout,
bool isInitialized = true,
bool expectValid = true) { bool expectValid = true) {
dawn_native::vulkan::ExternalImageDescriptorOpaqueFD descriptor; dawn_native::vulkan::ExternalImageDescriptorOpaqueFD descriptor;
descriptor.cTextureDescriptor = descriptor.cTextureDescriptor =
reinterpret_cast<const WGPUTextureDescriptor*>(textureDescriptor); reinterpret_cast<const WGPUTextureDescriptor*>(textureDescriptor);
descriptor.isCleared = isCleared; descriptor.isInitialized = isInitialized;
descriptor.allocationSize = allocationSize; descriptor.allocationSize = allocationSize;
descriptor.memoryTypeIndex = memoryTypeIndex; descriptor.memoryTypeIndex = memoryTypeIndex;
descriptor.memoryFD = memoryFd; descriptor.memoryFD = memoryFd;
descriptor.waitFDs = waitFDs; descriptor.waitFDs = waitFDs;
descriptor.releasedOldLayout = releasedOldLayout;
descriptor.releasedNewLayout = releasedNewLayout;
WGPUTexture texture = WGPUTexture texture =
dawn_native::vulkan::WrapVulkanImage(dawnDevice.Get(), &descriptor); dawn_native::vulkan::WrapVulkanImage(dawnDevice.Get(), &descriptor);
@ -184,11 +202,14 @@ namespace dawn_native { namespace vulkan {
// Exports the signal from a wrapped texture and ignores it // 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 // We have to export the signal before destroying the wrapped texture else it's an
// assertion failure // assertion failure
void IgnoreSignalSemaphore(wgpu::Device dawnDevice, wgpu::Texture wrappedTexture) { void IgnoreSignalSemaphore(wgpu::Texture wrappedTexture) {
int fd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(dawnDevice.Get(), dawn_native::vulkan::ExternalImageExportInfoOpaqueFD info;
wrappedTexture.Get()); dawn_native::vulkan::ExportVulkanImage(wrappedTexture.Get(),
ASSERT_NE(fd, -1); VK_IMAGE_LAYOUT_GENERAL, &info);
close(fd); for (int handle : info.semaphoreHandles) {
ASSERT_NE(handle, -1);
close(handle);
}
} }
protected: protected:
@ -244,7 +265,7 @@ namespace dawn_native { namespace vulkan {
WrapVulkanImage(device, &defaultDescriptor, defaultFd, defaultAllocationSize, WrapVulkanImage(device, &defaultDescriptor, defaultFd, defaultAllocationSize,
defaultMemoryTypeIndex, {}, true, true); defaultMemoryTypeIndex, {}, true, true);
EXPECT_NE(texture.Get(), nullptr); EXPECT_NE(texture.Get(), nullptr);
IgnoreSignalSemaphore(device, texture); IgnoreSignalSemaphore(texture);
} }
// Test an error occurs if the texture descriptor is missing // Test an error occurs if the texture descriptor is missing
@ -319,10 +340,12 @@ namespace dawn_native { namespace vulkan {
WrapVulkanImage(device, &defaultDescriptor, defaultFd, defaultAllocationSize, WrapVulkanImage(device, &defaultDescriptor, defaultFd, defaultAllocationSize,
defaultMemoryTypeIndex, {}, true, true); defaultMemoryTypeIndex, {}, true, true);
ASSERT_NE(texture.Get(), nullptr); ASSERT_NE(texture.Get(), nullptr);
IgnoreSignalSemaphore(device, texture); IgnoreSignalSemaphore(texture);
ASSERT_DEVICE_ERROR(int fd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(
device.Get(), texture.Get())); dawn_native::vulkan::ExternalImageExportInfoOpaqueFD exportInfo;
ASSERT_EQ(fd, -1); 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 an error occurs if we try to export the signal semaphore from a normal texture
@ -330,9 +353,11 @@ namespace dawn_native { namespace vulkan {
DAWN_SKIP_TEST_IF(UsesWire()); DAWN_SKIP_TEST_IF(UsesWire());
wgpu::Texture texture = device.CreateTexture(&defaultDescriptor); wgpu::Texture texture = device.CreateTexture(&defaultDescriptor);
ASSERT_NE(texture.Get(), nullptr); ASSERT_NE(texture.Get(), nullptr);
ASSERT_DEVICE_ERROR(int fd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(
device.Get(), texture.Get())); dawn_native::vulkan::ExternalImageExportInfoOpaqueFD exportInfo;
ASSERT_EQ(fd, -1); 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 an error occurs if we try to export the signal semaphore from a destroyed texture
@ -341,9 +366,11 @@ namespace dawn_native { namespace vulkan {
wgpu::Texture texture = device.CreateTexture(&defaultDescriptor); wgpu::Texture texture = device.CreateTexture(&defaultDescriptor);
ASSERT_NE(texture.Get(), nullptr); ASSERT_NE(texture.Get(), nullptr);
texture.Destroy(); texture.Destroy();
ASSERT_DEVICE_ERROR(int fd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(
device.Get(), texture.Get())); dawn_native::vulkan::ExternalImageExportInfoOpaqueFD exportInfo;
ASSERT_EQ(fd, -1); 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. // Fixture to test using external memory textures through different usages.
@ -456,97 +483,58 @@ namespace dawn_native { namespace vulkan {
// Import the image on |secondDevice| // Import the image on |secondDevice|
wgpu::Texture wrappedTexture = wgpu::Texture wrappedTexture =
WrapVulkanImage(secondDevice, &defaultDescriptor, defaultFd, defaultAllocationSize, WrapVulkanImage(secondDevice, &defaultDescriptor, defaultFd, defaultAllocationSize,
defaultMemoryTypeIndex, {}); defaultMemoryTypeIndex, {}, VK_IMAGE_LAYOUT_UNDEFINED,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
// Clear |wrappedTexture| on |secondDevice| // Clear |wrappedTexture| on |secondDevice|
ClearImage(secondDevice, wrappedTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f}); ClearImage(secondDevice, wrappedTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f});
int signalFd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(secondDevice.Get(), dawn_native::vulkan::ExternalImageExportInfoOpaqueFD exportInfo;
wrappedTexture.Get()); dawn_native::vulkan::ExportVulkanImage(wrappedTexture.Get(),
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, &exportInfo);
// Import the image to |device|, making sure we wait on signalFd // Import the image to |device|, making sure we wait on signalFd
int memoryFd = GetMemoryFd(deviceVk, defaultAllocation); int memoryFd = GetMemoryFd(deviceVk, defaultAllocation);
wgpu::Texture nextWrappedTexture = wgpu::Texture nextWrappedTexture =
WrapVulkanImage(device, &defaultDescriptor, memoryFd, defaultAllocationSize, WrapVulkanImage(device, &defaultDescriptor, memoryFd, defaultAllocationSize,
defaultMemoryTypeIndex, {signalFd}); defaultMemoryTypeIndex, exportInfo.semaphoreHandles,
exportInfo.releasedOldLayout, exportInfo.releasedNewLayout);
// Verify |device| sees the changes from |secondDevice| // Verify |device| sees the changes from |secondDevice|
EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), nextWrappedTexture, 0, 0); EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), nextWrappedTexture, 0, 0);
IgnoreSignalSemaphore(device, nextWrappedTexture); IgnoreSignalSemaphore(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());
// WrapVulkanImage consumes the file descriptor so we can't import defaultFd twice.
// Duplicate the file descriptor so we can import it twice.
int defaultFdCopy = dup(defaultFd);
ASSERT(defaultFdCopy != -1);
// Import the image on |device
wgpu::Texture wrappedTextureAlias =
WrapVulkanImage(device, &defaultDescriptor, defaultFdCopy, defaultAllocationSize,
defaultMemoryTypeIndex, {});
// Import the image on |secondDevice|
wgpu::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);
wgpu::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| // Clear an image in |secondDevice|
// Verify clear color is not visible in |device| if we import the texture as not cleared // Verify clear color is not visible in |device| if we import the texture as not cleared
TEST_P(VulkanImageWrappingUsageTests, UnclearedTextureIsCleared) { TEST_P(VulkanImageWrappingUsageTests, UninitializedTextureIsCleared) {
DAWN_SKIP_TEST_IF(UsesWire()); DAWN_SKIP_TEST_IF(UsesWire());
// Import the image on |secondDevice| // Import the image on |secondDevice|
wgpu::Texture wrappedTexture = wgpu::Texture wrappedTexture =
WrapVulkanImage(secondDevice, &defaultDescriptor, defaultFd, defaultAllocationSize, WrapVulkanImage(secondDevice, &defaultDescriptor, defaultFd, defaultAllocationSize,
defaultMemoryTypeIndex, {}); defaultMemoryTypeIndex, {}, VK_IMAGE_LAYOUT_UNDEFINED,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
// Clear |wrappedTexture| on |secondDevice| // Clear |wrappedTexture| on |secondDevice|
ClearImage(secondDevice, wrappedTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f}); ClearImage(secondDevice, wrappedTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f});
int signalFd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(secondDevice.Get(), dawn_native::vulkan::ExternalImageExportInfoOpaqueFD exportInfo;
wrappedTexture.Get()); dawn_native::vulkan::ExportVulkanImage(wrappedTexture.Get(),
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, &exportInfo);
// Import the image to |device|, making sure we wait on signalFd // Import the image to |device|, making sure we wait on the semaphore
int memoryFd = GetMemoryFd(deviceVk, defaultAllocation); int memoryFd = GetMemoryFd(deviceVk, defaultAllocation);
wgpu::Texture nextWrappedTexture = wgpu::Texture nextWrappedTexture =
WrapVulkanImage(device, &defaultDescriptor, memoryFd, defaultAllocationSize, WrapVulkanImage(device, &defaultDescriptor, memoryFd, defaultAllocationSize,
defaultMemoryTypeIndex, {signalFd}, false); defaultMemoryTypeIndex, exportInfo.semaphoreHandles,
exportInfo.releasedOldLayout, exportInfo.releasedNewLayout, false);
// Verify |device| doesn't see the changes from |secondDevice| // Verify |device| doesn't see the changes from |secondDevice|
EXPECT_PIXEL_RGBA8_EQ(RGBA8(0, 0, 0, 0), nextWrappedTexture, 0, 0); EXPECT_PIXEL_RGBA8_EQ(RGBA8(0, 0, 0, 0), nextWrappedTexture, 0, 0);
IgnoreSignalSemaphore(device, nextWrappedTexture); IgnoreSignalSemaphore(nextWrappedTexture);
} }
// Import a texture into |secondDevice| // Import a texture into |secondDevice|
@ -558,19 +546,22 @@ namespace dawn_native { namespace vulkan {
// Import the image on |secondDevice| // Import the image on |secondDevice|
wgpu::Texture wrappedTexture = wgpu::Texture wrappedTexture =
WrapVulkanImage(secondDevice, &defaultDescriptor, defaultFd, defaultAllocationSize, WrapVulkanImage(secondDevice, &defaultDescriptor, defaultFd, defaultAllocationSize,
defaultMemoryTypeIndex, {}); defaultMemoryTypeIndex, {}, VK_IMAGE_LAYOUT_UNDEFINED,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
// Clear |wrappedTexture| on |secondDevice| // Clear |wrappedTexture| on |secondDevice|
ClearImage(secondDevice, wrappedTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f}); ClearImage(secondDevice, wrappedTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f});
int signalFd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(secondDevice.Get(), dawn_native::vulkan::ExternalImageExportInfoOpaqueFD exportInfo;
wrappedTexture.Get()); dawn_native::vulkan::ExportVulkanImage(wrappedTexture.Get(),
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, &exportInfo);
// Import the image to |device|, making sure we wait on |signalFd| // Import the image to |device|, making sure we wait on the semaphore
int memoryFd = GetMemoryFd(deviceVk, defaultAllocation); int memoryFd = GetMemoryFd(deviceVk, defaultAllocation);
wgpu::Texture deviceWrappedTexture = wgpu::Texture deviceWrappedTexture =
WrapVulkanImage(device, &defaultDescriptor, memoryFd, defaultAllocationSize, WrapVulkanImage(device, &defaultDescriptor, memoryFd, defaultAllocationSize,
defaultMemoryTypeIndex, {signalFd}); defaultMemoryTypeIndex, exportInfo.semaphoreHandles,
exportInfo.releasedOldLayout, exportInfo.releasedNewLayout);
// Create a second texture on |device| // Create a second texture on |device|
wgpu::Texture copyDstTexture = device.CreateTexture(&defaultDescriptor); wgpu::Texture copyDstTexture = device.CreateTexture(&defaultDescriptor);
@ -581,7 +572,7 @@ namespace dawn_native { namespace vulkan {
// Verify |copyDstTexture| sees changes from |secondDevice| // Verify |copyDstTexture| sees changes from |secondDevice|
EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), copyDstTexture, 0, 0); EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), copyDstTexture, 0, 0);
IgnoreSignalSemaphore(device, deviceWrappedTexture); IgnoreSignalSemaphore(deviceWrappedTexture);
} }
// Import a texture into |device| // Import a texture into |device|
@ -596,21 +587,23 @@ namespace dawn_native { namespace vulkan {
DAWN_SKIP_TEST_IF(UsesWire()); DAWN_SKIP_TEST_IF(UsesWire());
// Import the image on |device| // Import the image on |device|
wgpu::Texture wrappedTexture = wgpu::Texture wrappedTexture = WrapVulkanImage(
WrapVulkanImage(device, &defaultDescriptor, defaultFd, defaultAllocationSize, device, &defaultDescriptor, defaultFd, defaultAllocationSize, defaultMemoryTypeIndex,
defaultMemoryTypeIndex, {}); {}, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
// Clear |wrappedTexture| on |device| // Clear |wrappedTexture| on |device|
ClearImage(device, wrappedTexture, {5 / 255.0f, 6 / 255.0f, 7 / 255.0f, 8 / 255.0f}); ClearImage(device, wrappedTexture, {5 / 255.0f, 6 / 255.0f, 7 / 255.0f, 8 / 255.0f});
int signalFd = dawn_native::vulkan::ExternalImageExportInfoOpaqueFD exportInfo;
dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(device.Get(), wrappedTexture.Get()); dawn_native::vulkan::ExportVulkanImage(wrappedTexture.Get(),
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &exportInfo);
// Import the image to |secondDevice|, making sure we wait on |signalFd| // Import the image to |secondDevice|, making sure we wait on the semaphore
int memoryFd = GetMemoryFd(deviceVk, defaultAllocation); int memoryFd = GetMemoryFd(deviceVk, defaultAllocation);
wgpu::Texture secondDeviceWrappedTexture = wgpu::Texture secondDeviceWrappedTexture =
WrapVulkanImage(secondDevice, &defaultDescriptor, memoryFd, defaultAllocationSize, WrapVulkanImage(secondDevice, &defaultDescriptor, memoryFd, defaultAllocationSize,
defaultMemoryTypeIndex, {signalFd}); defaultMemoryTypeIndex, exportInfo.semaphoreHandles,
exportInfo.releasedOldLayout, exportInfo.releasedNewLayout);
// Create a texture with color B on |secondDevice| // Create a texture with color B on |secondDevice|
wgpu::Texture copySrcTexture = secondDevice.CreateTexture(&defaultDescriptor); wgpu::Texture copySrcTexture = secondDevice.CreateTexture(&defaultDescriptor);
@ -622,18 +615,21 @@ namespace dawn_native { namespace vulkan {
secondDeviceWrappedTexture); secondDeviceWrappedTexture);
// Re-import back into |device|, waiting on |secondDevice|'s signal // Re-import back into |device|, waiting on |secondDevice|'s signal
signalFd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD( dawn_native::vulkan::ExternalImageExportInfoOpaqueFD secondExportInfo;
secondDevice.Get(), secondDeviceWrappedTexture.Get()); dawn_native::vulkan::ExportVulkanImage(secondDeviceWrappedTexture.Get(),
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
&secondExportInfo);
memoryFd = GetMemoryFd(deviceVk, defaultAllocation); memoryFd = GetMemoryFd(deviceVk, defaultAllocation);
wgpu::Texture nextWrappedTexture = wgpu::Texture nextWrappedTexture =
WrapVulkanImage(device, &defaultDescriptor, memoryFd, defaultAllocationSize, WrapVulkanImage(device, &defaultDescriptor, memoryFd, defaultAllocationSize,
defaultMemoryTypeIndex, {signalFd}); defaultMemoryTypeIndex, secondExportInfo.semaphoreHandles,
secondExportInfo.releasedOldLayout, secondExportInfo.releasedNewLayout);
// Verify |nextWrappedTexture| contains the color from our copy // Verify |nextWrappedTexture| contains the color from our copy
EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), nextWrappedTexture, 0, 0); EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), nextWrappedTexture, 0, 0);
IgnoreSignalSemaphore(device, nextWrappedTexture); IgnoreSignalSemaphore(nextWrappedTexture);
} }
// Import a texture from |secondDevice| // Import a texture from |secondDevice|
@ -645,19 +641,22 @@ namespace dawn_native { namespace vulkan {
// Import the image on |secondDevice| // Import the image on |secondDevice|
wgpu::Texture wrappedTexture = wgpu::Texture wrappedTexture =
WrapVulkanImage(secondDevice, &defaultDescriptor, defaultFd, defaultAllocationSize, WrapVulkanImage(secondDevice, &defaultDescriptor, defaultFd, defaultAllocationSize,
defaultMemoryTypeIndex, {}); defaultMemoryTypeIndex, {}, VK_IMAGE_LAYOUT_UNDEFINED,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
// Clear |wrappedTexture| on |secondDevice| // Clear |wrappedTexture| on |secondDevice|
ClearImage(secondDevice, wrappedTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f}); ClearImage(secondDevice, wrappedTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f});
int signalFd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(secondDevice.Get(), dawn_native::vulkan::ExternalImageExportInfoOpaqueFD exportInfo;
wrappedTexture.Get()); dawn_native::vulkan::ExportVulkanImage(wrappedTexture.Get(),
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, &exportInfo);
// Import the image to |device|, making sure we wait on |signalFd| // Import the image to |device|, making sure we wait on the semaphore
int memoryFd = GetMemoryFd(deviceVk, defaultAllocation); int memoryFd = GetMemoryFd(deviceVk, defaultAllocation);
wgpu::Texture deviceWrappedTexture = wgpu::Texture deviceWrappedTexture =
WrapVulkanImage(device, &defaultDescriptor, memoryFd, defaultAllocationSize, WrapVulkanImage(device, &defaultDescriptor, memoryFd, defaultAllocationSize,
defaultMemoryTypeIndex, {signalFd}); defaultMemoryTypeIndex, exportInfo.semaphoreHandles,
exportInfo.releasedOldLayout, exportInfo.releasedNewLayout);
// Create a destination buffer on |device| // Create a destination buffer on |device|
wgpu::BufferDescriptor bufferDesc; wgpu::BufferDescriptor bufferDesc;
@ -681,7 +680,7 @@ namespace dawn_native { namespace vulkan {
uint32_t expected = 0x04030201; uint32_t expected = 0x04030201;
EXPECT_BUFFER_U32_EQ(expected, copyDstBuffer, 0); EXPECT_BUFFER_U32_EQ(expected, copyDstBuffer, 0);
IgnoreSignalSemaphore(device, deviceWrappedTexture); IgnoreSignalSemaphore(deviceWrappedTexture);
} }
// Import a texture into |device| // Import a texture into |device|
@ -696,21 +695,23 @@ namespace dawn_native { namespace vulkan {
DAWN_SKIP_TEST_IF(UsesWire()); DAWN_SKIP_TEST_IF(UsesWire());
// Import the image on |device| // Import the image on |device|
wgpu::Texture wrappedTexture = wgpu::Texture wrappedTexture = WrapVulkanImage(
WrapVulkanImage(device, &defaultDescriptor, defaultFd, defaultAllocationSize, device, &defaultDescriptor, defaultFd, defaultAllocationSize, defaultMemoryTypeIndex,
defaultMemoryTypeIndex, {}); {}, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
// Clear |wrappedTexture| on |device| // Clear |wrappedTexture| on |device|
ClearImage(device, wrappedTexture, {5 / 255.0f, 6 / 255.0f, 7 / 255.0f, 8 / 255.0f}); ClearImage(device, wrappedTexture, {5 / 255.0f, 6 / 255.0f, 7 / 255.0f, 8 / 255.0f});
int signalFd = dawn_native::vulkan::ExternalImageExportInfoOpaqueFD exportInfo;
dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(device.Get(), wrappedTexture.Get()); dawn_native::vulkan::ExportVulkanImage(wrappedTexture.Get(),
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, &exportInfo);
// Import the image to |secondDevice|, making sure we wait on |signalFd| // Import the image to |secondDevice|, making sure we wait on |signalFd|
int memoryFd = GetMemoryFd(deviceVk, defaultAllocation); int memoryFd = GetMemoryFd(deviceVk, defaultAllocation);
wgpu::Texture secondDeviceWrappedTexture = wgpu::Texture secondDeviceWrappedTexture =
WrapVulkanImage(secondDevice, &defaultDescriptor, memoryFd, defaultAllocationSize, WrapVulkanImage(secondDevice, &defaultDescriptor, memoryFd, defaultAllocationSize,
defaultMemoryTypeIndex, {signalFd}); defaultMemoryTypeIndex, exportInfo.semaphoreHandles,
exportInfo.releasedOldLayout, exportInfo.releasedNewLayout);
// Copy color B on |secondDevice| // Copy color B on |secondDevice|
wgpu::Queue secondDeviceQueue = secondDevice.GetDefaultQueue(); wgpu::Queue secondDeviceQueue = secondDevice.GetDefaultQueue();
@ -732,18 +733,21 @@ namespace dawn_native { namespace vulkan {
secondDeviceQueue.Submit(1, &commands); secondDeviceQueue.Submit(1, &commands);
// Re-import back into |device|, waiting on |secondDevice|'s signal // Re-import back into |device|, waiting on |secondDevice|'s signal
signalFd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD( dawn_native::vulkan::ExternalImageExportInfoOpaqueFD secondExportInfo;
secondDevice.Get(), secondDeviceWrappedTexture.Get()); dawn_native::vulkan::ExportVulkanImage(secondDeviceWrappedTexture.Get(),
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
&secondExportInfo);
memoryFd = GetMemoryFd(deviceVk, defaultAllocation); memoryFd = GetMemoryFd(deviceVk, defaultAllocation);
wgpu::Texture nextWrappedTexture = wgpu::Texture nextWrappedTexture =
WrapVulkanImage(device, &defaultDescriptor, memoryFd, defaultAllocationSize, WrapVulkanImage(device, &defaultDescriptor, memoryFd, defaultAllocationSize,
defaultMemoryTypeIndex, {signalFd}); defaultMemoryTypeIndex, secondExportInfo.semaphoreHandles,
secondExportInfo.releasedOldLayout, secondExportInfo.releasedNewLayout);
// Verify |nextWrappedTexture| contains the color from our copy // Verify |nextWrappedTexture| contains the color from our copy
EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), nextWrappedTexture, 0, 0); EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), nextWrappedTexture, 0, 0);
IgnoreSignalSemaphore(device, nextWrappedTexture); IgnoreSignalSemaphore(nextWrappedTexture);
} }
// Import a texture from |secondDevice| // Import a texture from |secondDevice|
@ -756,19 +760,22 @@ namespace dawn_native { namespace vulkan {
// Import the image on |secondDevice| // Import the image on |secondDevice|
wgpu::Texture wrappedTexture = wgpu::Texture wrappedTexture =
WrapVulkanImage(secondDevice, &defaultDescriptor, defaultFd, defaultAllocationSize, WrapVulkanImage(secondDevice, &defaultDescriptor, defaultFd, defaultAllocationSize,
defaultMemoryTypeIndex, {}); defaultMemoryTypeIndex, {}, VK_IMAGE_LAYOUT_UNDEFINED,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
// Clear |wrappedTexture| on |secondDevice| // Clear |wrappedTexture| on |secondDevice|
ClearImage(secondDevice, wrappedTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f}); ClearImage(secondDevice, wrappedTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f});
int signalFd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(secondDevice.Get(), dawn_native::vulkan::ExternalImageExportInfoOpaqueFD exportInfo;
wrappedTexture.Get()); dawn_native::vulkan::ExportVulkanImage(wrappedTexture.Get(),
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, &exportInfo);
// Import the image to |device|, making sure we wait on |signalFd| // Import the image to |device|, making sure we wait on the semaphore
int memoryFd = GetMemoryFd(deviceVk, defaultAllocation); int memoryFd = GetMemoryFd(deviceVk, defaultAllocation);
wgpu::Texture deviceWrappedTexture = wgpu::Texture deviceWrappedTexture =
WrapVulkanImage(device, &defaultDescriptor, memoryFd, defaultAllocationSize, WrapVulkanImage(device, &defaultDescriptor, memoryFd, defaultAllocationSize,
defaultMemoryTypeIndex, {signalFd}); defaultMemoryTypeIndex, exportInfo.semaphoreHandles,
exportInfo.releasedOldLayout, exportInfo.releasedNewLayout);
// Create a second texture on |device| // Create a second texture on |device|
wgpu::Texture copyDstTexture = device.CreateTexture(&defaultDescriptor); wgpu::Texture copyDstTexture = device.CreateTexture(&defaultDescriptor);
@ -788,7 +795,7 @@ namespace dawn_native { namespace vulkan {
// Verify |secondCopyDstTexture| sees changes from |secondDevice| // Verify |secondCopyDstTexture| sees changes from |secondDevice|
EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), secondCopyDstTexture, 0, 0); EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), secondCopyDstTexture, 0, 0);
IgnoreSignalSemaphore(device, deviceWrappedTexture); IgnoreSignalSemaphore(deviceWrappedTexture);
} }
// Tex A on device 3 (external export) // Tex A on device 3 (external export)
@ -844,10 +851,12 @@ namespace dawn_native { namespace vulkan {
// Import TexA, TexB on device 3 // Import TexA, TexB on device 3
wgpu::Texture wrappedTexADevice3 = WrapVulkanImage( wgpu::Texture wrappedTexADevice3 = WrapVulkanImage(
thirdDevice, &defaultDescriptor, memoryFdA, allocationSizeA, memoryTypeIndexA, {}); thirdDevice, &defaultDescriptor, memoryFdA, allocationSizeA, memoryTypeIndexA, {},
VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
wgpu::Texture wrappedTexBDevice3 = WrapVulkanImage( wgpu::Texture wrappedTexBDevice3 = WrapVulkanImage(
thirdDevice, &defaultDescriptor, memoryFdB, allocationSizeB, memoryTypeIndexB, {}); thirdDevice, &defaultDescriptor, memoryFdB, allocationSizeB, memoryTypeIndexB, {},
VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
// Clear TexA // Clear TexA
ClearImage(thirdDevice, wrappedTexADevice3, ClearImage(thirdDevice, wrappedTexADevice3,
@ -857,32 +866,39 @@ namespace dawn_native { namespace vulkan {
SimpleCopyTextureToTexture(thirdDevice, thirdDeviceQueue, wrappedTexADevice3, SimpleCopyTextureToTexture(thirdDevice, thirdDeviceQueue, wrappedTexADevice3,
wrappedTexBDevice3); wrappedTexBDevice3);
int signalFdTexBDevice3 = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD( dawn_native::vulkan::ExternalImageExportInfoOpaqueFD exportInfoTexBDevice3;
thirdDevice.Get(), wrappedTexBDevice3.Get()); dawn_native::vulkan::ExportVulkanImage(
IgnoreSignalSemaphore(thirdDevice, wrappedTexADevice3); wrappedTexBDevice3.Get(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, &exportInfoTexBDevice3);
IgnoreSignalSemaphore(wrappedTexADevice3);
// Import TexB, TexC on device 2 // Import TexB, TexC on device 2
memoryFdB = GetMemoryFd(secondDeviceVk, allocationB); memoryFdB = GetMemoryFd(secondDeviceVk, allocationB);
wgpu::Texture wrappedTexBDevice2 = wgpu::Texture wrappedTexBDevice2 = WrapVulkanImage(
WrapVulkanImage(secondDevice, &defaultDescriptor, memoryFdB, allocationSizeB, secondDevice, &defaultDescriptor, memoryFdB, allocationSizeB, memoryTypeIndexB,
memoryTypeIndexB, {signalFdTexBDevice3}); exportInfoTexBDevice3.semaphoreHandles, exportInfoTexBDevice3.releasedOldLayout,
exportInfoTexBDevice3.releasedNewLayout);
wgpu::Texture wrappedTexCDevice2 = WrapVulkanImage( wgpu::Texture wrappedTexCDevice2 = WrapVulkanImage(
secondDevice, &defaultDescriptor, memoryFdC, allocationSizeC, memoryTypeIndexC, {}); secondDevice, &defaultDescriptor, memoryFdC, allocationSizeC, memoryTypeIndexC, {},
VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
// Copy B->C on device 2 // Copy B->C on device 2
SimpleCopyTextureToTexture(secondDevice, secondDeviceQueue, wrappedTexBDevice2, SimpleCopyTextureToTexture(secondDevice, secondDeviceQueue, wrappedTexBDevice2,
wrappedTexCDevice2); wrappedTexCDevice2);
int signalFdTexCDevice2 = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD( dawn_native::vulkan::ExternalImageExportInfoOpaqueFD exportInfoTexCDevice2;
secondDevice.Get(), wrappedTexCDevice2.Get()); dawn_native::vulkan::ExportVulkanImage(
IgnoreSignalSemaphore(secondDevice, wrappedTexBDevice2); wrappedTexCDevice2.Get(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, &exportInfoTexCDevice2);
IgnoreSignalSemaphore(wrappedTexBDevice2);
// Import TexC on device 1 // Import TexC on device 1
memoryFdC = GetMemoryFd(deviceVk, allocationC); memoryFdC = GetMemoryFd(deviceVk, allocationC);
wgpu::Texture wrappedTexCDevice1 = wgpu::Texture wrappedTexCDevice1 = WrapVulkanImage(
WrapVulkanImage(device, &defaultDescriptor, memoryFdC, allocationSizeC, device, &defaultDescriptor, memoryFdC, allocationSizeC, memoryTypeIndexC,
memoryTypeIndexC, {signalFdTexCDevice2}); exportInfoTexCDevice2.semaphoreHandles, exportInfoTexCDevice2.releasedOldLayout,
exportInfoTexCDevice2.releasedNewLayout);
// Create TexD on device 1 // Create TexD on device 1
wgpu::Texture texD = device.CreateTexture(&defaultDescriptor); wgpu::Texture texD = device.CreateTexture(&defaultDescriptor);
@ -900,7 +916,7 @@ namespace dawn_native { namespace vulkan {
deviceVk->GetFencedDeleter()->DeleteWhenUnused(imageC); deviceVk->GetFencedDeleter()->DeleteWhenUnused(imageC);
deviceVk->GetFencedDeleter()->DeleteWhenUnused(allocationC); deviceVk->GetFencedDeleter()->DeleteWhenUnused(allocationC);
IgnoreSignalSemaphore(device, wrappedTexCDevice1); IgnoreSignalSemaphore(wrappedTexCDevice1);
} }
// Tests a larger image is preserved when importing // Tests a larger image is preserved when importing
@ -938,8 +954,9 @@ namespace dawn_native { namespace vulkan {
&allocationA, &allocationSizeA, &memoryTypeIndexA, &memoryFdA); &allocationA, &allocationSizeA, &memoryTypeIndexA, &memoryFdA);
// Import the image on |secondDevice| // Import the image on |secondDevice|
wgpu::Texture wrappedTexture = WrapVulkanImage(secondDevice, &descriptor, memoryFdA, wgpu::Texture wrappedTexture =
allocationSizeA, memoryTypeIndexA, {}); WrapVulkanImage(secondDevice, &descriptor, memoryFdA, allocationSizeA, memoryTypeIndexA,
{}, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
// Draw a non-trivial picture // Draw a non-trivial picture
uint32_t width = 640, height = 480, pixelSize = 4; uint32_t width = 640, height = 480, pixelSize = 4;
@ -975,13 +992,17 @@ namespace dawn_native { namespace vulkan {
secondDeviceQueue.Submit(1, &commands); secondDeviceQueue.Submit(1, &commands);
} }
int signalFd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(secondDevice.Get(), dawn_native::vulkan::ExternalImageExportInfoOpaqueFD exportInfo;
wrappedTexture.Get()); dawn_native::vulkan::ExportVulkanImage(wrappedTexture.Get(),
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, &exportInfo);
int memoryFd = GetMemoryFd(secondDeviceVk, allocationA); int memoryFd = GetMemoryFd(secondDeviceVk, allocationA);
// Import the image on |device| // Import the image on |device|
wgpu::Texture nextWrappedTexture = WrapVulkanImage( wgpu::Texture nextWrappedTexture =
device, &descriptor, memoryFd, allocationSizeA, memoryTypeIndexA, {signalFd}); WrapVulkanImage(device, &descriptor, memoryFd, allocationSizeA, memoryTypeIndexA,
exportInfo.semaphoreHandles, exportInfo.releasedOldLayout,
exportInfo.releasedNewLayout);
// Copy the image into a buffer for comparison // Copy the image into a buffer for comparison
wgpu::BufferDescriptor copyDesc; wgpu::BufferDescriptor copyDesc;
@ -1006,7 +1027,7 @@ namespace dawn_native { namespace vulkan {
EXPECT_BUFFER_U32_RANGE_EQ(reinterpret_cast<uint32_t*>(data.data()), copyDstBuffer, 0, EXPECT_BUFFER_U32_RANGE_EQ(reinterpret_cast<uint32_t*>(data.data()), copyDstBuffer, 0,
data.size() / 4); data.size() / 4);
IgnoreSignalSemaphore(device, nextWrappedTexture); IgnoreSignalSemaphore(nextWrappedTexture);
secondDeviceVk->GetFencedDeleter()->DeleteWhenUnused(imageA); secondDeviceVk->GetFencedDeleter()->DeleteWhenUnused(imageA);
secondDeviceVk->GetFencedDeleter()->DeleteWhenUnused(allocationA); secondDeviceVk->GetFencedDeleter()->DeleteWhenUnused(allocationA);
} }