v3dv/android: Add AHardwareBuffer support

The patch allows enabling Vulkan-based UI (SKIA, HWUI) on Android.
To enable - add 'TARGET_USES_VULKAN := true' into your device.mk file.

Passes:
 - dEQP-VK.api.external.memory.android_hardware_buffer.*
 - android.graphics.cts.BasicVulkanGpuTest
 - android.graphics.cts.MediaVulkanGpuTest

Camera preview window works.

Signed-off-by: Roman Stratiienko <roman.o.stratiienko@globallogic.com>
Reviewed-by: Alejandro Piñeiro <apinheiro@igalia.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/14195>
This commit is contained in:
Roman Stratiienko
2023-09-02 22:29:05 +03:00
committed by Marge Bot
parent def20cbb07
commit 733909a637
5 changed files with 271 additions and 8 deletions

View File

@@ -35,6 +35,9 @@
#include <vulkan/vk_android_native_buffer.h>
#include <vulkan/vk_icd.h>
#include "vk_android.h"
#include "vulkan/util/vk_enum_defines.h"
#include "util/libsync.h"
#include "util/log.h"
#include "util/os_file.h"
@@ -358,3 +361,184 @@ v3dv_GetSwapchainGrallocUsage2ANDROID(
return VK_SUCCESS;
}
#endif
/* ----------------------------- AHardwareBuffer --------------------------- */
static VkResult
get_ahb_buffer_format_properties2(VkDevice device_h, const struct AHardwareBuffer *buffer,
VkAndroidHardwareBufferFormatProperties2ANDROID *pProperties)
{
V3DV_FROM_HANDLE(v3dv_device, device, device_h);
/* Get a description of buffer contents . */
AHardwareBuffer_Desc desc;
AHardwareBuffer_describe(buffer, &desc);
/* Verify description. */
const uint64_t gpu_usage = AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE |
AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT |
AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER;
/* "Buffer must be a valid Android hardware buffer object with at least
* one of the AHARDWAREBUFFER_USAGE_GPU_* usage flags."
*/
if (!(desc.usage & (gpu_usage)))
return VK_ERROR_INVALID_EXTERNAL_HANDLE;
/* Fill properties fields based on description. */
VkAndroidHardwareBufferFormatProperties2ANDROID *p = pProperties;
p->samplerYcbcrConversionComponents.r = VK_COMPONENT_SWIZZLE_IDENTITY;
p->samplerYcbcrConversionComponents.g = VK_COMPONENT_SWIZZLE_IDENTITY;
p->samplerYcbcrConversionComponents.b = VK_COMPONENT_SWIZZLE_IDENTITY;
p->samplerYcbcrConversionComponents.a = VK_COMPONENT_SWIZZLE_IDENTITY;
p->suggestedYcbcrModel = VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_601;
p->suggestedYcbcrRange = VK_SAMPLER_YCBCR_RANGE_ITU_FULL;
p->suggestedXChromaOffset = VK_CHROMA_LOCATION_MIDPOINT;
p->suggestedYChromaOffset = VK_CHROMA_LOCATION_MIDPOINT;
VkFormatProperties2 format_properties = {.sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2};
p->format = vk_ahb_format_to_image_format(desc.format);
VkFormat external_format = p->format;
if (p->format != VK_FORMAT_UNDEFINED)
goto finish;
/* External format only case
*
* From vkGetAndroidHardwareBufferPropertiesANDROID spec:
* "If the Android hardware buffer has one of the formats listed in the Format
* Equivalence table (see spec.), then format must have the equivalent Vulkan
* format listed in the table. Otherwise, format may be VK_FORMAT_UNDEFINED,
* indicating the Android hardware buffer can only be used with an external format."
*
* From SKIA source code analysis: p->format MUST be VK_FORMAT_UNDEFINED, if the
* format is not in the Equivalence table.
*/
struct u_gralloc_buffer_handle gr_handle = {
.handle = AHardwareBuffer_getNativeHandle(buffer),
.pixel_stride = desc.stride,
.hal_format = desc.format,
};
struct u_gralloc_buffer_basic_info info;
if (u_gralloc_get_buffer_basic_info(device->gralloc, &gr_handle, &info) != 0)
return VK_ERROR_INVALID_EXTERNAL_HANDLE;
switch (info.drm_fourcc) {
case DRM_FORMAT_YVU420:
/* Assuming that U and V planes are swapped earlier */
external_format = VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM;
break;
case DRM_FORMAT_NV12:
external_format = VK_FORMAT_G8_B8R8_2PLANE_420_UNORM;
break;
default:;
mesa_loge("Unsupported external DRM format: %d", info.drm_fourcc);
return VK_ERROR_INVALID_EXTERNAL_HANDLE;
}
struct u_gralloc_buffer_color_info color_info;
if (u_gralloc_get_buffer_color_info(device->gralloc, &gr_handle, &color_info) == 0) {
switch (color_info.yuv_color_space) {
case __DRI_YUV_COLOR_SPACE_ITU_REC601:
p->suggestedYcbcrModel = VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_601;
break;
case __DRI_YUV_COLOR_SPACE_ITU_REC709:
p->suggestedYcbcrModel = VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_709;
break;
case __DRI_YUV_COLOR_SPACE_ITU_REC2020:
p->suggestedYcbcrModel = VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_2020;
break;
default:
break;
}
p->suggestedYcbcrRange = (color_info.sample_range == __DRI_YUV_NARROW_RANGE) ?
VK_SAMPLER_YCBCR_RANGE_ITU_NARROW : VK_SAMPLER_YCBCR_RANGE_ITU_FULL;
p->suggestedXChromaOffset = (color_info.horizontal_siting == __DRI_YUV_CHROMA_SITING_0_5) ?
VK_CHROMA_LOCATION_MIDPOINT : VK_CHROMA_LOCATION_COSITED_EVEN;
p->suggestedYChromaOffset = (color_info.vertical_siting == __DRI_YUV_CHROMA_SITING_0_5) ?
VK_CHROMA_LOCATION_MIDPOINT : VK_CHROMA_LOCATION_COSITED_EVEN;
}
finish:
v3dv_GetPhysicalDeviceFormatProperties2(v3dv_physical_device_to_handle(device->pdevice),
external_format, &format_properties);
/* v3dv doesn't support direct sampling from linear images but has a logic to copy
* from linear to tiled images implicitly before sampling. Therefore expose optimal
* features for both linear and optimal tiling.
*/
p->formatFeatures = format_properties.formatProperties.optimalTilingFeatures;
p->externalFormat = external_format;
/* From vkGetAndroidHardwareBufferPropertiesANDROID spec:
* "The formatFeatures member *must* include
* VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_BIT and at least one of
* VK_FORMAT_FEATURE_2_MIDPOINT_CHROMA_SAMPLES_BIT or
* VK_FORMAT_FEATURE_2_COSITED_CHROMA_SAMPLES_BIT"
*/
p->formatFeatures |= VK_FORMAT_FEATURE_2_MIDPOINT_CHROMA_SAMPLES_BIT_KHR;
return VK_SUCCESS;
}
VkResult
v3dv_GetAndroidHardwareBufferPropertiesANDROID(VkDevice device_h,
const struct AHardwareBuffer *buffer,
VkAndroidHardwareBufferPropertiesANDROID *pProperties)
{
V3DV_FROM_HANDLE(v3dv_device, dev, device_h);
struct v3dv_physical_device *pdevice = dev->pdevice;
VkResult result;
VkAndroidHardwareBufferFormatPropertiesANDROID *format_prop =
vk_find_struct(pProperties->pNext, ANDROID_HARDWARE_BUFFER_FORMAT_PROPERTIES_ANDROID);
/* Fill format properties of an Android hardware buffer. */
if (format_prop) {
VkAndroidHardwareBufferFormatProperties2ANDROID format_prop2 = {
.sType = VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_FORMAT_PROPERTIES_2_ANDROID,
};
result = get_ahb_buffer_format_properties2(device_h, buffer, &format_prop2);
if (result != VK_SUCCESS)
return result;
format_prop->format = format_prop2.format;
format_prop->externalFormat = format_prop2.externalFormat;
format_prop->formatFeatures =
vk_format_features2_to_features(format_prop2.formatFeatures);
format_prop->samplerYcbcrConversionComponents =
format_prop2.samplerYcbcrConversionComponents;
format_prop->suggestedYcbcrModel = format_prop2.suggestedYcbcrModel;
format_prop->suggestedYcbcrRange = format_prop2.suggestedYcbcrRange;
format_prop->suggestedXChromaOffset = format_prop2.suggestedXChromaOffset;
format_prop->suggestedYChromaOffset = format_prop2.suggestedYChromaOffset;
}
VkAndroidHardwareBufferFormatProperties2ANDROID *format_prop2 =
vk_find_struct(pProperties->pNext, ANDROID_HARDWARE_BUFFER_FORMAT_PROPERTIES_2_ANDROID);
if (format_prop2) {
result = get_ahb_buffer_format_properties2(device_h, buffer, format_prop2);
if (result != VK_SUCCESS)
return result;
}
const native_handle_t *handle = AHardwareBuffer_getNativeHandle(buffer);
assert(handle && handle->numFds > 0);
pProperties->allocationSize = lseek(handle->data[0], 0, SEEK_END);
/* All memory types. */
pProperties->memoryTypeBits = (1u << pdevice->memory.memoryTypeCount) - 1;
return VK_SUCCESS;
}

