| // Copyright 2023 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 "dawn/native/vulkan/SharedTextureMemoryVk.h" |
| |
| #include <algorithm> |
| #include <utility> |
| #include <vector> |
| |
| #include "dawn/native/ChainUtils.h" |
| #include "dawn/native/Instance.h" |
| #include "dawn/native/SystemHandle.h" |
| #include "dawn/native/vulkan/DeviceVk.h" |
| #include "dawn/native/vulkan/FencedDeleter.h" |
| #include "dawn/native/vulkan/PhysicalDeviceVk.h" |
| #include "dawn/native/vulkan/ResourceMemoryAllocatorVk.h" |
| #include "dawn/native/vulkan/SharedFenceVk.h" |
| #include "dawn/native/vulkan/TextureVk.h" |
| #include "dawn/native/vulkan/UtilsVulkan.h" |
| #include "dawn/native/vulkan/VulkanError.h" |
| #include "dawn/native/wgpu_structs_autogen.h" |
| |
| #if DAWN_PLATFORM_IS(ANDROID) |
| #include <android/hardware_buffer.h> |
| |
| #include "dawn/native/AHBFunctions.h" |
| #endif // DAWN_PLATFORM_IS(ANDROID) |
| |
| namespace dawn::native::vulkan { |
| |
| namespace { |
| |
| #if DAWN_PLATFORM_IS(LINUX) |
| |
| // Encoding from <drm/drm_fourcc.h> |
| constexpr uint32_t DrmFourccCode(uint32_t a, uint32_t b, uint32_t c, uint32_t d) { |
| return a | (b << 8) | (c << 16) | (d << 24); |
| } |
| |
| constexpr uint32_t DrmFourccCode(char a, char b, char c, char d) { |
| return DrmFourccCode(static_cast<uint32_t>(a), static_cast<uint32_t>(b), |
| static_cast<uint32_t>(c), static_cast<uint32_t>(d)); |
| } |
| |
| constexpr auto kDrmFormatR8 = DrmFourccCode('R', '8', ' ', ' '); /* [7:0] R */ |
| constexpr auto kDrmFormatGR88 = |
| DrmFourccCode('G', 'R', '8', '8'); /* [15:0] G:R 8:8 little endian */ |
| constexpr auto kDrmFormatXRGB8888 = |
| DrmFourccCode('X', 'R', '2', '4'); /* [15:0] x:R:G:B 8:8:8:8 little endian */ |
| constexpr auto kDrmFormatXBGR8888 = |
| DrmFourccCode('X', 'B', '2', '4'); /* [15:0] x:B:G:R 8:8:8:8 little endian */ |
| constexpr auto kDrmFormatARGB8888 = |
| DrmFourccCode('A', 'R', '2', '4'); /* [31:0] A:R:G:B 8:8:8:8 little endian */ |
| constexpr auto kDrmFormatABGR8888 = |
| DrmFourccCode('A', 'B', '2', '4'); /* [31:0] A:B:G:R 8:8:8:8 little endian */ |
| constexpr auto kDrmFormatABGR2101010 = |
| DrmFourccCode('A', 'B', '3', '0'); /* [31:0] A:B:G:R 2:10:10:10 little endian */ |
| constexpr auto kDrmFormatABGR16161616F = |
| DrmFourccCode('A', 'B', '4', 'H'); /* [63:0] A:B:G:R 16:16:16:16 little endian */ |
| constexpr auto kDrmFormatNV12 = DrmFourccCode('N', 'V', '1', '2'); /* 2x2 subsampled Cr:Cb plane */ |
| |
| ResultOrError<wgpu::TextureFormat> FormatFromDrmFormat(uint32_t drmFormat) { |
| switch (drmFormat) { |
| case kDrmFormatR8: |
| return wgpu::TextureFormat::R8Unorm; |
| case kDrmFormatGR88: |
| return wgpu::TextureFormat::RG8Unorm; |
| case kDrmFormatXRGB8888: |
| case kDrmFormatARGB8888: |
| return wgpu::TextureFormat::BGRA8Unorm; |
| case kDrmFormatXBGR8888: |
| case kDrmFormatABGR8888: |
| return wgpu::TextureFormat::RGBA8Unorm; |
| case kDrmFormatABGR2101010: |
| return wgpu::TextureFormat::RGB10A2Unorm; |
| case kDrmFormatABGR16161616F: |
| return wgpu::TextureFormat::RGBA16Float; |
| case kDrmFormatNV12: |
| return wgpu::TextureFormat::R8BG8Biplanar420Unorm; |
| default: |
| return DAWN_VALIDATION_ERROR("Unsupported drm format %x.", drmFormat); |
| } |
| } |
| |
| #endif // DAWN_PLATFORM_IS(LINUX) |
| |
| // Creates a VkImage with VkExternalMemoryImageCreateInfo::handlesTypes set to |
| // `externalMemoryHandleTypeFlagBits`. The rest of the parameters are computed from `properties` |
| // and `imageFormatInfo`. Additional structs may be chained by passing them in the varardic |
| // parameter args. |
| template <typename... AdditionalChains> |
| ResultOrError<VkImage> CreateExternalVkImage( |
| Device* device, |
| const SharedTextureMemoryProperties& properties, |
| const VkPhysicalDeviceImageFormatInfo2& imageFormatInfo, |
| VkExternalMemoryHandleTypeFlagBits externalMemoryHandleTypeFlagBits, |
| AdditionalChains*... additionalChains) { |
| VkImageCreateInfo createInfo = {}; |
| createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; |
| createInfo.flags = imageFormatInfo.flags; |
| createInfo.imageType = imageFormatInfo.type; |
| createInfo.format = imageFormatInfo.format; |
| createInfo.extent = {properties.size.width, properties.size.height, 1}; |
| createInfo.mipLevels = 1; |
| createInfo.arrayLayers = properties.size.depthOrArrayLayers; |
| createInfo.samples = VK_SAMPLE_COUNT_1_BIT; |
| createInfo.tiling = imageFormatInfo.tiling; |
| createInfo.usage = imageFormatInfo.usage; |
| createInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; |
| createInfo.queueFamilyIndexCount = 0; |
| createInfo.pQueueFamilyIndices = nullptr; |
| createInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; |
| |
| VkExternalMemoryImageCreateInfo externalMemoryImageCreateInfo = {}; |
| externalMemoryImageCreateInfo.handleTypes = externalMemoryHandleTypeFlagBits; |
| |
| PNextChainBuilder createInfoChain(&createInfo); |
| createInfoChain.Add(&externalMemoryImageCreateInfo, |
| VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO); |
| |
| (createInfoChain.Add(additionalChains), ...); |
| |
| // Create the VkImage. |
| VkImage vkImage; |
| DAWN_TRY(CheckVkSuccess( |
| device->fn.CreateImage(device->GetVkDevice(), &createInfo, nullptr, &*vkImage), |
| "vkCreateImage")); |
| return vkImage; |
| } |
| |
| // Add `VkPhysicalDeviceExternalImageFormatInfo` and `additionalChains` to `imageFormatInfo` and |
| // check this memory type and format combination can be imported. |
| template <typename... AdditionalChains> |
| MaybeError CheckExternalImageFormatSupport( |
| Device* device, |
| const SharedTextureMemoryProperties& properties, |
| VkPhysicalDeviceImageFormatInfo2* imageFormatInfo, |
| VkExternalMemoryHandleTypeFlagBits externalMemoryHandleTypeFlagBits, |
| AdditionalChains*... additionalChains) { |
| VkPhysicalDeviceExternalImageFormatInfo externalImageFormatInfo = {}; |
| externalImageFormatInfo.handleType = externalMemoryHandleTypeFlagBits; |
| |
| PNextChainBuilder imageFormatInfoChain(imageFormatInfo); |
| imageFormatInfoChain.Add(&externalImageFormatInfo, |
| VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO); |
| (imageFormatInfoChain.Add(additionalChains), ...); |
| |
| VkImageFormatProperties2 imageFormatProps = {}; |
| imageFormatProps.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2; |
| |
| VkExternalImageFormatProperties externalImageFormatProps = {}; |
| |
| PNextChainBuilder imageFormatPropsChain(&imageFormatProps); |
| imageFormatPropsChain.Add(&externalImageFormatProps, |
| VK_STRUCTURE_TYPE_EXTERNAL_IMAGE_FORMAT_PROPERTIES); |
| |
| DAWN_TRY_CONTEXT( |
| CheckVkSuccess(device->fn.GetPhysicalDeviceImageFormatProperties2( |
| ToBackend(device->GetPhysicalDevice())->GetVkPhysicalDevice(), |
| imageFormatInfo, &imageFormatProps), |
| "vkGetPhysicalDeviceImageFormatProperties"), |
| "checking import support for external memory type %x with %s %s\n", |
| externalMemoryHandleTypeFlagBits, properties.format, properties.usage); |
| |
| VkExternalMemoryFeatureFlags featureFlags = |
| externalImageFormatProps.externalMemoryProperties.externalMemoryFeatures; |
| DAWN_INVALID_IF(!(featureFlags & VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT), |
| "Vulkan memory is not importable."); |
| |
| return {}; |
| } |
| |
| // Add `additionalChains` to `memoryAllocateInfo` and call vkAllocateMemory. |
| template <typename... AdditionalChains> |
| ResultOrError<VkDeviceMemory> AllocateDeviceMemory(Device* device, |
| VkMemoryAllocateInfo* memoryAllocateInfo, |
| AdditionalChains*... additionalChains) { |
| PNextChainBuilder memoryAllocateInfoChain(memoryAllocateInfo); |
| |
| (memoryAllocateInfoChain.Add(additionalChains), ...); |
| |
| VkDeviceMemory vkDeviceMemory; |
| DAWN_TRY(CheckVkSuccess(device->fn.AllocateMemory(device->GetVkDevice(), memoryAllocateInfo, |
| nullptr, &*vkDeviceMemory), |
| "vkAllocateMemory")); |
| return vkDeviceMemory; |
| } |
| |
| } // namespace |
| |
| // static |
| ResultOrError<Ref<SharedTextureMemory>> SharedTextureMemory::Create( |
| Device* device, |
| StringView label, |
| const SharedTextureMemoryDmaBufDescriptor* descriptor) { |
| #if DAWN_PLATFORM_IS(LINUX) |
| VkDevice vkDevice = device->GetVkDevice(); |
| VkPhysicalDevice vkPhysicalDevice = |
| ToBackend(device->GetPhysicalDevice())->GetVkPhysicalDevice(); |
| |
| const VkExternalMemoryHandleTypeFlagBits handleType = |
| VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT; |
| |
| DAWN_INVALID_IF(descriptor->size.depthOrArrayLayers != 1, "depthOrArrayLayers was not 1."); |
| |
| SharedTextureMemoryProperties properties; |
| properties.size = {descriptor->size.width, descriptor->size.height, |
| descriptor->size.depthOrArrayLayers}; |
| |
| DAWN_TRY_ASSIGN(properties.format, FormatFromDrmFormat(descriptor->drmFormat)); |
| |
| properties.usage = wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst | |
| wgpu::TextureUsage::TextureBinding | wgpu::TextureUsage::StorageBinding | |
| wgpu::TextureUsage::RenderAttachment; |
| |
| // Create the SharedTextureMemory object. |
| Ref<SharedTextureMemory> sharedTextureMemory = |
| SharedTextureMemory::Create(device, label, properties, VK_QUEUE_FAMILY_EXTERNAL_KHR); |
| |
| // Reflect properties to reify them. |
| sharedTextureMemory->APIGetProperties(&properties); |
| |
| const Format* internalFormat = nullptr; |
| DAWN_TRY_ASSIGN(internalFormat, device->GetInternalFormat(properties.format)); |
| |
| const auto& compatibleViewFormats = device->GetCompatibleViewFormats(*internalFormat); |
| |
| VkFormat vkFormat = VulkanImageFormat(device, properties.format); |
| |
| // Usage flags to create the image with. |
| VkImageUsageFlags vkUsageFlags = VulkanImageUsage(device, properties.usage, *internalFormat); |
| |
| // Number of memory planes in the image which will be queried from the DRM modifier. |
| uint32_t memoryPlaneCount; |
| |
| // Info describing the image import. We will use this to check the import is valid, and then |
| // perform the actual VkImage creation. |
| VkPhysicalDeviceImageFormatInfo2 imageFormatInfo = {}; |
| // List of view formats the image can be created. |
| std::array<VkFormat, 3> viewFormats; |
| VkImageFormatListCreateInfo imageFormatListInfo = {}; |
| imageFormatListInfo.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO; |
| |
| // The Vulkan spec seems to be too strict in that: |
| // - use of DRM modifiers requires passing an image format list |
| // - that image format list can't have sRGB formats if the usage has StorageBinding |
| // In practice, it's "fine" to create a normal VkImage with StorageBinding and sRGB in the |
| // format list, and the VVL here may be wrong. See also see |
| // https://github.com/gpuweb/gpuweb/issues/4426. The support check may be unnecessarily strict. |
| // TODO(crbug.com/dawn/2304): Follow up with the Vulkan spec and try to lift this. |
| // Here, we set `addViewFormats` after checking for support to work around the strictness. |
| bool addViewFormats = false; |
| |
| // Validate that the import is valid. |
| { |
| // Verify plane count for the modifier. |
| // https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkDrmFormatModifierPropertiesEXT.html#_description |
| VkDrmFormatModifierPropertiesEXT drmModifierProps; |
| DAWN_TRY_ASSIGN(drmModifierProps, |
| GetFormatModifierProps(device->fn, vkPhysicalDevice, vkFormat, |
| descriptor->drmModifier)); |
| memoryPlaneCount = drmModifierProps.drmFormatModifierPlaneCount; |
| if (drmModifierProps.drmFormatModifier == 0 /* DRM_FORMAT_MOD_LINEAR */) { |
| uint32_t formatPlaneCount = GetAspectCount(internalFormat->aspects); |
| DAWN_INVALID_IF(memoryPlaneCount != formatPlaneCount, |
| "DRM format plane count (%u) must match the format plane count (%u) if " |
| "drmModifier is DRM_FORMAT_MOD_LINEAR.", |
| memoryPlaneCount, formatPlaneCount); |
| } |
| DAWN_INVALID_IF( |
| memoryPlaneCount != descriptor->planeCount, |
| "Memory plane count (%x) for drm format (%u) and modifier (%u) specify a plane " |
| "count of %u which " |
| "does not match the provided plane count (%u)", |
| vkFormat, descriptor->drmFormat, descriptor->drmModifier, memoryPlaneCount, |
| descriptor->planeCount); |
| DAWN_INVALID_IF(memoryPlaneCount == 0, "Memory plane count must not be 0"); |
| DAWN_INVALID_IF(memoryPlaneCount > kMaxPlanesPerFormat, |
| "Memory plane count (%u) must not exceed %u.", memoryPlaneCount, |
| kMaxPlanesPerFormat); |
| |
| // Verify that the format modifier of the external memory and the requested Vulkan format |
| // are actually supported together in a dma-buf import. |
| imageFormatInfo.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2; |
| imageFormatInfo.format = vkFormat; |
| imageFormatInfo.type = VK_IMAGE_TYPE_2D; |
| imageFormatInfo.tiling = VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT; |
| imageFormatInfo.usage = vkUsageFlags; |
| imageFormatInfo.flags = 0; |
| |
| VkPhysicalDeviceImageDrmFormatModifierInfoEXT drmModifierInfo = {}; |
| drmModifierInfo.sType = |
| VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_DRM_FORMAT_MODIFIER_INFO_EXT; |
| drmModifierInfo.drmFormatModifier = descriptor->drmModifier; |
| drmModifierInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; |
| |
| constexpr wgpu::TextureUsage kUsageRequiringView = wgpu::TextureUsage::RenderAttachment | |
| wgpu::TextureUsage::TextureBinding | |
| wgpu::TextureUsage::StorageBinding; |
| const bool mayNeedView = (properties.usage & kUsageRequiringView) != 0; |
| |
| if (mayNeedView) { |
| // Add the mutable format bit for view reinterpretation. |
| imageFormatInfo.flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT; |
| |
| // Append the list of view formats the image must be compatible with. |
| if (device->GetDeviceInfo().HasExt(DeviceExt::ImageFormatList)) { |
| if (internalFormat->IsMultiPlanar()) { |
| viewFormats = { |
| VulkanImageFormat(device, |
| internalFormat->GetAspectInfo(Aspect::Plane0).format), |
| VulkanImageFormat(device, |
| internalFormat->GetAspectInfo(Aspect::Plane1).format)}; |
| imageFormatListInfo.viewFormatCount = 2; |
| } else { |
| // Pass the format as the one and only allowed view format. |
| // Use of VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT requires passing a non-zero |
| // list. |
| const bool needsBGRA8UnormStoragePolyfill = |
| properties.format == wgpu::TextureFormat::BGRA8Unorm && |
| (properties.usage & wgpu::TextureUsage::StorageBinding); |
| if (compatibleViewFormats.empty()) { |
| DAWN_ASSERT(!needsBGRA8UnormStoragePolyfill); |
| viewFormats = {vkFormat}; |
| imageFormatListInfo.viewFormatCount = 1; |
| } else { |
| viewFormats[imageFormatListInfo.viewFormatCount++] = vkFormat; |
| if (needsBGRA8UnormStoragePolyfill) { |
| viewFormats[imageFormatListInfo.viewFormatCount++] = |
| VK_FORMAT_R8G8B8A8_UNORM; |
| } |
| addViewFormats = true; |
| } |
| } |
| imageFormatListInfo.pViewFormats = viewFormats.data(); |
| } |
| } |
| |
| if (imageFormatListInfo.viewFormatCount > 0) { |
| DAWN_TRY_CONTEXT( |
| CheckExternalImageFormatSupport(device, properties, &imageFormatInfo, handleType, |
| &drmModifierInfo, &imageFormatListInfo), |
| "checking import support for fd import of dma buf"); |
| } else { |
| DAWN_TRY_CONTEXT(CheckExternalImageFormatSupport(device, properties, &imageFormatInfo, |
| handleType, &drmModifierInfo), |
| "checking import support for fd import of dma buf"); |
| } |
| } |
| |
| // Validate that there is a single FD. If there is more than one FD, Dawn will need to validate |
| // the format has VK_FORMAT_FEATURE_DISJOINT_BIT, create the VkImage with |
| // VK_IMAGE_CREATE_DISJOINT_BIT, and separately bind the image planes to memory. Dawn doesn't |
| // support use of VK_IMAGE_CREATE_DISJOINT_BIT currently. See crbug.com/42240514. |
| int fd = descriptor->planes[0].fd; |
| for (uint32_t i = 1; i < descriptor->planeCount; ++i) { |
| DAWN_INVALID_IF(descriptor->planes[i].fd != fd, |
| "descriptor->planes[%u].fd (%i) does not match other plane fd (%i). All " |
| "fds must be the same.", |
| i, descriptor->planes[i].fd, fd); |
| } |
| |
| // Don't add the view format if backend validation is enabled, otherwise most image creations |
| // will fail with VVL. This view format is only needed for sRGB reinterpretation. |
| // TODO(crbug.com/dawn/2304): Investigate if this is a bug in VVL. |
| if (addViewFormats && !device->GetAdapter()->GetInstance()->IsBackendValidationEnabled()) { |
| DAWN_ASSERT(compatibleViewFormats.size() == 1u); |
| viewFormats[imageFormatListInfo.viewFormatCount++] = |
| VulkanImageFormat(device, compatibleViewFormats[0]->format); |
| } |
| |
| // Create the VkImage for the import. |
| { |
| std::array<VkSubresourceLayout, kMaxPlanesPerFormat> planeLayouts; |
| for (uint32_t plane = 0u; plane < memoryPlaneCount; ++plane) { |
| planeLayouts[plane].offset = descriptor->planes[plane].offset; |
| planeLayouts[plane].size = 0; // VK_EXT_image_drm_format_modifier mandates size = 0. |
| planeLayouts[plane].rowPitch = descriptor->planes[plane].stride; |
| planeLayouts[plane].arrayPitch = 0; // Not an array texture |
| planeLayouts[plane].depthPitch = 0; // Not a depth texture |
| } |
| |
| VkImageDrmFormatModifierExplicitCreateInfoEXT explicitCreateInfo = {}; |
| explicitCreateInfo.sType = |
| VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_EXPLICIT_CREATE_INFO_EXT; |
| explicitCreateInfo.drmFormatModifier = descriptor->drmModifier; |
| explicitCreateInfo.drmFormatModifierPlaneCount = memoryPlaneCount; |
| explicitCreateInfo.pPlaneLayouts = planeLayouts.data(); |
| |
| VkImage vkImage; |
| DAWN_TRY_ASSIGN(vkImage, |
| CreateExternalVkImage(device, properties, imageFormatInfo, handleType, |
| &imageFormatListInfo, &explicitCreateInfo)); |
| |
| sharedTextureMemory->mVkImage = |
| AcquireRef(new RefCountedVkHandle<VkImage>(device, vkImage)); |
| } |
| |
| // Import the memory plane(s) as VkDeviceMemory and bind to the VkImage. |
| VkMemoryFdPropertiesKHR fdProperties; |
| fdProperties.sType = VK_STRUCTURE_TYPE_MEMORY_FD_PROPERTIES_KHR; |
| fdProperties.pNext = nullptr; |
| |
| // Get the valid memory types that the external memory can be imported as. |
| DAWN_TRY(CheckVkSuccess(device->fn.GetMemoryFdPropertiesKHR( |
| vkDevice, handleType, descriptor->planes[0].fd, &fdProperties), |
| "vkGetMemoryFdPropertiesKHR")); |
| |
| // Get the valid memory types for the VkImage. |
| VkMemoryRequirements memoryRequirements; |
| device->fn.GetImageMemoryRequirements(vkDevice, sharedTextureMemory->mVkImage->Get(), |
| &memoryRequirements); |
| |
| // Choose the best memory type that satisfies both the image's constraint and the |
| // import's constraint. |
| memoryRequirements.memoryTypeBits &= fdProperties.memoryTypeBits; |
| int memoryTypeIndex = device->GetResourceMemoryAllocator()->FindBestTypeIndex( |
| memoryRequirements, MemoryKind::Opaque); |
| DAWN_INVALID_IF(memoryTypeIndex == -1, "Unable to find an appropriate memory type for import."); |
| |
| SystemHandle memoryFD; |
| DAWN_TRY_ASSIGN(memoryFD, SystemHandle::Duplicate(fd)); |
| |
| VkMemoryAllocateInfo memoryAllocateInfo = {}; |
| memoryAllocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; |
| memoryAllocateInfo.allocationSize = memoryRequirements.size; |
| memoryAllocateInfo.memoryTypeIndex = memoryTypeIndex; |
| |
| VkImportMemoryFdInfoKHR importMemoryFdInfo; |
| importMemoryFdInfo.sType = VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHR; |
| importMemoryFdInfo.handleType = handleType; |
| importMemoryFdInfo.fd = memoryFD.Get(); |
| |
| // Import the fd as VkDeviceMemory |
| VkDeviceMemory vkDeviceMemory; |
| DAWN_TRY_ASSIGN(vkDeviceMemory, |
| AllocateDeviceMemory(device, &memoryAllocateInfo, &importMemoryFdInfo)); |
| |
| memoryFD.Detach(); // Ownership transfered to the VkDeviceMemory. |
| sharedTextureMemory->mVkDeviceMemory = |
| AcquireRef(new RefCountedVkHandle<VkDeviceMemory>(device, vkDeviceMemory)); |
| |
| // Bind the VkImage to the memory. |
| DAWN_TRY( |
| CheckVkSuccess(device->fn.BindImageMemory(vkDevice, sharedTextureMemory->mVkImage->Get(), |
| sharedTextureMemory->mVkDeviceMemory->Get(), 0), |
| "vkBindImageMemory")); |
| return sharedTextureMemory; |
| #else |
| DAWN_UNREACHABLE(); |
| #endif // DAWN_PLATFORM_IS(LINUX) |
| } |
| |
| // static |
| ResultOrError<Ref<SharedTextureMemory>> SharedTextureMemory::Create( |
| Device* device, |
| StringView label, |
| const SharedTextureMemoryAHardwareBufferDescriptor* descriptor) { |
| #if DAWN_PLATFORM_IS(ANDROID) |
| const auto* ahbFunctions = |
| ToBackend(device->GetAdapter()->GetPhysicalDevice())->GetOrLoadAHBFunctions(); |
| VkDevice vkDevice = device->GetVkDevice(); |
| |
| auto* aHardwareBuffer = static_cast<struct AHardwareBuffer*>(descriptor->handle); |
| |
| bool useExternalFormat = descriptor->useExternalFormat; |
| |
| const VkExternalMemoryHandleTypeFlagBits handleType = |
| VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID; |
| |
| // Reflect the properties of the AHardwareBuffer. |
| SharedTextureMemoryProperties properties = |
| GetAHBSharedTextureMemoryProperties(ahbFunctions, aHardwareBuffer); |
| |
| if (useExternalFormat) { |
| // When using the external YUV texture format, only TextureBinding usage is valid. |
| properties.usage &= wgpu::TextureUsage::TextureBinding; |
| } |
| |
| VkFormat vkFormat; |
| YCbCrVkDescriptor yCbCrAHBInfo; |
| SampleTypeBit externalSampleType; |
| VkAndroidHardwareBufferPropertiesANDROID bufferProperties = { |
| .sType = VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_PROPERTIES_ANDROID, |
| }; |
| VkExternalFormatANDROID externalFormatAndroid = { |
| .sType = VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID, |
| }; |
| |
| // Query the properties to find the appropriate VkFormat and memory type. |
| { |
| VkAndroidHardwareBufferFormatPropertiesANDROID bufferFormatProperties; |
| PNextChainBuilder bufferPropertiesChain(&bufferProperties); |
| bufferPropertiesChain.Add( |
| &bufferFormatProperties, |
| VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_FORMAT_PROPERTIES_ANDROID); |
| |
| DAWN_TRY(CheckVkSuccess(device->fn.GetAndroidHardwareBufferPropertiesANDROID( |
| vkDevice, aHardwareBuffer, &bufferProperties), |
| "vkGetAndroidHardwareBufferPropertiesANDROID")); |
| |
| // TODO(crbug.com/dawn/2476): Validate more as per |
| // https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkImageCreateInfo.html |
| if (useExternalFormat) { |
| DAWN_INVALID_IF( |
| bufferFormatProperties.externalFormat == 0, |
| "AHardwareBuffer with external sampler must have non-zero external format."); |
| vkFormat = VK_FORMAT_UNDEFINED; |
| externalFormatAndroid.externalFormat = bufferFormatProperties.externalFormat; |
| properties.format = wgpu::TextureFormat::External; |
| } else { |
| vkFormat = bufferFormatProperties.format; |
| externalFormatAndroid.externalFormat = 0; |
| DAWN_TRY_ASSIGN(properties.format, FormatFromVkFormat(device, vkFormat)); |
| } |
| |
| // Populate the YCbCr info. |
| yCbCrAHBInfo.externalFormat = externalFormatAndroid.externalFormat; |
| yCbCrAHBInfo.vkFormat = vkFormat; |
| yCbCrAHBInfo.vkYCbCrModel = bufferFormatProperties.suggestedYcbcrModel; |
| yCbCrAHBInfo.vkYCbCrRange = bufferFormatProperties.suggestedYcbcrRange; |
| yCbCrAHBInfo.vkComponentSwizzleRed = |
| bufferFormatProperties.samplerYcbcrConversionComponents.r; |
| yCbCrAHBInfo.vkComponentSwizzleGreen = |
| bufferFormatProperties.samplerYcbcrConversionComponents.g; |
| yCbCrAHBInfo.vkComponentSwizzleBlue = |
| bufferFormatProperties.samplerYcbcrConversionComponents.b; |
| yCbCrAHBInfo.vkComponentSwizzleAlpha = |
| bufferFormatProperties.samplerYcbcrConversionComponents.a; |
| yCbCrAHBInfo.vkXChromaOffset = bufferFormatProperties.suggestedXChromaOffset; |
| yCbCrAHBInfo.vkYChromaOffset = bufferFormatProperties.suggestedYChromaOffset; |
| |
| uint32_t formatFeatures = bufferFormatProperties.formatFeatures; |
| if (formatFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_LINEAR_FILTER_BIT) { |
| yCbCrAHBInfo.vkChromaFilter = wgpu::FilterMode::Linear; |
| externalSampleType = SampleTypeBit::UnfilterableFloat | SampleTypeBit::Float; |
| } else { |
| yCbCrAHBInfo.vkChromaFilter = wgpu::FilterMode::Nearest; |
| externalSampleType = SampleTypeBit::UnfilterableFloat; |
| } |
| yCbCrAHBInfo.forceExplicitReconstruction = |
| formatFeatures & |
| VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_CHROMA_RECONSTRUCTION_EXPLICIT_BIT; |
| } |
| |
| const Format* internalFormat = nullptr; |
| DAWN_TRY_ASSIGN(internalFormat, device->GetInternalFormat(properties.format)); |
| |
| DAWN_INVALID_IF(internalFormat->IsMultiPlanar(), |
| "Multi-planar AHardwareBuffer not supported yet."); |
| |
| // Create the SharedTextureMemory object. |
| Ref<SharedTextureMemory> sharedTextureMemory = |
| SharedTextureMemory::Create(device, label, properties, VK_QUEUE_FAMILY_FOREIGN_EXT); |
| |
| sharedTextureMemory->mYCbCrAHBInfo = yCbCrAHBInfo; |
| sharedTextureMemory->GetContents()->SetExternalFormatSupportedSampleTypes(externalSampleType); |
| |
| // Reflect properties to reify them. |
| sharedTextureMemory->APIGetProperties(&properties); |
| |
| // Compute the Vulkan usage flags to create the image with. |
| VkImageUsageFlags vkUsageFlags = VulkanImageUsage(device, properties.usage, *internalFormat); |
| |
| const auto& compatibleViewFormats = device->GetCompatibleViewFormats(*internalFormat); |
| |
| // Info describing the image import. We will use this to check the import is valid, and then |
| // perform the actual VkImage creation. |
| VkPhysicalDeviceImageFormatInfo2 imageFormatInfo = {}; |
| // List of view formats the image can be created. |
| std::array<VkFormat, 2> viewFormats; |
| VkImageFormatListCreateInfo imageFormatListInfo = {}; |
| imageFormatListInfo.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO; |
| |
| // Validate that the import is valid |
| { |
| // Verify that the format modifier of the external memory and the requested Vulkan format |
| // are actually supported together in an AHB import. |
| imageFormatInfo.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2; |
| imageFormatInfo.format = vkFormat; |
| imageFormatInfo.type = VK_IMAGE_TYPE_2D; |
| imageFormatInfo.tiling = VK_IMAGE_TILING_OPTIMAL; |
| imageFormatInfo.usage = vkUsageFlags; |
| imageFormatInfo.flags = 0; |
| |
| if (!useExternalFormat) { |
| constexpr wgpu::TextureUsage kUsageRequiringView = |
| wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::TextureBinding | |
| wgpu::TextureUsage::StorageBinding; |
| const bool mayNeedViewReinterpretation = |
| (properties.usage & kUsageRequiringView) != 0 && !compatibleViewFormats.empty(); |
| const bool needsBGRA8UnormStoragePolyfill = |
| properties.format == wgpu::TextureFormat::BGRA8Unorm && |
| (properties.usage & wgpu::TextureUsage::StorageBinding); |
| if (mayNeedViewReinterpretation || needsBGRA8UnormStoragePolyfill) { |
| // Add the mutable format bit for view reinterpretation. |
| imageFormatInfo.flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT; |
| |
| if (properties.usage & wgpu::TextureUsage::StorageBinding) { |
| // Don't use an image format list because it becomes impossible to make an |
| // rgba8unorm storage texture which may be reinterpreted as rgba8unorm-srgb, |
| // because the srgb format doesn't support storage. Creation with an explicit |
| // format list that includes srgb will fail. |
| // This is the same issue seen with the DMA buf import path which has a |
| // workaround to bypass the support check. |
| // TODO(crbug.com/dawn/2304): If the dma buf import is resolved in a better way, |
| // apply the same fix here. |
| } else if (device->GetDeviceInfo().HasExt(DeviceExt::ImageFormatList)) { |
| // Set the list of view formats the image can be compatible with. |
| DAWN_ASSERT(compatibleViewFormats.size() == 1u); |
| viewFormats[0] = vkFormat; |
| viewFormats[1] = VulkanImageFormat(device, compatibleViewFormats[0]->format); |
| imageFormatListInfo.viewFormatCount = 2; |
| imageFormatListInfo.pViewFormats = viewFormats.data(); |
| } |
| } |
| |
| if (imageFormatListInfo.viewFormatCount > 0) { |
| DAWN_TRY_CONTEXT( |
| CheckExternalImageFormatSupport(device, properties, &imageFormatInfo, |
| handleType, &imageFormatListInfo), |
| "checking import support of AHardwareBuffer"); |
| } else { |
| DAWN_TRY_CONTEXT(CheckExternalImageFormatSupport(device, properties, |
| &imageFormatInfo, handleType), |
| "checking import support of AHardwareBuffer"); |
| } |
| } |
| } |
| |
| // Create the VkImage for the import. |
| { |
| VkImage vkImage; |
| DAWN_TRY_ASSIGN(vkImage, |
| CreateExternalVkImage(device, properties, imageFormatInfo, handleType, |
| &imageFormatListInfo, &externalFormatAndroid)); |
| |
| sharedTextureMemory->mVkImage = |
| AcquireRef(new RefCountedVkHandle<VkImage>(device, vkImage)); |
| } |
| |
| // Import the memory as VkDeviceMemory and bind to the VkImage. |
| { |
| // Choose the best memory type that satisfies the import's constraint. |
| VkMemoryRequirements memoryRequirements; |
| memoryRequirements.memoryTypeBits = bufferProperties.memoryTypeBits; |
| int memoryTypeIndex = device->GetResourceMemoryAllocator()->FindBestTypeIndex( |
| memoryRequirements, MemoryKind::Opaque); |
| DAWN_INVALID_IF(memoryTypeIndex == -1, |
| "Unable to find an appropriate memory type for import."); |
| |
| VkMemoryAllocateInfo memoryAllocateInfo = {}; |
| memoryAllocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; |
| memoryAllocateInfo.allocationSize = bufferProperties.allocationSize; |
| memoryAllocateInfo.memoryTypeIndex = memoryTypeIndex; |
| |
| VkImportAndroidHardwareBufferInfoANDROID importMemoryAHBInfo = {}; |
| importMemoryAHBInfo.sType = VK_STRUCTURE_TYPE_IMPORT_ANDROID_HARDWARE_BUFFER_INFO_ANDROID; |
| importMemoryAHBInfo.buffer = aHardwareBuffer; |
| |
| // https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#memory-external-android-hardware-buffer-image-resources |
| // AHardwareBuffer imports *must* use dedicated allocations. |
| VkMemoryDedicatedAllocateInfo dedicatedAllocateInfo; |
| dedicatedAllocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO; |
| dedicatedAllocateInfo.image = sharedTextureMemory->mVkImage->Get(); |
| dedicatedAllocateInfo.buffer = VkBuffer{}; |
| |
| // Import the AHardwareBuffer as VkDeviceMemory |
| VkDeviceMemory vkDeviceMemory; |
| DAWN_TRY_ASSIGN(vkDeviceMemory, |
| AllocateDeviceMemory(device, &memoryAllocateInfo, &dedicatedAllocateInfo, |
| &importMemoryAHBInfo)); |
| |
| sharedTextureMemory->mVkDeviceMemory = |
| AcquireRef(new RefCountedVkHandle<VkDeviceMemory>(device, vkDeviceMemory)); |
| |
| // Bind the VkImage to the memory. |
| DAWN_TRY(CheckVkSuccess( |
| device->fn.BindImageMemory(vkDevice, sharedTextureMemory->mVkImage->Get(), |
| sharedTextureMemory->mVkDeviceMemory->Get(), 0), |
| "vkBindImageMemory")); |
| |
| // Verify the texture memory requirements fit within the constraints of the AHardwareBuffer. |
| device->fn.GetImageMemoryRequirements(vkDevice, sharedTextureMemory->mVkImage->Get(), |
| &memoryRequirements); |
| |
| DAWN_INVALID_IF((memoryRequirements.memoryTypeBits & bufferProperties.memoryTypeBits) == 0, |
| "Required memory type bits (%u) do not overlap with AHardwareBuffer memory " |
| "type bits (%u).", |
| memoryRequirements.memoryTypeBits, bufferProperties.memoryTypeBits); |
| |
| if (!device->IsToggleEnabled(Toggle::IgnoreImportedAHardwareBufferVulkanImageSize)) { |
| DAWN_INVALID_IF(memoryRequirements.size > bufferProperties.allocationSize, |
| "Required texture memory size (%u) is larger than the AHardwareBuffer " |
| "allocation size (%u).", |
| memoryRequirements.size, bufferProperties.allocationSize); |
| } |
| } |
| return sharedTextureMemory; |
| #else |
| DAWN_UNREACHABLE(); |
| #endif // DAWN_PLATFORM_IS(ANDROID) |
| } |
| |
| // static |
| ResultOrError<Ref<SharedTextureMemory>> SharedTextureMemory::Create( |
| Device* device, |
| StringView label, |
| const SharedTextureMemoryOpaqueFDDescriptor* descriptor) { |
| #if DAWN_PLATFORM_IS(POSIX) |
| VkDevice vkDevice = device->GetVkDevice(); |
| |
| const VkExternalMemoryHandleTypeFlagBits handleType = |
| VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT; |
| |
| const VkImageCreateInfo* createInfo = |
| static_cast<const VkImageCreateInfo*>(descriptor->vkImageCreateInfo); |
| DAWN_INVALID_IF( |
| createInfo->sType != VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, |
| "descriptor->vkImageCreateInfo.sType was not VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO."); |
| |
| // Validate the createInfo chain. |
| const VkExternalMemoryImageCreateInfo* externalMemoryImageCreateInfo = nullptr; |
| const VkImageFormatListCreateInfo* imageFormatListInfo = nullptr; |
| { |
| const VkBaseInStructure* current = static_cast<const VkBaseInStructure*>(createInfo->pNext); |
| while (current != nullptr) { |
| switch (current->sType) { |
| case VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO: |
| // TODO(crbug.com/dawn/1745): Use this to inform supported types of WebGPU |
| // format reinterpretation (srgb). |
| imageFormatListInfo = |
| reinterpret_cast<const VkImageFormatListCreateInfo*>(current); |
| break; |
| case VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO: |
| externalMemoryImageCreateInfo = |
| reinterpret_cast<const VkExternalMemoryImageCreateInfo*>(current); |
| DAWN_INVALID_IF((externalMemoryImageCreateInfo->handleTypes & handleType) == 0, |
| "VkExternalMemoryImageCreateInfo::handleTypes (chained on " |
| "descriptor->vkImageCreateInfo) did not have " |
| "VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT."); |
| break; |
| default: |
| return DAWN_VALIDATION_ERROR( |
| "Unsupported descriptor->vkImageCreateInfo chain with sType 0x%x", |
| current->sType); |
| } |
| current = current->pNext; |
| } |
| } |
| |
| DAWN_INVALID_IF( |
| externalMemoryImageCreateInfo == nullptr, |
| "descriptor->vkImageCreateInfo did not have chain with VkExternalMemoryImageCreateInfo"); |
| |
| DAWN_INVALID_IF( |
| (createInfo->usage & VK_IMAGE_USAGE_TRANSFER_DST_BIT) == 0, |
| "descriptor->vkImageCreateInfo.usage did not have VK_IMAGE_USAGE_TRANSFER_DST_BIT"); |
| |
| const bool isBGRA8UnormStorage = createInfo->format == VK_FORMAT_B8G8R8A8_UNORM && |
| (createInfo->usage & VK_IMAGE_USAGE_STORAGE_BIT) != 0; |
| DAWN_INVALID_IF( |
| isBGRA8UnormStorage && (createInfo->flags & VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT) == 0, |
| "descriptor->vkImageCreateInfo.flags did not have VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT when " |
| "usage has VK_IMAGE_USAGE_STORAGE_BIT and format is VK_FORMAT_B8G8R8A8_UNORM"); |
| |
| // Populate the properties from the VkImageCreateInfo |
| SharedTextureMemoryProperties properties{}; |
| properties.size = { |
| createInfo->extent.width, |
| createInfo->extent.height, |
| std::max(createInfo->arrayLayers, createInfo->extent.depth), |
| }; |
| DAWN_TRY_ASSIGN(properties.format, FormatFromVkFormat(device, createInfo->format)); |
| if (createInfo->usage & VK_IMAGE_USAGE_TRANSFER_SRC_BIT) { |
| properties.usage |= wgpu::TextureUsage::CopySrc; |
| } |
| if (createInfo->usage & VK_IMAGE_USAGE_TRANSFER_DST_BIT) { |
| properties.usage |= wgpu::TextureUsage::CopyDst; |
| } |
| if (createInfo->usage & VK_IMAGE_USAGE_SAMPLED_BIT) { |
| properties.usage |= wgpu::TextureUsage::TextureBinding; |
| } |
| if (createInfo->usage & VK_IMAGE_USAGE_STORAGE_BIT) { |
| properties.usage |= wgpu::TextureUsage::StorageBinding; |
| } |
| if (createInfo->usage & |
| (VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT)) { |
| properties.usage |= wgpu::TextureUsage::RenderAttachment; |
| } |
| |
| const Format* internalFormat; |
| DAWN_TRY_ASSIGN(internalFormat, device->GetInternalFormat(properties.format)); |
| |
| const auto& compatibleViewFormats = device->GetCompatibleViewFormats(*internalFormat); |
| |
| // Create the SharedTextureMemory object. |
| Ref<SharedTextureMemory> sharedTextureMemory = |
| SharedTextureMemory::Create(device, label, properties, VK_QUEUE_FAMILY_EXTERNAL_KHR); |
| |
| // Reflect properties to reify them. |
| sharedTextureMemory->APIGetProperties(&properties); |
| |
| constexpr wgpu::TextureUsage kUsageRequiringView = wgpu::TextureUsage::RenderAttachment | |
| wgpu::TextureUsage::TextureBinding | |
| wgpu::TextureUsage::StorageBinding; |
| const bool mayNeedViewReinterpretation = |
| (properties.usage & kUsageRequiringView) != 0 && !compatibleViewFormats.empty(); |
| |
| DAWN_INVALID_IF( |
| mayNeedViewReinterpretation && !(createInfo->flags & VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT), |
| "VkImageCreateInfo::flags did not have VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT " |
| "which is required for view format reinterpretation."); |
| |
| if (imageFormatListInfo && mayNeedViewReinterpretation) { |
| auto viewFormatsBegin = imageFormatListInfo->pViewFormats; |
| auto viewFormatsEnd = |
| imageFormatListInfo->pViewFormats + imageFormatListInfo->viewFormatCount; |
| VkFormat baseVkFormat = VulkanImageFormat(device, properties.format); |
| |
| DAWN_INVALID_IF( |
| std::find(viewFormatsBegin, viewFormatsEnd, baseVkFormat) == viewFormatsEnd, |
| "VkImageFormatCreateInfo did not contain VkFormat 0x%x which may be required to " |
| "create a texture view with %s.", |
| baseVkFormat, properties.format); |
| |
| for (const auto* f : compatibleViewFormats) { |
| VkFormat vkFormat = VulkanImageFormat(device, f->format); |
| DAWN_INVALID_IF(std::find(viewFormatsBegin, viewFormatsEnd, vkFormat) == viewFormatsEnd, |
| "VkImageFormatCreateInfo did not contain VkFormat 0x%x which may be " |
| "required to create a texture view with %s.", |
| vkFormat, f->format); |
| } |
| } |
| |
| // Validate that an OpaqueFD import with this createInfo is valid. |
| { |
| VkPhysicalDeviceImageFormatInfo2 imageFormatInfo; |
| imageFormatInfo.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2_KHR; |
| imageFormatInfo.pNext = nullptr; |
| imageFormatInfo.format = createInfo->format; |
| imageFormatInfo.type = createInfo->imageType; |
| imageFormatInfo.tiling = createInfo->tiling; |
| imageFormatInfo.usage = createInfo->usage; |
| imageFormatInfo.flags = createInfo->flags; |
| |
| if (imageFormatListInfo) { |
| VkImageFormatListCreateInfo imageFormatListInfoCopy; |
| imageFormatListInfoCopy = *imageFormatListInfo; |
| DAWN_TRY_CONTEXT(CheckExternalImageFormatSupport(device, properties, &imageFormatInfo, |
| handleType, &imageFormatListInfoCopy), |
| "checking import support for opaque fd import"); |
| } else { |
| DAWN_TRY_CONTEXT( |
| CheckExternalImageFormatSupport(device, properties, &imageFormatInfo, handleType), |
| "checking import support for opaque fd import"); |
| } |
| } |
| |
| // Create the VkImage |
| { |
| VkImage vkImage; |
| DAWN_TRY(CheckVkSuccess(device->fn.CreateImage(vkDevice, createInfo, nullptr, &*vkImage), |
| "vkCreateImage")); |
| sharedTextureMemory->mVkImage = |
| AcquireRef(new RefCountedVkHandle<VkImage>(device, vkImage)); |
| } |
| |
| // Import the memoryFD as VkDeviceMemory and bind to the VkImage. |
| { |
| VkMemoryRequirements requirements; |
| device->fn.GetImageMemoryRequirements(device->GetVkDevice(), |
| sharedTextureMemory->mVkImage->Get(), &requirements); |
| DAWN_INVALID_IF(requirements.size > descriptor->allocationSize, |
| "Required texture memory size (%u) is larger than the memory fd " |
| "allocation size (%u).", |
| requirements.size, descriptor->allocationSize); |
| |
| SystemHandle memoryFD; |
| DAWN_TRY_ASSIGN(memoryFD, SystemHandle::Duplicate(descriptor->memoryFD)); |
| |
| VkMemoryDedicatedAllocateInfo dedicatedAllocateInfo{}; |
| dedicatedAllocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO; |
| dedicatedAllocateInfo.image = sharedTextureMemory->mVkImage->Get(); |
| |
| VkImportMemoryFdInfoKHR importMemoryFdInfo{}; |
| importMemoryFdInfo.sType = VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHR; |
| importMemoryFdInfo.handleType = handleType; |
| importMemoryFdInfo.fd = memoryFD.Get(); |
| |
| VkMemoryAllocateInfo memoryAllocateInfo = {}; |
| memoryAllocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; |
| memoryAllocateInfo.allocationSize = descriptor->allocationSize; |
| memoryAllocateInfo.memoryTypeIndex = descriptor->memoryTypeIndex; |
| |
| // Import as VkDeviceMemory |
| VkDeviceMemory vkDeviceMemory; |
| if (descriptor->dedicatedAllocation) { |
| DAWN_TRY_ASSIGN(vkDeviceMemory, |
| AllocateDeviceMemory(device, &memoryAllocateInfo, &importMemoryFdInfo, |
| &dedicatedAllocateInfo)); |
| } else { |
| DAWN_TRY_ASSIGN(vkDeviceMemory, |
| AllocateDeviceMemory(device, &memoryAllocateInfo, &importMemoryFdInfo)); |
| } |
| |
| memoryFD.Detach(); // Ownership transfered to the VkDeviceMemory. |
| sharedTextureMemory->mVkDeviceMemory = |
| AcquireRef(new RefCountedVkHandle<VkDeviceMemory>(device, vkDeviceMemory)); |
| |
| // Bind the VkImage to the memory. |
| DAWN_TRY(CheckVkSuccess( |
| device->fn.BindImageMemory(vkDevice, sharedTextureMemory->mVkImage->Get(), |
| sharedTextureMemory->mVkDeviceMemory->Get(), 0), |
| "vkBindImageMemory")); |
| } |
| return sharedTextureMemory; |
| #else |
| DAWN_UNREACHABLE(); |
| #endif // DAWN_PLATFORM_IS(POSIX) |
| } |
| |
| // static |
| Ref<SharedTextureMemory> SharedTextureMemory::Create( |
| Device* device, |
| StringView label, |
| const SharedTextureMemoryProperties& properties, |
| uint32_t queueFamilyIndex) { |
| Ref<SharedTextureMemory> sharedTextureMemory = |
| AcquireRef(new SharedTextureMemory(device, label, properties, queueFamilyIndex)); |
| sharedTextureMemory->Initialize(); |
| return sharedTextureMemory; |
| } |
| |
| SharedTextureMemory::SharedTextureMemory(Device* device, |
| StringView label, |
| const SharedTextureMemoryProperties& properties, |
| uint32_t queueFamilyIndex) |
| : SharedTextureMemoryBase(device, label, properties), mQueueFamilyIndex(queueFamilyIndex) {} |
| |
| RefCountedVkHandle<VkDeviceMemory>* SharedTextureMemory::GetVkDeviceMemory() const { |
| return mVkDeviceMemory.Get(); |
| } |
| |
| RefCountedVkHandle<VkImage>* SharedTextureMemory::GetVkImage() const { |
| return mVkImage.Get(); |
| } |
| |
| uint32_t SharedTextureMemory::GetQueueFamilyIndex() const { |
| return mQueueFamilyIndex; |
| } |
| |
| void SharedTextureMemory::DestroyImpl() { |
| mVkImage = nullptr; |
| mVkDeviceMemory = nullptr; |
| } |
| |
| ResultOrError<Ref<TextureBase>> SharedTextureMemory::CreateTextureImpl( |
| const UnpackedPtr<TextureDescriptor>& descriptor) { |
| return SharedTexture::Create(this, descriptor); |
| } |
| |
| MaybeError SharedTextureMemory::BeginAccessImpl( |
| TextureBase* texture, |
| const UnpackedPtr<BeginAccessDescriptor>& descriptor) { |
| // TODO(dawn/2276): support concurrent read access. |
| DAWN_INVALID_IF(descriptor->concurrentRead, "Vulkan backend doesn't support concurrent read."); |
| DAWN_INVALID_IF( |
| texture->GetFormat().format == wgpu::TextureFormat::External && !descriptor->initialized, |
| "BeginAccess with Texture format (%s) must be initialized", texture->GetFormat().format); |
| |
| wgpu::SType type; |
| DAWN_TRY_ASSIGN( |
| type, (descriptor.ValidateBranches<Branch<SharedTextureMemoryVkImageLayoutBeginState>>())); |
| DAWN_ASSERT(type == wgpu::SType::SharedTextureMemoryVkImageLayoutBeginState); |
| |
| auto vkLayoutBeginState = descriptor.Get<SharedTextureMemoryVkImageLayoutBeginState>(); |
| DAWN_ASSERT(vkLayoutBeginState != nullptr); |
| |
| for (size_t i = 0; i < descriptor->fenceCount; ++i) { |
| // All fences are backed by binary semaphores. |
| DAWN_INVALID_IF(descriptor->signaledValues[i] != 1, "%s signaled value (%u) was not 1.", |
| descriptor->fences[i], descriptor->signaledValues[i]); |
| } |
| static_cast<SharedTexture*>(texture)->SetPendingAcquire( |
| static_cast<VkImageLayout>(vkLayoutBeginState->oldLayout), |
| static_cast<VkImageLayout>(vkLayoutBeginState->newLayout)); |
| return {}; |
| } |
| |
| #if DAWN_PLATFORM_IS(FUCHSIA) || DAWN_PLATFORM_IS(LINUX) |
| ResultOrError<FenceAndSignalValue> SharedTextureMemory::EndAccessImpl( |
| TextureBase* texture, |
| ExecutionSerial lastUsageSerial, |
| UnpackedPtr<EndAccessState>& state) { |
| wgpu::SType type; |
| DAWN_TRY_ASSIGN(type, |
| (state.ValidateBranches<Branch<SharedTextureMemoryVkImageLayoutEndState>>())); |
| DAWN_ASSERT(type == wgpu::SType::SharedTextureMemoryVkImageLayoutEndState); |
| |
| auto vkLayoutEndState = state.Get<SharedTextureMemoryVkImageLayoutEndState>(); |
| DAWN_ASSERT(vkLayoutEndState != nullptr); |
| |
| #if DAWN_PLATFORM_IS(FUCHSIA) |
| DAWN_INVALID_IF(!GetDevice()->HasFeature(Feature::SharedFenceVkSemaphoreZirconHandle), |
| "Required feature (%s) for %s is missing.", |
| wgpu::FeatureName::SharedFenceVkSemaphoreZirconHandle, |
| wgpu::SharedFenceType::VkSemaphoreZirconHandle); |
| #elif DAWN_PLATFORM_IS(LINUX) |
| DAWN_INVALID_IF(!GetDevice()->HasFeature(Feature::SharedFenceSyncFD) && |
| !GetDevice()->HasFeature(Feature::SharedFenceVkSemaphoreSyncFD) && |
| !GetDevice()->HasFeature(Feature::SharedFenceVkSemaphoreOpaqueFD), |
| "Required feature (%s or %s or %s) for %s or %s is missing.", |
| wgpu::FeatureName::SharedFenceVkSemaphoreOpaqueFD, |
| wgpu::FeatureName::SharedFenceSyncFD, |
| wgpu::FeatureName::SharedFenceVkSemaphoreSyncFD, |
| wgpu::SharedFenceType::VkSemaphoreOpaqueFD, wgpu::SharedFenceType::SyncFD); |
| #endif |
| |
| SystemHandle handle; |
| { |
| ExternalSemaphoreHandle semaphoreHandle; |
| VkImageLayout releasedOldLayout; |
| VkImageLayout releasedNewLayout; |
| DAWN_TRY(static_cast<SharedTexture*>(texture)->EndAccess( |
| &semaphoreHandle, &releasedOldLayout, &releasedNewLayout)); |
| // Handle is acquired from the texture so we need to make sure to close it. |
| // TODO(dawn:1745): Consider using one event per submit that is tracked by the |
| // CommandRecordingContext so that we don't need to create one handle per texture, |
| // and so we don't need to acquire it here to close it. |
| handle = SystemHandle::Acquire(semaphoreHandle); |
| vkLayoutEndState->oldLayout = releasedOldLayout; |
| vkLayoutEndState->newLayout = releasedNewLayout; |
| } |
| |
| Ref<SharedFence> fence; |
| |
| #if DAWN_PLATFORM_IS(FUCHSIA) |
| SharedFenceVkSemaphoreZirconHandleDescriptor desc; |
| desc.handle = handle.Get(); |
| |
| DAWN_TRY_ASSIGN(fence, |
| SharedFence::Create(ToBackend(GetDevice()), "Internal VkSemaphore", &desc)); |
| #elif DAWN_PLATFORM_IS(LINUX) |
| if (GetDevice()->HasFeature(Feature::SharedFenceSyncFD) || |
| GetDevice()->HasFeature(Feature::SharedFenceVkSemaphoreSyncFD)) { |
| SharedFenceSyncFDDescriptor desc; |
| desc.handle = handle.Get(); |
| |
| DAWN_TRY_ASSIGN(fence, |
| SharedFence::Create(ToBackend(GetDevice()), "Internal VkSemaphore", &desc)); |
| } else { |
| SharedFenceVkSemaphoreOpaqueFDDescriptor desc; |
| desc.handle = handle.Get(); |
| |
| DAWN_TRY_ASSIGN(fence, |
| SharedFence::Create(ToBackend(GetDevice()), "Internal VkSemaphore", &desc)); |
| } |
| #endif |
| // All semaphores are binary semaphores. |
| return FenceAndSignalValue{std::move(fence), 1}; |
| } |
| |
| #else // DAWN_PLATFORM_IS(FUCHSIA) || DAWN_PLATFORM_IS(LINUX) |
| |
| ResultOrError<FenceAndSignalValue> SharedTextureMemory::EndAccessImpl( |
| TextureBase* texture, |
| ExecutionSerial lastUsageSerial, |
| UnpackedPtr<EndAccessState>& state) { |
| return DAWN_VALIDATION_ERROR("No shared fence features supported."); |
| } |
| |
| #endif // DAWN_PLATFORM_IS(FUCHSIA) || DAWN_PLATFORM_IS(LINUX) |
| |
| MaybeError SharedTextureMemory::GetChainedProperties( |
| UnpackedPtr<SharedTextureMemoryProperties>& properties) const { |
| auto ahbProperties = properties.Get<SharedTextureMemoryAHardwareBufferProperties>(); |
| |
| if (!ahbProperties) { |
| return {}; |
| } |
| |
| if (ahbProperties->yCbCrInfo.nextInChain) { |
| return DAWN_VALIDATION_ERROR( |
| "yCBCrInfo field of SharedTextureMemoryAHardwareBufferProperties has a chained " |
| "struct."); |
| } |
| |
| ahbProperties->yCbCrInfo = mYCbCrAHBInfo; |
| |
| return {}; |
| } |
| |
| } // namespace dawn::native::vulkan |