Add support for external textures in Vulkan, specifically GrAHardwareBufferImageGenerator.

Bug: skia:
Change-Id: Ic2e79734cd1a450bc30dcd441eaed10d8b8ccda2
Reviewed-on: https://skia-review.googlesource.com/c/164900
Commit-Queue: Greg Daniel <egdaniel@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
diff --git a/src/gpu/GrAHardwareBufferImageGenerator.cpp b/src/gpu/GrAHardwareBufferImageGenerator.cpp
index b7ca5bd..b6cc28d 100644
--- a/src/gpu/GrAHardwareBufferImageGenerator.cpp
+++ b/src/gpu/GrAHardwareBufferImageGenerator.cpp
@@ -189,19 +189,44 @@
         return GrBackendTexture();
     }
 
-    SkASSERT(format == hwbFormatProps.format);
-    SkASSERT(SkToBool(VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT & hwbFormatProps.formatFeatures) &&
-             SkToBool(VK_FORMAT_FEATURE_TRANSFER_SRC_BIT & hwbFormatProps.formatFeatures) &&
-             SkToBool(VK_FORMAT_FEATURE_TRANSFER_DST_BIT & hwbFormatProps.formatFeatures));
+    VkExternalFormatANDROID externalFormat;
+    externalFormat.sType = VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID;
+    externalFormat.pNext = nullptr;
+    externalFormat.externalFormat = 0;  // If this is zero it is as if we aren't using this struct.
 
-    const VkExternalMemoryImageCreateInfo externalMemoryImageInfo {
-        VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO, // sType
-        nullptr, // pNext
-        VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID, // handleTypes
+    const GrVkYcbcrConversionInfo* ycbcrConversion = backendFormat.getVkYcbcrConversionInfo();
+    if (!ycbcrConversion) {
+        return GrBackendTexture();
+    }
+
+    if (hwbFormatProps.format != VK_FORMAT_UNDEFINED) {
+        // TODO: We should not assume the transfer features here and instead should have a way for
+        // Ganesh's tracking of intenral images to report whether or not they support transfers.
+        SkASSERT(SkToBool(VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT & hwbFormatProps.formatFeatures) &&
+                 SkToBool(VK_FORMAT_FEATURE_TRANSFER_SRC_BIT & hwbFormatProps.formatFeatures) &&
+                 SkToBool(VK_FORMAT_FEATURE_TRANSFER_DST_BIT & hwbFormatProps.formatFeatures));
+        SkASSERT(!ycbcrConversion->isValid());
+    } else {
+        SkASSERT(ycbcrConversion->isValid());
+        // We have an external only format
+        SkASSERT(SkToBool(VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT & hwbFormatProps.formatFeatures));
+        SkASSERT(format == VK_FORMAT_UNDEFINED);
+        SkASSERT(hwbFormatProps.externalFormat == ycbcrConversion->fExternalFormat);
+        externalFormat.externalFormat = hwbFormatProps.externalFormat;
+    }
+    SkASSERT(format == hwbFormatProps.format);
+
+    const VkExternalMemoryImageCreateInfo externalMemoryImageInfo{
+            VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO,                 // sType
+            &externalFormat,                                                     // pNext
+            VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID,  // handleTypes
     };
-    VkImageUsageFlags usageFlags = VK_IMAGE_USAGE_SAMPLED_BIT |
-                                   VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
-                                   VK_IMAGE_USAGE_TRANSFER_DST_BIT;
+    VkImageUsageFlags usageFlags = VK_IMAGE_USAGE_SAMPLED_BIT;
+    if (format != VK_FORMAT_UNDEFINED) {
+        usageFlags = usageFlags |
+                VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
+                VK_IMAGE_USAGE_TRANSFER_DST_BIT;
+    }
 
     // TODO: Check the supported tilings vkGetPhysicalDeviceImageFormatProperties2 to see if we have
     // to use linear. Add better linear support throughout Ganesh.
@@ -231,22 +256,6 @@
         return GrBackendTexture();
     }
 
