| /// 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 <drm_fourcc.h> |
| #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 <assert.h> |
| |
| #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<VkAndroidHardwareBufferFormatPropertiesANDROID>(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<uint64_t>(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 |