Correct features and image properties of chroma subsampled formats.

Under Metal, `GBGR422` and `BGRG422` formats don't support linear
textures, mipmapping, or multisampled, arrayed, 1D, 3D, or cube
images. Many of these don't make sense for multiplanar images, either,
so I've disabled them there as well. Vulkan also forbids creating buffer
views in a chroma subsampled format, which we can't do on Metal anyway
due to linear textures not supporting this. Finally, Vulkan forbids
blitting between chroma subsampled formats.

Don't advertise GBGR/BGRG formats greater than 8 bits. Metal has no
corresponding public pixel format.

Make sure `samplerYcbcrProperties.combinedImageSamplerDescriptorCount`
is at least 1. According to the Vulkan spec:

> `combinedImageSamplerDescriptorCount` is a number between 1 and the
> number of planes in the format.

For single-plane formats, use the directly mapped `MTLPixelFormat`
instead of trying to guess the correct format. This is important for
`G8B8G8R8_422_UNORM` and `B8G8R8G8_422_UNORM`, since these shouldn't map
to `RGBA8Unorm`.

Don't adjust the extent for plane 0. Plane 0 is never subsampled, even
with GBGR/BGRG formats. The subsampled R/B components are instead
interleaved in these formats with the fully sampled G. This fixes a
validation error creating a GBGR/BGRG texture with an odd size.

Don't warn when there's no `MTLPixelFormat` for a multiplanar format.
These deliberately have no `MTLPixelFormat`, because there is no single
`MTLTexture` corresponding to the entire image.

The extra checks in `MVKImage` are to ensure that the
`dEQP-VK.api.invariance.random` test doesn't crash.
This commit is contained in:
Chip Davis 2020-09-04 13:06:50 -05:00
parent cadc011c67
commit 4b592be2d2
3 changed files with 78 additions and 28 deletions

View File

