From 278a7208ca9c66587cbf0cd52fe17e450b484053 Mon Sep 17 00:00:00 2001 From: Chip Davis Date: Thu, 17 Sep 2020 19:04:41 -0500 Subject: [PATCH 01/10] MVKImage: Handle VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT. Only set `MTLTextureUsagePixelFormatView` if this bit is set. This should reduce the usage of this bit, which disables lossless compression on Apple GPUs, in many cases. We continue to set it anyway for `VK_IMAGE_USAGE_TRANSFER_SRC_BIT`; this is because we create those texture views on the application's behalf, to implement `vkCopyImage()` where the source and destination formats do not agree. We also continue to set it for depth/stencil formats; Metal requires it in order to use only the stencil aspect in a view. --- MoltenVK/MoltenVK/GPUObjects/MVKImage.h | 1 + MoltenVK/MoltenVK/GPUObjects/MVKImage.mm | 3 ++- MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.h | 1 + .../MoltenVK/GPUObjects/MVKPixelFormats.mm | 18 ++++++++++++++---- 4 files changed, 18 insertions(+), 5 deletions(-) diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKImage.h b/MoltenVK/MoltenVK/GPUObjects/MVKImage.h index b88450f6..58821322 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKImage.h +++ b/MoltenVK/MoltenVK/GPUObjects/MVKImage.h @@ -367,6 +367,7 @@ protected: bool _is3DCompressed; bool _isAliasable; bool _hasExtendedUsage; + bool _hasMutableFormat; }; diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm b/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm index 210c1ffb..40a9414d 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm @@ -123,7 +123,7 @@ MTLTextureDescriptor* MVKImagePlane::newMTLTextureDescriptor() { mtlTexDesc.mipmapLevelCount = _image->_mipLevels; mtlTexDesc.sampleCount = mvkSampleCountFromVkSampleCountFlagBits(_image->_samples); mtlTexDesc.arrayLength = _image->_arrayLayers; - mtlTexDesc.usageMVK = _image->getPixelFormats()->getMTLTextureUsage(_image->_usage, mtlPixFmt, minUsage, _image->_isLinear, _image->_hasExtendedUsage); + mtlTexDesc.usageMVK = _image->getPixelFormats()->getMTLTextureUsage(_image->_usage, mtlPixFmt, minUsage, _image->_isLinear, _image->_hasMutableFormat, _image->_hasExtendedUsage); mtlTexDesc.storageModeMVK = _image->getMTLStorageMode(); mtlTexDesc.cpuCacheMode = _image->getMTLCPUCacheMode(); @@ -804,6 +804,7 @@ MVKImage::MVKImage(MVKDevice* device, const VkImageCreateInfo* pCreateInfo) : MV MVKPixelFormats* pixFmts = getPixelFormats(); _vkFormat = pCreateInfo->format; _usage = pCreateInfo->usage; + _hasMutableFormat = mvkIsAnyFlagEnabled(pCreateInfo->flags, VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT); _hasExtendedUsage = mvkIsAnyFlagEnabled(pCreateInfo->flags, VK_IMAGE_CREATE_EXTENDED_USAGE_BIT); _is3DCompressed = (getImageType() == VK_IMAGE_TYPE_3D) && (pixFmts->getFormatType(pCreateInfo->format) == kMVKFormatCompressed) && !_device->_pMetalFeatures->native3DCompressedTextures; diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.h b/MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.h index e87063e0..5b73547d 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.h +++ b/MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.h @@ -350,6 +350,7 @@ public: MTLPixelFormat mtlFormat, MTLTextureUsage minUsage = MTLTextureUsageUnknown, bool isLinear = false, + bool isMutableFormat = true, bool isExtended = false); /** Enumerates all formats that support the given features, calling a specified function for each one. */ diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.mm b/MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.mm index d27b4b2b..a265dcd0 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.mm @@ -465,6 +465,7 @@ MTLTextureUsage MVKPixelFormats::getMTLTextureUsage(VkImageUsageFlags vkImageUsa MTLPixelFormat mtlFormat, MTLTextureUsage minUsage, bool isLinear, + bool isMutableFormat, bool isExtended) { bool isDepthFmt = isDepthFormat(mtlFormat); bool isStencilFmt = isStencilFormat(mtlFormat); @@ -514,13 +515,22 @@ MTLTextureUsage MVKPixelFormats::getMTLTextureUsage(VkImageUsageFlags vkImageUsa } // Create view on, but only on color formats, or combined depth-stencil formats if supported by the GPU... - if (mvkIsAnyFlagEnabled(vkImageUsageFlags, (VK_IMAGE_USAGE_TRANSFER_SRC_BIT | // May use temp view if transfer involves format change - VK_IMAGE_USAGE_SAMPLED_BIT | + if ((mvkIsAnyFlagEnabled(vkImageUsageFlags, (VK_IMAGE_USAGE_TRANSFER_SRC_BIT)) || // May use temp view if transfer involves format change + (isMutableFormat && + mvkIsAnyFlagEnabled(vkImageUsageFlags, (VK_IMAGE_USAGE_SAMPLED_BIT | + VK_IMAGE_USAGE_STORAGE_BIT | + VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT | + VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT)))) && + isColorFormat) { + + mvkEnableFlags(mtlUsage, MTLTextureUsagePixelFormatView); + } + if (mvkIsAnyFlagEnabled(vkImageUsageFlags, (VK_IMAGE_USAGE_TRANSFER_SRC_BIT | // May use temp view if transfer involves format change + VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT | - VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)) && - (isColorFormat || (isCombinedDepthStencilFmt && supportsStencilViews))) { + isCombinedDepthStencilFmt && supportsStencilViews) { mvkEnableFlags(mtlUsage, MTLTextureUsagePixelFormatView); } From c37f17647cc4e6c0b78aac4fb36433ab6be1cfc8 Mon Sep 17 00:00:00 2001 From: Chip Davis Date: Sun, 20 Sep 2020 15:10:16 -0500 Subject: [PATCH 02/10] MVKDevice: Limit the maximum number of inline uniform blocks on Mac. Mac GPUs only support 14 dynamic buffers. One of those is used for push constants, leaving 13 for inline uniform blocks. --- MoltenVK/MoltenVK/API/vk_mvk_moltenvk.h | 1 + MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm | 12 ++++++++---- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/MoltenVK/MoltenVK/API/vk_mvk_moltenvk.h b/MoltenVK/MoltenVK/API/vk_mvk_moltenvk.h index d6025c45..80f1e10a 100644 --- a/MoltenVK/MoltenVK/API/vk_mvk_moltenvk.h +++ b/MoltenVK/MoltenVK/API/vk_mvk_moltenvk.h @@ -620,6 +620,7 @@ typedef struct { VkBool32 sharedLinearTextures; /**< If true, linear textures and texture buffers can be created from buffers in Shared storage. */ VkBool32 depthResolve; /**< If true, resolving depth textures with filters other than Sample0 is supported. */ VkBool32 stencilResolve; /**< If true, resolving stencil textures with filters other than Sample0 is supported. */ + uint32_t maxPerStageDynamicMTLBufferCount; /**< The maximum number of inline buffers that can be set on a command buffer. */ } MVKPhysicalDeviceMetalFeatures; /** MoltenVK performance of a particular type of activity. */ diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm index f7541235..654b719e 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm @@ -298,10 +298,10 @@ void MVKPhysicalDevice::getProperties(VkPhysicalDeviceProperties2* properties) { case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INLINE_UNIFORM_BLOCK_PROPERTIES_EXT: { auto* inlineUniformBlockProps = (VkPhysicalDeviceInlineUniformBlockPropertiesEXT*)next; inlineUniformBlockProps->maxInlineUniformBlockSize = _metalFeatures.dynamicMTLBufferSize; - inlineUniformBlockProps->maxPerStageDescriptorInlineUniformBlocks = _properties.limits.maxPerStageDescriptorUniformBuffers; - inlineUniformBlockProps->maxPerStageDescriptorUpdateAfterBindInlineUniformBlocks = _properties.limits.maxPerStageDescriptorUniformBuffers; - inlineUniformBlockProps->maxDescriptorSetInlineUniformBlocks = _properties.limits.maxDescriptorSetUniformBuffers; - inlineUniformBlockProps->maxDescriptorSetUpdateAfterBindInlineUniformBlocks = _properties.limits.maxDescriptorSetUniformBuffers; + inlineUniformBlockProps->maxPerStageDescriptorInlineUniformBlocks = _metalFeatures.dynamicMTLBufferSize ? _metalFeatures.maxPerStageDynamicMTLBufferCount - 1 : 0; // Less one for push constants + inlineUniformBlockProps->maxPerStageDescriptorUpdateAfterBindInlineUniformBlocks = inlineUniformBlockProps->maxPerStageDescriptorInlineUniformBlocks; + inlineUniformBlockProps->maxDescriptorSetInlineUniformBlocks = (inlineUniformBlockProps->maxPerStageDescriptorInlineUniformBlocks * 4); + inlineUniformBlockProps->maxDescriptorSetUpdateAfterBindInlineUniformBlocks = (inlineUniformBlockProps->maxPerStageDescriptorUpdateAfterBindInlineUniformBlocks * 4); break; } case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ROBUSTNESS_2_PROPERTIES_EXT: { @@ -990,6 +990,7 @@ void MVKPhysicalDevice::initMetalFeatures() { _metalFeatures.maxPerStageBufferCount = 31; _metalFeatures.maxMTLBufferSize = (256 * MEBI); _metalFeatures.dynamicMTLBufferSize = 0; + _metalFeatures.maxPerStageDynamicMTLBufferCount = 0; _metalFeatures.maxPerStageSamplerCount = 16; _metalFeatures.maxQueryBufferSize = (64 * KIBI); @@ -1015,6 +1016,7 @@ void MVKPhysicalDevice::initMetalFeatures() { _metalFeatures.maxTextureDimension = (8 * KIBI); _metalFeatures.dynamicMTLBufferSize = (4 * KIBI); _metalFeatures.sharedLinearTextures = true; + _metalFeatures.maxPerStageDynamicMTLBufferCount = _metalFeatures.maxPerStageBufferCount; if (supportsMTLFeatureSet(tvOS_GPUFamily1_v2)) { _metalFeatures.mslVersionEnum = MTLLanguageVersion1_2; @@ -1069,6 +1071,7 @@ void MVKPhysicalDevice::initMetalFeatures() { _metalFeatures.mslVersionEnum = MTLLanguageVersion1_1; _metalFeatures.dynamicMTLBufferSize = (4 * KIBI); _metalFeatures.maxTextureDimension = (8 * KIBI); + _metalFeatures.maxPerStageDynamicMTLBufferCount = _metalFeatures.maxPerStageBufferCount; } if (supportsMTLFeatureSet(iOS_GPUFamily1_v3)) { @@ -1150,6 +1153,7 @@ void MVKPhysicalDevice::initMetalFeatures() { _metalFeatures.combinedStoreResolveAction = true; _metalFeatures.deferredStoreActions = true; _metalFeatures.maxMTLBufferSize = (1 * GIBI); + _metalFeatures.maxPerStageDynamicMTLBufferCount = 14; } if (supportsMTLFeatureSet(macOS_GPUFamily1_v3)) { From 4b592be2d2bb9462adcc46009b0a7783418592b0 Mon Sep 17 00:00:00 2001 From: Chip Davis Date: Fri, 4 Sep 2020 13:06:50 -0500 Subject: [PATCH 03/10] 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. --- MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm | 36 +++++++++++---- MoltenVK/MoltenVK/GPUObjects/MVKImage.mm | 25 ++++++++++- .../MoltenVK/GPUObjects/MVKPixelFormats.mm | 45 +++++++++++-------- 3 files changed, 78 insertions(+), 28 deletions(-) diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm index f7541235..d9f1d6e8 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm @@ -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 diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm b/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm index 210c1ffb..5407e263 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm @@ -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.")); diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.mm b/MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.mm index d27b4b2b..f758b4a4 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.mm @@ -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); From f3eda6218e8b38c0446aa829eff6ab538ae6f079 Mon Sep 17 00:00:00 2001 From: Chip Davis Date: Mon, 14 Sep 2020 14:23:50 -0500 Subject: [PATCH 04/10] MVKDevice: Fix getting the required alignment for multiplanar textures. In that case, use the format for plane 1. In a two-plane format, that will have two components and thus will have stricter alignment requirements. --- MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm index d9f1d6e8..ca24eb19 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm @@ -2980,8 +2980,19 @@ uint32_t MVKDevice::getMetalBufferIndexForVertexAttributeBinding(uint32_t bindin VkDeviceSize MVKDevice::getVkFormatTexelBufferAlignment(VkFormat format, MVKBaseObject* mvkObj) { VkDeviceSize deviceAlignment = 0; id mtlDev = getMTLDevice(); + MVKPixelFormats* mvkPixFmts = getPixelFormats(); if ([mtlDev respondsToSelector: @selector(minimumLinearTextureAlignmentForPixelFormat:)]) { - deviceAlignment = [mtlDev minimumLinearTextureAlignmentForPixelFormat: getPixelFormats()->getMTLPixelFormat(format)]; + MTLPixelFormat mtlPixFmt = mvkPixFmts->getMTLPixelFormat(format); + if (mvkPixFmts->getChromaSubsamplingPlaneCount(format) >= 2) { + // Use plane 1 to get the alignment requirements. In a 2-plane format, this will + // typically have stricter alignment requirements due to it being a 2-component format. + VkExtent2D blockTexelSize[3]; + uint32_t bytesPerBlock[3]; + MTLPixelFormat planePixFmts[3]; + mvkPixFmts->getChromaSubsamplingPlanes(format, blockTexelSize, bytesPerBlock, planePixFmts); + mtlPixFmt = planePixFmts[1]; + } + deviceAlignment = [mtlDev minimumLinearTextureAlignmentForPixelFormat: mtlPixFmt]; } return deviceAlignment ? deviceAlignment : _pProperties->limits.minTexelBufferOffsetAlignment; } From 400781e9505da6dc98c7285c09a19d806d6de0c1 Mon Sep 17 00:00:00 2001 From: Chip Davis Date: Sat, 19 Sep 2020 02:12:26 -0500 Subject: [PATCH 05/10] MVKImage: Use the plane's MTLPixelFormat to calculate memory sizes. We really don't want to use the `VkFormat` for the whole image. That has a block size set on it, which causes the size to be reduced by a factor of two or even four, in the case of a 420 format whose pitch exceeds the required alignment. If instead we use the plane `MTLPixelFormat`, we can sidestep that. --- MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm | 6 +----- MoltenVK/MoltenVK/GPUObjects/MVKImage.mm | 6 ++++-- MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.h | 3 +++ MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.mm | 17 +++++++++++++++++ 4 files changed, 25 insertions(+), 7 deletions(-) diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm index ca24eb19..7ac270a7 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm @@ -2986,11 +2986,7 @@ VkDeviceSize MVKDevice::getVkFormatTexelBufferAlignment(VkFormat format, MVKBase if (mvkPixFmts->getChromaSubsamplingPlaneCount(format) >= 2) { // Use plane 1 to get the alignment requirements. In a 2-plane format, this will // typically have stricter alignment requirements due to it being a 2-component format. - VkExtent2D blockTexelSize[3]; - uint32_t bytesPerBlock[3]; - MTLPixelFormat planePixFmts[3]; - mvkPixFmts->getChromaSubsamplingPlanes(format, blockTexelSize, bytesPerBlock, planePixFmts); - mtlPixFmt = planePixFmts[1]; + mtlPixFmt = mvkPixFmts->getChromaSubsamplingPlaneMTLPixelFormat(format, 1); } deviceAlignment = [mtlDev minimumLinearTextureAlignmentForPixelFormat: mtlPixFmt]; } diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm b/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm index 5407e263..72bde4e0 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm @@ -537,14 +537,16 @@ VkExtent3D MVKImage::getExtent3D(uint8_t planeIndex, uint32_t mipLevel) { } VkDeviceSize MVKImage::getBytesPerRow(uint8_t planeIndex, uint32_t mipLevel) { - size_t bytesPerRow = getPixelFormats()->getBytesPerRow(_vkFormat, getExtent3D(planeIndex, mipLevel).width); + MTLPixelFormat planeMTLPixFmt = getPixelFormats()->getChromaSubsamplingPlaneMTLPixelFormat(_vkFormat, planeIndex); + size_t bytesPerRow = getPixelFormats()->getBytesPerRow(planeMTLPixFmt, getExtent3D(planeIndex, mipLevel).width); return mvkAlignByteCount(bytesPerRow, _rowByteAlignment); } VkDeviceSize MVKImage::getBytesPerLayer(uint8_t planeIndex, uint32_t mipLevel) { + MTLPixelFormat planeMTLPixFmt = getPixelFormats()->getChromaSubsamplingPlaneMTLPixelFormat(_vkFormat, planeIndex); VkExtent3D extent = getExtent3D(planeIndex, mipLevel); size_t bytesPerRow = getBytesPerRow(planeIndex, mipLevel); - return getPixelFormats()->getBytesPerLayer(_vkFormat, bytesPerRow, extent.height); + return getPixelFormats()->getBytesPerLayer(planeMTLPixFmt, bytesPerRow, extent.height); } VkResult MVKImage::getSubresourceLayout(const VkImageSubresource* pSubresource, diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.h b/MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.h index e87063e0..c75ec09f 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.h +++ b/MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.h @@ -256,6 +256,9 @@ public: /** Returns the MSLFormatResolution of the specified chroma-subsampling (YCbCr) VkFormat */ SPIRV_CROSS_NAMESPACE::MSLFormatResolution getChromaSubsamplingResolution(VkFormat vkFormat); + /** Returns the MTLPixelFormat of the specified chroma-subsampling (YCbCr) VkFormat for the specified plane. */ + MTLPixelFormat getChromaSubsamplingPlaneMTLPixelFormat(VkFormat vkFormat, uint8_t planeIndex); + /** Returns the number of planes, blockTexelSize, bytesPerBlock and mtlPixFmt of each plane of the specified chroma-subsampling (YCbCr) VkFormat into the given arrays */ uint8_t getChromaSubsamplingPlanes(VkFormat vkFormat, VkExtent2D blockTexelSize[3], uint32_t bytesPerBlock[3], MTLPixelFormat mtlPixFmt[3]); diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.mm b/MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.mm index f758b4a4..7d71cbe7 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.mm @@ -250,6 +250,23 @@ SPIRV_CROSS_NAMESPACE::MSLFormatResolution MVKPixelFormats::getChromaSubsampling : SPIRV_CROSS_NAMESPACE::MSL_FORMAT_RESOLUTION_420; } +MTLPixelFormat MVKPixelFormats::getChromaSubsamplingPlaneMTLPixelFormat(VkFormat vkFormat, uint8_t planeIndex) { + uint8_t planes = getChromaSubsamplingPlaneCount(vkFormat); + uint8_t bits = getChromaSubsamplingComponentBits(vkFormat); + switch(planes) { + default: + case 1: + return getMTLPixelFormat(vkFormat); + case 2: + if (planeIndex == 1) { + return (bits == 8) ? MTLPixelFormatRG8Unorm : MTLPixelFormatRG16Unorm; + } + /* fallthrough */ + case 3: + return (bits == 8) ? MTLPixelFormatR8Unorm : MTLPixelFormatR16Unorm; + } +} + uint8_t MVKPixelFormats::getChromaSubsamplingPlanes(VkFormat vkFormat, VkExtent2D blockTexelSize[3], uint32_t bytesPerBlock[3], MTLPixelFormat mtlPixFmt[3]) { uint8_t planes = getChromaSubsamplingPlaneCount(vkFormat); uint8_t bits = getChromaSubsamplingComponentBits(vkFormat); From e4a5910f3b1cee35d177663eb0f64c6903bf36c7 Mon Sep 17 00:00:00 2001 From: Chip Davis Date: Sat, 19 Sep 2020 21:00:28 -0500 Subject: [PATCH 06/10] MVKImageView: Get the plane from the aspect mask for single-plane formats. This is the case where we might be getting a view for a single plane from a multiplanar image. --- MoltenVK/MoltenVK/GPUObjects/MVKImage.mm | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm b/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm index 72bde4e0..4a48b3e9 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm @@ -1336,7 +1336,7 @@ id MVKImageViewPlane::newMTLTexture() { mtlTextureType = MTLTextureType3D; sliceRange = NSMakeRange(0, 1); } - id mtlTex = _imageView->_image->getMTLTexture(MVKImage::getPlaneFromVkImageAspectFlags(_imageView->_subresourceRange.aspectMask)); + id mtlTex = _imageView->_image->getMTLTexture(_planeIndex); if (_device->_pMetalFeatures->nativeTextureSwizzle && _packedSwizzle) { return [mtlTex newTextureViewWithPixelFormat: _mtlPixFmt textureType: mtlTextureType @@ -1489,15 +1489,15 @@ MVKImageView::MVKImageView(MVKDevice* device, beginPlaneIndex = 0, endPlaneIndex = subsamplingPlaneCount; if (subsamplingPlaneCount == 0) { - endPlaneIndex = 1; - mtlPixFmtOfPlane[0] = getPixelFormats()->getMTLPixelFormat(pCreateInfo->format); + if (_subresourceRange.aspectMask & (VK_IMAGE_ASPECT_PLANE_0_BIT | VK_IMAGE_ASPECT_PLANE_1_BIT | VK_IMAGE_ASPECT_PLANE_2_BIT)) { + beginPlaneIndex = MVKImage::getPlaneFromVkImageAspectFlags(_subresourceRange.aspectMask); + } + endPlaneIndex = beginPlaneIndex + 1; + mtlPixFmtOfPlane[beginPlaneIndex] = getPixelFormats()->getMTLPixelFormat(pCreateInfo->format); } else { if (!mvkVkComponentMappingsMatch(pCreateInfo->components, {VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A})) { setConfigurationResult(reportError(VK_ERROR_FEATURE_NOT_PRESENT, "Image view swizzling for multi planar formats is not supported.")); } - if (_subresourceRange.aspectMask & (VK_IMAGE_ASPECT_PLANE_0_BIT | VK_IMAGE_ASPECT_PLANE_1_BIT | VK_IMAGE_ASPECT_PLANE_2_BIT)) { - beginPlaneIndex = endPlaneIndex = MVKImage::getPlaneFromVkImageAspectFlags(_subresourceRange.aspectMask); - } } for (uint8_t planeIndex = beginPlaneIndex; planeIndex < endPlaneIndex; planeIndex++) { _planes.push_back(new MVKImageViewPlane(this, planeIndex, mtlPixFmtOfPlane[planeIndex], pCreateInfo)); From 6fba50bba5422fad33346f1e223b017d39a0d00a Mon Sep 17 00:00:00 2001 From: Bill Hollings Date: Mon, 21 Sep 2020 18:08:54 -0400 Subject: [PATCH 07/10] Update What's New document with new linking options. --- Docs/Whats_New.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Docs/Whats_New.md b/Docs/Whats_New.md index 03b2cc56..6ee632b8 100644 --- a/Docs/Whats_New.md +++ b/Docs/Whats_New.md @@ -18,6 +18,8 @@ MoltenVK 1.1.0 Released 2020/09/28 +>**_Note:_** This release contains changes to library paths and framework linking options. + - Add support for Vulkan 1.1, including: - The `vkEnumerateInstanceVersion()` function - The `vkGetDeviceQueue2()` function @@ -38,6 +40,12 @@ Released 2020/09/28 future `MTLSharedEvent` Vulkan extension) - `VK_KHR_multiview` - Remove support for obsolete `VK_EXTX_portability_subset` extension. +- Redesign build and linking options that leverage newer framework technology: + - Add comprehensive support for multi-platform, multi-architecture `XCFrameworks`. + - Build fat single-platform, multi-architecture `dylibs`. + - Add support for *Apple Silicon* builds for *macOS* and *Simulators*. + - Remove support for distinct legacy frameworks and static libraries. + - Remove support for fat libraries and frameworks that span device and simulators. - Improve performance of tessellation control pipeline stage by processing multiple patches per workgroup. - `vkCmdBindDescriptorSets` order `pDynamicOffsets` by descriptor binding number From 4b4e5e912bd82008128800fb7b3f86fbe3b96140 Mon Sep 17 00:00:00 2001 From: Chip Davis Date: Thu, 17 Sep 2020 14:15:19 -0500 Subject: [PATCH 08/10] MVKImage: Always use a texel buffer for atomic linear storage images. If the image has a format that supports atomic access, or can be cast to a format which supports atomic access, then use a texel buffer, regardless of the memory type. If we can't use the `MTLBuffer` from the device memory, then create our own. For #1027. --- MoltenVK/MoltenVK/GPUObjects/MVKImage.h | 5 +- MoltenVK/MoltenVK/GPUObjects/MVKImage.mm | 68 +++++++++++++++---- .../MoltenVK/GPUObjects/MVKPixelFormats.h | 3 + .../MoltenVK/GPUObjects/MVKPixelFormats.mm | 4 ++ 4 files changed, 66 insertions(+), 14 deletions(-) diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKImage.h b/MoltenVK/MoltenVK/GPUObjects/MVKImage.h index 58821322..a8ab89ec 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKImage.h +++ b/MoltenVK/MoltenVK/GPUObjects/MVKImage.h @@ -144,8 +144,10 @@ protected: MVKImageMemoryBinding(MVKDevice* device, MVKImage* image, uint8_t planeIndex); MVKImage* _image; + id _mtlTexelBuffer = nil; + NSUInteger _mtlTexelBufferOffset = 0; uint8_t _planeIndex; - bool _usesTexelBuffer; + bool _ownsTexelBuffer = false; }; @@ -368,6 +370,7 @@ protected: bool _isAliasable; bool _hasExtendedUsage; bool _hasMutableFormat; + bool _isLinearForAtomics; }; diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm b/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm index a50fb5a2..9b685b7b 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm @@ -51,10 +51,10 @@ id MVKImagePlane::getMTLTexture() { newTextureWithDescriptor: mtlTexDesc iosurface: _image->_ioSurface plane: _planeIndex]; - } else if (memoryBinding->_usesTexelBuffer) { - _mtlTexture = [memoryBinding->_deviceMemory->getMTLBuffer() + } else if (memoryBinding->_mtlTexelBuffer) { + _mtlTexture = [memoryBinding->_mtlTexelBuffer newTextureWithDescriptor: mtlTexDesc - offset: memoryBinding->getDeviceMemoryOffset() + offset: memoryBinding->_mtlTexelBufferOffset bytesPerRow: _subresources[0].layout.rowPitch]; } else if (memoryBinding->_deviceMemory->getMTLHeap() && !_image->getIsDepthStencil()) { // Metal support for depth/stencil from heaps is flaky @@ -306,7 +306,7 @@ void MVKImagePlane::applyImageMemoryBarrier(VkPipelineStageFlags srcStageMask, MVKImageMemoryBinding* memBind = getMemoryBinding(); bool needsSync = memBind->needsHostReadSync(srcStageMask, dstStageMask, barrier); - bool needsPull = (!memBind->_usesTexelBuffer && + bool needsPull = ((!memBind->_mtlTexelBuffer || memBind->_ownsTexelBuffer) && memBind->isMemoryHostCoherent() && barrier.newLayout == VK_IMAGE_LAYOUT_GENERAL && mvkIsAnyFlagEnabled(barrier.dstAccessMask, (VK_ACCESS_HOST_READ_BIT | VK_ACCESS_MEMORY_READ_BIT))); @@ -375,9 +375,10 @@ VkResult MVKImageMemoryBinding::getMemoryRequirements(const void*, VkMemoryRequi case VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS: { auto* dedicatedReqs = (VkMemoryDedicatedRequirements*)next; bool writable = mvkIsAnyFlagEnabled(_image->_usage, VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT); + bool canUseTexelBuffer = _device->_pMetalFeatures->texelBuffers && _image->_isLinear && !_image->getIsCompressed(); dedicatedReqs->requiresDedicatedAllocation = _requiresDedicatedMemoryAllocation; dedicatedReqs->prefersDedicatedAllocation = (dedicatedReqs->requiresDedicatedAllocation || - (!_usesTexelBuffer && (writable || !_device->_pMetalFeatures->placementHeaps))); + (!canUseTexelBuffer && (writable || !_device->_pMetalFeatures->placementHeaps))); break; } default: @@ -392,11 +393,34 @@ VkResult MVKImageMemoryBinding::bindDeviceMemory(MVKDeviceMemory* mvkMem, VkDevi if (_deviceMemory) { _deviceMemory->removeImageMemoryBinding(this); } MVKResource::bindDeviceMemory(mvkMem, memOffset); - _usesTexelBuffer = _device->_pMetalFeatures->texelBuffers && _deviceMemory && _deviceMemory->_mtlBuffer; // Texel buffers available - _usesTexelBuffer = _usesTexelBuffer && (isMemoryHostAccessible() || _device->_pMetalFeatures->placementHeaps) && _image->_isLinear && !_image->getIsCompressed(); // Applicable memory layout + bool usesTexelBuffer = _device->_pMetalFeatures->texelBuffers && _deviceMemory; // Texel buffers available + usesTexelBuffer = usesTexelBuffer && (isMemoryHostAccessible() || _device->_pMetalFeatures->placementHeaps) && _image->_isLinear && !_image->getIsCompressed(); // Applicable memory layout // macOS before 10.15.5 cannot use shared memory for texel buffers. - _usesTexelBuffer = _usesTexelBuffer && (_device->_pMetalFeatures->sharedLinearTextures || !isMemoryHostCoherent()); + usesTexelBuffer = usesTexelBuffer && (_device->_pMetalFeatures->sharedLinearTextures || !isMemoryHostCoherent()); + + if (_image->_isLinearForAtomics) { + if (usesTexelBuffer && _deviceMemory->ensureMTLBuffer()) { + _mtlTexelBuffer = _deviceMemory->_mtlBuffer; + _mtlTexelBufferOffset = getDeviceMemoryOffset(); + } else { + // Create our own buffer for this. + if (_deviceMemory && _deviceMemory->_mtlHeap && _image->getMTLStorageMode() == _deviceMemory->_mtlStorageMode) { + _mtlTexelBuffer = [_deviceMemory->_mtlHeap newBufferWithLength: _byteCount options: _deviceMemory->getMTLResourceOptions() offset: getDeviceMemoryOffset()]; + if (_image->_isAliasable) { [_mtlTexelBuffer makeAliasable]; } + } else { + _mtlTexelBuffer = [getMTLDevice() newBufferWithLength: _byteCount options: _image->getMTLStorageMode() << MTLResourceStorageModeShift]; + } + if (!_mtlTexelBuffer) { + return reportError(VK_ERROR_OUT_OF_DEVICE_MEMORY, "Could not create an MTLBuffer for an image that requires a buffer backing store. Images that can be used for atomic accesses must have a texel buffer backing them."); + } + _mtlTexelBufferOffset = 0; + _ownsTexelBuffer = true; + } + } else if (usesTexelBuffer && _deviceMemory->_mtlBuffer) { + _mtlTexelBuffer = _deviceMemory->_mtlBuffer; + _mtlTexelBufferOffset = getDeviceMemoryOffset(); + } flushToDevice(getDeviceMemoryOffset(), getByteCount()); return _deviceMemory ? _deviceMemory->addImageMemoryBinding(this) : VK_SUCCESS; @@ -420,6 +444,9 @@ void MVKImageMemoryBinding::propagateDebugName() { for(uint8_t planeIndex = beginPlaneIndex(); planeIndex < endPlaneIndex(); planeIndex++) { _image->_planes[planeIndex]->propagateDebugName(); } + if (_ownsTexelBuffer) { + setLabelIfNotNil(_mtlTexelBuffer, _image->_debugName); + } } // Returns whether the specified image memory barrier requires a sync between this @@ -437,7 +464,7 @@ bool MVKImageMemoryBinding::needsHostReadSync(VkPipelineStageFlags srcStageMask, #endif } -bool MVKImageMemoryBinding::shouldFlushHostMemory() { return isMemoryHostAccessible() && !_usesTexelBuffer; } +bool MVKImageMemoryBinding::shouldFlushHostMemory() { return isMemoryHostAccessible() && (!_mtlTexelBuffer || _ownsTexelBuffer); } // Flushes the device memory at the specified memory range into the MTLTexture. Updates // all subresources that overlap the specified range and are in an updatable layout state. @@ -489,14 +516,12 @@ uint8_t MVKImageMemoryBinding::endPlaneIndex() const { return (_image->_memoryBindings.size() > 1) ? _planeIndex : (uint8_t)_image->_memoryBindings.size(); } -MVKImageMemoryBinding::MVKImageMemoryBinding(MVKDevice* device, MVKImage* image, uint8_t planeIndex) : MVKResource(device) { - _image = image; - _planeIndex = planeIndex; - _usesTexelBuffer = false; +MVKImageMemoryBinding::MVKImageMemoryBinding(MVKDevice* device, MVKImage* image, uint8_t planeIndex) : MVKResource(device), _image(image), _planeIndex(planeIndex) { } MVKImageMemoryBinding::~MVKImageMemoryBinding() { if (_deviceMemory) { _deviceMemory->removeImageMemoryBinding(this); } + if (_ownsTexelBuffer) { [_mtlTexelBuffer release]; } } @@ -809,6 +834,13 @@ MVKImage::MVKImage(MVKDevice* device, const VkImageCreateInfo* pCreateInfo) : MV _hasMutableFormat = mvkIsAnyFlagEnabled(pCreateInfo->flags, VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT); _hasExtendedUsage = mvkIsAnyFlagEnabled(pCreateInfo->flags, VK_IMAGE_CREATE_EXTENDED_USAGE_BIT); + // If this is a storage image of format R32_UINT or R32_SINT, or MUTABLE_FORMAT is set + // and R32_UINT is in the set of possible view formats, then we must use a texel buffer, + // or image atomics won't work. + // TODO: Also add handling for VK_KHR_image_format_list here. + _isLinearForAtomics = _isLinear && mvkIsAnyFlagEnabled(_usage, VK_IMAGE_USAGE_STORAGE_BIT) && + ((_vkFormat == VK_FORMAT_R32_UINT || _vkFormat == VK_FORMAT_R32_SINT) || + (_hasMutableFormat && pixFmts->getViewClass(_vkFormat) == MVKMTLViewClass::Color32)); _is3DCompressed = (getImageType() == VK_IMAGE_TYPE_3D) && (pixFmts->getFormatType(pCreateInfo->format) == kMVKFormatCompressed) && !_device->_pMetalFeatures->native3DCompressedTextures; _isDepthStencilAttachment = (mvkAreAllFlagsEnabled(pCreateInfo->usage, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) || mvkAreAllFlagsEnabled(pixFmts->getVkFormatProperties(pCreateInfo->format).optimalTilingFeatures, VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT)); @@ -845,6 +877,16 @@ MVKImage::MVKImage(MVKDevice* device, const VkImageCreateInfo* pCreateInfo) : MV memoryBinding->_byteCount += sizeAndAlign.size; memoryBinding->_byteAlignment = std::max(memoryBinding->_byteAlignment, (VkDeviceSize)sizeAndAlign.align); _isAliasable = mvkIsAnyFlagEnabled(pCreateInfo->flags, VK_IMAGE_CREATE_ALIAS_BIT); + } else if (_isLinearForAtomics && _device->_pMetalFeatures->placementHeaps) { + NSUInteger bufferLength = 0; + for (uint32_t mipLvl = 0; mipLvl < _mipLevels; mipLvl++) { + VkExtent3D mipExtent = getExtent3D(planeIndex, mipLvl); + bufferLength += getBytesPerLayer(planeIndex, mipLvl) * mipExtent.depth * _arrayLayers; + } + MTLSizeAndAlign sizeAndAlign = [_device->getMTLDevice() heapBufferSizeAndAlignWithLength: bufferLength options: MTLResourceStorageModePrivate]; + memoryBinding->_byteCount += sizeAndAlign.size; + memoryBinding->_byteAlignment = std::max(std::max(memoryBinding->_byteAlignment, _rowByteAlignment), (VkDeviceSize)sizeAndAlign.align); + _isAliasable = mvkIsAnyFlagEnabled(pCreateInfo->flags, VK_IMAGE_CREATE_ALIAS_BIT); } else { for (uint32_t mipLvl = 0; mipLvl < _mipLevels; mipLvl++) { VkExtent3D mipExtent = getExtent3D(planeIndex, mipLvl); diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.h b/MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.h index 72f82790..fa576abd 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.h +++ b/MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.h @@ -318,6 +318,9 @@ public: /** Returns the Metal format capabilities supported by the specified Metal format. */ MVKMTLFmtCaps getCapabilities(MTLPixelFormat mtlFormat, bool isExtended = false); + /** Returns the Metal view class of the specified Vulkan format. */ + MVKMTLViewClass getViewClass(VkFormat vkFormat); + /** Returns the Metal view class of the specified Metal format. */ MVKMTLViewClass getViewClass(MTLPixelFormat mtlFormat); diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.mm b/MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.mm index e9f03acc..a3443f21 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.mm @@ -352,6 +352,10 @@ MVKMTLFmtCaps MVKPixelFormats::getCapabilities(MTLPixelFormat mtlFormat, bool is return caps; } +MVKMTLViewClass MVKPixelFormats::getViewClass(VkFormat vkFormat) { + return getMTLPixelFormatDesc(getVkFormatDesc(vkFormat).mtlPixelFormat).mtlViewClass; +} + MVKMTLViewClass MVKPixelFormats::getViewClass(MTLPixelFormat mtlFormat) { return getMTLPixelFormatDesc(mtlFormat).mtlViewClass; } From 41b787dc8bfc796076cdd5f04d95791b48dcbb51 Mon Sep 17 00:00:00 2001 From: Chip Davis Date: Sun, 20 Sep 2020 22:28:48 -0500 Subject: [PATCH 09/10] MVKImage: Only create a stencil view for reading. We don't want to do this for stencil attachment views, because we use the original packed depth/stencil format in render pipelines, and Metal's validation layer for some reason doesn't consider packed formats and their corresponding stencil view formats to match. So only do this if the image view usage includes `SAMPLED` or `INPUT_ATTACHMENT`. --- MoltenVK/MoltenVK/GPUObjects/MVKImage.h | 1 + MoltenVK/MoltenVK/GPUObjects/MVKImage.mm | 11 ++++++++--- MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.mm | 3 +-- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKImage.h b/MoltenVK/MoltenVK/GPUObjects/MVKImage.h index 58821322..9afdbb73 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKImage.h +++ b/MoltenVK/MoltenVK/GPUObjects/MVKImage.h @@ -588,6 +588,7 @@ public: * This is a static function that can be used to validate image view formats prior to creating one. */ static VkResult validateSwizzledMTLPixelFormat(const VkImageViewCreateInfo* pCreateInfo, + VkImageUsageFlags usage, MVKVulkanAPIObject* apiObject, bool hasNativeSwizzleSupport, bool hasShaderSwizzleSupport, diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm b/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm index a50fb5a2..a068a159 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm @@ -1366,6 +1366,7 @@ MVKImageViewPlane::MVKImageViewPlane(MVKImageView* imageView, bool useSwizzle; _imageView->setConfigurationResult(_imageView->validateSwizzledMTLPixelFormat(pCreateInfo, + _imageView->_usage, _imageView, _device->_pMetalFeatures->nativeTextureSwizzle, _device->_pMVKConfig->fullImageViewSwizzle, @@ -1506,6 +1507,7 @@ MVKImageView::MVKImageView(MVKDevice* device, } VkResult MVKImageView::validateSwizzledMTLPixelFormat(const VkImageViewCreateInfo* pCreateInfo, + VkImageUsageFlags usage, MVKVulkanAPIObject* apiObject, bool hasNativeSwizzleSupport, bool hasShaderSwizzleSupport, @@ -1520,7 +1522,8 @@ VkResult MVKImageView::validateSwizzledMTLPixelFormat(const VkImageViewCreateInf // If we have an identity swizzle, we're all good. if (SWIZZLE_MATCHES(R, G, B, A)) { // Change to stencil-only format if only stencil aspect is requested - if (pCreateInfo->subresourceRange.aspectMask == VK_IMAGE_ASPECT_STENCIL_BIT) { + if (pCreateInfo->subresourceRange.aspectMask == VK_IMAGE_ASPECT_STENCIL_BIT && + mvkIsAnyFlagEnabled(usage, (VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT))) { if (mtlPixFmt == MTLPixelFormatDepth32Float_Stencil8) mtlPixFmt = MTLPixelFormatX32_Stencil8; #if MVK_MACOS @@ -1577,7 +1580,8 @@ VkResult MVKImageView::validateSwizzledMTLPixelFormat(const VkImageViewCreateInf case MTLPixelFormatDepth32Float_Stencil8: // If aspect mask looking only for stencil then change to stencil-only format even if shader swizzling is needed - if (pCreateInfo->subresourceRange.aspectMask == VK_IMAGE_ASPECT_STENCIL_BIT) { + if (pCreateInfo->subresourceRange.aspectMask == VK_IMAGE_ASPECT_STENCIL_BIT && + mvkIsAnyFlagEnabled(usage, (VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT))) { mtlPixFmt = MTLPixelFormatX32_Stencil8; if (SWIZZLE_MATCHES(R, ANY, ANY, ANY)) { return VK_SUCCESS; @@ -1588,7 +1592,8 @@ VkResult MVKImageView::validateSwizzledMTLPixelFormat(const VkImageViewCreateInf #if MVK_MACOS case MTLPixelFormatDepth24Unorm_Stencil8: // If aspect mask looking only for stencil then change to stencil-only format even if shader swizzling is needed - if (pCreateInfo->subresourceRange.aspectMask == VK_IMAGE_ASPECT_STENCIL_BIT) { + if (pCreateInfo->subresourceRange.aspectMask == VK_IMAGE_ASPECT_STENCIL_BIT && + mvkIsAnyFlagEnabled(usage, (VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT))) { mtlPixFmt = MTLPixelFormatX24_Stencil8; if (SWIZZLE_MATCHES(R, ANY, ANY, ANY)) { return VK_SUCCESS; diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.mm b/MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.mm index e9f03acc..34cb6c4e 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.mm @@ -545,8 +545,7 @@ MTLTextureUsage MVKPixelFormats::getMTLTextureUsage(VkImageUsageFlags vkImageUsa if (mvkIsAnyFlagEnabled(vkImageUsageFlags, (VK_IMAGE_USAGE_TRANSFER_SRC_BIT | // May use temp view if transfer involves format change VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT | - VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT | - VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)) && + VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT)) && isCombinedDepthStencilFmt && supportsStencilViews) { mvkEnableFlags(mtlUsage, MTLTextureUsagePixelFormatView); From f4065f8a7220bd05851544d0102bfad41e47ce79 Mon Sep 17 00:00:00 2001 From: Chip Davis Date: Mon, 21 Sep 2020 12:24:52 -0500 Subject: [PATCH 10/10] MVKDevice: Fix reported image format properties for 1D images as 2D. Fall through to the 2D case, so all the special handling for 2D is used for 1D as well. Also, make sure 1D doesn't report multisampling or support for 420 subsampled formats. There is no `MTLTextureType1DMultisample` anyway. Also, clear the `VkImageFormatProperties` struct if the format is not supported with the given parameters. Some tests seem to expect this. --- MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm index 5f0a1f45..cd7ab9e0 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm @@ -412,6 +412,8 @@ VkResult MVKPhysicalDevice::getImageFormatProperties(VkFormat format, if ( !pImageFormatProperties ) { return VK_SUCCESS; } + mvkClear(pImageFormatProperties); + // Metal does not support creating uncompressed views of compressed formats. // Metal does not support split-instance images. if (mvkIsAnyFlagEnabled(flags, VK_IMAGE_CREATE_BLOCK_TEXEL_VIEW_COMPATIBLE_BIT | VK_IMAGE_CREATE_SPLIT_INSTANCE_BIND_REGIONS_BIT)) { @@ -444,10 +446,7 @@ VkResult MVKPhysicalDevice::getImageFormatProperties(VkFormat format, case VK_IMAGE_TYPE_1D: maxExt.height = 1; maxExt.depth = 1; - if (mvkTreatTexture1DAs2D()) { - maxExt.width = pLimits->maxImageDimension2D; - maxLevels = mvkMipmapLevels3D(maxExt); - } else { + if (!mvkTreatTexture1DAs2D()) { maxExt.width = pLimits->maxImageDimension1D; maxLevels = 1; sampleCounts = VK_SAMPLE_COUNT_1_BIT; @@ -462,18 +461,27 @@ VkResult MVKPhysicalDevice::getImageFormatProperties(VkFormat format, 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; } - break; + // A 420 1D image doesn't make much sense. + if (isChromaSubsampled && _pixelFormats.getBlockTexelSize(format).height > 1) { + return VK_ERROR_FORMAT_NOT_SUPPORTED; + } + // Vulkan doesn't allow 1D multisampled images. + sampleCounts = VK_SAMPLE_COUNT_1_BIT; + /* fallthrough */ 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; } + // 1D cube images aren't supported. + if (type == VK_IMAGE_TYPE_1D) { return VK_ERROR_FORMAT_NOT_SUPPORTED; } maxExt.width = pLimits->maxImageDimensionCube; maxExt.height = pLimits->maxImageDimensionCube; } else { maxExt.width = pLimits->maxImageDimension2D; - maxExt.height = pLimits->maxImageDimension2D; + maxExt.height = (type == VK_IMAGE_TYPE_1D ? 1 : pLimits->maxImageDimension2D); } maxExt.depth = 1; if (tiling == VK_IMAGE_TILING_LINEAR) {