| // Copyright 2021 The Dawn & Tint Authors |
| // |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions are met: |
| // |
| // 1. Redistributions of source code must retain the above copyright notice, this |
| // list of conditions and the following disclaimer. |
| // |
| // 2. Redistributions in binary form must reproduce the above copyright notice, |
| // this list of conditions and the following disclaimer in the documentation |
| // and/or other materials provided with the distribution. |
| // |
| // 3. Neither the name of the copyright holder nor the names of its |
| // contributors may be used to endorse or promote products derived from |
| // this software without specific prior written permission. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE |
| // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
| // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
| // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
| // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| #include <unistd.h> |
| |
| #include <memory> |
| #include <utility> |
| #include <vector> |
| |
| #include "dawn/native/vulkan/DeviceVk.h" |
| #include "dawn/native/vulkan/FencedDeleter.h" |
| #include "dawn/native/vulkan/ResourceMemoryAllocatorVk.h" |
| #include "dawn/native/vulkan/UtilsVulkan.h" |
| #include "dawn/tests/white_box/VulkanImageWrappingTests_OpaqueFD.h" |
| #include "gtest/gtest.h" |
| #include "partition_alloc/pointers/raw_ptr.h" |
| |
| namespace dawn::native::vulkan { |
| |
| class ExternalSemaphoreOpaqueFD : public VulkanImageWrappingTestBackend::ExternalSemaphore { |
| public: |
| explicit ExternalSemaphoreOpaqueFD(int handle) : mHandle(handle) {} |
| ~ExternalSemaphoreOpaqueFD() override { |
| if (mHandle != -1) { |
| close(mHandle); |
| } |
| } |
| int AcquireHandle() { |
| int handle = mHandle; |
| mHandle = -1; |
| return handle; |
| } |
| |
| private: |
| int mHandle = -1; |
| }; |
| |
| class ExternalTextureOpaqueFD : public VulkanImageWrappingTestBackend::ExternalTexture { |
| public: |
| ExternalTextureOpaqueFD(native::vulkan::Device* device, |
| int fd, |
| VkDeviceMemory allocation, |
| VkImage handle, |
| VkDeviceSize allocationSize, |
| uint32_t memoryTypeIndex) |
| : mDevice(device), |
| mFd(fd), |
| mAllocation(allocation), |
| mHandle(handle), |
| allocationSize(allocationSize), |
| memoryTypeIndex(memoryTypeIndex) {} |
| |
| ~ExternalTextureOpaqueFD() override { |
| if (mFd != -1) { |
| close(mFd); |
| } |
| if (mAllocation != VK_NULL_HANDLE) { |
| mDevice->GetFencedDeleter()->DeleteWhenUnused(mAllocation); |
| } |
| if (mHandle != VK_NULL_HANDLE) { |
| mDevice->GetFencedDeleter()->DeleteWhenUnused(mHandle); |
| } |
| } |
| |
| int Dup() const { return dup(mFd); } |
| |
| private: |
| raw_ptr<native::vulkan::Device> mDevice; |
| int mFd = -1; |
| VkDeviceMemory mAllocation = VK_NULL_HANDLE; |
| VkImage mHandle = VK_NULL_HANDLE; |
| |
| public: |
| const VkDeviceSize allocationSize; |
| const uint32_t memoryTypeIndex; |
| }; |
| |
| class VulkanImageWrappingTestBackendOpaqueFD : public VulkanImageWrappingTestBackend { |
| public: |
| explicit VulkanImageWrappingTestBackendOpaqueFD(const wgpu::Device& device) : mDevice(device) { |
| mDeviceVk = native::vulkan::ToBackend(native::FromAPI(device.Get())); |
| } |
| |
| bool SupportsTestParams(const TestParams& params) const override { |
| return mDeviceVk->GetDeviceInfo().HasExt(DeviceExt::ExternalMemoryFD) && |
| (!params.useDedicatedAllocation || |
| mDeviceVk->GetDeviceInfo().HasExt(DeviceExt::DedicatedAllocation)); |
| } |
| |
| std::unique_ptr<ExternalTexture> CreateTexture(uint32_t width, |
| uint32_t height, |
| wgpu::TextureFormat format, |
| wgpu::TextureUsage usage) override { |
| EXPECT_EQ(format, wgpu::TextureFormat::RGBA8Unorm); |
| VkFormat vulkanFormat = VK_FORMAT_R8G8B8A8_UNORM; |
| |
| VkImage handle; |
| ::VkResult result = CreateImage(mDeviceVk, width, height, vulkanFormat, &handle); |
| EXPECT_EQ(result, VK_SUCCESS) << "Failed to create external image"; |
| |
| VkDeviceMemory allocation; |
| VkDeviceSize allocationSize; |
| uint32_t memoryTypeIndex; |
| ::VkResult resultBool = |
| AllocateMemory(mDeviceVk, handle, &allocation, &allocationSize, &memoryTypeIndex); |
| EXPECT_EQ(resultBool, VK_SUCCESS) << "Failed to allocate external memory"; |
| |
| result = BindMemory(mDeviceVk, handle, allocation); |
| EXPECT_EQ(result, VK_SUCCESS) << "Failed to bind image memory"; |
| |
| int fd = GetMemoryFd(mDeviceVk, allocation); |
| |
| return std::make_unique<ExternalTextureOpaqueFD>(mDeviceVk, fd, allocation, handle, |
| allocationSize, memoryTypeIndex); |
| } |
| |
| wgpu::Texture WrapImage(const wgpu::Device& device, |
| const ExternalTexture* texture, |
| const ExternalImageDescriptorVkForTesting& descriptor, |
| std::vector<std::unique_ptr<ExternalSemaphore>> semaphores) override { |
| const ExternalTextureOpaqueFD* textureOpaqueFD = |
| static_cast<const ExternalTextureOpaqueFD*>(texture); |
| std::vector<int> waitFDs; |
| for (auto& semaphore : semaphores) { |
| waitFDs.push_back( |
| static_cast<ExternalSemaphoreOpaqueFD*>(semaphore.get())->AcquireHandle()); |
| } |
| |
| ExternalImageDescriptorOpaqueFD descriptorOpaqueFD; |
| *static_cast<ExternalImageDescriptorVk*>(&descriptorOpaqueFD) = descriptor; |
| descriptorOpaqueFD.memoryFD = textureOpaqueFD->Dup(); |
| descriptorOpaqueFD.allocationSize = textureOpaqueFD->allocationSize; |
| descriptorOpaqueFD.memoryTypeIndex = textureOpaqueFD->memoryTypeIndex; |
| descriptorOpaqueFD.waitFDs = std::move(waitFDs); |
| |
| if (GetParam().detectDedicatedAllocation) { |
| descriptorOpaqueFD.dedicatedAllocation = NeedsDedicatedAllocation::Detect; |
| } else if (GetParam().useDedicatedAllocation) { |
| descriptorOpaqueFD.dedicatedAllocation = NeedsDedicatedAllocation::Yes; |
| } else { |
| descriptorOpaqueFD.dedicatedAllocation = NeedsDedicatedAllocation::No; |
| } |
| |
| return wgpu::Texture::Acquire( |
| native::vulkan::WrapVulkanImage(device.Get(), &descriptorOpaqueFD)); |
| } |
| |
| bool ExportImage(const wgpu::Texture& texture, |
| ExternalImageExportInfoVkForTesting* exportInfo) override { |
| ExternalImageExportInfoOpaqueFD infoOpaqueFD; |
| bool success = ExportVulkanImage(texture.Get(), VK_IMAGE_LAYOUT_UNDEFINED, &infoOpaqueFD); |
| |
| *static_cast<ExternalImageExportInfoVk*>(exportInfo) = infoOpaqueFD; |
| for (int fd : infoOpaqueFD.semaphoreHandles) { |
| EXPECT_NE(fd, -1); |
| exportInfo->semaphores.push_back(std::make_unique<ExternalSemaphoreOpaqueFD>(fd)); |
| } |
| |
| return success; |
| } |
| |
| private: |
| // Creates a VkImage with external memory |
| ::VkResult CreateImage(native::vulkan::Device* deviceVk, |
| uint32_t width, |
| uint32_t height, |
| VkFormat format, |
| VkImage* image) { |
| 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 = nullptr; |
| 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; |
| PNextChainBuilder createChain(&createInfo); |
| |
| VkExternalMemoryImageCreateInfoKHR externalInfo; |
| externalInfo.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR; |
| createChain.Add(&externalInfo, VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO_KHR); |
| |
| return deviceVk->fn.CreateImage(deviceVk->GetVkDevice(), &createInfo, nullptr, &**image); |
| } |
| |
| // Allocates memory for an image |
| ::VkResult AllocateMemory(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); |
| |
| int bestType = deviceVk->GetResourceMemoryAllocator()->FindBestTypeIndex( |
| requirements, MemoryKind::Opaque); |
| |
| VkMemoryAllocateInfo allocateInfo; |
| allocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; |
| allocateInfo.pNext = nullptr; |
| allocateInfo.allocationSize = requirements.size; |
| allocateInfo.memoryTypeIndex = static_cast<uint32_t>(bestType); |
| PNextChainBuilder allocateChain(&allocateInfo); |
| |
| // Import memory from file descriptor |
| VkExportMemoryAllocateInfoKHR externalInfo; |
| externalInfo.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR; |
| allocateChain.Add(&externalInfo, VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_KHR); |
| |
| // Use a dedicated memory allocation if testing that path. |
| VkMemoryDedicatedAllocateInfo dedicatedInfo; |
| if (GetParam().useDedicatedAllocation) { |
| dedicatedInfo.image = handle; |
| dedicatedInfo.buffer = VkBuffer{}; |
| allocateChain.Add(&dedicatedInfo, VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO); |
| } |
| |
| *allocationSize = allocateInfo.allocationSize; |
| *memoryTypeIndex = allocateInfo.memoryTypeIndex; |
| |
| return deviceVk->fn.AllocateMemory(deviceVk->GetVkDevice(), &allocateInfo, nullptr, |
| &**allocation); |
| } |
| |
| // Binds memory to an image |
| ::VkResult BindMemory(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(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(native::vulkan::Device* deviceVk, |
| uint32_t width, |
| uint32_t height, |
| VkFormat format, |
| VkImage* handle, |
| VkDeviceMemory* allocation, |
| VkDeviceSize* allocationSize, |
| uint32_t* memoryTypeIndex, |
| int* memoryFd) {} |
| |
| wgpu::Device mDevice; |
| raw_ptr<native::vulkan::Device> mDeviceVk; |
| }; |
| |
| std::unique_ptr<VulkanImageWrappingTestBackend> CreateOpaqueFDBackend(const wgpu::Device& device) { |
| return std::make_unique<VulkanImageWrappingTestBackendOpaqueFD>(device); |
| } |
| |
| } // namespace dawn::native::vulkan |