Validate format capabilities for MSAA, renderpass attachments, and vkCmdResolveImage().

vkGetPhysicalDeviceImageFormatProperties() tests whether format supports MSAA.
vkCreateRenderPass() validate each attachment supports format capabilities required
by each subpass that uses it.
vkCmdResolveImage() validate that destination attachment supports being resolved to.
MVKPixelFormats add getVkFormatCapabilities(), getMTLPixelFormatCapabilities(),
and vkFormatIsSupportedOrSubstitutable().
Rework type handling in flag functions in MVKFoundation.h.
This commit is contained in:
Bill Hollings 2020-03-26 00:05:01 -04:00
parent 50eba6fee1
commit 4f1cdef6af
8 changed files with 148 additions and 104 deletions

View File

@ -21,6 +21,7 @@ Released 2020/03/30
- Accurately populate Vulkan `VkFormatProperties` from `MTLPixelFormat` capabilities,
taking into consideration variations across `MTLDevice` Features Sets.
- Validate format capabilities for MSAA, renderpass attachments, and `vkCmdResolveImage()`.
- Fix issue where immutable samplers are removed during descriptor update.
- Guard against Metal validation assertion from reuse of query number within a single `MTLRenderEncoder`.
- Increase value of `VkPhysicalDeviceLimits::minStorageBufferOffsetAlignment`

View File