-    VkImageMemoryRequirementsInfo2 memReqsInfo;
-    memReqsInfo.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2;
-    memReqsInfo.pNext = nullptr;
-    memReqsInfo.image = image;
-
-    VkMemoryDedicatedRequirements dedicatedMemReqs;
-    dedicatedMemReqs.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS;
-    dedicatedMemReqs.pNext = nullptr;
-
-    VkMemoryRequirements2 memReqs;
-    memReqs.sType = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2;
-    memReqs.pNext = &dedicatedMemReqs;
-
-    VK_CALL(GetImageMemoryRequirements2(device, &memReqsInfo, &memReqs));
-    SkASSERT(VK_TRUE == dedicatedMemReqs.requiresDedicatedAllocation);
-
     VkPhysicalDeviceMemoryProperties2 phyDevMemProps;
     phyDevMemProps.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2;
     phyDevMemProps.pNext = nullptr;
@@ -325,6 +334,7 @@
     // support that extension. Or if we know the source of the AHardwareBuffer is not from a
     // "foreign" device we can leave them as external.
     imageInfo.fCurrentQueueFamily = VK_QUEUE_FAMILY_EXTERNAL;
+    imageInfo.fYcbcrConversionInfo = *ycbcrConversion;
 
     *deleteProc = GrAHardwareBufferImageGenerator::DeleteVkImage;
     *deleteCtx = new VulkanCleanupHelper(gpu, image, memory);
@@ -441,7 +451,8 @@
     }
 }
 
