blob: 2ff45078fa5dc7aa8e93f0953c74e44147a3fe77 [file] [log] [blame]
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "impeller/renderer/backend/vulkan/android/ahb_texture_source_vk.h"
#include "impeller/renderer/backend/vulkan/allocator_vk.h"
#include "impeller/renderer/backend/vulkan/context_vk.h"
#include "impeller/renderer/backend/vulkan/texture_source_vk.h"
#include "impeller/renderer/backend/vulkan/yuv_conversion_library_vk.h"
namespace impeller {
using AHBProperties = vk::StructureChain<
// For VK_ANDROID_external_memory_android_hardware_buffer
vk::AndroidHardwareBufferPropertiesANDROID,
// For VK_ANDROID_external_memory_android_hardware_buffer
vk::AndroidHardwareBufferFormatPropertiesANDROID>;
static vk::UniqueImage CreateVKImageWrapperForAndroidHarwareBuffer(
const vk::Device& device,
const AHBProperties& ahb_props,
const AHardwareBuffer_Desc& ahb_desc) {
const auto& ahb_format =
ahb_props.get<vk::AndroidHardwareBufferFormatPropertiesANDROID>();
vk::StructureChain<vk::ImageCreateInfo,
// For VK_KHR_external_memory
vk::ExternalMemoryImageCreateInfo,
// For VK_ANDROID_external_memory_android_hardware_buffer
vk::ExternalFormatANDROID>
image_chain;
auto& image_info = image_chain.get<vk::ImageCreateInfo>();
vk::ImageUsageFlags image_usage_flags;
if (ahb_desc.usage & AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE) {
image_usage_flags |= vk::ImageUsageFlagBits::eSampled;
}
if (ahb_desc.usage & AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER) {
image_usage_flags |= vk::ImageUsageFlagBits::eColorAttachment;
}
vk::ImageCreateFlags image_create_flags;
if (ahb_desc.usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT) {
image_create_flags |= vk::ImageCreateFlagBits::eProtected;
}
if (ahb_desc.usage & AHARDWAREBUFFER_USAGE_GPU_CUBE_MAP) {
image_create_flags |= vk::ImageCreateFlagBits::eCubeCompatible;
}
image_info.imageType = vk::ImageType::e2D;
image_info.format = ahb_format.format;
image_info.extent.width = ahb_desc.width;
image_info.extent.height = ahb_desc.height;
image_info.extent.depth = 1;
image_info.mipLevels =
(ahb_desc.usage & AHARDWAREBUFFER_USAGE_GPU_MIPMAP_COMPLETE)
? ISize{ahb_desc.width, ahb_desc.height}.MipCount()
: 1u;
image_info.arrayLayers = ahb_desc.layers;
image_info.samples = vk::SampleCountFlagBits::e1;
image_info.tiling = vk::ImageTiling::eOptimal;
image_info.usage = image_usage_flags;
image_info.flags = image_create_flags;
image_info.sharingMode = vk::SharingMode::eExclusive;
image_info.initialLayout = vk::ImageLayout::eUndefined;
image_chain.get<vk::ExternalMemoryImageCreateInfo>().handleTypes =
vk::ExternalMemoryHandleTypeFlagBits::eAndroidHardwareBufferANDROID;
// If the format isn't natively supported by Vulkan (i.e, be a part of the
// base vkFormat enum), an untyped "external format" must be specified when
// creating the image and the image views. Usually includes YUV formats.
if (ahb_format.format == vk::Format::eUndefined) {
image_chain.get<vk::ExternalFormatANDROID>().externalFormat =
ahb_format.externalFormat;
} else {
image_chain.unlink<vk::ExternalFormatANDROID>();
}
auto image = device.createImageUnique(image_chain.get());
if (image.result != vk::Result::eSuccess) {
VALIDATION_LOG << "Could not create image for external buffer: "
<< vk::to_string(image.result);
return {};
}
return std::move(image.value);
}
static vk::UniqueDeviceMemory ImportVKDeviceMemoryFromAndroidHarwareBuffer(
const vk::Device& device,
const vk::PhysicalDevice& physical_device,
const vk::Image& image,
struct AHardwareBuffer* hardware_buffer,
const AHBProperties& ahb_props) {
vk::PhysicalDeviceMemoryProperties memory_properties;
physical_device.getMemoryProperties(&memory_properties);
int memory_type_index = AllocatorVK::FindMemoryTypeIndex(
ahb_props.get().memoryTypeBits, memory_properties);
if (memory_type_index < 0) {
VALIDATION_LOG << "Could not find memory type of external image.";
return {};
}
vk::StructureChain<vk::MemoryAllocateInfo,
// Core in 1.1
vk::MemoryDedicatedAllocateInfo,
// For VK_ANDROID_external_memory_android_hardware_buffer
vk::ImportAndroidHardwareBufferInfoANDROID>
memory_chain;
auto& mem_alloc_info = memory_chain.get<vk::MemoryAllocateInfo>();
mem_alloc_info.allocationSize = ahb_props.get().allocationSize;
mem_alloc_info.memoryTypeIndex = memory_type_index;
auto& dedicated_alloc_info =
memory_chain.get<vk::MemoryDedicatedAllocateInfo>();
dedicated_alloc_info.image = image;
auto& ahb_import_info =
memory_chain.get<vk::ImportAndroidHardwareBufferInfoANDROID>();
ahb_import_info.buffer = hardware_buffer;
auto device_memory = device.allocateMemoryUnique(memory_chain.get());
if (device_memory.result != vk::Result::eSuccess) {
VALIDATION_LOG << "Could not allocate device memory for external image : "
<< vk::to_string(device_memory.result);
return {};
}
return std::move(device_memory.value);
}
static std::shared_ptr<YUVConversionVK> CreateYUVConversion(
const ContextVK& context,
const AHBProperties& ahb_props) {
YUVConversionDescriptorVK conversion_chain;
const auto& ahb_format =
ahb_props.get<vk::AndroidHardwareBufferFormatPropertiesANDROID>();
auto& conversion_info = conversion_chain.get();
conversion_info.format = ahb_format.format;
conversion_info.ycbcrModel = ahb_format.suggestedYcbcrModel;
conversion_info.ycbcrRange = ahb_format.suggestedYcbcrRange;
conversion_info.components = ahb_format.samplerYcbcrConversionComponents;
conversion_info.xChromaOffset = ahb_format.suggestedXChromaOffset;
conversion_info.yChromaOffset = ahb_format.suggestedYChromaOffset;
// If the potential format features of the sampler Y′CBCR conversion do not
// support VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_LINEAR_FILTER_BIT,
// chromaFilter must not be VK_FILTER_LINEAR.
//
// Since we are not checking, let's just default to a safe value.
conversion_info.chromaFilter = vk::Filter::eNearest;
conversion_info.forceExplicitReconstruction = false;
if (conversion_info.format == vk::Format::eUndefined) {
auto& external_format = conversion_chain.get<vk::ExternalFormatANDROID>();
external_format.externalFormat = ahb_format.externalFormat;
} else {
conversion_chain.unlink<vk::ExternalFormatANDROID>();
}
return context.GetYUVConversionLibrary()->GetConversion(conversion_chain);
}
static vk::UniqueImageView CreateVKImageView(
const vk::Device& device,
const vk::Image& image,
const vk::SamplerYcbcrConversion& yuv_conversion,
const AHBProperties& ahb_props,
const AHardwareBuffer_Desc& ahb_desc) {
const auto& ahb_format =
ahb_props.get<vk::AndroidHardwareBufferFormatPropertiesANDROID>();
vk::StructureChain<vk::ImageViewCreateInfo,
// Core in 1.1
vk::SamplerYcbcrConversionInfo>
view_chain;
auto& view_info = view_chain.get();
view_info.image = image;
view_info.viewType = vk::ImageViewType::e2D;
view_info.format = ahb_format.format;
view_info.subresourceRange.aspectMask = vk::ImageAspectFlagBits::eColor;
view_info.subresourceRange.baseMipLevel = 0u;
view_info.subresourceRange.baseArrayLayer = 0u;
view_info.subresourceRange.levelCount =
(ahb_desc.usage & AHARDWAREBUFFER_USAGE_GPU_MIPMAP_COMPLETE)
? ISize{ahb_desc.width, ahb_desc.height}.MipCount()
: 1u;
view_info.subresourceRange.layerCount = ahb_desc.layers;
// We need a custom YUV conversion only if we don't recognize the format.
if (view_info.format == vk::Format::eUndefined) {
view_chain.get<vk::SamplerYcbcrConversionInfo>().conversion =
yuv_conversion;
} else {
view_chain.unlink<vk::SamplerYcbcrConversionInfo>();
}
auto image_view = device.createImageViewUnique(view_info);
if (image_view.result != vk::Result::eSuccess) {
VALIDATION_LOG << "Could not create external image view: "
<< vk::to_string(image_view.result);
return {};
}
return std::move(image_view.value);
}
static PixelFormat ToPixelFormat(AHardwareBuffer_Format format) {
switch (format) {
case AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM:
return PixelFormat::kR8G8B8A8UNormInt;
case AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT:
return PixelFormat::kR16G16B16A16Float;
case AHARDWAREBUFFER_FORMAT_D24_UNORM_S8_UINT:
return PixelFormat::kD24UnormS8Uint;
case AHARDWAREBUFFER_FORMAT_D32_FLOAT_S8_UINT:
return PixelFormat::kD32FloatS8UInt;
case AHARDWAREBUFFER_FORMAT_S8_UINT:
return PixelFormat::kS8UInt;
case AHARDWAREBUFFER_FORMAT_R8_UNORM:
return PixelFormat::kR8UNormInt;
case AHARDWAREBUFFER_FORMAT_R10G10B10A10_UNORM:
case AHARDWAREBUFFER_FORMAT_R16G16_UINT:
case AHARDWAREBUFFER_FORMAT_D32_FLOAT:
case AHARDWAREBUFFER_FORMAT_R16_UINT:
case AHARDWAREBUFFER_FORMAT_D24_UNORM:
case AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420:
case AHARDWAREBUFFER_FORMAT_YCbCr_P010:
case AHARDWAREBUFFER_FORMAT_BLOB:
case AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM:
case AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM:
case AHARDWAREBUFFER_FORMAT_R8G8B8_UNORM:
case AHARDWAREBUFFER_FORMAT_D16_UNORM:
case AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM:
// Not understood by the rest of Impeller. Use a placeholder but create
// the native image and image views using the right external format.
break;
}
return PixelFormat::kR8G8B8A8UNormInt;
}
static TextureType ToTextureType(const AHardwareBuffer_Desc& ahb_desc) {
if (ahb_desc.layers == 1u) {
return TextureType::kTexture2D;
}
if (ahb_desc.layers % 6u == 0 &&
(ahb_desc.usage & AHARDWAREBUFFER_USAGE_GPU_CUBE_MAP)) {
return TextureType::kTextureCube;
}
// Our texture types seem to understand external OES textures. Should these be
// wired up instead?
return TextureType::kTexture2D;
}
static TextureDescriptor ToTextureDescriptor(
const AHardwareBuffer_Desc& ahb_desc) {
const auto ahb_size = ISize{ahb_desc.width, ahb_desc.height};
TextureDescriptor desc;
// We are not going to touch hardware buffers on the CPU or use them as
// transient attachments. Just treat them as device private.
desc.storage_mode = StorageMode::kDevicePrivate;
desc.format =
ToPixelFormat(static_cast<AHardwareBuffer_Format>(ahb_desc.format));
desc.size = ahb_size;
desc.type = ToTextureType(ahb_desc);
desc.sample_count = SampleCount::kCount1;
desc.compression_type = CompressionType::kLossless;
desc.mip_count = (ahb_desc.usage & AHARDWAREBUFFER_USAGE_GPU_MIPMAP_COMPLETE)
? ahb_size.MipCount()
: 1u;
return desc;
}
AHBTextureSourceVK::AHBTextureSourceVK(
const std::shared_ptr<ContextVK>& context,
struct AHardwareBuffer* ahb,
const AHardwareBuffer_Desc& ahb_desc)
: TextureSourceVK(ToTextureDescriptor(ahb_desc)) {
if (!context) {
VALIDATION_LOG << "Invalid context.";
return;
}
const auto& device = context->GetDevice();
const auto& physical_device = context->GetPhysicalDevice();
AHBProperties ahb_props;
if (device.getAndroidHardwareBufferPropertiesANDROID(ahb, &ahb_props.get()) !=
vk::Result::eSuccess) {
VALIDATION_LOG << "Could not determine properties of the Android hardware "
"buffer.";
return;
}
const auto& ahb_format =
ahb_props.get<vk::AndroidHardwareBufferFormatPropertiesANDROID>();
// Create an image to refer to our external image.
auto image =
CreateVKImageWrapperForAndroidHarwareBuffer(device, ahb_props, ahb_desc);
if (!image) {
return;
}
// Create a device memory allocation to refer to our external image.
auto device_memory = ImportVKDeviceMemoryFromAndroidHarwareBuffer(
device, physical_device, image.get(), ahb, ahb_props);
if (!device_memory) {
return;
}
// Bind the image to the image memory.
if (auto result = device.bindImageMemory(image.get(), device_memory.get(), 0);
result != vk::Result::eSuccess) {
VALIDATION_LOG << "Could not bind external device memory to image : "
<< vk::to_string(result);
return;
}
// Figure out how to perform YUV conversions.
auto yuv_conversion = CreateYUVConversion(*context, ahb_props);
if (!yuv_conversion || !yuv_conversion->IsValid()) {
return;
}
// Create image view for the newly created image.
auto image_view = CreateVKImageView(device, //
image.get(), //
yuv_conversion->GetConversion(), //
ahb_props, //
ahb_desc //
);
if (!image_view) {
return;
}
needs_yuv_conversion_ = ahb_format.format == vk::Format::eUndefined;
device_memory_ = std::move(device_memory);
image_ = std::move(image);
yuv_conversion_ = std::move(yuv_conversion);
image_view_ = std::move(image_view);
#ifdef IMPELLER_DEBUG
context->SetDebugName(device_memory_.get(), "AHB Device Memory");
context->SetDebugName(image_.get(), "AHB Image");
context->SetDebugName(yuv_conversion_->GetConversion(), "AHB YUV Conversion");
context->SetDebugName(image_view_.get(), "AHB ImageView");
#endif // IMPELLER_DEBUG
is_valid_ = true;
}
// |TextureSourceVK|
AHBTextureSourceVK::~AHBTextureSourceVK() = default;
bool AHBTextureSourceVK::IsValid() const {
return is_valid_;
}
// |TextureSourceVK|
vk::Image AHBTextureSourceVK::GetImage() const {
return image_.get();
}
// |TextureSourceVK|
vk::ImageView AHBTextureSourceVK::GetImageView() const {
return image_view_.get();
}
// |TextureSourceVK|
vk::ImageView AHBTextureSourceVK::GetRenderTargetView() const {
return image_view_.get();
}
// |TextureSourceVK|
bool AHBTextureSourceVK::IsSwapchainImage() const {
return false;
}
// |TextureSourceVK|
std::shared_ptr<YUVConversionVK> AHBTextureSourceVK::GetYUVConversion() const {
return needs_yuv_conversion_ ? yuv_conversion_ : nullptr;
}
} // namespace impeller