View File

@@ -49,6 +49,7 @@
#include "git_sha1.h"
#include "util/build_id.h"
#include "util/os_file.h"
#include "util/u_debug.h"
#include "util/format/u_format.h"
@@ -2404,11 +2405,6 @@ v3dv_AllocateMemory(VkDevice _device,
VkResult result;
if (mem->vk.ahardware_buffer) {
result = VK_ERROR_FEATURE_NOT_PRESENT;
goto done;
}
if (wsi_info) {
result = device_alloc_for_wsi(device, pAllocator, mem, alloc_size);
} else if (fd_info && fd_info->handleType) {
@@ -2418,11 +2414,20 @@ v3dv_AllocateMemory(VkDevice _device,
fd_info->fd, alloc_size, &mem->bo);
if (result == VK_SUCCESS)
close(fd_info->fd);
} else if (mem->vk.ahardware_buffer) {
#ifdef ANDROID
const native_handle_t *handle = AHardwareBuffer_getNativeHandle(mem->vk.ahardware_buffer);
assert(handle->numFds > 0);
size_t size = lseek(handle->data[0], 0, SEEK_END);
result = device_import_bo(device, pAllocator,
handle->data[0], size, &mem->bo);
#else
result = VK_ERROR_FEATURE_NOT_PRESENT;
#endif
} else {
result = device_alloc(device, mem, alloc_size);
}
done:
if (result != VK_SUCCESS) {
vk_device_memory_destroy(&device->vk, pAllocator, &mem->vk);
return vk_error(device, result);
@@ -2668,6 +2673,39 @@ v3dv_BindImageMemory2(VkDevice _device,
const VkBindImageMemoryInfo *pBindInfos)
{
for (uint32_t i = 0; i < bindInfoCount; i++) {
#ifdef ANDROID
V3DV_FROM_HANDLE(v3dv_device_memory, mem, pBindInfos[i].memory);
V3DV_FROM_HANDLE(v3dv_device, device, _device);
if (mem != NULL && mem->vk.ahardware_buffer) {
AHardwareBuffer_Desc description;
const native_handle_t *handle = AHardwareBuffer_getNativeHandle(mem->vk.ahardware_buffer);
V3DV_FROM_HANDLE(v3dv_image, image, pBindInfos[i].image);
AHardwareBuffer_describe(mem->vk.ahardware_buffer, &description);
struct u_gralloc_buffer_handle gr_handle = {
.handle = handle,
.pixel_stride = description.stride,
.hal_format = description.format,
};
VkResult result = v3dv_gralloc_to_drm_explicit_layout(
device->gralloc,
&gr_handle,
image->android_explicit_layout,
image->android_plane_layouts,
V3DV_MAX_PLANE_COUNT);
if (result != VK_SUCCESS)
return result;
result = v3dv_update_image_layout(
device, image, image->android_explicit_layout->drmFormatModifier,
/* disjoint = */ false, image->android_explicit_layout);
if (result != VK_SUCCESS)
return result;
}
#endif
const VkBindImageMemorySwapchainInfoKHR *swapchain_info =
vk_find_struct_const(pBindInfos->pNext,
BIND_IMAGE_MEMORY_SWAPCHAIN_INFO_KHR);

View File

@@ -22,13 +22,18 @@
*/
#include "v3dv_private.h"
#include "vk_util.h"
#ifdef ANDROID
#include "vk_android.h"
#endif
#include "vk_enum_defines.h"
#include "vk_util.h"
#include "drm-uapi/drm_fourcc.h"
#include "util/format/u_format.h"
#include "vulkan/wsi/wsi_common.h"
#include <vulkan/vulkan_android.h>
const uint8_t *
v3dv_get_format_swizzle(struct v3dv_device *device, VkFormat f, uint8_t plane)
{
@@ -660,6 +665,7 @@ v3dv_GetPhysicalDeviceImageFormatProperties2(VkPhysicalDevice physicalDevice,
const VkPhysicalDeviceExternalImageFormatInfo *external_info = NULL;
const VkPhysicalDeviceImageDrmFormatModifierInfoEXT *drm_format_mod_info = NULL;
VkExternalImageFormatProperties *external_props = NULL;
UNUSED VkAndroidHardwareBufferUsageANDROID *android_usage = NULL;
VkSamplerYcbcrConversionImageFormatProperties *ycbcr_props = NULL;
VkImageTiling tiling = base_info->tiling;
@@ -700,6 +706,9 @@ v3dv_GetPhysicalDeviceImageFormatProperties2(VkPhysicalDevice physicalDevice,
case VK_STRUCTURE_TYPE_EXTERNAL_IMAGE_FORMAT_PROPERTIES:
external_props = (void *) s;
break;
case VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_USAGE_ANDROID:
android_usage = (void *)s;
break;
case VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_IMAGE_FORMAT_PROPERTIES:
ycbcr_props = (void *) s;
break;
@@ -723,12 +732,26 @@ v3dv_GetPhysicalDeviceImageFormatProperties2(VkPhysicalDevice physicalDevice,
if (external_props)
external_props->externalMemoryProperties = prime_fd_props;
break;
case VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID:
if (external_props) {
external_props->externalMemoryProperties.exportFromImportedHandleTypes = 0;
external_props->externalMemoryProperties.compatibleHandleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID;
external_props->externalMemoryProperties.externalMemoryFeatures = VK_EXTERNAL_MEMORY_FEATURE_DEDICATED_ONLY_BIT | VK_EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT | VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT;
}
break;
default:
result = VK_ERROR_FORMAT_NOT_SUPPORTED;
break;
}
}
if (android_usage) {
#ifdef ANDROID
android_usage->androidHardwareBufferUsage =
vk_image_usage_to_ahb_usage(base_info->flags, base_info->usage);
#endif
}
done:
return result;
}

View File

@@ -490,6 +490,14 @@ v3dv_image_init(struct v3dv_device *device,
*/
image->vk.create_flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT;
#ifdef ANDROID
/* At this time, an AHB handle is not yet provided.
* Image layout will be filled up during vkBindImageMemory2
*/
if (image->is_ahb)
return VK_SUCCESS;
#endif
bool disjoint = image->vk.create_flags & VK_IMAGE_CREATE_DISJOINT_BIT;
return v3dv_update_image_layout(device, image, modifier, disjoint,
@@ -519,7 +527,12 @@ create_image(struct v3dv_device *device,
if (native_buffer != NULL)
image->is_native_buffer_memory = true;
if (image->is_native_buffer_memory) {
image->is_ahb = external_info && (external_info->handleTypes &
VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID);
assert(!(image->is_ahb && image->is_native_buffer_memory));
if (image->is_ahb || image->is_native_buffer_memory) {
image->android_explicit_layout = vk_alloc2(&device->vk.alloc, pAllocator,
sizeof(VkImageDrmFormatModifierExplicitCreateInfoEXT),
8,
@@ -536,7 +549,9 @@ create_image(struct v3dv_device *device,
result = vk_error(device, VK_ERROR_OUT_OF_HOST_MEMORY);
goto fail;
}
}
if (image->is_native_buffer_memory) {
struct u_gralloc_buffer_handle gr_handle = {
.handle = native_buffer->handle,
.hal_format = native_buffer->format,

View File

@@ -66,6 +66,7 @@
#endif
#ifdef ANDROID
#include <vndk/hardware_buffer.h>
#include "util/u_gralloc/u_gralloc.h"
#endif
@@ -736,6 +737,8 @@ struct v3dv_image {
#ifdef ANDROID
/* Image is backed by VK_ANDROID_native_buffer, */
bool is_native_buffer_memory;
/* Image is backed by VK_ANDROID_external_memory_android_hardware_buffer */
bool is_ahb;
VkImageDrmFormatModifierExplicitCreateInfoEXT *android_explicit_layout;
VkSubresourceLayout *android_plane_layouts;
#endif