| // 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, {})) |
| return false; |
| |
| // Initialize platform function pointers |
| vkCreateAndroidSurfaceKHR_ = |
| reinterpret_cast<PFN_vkCreateAndroidSurfaceKHR>(vkGetInstanceProcAddr( |
| vulkan_instance_.vk_instance(), "vkCreateAndroidSurfaceKHR")); |
| if (!vkCreateAndroidSurfaceKHR_) |
| 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 |