From 899c17090f31d2f2e4dc7f11aca74fdcd4b126d3 Mon Sep 17 00:00:00 2001 From: Brian Ho Date: Thu, 21 Nov 2019 16:02:10 +0000 Subject: [PATCH] Implement a dma-buf MemoryService This CL implements the MemoryService for importing memory and creating VkImages from a dma-buf handle. Under the hood, it uses the VK_EXT_external_memory_dma_buf and VK_EXT_image_drm_format_modifier extensions to find a memory type that supports dma-buf import. In addition, the extensions are also used to properly specify the stride and tiling of the dma-buf to vkAllocateMemory and vkCreateImage. BUG=chromium:996470 Change-Id: Ie72d73117a4cbafcb40468aab0952b783351d499 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/13785 Commit-Queue: Brian Ho Reviewed-by: Corentin Wallez --- BUILD.gn | 7 +- src/dawn_native/vulkan/DeviceVk.cpp | 4 + src/dawn_native/vulkan/DeviceVk.h | 2 + src/dawn_native/vulkan/TextureVk.cpp | 5 +- .../vulkan/external_memory/MemoryService.h | 4 +- .../external_memory/MemoryServiceDmaBuf.cpp | 271 ++++++++++++++++++ .../external_memory/MemoryServiceNull.cpp | 4 +- .../external_memory/MemoryServiceOpaqueFD.cpp | 4 +- .../MemoryServiceZirconHandle.cpp | 4 +- 9 files changed, 298 insertions(+), 7 deletions(-) create mode 100644 src/dawn_native/vulkan/external_memory/MemoryServiceDmaBuf.cpp diff --git a/BUILD.gn b/BUILD.gn index 6079642c47..0fd9cf4001 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -478,7 +478,12 @@ source_set("libdawn_native_sources") { "src/dawn_native/vulkan/external_semaphore/SemaphoreService.h", ] - if (is_linux) { + if (is_chromeos) { + sources += [ + "src/dawn_native/vulkan/external_memory/MemoryServiceDmaBuf.cpp", + "src/dawn_native/vulkan/external_semaphore/SemaphoreServiceOpaqueFD.cpp", + ] + } else if (is_linux) { sources += [ "src/dawn_native/vulkan/external_memory/MemoryServiceOpaqueFD.cpp", "src/dawn_native/vulkan/external_semaphore/SemaphoreServiceOpaqueFD.cpp", diff --git a/src/dawn_native/vulkan/DeviceVk.cpp b/src/dawn_native/vulkan/DeviceVk.cpp index f317588d1f..b9f326a119 100644 --- a/src/dawn_native/vulkan/DeviceVk.cpp +++ b/src/dawn_native/vulkan/DeviceVk.cpp @@ -722,6 +722,10 @@ namespace dawn_native { namespace vulkan { mResourceMemoryAllocator->Deallocate(allocation); } + int Device::FindBestMemoryTypeIndex(VkMemoryRequirements requirements, bool mappable) { + return mResourceMemoryAllocator->FindBestTypeIndex(requirements, mappable); + } + ResourceMemoryAllocator* Device::GetResourceMemoryAllocatorForTesting() const { return mResourceMemoryAllocator.get(); } diff --git a/src/dawn_native/vulkan/DeviceVk.h b/src/dawn_native/vulkan/DeviceVk.h index 74ae79da35..e5210d6b9f 100644 --- a/src/dawn_native/vulkan/DeviceVk.h +++ b/src/dawn_native/vulkan/DeviceVk.h @@ -95,6 +95,8 @@ namespace dawn_native { namespace vulkan { bool mappable); void DeallocateMemory(ResourceMemoryAllocation* allocation); + int FindBestMemoryTypeIndex(VkMemoryRequirements requirements, bool mappable); + ResourceMemoryAllocator* GetResourceMemoryAllocatorForTesting() const; private: diff --git a/src/dawn_native/vulkan/TextureVk.cpp b/src/dawn_native/vulkan/TextureVk.cpp index 3422ab0e03..5d11976455 100644 --- a/src/dawn_native/vulkan/TextureVk.cpp +++ b/src/dawn_native/vulkan/TextureVk.cpp @@ -485,7 +485,8 @@ namespace dawn_native { namespace vulkan { MaybeError Texture::InitializeFromExternal(const ExternalImageDescriptor* descriptor, external_memory::Service* externalMemoryService) { VkFormat format = VulkanImageFormat(GetFormat().format); - if (!externalMemoryService->SupportsCreateImage(descriptor, format)) { + VkImageUsageFlags usage = VulkanImageUsage(GetUsage(), GetFormat()); + if (!externalMemoryService->SupportsCreateImage(descriptor, format, usage)) { return DAWN_VALIDATION_ERROR("Creating an image from external memory is not supported"); } @@ -499,7 +500,7 @@ namespace dawn_native { namespace vulkan { baseCreateInfo.mipLevels = GetNumMipLevels(); baseCreateInfo.arrayLayers = GetArrayLayers(); baseCreateInfo.samples = VulkanSampleCount(GetSampleCount()); - baseCreateInfo.usage = VulkanImageUsage(GetUsage(), GetFormat()); + baseCreateInfo.usage = usage; baseCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; baseCreateInfo.queueFamilyIndexCount = 0; baseCreateInfo.pQueueFamilyIndices = nullptr; diff --git a/src/dawn_native/vulkan/external_memory/MemoryService.h b/src/dawn_native/vulkan/external_memory/MemoryService.h index 1d0d475eca..0c4b64d49a 100644 --- a/src/dawn_native/vulkan/external_memory/MemoryService.h +++ b/src/dawn_native/vulkan/external_memory/MemoryService.h @@ -44,7 +44,9 @@ namespace dawn_native { namespace vulkan { namespace external_memory { VkImageCreateFlags flags); // True if the device reports it supports creating VkImages from external memory. - bool SupportsCreateImage(const ExternalImageDescriptor* descriptor, VkFormat format); + bool SupportsCreateImage(const ExternalImageDescriptor* descriptor, + VkFormat format, + VkImageUsageFlags usage); // Returns the parameters required for importing memory ResultOrError GetMemoryImportParams( diff --git a/src/dawn_native/vulkan/external_memory/MemoryServiceDmaBuf.cpp b/src/dawn_native/vulkan/external_memory/MemoryServiceDmaBuf.cpp new file mode 100644 index 0000000000..e9944f8a4a --- /dev/null +++ b/src/dawn_native/vulkan/external_memory/MemoryServiceDmaBuf.cpp @@ -0,0 +1,271 @@ +// 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/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 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.GetPhysicalDeviceFormatProperties2KHR(physicalDevice, format, &formatProps); + + uint32_t modifierCount = formatModifierPropsList.drmFormatModifierCount; + std::vector formatModifierProps(modifierCount); + formatModifierPropsList.pDrmFormatModifierProperties = formatModifierProps.data(); + + fn.GetPhysicalDeviceFormatProperties2KHR(physicalDevice, format, &formatProps); + for (const auto& props : formatModifierProps) { + if (props.drmFormatModifier == modifier) { + uint32_t count = props.drmFormatModifierPlaneCount; + return count; + } + } + return DAWN_VALIDATION_ERROR("DRM format modifier not supported"); + } + + } // anonymous namespace + + Service::Service(Device* device) : mDevice(device) { + const VulkanDeviceInfo& deviceInfo = mDevice->GetDeviceInfo(); + const VulkanGlobalInfo& globalInfo = + ToBackend(mDevice->GetAdapter())->GetBackend()->GetGlobalInfo(); + + mSupported = globalInfo.getPhysicalDeviceProperties2 && + globalInfo.externalMemoryCapabilities && deviceInfo.externalMemory && + deviceInfo.externalMemoryFD && deviceInfo.externalMemoryDmaBuf && + deviceInfo.imageDrmFormatModifier; + } + + Service::~Service() = default; + + 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 != ExternalImageDescriptorType::DmaBuf) { + return false; + } + const ExternalImageDescriptorDmaBuf* dmaBufDescriptor = + static_cast(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 = mDevice->fn.GetPhysicalDeviceImageFormatProperties2KHR( + physicalDevice, &imageFormatInfo, &imageFormatProps); + if (result != VK_SUCCESS) { + return false; + } + VkExternalMemoryFeatureFlags featureFlags = + externalImageFormatProps.externalMemoryProperties.externalMemoryFeatures; + return featureFlags & VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT; + } + + ResultOrError Service::GetMemoryImportParams( + const ExternalImageDescriptor* descriptor, + VkImage image) { + if (descriptor->type != ExternalImageDescriptorType::DmaBuf) { + return DAWN_VALIDATION_ERROR("ExternalImageDescriptor is not a dma-buf descriptor"); + } + const ExternalImageDescriptorDmaBuf* dmaBufDescriptor = + static_cast(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->FindBestMemoryTypeIndex(memoryRequirements, false /** mappable */); + if (memoryTypeIndex == -1) { + return DAWN_VALIDATION_ERROR("Unable to find appropriate memory type for import"); + } + MemoryImportParams params = {memoryRequirements.size, memoryTypeIndex}; + return params; + } + + ResultOrError Service::ImportMemory(ExternalMemoryHandle handle, + const MemoryImportParams& importParams, + VkImage image) { + if (handle < 0) { + return DAWN_VALIDATION_ERROR("Trying to import memory with invalid handle"); + } + + VkMemoryDedicatedAllocateInfo memoryDedicatedAllocateInfo; + memoryDedicatedAllocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO; + memoryDedicatedAllocateInfo.pNext = nullptr; + memoryDedicatedAllocateInfo.image = image; + memoryDedicatedAllocateInfo.buffer = VK_NULL_HANDLE; + + 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 Service::CreateImage(const ExternalImageDescriptor* descriptor, + const VkImageCreateInfo& baseCreateInfo) { + if (descriptor->type != ExternalImageDescriptorType::DmaBuf) { + return DAWN_VALIDATION_ERROR("ExternalImageDescriptor is not a dma-buf descriptor"); + } + const ExternalImageDescriptorDmaBuf* dmaBufDescriptor = + static_cast(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 diff --git a/src/dawn_native/vulkan/external_memory/MemoryServiceNull.cpp b/src/dawn_native/vulkan/external_memory/MemoryServiceNull.cpp index 78144d6e0e..14d882a56a 100644 --- a/src/dawn_native/vulkan/external_memory/MemoryServiceNull.cpp +++ b/src/dawn_native/vulkan/external_memory/MemoryServiceNull.cpp @@ -32,7 +32,9 @@ namespace dawn_native { namespace vulkan { namespace external_memory { return false; } - bool Service::SupportsCreateImage(const ExternalImageDescriptor* descriptor, VkFormat format) { + bool Service::SupportsCreateImage(const ExternalImageDescriptor* descriptor, + VkFormat format, + VkImageUsageFlags usage) { return false; } diff --git a/src/dawn_native/vulkan/external_memory/MemoryServiceOpaqueFD.cpp b/src/dawn_native/vulkan/external_memory/MemoryServiceOpaqueFD.cpp index 5e6bf7cbad..2a31b31187 100644 --- a/src/dawn_native/vulkan/external_memory/MemoryServiceOpaqueFD.cpp +++ b/src/dawn_native/vulkan/external_memory/MemoryServiceOpaqueFD.cpp @@ -81,7 +81,9 @@ namespace dawn_native { namespace vulkan { namespace external_memory { !(memoryFlags & VK_EXTERNAL_MEMORY_FEATURE_DEDICATED_ONLY_BIT_KHR); } - bool Service::SupportsCreateImage(const ExternalImageDescriptor* descriptor, VkFormat format) { + bool Service::SupportsCreateImage(const ExternalImageDescriptor* descriptor, + VkFormat format, + VkImageUsageFlags usage) { return mSupported; } diff --git a/src/dawn_native/vulkan/external_memory/MemoryServiceZirconHandle.cpp b/src/dawn_native/vulkan/external_memory/MemoryServiceZirconHandle.cpp index bfbd5ccb67..8c70c677c5 100644 --- a/src/dawn_native/vulkan/external_memory/MemoryServiceZirconHandle.cpp +++ b/src/dawn_native/vulkan/external_memory/MemoryServiceZirconHandle.cpp @@ -81,7 +81,9 @@ namespace dawn_native { namespace vulkan { namespace external_memory { !(memoryFlags & VK_EXTERNAL_MEMORY_FEATURE_DEDICATED_ONLY_BIT_KHR); } - bool Service::SupportsCreateImage(const ExternalImageDescriptor* descriptor, VkFormat format) { + bool Service::SupportsCreateImage(const ExternalImageDescriptor* descriptor, + VkFormat format, + VkImageUsageFlags usage) { return mSupported; }