// 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(kLimitVkImageUsageToFormatFeaturesForAHB,
"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