blob: 057b7e72e119d3f41d424283428d9e268d5edb61 [file] [log] [blame]
/// Copyright (C) 2019 The Android Open Source Project
// Copyright (C) 2019 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "AndroidHardwareBuffer.h"
#if !defined(HOST_BUILD)
#if defined(__ANDROID__) || defined(__linux__)
#include <drm_fourcc.h>
#define DRM_FORMAT_YVU420_ANDROID fourcc_code('9', '9', '9', '7')
#endif
#endif
#include "../OpenglSystemCommon/HostConnection.h"
#include "vk_format_info.h"
#include "vk_util.h"
#include <assert.h>
namespace goldfish_vk {
// From Intel ANV implementation.
/* Construct ahw usage mask from image usage bits, see
* 'AHardwareBuffer Usage Equivalence' in Vulkan spec.
*/
uint64_t
getAndroidHardwareBufferUsageFromVkUsage(const VkImageCreateFlags vk_create,
const VkImageUsageFlags vk_usage)
{
uint64_t ahw_usage = 0;
if (vk_usage & VK_IMAGE_USAGE_SAMPLED_BIT)
ahw_usage |= AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE;
if (vk_usage & VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT)
ahw_usage |= AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE;
if (vk_usage & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT)
ahw_usage |= AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT;
if (vk_create & VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT)
ahw_usage |= AHARDWAREBUFFER_USAGE_GPU_CUBE_MAP;
if (vk_create & VK_IMAGE_CREATE_PROTECTED_BIT)
ahw_usage |= AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT;
/* No usage bits set - set at least one GPU usage. */
if (ahw_usage == 0)
ahw_usage = AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE;
return ahw_usage;
}
VkResult getAndroidHardwareBufferPropertiesANDROID(
Gralloc* grallocHelper,
const VkPhysicalDeviceMemoryProperties* memProps,
VkDevice,
const AHardwareBuffer* buffer,
VkAndroidHardwareBufferPropertiesANDROID* pProperties) {
const native_handle_t *handle =
AHardwareBuffer_getNativeHandle(buffer);
VkAndroidHardwareBufferFormatPropertiesANDROID* ahbFormatProps =
vk_find_struct<VkAndroidHardwareBufferFormatPropertiesANDROID>(pProperties);
if (ahbFormatProps) {
AHardwareBuffer_Desc desc;
AHardwareBuffer_describe(buffer, &desc);
const uint64_t gpu_usage =
AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE |
AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT |
AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER;
if (!(desc.usage & (gpu_usage))) {
return VK_ERROR_INVALID_EXTERNAL_HANDLE;
}
switch(desc.format) {
case AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM:
ahbFormatProps->format = VK_FORMAT_R8G8B8A8_UNORM;
break;
case AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM:
ahbFormatProps->format = VK_FORMAT_R8G8B8A8_UNORM;
break;
case AHARDWAREBUFFER_FORMAT_R8G8B8_UNORM:
ahbFormatProps->format = VK_FORMAT_R8G8B8_UNORM;
break;
case AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM:
ahbFormatProps->format = VK_FORMAT_R5G6B5_UNORM_PACK16;
break;
case AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT:
ahbFormatProps->format = VK_FORMAT_R16G16B16A16_SFLOAT;
break;
case AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM:
ahbFormatProps->format = VK_FORMAT_A2B10G10R10_UNORM_PACK32;
break;
case AHARDWAREBUFFER_FORMAT_D16_UNORM:
ahbFormatProps->format = VK_FORMAT_D16_UNORM;
break;
case AHARDWAREBUFFER_FORMAT_D24_UNORM:
ahbFormatProps->format = VK_FORMAT_X8_D24_UNORM_PACK32;
break;
case AHARDWAREBUFFER_FORMAT_D24_UNORM_S8_UINT:
ahbFormatProps->format = VK_FORMAT_D24_UNORM_S8_UINT;
break;
case AHARDWAREBUFFER_FORMAT_D32_FLOAT:
ahbFormatProps->format = VK_FORMAT_D32_SFLOAT;
break;
case AHARDWAREBUFFER_FORMAT_D32_FLOAT_S8_UINT:
ahbFormatProps->format = VK_FORMAT_D32_SFLOAT_S8_UINT;
break;
case AHARDWAREBUFFER_FORMAT_S8_UINT:
ahbFormatProps->format = VK_FORMAT_S8_UINT;
break;
default:
ahbFormatProps->format = VK_FORMAT_UNDEFINED;
}
ahbFormatProps->externalFormat = desc.format;
// The formatFeatures member must include
// VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT and at least one of
// VK_FORMAT_FEATURE_MIDPOINT_CHROMA_SAMPLES_BIT or
// VK_FORMAT_FEATURE_COSITED_CHROMA_SAMPLES_BIT, and should include
// VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT and
// VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_LINEAR_FILTER_BIT.
// org.skia.skqp.SkQPRunner#UnitTest_VulkanHardwareBuffer* requires the following:
// VK_FORMAT_FEATURE_TRANSFER_SRC_BIT
// VK_FORMAT_FEATURE_TRANSFER_DST_BIT
// VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT
ahbFormatProps->formatFeatures =
VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT |
VK_FORMAT_FEATURE_MIDPOINT_CHROMA_SAMPLES_BIT |
VK_FORMAT_FEATURE_TRANSFER_SRC_BIT |
VK_FORMAT_FEATURE_TRANSFER_DST_BIT |
VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT;
// "Implementations may not always be able to determine the color model,
// numerical range, or chroma offsets of the image contents, so the values in
// VkAndroidHardwareBufferFormatPropertiesANDROID are only suggestions.
// Applications should treat these values as sensible defaults to use in the
// absence of more reliable information obtained through some other means."
ahbFormatProps->samplerYcbcrConversionComponents.r = VK_COMPONENT_SWIZZLE_IDENTITY;
ahbFormatProps->samplerYcbcrConversionComponents.g = VK_COMPONENT_SWIZZLE_IDENTITY;
ahbFormatProps->samplerYcbcrConversionComponents.b = VK_COMPONENT_SWIZZLE_IDENTITY;
ahbFormatProps->samplerYcbcrConversionComponents.a = VK_COMPONENT_SWIZZLE_IDENTITY;
#if !defined(HOST_BUILD)
#if defined(__ANDROID__) || defined(__linux__)
if (android_format_is_yuv(desc.format)) {
uint32_t drmFormat = grallocHelper->getFormatDrmFourcc(handle);
if (drmFormat) {
// The host renderer is not aware of the plane ordering for YUV formats used
// in the guest and simply knows that the format "layout" is one of:
//
// * VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16
// * VK_FORMAT_G8_B8R8_2PLANE_420_UNORM
// * VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM
//
// With this, the guest needs to adjust the component swizzle based on plane
// ordering to ensure that the channels are interpreted correctly.
//
// From the Vulkan spec's "Sampler Y'CBCR Conversion" section:
//
// * Y comes from the G-channel (after swizzle)
// * U (CB) comes from the B-channel (after swizzle)
// * V (CR) comes from the R-channel (after swizzle)
//
// See https://www.khronos.org/registry/vulkan/specs/1.3-extensions/html/vkspec.html#textures-sampler-YCbCr-conversion
//
// To match the above, the guest needs to swizzle such that:
//
// * Y ends up in the G-channel
// * U (CB) ends up in the B-channel
// * V (CB) ends up in the R-channel
switch (drmFormat) {
case DRM_FORMAT_NV12:
// NV12 is a Y-plane followed by a interleaved UV-plane and is
// VK_FORMAT_G8_B8R8_2PLANE_420_UNORM on the host.
case DRM_FORMAT_P010:
// P010 is a Y-plane followed by a interleaved UV-plane and is
// VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16 on the host.
break;
case DRM_FORMAT_NV21:
// NV21 is a Y-plane followed by a interleaved VU-plane and is
// VK_FORMAT_G8_B8R8_2PLANE_420_UNORM on the host.
case DRM_FORMAT_YVU420:
// YV12 is a Y-plane, then a V-plane, and then a U-plane and is
// VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM on the host.
case DRM_FORMAT_YVU420_ANDROID:
// DRM_FORMAT_YVU420_ANDROID is the same as DRM_FORMAT_YVU420 with
// Android's extra alignement requirements.
ahbFormatProps->samplerYcbcrConversionComponents.r = VK_COMPONENT_SWIZZLE_B;
ahbFormatProps->samplerYcbcrConversionComponents.b = VK_COMPONENT_SWIZZLE_R;
break;
default:
ALOGE("%s: Unhandled YUV drm format:%" PRIu32, __FUNCTION__, drmFormat);
break;
}
}
}
#endif
#endif
ahbFormatProps->suggestedYcbcrModel =
android_format_is_yuv(desc.format) ?
VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_601 :
VK_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY;
ahbFormatProps->suggestedYcbcrRange = VK_SAMPLER_YCBCR_RANGE_ITU_FULL;
ahbFormatProps->suggestedXChromaOffset = VK_CHROMA_LOCATION_MIDPOINT;
ahbFormatProps->suggestedYChromaOffset = VK_CHROMA_LOCATION_MIDPOINT;
}
uint32_t colorBufferHandle =
grallocHelper->getHostHandle(handle);
if (!colorBufferHandle) {
return VK_ERROR_INVALID_EXTERNAL_HANDLE;
}
assert(memProps->memoryTypeCount < VK_MAX_MEMORY_TYPES);
pProperties->memoryTypeBits = (1u << memProps->memoryTypeCount) - 1;
pProperties->allocationSize =
grallocHelper->getAllocatedSize(handle);
return VK_SUCCESS;
}
// Based on Intel ANV implementation.
VkResult getMemoryAndroidHardwareBufferANDROID(struct AHardwareBuffer **pBuffer) {
/* Some quotes from Vulkan spec:
*
* "If the device memory was created by importing an Android hardware
* buffer, vkGetMemoryAndroidHardwareBufferANDROID must return that same
* Android hardware buffer object."
*
* "VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID must
* have been included in VkExportMemoryAllocateInfo::handleTypes when
* memory was created."
*/
if (!pBuffer) return VK_ERROR_OUT_OF_HOST_MEMORY;
if (!(*pBuffer)) return VK_ERROR_OUT_OF_HOST_MEMORY;
AHardwareBuffer_acquire(*pBuffer);
return VK_SUCCESS;
}
VkResult importAndroidHardwareBuffer(
Gralloc* grallocHelper,
const VkImportAndroidHardwareBufferInfoANDROID* info,
struct AHardwareBuffer **importOut) {
if (!info || !info->buffer) {
return VK_ERROR_INVALID_EXTERNAL_HANDLE;
}
uint32_t colorBufferHandle =
grallocHelper->getHostHandle(
AHardwareBuffer_getNativeHandle(info->buffer));
if (!colorBufferHandle) {
return VK_ERROR_INVALID_EXTERNAL_HANDLE;
}
auto ahb = info->buffer;
AHardwareBuffer_acquire(ahb);
if (importOut) *importOut = ahb;
return VK_SUCCESS;
}
VkResult createAndroidHardwareBuffer(
bool hasDedicatedImage,
bool hasDedicatedBuffer,
const VkExtent3D& imageExtent,
uint32_t imageLayers,
VkFormat imageFormat,
VkImageUsageFlags imageUsage,
VkImageCreateFlags imageCreateFlags,
VkDeviceSize bufferSize,
VkDeviceSize allocationInfoAllocSize,
struct AHardwareBuffer **out) {
uint32_t w = 0;
uint32_t h = 1;
uint32_t layers = 1;
uint32_t format = 0;
uint64_t usage = 0;
/* If caller passed dedicated information. */
if (hasDedicatedImage) {
w = imageExtent.width;
h = imageExtent.height;
layers = imageLayers;
format = android_format_from_vk(imageFormat);
usage = getAndroidHardwareBufferUsageFromVkUsage(imageCreateFlags, imageUsage);
} else if (hasDedicatedBuffer) {
w = bufferSize;
format = AHARDWAREBUFFER_FORMAT_BLOB;
usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN |
AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN |
AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER;
} else {
w = allocationInfoAllocSize;
format = AHARDWAREBUFFER_FORMAT_BLOB;
usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN |
AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN |
AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER;
}
struct AHardwareBuffer *ahw = NULL;
struct AHardwareBuffer_Desc desc = {
.width = w,
.height = h,
.layers = layers,
.format = format,
.usage = usage,
};
if (AHardwareBuffer_allocate(&desc, &ahw) != 0) {
return VK_ERROR_OUT_OF_HOST_MEMORY;
}
*out = ahw;
return VK_SUCCESS;
}
} // namespace goldfish_vk