-GrBackendFormat get_backend_format(GrBackendApi backend, uint32_t bufferFormat) {
+GrBackendFormat get_backend_format(GrContext* context, AHardwareBuffer* hardwareBuffer,
+                                   GrBackendApi backend, uint32_t bufferFormat) {
     if (backend == GrBackendApi::kOpenGL) {
         switch (bufferFormat) {
             //TODO: find out if we can detect, which graphic buffers support GR_GL_TEXTURE_2D
@@ -461,7 +472,6 @@
         }
     } else if (backend == GrBackendApi::kVulkan) {
         switch (bufferFormat) {
-            //TODO: find out if we can detect, which graphic buffers support GR_GL_TEXTURE_2D
             case AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM:
                 return GrBackendFormat::MakeVk(VK_FORMAT_R8G8B8A8_UNORM);
             case AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT:
@@ -474,8 +484,50 @@
                 return GrBackendFormat::MakeVk(VK_FORMAT_R8G8B8A8_UNORM);
             case AHARDWAREBUFFER_FORMAT_R8G8B8_UNORM:
                 return GrBackendFormat::MakeVk(VK_FORMAT_R8G8B8_UNORM);
-            default:
-                return GrBackendFormat::MakeVk(VK_FORMAT_R8G8B8_UNORM);
+            default: {
+                GrVkGpu* gpu = static_cast<GrVkGpu*>(context->contextPriv().getGpu());
+                SkASSERT(gpu);
+                VkDevice device = gpu->device();
+
+                if (!gpu->vkCaps().supportsAndroidHWBExternalMemory()) {
+                    return GrBackendFormat();
+                }
+                VkAndroidHardwareBufferFormatPropertiesANDROID hwbFormatProps;
+                hwbFormatProps.sType =
+                        VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_FORMAT_PROPERTIES_ANDROID;
+                hwbFormatProps.pNext = nullptr;
+
+                VkAndroidHardwareBufferPropertiesANDROID hwbProps;
+                hwbProps.sType = VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_PROPERTIES_ANDROID;
+                hwbProps.pNext = &hwbFormatProps;
+
+                VkResult err = VK_CALL(GetAndroidHardwareBufferProperties(device, hardwareBuffer,
+                                                                          &hwbProps));
+                if (VK_SUCCESS != err) {
+                    return GrBackendFormat();
+                }
+
+                if (hwbFormatProps.format != VK_FORMAT_UNDEFINED) {
+                    return GrBackendFormat();
+                }
+
+                GrVkYcbcrConversionInfo ycbcrConversion;
+                ycbcrConversion.fYcbcrModel = hwbFormatProps.suggestedYcbcrModel;
+                ycbcrConversion.fYcbcrRange = hwbFormatProps.suggestedYcbcrRange;
+                ycbcrConversion.fXChromaOffset = hwbFormatProps.suggestedXChromaOffset;
+                ycbcrConversion.fYChromaOffset = hwbFormatProps.suggestedYChromaOffset;
+                ycbcrConversion.fForceExplicitReconstruction = VK_FALSE;
+                ycbcrConversion.fExternalFormat = hwbFormatProps.externalFormat;
+                ycbcrConversion.fExternalFormatFeatures = hwbFormatProps.formatFeatures;
+                if (VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_LINEAR_FILTER_BIT &
+                    hwbFormatProps.formatFeatures) {
+                    ycbcrConversion.fChromaFilter = VK_FILTER_LINEAR;
+                } else {
+                    ycbcrConversion.fChromaFilter = VK_FILTER_NEAREST;
+                }
+
+                return GrBackendFormat::MakeVk(ycbcrConversion);
+            }
         }
     }
     return GrBackendFormat();
@@ -487,7 +539,8 @@
     }
 
     GrPixelConfig pixelConfig;
-    GrBackendFormat backendFormat = get_backend_format(context->contextPriv().getBackend(),
+    GrBackendFormat backendFormat = get_backend_format(context, fHardwareBuffer,
+                                                       context->contextPriv().getBackend(),
                                                        fBufferFormat);
     if (!context->contextPriv().caps()->getConfigFromBackendFormat(
             backendFormat, this->getInfo().colorType(), &pixelConfig)) {
@@ -505,6 +558,12 @@
     GrTextureType textureType = GrTextureType::k2D;
     if (context->contextPriv().getBackend() == GrBackendApi::kOpenGL) {
         textureType = GrTextureType::kExternal;
+    } else if (context->contextPriv().getBackend() == GrBackendApi::kVulkan) {
+        const VkFormat* format = backendFormat.getVkFormat();
+        SkASSERT(format);
+        if (*format == VK_FORMAT_UNDEFINED) {
+            textureType = GrTextureType::kExternal;
+        }
     }
 
     auto proxyProvider = context->contextPriv().proxyProvider();
diff --git a/src/gpu/vk/GrVkCaps.cpp b/src/gpu/vk/GrVkCaps.cpp
index 4797d39..fbd381f 100644
--- a/src/gpu/vk/GrVkCaps.cpp
+++ b/src/gpu/vk/GrVkCaps.cpp
@@ -705,9 +705,29 @@
     return true;
 }
 
-bool validate_image_info(VkFormat format, SkColorType ct, GrPixelConfig* config) {
+bool validate_image_info(VkFormat format, SkColorType ct, GrPixelConfig* config,
+                         bool hasYcbcrConversion) {
     *config = kUnknown_GrPixelConfig;
 
+    if (format == VK_FORMAT_UNDEFINED) {
+        // If the format is undefined then it is only valid as an external image which requires that
+        // we have a valid VkYcbcrConversion.
+        if (hasYcbcrConversion) {
+            // We don't actually care what the color type or config are since we won't use those
+            // values for external textures, but since our code requires setting a config here
+            // just default it to RGBA.
+            *config = kRGBA_8888_GrPixelConfig;
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    if (hasYcbcrConversion) {
+        // We only support having a ycbcr conversion for external images.
+        return false;
+    }
+
     switch (ct) {
         case kUnknown_SkColorType:
             return false;
@@ -779,7 +799,8 @@
         return false;
     }
 
-    return validate_image_info(imageInfo.fFormat, ct, config);
+    return validate_image_info(imageInfo.fFormat, ct, config,
+                               imageInfo.fYcbcrConversionInfo.isValid());
 }
 
 bool GrVkCaps::validateBackendRenderTarget(const GrBackendRenderTarget& rt, SkColorType ct,
@@ -789,16 +810,18 @@
         return false;
     }
 
-    return validate_image_info(imageInfo.fFormat, ct, config);
+    return validate_image_info(imageInfo.fFormat, ct, config,
+                               imageInfo.fYcbcrConversionInfo.isValid());
 }
 
 bool GrVkCaps::getConfigFromBackendFormat(const GrBackendFormat& format, SkColorType ct,
                                           GrPixelConfig* config) const {
     const VkFormat* vkFormat = format.getVkFormat();
-    if (!vkFormat) {
+    const GrVkYcbcrConversionInfo* ycbcrInfo = format.getVkYcbcrConversionInfo();
+    if (!vkFormat || !ycbcrInfo) {
         return false;
     }
-    return validate_image_info(*vkFormat, ct, config);
+    return validate_image_info(*vkFormat, ct, config, ycbcrInfo->isValid());
 }
 
 static bool get_yuva_config(VkFormat vkFormat, GrPixelConfig* config) {