| // Copyright 2020 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "gpu/vulkan/vulkan_image.h" |
| |
| #include "base/android/android_hardware_buffer_compat.h" |
| #include "base/debug/crash_logging.h" |
| #include "base/feature_list.h" |
| #include "base/logging.h" |
| #include "gpu/vulkan/vulkan_device_queue.h" |
| #include "gpu/vulkan/vulkan_function_pointers.h" |
| |
| namespace gpu { |
| |
| namespace { |
| BASE_FEATURE(LimitVkImageUsageToFormatFeaturesForAHB, |
| base::FEATURE_ENABLED_BY_DEFAULT); |
| |
| bool IsSinglePlaneRGBVulkanAHBFormat(VkFormat format) { |
| switch (format) { |
| // AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM |
| case VK_FORMAT_R8G8B8A8_UNORM: |
| // AHARDWAREBUFFER_FORMAT_R8G8B8_UNORM |
| case VK_FORMAT_R8G8B8_UNORM: |
| // AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM |
| case VK_FORMAT_R5G6B5_UNORM_PACK16: |
| // AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT |
| case VK_FORMAT_R16G16B16A16_SFLOAT: |
| // AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM |
| case VK_FORMAT_A2B10G10R10_UNORM_PACK32: |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| VkImageUsageFlags AHBUsageToImageUsage(uint64_t ahb_usage) { |
| VkImageUsageFlags usage_flags = 0; |
| |
| // Get Vulkan Image usage flag equivalence of AHB usage. |
| if (ahb_usage & AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE) { |
| usage_flags |= |
| VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT; |
| } |
| if (ahb_usage & AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT) { |
| usage_flags |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; |
| } |
| |
| // All AHB support these usages when imported into vulkan. |
| usage_flags |= |
| VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; |
| return usage_flags; |
| } |
| |
| VkImageUsageFlags VkFormatFeaturesToImageUsage(VkFormatFeatureFlags features) { |
| VkImageUsageFlags usage_flags = 0; |
| |
| if (features & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT) { |
| usage_flags |= VK_IMAGE_USAGE_SAMPLED_BIT; |
| } |
| |
| if (features & VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT) { |
| usage_flags |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | |
| VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT; |
| } |
| |
| if (features & VK_FORMAT_FEATURE_TRANSFER_SRC_BIT) { |
| usage_flags |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT; |
| } |
| |
| if (features & VK_FORMAT_FEATURE_TRANSFER_DST_BIT) { |
| usage_flags |= VK_IMAGE_USAGE_TRANSFER_DST_BIT; |
| } |
| |
| return usage_flags; |
| } |
| |
| } // namespace |
| |
| bool VulkanImage::InitializeFromGpuMemoryBufferHandle( |
| VulkanDeviceQueue* device_queue, |
| gfx::GpuMemoryBufferHandle gmb_handle, |
| const gfx::Size& size, |
| VkFormat format, |
| VkImageUsageFlags usage, |
| VkImageCreateFlags flags, |
| VkImageTiling image_tiling, |
| uint32_t queue_family_index) { |
| if (gmb_handle.type != gfx::GpuMemoryBufferType::ANDROID_HARDWARE_BUFFER) { |
| DLOG(ERROR) << "gmb_handle.type is not supported. type:" << gmb_handle.type; |
| return false; |
| } |
| DCHECK(gmb_handle.android_hardware_buffer.is_valid()); |
| SCOPED_CRASH_KEY_BOOL("vulkan", "gmb_buffer.is_valid", |
| gmb_handle.android_hardware_buffer.is_valid()); |
| auto& ahb_handle = gmb_handle.android_hardware_buffer; |
| |
| // To obtain format properties of an Android hardware buffer, include an |
| // instance of VkAndroidHardwareBufferFormatPropertiesANDROID in the pNext |
| // chain of the VkAndroidHardwareBufferPropertiesANDROID instance passed to |
| // vkGetAndroidHardwareBufferPropertiesANDROID. |
| VkAndroidHardwareBufferFormatPropertiesANDROID ahb_format_props = { |
| VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_FORMAT_PROPERTIES_ANDROID, |
| }; |
| VkAndroidHardwareBufferPropertiesANDROID ahb_props = { |
| .sType = VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_PROPERTIES_ANDROID, |
| .pNext = &ahb_format_props, |
| }; |
| |
| VkDevice vk_device = device_queue->GetVulkanDevice(); |
| VkResult result = vkGetAndroidHardwareBufferPropertiesANDROID( |
| vk_device, ahb_handle.get(), &ahb_props); |
| if (result != VK_SUCCESS) { |
| LOG(ERROR) |
| << "GetAhbProps: vkGetAndroidHardwareBufferPropertiesANDROID failed : " |
| << result; |
| return false; |
| } |
| |
| // To create an image with an external format, include an instance of |
| // VkExternalFormatANDROID in the pNext chain of VkImageCreateInfo. |
| VkExternalFormatANDROID external_format = { |
| .sType = VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID, |
| // If externalFormat is zero, the effect is as if the |
| // VkExternalFormatANDROID structure was not present. Otherwise, the image |
| // will have the specified external format. |
| .externalFormat = 0, |
| }; |
| |
| const bool should_use_external_format = |
| !IsSinglePlaneRGBVulkanAHBFormat(ahb_format_props.format); |
| |
| if (should_use_external_format) { |
| // externalFormat must be 0 or a value returned in the externalFormat member |
| // of VkAndroidHardwareBufferFormatPropertiesANDROID by an earlier call to |
| // vkGetAndroidHardwareBufferPropertiesANDROID. |
| external_format.externalFormat = ahb_format_props.externalFormat; |
| } |
| |
| // To define a set of external memory handle types that may be used as backing |
| // store for an image, add a VkExternalMemoryImageCreateInfo structure to the |
| // pNext chain of the VkImageCreateInfo structure. |
| VkExternalMemoryImageCreateInfo external_memory_image_info = { |
| .sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO, |
| .pNext = &external_format, |
| .handleTypes = |
| VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID, |
| }; |
| |
| // Get the AHB description. |
| AHardwareBuffer_Desc ahb_desc = {}; |
| base::AndroidHardwareBufferCompat::GetInstance().Describe(ahb_handle.get(), |
| &ahb_desc); |
| |
| // Get Vulkan Image usage flag equivalence of AHB usage. |
| VkImageUsageFlags usage_flags = AHBUsageToImageUsage(ahb_desc.usage); |
| |
| if (!(usage_flags & |
| (VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT))) { |
| LOG(ERROR) << "No valid usage flags found"; |
| return false; |
| } |
| |
| // If we're using external format, we should limit our usage to supported |
| // format features. |
| if (should_use_external_format && |
| base::FeatureList::IsEnabled(kLimitVkImageUsageToFormatFeaturesForAHB)) { |
| usage_flags &= |
| VkFormatFeaturesToImageUsage(ahb_format_props.formatFeatures); |
| } |
| |
| VkImageCreateFlags create_flags = 0; |
| if (ahb_desc.usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT) { |
| create_flags = VK_IMAGE_CREATE_PROTECTED_BIT; |
| } |
| |
| // To import memory created outside of the current Vulkan instance from an |
| // Android hardware buffer, add a VkImportAndroidHardwareBufferInfoANDROID |
| // structure to the pNext chain of the VkMemoryAllocateInfo structure. |
| VkImportAndroidHardwareBufferInfoANDROID ahb_import_info = { |
| .sType = VK_STRUCTURE_TYPE_IMPORT_ANDROID_HARDWARE_BUFFER_INFO_ANDROID, |
| .buffer = ahb_handle.get(), |
| }; |
| |
| VkMemoryRequirements requirements = { |
| .size = ahb_props.allocationSize, |
| .memoryTypeBits = ahb_props.memoryTypeBits, |
| }; |
| |
| if (!InitializeSingleOrJointPlanes( |
| device_queue, gfx::Size(ahb_desc.width, ahb_desc.height), |
| should_use_external_format ? VK_FORMAT_UNDEFINED |
| : ahb_format_props.format, |
| usage_flags, create_flags, VK_IMAGE_TILING_OPTIMAL, |
| &external_memory_image_info, &ahb_import_info, &requirements)) { |
| return false; |
| } |
| |
| queue_family_index_ = queue_family_index; |
| |
| if (should_use_external_format) { |
| ycbcr_info_.emplace(VK_FORMAT_UNDEFINED, ahb_format_props.externalFormat, |
| ahb_format_props.suggestedYcbcrModel, |
| ahb_format_props.suggestedYcbcrRange, |
| ahb_format_props.suggestedXChromaOffset, |
| ahb_format_props.suggestedYChromaOffset, |
| ahb_format_props.formatFeatures); |
| } |
| |
| return true; |
| } |
| |
| } // namespace gpu |