@ -467,6 +467,11 @@ void MVKCmdResolveImage::setContent(VkImage srcImage,
_dstImage->getTransferDescriptorData(_transferImageData);
_transferImageData.samples = _srcImage->getSampleCount();
// Validate
if ( !mvkAreAllFlagsEnabled(getPixelFormats()->getMTLPixelFormatCapabilities(_dstImage->getMTLPixelFormat()), kMVKMTLFmtCapsResolve) ) {
setConfigurationResult(reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCmdResolveImage(): %s cannot be used as a resolve destination on this device.", getPixelFormats()->getVkFormatName(_dstImage->getVkFormat())));
}
}
/**

View File

@ -246,11 +246,13 @@ VkResult MVKPhysicalDevice::getImageFormatProperties(VkFormat format,
VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT));
VkPhysicalDeviceLimits* pLimits = &_properties.limits;
VkExtent3D maxExt;
uint32_t maxLevels;
VkExtent3D maxExt = { 1, 1, 1};
uint32_t maxLevels = 1;
uint32_t maxLayers = hasAttachmentUsage ? pLimits->maxFramebufferLayers : pLimits->maxImageArrayLayers;
VkSampleCountFlags sampleCounts = _metalFeatures.supportedSampleCounts;
bool supportsMSAA = mvkAreAllFlagsEnabled(_pixelFormats.getVkFormatCapabilities(format), kMVKMTLFmtCapsMSAA);
VkSampleCountFlags sampleCounts = supportsMSAA ? _metalFeatures.supportedSampleCounts : VK_SAMPLE_COUNT_1_BIT;
switch (type) {
case VK_IMAGE_TYPE_1D:
maxExt.height = 1;
@ -274,6 +276,7 @@ VkResult MVKPhysicalDevice::getImageFormatProperties(VkFormat format,
if (mvkFmt == kMVKFormatCompressed) { return VK_ERROR_FORMAT_NOT_SUPPORTED; }
}
break;
case VK_IMAGE_TYPE_2D:
if (mvkIsAnyFlagEnabled(flags, VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT) ) {
maxExt.width = pLimits->maxImageDimensionCube;
@ -311,6 +314,7 @@ VkResult MVKPhysicalDevice::getImageFormatProperties(VkFormat format,
maxLevels = mvkMipmapLevels3D(maxExt);
}
break;
case VK_IMAGE_TYPE_3D:
// Metal does not allow linear tiling on 3D textures
if (tiling == VK_IMAGE_TILING_LINEAR) {
@ -337,19 +341,9 @@ VkResult MVKPhysicalDevice::getImageFormatProperties(VkFormat format,
maxLayers = 1;
sampleCounts = VK_SAMPLE_COUNT_1_BIT;
break;
default:
// Metal does not allow linear tiling on anything but 2D textures
if (tiling == VK_IMAGE_TILING_LINEAR) { return VK_ERROR_FORMAT_NOT_SUPPORTED; }
// Metal does not allow compressed or depth/stencil formats on anything but 2D textures
if (mvkFmt == kMVKFormatDepthStencil || mvkFmt == kMVKFormatCompressed) {
return VK_ERROR_FORMAT_NOT_SUPPORTED;
}
maxExt = { 1, 1, 1};
maxLayers = 1;
maxLevels = 1;
sampleCounts = VK_SAMPLE_COUNT_1_BIT;
break;
default:
return VK_ERROR_FORMAT_NOT_SUPPORTED; // Illegal VkImageType
}
pImageFormatProperties->maxExtent = maxExt;

View File

@ -87,10 +87,14 @@ typedef struct {
bool hasReportedSubstitution;
inline double bytesPerTexel() const { return (double)bytesPerBlock / (double)(blockTexelSize.width * blockTexelSize.height); };
inline bool isSupported() const { return (mtlPixelFormat != MTLPixelFormatInvalid); };
inline bool isSupportedOrSubstitutable() const { return isSupported() || (mtlPixelFormatSubstitute != MTLPixelFormatInvalid); };
inline MTLPixelFormat getMTLPixelFormatOrSubstitute() const { return mtlPixelFormat ? mtlPixelFormat : mtlPixelFormatSubstitute; }
inline bool vertexIsSupported() const { return (mtlVertexFormat != MTLVertexFormatInvalid); };
inline bool vertexIsSupportedOrSubstitutable() const { return vertexIsSupported() || (mtlVertexFormatSubstitute != MTLVertexFormatInvalid); };
inline MTLVertexFormat getMTLVertexFormatOrSubstitute() const { return mtlVertexFormat ? mtlVertexFormat : mtlVertexFormatSubstitute; }
} MVKVkFormatDesc;
/** Describes the properties of a MTLPixelFormat or MTLVertexFormat. */
@ -121,6 +125,9 @@ public:
/** Returns whether the VkFormat is supported by this implementation. */
bool vkFormatIsSupported(VkFormat vkFormat);
/** Returns whether the VkFormat is supported by this implementation, or can be substituted by one that is. */
bool vkFormatIsSupportedOrSubstitutable(VkFormat vkFormat);
/** Returns whether the MTLPixelFormat is supported by this implementation. */
bool mtlPixelFormatIsSupported(MTLPixelFormat mtlFormat);
@ -225,6 +232,12 @@ public:
/** Returns the default properties for the specified Vulkan format. */
VkFormatProperties getVkFormatProperties(VkFormat vkFormat);
/** Returns the Metal format capabilities supported by the specified Vulkan format. */
MVKMTLFmtCaps getVkFormatCapabilities(VkFormat vkFormat);
/** Returns the Metal format capabilities supported by the specified Metal format. */
MVKMTLFmtCaps getMTLPixelFormatCapabilities(MTLPixelFormat mtlFormat);
/** Returns the name of the specified Vulkan format. */
const char* getVkFormatName(VkFormat vkFormat);
@ -266,7 +279,9 @@ public:
protected:
MVKVkFormatDesc& getVkFormatDesc(VkFormat vkFormat);
MVKVkFormatDesc& getVkFormatDesc(MTLPixelFormat mtlFormat);
MVKMTLFormatDesc& getMTLPixelFormatDesc(VkFormat vkFormat);
MVKMTLFormatDesc& getMTLPixelFormatDesc(MTLPixelFormat mtlFormat);
MVKMTLFormatDesc& getMTLVertexFormatDesc(VkFormat vkFormat);
MVKMTLFormatDesc& getMTLVertexFormatDesc(MTLVertexFormat mtlFormat);
VkFormatFeatureFlags getOptimalTilingFeatures(MVKMTLFmtCaps mtlFmtCaps);
VkFormatFeatureFlags getLinearTilingFeatures(MVKMTLFmtCaps mtlFmtCaps, MVKFormatType mvkFmtType);

View File

@ -120,6 +120,10 @@ bool MVKPixelFormats::vkFormatIsSupported(VkFormat vkFormat) {
return getVkFormatDesc(vkFormat).isSupported();
}
bool MVKPixelFormats::vkFormatIsSupportedOrSubstitutable(VkFormat vkFormat) {
return getVkFormatDesc(vkFormat).isSupportedOrSubstitutable();
}
bool MVKPixelFormats::mtlPixelFormatIsSupported(MTLPixelFormat mtlFormat) {
return getMTLPixelFormatDesc(mtlFormat).isSupported();
}
@ -260,6 +264,14 @@ VkFormatProperties MVKPixelFormats::getVkFormatProperties(VkFormat vkFormat) {
return getVkFormatDesc(vkFormat).properties;
}
MVKMTLFmtCaps MVKPixelFormats::getVkFormatCapabilities(VkFormat vkFormat) {
return getMTLPixelFormatDesc(vkFormat).mtlFmtCaps;
}
MVKMTLFmtCaps MVKPixelFormats::getMTLPixelFormatCapabilities(MTLPixelFormat mtlFormat) {
return getMTLPixelFormatDesc(mtlFormat).mtlFmtCaps;
}
const char* MVKPixelFormats::getVkFormatName(VkFormat vkFormat) {
return getVkFormatDesc(vkFormat).name;
}
@ -389,23 +401,33 @@ MVKVkFormatDesc& MVKPixelFormats::getVkFormatDesc(VkFormat vkFormat) {
return _vkFormatDescriptions[fmtIdx];
}
// Return a reference to the Vulkan format descriptor corresponding to the MTLPixelFormat.
MVKVkFormatDesc& MVKPixelFormats::getVkFormatDesc(MTLPixelFormat mtlFormat) {
return getVkFormatDesc(getMTLPixelFormatDesc(mtlFormat).vkFormat);
}
// Return a reference to the Metal format descriptor corresponding to the VkFormat.
MVKMTLFormatDesc& MVKPixelFormats::getMTLPixelFormatDesc(VkFormat vkFormat) {
return getMTLPixelFormatDesc(getVkFormatDesc(vkFormat).getMTLPixelFormatOrSubstitute());
}
// Return a reference to the Metal format descriptor corresponding to the MTLPixelFormat.
MVKMTLFormatDesc& MVKPixelFormats::getMTLPixelFormatDesc(MTLPixelFormat mtlFormat) {
uint16_t fmtIdx = (mtlFormat < _mtlPixelFormatCount) ? _mtlFormatDescIndicesByMTLPixelFormats[mtlFormat] : 0;
return _mtlPixelFormatDescriptions[fmtIdx];
}
// Return a reference to the Metal format descriptor corresponding to the VkFormat.
MVKMTLFormatDesc& MVKPixelFormats::getMTLVertexFormatDesc(VkFormat vkFormat) {
return getMTLVertexFormatDesc(getVkFormatDesc(vkFormat).getMTLVertexFormatOrSubstitute());
}
// Return a reference to the Metal format descriptor corresponding to the MTLVertexFormat.
MVKMTLFormatDesc& MVKPixelFormats::getMTLVertexFormatDesc(MTLVertexFormat mtlFormat) {
uint16_t fmtIdx = (mtlFormat < _mtlVertexFormatCount) ? _mtlFormatDescIndicesByMTLVertexFormats[mtlFormat] : 0;
return _mtlVertexFormatDescriptions[fmtIdx];
}
// Return a reference to the Vulkan format descriptor corresponding to the MTLPixelFormat.
MVKVkFormatDesc& MVKPixelFormats::getVkFormatDesc(MTLPixelFormat mtlFormat) {
return getVkFormatDesc(getMTLPixelFormatDesc(mtlFormat).vkFormat);
}
#pragma mark Construction
@ -973,8 +995,7 @@ void MVKPixelFormats::addMTLPixelFormatCapabilities(id<MTLDevice> mtlDevice,
MTLPixelFormat mtlPixFmt,
MVKMTLFmtCaps mtlFmtCaps) {
if ( [mtlDevice supportsFeatureSet: mtlFeatSet] ) {
auto& fmtDesc = getMTLPixelFormatDesc(mtlPixFmt);
fmtDesc.mtlFmtCaps = (MVKMTLFmtCaps)(fmtDesc.mtlFmtCaps | mtlFmtCaps);
mvkEnableFlags(getMTLPixelFormatDesc(mtlPixFmt).mtlFmtCaps, mtlFmtCaps);
}
}
@ -984,8 +1005,7 @@ void MVKPixelFormats::addMTLVertexFormatCapabilities(id<MTLDevice> mtlDevice,
MTLVertexFormat mtlVtxFmt,
MVKMTLFmtCaps mtlFmtCaps) {
if ( [mtlDevice supportsFeatureSet: mtlFeatSet] ) {
auto& fmtDesc = getMTLVertexFormatDesc(mtlVtxFmt);
fmtDesc.mtlFmtCaps = (MVKMTLFmtCaps)(fmtDesc.mtlFmtCaps | mtlFmtCaps);
mvkEnableFlags(getMTLVertexFormatDesc(mtlVtxFmt).mtlFmtCaps, mtlFmtCaps);
}
}
@ -1159,8 +1179,8 @@ void MVKPixelFormats::buildVkFormatMaps() {
// Vulkan format features
MVKFormatType fmtType = vkDesc.formatType;
MVKMTLFmtCaps mtlPixFmtCaps = getMTLPixelFormatDesc(vkDesc.mtlPixelFormat ?: vkDesc.mtlPixelFormatSubstitute).mtlFmtCaps;
MVKMTLFmtCaps mtlVtxFmtCaps = getMTLVertexFormatDesc(vkDesc.mtlVertexFormat ?: vkDesc.mtlVertexFormatSubstitute).mtlFmtCaps;
MVKMTLFmtCaps mtlPixFmtCaps = getMTLPixelFormatDesc(vkFmt).mtlFmtCaps;
MVKMTLFmtCaps mtlVtxFmtCaps = getMTLVertexFormatDesc(vkFmt).mtlFmtCaps;
vkDesc.properties = { getLinearTilingFeatures(mtlPixFmtCaps, fmtType),
getOptimalTilingFeatures(mtlPixFmtCaps),
getBufferFeatures(mtlPixFmtCaps, mtlVtxFmtCaps, fmtType) };

View File

@ -84,7 +84,7 @@ private:
friend class MVKRenderPass;
friend class MVKRenderPassAttachment;
bool isUsingAttachmentAt(uint32_t rpAttIdx);
MVKMTLFmtCaps getRequiredFormatCapabilitiesForAttachmentAt(uint32_t rpAttIdx);
MVKRenderPass* _renderPass;
uint32_t _subpassIndex;
@ -134,8 +134,6 @@ public:
const VkAttachmentDescription* pCreateInfo);
protected:
VkAttachmentDescription validate(const VkAttachmentDescription* pCreateInfo);
VkAttachmentDescription _info;
MVKRenderPass* _renderPass;
uint32_t _attachmentIndex;

View File

@ -197,24 +197,33 @@ void MVKRenderSubpass::populateClearAttachments(MVKVector<VkClearAttachment>& cl
}
}
/**
* Returns whether this subpass uses the attachment at the specified index within the
* parent renderpass, as any of input, color, resolve, or depth/stencil attachment type.
*/
bool MVKRenderSubpass::isUsingAttachmentAt(uint32_t rpAttIdx) {
//Returns the format capabilities required by this render subpass.
// The Vulkan spec is unclear about whether a subpass can use an attachment in multiple places,
// so accumulate the capabilities from all possible attachments, just to be safe.
MVKMTLFmtCaps MVKRenderSubpass::getRequiredFormatCapabilitiesForAttachmentAt(uint32_t rpAttIdx) {
MVKMTLFmtCaps caps = kMVKMTLFmtCapsNone;
for (auto& att : _inputAttachments) {
if (att.attachment == rpAttIdx) { return true; }
if (att.attachment == rpAttIdx) {
mvkEnableFlags(caps, kMVKMTLFmtCapsNone); // TODO: input attachments are current unsupported
break;
}
}
for (auto& att : _colorAttachments) {
if (att.attachment == rpAttIdx) { return true; }
if (att.attachment == rpAttIdx) {
mvkEnableFlags(caps, kMVKMTLFmtCapsColorAtt);
break;
}
}
for (auto& att : _resolveAttachments) {
if (att.attachment == rpAttIdx) { return true; }
if (att.attachment == rpAttIdx) {
mvkEnableFlags(caps, kMVKMTLFmtCapsResolve);
break;
}
if (_depthStencilAttachment.attachment == rpAttIdx) { return true; }
}
if (_depthStencilAttachment.attachment == rpAttIdx) { mvkEnableFlags(caps, kMVKMTLFmtCapsDSAtt); }
return false;
return caps;
}
MVKRenderSubpass::MVKRenderSubpass(MVKRenderPass* renderPass,
@ -310,33 +319,35 @@ bool MVKRenderPassAttachment::shouldUseClearAttachment(MVKRenderSubpass* subpass
MVKRenderPassAttachment::MVKRenderPassAttachment(MVKRenderPass* renderPass,
const VkAttachmentDescription* pCreateInfo) {
_info = *pCreateInfo;
_renderPass = renderPass;
_attachmentIndex = uint32_t(_renderPass->_attachments.size());
// Determine the indices of the first and last render subpasses to use that attachment.
// Validate pixel format is supported
MVKPixelFormats* pixFmts = _renderPass->getPixelFormats();
if ( !pixFmts->vkFormatIsSupportedOrSubstitutable(_info.format) ) {
_renderPass->setConfigurationResult(reportError(VK_ERROR_FORMAT_NOT_SUPPORTED, "vkCreateRenderPass(): Attachment format %s is not supported on this device.", _renderPass->getPixelFormats()->getVkFormatName(_info.format)));
}
// Determine the indices of the first and last render subpasses to use this attachment.
_firstUseSubpassIdx = kMVKUndefinedLargeUInt32;
_lastUseSubpassIdx = 0;
for (auto& subPass : _renderPass->_subpasses) {
if (subPass.isUsingAttachmentAt(_attachmentIndex)) {
// If it uses this attachment, the subpass will identify required format capabilities.
MVKMTLFmtCaps reqCaps = subPass.getRequiredFormatCapabilitiesForAttachmentAt(_attachmentIndex);
if (reqCaps) {
uint32_t spIdx = subPass._subpassIndex;
_firstUseSubpassIdx = MIN(spIdx, _firstUseSubpassIdx);
_lastUseSubpassIdx = MAX(spIdx, _lastUseSubpassIdx);
_firstUseSubpassIdx = min(spIdx, _firstUseSubpassIdx);
_lastUseSubpassIdx = max(spIdx, _lastUseSubpassIdx);
// Validate that the attachment pixel format supports the capabilities required by the subpass.
if ( !mvkAreAllFlagsEnabled(pixFmts->getVkFormatCapabilities(_info.format), reqCaps) ) {
_renderPass->setConfigurationResult(reportError(VK_ERROR_FORMAT_NOT_SUPPORTED, "vkCreateRenderPass(): Attachment format %s on this device does not support the VkFormat attachment capabilities required by the subpass at index %d.", _renderPass->getPixelFormats()->getVkFormatName(_info.format), spIdx));
}
}
}
}
_info = validate(pCreateInfo);
}
// Validate and potentially modify the create info
VkAttachmentDescription MVKRenderPassAttachment::validate(const VkAttachmentDescription* pCreateInfo) {
VkAttachmentDescription info = *pCreateInfo;
if ( !_renderPass->getPixelFormats()->getMTLPixelFormatFromVkFormat(info.format) ) {
_renderPass->setConfigurationResult(reportError(VK_ERROR_FORMAT_NOT_SUPPORTED, "vkCreateRenderPass(): Attachment format %s is not supported on this device.", _renderPass->getPixelFormats()->getVkFormatName(info.format)));
}
return info;
}
#pragma mark -
#pragma mark MVKRenderPass

View File

@ -497,26 +497,26 @@ bool mvkSetOrClear(T* pDest, const T* pSrc) {
#pragma mark Boolean flags
/** Enables the flags (sets bits to 1) within the value parameter specified by the bitMask parameter. */
template<typename T1, typename T2>
void mvkEnableFlags(T1& value, const T2 bitMask) { value |= bitMask; }
template<typename Tv, typename Tm>
void mvkEnableFlags(Tv& value, const Tm bitMask) { value = (Tv)(value | bitMask); }
/** Disables the flags (sets bits to 0) within the value parameter specified by the bitMask parameter. */
template<typename T1, typename T2>
void mvkDisableFlags(T1& value, const T2 bitMask) { value &= ~bitMask; }
template<typename Tv, typename Tm>
void mvkDisableFlags(Tv& value, const Tm bitMask) { value = (Tv)(value & ~(Tv)bitMask); }
/** Returns whether the specified value has ANY of the flags specified in bitMask enabled (set to 1). */
template<typename T1, typename T2>
bool mvkIsAnyFlagEnabled(T1 value, const T2 bitMask) { return !!(value & bitMask); }
template<typename Tv, typename Tm>
bool mvkIsAnyFlagEnabled(Tv value, const Tm bitMask) { return !!(value & bitMask); }
/** Returns whether the specified value has ALL of the flags specified in bitMask enabled (set to 1). */
template<typename T1, typename T2>
bool mvkAreAllFlagsEnabled(T1 value, const T2 bitMask) { return ((value & bitMask) == bitMask); }
template<typename Tv, typename Tm>
bool mvkAreAllFlagsEnabled(Tv value, const Tm bitMask) { return ((value & bitMask) == bitMask); }
/** Returns whether the specified value has ONLY one or more of the flags specified in bitMask enabled (set to 1), and none others. */
template<typename T1, typename T2>
bool mvkIsOnlyAnyFlagEnabled(T1 value, const T2 bitMask) { return (mvkIsAnyFlagEnabled(value, bitMask) && ((value | bitMask) == bitMask)); }
template<typename Tv, typename Tm>
bool mvkIsOnlyAnyFlagEnabled(Tv value, const Tm bitMask) { return (mvkIsAnyFlagEnabled(value, bitMask) && ((value | bitMask) == bitMask)); }
/** Returns whether the specified value has ONLY ALL of the flags specified in bitMask enabled (set to 1), and none others. */
template<typename T1, typename T2>
bool mvkAreOnlyAllFlagsEnabled(T1 value, const T2 bitMask) { return (value == bitMask); }
template<typename Tv, typename Tm>
bool mvkAreOnlyAllFlagsEnabled(Tv value, const Tm bitMask) { return (value == bitMask); }