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) {