| // Copyright 2019 The Dawn Authors | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| #include "common/Assert.h" | |
| #include "dawn_native/vulkan/AdapterVk.h" | |
| #include "dawn_native/vulkan/BackendVk.h" | |
| #include "dawn_native/vulkan/DeviceVk.h" | |
| #include "dawn_native/vulkan/ResourceMemoryAllocatorVk.h" | |
| #include "dawn_native/vulkan/VulkanError.h" | |
| #include "dawn_native/vulkan/external_memory/MemoryService.h" | |
| namespace dawn_native { namespace vulkan { namespace external_memory { | |
| namespace { | |
| // Some modifiers use multiple planes (for example, see the comment for | |
| // I915_FORMAT_MOD_Y_TILED_CCS in drm/drm_fourcc.h), but dma-buf import in Dawn only | |
| // supports single-plane formats. | |
| ResultOrError<uint32_t> GetModifierPlaneCount(const VulkanFunctions& fn, | |
| VkPhysicalDevice physicalDevice, | |
| VkFormat format, | |
| uint64_t modifier) { | |
| VkDrmFormatModifierPropertiesListEXT formatModifierPropsList; | |
| formatModifierPropsList.sType = | |
| VK_STRUCTURE_TYPE_DRM_FORMAT_MODIFIER_PROPERTIES_LIST_EXT; | |
| formatModifierPropsList.pNext = nullptr; | |
| formatModifierPropsList.drmFormatModifierCount = 0; | |
| formatModifierPropsList.pDrmFormatModifierProperties = nullptr; | |
| VkFormatProperties2 formatProps; | |
| formatProps.sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2; | |
| formatProps.pNext = &formatModifierPropsList; | |
| fn.GetPhysicalDeviceFormatProperties2(physicalDevice, format, &formatProps); | |
| uint32_t modifierCount = formatModifierPropsList.drmFormatModifierCount; | |
| std::vector<VkDrmFormatModifierPropertiesEXT> formatModifierProps(modifierCount); | |
| formatModifierPropsList.pDrmFormatModifierProperties = formatModifierProps.data(); | |
| fn.GetPhysicalDeviceFormatProperties2(physicalDevice, format, &formatProps); | |
| for (const auto& props : formatModifierProps) { | |
| if (props.drmFormatModifier == modifier) { | |
| uint32_t count = props.drmFormatModifierPlaneCount; | |
| return count; | |
| } | |
| } | |
| return DAWN_FORMAT_VALIDATION_ERROR("DRM format modifier not supported."); | |
| } | |
| } // anonymous namespace | |
| Service::Service(Device* device) | |
| : mDevice(device), mSupported(CheckSupport(device->GetDeviceInfo())) { | |
| } | |
| Service::~Service() = default; | |
| // static | |
| bool Service::CheckSupport(const VulkanDeviceInfo& deviceInfo) { | |
| return deviceInfo.HasExt(DeviceExt::ExternalMemoryFD) && | |
| deviceInfo.HasExt(DeviceExt::ImageDrmFormatModifier); | |
| } | |
| bool Service::SupportsImportMemory(VkFormat format, | |
| VkImageType type, | |
| VkImageTiling tiling, | |
| VkImageUsageFlags usage, | |
| VkImageCreateFlags flags) { | |
| return mSupported; | |
| } | |
| bool Service::SupportsCreateImage(const ExternalImageDescriptor* descriptor, | |
| VkFormat format, | |
| VkImageUsageFlags usage) { | |
| // Early out before we try using extension functions | |
| if (!mSupported) { | |
| return false; | |
| } | |
| if (descriptor->type != ExternalImageType::DmaBuf) { | |
| return false; | |
| } | |
| const ExternalImageDescriptorDmaBuf* dmaBufDescriptor = | |
| static_cast<const ExternalImageDescriptorDmaBuf*>(descriptor); | |
| // Verify plane count for the modifier. | |
| VkPhysicalDevice physicalDevice = ToBackend(mDevice->GetAdapter())->GetPhysicalDevice(); | |
| uint32_t planeCount = 0; | |
| if (mDevice->ConsumedError(GetModifierPlaneCount(mDevice->fn, physicalDevice, format, | |
| dmaBufDescriptor->drmModifier), | |
| &planeCount)) { | |
| return false; | |
| } | |
| if (planeCount == 0) { | |
| return false; | |
| } | |
| // TODO(hob): Support multi-plane formats like I915_FORMAT_MOD_Y_TILED_CCS. | |
| if (planeCount > 1) { | |
| return false; | |
| } | |
| // Verify that the format modifier of the external memory and the requested Vulkan format | |
| // are actually supported together in a dma-buf import. | |
| VkPhysicalDeviceImageDrmFormatModifierInfoEXT drmModifierInfo; | |
| drmModifierInfo.sType = | |
| VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_DRM_FORMAT_MODIFIER_INFO_EXT; | |
| drmModifierInfo.pNext = nullptr; | |
| drmModifierInfo.drmFormatModifier = dmaBufDescriptor->drmModifier; | |
| drmModifierInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; | |
| VkPhysicalDeviceExternalImageFormatInfo externalImageFormatInfo; | |
| externalImageFormatInfo.sType = | |
| VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO; | |
| externalImageFormatInfo.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT; | |
| externalImageFormatInfo.pNext = &drmModifierInfo; | |
| VkPhysicalDeviceImageFormatInfo2 imageFormatInfo; | |
| imageFormatInfo.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2; | |
| imageFormatInfo.format = format; | |
| imageFormatInfo.type = VK_IMAGE_TYPE_2D; | |
| imageFormatInfo.tiling = VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT; | |
| imageFormatInfo.usage = usage; | |
| imageFormatInfo.flags = 0; | |
| imageFormatInfo.pNext = &externalImageFormatInfo; | |
| VkExternalImageFormatProperties externalImageFormatProps; | |
| externalImageFormatProps.sType = VK_STRUCTURE_TYPE_EXTERNAL_IMAGE_FORMAT_PROPERTIES; | |
| externalImageFormatProps.pNext = nullptr; | |
| VkImageFormatProperties2 imageFormatProps; | |
| imageFormatProps.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2; | |
| imageFormatProps.pNext = &externalImageFormatProps; | |
| VkResult result = VkResult::WrapUnsafe(mDevice->fn.GetPhysicalDeviceImageFormatProperties2( | |
| physicalDevice, &imageFormatInfo, &imageFormatProps)); | |
| if (result != VK_SUCCESS) { | |
| return false; | |
| } | |
| VkExternalMemoryFeatureFlags featureFlags = | |
| externalImageFormatProps.externalMemoryProperties.externalMemoryFeatures; | |
| return featureFlags & VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT; | |
| } | |
| ResultOrError<MemoryImportParams> Service::GetMemoryImportParams( | |
| const ExternalImageDescriptor* descriptor, | |
| VkImage image) { | |
| DAWN_INVALID_IF(descriptor->type != ExternalImageType::DmaBuf, | |
| "ExternalImageDescriptor is not a ExternalImageDescriptorDmaBuf."); | |
| const ExternalImageDescriptorDmaBuf* dmaBufDescriptor = | |
| static_cast<const ExternalImageDescriptorDmaBuf*>(descriptor); | |
| VkDevice device = mDevice->GetVkDevice(); | |
| // Get the valid memory types for the VkImage. | |
| VkMemoryRequirements memoryRequirements; | |
| mDevice->fn.GetImageMemoryRequirements(device, image, &memoryRequirements); | |
| 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. | |
| mDevice->fn.GetMemoryFdPropertiesKHR(device, VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT, | |
| dmaBufDescriptor->memoryFD, &fdProperties); | |
| // Choose the best memory type that satisfies both the image's constraint and the import's | |
| // constraint. | |
| memoryRequirements.memoryTypeBits &= fdProperties.memoryTypeBits; | |
| int memoryTypeIndex = mDevice->GetResourceMemoryAllocator()->FindBestTypeIndex( | |
| memoryRequirements, MemoryKind::Opaque); | |
| DAWN_INVALID_IF(memoryTypeIndex == -1, | |
| "Unable to find an appropriate memory type for import."); | |
| MemoryImportParams params = {memoryRequirements.size, | |
| static_cast<uint32_t>(memoryTypeIndex)}; | |
| return params; | |
| } | |
| ResultOrError<VkDeviceMemory> Service::ImportMemory(ExternalMemoryHandle handle, | |
| const MemoryImportParams& importParams, | |
| VkImage image) { | |
| DAWN_INVALID_IF(handle < 0, "Importing memory with an invalid handle."); | |
| VkMemoryDedicatedAllocateInfo memoryDedicatedAllocateInfo; | |
| memoryDedicatedAllocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO; | |
| memoryDedicatedAllocateInfo.pNext = nullptr; | |
| memoryDedicatedAllocateInfo.image = image; | |
| memoryDedicatedAllocateInfo.buffer = VkBuffer{}; | |
| VkImportMemoryFdInfoKHR importMemoryFdInfo; | |
| importMemoryFdInfo.sType = VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHR; | |
| importMemoryFdInfo.pNext = &memoryDedicatedAllocateInfo; | |
| importMemoryFdInfo.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT, | |
| importMemoryFdInfo.fd = handle; | |
| VkMemoryAllocateInfo memoryAllocateInfo; | |
| memoryAllocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; | |
| memoryAllocateInfo.pNext = &importMemoryFdInfo; | |
| memoryAllocateInfo.allocationSize = importParams.allocationSize; | |
| memoryAllocateInfo.memoryTypeIndex = importParams.memoryTypeIndex; | |
| VkDeviceMemory allocatedMemory = VK_NULL_HANDLE; | |
| DAWN_TRY( | |
| CheckVkSuccess(mDevice->fn.AllocateMemory(mDevice->GetVkDevice(), &memoryAllocateInfo, | |
| nullptr, &*allocatedMemory), | |
| "vkAllocateMemory")); | |
| return allocatedMemory; | |
| } | |
| ResultOrError<VkImage> Service::CreateImage(const ExternalImageDescriptor* descriptor, | |
| const VkImageCreateInfo& baseCreateInfo) { | |
| DAWN_INVALID_IF(descriptor->type != ExternalImageType::DmaBuf, | |
| "ExternalImageDescriptor is not a dma-buf descriptor."); | |
| const ExternalImageDescriptorDmaBuf* dmaBufDescriptor = | |
| static_cast<const ExternalImageDescriptorDmaBuf*>(descriptor); | |
| VkPhysicalDevice physicalDevice = ToBackend(mDevice->GetAdapter())->GetPhysicalDevice(); | |
| VkDevice device = mDevice->GetVkDevice(); | |
| // Dawn currently doesn't support multi-plane formats, so we only need to create a single | |
| // VkSubresourceLayout here. | |
| VkSubresourceLayout planeLayout; | |
| planeLayout.offset = 0; | |
| planeLayout.size = 0; // VK_EXT_image_drm_format_modifier mandates size = 0. | |
| planeLayout.rowPitch = dmaBufDescriptor->stride; | |
| planeLayout.arrayPitch = 0; // Not an array texture | |
| planeLayout.depthPitch = 0; // Not a depth texture | |
| uint32_t planeCount; | |
| DAWN_TRY_ASSIGN(planeCount, | |
| GetModifierPlaneCount(mDevice->fn, physicalDevice, baseCreateInfo.format, | |
| dmaBufDescriptor->drmModifier)); | |
| ASSERT(planeCount == 1); | |
| VkImageDrmFormatModifierExplicitCreateInfoEXT explicitCreateInfo; | |
| explicitCreateInfo.sType = | |
| VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_EXPLICIT_CREATE_INFO_EXT; | |
| explicitCreateInfo.pNext = NULL; | |
| explicitCreateInfo.drmFormatModifier = dmaBufDescriptor->drmModifier; | |
| explicitCreateInfo.drmFormatModifierPlaneCount = planeCount; | |
| explicitCreateInfo.pPlaneLayouts = &planeLayout; | |
| VkExternalMemoryImageCreateInfo externalMemoryImageCreateInfo; | |
| externalMemoryImageCreateInfo.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO; | |
| externalMemoryImageCreateInfo.pNext = &explicitCreateInfo; | |
| externalMemoryImageCreateInfo.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT; | |
| VkImageCreateInfo createInfo = baseCreateInfo; | |
| createInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; | |
| createInfo.pNext = &externalMemoryImageCreateInfo; | |
| createInfo.flags = 0; | |
| createInfo.tiling = VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT; | |
| // Create a new VkImage with tiling equal to the DRM format modifier. | |
| VkImage image; | |
| DAWN_TRY(CheckVkSuccess(mDevice->fn.CreateImage(device, &createInfo, nullptr, &*image), | |
| "CreateImage")); | |
| return image; | |
| } | |
| }}} // namespace dawn_native::vulkan::external_memory |