blob: 0cd74511ebced1950a7ed2a5d03a0ab93f3d7399 [file] [log] [blame]
// Copyright (c) 2018 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/android/vulkan_implementation_android.h"
#include "base/android/android_hardware_buffer_compat.h"
#include "base/bind_helpers.h"
#include "base/files/file_path.h"
#include "base/logging.h"
#include "gpu/vulkan/vulkan_device_queue.h"
#include "gpu/vulkan/vulkan_function_pointers.h"
#include "gpu/vulkan/vulkan_instance.h"
#include "gpu/vulkan/vulkan_surface.h"
#include "ui/gfx/gpu_fence.h"
namespace gpu {
VulkanImplementationAndroid::VulkanImplementationAndroid() = default;
VulkanImplementationAndroid::~VulkanImplementationAndroid() = default;
bool VulkanImplementationAndroid::InitializeVulkanInstance() {
std::vector<const char*> required_extensions = {
VK_KHR_SURFACE_EXTENSION_NAME, VK_KHR_ANDROID_SURFACE_EXTENSION_NAME,
VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME,
VK_KHR_EXTERNAL_SEMAPHORE_CAPABILITIES_EXTENSION_NAME};
VulkanFunctionPointers* vulkan_function_pointers =
gpu::GetVulkanFunctionPointers();
base::NativeLibraryLoadError native_library_load_error;
vulkan_function_pointers->vulkan_loader_library_ = base::LoadNativeLibrary(
base::FilePath("libvulkan.so"), &native_library_load_error);
if (!vulkan_function_pointers->vulkan_loader_library_)
return false;
if (!vulkan_instance_.Initialize(required_extensions, {})) {
vulkan_instance_.Destroy();
return false;
}
// Initialize platform function pointers
vkCreateAndroidSurfaceKHR_ =
reinterpret_cast<PFN_vkCreateAndroidSurfaceKHR>(vkGetInstanceProcAddr(
vulkan_instance_.vk_instance(), "vkCreateAndroidSurfaceKHR"));
if (!vkCreateAndroidSurfaceKHR_) {
LOG(ERROR) << "vkCreateAndroidSurfaceKHR not found";
vulkan_instance_.Destroy();
return false;
}
return true;
}
VkInstance VulkanImplementationAndroid::GetVulkanInstance() {
return vulkan_instance_.vk_instance();
}
std::unique_ptr<VulkanSurface> VulkanImplementationAndroid::CreateViewSurface(
gfx::AcceleratedWidget window) {
VkSurfaceKHR surface;
VkAndroidSurfaceCreateInfoKHR surface_create_info = {};
surface_create_info.sType = VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR;
surface_create_info.window = window;
VkResult result = vkCreateAndroidSurfaceKHR_(
GetVulkanInstance(), &surface_create_info, nullptr, &surface);
if (VK_SUCCESS != result) {
DLOG(ERROR) << "vkCreateAndroidSurfaceKHR() failed: " << result;
return nullptr;
}
return std::make_unique<VulkanSurface>(GetVulkanInstance(), surface);
}
bool VulkanImplementationAndroid::GetPhysicalDevicePresentationSupport(
VkPhysicalDevice device,
const std::vector<VkQueueFamilyProperties>& queue_family_properties,
uint32_t queue_family_index) {
// On Android, all physical devices and queue families must be capable of
// presentation with any native window.
// As a result there is no Android-specific query for these capabilities.
return true;
}
std::vector<const char*>
VulkanImplementationAndroid::GetRequiredDeviceExtensions() {
// VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME also requires
// VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME as per spec.
return {VK_KHR_SWAPCHAIN_EXTENSION_NAME,
VK_KHR_EXTERNAL_SEMAPHORE_EXTENSION_NAME,
VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME,
VK_ANDROID_EXTERNAL_MEMORY_ANDROID_HARDWARE_BUFFER_EXTENSION_NAME,
VK_KHR_EXTERNAL_MEMORY_EXTENSION_NAME,
VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME,
VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME};
}
VkFence VulkanImplementationAndroid::CreateVkFenceForGpuFence(
VkDevice vk_device) {
NOTREACHED();
return VK_NULL_HANDLE;
}
std::unique_ptr<gfx::GpuFence>
VulkanImplementationAndroid::ExportVkFenceToGpuFence(VkDevice vk_device,
VkFence vk_fence) {
NOTREACHED();
return nullptr;
}
bool VulkanImplementationAndroid::ImportSemaphoreFdKHR(
VkDevice vk_device,
base::ScopedFD sync_fd,
VkSemaphore* vk_semaphore) {
// Create a VkSemaphore.
VkSemaphoreCreateInfo info = {VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO};
bool result = vkCreateSemaphore(vk_device, &info, nullptr, vk_semaphore);
if (result != VK_SUCCESS) {
LOG(ERROR) << "vkCreateSemaphore failed : " << result;
return false;
}
// Create VkImportSemaphoreFdInfoKHR structure.
VkImportSemaphoreFdInfoKHR import;
import.sType = VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_FD_INFO_KHR;
import.pNext = nullptr;
import.semaphore = *vk_semaphore;
import.flags = VK_SEMAPHORE_IMPORT_TEMPORARY_BIT_KHR;
// VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT specifies a POSIX file
// descriptor handle to a Linux Sync File or Android Fence object.
import.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT;
import.fd = sync_fd.get();
// Import the fd into the semaphore.
result = vkImportSemaphoreFdKHR(vk_device, &import);
if (result != VK_SUCCESS) {
LOG(ERROR) << "vkImportSemaphoreFdKHR failed : " << result;
vkDestroySemaphore(vk_device, *vk_semaphore, nullptr);
return false;
}
// If import is successful, the VkSemaphore object takes the ownership of fd.
ignore_result(sync_fd.release());
return true;
}
bool VulkanImplementationAndroid::GetSemaphoreFdKHR(VkDevice vk_device,
VkSemaphore vk_semaphore,
base::ScopedFD* sync_fd) {
// Create VkSemaphoreGetFdInfoKHR structure.
VkSemaphoreGetFdInfoKHR info;
info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR;
info.pNext = nullptr;
info.semaphore = vk_semaphore;
info.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT;
// Create a new sync fd from the semaphore.
int fd = -1;
bool result = vkGetSemaphoreFdKHR(vk_device, &info, &fd);
if (result != VK_SUCCESS) {
LOG(ERROR) << "vkGetSemaphoreFdKHR failed : " << result;
sync_fd->reset(-1);
return false;
}
// Transfer the ownership of the fd to the caller.
sync_fd->reset(fd);
return true;
}
bool VulkanImplementationAndroid::CreateVkImageAndImportAHB(
const VkDevice& vk_device,
const VkPhysicalDevice& vk_physical_device,
const gfx::Size& size,
base::android::ScopedHardwareBufferHandle ahb_handle,
VkImage* vk_image,
VkImageCreateInfo* vk_image_info,
VkDeviceMemory* vk_device_memory,
VkDeviceSize* mem_allocation_size) {
DCHECK(ahb_handle.is_valid());
DCHECK(vk_image);
DCHECK(vk_image_info);
DCHECK(vk_device_memory);
DCHECK(mem_allocation_size);
// 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;
ahb_format_props.sType =
VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_FORMAT_PROPERTIES_ANDROID;
ahb_format_props.pNext = nullptr;
VkAndroidHardwareBufferPropertiesANDROID ahb_props;
ahb_props.sType =
VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_PROPERTIES_ANDROID;
ahb_props.pNext = &ahb_format_props;
bool result = vkGetAndroidHardwareBufferPropertiesANDROID(
vk_device, ahb_handle.get(), &ahb_props);
if (result != VK_SUCCESS) {
LOG(ERROR) << "GetAndroidHardwareBufferProperties 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;
external_format.sType = VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID;
external_format.pNext = nullptr;
// If externalFormat is zero, the effect is as if the VkExternalFormatANDROID
// structure was not present. Otherwise, the image will have the specified
// external format.
external_format.externalFormat = 0;
// If image has an external format, format must be VK_FORMAT_UNDEFINED.
if (ahb_format_props.format == VK_FORMAT_UNDEFINED) {
// 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;
external_memory_image_info.sType =
VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO;
external_memory_image_info.pNext = &external_format;
external_memory_image_info.handleTypes =
VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID;
// Intended usage of the image.
VkImageUsageFlags usage_flags = 0;
// 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.
if (ahb_desc.usage & AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE) {
usage_flags = usage_flags | VK_IMAGE_USAGE_SAMPLED_BIT |
VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT;
}
if (ahb_desc.usage & AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT) {
usage_flags |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
}
if (ahb_desc.usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT) {
usage_flags |= VK_IMAGE_CREATE_PROTECTED_BIT;
}
// TODO(vikassoni) : AHARDWAREBUFFER_USAGE_GPU_CUBE_MAP is supported from API
// level 28 which is not part of current android_ndk version in chromium. Add
// equvalent VK usage later.
if (!usage_flags) {
LOG(ERROR) << "No valid usage flags found";
return false;
}
// Find the first set bit to use as memoryTypeIndex.
uint32_t memory_type_bits = ahb_props.memoryTypeBits;
int32_t type_index = -1;
for (uint32_t i = 0; memory_type_bits;
memory_type_bits = memory_type_bits >> 0x1, ++i) {
if (memory_type_bits & 0x1) {
type_index = i;
break;
}
}
if (type_index == -1) {
LOG(ERROR) << "No valid memoryTypeIndex found";
return false;
}
// Populate VkImageCreateInfo.
vk_image_info->sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
vk_image_info->pNext = &external_memory_image_info;
vk_image_info->flags = 0;
vk_image_info->imageType = VK_IMAGE_TYPE_2D;
vk_image_info->format = ahb_format_props.format;
vk_image_info->extent = {static_cast<uint32_t>(size.width()),
static_cast<uint32_t>(size.height()), 1};
vk_image_info->mipLevels = 1;
vk_image_info->arrayLayers = 1;
vk_image_info->samples = VK_SAMPLE_COUNT_1_BIT;
vk_image_info->tiling = VK_IMAGE_TILING_OPTIMAL;
vk_image_info->usage = usage_flags;
vk_image_info->sharingMode = VK_SHARING_MODE_EXCLUSIVE;
vk_image_info->queueFamilyIndexCount = 0;
vk_image_info->pQueueFamilyIndices = 0;
vk_image_info->initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
// Create Vk Image.
result = vkCreateImage(vk_device, vk_image_info, nullptr, vk_image);
if (result != VK_SUCCESS) {
LOG(ERROR) << "vkCreateImage failed : " << result;
return false;
}
// 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;
ahb_import_info.sType =
VK_STRUCTURE_TYPE_IMPORT_ANDROID_HARDWARE_BUFFER_INFO_ANDROID;
ahb_import_info.pNext = nullptr;
ahb_import_info.buffer = ahb_handle.get();
// If the VkMemoryAllocateInfo pNext chain includes a
// VkMemoryDedicatedAllocateInfo structure, then that structure includes a
// handle of the sole buffer or image resource that the memory can be bound
// to.
VkMemoryDedicatedAllocateInfo dedicated_alloc_info;
dedicated_alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO;
dedicated_alloc_info.pNext = &ahb_import_info;
dedicated_alloc_info.image = *vk_image;
dedicated_alloc_info.buffer = VK_NULL_HANDLE;
// An instance of the VkMemoryAllocateInfo structure defines a memory import
// operation.
VkMemoryAllocateInfo mem_alloc_info;
mem_alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
mem_alloc_info.pNext = &dedicated_alloc_info;
// If the parameters define an import operation and the external handle type
// is VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID,
// allocationSize must be the size returned by
// vkGetAndroidHardwareBufferPropertiesANDROID for the Android hardware
// buffer.
mem_alloc_info.allocationSize = ahb_props.allocationSize;
mem_alloc_info.memoryTypeIndex = type_index;
// A Vulkan device operates on data in device memory via memory objects that
// are represented in the API by a VkDeviceMemory handle.
// Allocate memory.
result =
vkAllocateMemory(vk_device, &mem_alloc_info, nullptr, vk_device_memory);
if (result != VK_SUCCESS) {
LOG(ERROR) << "vkAllocateMemory failed : " << result;
vkDestroyImage(vk_device, *vk_image, nullptr);
return false;
}
// Attach memory to the image object.
result = vkBindImageMemory(vk_device, *vk_image, *vk_device_memory, 0);
if (result != VK_SUCCESS) {
LOG(ERROR) << "vkBindImageMemory failed : " << result;
vkDestroyImage(vk_device, *vk_image, nullptr);
vkFreeMemory(vk_device, *vk_device_memory, nullptr);
return false;
}
*mem_allocation_size = mem_alloc_info.allocationSize;
return true;
}
} // namespace gpu