blob: b4567d76321353075a0b850c9678321b27f0edfb [file] [log] [blame]
// Copyright 2020 The Chromium 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 "gpu/vulkan/vulkan_image.h"
#include <tuple>
#include "base/containers/cxx20_erase.h"
#include "base/logging.h"
#include "gpu/vulkan/vulkan_device_queue.h"
#include "gpu/vulkan/vulkan_function_pointers.h"
namespace gpu {
// static
std::unique_ptr<VulkanImage> VulkanImage::CreateWithExternalMemoryAndModifiers(
VulkanDeviceQueue* device_queue,
const gfx::Size& size,
VkFormat format,
std::vector<uint64_t> modifiers,
VkImageUsageFlags usage,
VkImageCreateFlags flags) {
auto image = std::make_unique<VulkanImage>(base::PassKey<VulkanImage>());
if (!image->InitializeWithExternalMemoryAndModifiers(
device_queue, size, format, std::move(modifiers), usage, flags)) {
return nullptr;
}
return image;
}
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::NATIVE_PIXMAP) {
DLOG(ERROR) << "GpuMemoryBuffer is not supported. type:" << gmb_handle.type;
return false;
}
queue_family_index_ = queue_family_index;
auto& native_pixmap_handle = gmb_handle.native_pixmap_handle;
// 2 plane images are ok, they just need ycbcr set up.
DCHECK_LT(native_pixmap_handle.planes.size(), 3u);
if (native_pixmap_handle.planes.size() == 2) {
ycbcr_info_ = VulkanYCbCrInfo(
/*image_format=*/format,
/*external_format=*/0,
/*suggested_ycbcr_model=*/native_pixmap_handle.planes.size(),
/*suggested_ycbcr_range=*/1,
/*suggested_xchroma_offset=*/0,
/*suggested_ychroma_offset=*/0,
// The same flags that VaapiVideoDecoderUses to create the texture.
/*format_features=*/VK_FORMAT_FEATURE_COSITED_CHROMA_SAMPLES_BIT |
VK_FORMAT_FEATURE_TRANSFER_DST_BIT |
VK_FORMAT_FEATURE_TRANSFER_SRC_BIT |
VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT |
VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT |
VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT);
}
auto& scoped_fd = native_pixmap_handle.planes[0].fd;
if (!scoped_fd.is_valid()) {
DLOG(ERROR) << "GpuMemoryBufferHandle doesn't have a valid fd.";
return false;
}
bool using_modifier =
native_pixmap_handle.modifier != gfx::NativePixmapHandle::kNoModifier &&
gfx::HasExtension(device_queue->enabled_extensions(),
VK_EXT_IMAGE_DRM_FORMAT_MODIFIER_EXTENSION_NAME);
VkExternalMemoryImageCreateInfoKHR external_image_create_info = {
.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO_KHR,
.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT,
};
VkImageDrmFormatModifierListCreateInfoEXT modifier_info = {
.sType = VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_LIST_CREATE_INFO_EXT,
.drmFormatModifierCount = 1,
.pDrmFormatModifiers = &native_pixmap_handle.modifier,
};
if (using_modifier) {
DCHECK_EQ(image_tiling, VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT);
external_image_create_info.pNext = &modifier_info;
}
int memory_fd = scoped_fd.release();
VkImportMemoryFdInfoKHR import_memory_fd_info = {
.sType = VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHR,
.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT,
.fd = memory_fd,
};
VkMemoryRequirements* requirements = nullptr;
bool result = Initialize(device_queue, size, format, usage, flags,
image_tiling, &external_image_create_info,
&import_memory_fd_info, requirements);
// If Initialize successfully, the fd in scoped_fd should be owned by vulkan,
// otherwise take the ownership of the fd back.
if (!result) {
scoped_fd.reset(memory_fd);
}
return result;
}
bool VulkanImage::InitializeWithExternalMemoryAndModifiers(
VulkanDeviceQueue* device_queue,
const gfx::Size& size,
VkFormat format,
std::vector<uint64_t> modifiers,
VkImageUsageFlags usage,
VkImageCreateFlags flags) {
DCHECK(gfx::HasExtension(device_queue->enabled_extensions(),
VK_EXT_IMAGE_DRM_FORMAT_MODIFIER_EXTENSION_NAME));
DCHECK(!modifiers.empty());
VkPhysicalDevice physical_device = device_queue->GetVulkanPhysicalDevice();
// Query all supported format modifiers.
VkDrmFormatModifierPropertiesListEXT modifier_props_list = {
.sType = VK_STRUCTURE_TYPE_DRM_FORMAT_MODIFIER_PROPERTIES_LIST_EXT,
};
VkFormatProperties2 format_props = {
.sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2,
.pNext = &modifier_props_list,
};
vkGetPhysicalDeviceFormatProperties2(physical_device, format, &format_props);
std::vector<VkDrmFormatModifierPropertiesEXT> props_vector;
props_vector.resize(modifier_props_list.drmFormatModifierCount);
modifier_props_list.pDrmFormatModifierProperties = props_vector.data();
vkGetPhysicalDeviceFormatProperties2(physical_device, format, &format_props);
// Call GetImageFormatProperties with every modifier and filter the list
// down to those that we know work.
base::EraseIf(props_vector, [&](const VkDrmFormatModifierPropertiesEXT& p) {
VkPhysicalDeviceImageDrmFormatModifierInfoEXT mod_info = {
.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_DRM_FORMAT_MODIFIER_INFO_EXT,
.drmFormatModifier = p.drmFormatModifier,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
};
VkPhysicalDeviceImageFormatInfo2 format_info = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2,
.pNext = &mod_info,
.format = format,
.type = VK_IMAGE_TYPE_2D,
.tiling = VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT,
.usage = usage,
.flags = flags,
};
VkImageFormatProperties2 format_props = {
.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2,
};
auto result = vkGetPhysicalDeviceImageFormatProperties2(
physical_device, &format_info, &format_props);
return result != VK_SUCCESS;
});
if (props_vector.empty())
return false;
// Find compatible modifiers.
base::EraseIf(modifiers, [&props_vector](uint64_t modifier) {
for (const auto& modifier_props : props_vector) {
if (modifier == modifier_props.drmFormatModifier)
return false;
}
return true;
});
if (modifiers.empty())
return false;
VkImageDrmFormatModifierListCreateInfoEXT modifier_list = {
.sType = VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_LIST_CREATE_INFO_EXT,
.drmFormatModifierCount = static_cast<uint32_t>(modifiers.size()),
.pDrmFormatModifiers = modifiers.data(),
};
if (!InitializeWithExternalMemory(device_queue, size, format, usage, flags,
VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT,
&modifier_list,
/*memory_allocation_info_next=*/nullptr)) {
return false;
}
// Vulkan implementation will select a modifier from |modifiers|, so we need
// to query it from the VkImage.
VkImageDrmFormatModifierPropertiesEXT image_modifier_props = {
.sType = VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_PROPERTIES_EXT,
};
auto result = vkGetImageDrmFormatModifierPropertiesEXT(
device_queue->GetVulkanDevice(), image_, &image_modifier_props);
DCHECK_EQ(result, VK_SUCCESS);
modifier_ = image_modifier_props.drmFormatModifier;
for (auto props : props_vector) {
if (props.drmFormatModifier == modifier_) {
plane_count_ = props.drmFormatModifierPlaneCount;
break;
}
}
DCHECK_GE(plane_count_, 1u);
DCHECK_LE(plane_count_, 3u);
for (uint32_t i = 0; i < plane_count_; i++) {
// Based on spec VK_IMAGE_ASPECT_MEMORY_PLANE_i_BIT_EXT should be used for
// VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT tiling. But we have to use
// VK_IMAGE_ASPECT_PLANE_i_BIT because mesa only handles
// VK_IMAGE_ASPECT_PLANE_i_BIT.
// TODO(penghuang): use VK_IMAGE_ASPECT_MEMORY_PLANE_i_BIT_EXT when the mesa
// can handle VK_IMAGE_ASPECT_MEMORY_PLANE_i_BIT_EXT.
const VkImageSubresource image_subresource = {
.aspectMask =
static_cast<VkImageAspectFlags>(VK_IMAGE_ASPECT_PLANE_0_BIT << i),
.mipLevel = 0,
.arrayLayer = 0,
};
vkGetImageSubresourceLayout(device_queue->GetVulkanDevice(), image_,
&image_subresource, &layouts_[i]);
}
return true;
}
} // namespace gpu