/// 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(__ANDROID__) || defined(__linux__) #include #define DRM_FORMAT_YVU420_ANDROID fourcc_code('9', '9', '9', '7') #define DRM_FORMAT_D16_UNORM fourcc_code('9', '9', '9', '6') #define DRM_FORMAT_D24_UNORM fourcc_code('9', '9', '9', '5') #define DRM_FORMAT_D24_UNORM_S8_UINT fourcc_code('9', '9', '9', '4') #define DRM_FORMAT_D32_FLOAT fourcc_code('9', '9', '9', '3') #define DRM_FORMAT_D32_FLOAT_S8_UINT fourcc_code('9', '9', '9', '2') #define DRM_FORMAT_S8_UINT fourcc_code('9', '9', '9', '1') #endif #include #include "gfxstream/guest/Gralloc.h" #include "vk_format_info.h" #include "vk_util.h" #include "util/log.h" namespace gfxstream { namespace 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( gfxstream::Gralloc* grallocHelper, const AHardwareBuffer* buffer, VkAndroidHardwareBufferPropertiesANDROID* pProperties) { VkAndroidHardwareBufferFormatPropertiesANDROID* ahbFormatProps = vk_find_struct(pProperties); const auto format = grallocHelper->getFormat(buffer); if (ahbFormatProps) { switch (format) { case AHARDWAREBUFFER_FORMAT_R8_UNORM: ahbFormatProps->format = VK_FORMAT_R8_UNORM; ahbFormatProps->externalFormat = DRM_FORMAT_R8; break; case AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM: ahbFormatProps->format = VK_FORMAT_R8G8B8A8_UNORM; ahbFormatProps->externalFormat = DRM_FORMAT_ABGR8888; break; case AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM: ahbFormatProps->format = VK_FORMAT_R8G8B8A8_UNORM; ahbFormatProps->externalFormat = DRM_FORMAT_XBGR8888; break; case AHARDWAREBUFFER_FORMAT_R8G8B8_UNORM: ahbFormatProps->format = VK_FORMAT_R8G8B8_UNORM; ahbFormatProps->externalFormat = DRM_FORMAT_BGR888; break; case AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM: ahbFormatProps->format = VK_FORMAT_R5G6B5_UNORM_PACK16; ahbFormatProps->externalFormat = DRM_FORMAT_RGB565; break; case AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT: ahbFormatProps->format = VK_FORMAT_R16G16B16A16_SFLOAT; ahbFormatProps->externalFormat = DRM_FORMAT_ABGR16161616F; break; case AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM: ahbFormatProps->format = VK_FORMAT_A2B10G10R10_UNORM_PACK32; ahbFormatProps->externalFormat = DRM_FORMAT_ABGR2101010; break; case AHARDWAREBUFFER_FORMAT_D16_UNORM: ahbFormatProps->format = VK_FORMAT_D16_UNORM; ahbFormatProps->externalFormat = DRM_FORMAT_D16_UNORM; break; case AHARDWAREBUFFER_FORMAT_D24_UNORM: ahbFormatProps->format = VK_FORMAT_X8_D24_UNORM_PACK32; ahbFormatProps->externalFormat = DRM_FORMAT_D24_UNORM; break; case AHARDWAREBUFFER_FORMAT_D24_UNORM_S8_UINT: ahbFormatProps->format = VK_FORMAT_D24_UNORM_S8_UINT; ahbFormatProps->externalFormat = DRM_FORMAT_D24_UNORM_S8_UINT; break; case AHARDWAREBUFFER_FORMAT_D32_FLOAT: ahbFormatProps->format = VK_FORMAT_D32_SFLOAT; ahbFormatProps->externalFormat = DRM_FORMAT_D32_FLOAT; break; case AHARDWAREBUFFER_FORMAT_D32_FLOAT_S8_UINT: ahbFormatProps->format = VK_FORMAT_D32_SFLOAT_S8_UINT; ahbFormatProps->externalFormat = DRM_FORMAT_D32_FLOAT_S8_UINT; break; case AHARDWAREBUFFER_FORMAT_S8_UINT: ahbFormatProps->format = VK_FORMAT_S8_UINT; ahbFormatProps->externalFormat = DRM_FORMAT_S8_UINT; break; default: ahbFormatProps->format = VK_FORMAT_UNDEFINED; ahbFormatProps->externalFormat = DRM_FORMAT_INVALID; } // 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(__ANDROID__) || defined(__linux__) if (android_format_is_yuv(format)) { uint32_t drmFormat = grallocHelper->getFormatDrmFourcc(buffer); ahbFormatProps->externalFormat = static_cast(drmFormat); 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. break; 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_YUV420: // YUV420 is a Y-plane, then a U-plane, and then a V-plane and is // VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM 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: // YVU420 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: mesa_loge("Unhandled YUV drm format:%u", drmFormat); break; } } } #endif ahbFormatProps->suggestedYcbcrModel = android_format_is_yuv(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(buffer); if (!colorBufferHandle) { return VK_ERROR_INVALID_EXTERNAL_HANDLE; } pProperties->allocationSize = grallocHelper->getAllocatedSize(buffer); return VK_SUCCESS; } // Based on Intel ANV implementation. VkResult getMemoryAndroidHardwareBufferANDROID(gfxstream::Gralloc* gralloc, 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; gralloc->acquire(*pBuffer); return VK_SUCCESS; } VkResult importAndroidHardwareBuffer(gfxstream::Gralloc* grallocHelper, const VkImportAndroidHardwareBufferInfoANDROID* info, struct AHardwareBuffer** importOut) { if (!info || !info->buffer) { return VK_ERROR_INVALID_EXTERNAL_HANDLE; } auto ahb = info->buffer; uint32_t colorBufferHandle = grallocHelper->getHostHandle(ahb); if (!colorBufferHandle) { return VK_ERROR_INVALID_EXTERNAL_HANDLE; } grallocHelper->acquire(ahb); if (importOut) *importOut = ahb; return VK_SUCCESS; } VkResult createAndroidHardwareBuffer(gfxstream::Gralloc* gralloc, 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* ahb = NULL; if (gralloc->allocate(w, h, format, usage, &ahb) != 0) { return VK_ERROR_OUT_OF_HOST_MEMORY; } *out = ahb; return VK_SUCCESS; } } // namespace vk } // namespace gfxstream