@ -419,11 +419,19 @@ VkResult MVKPhysicalDevice::getImageFormatProperties(VkFormat format,
}
MVKFormatType mvkFmt = _pixelFormats.getFormatType(format);
bool isChromaSubsampled = _pixelFormats.getChromaSubsamplingPlaneCount(format) > 0;
bool isMultiPlanar = _pixelFormats.getChromaSubsamplingPlaneCount(format) > 1;
bool isBGRG = isChromaSubsampled && !isMultiPlanar && _pixelFormats.getBlockTexelSize(format).width > 1;
bool hasAttachmentUsage = mvkIsAnyFlagEnabled(usage, (VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT |
VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT |
VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT));
// Disjoint memory requires a multiplanar format.
if (!isMultiPlanar && mvkIsAnyFlagEnabled(flags, VK_IMAGE_CREATE_DISJOINT_BIT)) {
return VK_ERROR_FORMAT_NOT_SUPPORTED;
}
VkPhysicalDeviceLimits* pLimits = &_properties.limits;
VkExtent3D maxExt = { 1, 1, 1};
uint32_t maxLevels = 1;
@ -453,11 +461,14 @@ VkResult MVKPhysicalDevice::getImageFormatProperties(VkFormat format,
// Metal does not allow compressed or depth/stencil formats on native 1D textures
if (mvkFmt == kMVKFormatDepthStencil) { return VK_ERROR_FORMAT_NOT_SUPPORTED; }
if (mvkFmt == kMVKFormatCompressed) { return VK_ERROR_FORMAT_NOT_SUPPORTED; }
if (isChromaSubsampled) { return VK_ERROR_FORMAT_NOT_SUPPORTED; }
}
break;
case VK_IMAGE_TYPE_2D:
if (mvkIsAnyFlagEnabled(flags, VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT) ) {
// Chroma-subsampled cube images aren't supported.
if (isChromaSubsampled) { return VK_ERROR_FORMAT_NOT_SUPPORTED; }
maxExt.width = pLimits->maxImageDimensionCube;
maxExt.height = pLimits->maxImageDimensionCube;
} else {
@ -467,15 +478,16 @@ VkResult MVKPhysicalDevice::getImageFormatProperties(VkFormat format,
maxExt.depth = 1;
if (tiling == VK_IMAGE_TILING_LINEAR) {
// Linear textures have additional restrictions under Metal:
// - They may not be depth/stencil or compressed textures.
if (mvkFmt == kMVKFormatDepthStencil || mvkFmt == kMVKFormatCompressed) {
// - They may not be depth/stencil, compressed, or chroma subsampled textures.
// We allow multi-planar formats because those internally use non-subsampled formats.
if (mvkFmt == kMVKFormatDepthStencil || mvkFmt == kMVKFormatCompressed || isBGRG) {
return VK_ERROR_FORMAT_NOT_SUPPORTED;
}
#if MVK_MACOS
// - On macOS, Linear textures may not be used as framebuffer attachments.
if (hasAttachmentUsage) { return VK_ERROR_FORMAT_NOT_SUPPORTED; }
#endif
// Linear textures may only have one mip level. layer & sample
// Linear textures may only have one mip level, layer & sample.
maxLevels = 1;
maxLayers = 1;
sampleCounts = VK_SAMPLE_COUNT_1_BIT;
@ -483,14 +495,22 @@ VkResult MVKPhysicalDevice::getImageFormatProperties(VkFormat format,
VkFormatProperties fmtProps;
getFormatProperties(format, &fmtProps);
// Compressed multisampled textures aren't supported.
// Chroma-subsampled multisampled textures aren't supported.
// Multisampled cube textures aren't supported.
// Non-renderable multisampled textures aren't supported.
if (mvkFmt == kMVKFormatCompressed ||
if (mvkFmt == kMVKFormatCompressed || isChromaSubsampled ||
mvkIsAnyFlagEnabled(flags, VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT) ||
!mvkIsAnyFlagEnabled(fmtProps.optimalTilingFeatures, VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT|VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) ) {
sampleCounts = VK_SAMPLE_COUNT_1_BIT;
}
maxLevels = mvkMipmapLevels3D(maxExt);
// BGRG and GBGR images may only have one mip level and one layer.
// Other chroma subsampled formats may have multiple mip levels, but still only one layer.
if (isChromaSubsampled) {
maxLevels = isBGRG ? 1 : mvkMipmapLevels3D(maxExt);
maxLayers = 1;
} else {
maxLevels = mvkMipmapLevels3D(maxExt);
}
}
break;
@ -500,7 +520,8 @@ VkResult MVKPhysicalDevice::getImageFormatProperties(VkFormat format,
return VK_ERROR_FORMAT_NOT_SUPPORTED;
}
// Metal does not allow compressed or depth/stencil formats on 3D textures
if (mvkFmt == kMVKFormatDepthStencil
if (mvkFmt == kMVKFormatDepthStencil ||
isChromaSubsampled
#if MVK_IOS_OR_TVOS
|| mvkFmt == kMVKFormatCompressed
#endif
@ -560,7 +581,7 @@ VkResult MVKPhysicalDevice::getImageFormatProperties(const VkPhysicalDeviceImage
switch (nextProps->sType) {
case VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_IMAGE_FORMAT_PROPERTIES: {
auto* samplerYcbcrConvProps = (VkSamplerYcbcrConversionImageFormatProperties*)nextProps;
samplerYcbcrConvProps->combinedImageSamplerDescriptorCount = _pixelFormats.getChromaSubsamplingPlaneCount(pImageFormatInfo->format);
samplerYcbcrConvProps->combinedImageSamplerDescriptorCount = std::max(_pixelFormats.getChromaSubsamplingPlaneCount(pImageFormatInfo->format), (uint8_t)1u);
break;
}
default:
@ -1516,7 +1537,6 @@ void MVKPhysicalDevice::initProperties() {
uint32_t maxStorage = 0, maxUniform = 0;
bool singleTexelStorage = true, singleTexelUniform = true;
_pixelFormats.enumerateSupportedFormats({0, 0, VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT}, true, [&](VkFormat vk) {
if ( _pixelFormats.getChromaSubsamplingComponentBits(vk) > 0 ) { return false; } // Skip chroma subsampling formats
MTLPixelFormat mtlFmt = _pixelFormats.getMTLPixelFormat(vk);
if ( !mtlFmt ) { return false; } // If format is invalid, avoid validation errors on MTLDevice format alignment calls

View File

@ -529,7 +529,7 @@ bool MVKImage::getIsCompressed() { return getPixelFormats()->getFormatType(_vkFo
VkExtent3D MVKImage::getExtent3D(uint8_t planeIndex, uint32_t mipLevel) {
VkExtent3D extent = _extent;
if (_hasChromaSubsampling) {
if (_hasChromaSubsampling && planeIndex > 0) {
extent.width /= _planes[planeIndex]->_blockTexelSize.width;
extent.height /= _planes[planeIndex]->_blockTexelSize.height;
}
@ -881,6 +881,10 @@ VkSampleCountFlagBits MVKImage::validateSamples(const VkImageCreateInfo* pCreate
setConfigurationResult(reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCreateImage() : Under Metal, multisampling cannot be used with compressed images. Setting sample count to 1."));
validSamples = VK_SAMPLE_COUNT_1_BIT;
}
if (getPixelFormats()->getChromaSubsamplingPlaneCount(pCreateInfo->format) > 0) {
setConfigurationResult(reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCreateImage() : Under Metal, multisampling cannot be used with chroma subsampled images. Setting sample count to 1."));
validSamples = VK_SAMPLE_COUNT_1_BIT;
}
if (pCreateInfo->arrayLayers > 1) {
if ( !_device->_pMetalFeatures->multisampleArrayTextures ) {
@ -901,6 +905,7 @@ void MVKImage::validateConfig(const VkImageCreateInfo* pCreateInfo, bool isAttac
bool is2D = (getImageType() == VK_IMAGE_TYPE_2D);
bool isCompressed = pixFmts->getFormatType(pCreateInfo->format) == kMVKFormatCompressed;
bool isChromaSubsampled = pixFmts->getChromaSubsamplingPlaneCount(pCreateInfo->format) > 0;
#if MVK_IOS_OR_TVOS
if (isCompressed && !is2D) {
@ -917,6 +922,16 @@ void MVKImage::validateConfig(const VkImageCreateInfo* pCreateInfo, bool isAttac
}
#endif
if (isChromaSubsampled && !is2D) {
setConfigurationResult(reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCreateImage() : Under Metal, chroma subsampled formats may only be used with 2D images."));
}
if (isChromaSubsampled && mvkIsAnyFlagEnabled(pCreateInfo->flags, VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT)) {
setConfigurationResult(reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCreateImage() : Under Metal, chroma subsampled formats may not be used with cube images."));
}
if (isChromaSubsampled && (pCreateInfo->arrayLayers > 1)) {
setConfigurationResult(reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCreateImage() : Chroma-subsampled formats may only have one array layer."));
}
if ((pixFmts->getFormatType(pCreateInfo->format) == kMVKFormatDepthStencil) && !is2D ) {
setConfigurationResult(reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCreateImage() : Under Metal, depth/stencil formats may only be used with 2D images."));
}
@ -940,6 +955,10 @@ uint32_t MVKImage::validateMipLevels(const VkImageCreateInfo* pCreateInfo, bool
if (validMipLevels == 1) { return validMipLevels; }
if (getPixelFormats()->getChromaSubsamplingPlaneCount(pCreateInfo->format) == 1) {
setConfigurationResult(reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCreateImage() : Under Metal, GBGR and BGRG images cannot use mipmaps. Setting mip levels to 1."));
validMipLevels = 1;
}
if (getImageType() == VK_IMAGE_TYPE_1D) {
setConfigurationResult(reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCreateImage() : Under Metal, native 1D images cannot use mipmaps. Setting mip levels to 1. Consider enabling MVK_CONFIG_TEXTURE_1D_AS_2D."));
validMipLevels = 1;
@ -967,6 +986,10 @@ bool MVKImage::validateLinear(const VkImageCreateInfo* pCreateInfo, bool isAttac
setConfigurationResult(reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCreateImage() : If tiling is VK_IMAGE_TILING_LINEAR, format must not be a compressed format."));
isLin = false;
}
if (getPixelFormats()->getChromaSubsamplingPlaneCount(pCreateInfo->format) == 1) {
setConfigurationResult(reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCreateImage() : If tiling is VK_IMAGE_TILING_LINEAR, format must not be a single-plane chroma subsampled format."));
isLin = false;
}
if (pCreateInfo->mipLevels > 1) {
setConfigurationResult(reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCreateImage() : If tiling is VK_IMAGE_TILING_LINEAR, mipLevels must be 1."));

View File

@ -190,7 +190,7 @@ MTLPixelFormat MVKPixelFormats::getMTLPixelFormat(VkFormat vkFormat) {
// If the MTLPixelFormat is not supported but VkFormat is valid,
// attempt to substitute a different format and potentially report an error.
if ( !mtlPixFmt && vkFormat ) {
if ( !mtlPixFmt && vkFormat && vkDesc.chromaSubsamplingPlaneCount <= 1 ) {
mtlPixFmt = vkDesc.mtlPixelFormatSubstitute;
// Report an error if there is no substitute, or the first time a substitution is made.
@ -273,7 +273,7 @@ uint8_t MVKPixelFormats::getChromaSubsamplingPlanes(VkFormat vkFormat, VkExtent2
return 0;
case 1:
bytesPerBlock[0] *= 4;
mtlPixFmt[0] = (bits == 8) ? MTLPixelFormatRGBA8Unorm : MTLPixelFormatRGBA16Unorm;
mtlPixFmt[0] = getMTLPixelFormat(vkFormat);
break;
case 2:
blockTexelSize[0] = VkExtent2D{1, 1};
@ -837,9 +837,9 @@ void MVKPixelFormats::initVkFormatCapabilities() {
addVkFormatDescChromaSubsampling( G8_B8_R8_3PLANE_422_UNORM, Invalid, 3, 8, 2, 1, 4 );
addVkFormatDescChromaSubsampling( G8_B8R8_2PLANE_422_UNORM, Invalid, 2, 8, 2, 1, 4 );
addVkFormatDescChromaSubsampling( G8_B8_R8_3PLANE_444_UNORM, Invalid, 3, 8, 1, 1, 3 );
addVkFormatDescChromaSubsampling( R10X6_UNORM_PACK16, Invalid, 0, 10, 1, 1, 2 );
addVkFormatDescChromaSubsampling( R10X6G10X6_UNORM_2PACK16, Invalid, 0, 10, 1, 1, 4 );
addVkFormatDescChromaSubsampling( R10X6G10X6B10X6A10X6_UNORM_4PACK16, Invalid, 0, 10, 1, 1, 8 );
addVkFormatDescChromaSubsampling( R10X6_UNORM_PACK16, R16Unorm, 0, 10, 1, 1, 2 );
addVkFormatDescChromaSubsampling( R10X6G10X6_UNORM_2PACK16, RG16Unorm, 0, 10, 1, 1, 4 );
addVkFormatDescChromaSubsampling( R10X6G10X6B10X6A10X6_UNORM_4PACK16, RGBA16Unorm, 0, 10, 1, 1, 8 );
addVkFormatDescChromaSubsampling( G10X6B10X6G10X6R10X6_422_UNORM_4PACK16, Invalid, 1, 10, 2, 1, 8 );
addVkFormatDescChromaSubsampling( B10X6G10X6R10X6G10X6_422_UNORM_4PACK16, Invalid, 1, 10, 2, 1, 8 );
addVkFormatDescChromaSubsampling( G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16, Invalid, 3, 10, 2, 2, 12 );
@ -847,9 +847,9 @@ void MVKPixelFormats::initVkFormatCapabilities() {
addVkFormatDescChromaSubsampling( G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16, Invalid, 3, 10, 2, 1, 8 );
addVkFormatDescChromaSubsampling( G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16, Invalid, 2, 10, 2, 1, 8 );
addVkFormatDescChromaSubsampling( G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16, Invalid, 3, 10, 1, 1, 6 );
addVkFormatDescChromaSubsampling( R12X4_UNORM_PACK16, Invalid, 0, 12, 1, 1, 2 );
addVkFormatDescChromaSubsampling( R12X4G12X4_UNORM_2PACK16, Invalid, 0, 12, 1, 1, 4 );
addVkFormatDescChromaSubsampling( R12X4G12X4B12X4A12X4_UNORM_4PACK16, Invalid, 0, 12, 1, 1, 8 );
addVkFormatDescChromaSubsampling( R12X4_UNORM_PACK16, R16Unorm, 0, 12, 1, 1, 2 );
addVkFormatDescChromaSubsampling( R12X4G12X4_UNORM_2PACK16, RG16Unorm, 0, 12, 1, 1, 4 );
addVkFormatDescChromaSubsampling( R12X4G12X4B12X4A12X4_UNORM_4PACK16, RGBA16Unorm, 0, 12, 1, 1, 8 );
addVkFormatDescChromaSubsampling( G12X4B12X4G12X4R12X4_422_UNORM_4PACK16, Invalid, 1, 12, 2, 1, 8 );
addVkFormatDescChromaSubsampling( B12X4G12X4R12X4G12X4_422_UNORM_4PACK16, Invalid, 1, 12, 2, 1, 8 );
addVkFormatDescChromaSubsampling( G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16, Invalid, 3, 12, 2, 2, 12 );
@ -1620,9 +1620,7 @@ typedef enum : VkFormatFeatureFlags {
kMVKVkFormatFeatureFlagsTexDSAtt = (VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT),
kMVKVkFormatFeatureFlagsTexBlend = (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT),
kMVKVkFormatFeatureFlagsTexTransfer = (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT |
VK_FORMAT_FEATURE_TRANSFER_DST_BIT |
VK_FORMAT_FEATURE_BLIT_SRC_BIT |
VK_FORMAT_FEATURE_BLIT_DST_BIT),
VK_FORMAT_FEATURE_TRANSFER_DST_BIT),
kMVKVkFormatFeatureFlagsTexChromaSubsampling = (VK_FORMAT_FEATURE_MIDPOINT_CHROMA_SAMPLES_BIT_KHR |
VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_LINEAR_FILTER_BIT_KHR),
kMVKVkFormatFeatureFlagsTexMultiPlanar = (VK_FORMAT_FEATURE_COSITED_CHROMA_SAMPLES_BIT_KHR |
@ -1650,12 +1648,13 @@ void MVKPixelFormats::setFormatProperties(MVKVkFormatDesc& vkDesc) {
vkProps.linearTilingFeatures = kMVKVkFormatFeatureFlagsTexNone;
// Chroma subsampling and multi planar features
if (getChromaSubsamplingComponentBits(vkDesc.vkFormat) > 0) {
vkProps.optimalTilingFeatures = kMVKVkFormatFeatureFlagsTexTransfer;
}
uint8_t chromaSubsamplingPlaneCount = getChromaSubsamplingPlaneCount(vkDesc.vkFormat);
if (chromaSubsamplingPlaneCount > 0) {
mtlPixFmtCaps = kMVKMTLFmtCapsRF;
uint8_t chromaSubsamplingComponentBits = getChromaSubsamplingComponentBits(vkDesc.vkFormat);
if (chromaSubsamplingComponentBits > 0) {
if (mtlPixFmtCaps != 0 || chromaSubsamplingPlaneCount > 1) {
mtlPixFmtCaps = kMVKMTLFmtCapsRF;
vkProps.optimalTilingFeatures = kMVKVkFormatFeatureFlagsTexTransfer;
}
enableFormatFeatures(ChromaSubsampling, Tex, mtlPixFmtCaps, vkProps.optimalTilingFeatures);
}
if (chromaSubsamplingPlaneCount > 1) {
@ -1670,8 +1669,15 @@ void MVKPixelFormats::setFormatProperties(MVKVkFormatDesc& vkDesc) {
enableFormatFeatures(DSAtt, Tex, mtlPixFmtCaps, vkProps.optimalTilingFeatures);
enableFormatFeatures(Blend, Tex, mtlPixFmtCaps, vkProps.optimalTilingFeatures);
if (chromaSubsamplingComponentBits > 0) {
// Vulkan forbids blits between chroma-subsampled formats.
mvkDisableFlags(vkProps.optimalTilingFeatures, (VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT));
}
// Linear tiling is not available to depth/stencil or compressed formats.
if ( !(vkDesc.formatType == kMVKFormatDepthStencil || vkDesc.formatType == kMVKFormatCompressed) ) {
// GBGR and BGRG formats also do not support linear tiling in Metal.
if ( !(vkDesc.formatType == kMVKFormatDepthStencil || vkDesc.formatType == kMVKFormatCompressed ||
(chromaSubsamplingPlaneCount == 1 && vkDesc.blockTexelSize.width > 1)) ) {
// Start with optimal tiling features, and modify.
vkProps.linearTilingFeatures = vkProps.optimalTilingFeatures;
@ -1686,9 +1692,10 @@ void MVKPixelFormats::setFormatProperties(MVKVkFormatDesc& vkDesc) {
#endif
}
// Texel buffers are not available to depth/stencil or compressed formats.
// Texel buffers are not available to depth/stencil, compressed, or chroma subsampled formats.
vkProps.bufferFeatures = kMVKVkFormatFeatureFlagsTexNone;
if ( !(vkDesc.formatType == kMVKFormatDepthStencil || vkDesc.formatType == kMVKFormatCompressed) ) {
if ( !(vkDesc.formatType == kMVKFormatDepthStencil || vkDesc.formatType == kMVKFormatCompressed ||
chromaSubsamplingComponentBits > 0) ) {
enableFormatFeatures(Read, Buf, mtlPixFmtCaps, vkProps.bufferFeatures);
enableFormatFeatures(Write, Buf, mtlPixFmtCaps, vkProps.bufferFeatures);
enableFormatFeatures(Atomic, Buf, mtlPixFmtCaps, vkProps.bufferFeatures);