From f28d0ae6145737e61b069cf56475a10c903345d6 Mon Sep 17 00:00:00 2001 From: Kai Ninomiya Date: Fri, 31 Jan 2020 02:09:06 +0000 Subject: [PATCH] Revert "Remove VK_DEFINE_NON_DISPATCHABLE_HANDLE magic, use explicit VkHandle wrapper" This reverts commit 4e17d5c2483b63d4863162d692a1a961d1dcb958. Reason for revert: broken on chromeos Original change's description: > Remove VK_DEFINE_NON_DISPATCHABLE_HANDLE magic, use explicit VkHandle wrapper > > Overriding VK_DEFINE_NON_DISPATCHABLE_HANDLE changes the function > signatures of Vulkan functions, changing their ABI and making us > incompatible with real drivers. This removes that magic, and replaces it > with an explicit wrapper, VkHandle, which has much of the same > functionality as the original VkNonDispatchableHandle. > > It adds definitions for dawn_native::vulkan::VkBuffer et al, which > shadow the native ::VkBuffer et al. This retains type safety throughout > the Vulkan backend without changing every single usage. > > Notably, the following things had to change: > - An explicit conversion from VkBuffer* to ::VkBuffer* is needed for > arrays. This is implemented as a reinterpret_cast, which is still > safe as the new VkHandle still has the same memory layout properties > as VkNonDispatchableHandle did. > - When pointing to a VkHandle as an output pointer, it's now necessary > to explicitly get the native ::VkBuffer (via operator*) and point to it. > > Bug: chromium:1046362 > Change-Id: I9c5691b6e295aca1b46d4e3d0203956e4d570285 > Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/15580 > Reviewed-by: Austin Eng > Reviewed-by: Kai Ninomiya > Commit-Queue: Kai Ninomiya TBR=cwallez@chromium.org,kainino@chromium.org,enga@chromium.org Change-Id: I500df2e34fd0f245ad04c517ff028ddd7bb5a2bf No-Presubmit: true No-Tree-Checks: true No-Try: true Bug: chromium:1046362 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/15620 Reviewed-by: Kai Ninomiya Commit-Queue: Kai Ninomiya --- src/common/vulkan_platform.h | 179 +- src/dawn_native/vulkan/BackendVk.cpp | 2 +- src/dawn_native/vulkan/BindGroupLayoutVk.cpp | 13 +- src/dawn_native/vulkan/BindGroupVk.h | 1 - src/dawn_native/vulkan/BufferVk.cpp | 2 +- src/dawn_native/vulkan/CommandBufferVk.cpp | 8 +- src/dawn_native/vulkan/ComputePipelineVk.cpp | 6 +- src/dawn_native/vulkan/DeviceVk.cpp | 12 +- .../vulkan/NativeSwapChainImplVk.cpp | 18 +- src/dawn_native/vulkan/PipelineLayoutVk.cpp | 4 +- src/dawn_native/vulkan/RenderPassCache.cpp | 6 +- src/dawn_native/vulkan/RenderPipelineVk.cpp | 6 +- .../vulkan/ResourceMemoryAllocatorVk.cpp | 2 +- src/dawn_native/vulkan/SamplerVk.cpp | 2 +- src/dawn_native/vulkan/ShaderModuleVk.cpp | 2 +- src/dawn_native/vulkan/StagingBufferVk.cpp | 2 +- src/dawn_native/vulkan/SwapChainVk.cpp | 3 +- src/dawn_native/vulkan/TextureVk.cpp | 4 +- src/dawn_native/vulkan/VulkanBackend.cpp | 2 +- .../external_memory/MemoryServiceOpaqueFD.cpp | 4 +- .../SemaphoreServiceOpaqueFD.cpp | 4 +- src/include/dawn_native/VulkanBackend.h | 4 +- src/tests/DawnTest.h | 12 +- .../white_box/VulkanImageWrappingTests.cpp | 1843 ++++++++--------- 24 files changed, 1058 insertions(+), 1083 deletions(-) diff --git a/src/common/vulkan_platform.h b/src/common/vulkan_platform.h index 913f011f03..35742a70d8 100644 --- a/src/common/vulkan_platform.h +++ b/src/common/vulkan_platform.h @@ -18,9 +18,6 @@ #if !defined(DAWN_ENABLE_BACKEND_VULKAN) # error "vulkan_platform.h included without the Vulkan backend enabled" #endif -#if defined(VULKAN_CORE_H_) -# error "vulkan.h included before vulkan_platform.h" -#endif #include "common/Platform.h" @@ -36,9 +33,10 @@ // (like vulkan.h on 64 bit) but makes sure the types are different on 32 bit architectures. #if defined(DAWN_PLATFORM_64_BIT) -# define DAWN_DEFINE_NATIVE_NON_DISPATCHABLE_HANDLE(object) using object = struct object##_T*; +# define DAWN_DEFINE_NATIVE_NON_DISPATCHABLE_HANDLE(object) \ + using object##Native = struct object##_T*; #elif defined(DAWN_PLATFORM_32_BIT) -# define DAWN_DEFINE_NATIVE_NON_DISPATCHABLE_HANDLE(object) using object = uint64_t; +# define DAWN_DEFINE_NATIVE_NON_DISPATCHABLE_HANDLE(object) using object##Native = uint64_t; #else # error "Unsupported platform" #endif @@ -55,106 +53,105 @@ DAWN_DEFINE_NATIVE_NON_DISPATCHABLE_HANDLE(VkSomeHandle) // One way to get the alignment inside structures of a type is to look at the alignment of it // wrapped in a structure. Hence VkSameHandleNativeWrappe -namespace dawn_native { namespace vulkan { +template +struct WrapperStruct { + T member; +}; - namespace detail { - template - struct WrapperStruct { - T member; - }; +template +static constexpr size_t AlignOfInStruct = alignof(WrapperStruct); - template - static constexpr size_t AlignOfInStruct = alignof(WrapperStruct); +static constexpr size_t kNativeVkHandleAlignment = AlignOfInStruct; +static constexpr size_t kUint64Alignment = AlignOfInStruct; - static constexpr size_t kNativeVkHandleAlignment = AlignOfInStruct; - static constexpr size_t kUint64Alignment = AlignOfInStruct; - - // Simple handle types that supports "nullptr_t" as a 0 value. - template - class alignas(detail::kNativeVkHandleAlignment) VkHandle { - public: - // Default constructor and assigning of VK_NULL_HANDLE - VkHandle() = default; - VkHandle(std::nullptr_t) { - } - - // Use default copy constructor/assignment - VkHandle(const VkHandle& other) = default; - VkHandle& operator=(const VkHandle&) = default; - - // Comparisons between handles - bool operator==(VkHandle other) const { - return mHandle == other.mHandle; - } - bool operator!=(VkHandle other) const { - return mHandle != other.mHandle; - } - - // Comparisons between handles and VK_NULL_HANDLE - bool operator==(std::nullptr_t) const { - return mHandle == 0; - } - bool operator!=(std::nullptr_t) const { - return mHandle != 0; - } - - // Implicit conversion to real Vulkan types. - operator HandleType() const { - return GetHandle(); - } - - HandleType GetHandle() const { - return mHandle; - } - - HandleType& operator*() { - return mHandle; - } - - static VkHandle CreateFromHandle(HandleType handle) { - return VkHandle{handle}; - } - - private: - explicit VkHandle(HandleType handle) : mHandle(handle) { - } - - HandleType mHandle = 0; - }; - } // namespace detail - - static constexpr std::nullptr_t VK_NULL_HANDLE = nullptr; - - template - HandleType* AsVkArray(detail::VkHandle* handle) { - return reinterpret_cast(handle); +// Simple handle types that supports "nullptr_t" as a 0 value. +template +class alignas(kNativeVkHandleAlignment) VkNonDispatchableHandle { + public: + // Default constructor and assigning of VK_NULL_HANDLE + VkNonDispatchableHandle() = default; + VkNonDispatchableHandle(std::nullptr_t) : mHandle(0) { } -}} // namespace dawn_native::vulkan + // Use default copy constructor/assignment + VkNonDispatchableHandle(const VkNonDispatchableHandle& other) = default; + VkNonDispatchableHandle& operator=(const VkNonDispatchableHandle&) = default; -#define VK_DEFINE_NON_DISPATCHABLE_HANDLE(object) \ - DAWN_DEFINE_NATIVE_NON_DISPATCHABLE_HANDLE(object) \ - namespace dawn_native { namespace vulkan { \ - using object = detail::VkHandle; \ - static_assert(sizeof(object) == sizeof(uint64_t), ""); \ - static_assert(alignof(object) == detail::kUint64Alignment, ""); \ - static_assert(sizeof(object) == sizeof(::object), ""); \ - static_assert(alignof(object) == detail::kNativeVkHandleAlignment, ""); \ - } \ - } // namespace dawn_native::vulkan + // Comparisons between handles + bool operator==(VkNonDispatchableHandle other) const { + return mHandle == other.mHandle; + } + bool operator!=(VkNonDispatchableHandle other) const { + return mHandle != other.mHandle; + } -#include + // Comparisons between handles and VK_NULL_HANDLE + bool operator==(std::nullptr_t) const { + return mHandle == 0; + } + bool operator!=(std::nullptr_t) const { + return mHandle != 0; + } + + // The regular Vulkan handle type depends on the pointer width but is always 64 bits wide. + // - On 64bit it is an opaque pointer type, probably to help with type safety + // - On 32bit it is a uint64_t because pointers aren't wide enough (and non dispatchable + // handles can be optimized to not be pointer but contain GPU virtual addresses or the + // data in a packed form). + // Because of this we need two types of conversions from our handle type: to uint64_t and to + // the "native" Vulkan type that may not be an uint64_t + + static VkNonDispatchableHandle CreateFromU64(uint64_t handle) { + return {handle}; + } + + uint64_t GetU64() const { + return mHandle; + } -// Redefine VK_NULL_HANDLE for better type safety where possible. -#undef VK_NULL_HANDLE #if defined(DAWN_PLATFORM_64_BIT) -static constexpr nullptr_t VK_NULL_HANDLE = nullptr; + static VkNonDispatchableHandle CreateFromHandle(HandleType handle) { + return CreateFromU64(static_cast(reinterpret_cast(handle))); + } + + HandleType GetHandle() const { + return mHandle; + } #elif defined(DAWN_PLATFORM_32_BIT) -static constexpr uint64_t VK_NULL_HANDLE = 0; + static VkNonDispatchableHandle CreateFromHandle(HandleType handle) { + return {handle}; + } + + HandleType GetHandle() const { + return mHandle; + } #else # error "Unsupported platform" #endif + private: + VkNonDispatchableHandle(uint64_t handle) : mHandle(handle) { + } + + uint64_t mHandle = 0; +}; + +#define VK_DEFINE_NON_DISPATCHABLE_HANDLE(object) \ + struct VkTag##object; \ + DAWN_DEFINE_NATIVE_NON_DISPATCHABLE_HANDLE(object) \ + using object = VkNonDispatchableHandle; \ + static_assert(sizeof(object) == sizeof(uint64_t), ""); \ + static_assert(alignof(object) == kUint64Alignment, ""); \ + static_assert(sizeof(object) == sizeof(object##Native), ""); \ + static_assert(alignof(object) == kNativeVkHandleAlignment, ""); + +# include + + // VK_NULL_HANDLE is defined to 0 but we don't want our handle type to compare to arbitrary + // integers. Redefine VK_NULL_HANDLE to nullptr that has its own type. +# undef VK_NULL_HANDLE +# define VK_NULL_HANDLE nullptr + // Remove windows.h macros after vulkan_platform's include of windows.h #if defined(DAWN_PLATFORM_WINDOWS) # include "common/windows_with_undefs.h" diff --git a/src/dawn_native/vulkan/BackendVk.cpp b/src/dawn_native/vulkan/BackendVk.cpp index d2745cb5e5..d2aaa76475 100644 --- a/src/dawn_native/vulkan/BackendVk.cpp +++ b/src/dawn_native/vulkan/BackendVk.cpp @@ -272,7 +272,7 @@ namespace dawn_native { namespace vulkan { createInfo.pUserData = this; return CheckVkSuccess(mFunctions.CreateDebugReportCallbackEXT( - mInstance, &createInfo, nullptr, &*mDebugReportCallback), + mInstance, &createInfo, nullptr, &mDebugReportCallback), "vkCreateDebugReportcallback"); } diff --git a/src/dawn_native/vulkan/BindGroupLayoutVk.cpp b/src/dawn_native/vulkan/BindGroupLayoutVk.cpp index 90c8366272..89ce53d98f 100644 --- a/src/dawn_native/vulkan/BindGroupLayoutVk.cpp +++ b/src/dawn_native/vulkan/BindGroupLayoutVk.cpp @@ -105,7 +105,7 @@ namespace dawn_native { namespace vulkan { Device* device = ToBackend(GetDevice()); DAWN_TRY(CheckVkSuccess(device->fn.CreateDescriptorSetLayout( - device->GetVkDevice(), &createInfo, nullptr, &*mHandle), + device->GetVkDevice(), &createInfo, nullptr, &mHandle), "CreateDescriptorSetLayout")); // Compute the size of descriptor pools used for this layout. @@ -171,7 +171,7 @@ namespace dawn_native { namespace vulkan { VkDescriptorPool descriptorPool; DAWN_TRY(CheckVkSuccess(device->fn.CreateDescriptorPool(device->GetVkDevice(), &createInfo, - nullptr, &*descriptorPool), + nullptr, &descriptorPool), "CreateDescriptorPool")); // Allocate our single set. @@ -180,13 +180,12 @@ namespace dawn_native { namespace vulkan { allocateInfo.pNext = nullptr; allocateInfo.descriptorPool = descriptorPool; allocateInfo.descriptorSetCount = 1; - allocateInfo.pSetLayouts = &*mHandle; + allocateInfo.pSetLayouts = &mHandle; VkDescriptorSet descriptorSet; - MaybeError result = - CheckVkSuccess(device->fn.AllocateDescriptorSets(device->GetVkDevice(), &allocateInfo, - &*descriptorSet), - "AllocateDescriptorSets"); + MaybeError result = CheckVkSuccess( + device->fn.AllocateDescriptorSets(device->GetVkDevice(), &allocateInfo, &descriptorSet), + "AllocateDescriptorSets"); if (result.IsError()) { // On an error we can destroy the pool immediately because no command references it. diff --git a/src/dawn_native/vulkan/BindGroupVk.h b/src/dawn_native/vulkan/BindGroupVk.h index 9fa857b745..4dd4c218a7 100644 --- a/src/dawn_native/vulkan/BindGroupVk.h +++ b/src/dawn_native/vulkan/BindGroupVk.h @@ -17,7 +17,6 @@ #include "dawn_native/BindGroup.h" -#include "common/vulkan_platform.h" #include "dawn_native/vulkan/BindGroupLayoutVk.h" namespace dawn_native { namespace vulkan { diff --git a/src/dawn_native/vulkan/BufferVk.cpp b/src/dawn_native/vulkan/BufferVk.cpp index 4a1af60588..8f8b3aa2f0 100644 --- a/src/dawn_native/vulkan/BufferVk.cpp +++ b/src/dawn_native/vulkan/BufferVk.cpp @@ -147,7 +147,7 @@ namespace dawn_native { namespace vulkan { Device* device = ToBackend(GetDevice()); DAWN_TRY(CheckVkSuccess( - device->fn.CreateBuffer(device->GetVkDevice(), &createInfo, nullptr, &*mHandle), + device->fn.CreateBuffer(device->GetVkDevice(), &createInfo, nullptr, &mHandle), "vkCreateBuffer")); VkMemoryRequirements requirements; diff --git a/src/dawn_native/vulkan/CommandBufferVk.cpp b/src/dawn_native/vulkan/CommandBufferVk.cpp index 7e128066c7..b845e3b6d7 100644 --- a/src/dawn_native/vulkan/CommandBufferVk.cpp +++ b/src/dawn_native/vulkan/CommandBufferVk.cpp @@ -106,7 +106,7 @@ namespace dawn_native { namespace vulkan { ? dynamicOffsets[dirtyIndex].data() : nullptr; device->fn.CmdBindDescriptorSets(commands, bindPoint, pipelineLayout, dirtyIndex, 1, - &*set, dynamicOffsetCounts[dirtyIndex], + &set, dynamicOffsetCounts[dirtyIndex], dynamicOffset); } } @@ -255,14 +255,14 @@ namespace dawn_native { namespace vulkan { createInfo.flags = 0; createInfo.renderPass = renderPassVK; createInfo.attachmentCount = attachmentCount; - createInfo.pAttachments = AsVkArray(attachments.data()); + createInfo.pAttachments = attachments.data(); createInfo.width = renderPass->width; createInfo.height = renderPass->height; createInfo.layers = 1; DAWN_TRY( CheckVkSuccess(device->fn.CreateFramebuffer(device->GetVkDevice(), &createInfo, - nullptr, &*framebuffer), + nullptr, &framebuffer), "CreateFramebuffer")); // We don't reuse VkFramebuffers so mark the framebuffer for deletion as soon as the @@ -827,7 +827,7 @@ namespace dawn_native { namespace vulkan { VkBuffer buffer = ToBackend(cmd->buffer)->GetHandle(); VkDeviceSize offset = static_cast(cmd->offset); - device->fn.CmdBindVertexBuffers(commands, cmd->slot, 1, &*buffer, &offset); + device->fn.CmdBindVertexBuffers(commands, cmd->slot, 1, &buffer, &offset); } break; default: diff --git a/src/dawn_native/vulkan/ComputePipelineVk.cpp b/src/dawn_native/vulkan/ComputePipelineVk.cpp index 16dd8e7d15..2f37620ded 100644 --- a/src/dawn_native/vulkan/ComputePipelineVk.cpp +++ b/src/dawn_native/vulkan/ComputePipelineVk.cpp @@ -38,7 +38,7 @@ namespace dawn_native { namespace vulkan { createInfo.pNext = nullptr; createInfo.flags = 0; createInfo.layout = ToBackend(descriptor->layout)->GetHandle(); - createInfo.basePipelineHandle = ::VK_NULL_HANDLE; + createInfo.basePipelineHandle = VK_NULL_HANDLE; createInfo.basePipelineIndex = -1; createInfo.stage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; @@ -51,8 +51,8 @@ namespace dawn_native { namespace vulkan { Device* device = ToBackend(GetDevice()); return CheckVkSuccess( - device->fn.CreateComputePipelines(device->GetVkDevice(), ::VK_NULL_HANDLE, 1, - &createInfo, nullptr, &*mHandle), + device->fn.CreateComputePipelines(device->GetVkDevice(), VK_NULL_HANDLE, 1, &createInfo, + nullptr, &mHandle), "CreateComputePipeline"); } diff --git a/src/dawn_native/vulkan/DeviceVk.cpp b/src/dawn_native/vulkan/DeviceVk.cpp index e78718e386..bc4179a98b 100644 --- a/src/dawn_native/vulkan/DeviceVk.cpp +++ b/src/dawn_native/vulkan/DeviceVk.cpp @@ -272,13 +272,13 @@ namespace dawn_native { namespace vulkan { submitInfo.pNext = nullptr; submitInfo.waitSemaphoreCount = static_cast(mRecordingContext.waitSemaphores.size()); - submitInfo.pWaitSemaphores = AsVkArray(mRecordingContext.waitSemaphores.data()); + submitInfo.pWaitSemaphores = mRecordingContext.waitSemaphores.data(); submitInfo.pWaitDstStageMask = dstStageMasks.data(); submitInfo.commandBufferCount = 1; submitInfo.pCommandBuffers = &mRecordingContext.commandBuffer; submitInfo.signalSemaphoreCount = static_cast(mRecordingContext.signalSemaphores.size()); - submitInfo.pSignalSemaphores = AsVkArray(mRecordingContext.signalSemaphores.data()); + submitInfo.pSignalSemaphores = mRecordingContext.signalSemaphores.data(); VkFence fence = VK_NULL_HANDLE; DAWN_TRY_ASSIGN(fence, GetUnusedFence()); @@ -474,7 +474,7 @@ namespace dawn_native { namespace vulkan { ResultOrError Device::GetUnusedFence() { if (!mUnusedFences.empty()) { VkFence fence = mUnusedFences.back(); - DAWN_TRY(CheckVkSuccess(fn.ResetFences(mVkDevice, 1, &*fence), "vkResetFences")); + DAWN_TRY(CheckVkSuccess(fn.ResetFences(mVkDevice, 1, &fence), "vkResetFences")); mUnusedFences.pop_back(); return fence; @@ -486,7 +486,7 @@ namespace dawn_native { namespace vulkan { createInfo.flags = 0; VkFence fence = VK_NULL_HANDLE; - DAWN_TRY(CheckVkSuccess(fn.CreateFence(mVkDevice, &createInfo, nullptr, &*fence), + DAWN_TRY(CheckVkSuccess(fn.CreateFence(mVkDevice, &createInfo, nullptr, &fence), "vkCreateFence")); return fence; @@ -539,7 +539,7 @@ namespace dawn_native { namespace vulkan { createInfo.queueFamilyIndex = mQueueFamily; DAWN_TRY(CheckVkSuccess(fn.CreateCommandPool(mVkDevice, &createInfo, nullptr, - &*mRecordingContext.commandPool), + &mRecordingContext.commandPool), "vkCreateCommandPool")); VkCommandBufferAllocateInfo allocateInfo; @@ -756,7 +756,7 @@ namespace dawn_native { namespace vulkan { VkResult result = VkResult::WrapUnsafe(VK_TIMEOUT); do { result = VkResult::WrapUnsafe( - INJECT_ERROR_OR_RUN(fn.WaitForFences(mVkDevice, 1, &*fence, true, UINT64_MAX), + INJECT_ERROR_OR_RUN(fn.WaitForFences(mVkDevice, 1, &fence, true, UINT64_MAX), VK_ERROR_DEVICE_LOST)); } while (result == VK_TIMEOUT); diff --git a/src/dawn_native/vulkan/NativeSwapChainImplVk.cpp b/src/dawn_native/vulkan/NativeSwapChainImplVk.cpp index ada090f7de..a330d71165 100644 --- a/src/dawn_native/vulkan/NativeSwapChainImplVk.cpp +++ b/src/dawn_native/vulkan/NativeSwapChainImplVk.cpp @@ -136,7 +136,7 @@ namespace dawn_native { namespace vulkan { createInfo.oldSwapchain = oldSwapchain; if (mDevice->fn.CreateSwapchainKHR(mDevice->GetVkDevice(), &createInfo, nullptr, - &*mSwapChain) != VK_SUCCESS) { + &mSwapChain) != VK_SUCCESS) { ASSERT(false); } @@ -151,7 +151,7 @@ namespace dawn_native { namespace vulkan { ASSERT(count >= mConfig.minImageCount); mSwapChainImages.resize(count); if (mDevice->fn.GetSwapchainImagesKHR(mDevice->GetVkDevice(), mSwapChain, &count, - AsVkArray(mSwapChainImages.data())) != VK_SUCCESS) { + mSwapChainImages.data()) != VK_SUCCESS) { ASSERT(false); } @@ -168,7 +168,7 @@ namespace dawn_native { namespace vulkan { barrier.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; barrier.srcQueueFamilyIndex = 0; barrier.dstQueueFamilyIndex = 0; - barrier.image = *image; + barrier.image = image; barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; barrier.subresourceRange.baseMipLevel = 0; barrier.subresourceRange.levelCount = 1; @@ -197,22 +197,18 @@ namespace dawn_native { namespace vulkan { createInfo.pNext = nullptr; createInfo.flags = 0; if (mDevice->fn.CreateSemaphore(mDevice->GetVkDevice(), &createInfo, nullptr, - &*semaphore) != VK_SUCCESS) { + &semaphore) != VK_SUCCESS) { ASSERT(false); } } if (mDevice->fn.AcquireNextImageKHR(mDevice->GetVkDevice(), mSwapChain, std::numeric_limits::max(), semaphore, - VkFence{}, &mLastImageIndex) != VK_SUCCESS) { + VK_NULL_HANDLE, &mLastImageIndex) != VK_SUCCESS) { ASSERT(false); } - nextTexture->texture.u64 = -#if defined(DAWN_PLATFORM_64_BIT) - reinterpret_cast -#endif - (*mSwapChainImages[mLastImageIndex]); + nextTexture->texture.u64 = mSwapChainImages[mLastImageIndex].GetU64(); mDevice->GetPendingRecordingContext()->waitSemaphores.push_back(semaphore); return DAWN_SWAP_CHAIN_NO_ERROR; @@ -231,7 +227,7 @@ namespace dawn_native { namespace vulkan { presentInfo.waitSemaphoreCount = 0; presentInfo.pWaitSemaphores = nullptr; presentInfo.swapchainCount = 1; - presentInfo.pSwapchains = &*mSwapChain; + presentInfo.pSwapchains = &mSwapChain; presentInfo.pImageIndices = &mLastImageIndex; presentInfo.pResults = nullptr; diff --git a/src/dawn_native/vulkan/PipelineLayoutVk.cpp b/src/dawn_native/vulkan/PipelineLayoutVk.cpp index 847ba60f82..dd123af35e 100644 --- a/src/dawn_native/vulkan/PipelineLayoutVk.cpp +++ b/src/dawn_native/vulkan/PipelineLayoutVk.cpp @@ -48,13 +48,13 @@ namespace dawn_native { namespace vulkan { createInfo.pNext = nullptr; createInfo.flags = 0; createInfo.setLayoutCount = numSetLayouts; - createInfo.pSetLayouts = AsVkArray(setLayouts.data()); + createInfo.pSetLayouts = setLayouts.data(); createInfo.pushConstantRangeCount = 0; createInfo.pPushConstantRanges = nullptr; Device* device = ToBackend(GetDevice()); return CheckVkSuccess( - device->fn.CreatePipelineLayout(device->GetVkDevice(), &createInfo, nullptr, &*mHandle), + device->fn.CreatePipelineLayout(device->GetVkDevice(), &createInfo, nullptr, &mHandle), "CreatePipelineLayout"); } diff --git a/src/dawn_native/vulkan/RenderPassCache.cpp b/src/dawn_native/vulkan/RenderPassCache.cpp index 47330f1204..87424589f2 100644 --- a/src/dawn_native/vulkan/RenderPassCache.cpp +++ b/src/dawn_native/vulkan/RenderPassCache.cpp @@ -191,9 +191,9 @@ namespace dawn_native { namespace vulkan { // Create the render pass from the zillion parameters VkRenderPass renderPass; - DAWN_TRY(CheckVkSuccess(mDevice->fn.CreateRenderPass(mDevice->GetVkDevice(), &createInfo, - nullptr, &*renderPass), - "CreateRenderPass")); + DAWN_TRY(CheckVkSuccess( + mDevice->fn.CreateRenderPass(mDevice->GetVkDevice(), &createInfo, nullptr, &renderPass), + "CreateRenderPass")); return renderPass; } diff --git a/src/dawn_native/vulkan/RenderPipelineVk.cpp b/src/dawn_native/vulkan/RenderPipelineVk.cpp index b0ba4fd701..4b770a518a 100644 --- a/src/dawn_native/vulkan/RenderPipelineVk.cpp +++ b/src/dawn_native/vulkan/RenderPipelineVk.cpp @@ -495,12 +495,12 @@ namespace dawn_native { namespace vulkan { createInfo.layout = ToBackend(GetLayout())->GetHandle(); createInfo.renderPass = renderPass; createInfo.subpass = 0; - createInfo.basePipelineHandle = VkPipeline{}; + createInfo.basePipelineHandle = VK_NULL_HANDLE; createInfo.basePipelineIndex = -1; return CheckVkSuccess( - device->fn.CreateGraphicsPipelines(device->GetVkDevice(), VkPipelineCache{}, 1, - &createInfo, nullptr, &*mHandle), + device->fn.CreateGraphicsPipelines(device->GetVkDevice(), VK_NULL_HANDLE, 1, + &createInfo, nullptr, &mHandle), "CreateGraphicsPipeline"); } diff --git a/src/dawn_native/vulkan/ResourceMemoryAllocatorVk.cpp b/src/dawn_native/vulkan/ResourceMemoryAllocatorVk.cpp index 3d8ded5b5c..25fa5ee0db 100644 --- a/src/dawn_native/vulkan/ResourceMemoryAllocatorVk.cpp +++ b/src/dawn_native/vulkan/ResourceMemoryAllocatorVk.cpp @@ -73,7 +73,7 @@ namespace dawn_native { namespace vulkan { // First check OOM that we want to surface to the application. DAWN_TRY(CheckVkOOMThenSuccess( mDevice->fn.AllocateMemory(mDevice->GetVkDevice(), &allocateInfo, nullptr, - &*allocatedMemory), + &allocatedMemory), "vkAllocateMemory")); ASSERT(allocatedMemory != VK_NULL_HANDLE); diff --git a/src/dawn_native/vulkan/SamplerVk.cpp b/src/dawn_native/vulkan/SamplerVk.cpp index 67d70f8d7c..05baf71fc9 100644 --- a/src/dawn_native/vulkan/SamplerVk.cpp +++ b/src/dawn_native/vulkan/SamplerVk.cpp @@ -87,7 +87,7 @@ namespace dawn_native { namespace vulkan { Device* device = ToBackend(GetDevice()); return CheckVkSuccess( - device->fn.CreateSampler(device->GetVkDevice(), &createInfo, nullptr, &*mHandle), + device->fn.CreateSampler(device->GetVkDevice(), &createInfo, nullptr, &mHandle), "CreateSampler"); } diff --git a/src/dawn_native/vulkan/ShaderModuleVk.cpp b/src/dawn_native/vulkan/ShaderModuleVk.cpp index a9e717ce9c..140abba098 100644 --- a/src/dawn_native/vulkan/ShaderModuleVk.cpp +++ b/src/dawn_native/vulkan/ShaderModuleVk.cpp @@ -74,7 +74,7 @@ namespace dawn_native { namespace vulkan { Device* device = ToBackend(GetDevice()); return CheckVkSuccess( - device->fn.CreateShaderModule(device->GetVkDevice(), &createInfo, nullptr, &*mHandle), + device->fn.CreateShaderModule(device->GetVkDevice(), &createInfo, nullptr, &mHandle), "CreateShaderModule"); } diff --git a/src/dawn_native/vulkan/StagingBufferVk.cpp b/src/dawn_native/vulkan/StagingBufferVk.cpp index 4fa3b7b651..42623188e3 100644 --- a/src/dawn_native/vulkan/StagingBufferVk.cpp +++ b/src/dawn_native/vulkan/StagingBufferVk.cpp @@ -36,7 +36,7 @@ namespace dawn_native { namespace vulkan { createInfo.pQueueFamilyIndices = 0; DAWN_TRY(CheckVkSuccess( - mDevice->fn.CreateBuffer(mDevice->GetVkDevice(), &createInfo, nullptr, &*mBuffer), + mDevice->fn.CreateBuffer(mDevice->GetVkDevice(), &createInfo, nullptr, &mBuffer), "vkCreateBuffer")); VkMemoryRequirements requirements; diff --git a/src/dawn_native/vulkan/SwapChainVk.cpp b/src/dawn_native/vulkan/SwapChainVk.cpp index e64f5e52f2..876af8e164 100644 --- a/src/dawn_native/vulkan/SwapChainVk.cpp +++ b/src/dawn_native/vulkan/SwapChainVk.cpp @@ -47,8 +47,7 @@ namespace dawn_native { namespace vulkan { return nullptr; } - VkImage nativeTexture = - VkImage::CreateFromHandle(reinterpret_cast<::VkImage>(next.texture.u64)); + VkImage nativeTexture = VkImage::CreateFromU64(next.texture.u64); return new Texture(ToBackend(GetDevice()), descriptor, nativeTexture); } diff --git a/src/dawn_native/vulkan/TextureVk.cpp b/src/dawn_native/vulkan/TextureVk.cpp index 6f2fc9e94e..72a7653685 100644 --- a/src/dawn_native/vulkan/TextureVk.cpp +++ b/src/dawn_native/vulkan/TextureVk.cpp @@ -460,7 +460,7 @@ namespace dawn_native { namespace vulkan { createInfo.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT; DAWN_TRY(CheckVkSuccess( - device->fn.CreateImage(device->GetVkDevice(), &createInfo, nullptr, &*mHandle), + device->fn.CreateImage(device->GetVkDevice(), &createInfo, nullptr, &mHandle), "CreateImage")); // Create the image memory and associate it with the container @@ -806,7 +806,7 @@ namespace dawn_native { namespace vulkan { createInfo.subresourceRange.layerCount = descriptor->arrayLayerCount; return CheckVkSuccess( - device->fn.CreateImageView(device->GetVkDevice(), &createInfo, nullptr, &*mHandle), + device->fn.CreateImageView(device->GetVkDevice(), &createInfo, nullptr, &mHandle), "CreateImageView"); } diff --git a/src/dawn_native/vulkan/VulkanBackend.cpp b/src/dawn_native/vulkan/VulkanBackend.cpp index 2a9361f409..ad99a27401 100644 --- a/src/dawn_native/vulkan/VulkanBackend.cpp +++ b/src/dawn_native/vulkan/VulkanBackend.cpp @@ -42,7 +42,7 @@ namespace dawn_native { namespace vulkan { // Explicitly export this function because it uses the "native" type for surfaces while the // header as seen in this file uses the wrapped type. DAWN_NATIVE_EXPORT DawnSwapChainImplementation - CreateNativeSwapChainImpl(WGPUDevice device, ::VkSurfaceKHR surfaceNative) { + CreateNativeSwapChainImpl(WGPUDevice device, VkSurfaceKHRNative surfaceNative) { Device* backendDevice = reinterpret_cast(device); VkSurfaceKHR surface = VkSurfaceKHR::CreateFromHandle(surfaceNative); diff --git a/src/dawn_native/vulkan/external_memory/MemoryServiceOpaqueFD.cpp b/src/dawn_native/vulkan/external_memory/MemoryServiceOpaqueFD.cpp index a8bc1819dd..a6bb2fadce 100644 --- a/src/dawn_native/vulkan/external_memory/MemoryServiceOpaqueFD.cpp +++ b/src/dawn_native/vulkan/external_memory/MemoryServiceOpaqueFD.cpp @@ -130,7 +130,7 @@ namespace dawn_native { namespace vulkan { namespace external_memory { VkDeviceMemory allocatedMemory = VK_NULL_HANDLE; DAWN_TRY(CheckVkSuccess(mDevice->fn.AllocateMemory(mDevice->GetVkDevice(), &allocateInfo, - nullptr, &*allocatedMemory), + nullptr, &allocatedMemory), "vkAllocateMemory")); return allocatedMemory; } @@ -146,7 +146,7 @@ namespace dawn_native { namespace vulkan { namespace external_memory { VkImage image; DAWN_TRY(CheckVkSuccess( - mDevice->fn.CreateImage(mDevice->GetVkDevice(), &createInfo, nullptr, &*image), + mDevice->fn.CreateImage(mDevice->GetVkDevice(), &createInfo, nullptr, &image), "CreateImage")); return image; } diff --git a/src/dawn_native/vulkan/external_semaphore/SemaphoreServiceOpaqueFD.cpp b/src/dawn_native/vulkan/external_semaphore/SemaphoreServiceOpaqueFD.cpp index 3e1d3f0141..ea7bf47d0f 100644 --- a/src/dawn_native/vulkan/external_semaphore/SemaphoreServiceOpaqueFD.cpp +++ b/src/dawn_native/vulkan/external_semaphore/SemaphoreServiceOpaqueFD.cpp @@ -72,7 +72,7 @@ namespace dawn_native { namespace vulkan { namespace external_semaphore { info.flags = 0; DAWN_TRY(CheckVkSuccess( - mDevice->fn.CreateSemaphore(mDevice->GetVkDevice(), &info, nullptr, &*semaphore), + mDevice->fn.CreateSemaphore(mDevice->GetVkDevice(), &info, nullptr, &semaphore), "vkCreateSemaphore")); VkImportSemaphoreFdInfoKHR importSemaphoreFdInfo; @@ -109,7 +109,7 @@ namespace dawn_native { namespace vulkan { namespace external_semaphore { VkSemaphore signalSemaphore; DAWN_TRY( CheckVkSuccess(mDevice->fn.CreateSemaphore(mDevice->GetVkDevice(), &semaphoreCreateInfo, - nullptr, &*signalSemaphore), + nullptr, &signalSemaphore), "vkCreateSemaphore")); return signalSemaphore; } diff --git a/src/include/dawn_native/VulkanBackend.h b/src/include/dawn_native/VulkanBackend.h index 005a655ce7..30dbb05b84 100644 --- a/src/include/dawn_native/VulkanBackend.h +++ b/src/include/dawn_native/VulkanBackend.h @@ -47,8 +47,8 @@ namespace dawn_native { namespace vulkan { DAWN_NATIVE_EXPORT PFN_vkVoidFunction GetInstanceProcAddr(WGPUDevice device, const char* pName); - DAWN_NATIVE_EXPORT DawnSwapChainImplementation - CreateNativeSwapChainImpl(WGPUDevice device, ::VkSurfaceKHR surface); + DAWN_NATIVE_EXPORT DawnSwapChainImplementation CreateNativeSwapChainImpl(WGPUDevice device, + VkSurfaceKHR surface); DAWN_NATIVE_EXPORT WGPUTextureFormat GetNativeSwapChainPreferredFormat(const DawnSwapChainImplementation* swapChain); diff --git a/src/tests/DawnTest.h b/src/tests/DawnTest.h index 9cdfd3e600..677f67007e 100644 --- a/src/tests/DawnTest.h +++ b/src/tests/DawnTest.h @@ -32,30 +32,30 @@ // them. #define EXPECT_BUFFER_U32_EQ(expected, buffer, offset) \ AddBufferExpectation(__FILE__, __LINE__, buffer, offset, sizeof(uint32_t), \ - new ::detail::ExpectEq(expected)) + new detail::ExpectEq(expected)) #define EXPECT_BUFFER_U32_RANGE_EQ(expected, buffer, offset, count) \ AddBufferExpectation(__FILE__, __LINE__, buffer, offset, sizeof(uint32_t) * count, \ - new ::detail::ExpectEq(expected, count)) + new detail::ExpectEq(expected, count)) // Test a pixel of the mip level 0 of a 2D texture. #define EXPECT_PIXEL_RGBA8_EQ(expected, texture, x, y) \ AddTextureExpectation(__FILE__, __LINE__, texture, x, y, 1, 1, 0, 0, sizeof(RGBA8), \ - new ::detail::ExpectEq(expected)) + new detail::ExpectEq(expected)) #define EXPECT_TEXTURE_RGBA8_EQ(expected, texture, x, y, width, height, level, slice) \ AddTextureExpectation(__FILE__, __LINE__, texture, x, y, width, height, level, slice, \ sizeof(RGBA8), \ - new ::detail::ExpectEq(expected, (width) * (height))) + new detail::ExpectEq(expected, (width) * (height))) #define EXPECT_PIXEL_FLOAT_EQ(expected, texture, x, y) \ AddTextureExpectation(__FILE__, __LINE__, texture, x, y, 1, 1, 0, 0, sizeof(float), \ - new ::detail::ExpectEq(expected)) + new detail::ExpectEq(expected)) #define EXPECT_TEXTURE_FLOAT_EQ(expected, texture, x, y, width, height, level, slice) \ AddTextureExpectation(__FILE__, __LINE__, texture, x, y, width, height, level, slice, \ sizeof(float), \ - new ::detail::ExpectEq(expected, (width) * (height))) + new detail::ExpectEq(expected, (width) * (height))) // Should only be used to test validation of function that can't be tested by regular validation // tests; diff --git a/src/tests/white_box/VulkanImageWrappingTests.cpp b/src/tests/white_box/VulkanImageWrappingTests.cpp index 1db9643967..de7d8fe321 100644 --- a/src/tests/white_box/VulkanImageWrappingTests.cpp +++ b/src/tests/white_box/VulkanImageWrappingTests.cpp @@ -25,1008 +25,993 @@ #include "utils/SystemUtils.h" #include "utils/WGPUHelpers.h" -namespace dawn_native { namespace vulkan { +namespace { - namespace { - - class VulkanImageWrappingTestBase : public DawnTest { - public: - void TestSetUp() override { - if (UsesWire()) { - return; - } - - deviceVk = reinterpret_cast(device.Get()); + class VulkanImageWrappingTestBase : public DawnTest { + public: + void TestSetUp() override { + if (UsesWire()) { + return; } - // Creates a VkImage with external memory - ::VkResult CreateImage(dawn_native::vulkan::Device* deviceVk, + deviceVk = reinterpret_cast(device.Get()); + } + + // Creates a VkImage with external memory + VkResult CreateImage(dawn_native::vulkan::Device* deviceVk, + uint32_t width, + uint32_t height, + VkFormat format, + VkImage* image) { + VkExternalMemoryImageCreateInfoKHR externalInfo; + externalInfo.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO_KHR; + externalInfo.pNext = nullptr; + externalInfo.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR; + + auto usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | + VK_IMAGE_USAGE_TRANSFER_DST_BIT; + + VkImageCreateInfo createInfo; + createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + createInfo.pNext = &externalInfo; + createInfo.flags = VK_IMAGE_CREATE_ALIAS_BIT_KHR; + createInfo.imageType = VK_IMAGE_TYPE_2D; + createInfo.format = format; + createInfo.extent = {width, height, 1}; + createInfo.mipLevels = 1; + createInfo.arrayLayers = 1; + createInfo.samples = VK_SAMPLE_COUNT_1_BIT; + createInfo.tiling = VK_IMAGE_TILING_OPTIMAL; + createInfo.usage = usage; + createInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + createInfo.queueFamilyIndexCount = 0; + createInfo.pQueueFamilyIndices = nullptr; + createInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + + return deviceVk->fn.CreateImage(deviceVk->GetVkDevice(), &createInfo, nullptr, image); + } + + // Allocates memory for an image + VkResult AllocateMemory(dawn_native::vulkan::Device* deviceVk, + VkImage handle, + VkDeviceMemory* allocation, + VkDeviceSize* allocationSize, + uint32_t* memoryTypeIndex) { + // Create the image memory and associate it with the container + VkMemoryRequirements requirements; + deviceVk->fn.GetImageMemoryRequirements(deviceVk->GetVkDevice(), handle, &requirements); + + // Import memory from file descriptor + VkExportMemoryAllocateInfoKHR externalInfo; + externalInfo.sType = VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_KHR; + externalInfo.pNext = nullptr; + externalInfo.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR; + + int bestType = deviceVk->GetResourceMemoryAllocatorForTesting()->FindBestTypeIndex( + requirements, false); + VkMemoryAllocateInfo allocateInfo; + allocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + allocateInfo.pNext = &externalInfo; + allocateInfo.allocationSize = requirements.size; + allocateInfo.memoryTypeIndex = static_cast(bestType); + + *allocationSize = allocateInfo.allocationSize; + *memoryTypeIndex = allocateInfo.memoryTypeIndex; + + return deviceVk->fn.AllocateMemory(deviceVk->GetVkDevice(), &allocateInfo, nullptr, + allocation); + } + + // Binds memory to an image + VkResult BindMemory(dawn_native::vulkan::Device* deviceVk, + VkImage handle, + VkDeviceMemory* memory) { + return deviceVk->fn.BindImageMemory(deviceVk->GetVkDevice(), handle, *memory, 0); + } + + // Extracts a file descriptor representing memory on a device + int GetMemoryFd(dawn_native::vulkan::Device* deviceVk, VkDeviceMemory memory) { + VkMemoryGetFdInfoKHR getFdInfo; + getFdInfo.sType = VK_STRUCTURE_TYPE_MEMORY_GET_FD_INFO_KHR; + getFdInfo.pNext = nullptr; + getFdInfo.memory = memory; + getFdInfo.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR; + + int memoryFd = -1; + deviceVk->fn.GetMemoryFdKHR(deviceVk->GetVkDevice(), &getFdInfo, &memoryFd); + + EXPECT_GE(memoryFd, 0) << "Failed to get file descriptor for external memory"; + return memoryFd; + } + + // Prepares and exports memory for an image on a given device + void CreateBindExportImage(dawn_native::vulkan::Device* deviceVk, uint32_t width, uint32_t height, VkFormat format, - VkImage* image) { - VkExternalMemoryImageCreateInfoKHR externalInfo; - externalInfo.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO_KHR; - externalInfo.pNext = nullptr; - externalInfo.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR; + VkImage* handle, + VkDeviceMemory* allocation, + VkDeviceSize* allocationSize, + uint32_t* memoryTypeIndex, + int* memoryFd) { + VkResult result = CreateImage(deviceVk, width, height, format, handle); + EXPECT_EQ(result, VK_SUCCESS) << "Failed to create external image"; - auto usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | - VK_IMAGE_USAGE_TRANSFER_DST_BIT; + VkResult resultBool = + AllocateMemory(deviceVk, *handle, allocation, allocationSize, memoryTypeIndex); + EXPECT_EQ(resultBool, VK_SUCCESS) << "Failed to allocate external memory"; - VkImageCreateInfo createInfo; - createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; - createInfo.pNext = &externalInfo; - createInfo.flags = VK_IMAGE_CREATE_ALIAS_BIT_KHR; - createInfo.imageType = VK_IMAGE_TYPE_2D; - createInfo.format = format; - createInfo.extent = {width, height, 1}; - createInfo.mipLevels = 1; - createInfo.arrayLayers = 1; - createInfo.samples = VK_SAMPLE_COUNT_1_BIT; - createInfo.tiling = VK_IMAGE_TILING_OPTIMAL; - createInfo.usage = usage; - createInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - createInfo.queueFamilyIndexCount = 0; - createInfo.pQueueFamilyIndices = nullptr; - createInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + result = BindMemory(deviceVk, *handle, allocation); + EXPECT_EQ(result, VK_SUCCESS) << "Failed to bind image memory"; - return deviceVk->fn.CreateImage(deviceVk->GetVkDevice(), &createInfo, nullptr, - &**image); - } - - // Allocates memory for an image - ::VkResult AllocateMemory(dawn_native::vulkan::Device* deviceVk, - VkImage handle, - VkDeviceMemory* allocation, - VkDeviceSize* allocationSize, - uint32_t* memoryTypeIndex) { - // Create the image memory and associate it with the container - VkMemoryRequirements requirements; - deviceVk->fn.GetImageMemoryRequirements(deviceVk->GetVkDevice(), handle, - &requirements); - - // Import memory from file descriptor - VkExportMemoryAllocateInfoKHR externalInfo; - externalInfo.sType = VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_KHR; - externalInfo.pNext = nullptr; - externalInfo.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR; - - int bestType = deviceVk->GetResourceMemoryAllocatorForTesting()->FindBestTypeIndex( - requirements, false); - VkMemoryAllocateInfo allocateInfo; - allocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; - allocateInfo.pNext = &externalInfo; - allocateInfo.allocationSize = requirements.size; - allocateInfo.memoryTypeIndex = static_cast(bestType); - - *allocationSize = allocateInfo.allocationSize; - *memoryTypeIndex = allocateInfo.memoryTypeIndex; - - return deviceVk->fn.AllocateMemory(deviceVk->GetVkDevice(), &allocateInfo, nullptr, - &**allocation); - } - - // Binds memory to an image - ::VkResult BindMemory(dawn_native::vulkan::Device* deviceVk, - VkImage handle, - VkDeviceMemory* memory) { - return deviceVk->fn.BindImageMemory(deviceVk->GetVkDevice(), handle, *memory, 0); - } - - // Extracts a file descriptor representing memory on a device - int GetMemoryFd(dawn_native::vulkan::Device* deviceVk, VkDeviceMemory memory) { - VkMemoryGetFdInfoKHR getFdInfo; - getFdInfo.sType = VK_STRUCTURE_TYPE_MEMORY_GET_FD_INFO_KHR; - getFdInfo.pNext = nullptr; - getFdInfo.memory = memory; - getFdInfo.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR; - - int memoryFd = -1; - deviceVk->fn.GetMemoryFdKHR(deviceVk->GetVkDevice(), &getFdInfo, &memoryFd); - - EXPECT_GE(memoryFd, 0) << "Failed to get file descriptor for external memory"; - return memoryFd; - } - - // Prepares and exports memory for an image on a given device - void CreateBindExportImage(dawn_native::vulkan::Device* deviceVk, - uint32_t width, - uint32_t height, - VkFormat format, - VkImage* handle, - VkDeviceMemory* allocation, - VkDeviceSize* allocationSize, - uint32_t* memoryTypeIndex, - int* memoryFd) { - ::VkResult result = CreateImage(deviceVk, width, height, format, handle); - EXPECT_EQ(result, VK_SUCCESS) << "Failed to create external image"; - - ::VkResult resultBool = - AllocateMemory(deviceVk, *handle, allocation, allocationSize, memoryTypeIndex); - EXPECT_EQ(resultBool, VK_SUCCESS) << "Failed to allocate external memory"; - - result = BindMemory(deviceVk, *handle, allocation); - EXPECT_EQ(result, VK_SUCCESS) << "Failed to bind image memory"; - - *memoryFd = GetMemoryFd(deviceVk, *allocation); - } - - // Wraps a vulkan image from external memory - wgpu::Texture WrapVulkanImage(wgpu::Device device, - const wgpu::TextureDescriptor* textureDescriptor, - int memoryFd, - VkDeviceSize allocationSize, - uint32_t memoryTypeIndex, - std::vector waitFDs, - bool isCleared = true, - bool expectValid = true) { - dawn_native::vulkan::ExternalImageDescriptorOpaqueFD descriptor; - descriptor.cTextureDescriptor = - reinterpret_cast(textureDescriptor); - descriptor.isCleared = isCleared; - descriptor.allocationSize = allocationSize; - descriptor.memoryTypeIndex = memoryTypeIndex; - descriptor.memoryFD = memoryFd; - descriptor.waitFDs = waitFDs; - - WGPUTexture texture = - dawn_native::vulkan::WrapVulkanImage(device.Get(), &descriptor); - - if (expectValid) { - EXPECT_NE(texture, nullptr) << "Failed to wrap image, are external memory / " - "semaphore extensions supported?"; - } else { - EXPECT_EQ(texture, nullptr); - } - - return wgpu::Texture::Acquire(texture); - } - - // Exports the signal from a wrapped texture and ignores it - // We have to export the signal before destroying the wrapped texture else it's an - // assertion failure - void IgnoreSignalSemaphore(wgpu::Device device, wgpu::Texture wrappedTexture) { - int fd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(device.Get(), - wrappedTexture.Get()); - ASSERT_NE(fd, -1); - close(fd); - } - - protected: - dawn_native::vulkan::Device* deviceVk; - }; - - } // anonymous namespace - - class VulkanImageWrappingValidationTests : public VulkanImageWrappingTestBase { - public: - void TestSetUp() override { - VulkanImageWrappingTestBase::TestSetUp(); - if (UsesWire()) { - return; - } - - CreateBindExportImage(deviceVk, 1, 1, VK_FORMAT_R8G8B8A8_UNORM, &defaultImage, - &defaultAllocation, &defaultAllocationSize, - &defaultMemoryTypeIndex, &defaultFd); - defaultDescriptor.dimension = wgpu::TextureDimension::e2D; - defaultDescriptor.format = wgpu::TextureFormat::RGBA8Unorm; - defaultDescriptor.size = {1, 1, 1}; - defaultDescriptor.sampleCount = 1; - defaultDescriptor.arrayLayerCount = 1; - defaultDescriptor.mipLevelCount = 1; - defaultDescriptor.usage = wgpu::TextureUsage::OutputAttachment | - wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst; + *memoryFd = GetMemoryFd(deviceVk, *allocation); } - void TearDown() override { - if (UsesWire()) { - VulkanImageWrappingTestBase::TearDown(); - return; + // Wraps a vulkan image from external memory + wgpu::Texture WrapVulkanImage(wgpu::Device device, + const wgpu::TextureDescriptor* textureDescriptor, + int memoryFd, + VkDeviceSize allocationSize, + uint32_t memoryTypeIndex, + std::vector waitFDs, + bool isCleared = true, + bool expectValid = true) { + dawn_native::vulkan::ExternalImageDescriptorOpaqueFD descriptor; + descriptor.cTextureDescriptor = + reinterpret_cast(textureDescriptor); + descriptor.isCleared = isCleared; + descriptor.allocationSize = allocationSize; + descriptor.memoryTypeIndex = memoryTypeIndex; + descriptor.memoryFD = memoryFd; + descriptor.waitFDs = waitFDs; + + WGPUTexture texture = dawn_native::vulkan::WrapVulkanImage(device.Get(), &descriptor); + + if (expectValid) { + EXPECT_NE(texture, nullptr) << "Failed to wrap image, are external memory / " + "semaphore extensions supported?"; + } else { + EXPECT_EQ(texture, nullptr); } - deviceVk->GetFencedDeleter()->DeleteWhenUnused(defaultImage); - deviceVk->GetFencedDeleter()->DeleteWhenUnused(defaultAllocation); - VulkanImageWrappingTestBase::TearDown(); + return wgpu::Texture::Acquire(texture); + } + + // Exports the signal from a wrapped texture and ignores it + // We have to export the signal before destroying the wrapped texture else it's an assertion + // failure + void IgnoreSignalSemaphore(wgpu::Device device, wgpu::Texture wrappedTexture) { + int fd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(device.Get(), + wrappedTexture.Get()); + ASSERT_NE(fd, -1); + close(fd); } protected: - wgpu::TextureDescriptor defaultDescriptor; - VkImage defaultImage; - VkDeviceMemory defaultAllocation; - VkDeviceSize defaultAllocationSize; - uint32_t defaultMemoryTypeIndex; - int defaultFd; + dawn_native::vulkan::Device* deviceVk; }; - // Test no error occurs if the import is valid - TEST_P(VulkanImageWrappingValidationTests, SuccessfulImport) { - DAWN_SKIP_TEST_IF(UsesWire()); - wgpu::Texture texture = - WrapVulkanImage(device, &defaultDescriptor, defaultFd, defaultAllocationSize, - defaultMemoryTypeIndex, {}, true, true); - EXPECT_NE(texture.Get(), nullptr); - IgnoreSignalSemaphore(device, texture); - } +} // anonymous namespace - // Test an error occurs if the texture descriptor is missing - TEST_P(VulkanImageWrappingValidationTests, MissingTextureDescriptor) { - DAWN_SKIP_TEST_IF(UsesWire()); - ASSERT_DEVICE_ERROR(wgpu::Texture texture = - WrapVulkanImage(device, nullptr, defaultFd, defaultAllocationSize, - defaultMemoryTypeIndex, {}, true, false)); - EXPECT_EQ(texture.Get(), nullptr); - } - - // Test an error occurs if the texture descriptor is invalid - TEST_P(VulkanImageWrappingValidationTests, InvalidTextureDescriptor) { - DAWN_SKIP_TEST_IF(UsesWire()); - wgpu::ChainedStruct chainedDescriptor; - defaultDescriptor.nextInChain = &chainedDescriptor; - - ASSERT_DEVICE_ERROR(wgpu::Texture texture = WrapVulkanImage( - device, &defaultDescriptor, defaultFd, defaultAllocationSize, - defaultMemoryTypeIndex, {}, true, false)); - EXPECT_EQ(texture.Get(), nullptr); - } - - // Test an error occurs if the descriptor dimension isn't 2D - TEST_P(VulkanImageWrappingValidationTests, InvalidTextureDimension) { - DAWN_SKIP_TEST_IF(UsesWire()); - defaultDescriptor.dimension = wgpu::TextureDimension::e1D; - - ASSERT_DEVICE_ERROR(wgpu::Texture texture = WrapVulkanImage( - device, &defaultDescriptor, defaultFd, defaultAllocationSize, - defaultMemoryTypeIndex, {}, true, false)); - EXPECT_EQ(texture.Get(), nullptr); - } - - // Test an error occurs if the descriptor mip level count isn't 1 - TEST_P(VulkanImageWrappingValidationTests, InvalidMipLevelCount) { - DAWN_SKIP_TEST_IF(UsesWire()); - defaultDescriptor.mipLevelCount = 2; - - ASSERT_DEVICE_ERROR(wgpu::Texture texture = WrapVulkanImage( - device, &defaultDescriptor, defaultFd, defaultAllocationSize, - defaultMemoryTypeIndex, {}, true, false)); - EXPECT_EQ(texture.Get(), nullptr); - } - - // Test an error occurs if the descriptor array layer count isn't 1 - TEST_P(VulkanImageWrappingValidationTests, InvalidArrayLayerCount) { - DAWN_SKIP_TEST_IF(UsesWire()); - defaultDescriptor.arrayLayerCount = 2; - - ASSERT_DEVICE_ERROR(wgpu::Texture texture = WrapVulkanImage( - device, &defaultDescriptor, defaultFd, defaultAllocationSize, - defaultMemoryTypeIndex, {}, true, false)); - EXPECT_EQ(texture.Get(), nullptr); - } - - // Test an error occurs if the descriptor sample count isn't 1 - TEST_P(VulkanImageWrappingValidationTests, InvalidSampleCount) { - DAWN_SKIP_TEST_IF(UsesWire()); - defaultDescriptor.sampleCount = 4; - - ASSERT_DEVICE_ERROR(wgpu::Texture texture = WrapVulkanImage( - device, &defaultDescriptor, defaultFd, defaultAllocationSize, - defaultMemoryTypeIndex, {}, true, false)); - EXPECT_EQ(texture.Get(), nullptr); - } - - // Test an error occurs if we try to export the signal semaphore twice - TEST_P(VulkanImageWrappingValidationTests, DoubleSignalSemaphoreExport) { - DAWN_SKIP_TEST_IF(UsesWire()); - wgpu::Texture texture = - WrapVulkanImage(device, &defaultDescriptor, defaultFd, defaultAllocationSize, - defaultMemoryTypeIndex, {}, true, true); - ASSERT_NE(texture.Get(), nullptr); - IgnoreSignalSemaphore(device, texture); - ASSERT_DEVICE_ERROR(int fd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD( - device.Get(), texture.Get())); - ASSERT_EQ(fd, -1); - } - - // Test an error occurs if we try to export the signal semaphore from a normal texture - TEST_P(VulkanImageWrappingValidationTests, NormalTextureSignalSemaphoreExport) { - DAWN_SKIP_TEST_IF(UsesWire()); - wgpu::Texture texture = device.CreateTexture(&defaultDescriptor); - ASSERT_NE(texture.Get(), nullptr); - ASSERT_DEVICE_ERROR(int fd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD( - device.Get(), texture.Get())); - ASSERT_EQ(fd, -1); - } - - // Test an error occurs if we try to export the signal semaphore from a destroyed texture - TEST_P(VulkanImageWrappingValidationTests, DestroyedTextureSignalSemaphoreExport) { - DAWN_SKIP_TEST_IF(UsesWire()); - wgpu::Texture texture = device.CreateTexture(&defaultDescriptor); - ASSERT_NE(texture.Get(), nullptr); - texture.Destroy(); - ASSERT_DEVICE_ERROR(int fd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD( - device.Get(), texture.Get())); - ASSERT_EQ(fd, -1); - } - - // Fixture to test using external memory textures through different usages. - // These tests are skipped if the harness is using the wire. - class VulkanImageWrappingUsageTests : public VulkanImageWrappingTestBase { - public: - void TestSetUp() override { - VulkanImageWrappingTestBase::TestSetUp(); - if (UsesWire()) { - return; - } - - // Create another device based on the original - backendAdapter = - reinterpret_cast(deviceVk->GetAdapter()); - deviceDescriptor.forceEnabledToggles = GetParam().forceEnabledWorkarounds; - deviceDescriptor.forceDisabledToggles = GetParam().forceDisabledWorkarounds; - - secondDeviceVk = reinterpret_cast( - backendAdapter->CreateDevice(&deviceDescriptor)); - secondDevice = wgpu::Device::Acquire(reinterpret_cast(secondDeviceVk)); - - CreateBindExportImage(deviceVk, 1, 1, VK_FORMAT_R8G8B8A8_UNORM, &defaultImage, - &defaultAllocation, &defaultAllocationSize, - &defaultMemoryTypeIndex, &defaultFd); - defaultDescriptor.dimension = wgpu::TextureDimension::e2D; - defaultDescriptor.format = wgpu::TextureFormat::RGBA8Unorm; - defaultDescriptor.size = {1, 1, 1}; - defaultDescriptor.sampleCount = 1; - defaultDescriptor.arrayLayerCount = 1; - defaultDescriptor.mipLevelCount = 1; - defaultDescriptor.usage = wgpu::TextureUsage::OutputAttachment | - wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst; +class VulkanImageWrappingValidationTests : public VulkanImageWrappingTestBase { + public: + void TestSetUp() override { + VulkanImageWrappingTestBase::TestSetUp(); + if (UsesWire()) { + return; } - void TearDown() override { - if (UsesWire()) { - VulkanImageWrappingTestBase::TearDown(); - return; - } + CreateBindExportImage(deviceVk, 1, 1, VK_FORMAT_R8G8B8A8_UNORM, &defaultImage, + &defaultAllocation, &defaultAllocationSize, &defaultMemoryTypeIndex, + &defaultFd); + defaultDescriptor.dimension = wgpu::TextureDimension::e2D; + defaultDescriptor.format = wgpu::TextureFormat::RGBA8Unorm; + defaultDescriptor.size = {1, 1, 1}; + defaultDescriptor.sampleCount = 1; + defaultDescriptor.arrayLayerCount = 1; + defaultDescriptor.mipLevelCount = 1; + defaultDescriptor.usage = wgpu::TextureUsage::OutputAttachment | + wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst; + } - deviceVk->GetFencedDeleter()->DeleteWhenUnused(defaultImage); - deviceVk->GetFencedDeleter()->DeleteWhenUnused(defaultAllocation); + void TearDown() override { + if (UsesWire()) { VulkanImageWrappingTestBase::TearDown(); + return; } - protected: - wgpu::Device secondDevice; - dawn_native::vulkan::Device* secondDeviceVk; + deviceVk->GetFencedDeleter()->DeleteWhenUnused(defaultImage); + deviceVk->GetFencedDeleter()->DeleteWhenUnused(defaultAllocation); + VulkanImageWrappingTestBase::TearDown(); + } - dawn_native::vulkan::Adapter* backendAdapter; - dawn_native::DeviceDescriptor deviceDescriptor; + protected: + wgpu::TextureDescriptor defaultDescriptor; + VkImage defaultImage; + VkDeviceMemory defaultAllocation; + VkDeviceSize defaultAllocationSize; + uint32_t defaultMemoryTypeIndex; + int defaultFd; +}; - wgpu::TextureDescriptor defaultDescriptor; - VkImage defaultImage; - VkDeviceMemory defaultAllocation; - VkDeviceSize defaultAllocationSize; - uint32_t defaultMemoryTypeIndex; - int defaultFd; +// Test no error occurs if the import is valid +TEST_P(VulkanImageWrappingValidationTests, SuccessfulImport) { + DAWN_SKIP_TEST_IF(UsesWire()); + wgpu::Texture texture = + WrapVulkanImage(device, &defaultDescriptor, defaultFd, defaultAllocationSize, + defaultMemoryTypeIndex, {}, true, true); + EXPECT_NE(texture.Get(), nullptr); + IgnoreSignalSemaphore(device, texture); +} - // Clear a texture on a given device - void ClearImage(wgpu::Device device, wgpu::Texture wrappedTexture, wgpu::Color clearColor) { - wgpu::TextureView wrappedView = wrappedTexture.CreateView(); +// Test an error occurs if the texture descriptor is missing +TEST_P(VulkanImageWrappingValidationTests, MissingTextureDescriptor) { + DAWN_SKIP_TEST_IF(UsesWire()); + ASSERT_DEVICE_ERROR(wgpu::Texture texture = + WrapVulkanImage(device, nullptr, defaultFd, defaultAllocationSize, + defaultMemoryTypeIndex, {}, true, false)); + EXPECT_EQ(texture.Get(), nullptr); +} - // Submit a clear operation - utils::ComboRenderPassDescriptor renderPassDescriptor({wrappedView}, {}); - renderPassDescriptor.cColorAttachments[0].clearColor = clearColor; +// Test an error occurs if the texture descriptor is invalid +TEST_P(VulkanImageWrappingValidationTests, InvalidTextureDescriptor) { + DAWN_SKIP_TEST_IF(UsesWire()); + wgpu::ChainedStruct chainedDescriptor; + defaultDescriptor.nextInChain = &chainedDescriptor; - wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); - wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDescriptor); - pass.EndPass(); + ASSERT_DEVICE_ERROR(wgpu::Texture texture = WrapVulkanImage( + device, &defaultDescriptor, defaultFd, defaultAllocationSize, + defaultMemoryTypeIndex, {}, true, false)); + EXPECT_EQ(texture.Get(), nullptr); +} - wgpu::CommandBuffer commands = encoder.Finish(); +// Test an error occurs if the descriptor dimension isn't 2D +TEST_P(VulkanImageWrappingValidationTests, InvalidTextureDimension) { + DAWN_SKIP_TEST_IF(UsesWire()); + defaultDescriptor.dimension = wgpu::TextureDimension::e1D; - wgpu::Queue queue = device.CreateQueue(); - queue.Submit(1, &commands); + ASSERT_DEVICE_ERROR(wgpu::Texture texture = WrapVulkanImage( + device, &defaultDescriptor, defaultFd, defaultAllocationSize, + defaultMemoryTypeIndex, {}, true, false)); + EXPECT_EQ(texture.Get(), nullptr); +} + +// Test an error occurs if the descriptor mip level count isn't 1 +TEST_P(VulkanImageWrappingValidationTests, InvalidMipLevelCount) { + DAWN_SKIP_TEST_IF(UsesWire()); + defaultDescriptor.mipLevelCount = 2; + + ASSERT_DEVICE_ERROR(wgpu::Texture texture = WrapVulkanImage( + device, &defaultDescriptor, defaultFd, defaultAllocationSize, + defaultMemoryTypeIndex, {}, true, false)); + EXPECT_EQ(texture.Get(), nullptr); +} + +// Test an error occurs if the descriptor array layer count isn't 1 +TEST_P(VulkanImageWrappingValidationTests, InvalidArrayLayerCount) { + DAWN_SKIP_TEST_IF(UsesWire()); + defaultDescriptor.arrayLayerCount = 2; + + ASSERT_DEVICE_ERROR(wgpu::Texture texture = WrapVulkanImage( + device, &defaultDescriptor, defaultFd, defaultAllocationSize, + defaultMemoryTypeIndex, {}, true, false)); + EXPECT_EQ(texture.Get(), nullptr); +} + +// Test an error occurs if the descriptor sample count isn't 1 +TEST_P(VulkanImageWrappingValidationTests, InvalidSampleCount) { + DAWN_SKIP_TEST_IF(UsesWire()); + defaultDescriptor.sampleCount = 4; + + ASSERT_DEVICE_ERROR(wgpu::Texture texture = WrapVulkanImage( + device, &defaultDescriptor, defaultFd, defaultAllocationSize, + defaultMemoryTypeIndex, {}, true, false)); + EXPECT_EQ(texture.Get(), nullptr); +} + +// Test an error occurs if we try to export the signal semaphore twice +TEST_P(VulkanImageWrappingValidationTests, DoubleSignalSemaphoreExport) { + DAWN_SKIP_TEST_IF(UsesWire()); + wgpu::Texture texture = + WrapVulkanImage(device, &defaultDescriptor, defaultFd, defaultAllocationSize, + defaultMemoryTypeIndex, {}, true, true); + ASSERT_NE(texture.Get(), nullptr); + IgnoreSignalSemaphore(device, texture); + ASSERT_DEVICE_ERROR( + int fd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(device.Get(), texture.Get())); + ASSERT_EQ(fd, -1); +} + +// Test an error occurs if we try to export the signal semaphore from a normal texture +TEST_P(VulkanImageWrappingValidationTests, NormalTextureSignalSemaphoreExport) { + DAWN_SKIP_TEST_IF(UsesWire()); + wgpu::Texture texture = device.CreateTexture(&defaultDescriptor); + ASSERT_NE(texture.Get(), nullptr); + ASSERT_DEVICE_ERROR( + int fd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(device.Get(), texture.Get())); + ASSERT_EQ(fd, -1); +} + +// Test an error occurs if we try to export the signal semaphore from a destroyed texture +TEST_P(VulkanImageWrappingValidationTests, DestroyedTextureSignalSemaphoreExport) { + DAWN_SKIP_TEST_IF(UsesWire()); + wgpu::Texture texture = device.CreateTexture(&defaultDescriptor); + ASSERT_NE(texture.Get(), nullptr); + texture.Destroy(); + ASSERT_DEVICE_ERROR( + int fd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(device.Get(), texture.Get())); + ASSERT_EQ(fd, -1); +} + +// Fixture to test using external memory textures through different usages. +// These tests are skipped if the harness is using the wire. +class VulkanImageWrappingUsageTests : public VulkanImageWrappingTestBase { + public: + void TestSetUp() override { + VulkanImageWrappingTestBase::TestSetUp(); + if (UsesWire()) { + return; } - // Submits a 1x1x1 copy from source to destination - void SimpleCopyTextureToTexture(wgpu::Device device, - wgpu::Queue queue, - wgpu::Texture source, - wgpu::Texture destination) { - wgpu::TextureCopyView copySrc; - copySrc.texture = source; - copySrc.mipLevel = 0; - copySrc.arrayLayer = 0; - copySrc.origin = {0, 0, 0}; + // Create another device based on the original + backendAdapter = reinterpret_cast(deviceVk->GetAdapter()); + deviceDescriptor.forceEnabledToggles = GetParam().forceEnabledWorkarounds; + deviceDescriptor.forceDisabledToggles = GetParam().forceDisabledWorkarounds; - wgpu::TextureCopyView copyDst; - copyDst.texture = destination; - copyDst.mipLevel = 0; - copyDst.arrayLayer = 0; - copyDst.origin = {0, 0, 0}; + secondDeviceVk = reinterpret_cast( + backendAdapter->CreateDevice(&deviceDescriptor)); + secondDevice = wgpu::Device::Acquire(reinterpret_cast(secondDeviceVk)); - wgpu::Extent3D copySize = {1, 1, 1}; + CreateBindExportImage(deviceVk, 1, 1, VK_FORMAT_R8G8B8A8_UNORM, &defaultImage, + &defaultAllocation, &defaultAllocationSize, &defaultMemoryTypeIndex, + &defaultFd); + defaultDescriptor.dimension = wgpu::TextureDimension::e2D; + defaultDescriptor.format = wgpu::TextureFormat::RGBA8Unorm; + defaultDescriptor.size = {1, 1, 1}; + defaultDescriptor.sampleCount = 1; + defaultDescriptor.arrayLayerCount = 1; + defaultDescriptor.mipLevelCount = 1; + defaultDescriptor.usage = wgpu::TextureUsage::OutputAttachment | + wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst; + } - wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); - encoder.CopyTextureToTexture(©Src, ©Dst, ©Size); - wgpu::CommandBuffer commands = encoder.Finish(); - - queue.Submit(1, &commands); + void TearDown() override { + if (UsesWire()) { + VulkanImageWrappingTestBase::TearDown(); + return; } - }; - // Clear an image in |secondDevice| - // Verify clear color is visible in |device| - TEST_P(VulkanImageWrappingUsageTests, ClearImageAcrossDevices) { - DAWN_SKIP_TEST_IF(UsesWire()); - - // 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| - EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), nextWrappedTexture, 0, 0); - - IgnoreSignalSemaphore(device, nextWrappedTexture); + deviceVk->GetFencedDeleter()->DeleteWhenUnused(defaultImage); + deviceVk->GetFencedDeleter()->DeleteWhenUnused(defaultAllocation); + VulkanImageWrappingTestBase::TearDown(); } - // 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()); - // Import the image on |device - wgpu::Texture wrappedTextureAlias = - WrapVulkanImage(device, &defaultDescriptor, defaultFd, defaultAllocationSize, - defaultMemoryTypeIndex, {}); + protected: + wgpu::Device secondDevice; + dawn_native::vulkan::Device* secondDeviceVk; - int memoryFd = GetMemoryFd(deviceVk, defaultAllocation); + dawn_native::vulkan::Adapter* backendAdapter; + dawn_native::DeviceDescriptor deviceDescriptor; - // Import the image on |secondDevice| - wgpu::Texture wrappedTexture = - WrapVulkanImage(secondDevice, &defaultDescriptor, defaultFd, defaultAllocationSize, - defaultMemoryTypeIndex, {}); + wgpu::TextureDescriptor defaultDescriptor; + VkImage defaultImage; + VkDeviceMemory defaultAllocation; + VkDeviceSize defaultAllocationSize; + uint32_t defaultMemoryTypeIndex; + int defaultFd; - // Clear |wrappedTexture| on |secondDevice| - ClearImage(secondDevice, wrappedTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f}); + // Clear a texture on a given device + void ClearImage(wgpu::Device device, wgpu::Texture wrappedTexture, wgpu::Color clearColor) { + wgpu::TextureView wrappedView = wrappedTexture.CreateView(); - int signalFd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(secondDevice.Get(), - wrappedTexture.Get()); + // Submit a clear operation + utils::ComboRenderPassDescriptor renderPassDescriptor({wrappedView}, {}); + renderPassDescriptor.cColorAttachments[0].clearColor = clearColor; - // Import the image to |device|, making sure we wait on signalFd - memoryFd = GetMemoryFd(deviceVk, defaultAllocation); - wgpu::Texture nextWrappedTexture = - WrapVulkanImage(device, &defaultDescriptor, memoryFd, defaultAllocationSize, - defaultMemoryTypeIndex, {signalFd}); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDescriptor); + pass.EndPass(); - // Verify |device| sees the changes from |secondDevice| (waits) - EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), nextWrappedTexture, 0, 0); + wgpu::CommandBuffer commands = encoder.Finish(); - // 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); + wgpu::Queue queue = device.CreateQueue(); + queue.Submit(1, &commands); } - // Clear an image in |secondDevice| - // Verify clear color is not visible in |device| if we import the texture as not cleared - TEST_P(VulkanImageWrappingUsageTests, UnclearedTextureIsCleared) { - DAWN_SKIP_TEST_IF(UsesWire()); - - // 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}, false); - - // Verify |device| doesn't see the changes from |secondDevice| - EXPECT_PIXEL_RGBA8_EQ(RGBA8(0, 0, 0, 0), nextWrappedTexture, 0, 0); - - IgnoreSignalSemaphore(device, nextWrappedTexture); - } - - // Import a texture into |secondDevice| - // Issue a copy of the imported texture inside |device| to |copyDstTexture| - // Verify the clear color from |secondDevice| is visible in |copyDstTexture| - TEST_P(VulkanImageWrappingUsageTests, CopyTextureToTextureSrcSync) { - DAWN_SKIP_TEST_IF(UsesWire()); - - // 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 deviceWrappedTexture = - WrapVulkanImage(device, &defaultDescriptor, memoryFd, defaultAllocationSize, - defaultMemoryTypeIndex, {signalFd}); - - // Create a second texture on |device| - wgpu::Texture copyDstTexture = device.CreateTexture(&defaultDescriptor); - - // Copy |deviceWrappedTexture| into |copyDstTexture| - SimpleCopyTextureToTexture(device, queue, deviceWrappedTexture, copyDstTexture); - - // Verify |copyDstTexture| sees changes from |secondDevice| - EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), copyDstTexture, 0, 0); - - IgnoreSignalSemaphore(device, deviceWrappedTexture); - } - - // Import a texture into |device| - // Copy color A into texture on |device| - // Import same texture into |secondDevice|, waiting on the copy signal - // Copy color B using Texture to Texture copy on |secondDevice| - // Import texture back into |device|, waiting on color B signal - // Verify texture contains color B - // If texture destination isn't synchronized, |secondDevice| could copy color B - // into the texture first, then |device| writes color A - TEST_P(VulkanImageWrappingUsageTests, CopyTextureToTextureDstSync) { - DAWN_SKIP_TEST_IF(UsesWire()); - - // Import the image on |device| - wgpu::Texture wrappedTexture = - WrapVulkanImage(device, &defaultDescriptor, defaultFd, defaultAllocationSize, - defaultMemoryTypeIndex, {}); - - // Clear |wrappedTexture| on |device| - ClearImage(device, wrappedTexture, {5 / 255.0f, 6 / 255.0f, 7 / 255.0f, 8 / 255.0f}); - - int signalFd = - dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(device.Get(), wrappedTexture.Get()); - - // Import the image to |secondDevice|, making sure we wait on |signalFd| - int memoryFd = GetMemoryFd(deviceVk, defaultAllocation); - wgpu::Texture secondDeviceWrappedTexture = - WrapVulkanImage(secondDevice, &defaultDescriptor, memoryFd, defaultAllocationSize, - defaultMemoryTypeIndex, {signalFd}); - - // Create a texture with color B on |secondDevice| - wgpu::Texture copySrcTexture = secondDevice.CreateTexture(&defaultDescriptor); - ClearImage(secondDevice, copySrcTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f}); - - // Copy color B on |secondDevice| - wgpu::Queue secondDeviceQueue = secondDevice.CreateQueue(); - SimpleCopyTextureToTexture(secondDevice, secondDeviceQueue, copySrcTexture, - secondDeviceWrappedTexture); - - // Re-import back into |device|, waiting on |secondDevice|'s signal - signalFd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD( - secondDevice.Get(), secondDeviceWrappedTexture.Get()); - memoryFd = GetMemoryFd(deviceVk, defaultAllocation); - - wgpu::Texture nextWrappedTexture = - WrapVulkanImage(device, &defaultDescriptor, memoryFd, defaultAllocationSize, - defaultMemoryTypeIndex, {signalFd}); - - // Verify |nextWrappedTexture| contains the color from our copy - EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), nextWrappedTexture, 0, 0); - - IgnoreSignalSemaphore(device, nextWrappedTexture); - } - - // Import a texture from |secondDevice| - // Issue a copy of the imported texture inside |device| to |copyDstBuffer| - // Verify the clear color from |secondDevice| is visible in |copyDstBuffer| - TEST_P(VulkanImageWrappingUsageTests, CopyTextureToBufferSrcSync) { - DAWN_SKIP_TEST_IF(UsesWire()); - - // 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 deviceWrappedTexture = - WrapVulkanImage(device, &defaultDescriptor, memoryFd, defaultAllocationSize, - defaultMemoryTypeIndex, {signalFd}); - - // Create a destination buffer on |device| - wgpu::BufferDescriptor bufferDesc; - bufferDesc.size = 4; - bufferDesc.usage = wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::CopySrc; - wgpu::Buffer copyDstBuffer = device.CreateBuffer(&bufferDesc); - - // Copy |deviceWrappedTexture| into |copyDstBuffer| + // Submits a 1x1x1 copy from source to destination + void SimpleCopyTextureToTexture(wgpu::Device device, + wgpu::Queue queue, + wgpu::Texture source, + wgpu::Texture destination) { wgpu::TextureCopyView copySrc; - copySrc.texture = deviceWrappedTexture; + copySrc.texture = source; copySrc.mipLevel = 0; copySrc.arrayLayer = 0; copySrc.origin = {0, 0, 0}; - wgpu::BufferCopyView copyDst; - copyDst.buffer = copyDstBuffer; - copyDst.offset = 0; - copyDst.rowPitch = 256; - copyDst.imageHeight = 0; - - wgpu::Extent3D copySize = {1, 1, 1}; - - wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); - encoder.CopyTextureToBuffer(©Src, ©Dst, ©Size); - wgpu::CommandBuffer commands = encoder.Finish(); - queue.Submit(1, &commands); - - // Verify |copyDstBuffer| sees changes from |secondDevice| - uint32_t expected = 0x04030201; - EXPECT_BUFFER_U32_EQ(expected, copyDstBuffer, 0); - - IgnoreSignalSemaphore(device, deviceWrappedTexture); - } - - // Import a texture into |device| - // Copy color A into texture on |device| - // Import same texture into |secondDevice|, waiting on the copy signal - // Copy color B using Buffer to Texture copy on |secondDevice| - // Import texture back into |device|, waiting on color B signal - // Verify texture contains color B - // If texture destination isn't synchronized, |secondDevice| could copy color B - // into the texture first, then |device| writes color A - TEST_P(VulkanImageWrappingUsageTests, CopyBufferToTextureDstSync) { - DAWN_SKIP_TEST_IF(UsesWire()); - - // Import the image on |device| - wgpu::Texture wrappedTexture = - WrapVulkanImage(device, &defaultDescriptor, defaultFd, defaultAllocationSize, - defaultMemoryTypeIndex, {}); - - // Clear |wrappedTexture| on |device| - ClearImage(device, wrappedTexture, {5 / 255.0f, 6 / 255.0f, 7 / 255.0f, 8 / 255.0f}); - - int signalFd = - dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(device.Get(), wrappedTexture.Get()); - - // Import the image to |secondDevice|, making sure we wait on |signalFd| - int memoryFd = GetMemoryFd(deviceVk, defaultAllocation); - wgpu::Texture secondDeviceWrappedTexture = - WrapVulkanImage(secondDevice, &defaultDescriptor, memoryFd, defaultAllocationSize, - defaultMemoryTypeIndex, {signalFd}); - - // Copy color B on |secondDevice| - wgpu::Queue secondDeviceQueue = secondDevice.CreateQueue(); - - // Create a buffer on |secondDevice| - wgpu::Buffer copySrcBuffer = - utils::CreateBufferFromData(secondDevice, wgpu::BufferUsage::CopySrc, {0x04030201}); - - // Copy |copySrcBuffer| into |secondDeviceWrappedTexture| - wgpu::BufferCopyView copySrc; - copySrc.buffer = copySrcBuffer; - copySrc.offset = 0; - copySrc.rowPitch = 256; - copySrc.imageHeight = 0; - wgpu::TextureCopyView copyDst; - copyDst.texture = secondDeviceWrappedTexture; + copyDst.texture = destination; copyDst.mipLevel = 0; copyDst.arrayLayer = 0; copyDst.origin = {0, 0, 0}; wgpu::Extent3D copySize = {1, 1, 1}; + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.CopyTextureToTexture(©Src, ©Dst, ©Size); + wgpu::CommandBuffer commands = encoder.Finish(); + + queue.Submit(1, &commands); + } +}; + +// Clear an image in |secondDevice| +// Verify clear color is visible in |device| +TEST_P(VulkanImageWrappingUsageTests, ClearImageAcrossDevices) { + DAWN_SKIP_TEST_IF(UsesWire()); + + // 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| + EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), nextWrappedTexture, 0, 0); + + IgnoreSignalSemaphore(device, nextWrappedTexture); +} + +// Import texture to |device| and |secondDevice| +// Clear image in |secondDevice| +// Verify clear color is visible in |device| +// Verify the very first import into |device| also sees the change, since it should +// alias the same memory +TEST_P(VulkanImageWrappingUsageTests, ClearImageAcrossDevicesAliased) { + DAWN_SKIP_TEST_IF(UsesWire()); + // Import the image on |device + wgpu::Texture wrappedTextureAlias = WrapVulkanImage( + device, &defaultDescriptor, defaultFd, defaultAllocationSize, defaultMemoryTypeIndex, {}); + + int memoryFd = GetMemoryFd(deviceVk, defaultAllocation); + + // 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 + 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| +// Verify clear color is not visible in |device| if we import the texture as not cleared +TEST_P(VulkanImageWrappingUsageTests, UnclearedTextureIsCleared) { + DAWN_SKIP_TEST_IF(UsesWire()); + + // 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}, false); + + // Verify |device| doesn't see the changes from |secondDevice| + EXPECT_PIXEL_RGBA8_EQ(RGBA8(0, 0, 0, 0), nextWrappedTexture, 0, 0); + + IgnoreSignalSemaphore(device, nextWrappedTexture); +} + +// Import a texture into |secondDevice| +// Issue a copy of the imported texture inside |device| to |copyDstTexture| +// Verify the clear color from |secondDevice| is visible in |copyDstTexture| +TEST_P(VulkanImageWrappingUsageTests, CopyTextureToTextureSrcSync) { + DAWN_SKIP_TEST_IF(UsesWire()); + + // 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 deviceWrappedTexture = + WrapVulkanImage(device, &defaultDescriptor, memoryFd, defaultAllocationSize, + defaultMemoryTypeIndex, {signalFd}); + + // Create a second texture on |device| + wgpu::Texture copyDstTexture = device.CreateTexture(&defaultDescriptor); + + // Copy |deviceWrappedTexture| into |copyDstTexture| + SimpleCopyTextureToTexture(device, queue, deviceWrappedTexture, copyDstTexture); + + // Verify |copyDstTexture| sees changes from |secondDevice| + EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), copyDstTexture, 0, 0); + + IgnoreSignalSemaphore(device, deviceWrappedTexture); +} + +// Import a texture into |device| +// Copy color A into texture on |device| +// Import same texture into |secondDevice|, waiting on the copy signal +// Copy color B using Texture to Texture copy on |secondDevice| +// Import texture back into |device|, waiting on color B signal +// Verify texture contains color B +// If texture destination isn't synchronized, |secondDevice| could copy color B +// into the texture first, then |device| writes color A +TEST_P(VulkanImageWrappingUsageTests, CopyTextureToTextureDstSync) { + DAWN_SKIP_TEST_IF(UsesWire()); + + // Import the image on |device| + wgpu::Texture wrappedTexture = WrapVulkanImage( + device, &defaultDescriptor, defaultFd, defaultAllocationSize, defaultMemoryTypeIndex, {}); + + // Clear |wrappedTexture| on |device| + ClearImage(device, wrappedTexture, {5 / 255.0f, 6 / 255.0f, 7 / 255.0f, 8 / 255.0f}); + + int signalFd = + dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(device.Get(), wrappedTexture.Get()); + + // Import the image to |secondDevice|, making sure we wait on |signalFd| + int memoryFd = GetMemoryFd(deviceVk, defaultAllocation); + wgpu::Texture secondDeviceWrappedTexture = + WrapVulkanImage(secondDevice, &defaultDescriptor, memoryFd, defaultAllocationSize, + defaultMemoryTypeIndex, {signalFd}); + + // Create a texture with color B on |secondDevice| + wgpu::Texture copySrcTexture = secondDevice.CreateTexture(&defaultDescriptor); + ClearImage(secondDevice, copySrcTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f}); + + // Copy color B on |secondDevice| + wgpu::Queue secondDeviceQueue = secondDevice.CreateQueue(); + SimpleCopyTextureToTexture(secondDevice, secondDeviceQueue, copySrcTexture, + secondDeviceWrappedTexture); + + // Re-import back into |device|, waiting on |secondDevice|'s signal + signalFd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(secondDevice.Get(), + secondDeviceWrappedTexture.Get()); + memoryFd = GetMemoryFd(deviceVk, defaultAllocation); + + wgpu::Texture nextWrappedTexture = + WrapVulkanImage(device, &defaultDescriptor, memoryFd, defaultAllocationSize, + defaultMemoryTypeIndex, {signalFd}); + + // Verify |nextWrappedTexture| contains the color from our copy + EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), nextWrappedTexture, 0, 0); + + IgnoreSignalSemaphore(device, nextWrappedTexture); +} + +// Import a texture from |secondDevice| +// Issue a copy of the imported texture inside |device| to |copyDstBuffer| +// Verify the clear color from |secondDevice| is visible in |copyDstBuffer| +TEST_P(VulkanImageWrappingUsageTests, CopyTextureToBufferSrcSync) { + DAWN_SKIP_TEST_IF(UsesWire()); + + // 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 deviceWrappedTexture = + WrapVulkanImage(device, &defaultDescriptor, memoryFd, defaultAllocationSize, + defaultMemoryTypeIndex, {signalFd}); + + // Create a destination buffer on |device| + wgpu::BufferDescriptor bufferDesc; + bufferDesc.size = 4; + bufferDesc.usage = wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::CopySrc; + wgpu::Buffer copyDstBuffer = device.CreateBuffer(&bufferDesc); + + // Copy |deviceWrappedTexture| into |copyDstBuffer| + wgpu::TextureCopyView copySrc; + copySrc.texture = deviceWrappedTexture; + copySrc.mipLevel = 0; + copySrc.arrayLayer = 0; + copySrc.origin = {0, 0, 0}; + + wgpu::BufferCopyView copyDst; + copyDst.buffer = copyDstBuffer; + copyDst.offset = 0; + copyDst.rowPitch = 256; + copyDst.imageHeight = 0; + + wgpu::Extent3D copySize = {1, 1, 1}; + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.CopyTextureToBuffer(©Src, ©Dst, ©Size); + wgpu::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); + + // Verify |copyDstBuffer| sees changes from |secondDevice| + uint32_t expected = 0x04030201; + EXPECT_BUFFER_U32_EQ(expected, copyDstBuffer, 0); + + IgnoreSignalSemaphore(device, deviceWrappedTexture); +} + +// Import a texture into |device| +// Copy color A into texture on |device| +// Import same texture into |secondDevice|, waiting on the copy signal +// Copy color B using Buffer to Texture copy on |secondDevice| +// Import texture back into |device|, waiting on color B signal +// Verify texture contains color B +// If texture destination isn't synchronized, |secondDevice| could copy color B +// into the texture first, then |device| writes color A +TEST_P(VulkanImageWrappingUsageTests, CopyBufferToTextureDstSync) { + DAWN_SKIP_TEST_IF(UsesWire()); + + // Import the image on |device| + wgpu::Texture wrappedTexture = WrapVulkanImage( + device, &defaultDescriptor, defaultFd, defaultAllocationSize, defaultMemoryTypeIndex, {}); + + // Clear |wrappedTexture| on |device| + ClearImage(device, wrappedTexture, {5 / 255.0f, 6 / 255.0f, 7 / 255.0f, 8 / 255.0f}); + + int signalFd = + dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(device.Get(), wrappedTexture.Get()); + + // Import the image to |secondDevice|, making sure we wait on |signalFd| + int memoryFd = GetMemoryFd(deviceVk, defaultAllocation); + wgpu::Texture secondDeviceWrappedTexture = + WrapVulkanImage(secondDevice, &defaultDescriptor, memoryFd, defaultAllocationSize, + defaultMemoryTypeIndex, {signalFd}); + + // Copy color B on |secondDevice| + wgpu::Queue secondDeviceQueue = secondDevice.CreateQueue(); + + // Create a buffer on |secondDevice| + wgpu::Buffer copySrcBuffer = + utils::CreateBufferFromData(secondDevice, wgpu::BufferUsage::CopySrc, {0x04030201}); + + // Copy |copySrcBuffer| into |secondDeviceWrappedTexture| + wgpu::BufferCopyView copySrc; + copySrc.buffer = copySrcBuffer; + copySrc.offset = 0; + copySrc.rowPitch = 256; + copySrc.imageHeight = 0; + + wgpu::TextureCopyView copyDst; + copyDst.texture = secondDeviceWrappedTexture; + copyDst.mipLevel = 0; + copyDst.arrayLayer = 0; + copyDst.origin = {0, 0, 0}; + + wgpu::Extent3D copySize = {1, 1, 1}; + + wgpu::CommandEncoder encoder = secondDevice.CreateCommandEncoder(); + encoder.CopyBufferToTexture(©Src, ©Dst, ©Size); + wgpu::CommandBuffer commands = encoder.Finish(); + secondDeviceQueue.Submit(1, &commands); + + // Re-import back into |device|, waiting on |secondDevice|'s signal + signalFd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(secondDevice.Get(), + secondDeviceWrappedTexture.Get()); + memoryFd = GetMemoryFd(deviceVk, defaultAllocation); + + wgpu::Texture nextWrappedTexture = + WrapVulkanImage(device, &defaultDescriptor, memoryFd, defaultAllocationSize, + defaultMemoryTypeIndex, {signalFd}); + + // Verify |nextWrappedTexture| contains the color from our copy + EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), nextWrappedTexture, 0, 0); + + IgnoreSignalSemaphore(device, nextWrappedTexture); +} + +// Import a texture from |secondDevice| +// Issue a copy of the imported texture inside |device| to |copyDstTexture| +// Issue second copy to |secondCopyDstTexture| +// Verify the clear color from |secondDevice| is visible in both copies +TEST_P(VulkanImageWrappingUsageTests, DoubleTextureUsage) { + DAWN_SKIP_TEST_IF(UsesWire()); + + // 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 deviceWrappedTexture = + WrapVulkanImage(device, &defaultDescriptor, memoryFd, defaultAllocationSize, + defaultMemoryTypeIndex, {signalFd}); + + // Create a second texture on |device| + wgpu::Texture copyDstTexture = device.CreateTexture(&defaultDescriptor); + + // Create a third texture on |device| + wgpu::Texture secondCopyDstTexture = device.CreateTexture(&defaultDescriptor); + + // Copy |deviceWrappedTexture| into |copyDstTexture| + SimpleCopyTextureToTexture(device, queue, deviceWrappedTexture, copyDstTexture); + + // Copy |deviceWrappedTexture| into |secondCopyDstTexture| + SimpleCopyTextureToTexture(device, queue, deviceWrappedTexture, secondCopyDstTexture); + + // Verify |copyDstTexture| sees changes from |secondDevice| + EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), copyDstTexture, 0, 0); + + // Verify |secondCopyDstTexture| sees changes from |secondDevice| + EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), secondCopyDstTexture, 0, 0); + + IgnoreSignalSemaphore(device, deviceWrappedTexture); +} + +// Tex A on device 3 (external export) +// Tex B on device 2 (external export) +// Tex C on device 1 (external export) +// Clear color for A on device 3 +// Copy A->B on device 3 +// Copy B->C on device 2 (wait on B from previous op) +// Copy C->D on device 1 (wait on C from previous op) +// Verify D has same color as A +TEST_P(VulkanImageWrappingUsageTests, ChainTextureCopy) { + DAWN_SKIP_TEST_IF(UsesWire()); + + // Close |defaultFd| since this test doesn't import it anywhere + close(defaultFd); + + // device 1 = |device| + // device 2 = |secondDevice| + // Create device 3 + dawn_native::vulkan::Device* thirdDeviceVk = reinterpret_cast( + backendAdapter->CreateDevice(&deviceDescriptor)); + wgpu::Device thirdDevice = wgpu::Device::Acquire(reinterpret_cast(thirdDeviceVk)); + + // Make queue for device 2 and 3 + wgpu::Queue secondDeviceQueue = secondDevice.CreateQueue(); + wgpu::Queue thirdDeviceQueue = thirdDevice.CreateQueue(); + + // Allocate memory for A, B, C + VkImage imageA; + VkDeviceMemory allocationA; + int memoryFdA; + VkDeviceSize allocationSizeA; + uint32_t memoryTypeIndexA; + CreateBindExportImage(thirdDeviceVk, 1, 1, VK_FORMAT_R8G8B8A8_UNORM, &imageA, &allocationA, + &allocationSizeA, &memoryTypeIndexA, &memoryFdA); + + VkImage imageB; + VkDeviceMemory allocationB; + int memoryFdB; + VkDeviceSize allocationSizeB; + uint32_t memoryTypeIndexB; + CreateBindExportImage(secondDeviceVk, 1, 1, VK_FORMAT_R8G8B8A8_UNORM, &imageB, &allocationB, + &allocationSizeB, &memoryTypeIndexB, &memoryFdB); + + VkImage imageC; + VkDeviceMemory allocationC; + int memoryFdC; + VkDeviceSize allocationSizeC; + uint32_t memoryTypeIndexC; + CreateBindExportImage(deviceVk, 1, 1, VK_FORMAT_R8G8B8A8_UNORM, &imageC, &allocationC, + &allocationSizeC, &memoryTypeIndexC, &memoryFdC); + + // Import TexA, TexB on device 3 + wgpu::Texture wrappedTexADevice3 = WrapVulkanImage(thirdDevice, &defaultDescriptor, memoryFdA, + allocationSizeA, memoryTypeIndexA, {}); + + wgpu::Texture wrappedTexBDevice3 = WrapVulkanImage(thirdDevice, &defaultDescriptor, memoryFdB, + allocationSizeB, memoryTypeIndexB, {}); + + // Clear TexA + ClearImage(thirdDevice, wrappedTexADevice3, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f}); + + // Copy A->B + SimpleCopyTextureToTexture(thirdDevice, thirdDeviceQueue, wrappedTexADevice3, + wrappedTexBDevice3); + + int signalFdTexBDevice3 = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD( + thirdDevice.Get(), wrappedTexBDevice3.Get()); + IgnoreSignalSemaphore(thirdDevice, wrappedTexADevice3); + + // Import TexB, TexC on device 2 + memoryFdB = GetMemoryFd(secondDeviceVk, allocationB); + wgpu::Texture wrappedTexBDevice2 = + WrapVulkanImage(secondDevice, &defaultDescriptor, memoryFdB, allocationSizeB, + memoryTypeIndexB, {signalFdTexBDevice3}); + + wgpu::Texture wrappedTexCDevice2 = WrapVulkanImage(secondDevice, &defaultDescriptor, memoryFdC, + allocationSizeC, memoryTypeIndexC, {}); + + // Copy B->C on device 2 + SimpleCopyTextureToTexture(secondDevice, secondDeviceQueue, wrappedTexBDevice2, + wrappedTexCDevice2); + + int signalFdTexCDevice2 = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD( + secondDevice.Get(), wrappedTexCDevice2.Get()); + IgnoreSignalSemaphore(secondDevice, wrappedTexBDevice2); + + // Import TexC on device 1 + memoryFdC = GetMemoryFd(deviceVk, allocationC); + wgpu::Texture wrappedTexCDevice1 = + WrapVulkanImage(device, &defaultDescriptor, memoryFdC, allocationSizeC, memoryTypeIndexC, + {signalFdTexCDevice2}); + + // Create TexD on device 1 + wgpu::Texture texD = device.CreateTexture(&defaultDescriptor); + + // Copy C->D on device 1 + SimpleCopyTextureToTexture(device, queue, wrappedTexCDevice1, texD); + + // Verify D matches clear color + EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), texD, 0, 0); + + thirdDeviceVk->GetFencedDeleter()->DeleteWhenUnused(imageA); + thirdDeviceVk->GetFencedDeleter()->DeleteWhenUnused(allocationA); + secondDeviceVk->GetFencedDeleter()->DeleteWhenUnused(imageB); + secondDeviceVk->GetFencedDeleter()->DeleteWhenUnused(allocationB); + deviceVk->GetFencedDeleter()->DeleteWhenUnused(imageC); + deviceVk->GetFencedDeleter()->DeleteWhenUnused(allocationC); + + IgnoreSignalSemaphore(device, wrappedTexCDevice1); +} + +// Tests a larger image is preserved when importing +// TODO(http://crbug.com/dawn/206): This fails on AMD +TEST_P(VulkanImageWrappingUsageTests, LargerImage) { + DAWN_SKIP_TEST_IF(UsesWire() || IsAMD()); + + close(defaultFd); + + wgpu::TextureDescriptor descriptor; + descriptor.dimension = wgpu::TextureDimension::e2D; + descriptor.size.width = 640; + descriptor.size.height = 480; + descriptor.size.depth = 1; + descriptor.arrayLayerCount = 1; + descriptor.sampleCount = 1; + descriptor.format = wgpu::TextureFormat::BGRA8Unorm; + descriptor.mipLevelCount = 1; + descriptor.usage = wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::CopySrc; + + // Fill memory with textures to trigger layout issues on AMD + std::vector textures; + for (int i = 0; i < 20; i++) { + textures.push_back(device.CreateTexture(&descriptor)); + } + + wgpu::Queue secondDeviceQueue = secondDevice.CreateQueue(); + + // Make an image on |secondDevice| + VkImage imageA; + VkDeviceMemory allocationA; + int memoryFdA; + VkDeviceSize allocationSizeA; + uint32_t memoryTypeIndexA; + CreateBindExportImage(secondDeviceVk, 640, 480, VK_FORMAT_R8G8B8A8_UNORM, &imageA, &allocationA, + &allocationSizeA, &memoryTypeIndexA, &memoryFdA); + + // Import the image on |secondDevice| + wgpu::Texture wrappedTexture = WrapVulkanImage(secondDevice, &descriptor, memoryFdA, + allocationSizeA, memoryTypeIndexA, {}); + + // Draw a non-trivial picture + int width = 640, height = 480, pixelSize = 4; + uint32_t rowPitch = Align(width * pixelSize, kTextureRowPitchAlignment); + uint32_t size = rowPitch * (height - 1) + width * pixelSize; + unsigned char data[size]; + for (int row = 0; row < height; row++) { + for (int col = 0; col < width; col++) { + float normRow = static_cast(row) / height; + float normCol = static_cast(col) / width; + float dist = sqrt(normRow * normRow + normCol * normCol) * 3; + dist = dist - static_cast(dist); + data[4 * (row * width + col)] = static_cast(dist * 255); + data[4 * (row * width + col) + 1] = static_cast(dist * 255); + data[4 * (row * width + col) + 2] = static_cast(dist * 255); + data[4 * (row * width + col) + 3] = 255; + } + } + + // Write the picture + { + wgpu::Buffer copySrcBuffer = + utils::CreateBufferFromData(secondDevice, data, size, wgpu::BufferUsage::CopySrc); + wgpu::BufferCopyView copySrc = utils::CreateBufferCopyView(copySrcBuffer, 0, rowPitch, 0); + wgpu::TextureCopyView copyDst = + utils::CreateTextureCopyView(wrappedTexture, 0, 0, {0, 0, 0}); + wgpu::Extent3D copySize = {width, height, 1}; + wgpu::CommandEncoder encoder = secondDevice.CreateCommandEncoder(); encoder.CopyBufferToTexture(©Src, ©Dst, ©Size); wgpu::CommandBuffer commands = encoder.Finish(); secondDeviceQueue.Submit(1, &commands); - - // Re-import back into |device|, waiting on |secondDevice|'s signal - signalFd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD( - secondDevice.Get(), secondDeviceWrappedTexture.Get()); - memoryFd = GetMemoryFd(deviceVk, defaultAllocation); - - wgpu::Texture nextWrappedTexture = - WrapVulkanImage(device, &defaultDescriptor, memoryFd, defaultAllocationSize, - defaultMemoryTypeIndex, {signalFd}); - - // Verify |nextWrappedTexture| contains the color from our copy - EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), nextWrappedTexture, 0, 0); - - IgnoreSignalSemaphore(device, nextWrappedTexture); } - // Import a texture from |secondDevice| - // Issue a copy of the imported texture inside |device| to |copyDstTexture| - // Issue second copy to |secondCopyDstTexture| - // Verify the clear color from |secondDevice| is visible in both copies - TEST_P(VulkanImageWrappingUsageTests, DoubleTextureUsage) { - DAWN_SKIP_TEST_IF(UsesWire()); + int signalFd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(secondDevice.Get(), + wrappedTexture.Get()); + int memoryFd = GetMemoryFd(secondDeviceVk, allocationA); - // Import the image on |secondDevice| - wgpu::Texture wrappedTexture = - WrapVulkanImage(secondDevice, &defaultDescriptor, defaultFd, defaultAllocationSize, - defaultMemoryTypeIndex, {}); + // Import the image on |device| + wgpu::Texture nextWrappedTexture = WrapVulkanImage( + device, &descriptor, memoryFd, allocationSizeA, memoryTypeIndexA, {signalFd}); - // Clear |wrappedTexture| on |secondDevice| - ClearImage(secondDevice, wrappedTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f}); + // Copy the image into a buffer for comparison + wgpu::BufferDescriptor copyDesc; + copyDesc.size = size; + copyDesc.usage = wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst; + wgpu::Buffer copyDstBuffer = device.CreateBuffer(©Desc); + { + wgpu::TextureCopyView copySrc = + utils::CreateTextureCopyView(nextWrappedTexture, 0, 0, {0, 0, 0}); + wgpu::BufferCopyView copyDst = utils::CreateBufferCopyView(copyDstBuffer, 0, rowPitch, 0); - int signalFd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(secondDevice.Get(), - wrappedTexture.Get()); + wgpu::Extent3D copySize = {width, height, 1}; - // Import the image to |device|, making sure we wait on |signalFd| - int memoryFd = GetMemoryFd(deviceVk, defaultAllocation); - wgpu::Texture deviceWrappedTexture = - WrapVulkanImage(device, &defaultDescriptor, memoryFd, defaultAllocationSize, - defaultMemoryTypeIndex, {signalFd}); - - // Create a second texture on |device| - wgpu::Texture copyDstTexture = device.CreateTexture(&defaultDescriptor); - - // Create a third texture on |device| - wgpu::Texture secondCopyDstTexture = device.CreateTexture(&defaultDescriptor); - - // Copy |deviceWrappedTexture| into |copyDstTexture| - SimpleCopyTextureToTexture(device, queue, deviceWrappedTexture, copyDstTexture); - - // Copy |deviceWrappedTexture| into |secondCopyDstTexture| - SimpleCopyTextureToTexture(device, queue, deviceWrappedTexture, secondCopyDstTexture); - - // Verify |copyDstTexture| sees changes from |secondDevice| - EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), copyDstTexture, 0, 0); - - // Verify |secondCopyDstTexture| sees changes from |secondDevice| - EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), secondCopyDstTexture, 0, 0); - - IgnoreSignalSemaphore(device, deviceWrappedTexture); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.CopyTextureToBuffer(©Src, ©Dst, ©Size); + wgpu::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); } - // Tex A on device 3 (external export) - // Tex B on device 2 (external export) - // Tex C on device 1 (external export) - // Clear color for A on device 3 - // Copy A->B on device 3 - // Copy B->C on device 2 (wait on B from previous op) - // Copy C->D on device 1 (wait on C from previous op) - // Verify D has same color as A - TEST_P(VulkanImageWrappingUsageTests, ChainTextureCopy) { - DAWN_SKIP_TEST_IF(UsesWire()); + // Check the image is not corrupted on |device| + EXPECT_BUFFER_U32_RANGE_EQ(reinterpret_cast(data), copyDstBuffer, 0, size / 4); - // Close |defaultFd| since this test doesn't import it anywhere - close(defaultFd); + IgnoreSignalSemaphore(device, nextWrappedTexture); + secondDeviceVk->GetFencedDeleter()->DeleteWhenUnused(imageA); + secondDeviceVk->GetFencedDeleter()->DeleteWhenUnused(allocationA); +} - // device 1 = |device| - // device 2 = |secondDevice| - // Create device 3 - dawn_native::vulkan::Device* thirdDeviceVk = reinterpret_cast( - backendAdapter->CreateDevice(&deviceDescriptor)); - wgpu::Device thirdDevice = - wgpu::Device::Acquire(reinterpret_cast(thirdDeviceVk)); - - // Make queue for device 2 and 3 - wgpu::Queue secondDeviceQueue = secondDevice.CreateQueue(); - wgpu::Queue thirdDeviceQueue = thirdDevice.CreateQueue(); - - // Allocate memory for A, B, C - VkImage imageA; - VkDeviceMemory allocationA; - int memoryFdA; - VkDeviceSize allocationSizeA; - uint32_t memoryTypeIndexA; - CreateBindExportImage(thirdDeviceVk, 1, 1, VK_FORMAT_R8G8B8A8_UNORM, &imageA, &allocationA, - &allocationSizeA, &memoryTypeIndexA, &memoryFdA); - - VkImage imageB; - VkDeviceMemory allocationB; - int memoryFdB; - VkDeviceSize allocationSizeB; - uint32_t memoryTypeIndexB; - CreateBindExportImage(secondDeviceVk, 1, 1, VK_FORMAT_R8G8B8A8_UNORM, &imageB, &allocationB, - &allocationSizeB, &memoryTypeIndexB, &memoryFdB); - - VkImage imageC; - VkDeviceMemory allocationC; - int memoryFdC; - VkDeviceSize allocationSizeC; - uint32_t memoryTypeIndexC; - CreateBindExportImage(deviceVk, 1, 1, VK_FORMAT_R8G8B8A8_UNORM, &imageC, &allocationC, - &allocationSizeC, &memoryTypeIndexC, &memoryFdC); - - // Import TexA, TexB on device 3 - wgpu::Texture wrappedTexADevice3 = WrapVulkanImage( - thirdDevice, &defaultDescriptor, memoryFdA, allocationSizeA, memoryTypeIndexA, {}); - - wgpu::Texture wrappedTexBDevice3 = WrapVulkanImage( - thirdDevice, &defaultDescriptor, memoryFdB, allocationSizeB, memoryTypeIndexB, {}); - - // Clear TexA - ClearImage(thirdDevice, wrappedTexADevice3, - {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f}); - - // Copy A->B - SimpleCopyTextureToTexture(thirdDevice, thirdDeviceQueue, wrappedTexADevice3, - wrappedTexBDevice3); - - int signalFdTexBDevice3 = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD( - thirdDevice.Get(), wrappedTexBDevice3.Get()); - IgnoreSignalSemaphore(thirdDevice, wrappedTexADevice3); - - // Import TexB, TexC on device 2 - memoryFdB = GetMemoryFd(secondDeviceVk, allocationB); - wgpu::Texture wrappedTexBDevice2 = - WrapVulkanImage(secondDevice, &defaultDescriptor, memoryFdB, allocationSizeB, - memoryTypeIndexB, {signalFdTexBDevice3}); - - wgpu::Texture wrappedTexCDevice2 = WrapVulkanImage( - secondDevice, &defaultDescriptor, memoryFdC, allocationSizeC, memoryTypeIndexC, {}); - - // Copy B->C on device 2 - SimpleCopyTextureToTexture(secondDevice, secondDeviceQueue, wrappedTexBDevice2, - wrappedTexCDevice2); - - int signalFdTexCDevice2 = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD( - secondDevice.Get(), wrappedTexCDevice2.Get()); - IgnoreSignalSemaphore(secondDevice, wrappedTexBDevice2); - - // Import TexC on device 1 - memoryFdC = GetMemoryFd(deviceVk, allocationC); - wgpu::Texture wrappedTexCDevice1 = - WrapVulkanImage(device, &defaultDescriptor, memoryFdC, allocationSizeC, - memoryTypeIndexC, {signalFdTexCDevice2}); - - // Create TexD on device 1 - wgpu::Texture texD = device.CreateTexture(&defaultDescriptor); - - // Copy C->D on device 1 - SimpleCopyTextureToTexture(device, queue, wrappedTexCDevice1, texD); - - // Verify D matches clear color - EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), texD, 0, 0); - - thirdDeviceVk->GetFencedDeleter()->DeleteWhenUnused(imageA); - thirdDeviceVk->GetFencedDeleter()->DeleteWhenUnused(allocationA); - secondDeviceVk->GetFencedDeleter()->DeleteWhenUnused(imageB); - secondDeviceVk->GetFencedDeleter()->DeleteWhenUnused(allocationB); - deviceVk->GetFencedDeleter()->DeleteWhenUnused(imageC); - deviceVk->GetFencedDeleter()->DeleteWhenUnused(allocationC); - - IgnoreSignalSemaphore(device, wrappedTexCDevice1); - } - - // Tests a larger image is preserved when importing - // TODO(http://crbug.com/dawn/206): This fails on AMD - TEST_P(VulkanImageWrappingUsageTests, LargerImage) { - DAWN_SKIP_TEST_IF(UsesWire() || IsAMD()); - - close(defaultFd); - - wgpu::TextureDescriptor descriptor; - descriptor.dimension = wgpu::TextureDimension::e2D; - descriptor.size.width = 640; - descriptor.size.height = 480; - descriptor.size.depth = 1; - descriptor.arrayLayerCount = 1; - descriptor.sampleCount = 1; - descriptor.format = wgpu::TextureFormat::BGRA8Unorm; - descriptor.mipLevelCount = 1; - descriptor.usage = wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::CopySrc; - - // Fill memory with textures to trigger layout issues on AMD - std::vector textures; - for (int i = 0; i < 20; i++) { - textures.push_back(device.CreateTexture(&descriptor)); - } - - wgpu::Queue secondDeviceQueue = secondDevice.CreateQueue(); - - // Make an image on |secondDevice| - VkImage imageA; - VkDeviceMemory allocationA; - int memoryFdA; - VkDeviceSize allocationSizeA; - uint32_t memoryTypeIndexA; - CreateBindExportImage(secondDeviceVk, 640, 480, VK_FORMAT_R8G8B8A8_UNORM, &imageA, - &allocationA, &allocationSizeA, &memoryTypeIndexA, &memoryFdA); - - // Import the image on |secondDevice| - wgpu::Texture wrappedTexture = WrapVulkanImage(secondDevice, &descriptor, memoryFdA, - allocationSizeA, memoryTypeIndexA, {}); - - // Draw a non-trivial picture - int width = 640, height = 480, pixelSize = 4; - uint32_t rowPitch = Align(width * pixelSize, kTextureRowPitchAlignment); - uint32_t size = rowPitch * (height - 1) + width * pixelSize; - unsigned char data[size]; - for (int row = 0; row < height; row++) { - for (int col = 0; col < width; col++) { - float normRow = static_cast(row) / height; - float normCol = static_cast(col) / width; - float dist = sqrt(normRow * normRow + normCol * normCol) * 3; - dist = dist - static_cast(dist); - data[4 * (row * width + col)] = static_cast(dist * 255); - data[4 * (row * width + col) + 1] = static_cast(dist * 255); - data[4 * (row * width + col) + 2] = static_cast(dist * 255); - data[4 * (row * width + col) + 3] = 255; - } - } - - // Write the picture - { - wgpu::Buffer copySrcBuffer = - utils::CreateBufferFromData(secondDevice, data, size, wgpu::BufferUsage::CopySrc); - wgpu::BufferCopyView copySrc = - utils::CreateBufferCopyView(copySrcBuffer, 0, rowPitch, 0); - wgpu::TextureCopyView copyDst = - utils::CreateTextureCopyView(wrappedTexture, 0, 0, {0, 0, 0}); - wgpu::Extent3D copySize = {width, height, 1}; - - wgpu::CommandEncoder encoder = secondDevice.CreateCommandEncoder(); - encoder.CopyBufferToTexture(©Src, ©Dst, ©Size); - wgpu::CommandBuffer commands = encoder.Finish(); - secondDeviceQueue.Submit(1, &commands); - } - - int signalFd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(secondDevice.Get(), - wrappedTexture.Get()); - int memoryFd = GetMemoryFd(secondDeviceVk, allocationA); - - // Import the image on |device| - wgpu::Texture nextWrappedTexture = WrapVulkanImage( - device, &descriptor, memoryFd, allocationSizeA, memoryTypeIndexA, {signalFd}); - - // Copy the image into a buffer for comparison - wgpu::BufferDescriptor copyDesc; - copyDesc.size = size; - copyDesc.usage = wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst; - wgpu::Buffer copyDstBuffer = device.CreateBuffer(©Desc); - { - wgpu::TextureCopyView copySrc = - utils::CreateTextureCopyView(nextWrappedTexture, 0, 0, {0, 0, 0}); - wgpu::BufferCopyView copyDst = - utils::CreateBufferCopyView(copyDstBuffer, 0, rowPitch, 0); - - wgpu::Extent3D copySize = {width, height, 1}; - - wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); - encoder.CopyTextureToBuffer(©Src, ©Dst, ©Size); - wgpu::CommandBuffer commands = encoder.Finish(); - queue.Submit(1, &commands); - } - - // Check the image is not corrupted on |device| - EXPECT_BUFFER_U32_RANGE_EQ(reinterpret_cast(data), copyDstBuffer, 0, size / 4); - - IgnoreSignalSemaphore(device, nextWrappedTexture); - secondDeviceVk->GetFencedDeleter()->DeleteWhenUnused(imageA); - secondDeviceVk->GetFencedDeleter()->DeleteWhenUnused(allocationA); - } - - DAWN_INSTANTIATE_TEST(VulkanImageWrappingValidationTests, VulkanBackend); - DAWN_INSTANTIATE_TEST(VulkanImageWrappingUsageTests, VulkanBackend); - -}} // namespace dawn_native::vulkan +DAWN_INSTANTIATE_TEST(VulkanImageWrappingValidationTests, VulkanBackend); +DAWN_INSTANTIATE_TEST(VulkanImageWrappingUsageTests, VulkanBackend);