Refactor MVKImageView use without MVKDevice for testing formats.

Replace MVKImageView::getSwizzledMTLPixelFormat() with static validateSwizzledMTLPixelFormat()
to support querying format support without having to create fake MVKImageView instance from
MVKPhysicalDevice::getImageViewIsSupported().
Remove ability for MVKDeviceTrackingMixin and subclasses to work without a MVKDevice.
Remove mvkPlatformPixelFormats().
This commit is contained in:
Bill Hollings 2020-03-26 14:40:08 -04:00
parent 04ae0a61e0
commit 3fa95d4c06
6 changed files with 108 additions and 95 deletions

View File

@ -719,7 +719,6 @@ protected:
/**
* This mixin class adds the ability for an object to track the device that created it.
* Implementation supports an instance where the device is null.
*
* As a mixin, this class should only be used as a component of multiple inheritance.
* Any class that inherits from this class should also inherit from MVKBaseObject.
@ -733,18 +732,19 @@ public:
inline MVKDevice* getDevice() { return _device; }
/** Returns the underlying Metal device. */
inline id<MTLDevice> getMTLDevice() { return _device ? _device->getMTLDevice() : nil; }
inline id<MTLDevice> getMTLDevice() { return _device->getMTLDevice(); }
/** Returns info about the pixel format supported by the physical device. */
inline MVKPixelFormats* getPixelFormats() { return _device ? _device->getPixelFormats() : mvkPlatformPixelFormats(); }
inline MVKPixelFormats* getPixelFormats() { return _device->getPixelFormats(); }
/** Constructs an instance for the specified device. */
MVKDeviceTrackingMixin(MVKDevice* device) : _device(device) {}
MVKDeviceTrackingMixin(MVKDevice* device) : _device(device) { assert(_device); }
virtual ~MVKDeviceTrackingMixin() {}
protected:
virtual MVKBaseObject* getBaseObject() = 0;
MVKDevice* _device;
};

View File

@ -379,9 +379,9 @@ VkResult MVKPhysicalDevice::getImageFormatProperties(const VkPhysicalDeviceImage
// If the image format info links portability image view info, test if an image view of that configuration is supported
bool MVKPhysicalDevice::getImageViewIsSupported(const VkPhysicalDeviceImageFormatInfo2KHR *pImageFormatInfo) {
auto* next = (VkStructureType*)pImageFormatInfo->pNext;
auto* next = (MVKVkAPIStructHeader*)pImageFormatInfo->pNext;
while (next) {
switch ((int32_t)*next) {
switch ((uint32_t)next->sType) {
case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_VIEW_SUPPORT_EXTX: {
auto* portImgViewInfo = (VkPhysicalDeviceImageViewSupportEXTX*)next;
@ -390,7 +390,7 @@ bool MVKPhysicalDevice::getImageViewIsSupported(const VkPhysicalDeviceImageForma
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
.pNext = (VkStructureType*)portImgViewInfo->pNext,
.flags = portImgViewInfo->flags,
.image = VK_NULL_HANDLE,
.image = nullptr,
.viewType = portImgViewInfo->viewType,
.format = portImgViewInfo->format,
.components = portImgViewInfo->components,
@ -401,11 +401,15 @@ bool MVKPhysicalDevice::getImageViewIsSupported(const VkPhysicalDeviceImageForma
.baseArrayLayer = 0,
.layerCount = 1},
};
MVKImageView imgView(VK_NULL_HANDLE, &viewInfo, _mvkInstance->getMoltenVKConfiguration());
return imgView.getConfigurationResult() == VK_SUCCESS;
MTLPixelFormat mtlPixFmt;
bool useSwizzle;
return (MVKImageView::validateSwizzledMTLPixelFormat(&viewInfo, &_pixelFormats, this,
_metalFeatures.nativeTextureSwizzle,
_mvkInstance->getMoltenVKConfiguration()->fullImageViewSwizzle,
mtlPixFmt, useSwizzle) == VK_SUCCESS);
}
default:
next = (VkStructureType*)((VkPhysicalDeviceFeatures2*)next)->pNext;
next = (MVKVkAPIStructHeader*)next->pNext;
break;
}
}

View File

@ -323,6 +323,30 @@ public:
*/
void populateMTLRenderPassAttachmentDescriptorResolve(MTLRenderPassAttachmentDescriptor* mtlAttDesc);
/**
* Returns, in mtlPixFmt, a MTLPixelFormat, based on the MTLPixelFormat converted from
* the VkFormat, but possibly modified by the swizzles defined in the VkComponentMapping
* of the VkImageViewCreateInfo.
*
* Metal prior to version 3.0 does not support native per-texture swizzles, so if the swizzle
* is not an identity swizzle, this function attempts to find an alternate MTLPixelFormat that
* coincidentally matches the swizzled format.
*
* If a replacement MTLFormat was found, it is returned and useSwizzle is set to false.
* If a replacement MTLFormat could not be found, the original MTLPixelFormat is returned,
* and the useSwizzle is set to true, indicating that either native or shader swizzling
* should be used for this image view.
*
* This is a static function that can be used to validate image view formats prior to creating one.
*/
static VkResult validateSwizzledMTLPixelFormat(const VkImageViewCreateInfo* pCreateInfo,
MVKPixelFormats* mvkPixFmts,
MVKVulkanAPIObject* apiObject,
bool hasNativeSwizzleSupport,
bool hasShaderSwizzleSupport,
MTLPixelFormat& mtlPixFmt,
bool& useSwizzle);
#pragma mark Construction
@ -336,10 +360,6 @@ protected:
void propogateDebugName() override;
id<MTLTexture> newMTLTexture();
void initMTLTextureViewSupport();
MTLPixelFormat getSwizzledMTLPixelFormat(VkFormat format,
VkComponentMapping components,
bool& useSwizzle,
const MVKConfiguration* pMVKConfig);
void validateImageViewConfig(const VkImageViewCreateInfo* pCreateInfo);
MVKImage* _image;

View File

@ -634,7 +634,7 @@ MVKImage::MVKImage(MVKDevice* device, const VkImageCreateInfo* pCreateInfo) : MV
_mtlPixelFormat = pixFmts->getMTLPixelFormatFromVkFormat(pCreateInfo->format);
_usage = pCreateInfo->usage;
_is3DCompressed = (getImageType() == VK_IMAGE_TYPE_3D) && (pixFmts->getFormatTypeFromVkFormat(pCreateInfo->format) == kMVKFormatCompressed) && !getDevice()->_pMetalFeatures->native3DCompressedTextures;
_is3DCompressed = (getImageType() == VK_IMAGE_TYPE_3D) && (pixFmts->getFormatTypeFromVkFormat(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));
_canSupportMTLTextureView = !_isDepthStencilAttachment || _device->_pMetalFeatures->stencilViews;
@ -705,7 +705,7 @@ void MVKImage::validateConfig(const VkImageCreateInfo* pCreateInfo, bool isAttac
if (isCompressed && !is2D) {
if (getImageType() != VK_IMAGE_TYPE_3D) {
setConfigurationResult(reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCreateImage() : Under Metal, compressed formats may only be used with 2D or 3D images."));
} else if (!getDevice()->_pMetalFeatures->native3DCompressedTextures && !mvkCanDecodeFormat(pCreateInfo->format)) {
} else if (!_device->_pMetalFeatures->native3DCompressedTextures && !mvkCanDecodeFormat(pCreateInfo->format)) {
setConfigurationResult(reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCreateImage() : Under Metal, the %s compressed format may only be used with 2D images.", getPixelFormats()->getVkFormatName(pCreateInfo->format)));
}
}
@ -896,7 +896,7 @@ id<MTLTexture> MVKImageView::newMTLTexture() {
mtlTextureType = MTLTextureType3D;
sliceRange = NSMakeRange(0, 1);
}
if (getDevice()->_pMetalFeatures->nativeTextureSwizzle && _packedSwizzle) {
if (_device->_pMetalFeatures->nativeTextureSwizzle && _packedSwizzle) {
return [_image->getMTLTexture() newTextureViewWithPixelFormat: _mtlPixelFormat
textureType: mtlTextureType
levels: NSMakeRange(_subresourceRange.baseMipLevel, _subresourceRange.levelCount)
@ -913,27 +913,24 @@ id<MTLTexture> MVKImageView::newMTLTexture() {
#pragma mark Construction
// device and _image may be nil when a temporary instance
// is constructed to validate image view capabilities
MVKImageView::MVKImageView(MVKDevice* device,
const VkImageViewCreateInfo* pCreateInfo,
const MVKConfiguration* pAltMVKConfig) : MVKVulkanAPIDeviceObject(device) {
_image = (MVKImage*)pCreateInfo->image;
_usage = _image ? _image->_usage : 0;
_usage = _image->_usage;
auto* next = (VkStructureType*)pCreateInfo->pNext;
auto* next = (MVKVkAPIStructHeader*)pCreateInfo->pNext;
while (next) {
switch (*next) {
case VK_STRUCTURE_TYPE_IMAGE_VIEW_USAGE_CREATE_INFO: {
auto* pViewUsageInfo = (VkImageViewUsageCreateInfo*)next;
if (!(pViewUsageInfo->usage & ~_usage))
_usage = pViewUsageInfo->usage;
next = (VkStructureType*)pViewUsageInfo->pNext;
break;
}
default:
next = (VkStructureType*)((VkImageViewCreateInfo*)next)->pNext;
break;
switch ((uint32_t)next->sType) {
case VK_STRUCTURE_TYPE_IMAGE_VIEW_USAGE_CREATE_INFO: {
auto* pViewUsageInfo = (VkImageViewUsageCreateInfo*)next;
if (!(pViewUsageInfo->usage & ~_usage)) { _usage = pViewUsageInfo->usage; }
next = (MVKVkAPIStructHeader*)next->pNext;
break;
}
default:
next = (MVKVkAPIStructHeader*)next->pNext;
break;
}
}
@ -942,20 +939,21 @@ MVKImageView::MVKImageView(MVKDevice* device,
// Remember the subresource range, and determine the actual number of mip levels and texture slices
_subresourceRange = pCreateInfo->subresourceRange;
if (_subresourceRange.levelCount == VK_REMAINING_MIP_LEVELS) {
_subresourceRange.levelCount = _image ? (_image->getMipLevelCount() - _subresourceRange.baseMipLevel) : 1;
_subresourceRange.levelCount = _image->getMipLevelCount() - _subresourceRange.baseMipLevel;
}
if (_subresourceRange.layerCount == VK_REMAINING_ARRAY_LAYERS) {
_subresourceRange.layerCount = _image ? (_image->getLayerCount() - _subresourceRange.baseArrayLayer) : 1;
_subresourceRange.layerCount = _image->getLayerCount() - _subresourceRange.baseArrayLayer;
}
bool useSwizzle;
bool isMultisample = _image ? _image->getSampleCount() != VK_SAMPLE_COUNT_1_BIT : false;
_mtlTexture = nil;
_mtlPixelFormat = getSwizzledMTLPixelFormat(pCreateInfo->format, pCreateInfo->components, useSwizzle,
(_device ? _device->_pMVKConfig : pAltMVKConfig));
bool useSwizzle;
setConfigurationResult(validateSwizzledMTLPixelFormat(pCreateInfo, getPixelFormats(), this,
_device->_pMetalFeatures->nativeTextureSwizzle,
_device->_pMVKConfig->fullImageViewSwizzle,
_mtlPixelFormat, useSwizzle));
_packedSwizzle = useSwizzle ? mvkPackSwizzle(pCreateInfo->components) : 0;
_mtlTextureType = mvkMTLTextureTypeFromVkImageViewType(pCreateInfo->viewType, isMultisample);
_mtlTextureType = mvkMTLTextureTypeFromVkImageViewType(pCreateInfo->viewType,
_image->getSampleCount() != VK_SAMPLE_COUNT_1_BIT);
initMTLTextureViewSupport();
}
@ -982,71 +980,74 @@ void MVKImageView::validateImageViewConfig(const VkImageViewCreateInfo* pCreateI
}
}
// Returns a MTLPixelFormat, based on the MTLPixelFormat converted from the VkFormat, but possibly
// modified by the swizzles defined in the VkComponentMapping of the VkImageViewCreateInfo.
// Metal prior to version 3.0 does not support general per-texture swizzles, so if the swizzle is not an
// identity swizzle, this function attempts to find an alternate MTLPixelFormat that coincidentally
// matches the swizzled format.
// If a replacement MTLFormat was found, it is returned and useSwizzle is set to false.
// If a replacement MTLFormat could not be found, the original MTLPixelFormat is returned, and the
// useSwizzle is set to true, indicating that either native or shader swizzling should be used for
// this image view.
// The config is used to test whether full shader swizzle support is available, and to report an error if not.
MTLPixelFormat MVKImageView::getSwizzledMTLPixelFormat(VkFormat format,
VkComponentMapping components,
bool& useSwizzle,
const MVKConfiguration* pMVKConfig) {
// Attempt to find a valid format transformation swizzle first.
MTLPixelFormat mtlPF = getPixelFormats()->getMTLPixelFormatFromVkFormat(format);
VkResult MVKImageView::validateSwizzledMTLPixelFormat(const VkImageViewCreateInfo* pCreateInfo,
MVKPixelFormats* mvkPixFmts,
MVKVulkanAPIObject* apiObject,
bool hasNativeSwizzleSupport,
bool hasShaderSwizzleSupport,
MTLPixelFormat& mtlPixFmt,
bool& useSwizzle) {
useSwizzle = false;
mtlPixFmt = mvkPixFmts->getMTLPixelFormatFromVkFormat(pCreateInfo->format);
VkComponentMapping components = pCreateInfo->components;
#define SWIZZLE_MATCHES(R, G, B, A) mvkVkComponentMappingsMatch(components, {VK_COMPONENT_SWIZZLE_ ##R, VK_COMPONENT_SWIZZLE_ ##G, VK_COMPONENT_SWIZZLE_ ##B, VK_COMPONENT_SWIZZLE_ ##A} )
#define VK_COMPONENT_SWIZZLE_ANY VK_COMPONENT_SWIZZLE_MAX_ENUM
switch (mtlPF) {
// If we have an identity swizzle, we're all good.
if (SWIZZLE_MATCHES(R, G, B, A)) {
return VK_SUCCESS;
}
switch (mtlPixFmt) {
case MTLPixelFormatR8Unorm:
if (SWIZZLE_MATCHES(ZERO, ANY, ANY, R)) {
return MTLPixelFormatA8Unorm;
mtlPixFmt = MTLPixelFormatA8Unorm;
return VK_SUCCESS;
}
break;
case MTLPixelFormatA8Unorm:
if (SWIZZLE_MATCHES(A, ANY, ANY, ZERO)) {
return MTLPixelFormatR8Unorm;
mtlPixFmt = MTLPixelFormatR8Unorm;
return VK_SUCCESS;
}
break;
case MTLPixelFormatRGBA8Unorm:
if (SWIZZLE_MATCHES(B, G, R, A)) {
return MTLPixelFormatBGRA8Unorm;
mtlPixFmt = MTLPixelFormatBGRA8Unorm;
return VK_SUCCESS;
}
break;
case MTLPixelFormatRGBA8Unorm_sRGB:
if (SWIZZLE_MATCHES(B, G, R, A)) {
return MTLPixelFormatBGRA8Unorm_sRGB;
mtlPixFmt = MTLPixelFormatBGRA8Unorm_sRGB;
return VK_SUCCESS;
}
break;
case MTLPixelFormatBGRA8Unorm:
if (SWIZZLE_MATCHES(B, G, R, A)) {
return MTLPixelFormatRGBA8Unorm;
mtlPixFmt = MTLPixelFormatRGBA8Unorm;
return VK_SUCCESS;
}
break;
case MTLPixelFormatBGRA8Unorm_sRGB:
if (SWIZZLE_MATCHES(B, G, R, A)) {
return MTLPixelFormatRGBA8Unorm_sRGB;
mtlPixFmt = MTLPixelFormatRGBA8Unorm_sRGB;
return VK_SUCCESS;
}
break;
case MTLPixelFormatDepth32Float_Stencil8:
// If aspect mask looking only for stencil then change to stencil-only format even if shader swizzling is needed
if (_subresourceRange.aspectMask == VK_IMAGE_ASPECT_STENCIL_BIT) {
mtlPF = MTLPixelFormatX32_Stencil8;
if (pCreateInfo->subresourceRange.aspectMask == VK_IMAGE_ASPECT_STENCIL_BIT) {
mtlPixFmt = MTLPixelFormatX32_Stencil8;
if (SWIZZLE_MATCHES(R, ANY, ANY, ANY)) {
return mtlPF;
return VK_SUCCESS;
}
}
break;
@ -1054,10 +1055,10 @@ MTLPixelFormat MVKImageView::getSwizzledMTLPixelFormat(VkFormat format,
#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 (_subresourceRange.aspectMask == VK_IMAGE_ASPECT_STENCIL_BIT) {
mtlPF = MTLPixelFormatX24_Stencil8;
if (pCreateInfo->subresourceRange.aspectMask == VK_IMAGE_ASPECT_STENCIL_BIT) {
mtlPixFmt = MTLPixelFormatX24_Stencil8;
if (SWIZZLE_MATCHES(R, ANY, ANY, ANY)) {
return mtlPF;
return VK_SUCCESS;
}
}
break;
@ -1067,22 +1068,20 @@ MTLPixelFormat MVKImageView::getSwizzledMTLPixelFormat(VkFormat format,
break;
}
// No format transformation swizzles were found, so unless we have an identity swizzle, we'll need to use shader swizzling.
if ( !SWIZZLE_MATCHES(R, G, B, A)) {
useSwizzle = true;
if ( !pMVKConfig->fullImageViewSwizzle && !getDevice()->_pMetalFeatures->nativeTextureSwizzle ) {
const char* vkCmd = _image ? "vkCreateImageView(VkImageViewCreateInfo" : "vkGetPhysicalDeviceImageFormatProperties2KHR(VkPhysicalDeviceImageViewSupportEXTX";
const char* errMsg = ("The value of %s::components) (%s, %s, %s, %s), when applied to a VkImageView, requires full component swizzling to be enabled both at the"
" time when the VkImageView is created and at the time any pipeline that uses that VkImageView is compiled. Full component swizzling can"
" be enabled via the MVKConfiguration::fullImageViewSwizzle config parameter or MVK_CONFIG_FULL_IMAGE_VIEW_SWIZZLE environment variable.");
setConfigurationResult(reportError(VK_ERROR_FEATURE_NOT_PRESENT, errMsg, vkCmd,
mvkVkComponentSwizzleName(components.r), mvkVkComponentSwizzleName(components.g),
mvkVkComponentSwizzleName(components.b), mvkVkComponentSwizzleName(components.a)));
}
// No format transformation swizzles were found, so we'll need to use either native or shader swizzling.
useSwizzle = true;
if (hasNativeSwizzleSupport || hasShaderSwizzleSupport ) {
return VK_SUCCESS;
}
return mtlPF;
// Oh, oh. Neither native or shader swizzling is supported.
return apiObject->reportError(VK_ERROR_FEATURE_NOT_PRESENT,
"The value of %s::components) (%s, %s, %s, %s), when applied to a VkImageView, requires full component swizzling to be enabled both at the"
" time when the VkImageView is created and at the time any pipeline that uses that VkImageView is compiled. Full component swizzling can"
" be enabled via the MVKConfiguration::fullImageViewSwizzle config parameter or MVK_CONFIG_FULL_IMAGE_VIEW_SWIZZLE environment variable.",
pCreateInfo->image ? "vkCreateImageView(VkImageViewCreateInfo" : "vkGetPhysicalDeviceImageFormatProperties2KHR(VkPhysicalDeviceImageViewSupportEXTX",
mvkVkComponentSwizzleName(components.r), mvkVkComponentSwizzleName(components.g),
mvkVkComponentSwizzleName(components.b), mvkVkComponentSwizzleName(components.a));
}
// Determine whether this image view should use a Metal texture view,
@ -1104,7 +1103,7 @@ void MVKImageView::initMTLTextureViewSupport() {
((_mtlTextureType == MTLTextureType2D || _mtlTextureType == MTLTextureType2DArray) && is3D)) &&
_subresourceRange.levelCount == _image->_mipLevels &&
(is3D || _subresourceRange.layerCount == _image->_arrayLayers) &&
(!getDevice()->_pMetalFeatures->nativeTextureSwizzle || !_packedSwizzle)) {
(!_device->_pMetalFeatures->nativeTextureSwizzle || !_packedSwizzle)) {
_useMTLTextureView = false;
}
}

View File

@ -75,14 +75,6 @@ MTLTessellationPartitionMode mvkMTLTessellationPartitionModeFromSpvExecutionMode
#pragma mark -
#pragma mark Image properties
#pragma mark Texture formats
/**
* Returns info about the default pixel formats supported by the platform,
* without taking into consideration support at the MTLDevice level.
*/
MVKPixelFormats* mvkPlatformPixelFormats();
/** Returns whether 1D textures should be treated as Metal 2D textures with height 1. */
bool mvkTreatTexture1DAs2D();

View File

@ -34,8 +34,6 @@ using namespace std;
static MVKPixelFormats _platformPixelFormats;
MVKPixelFormats* mvkPlatformPixelFormats() { return &_platformPixelFormats; }
MVK_PUBLIC_SYMBOL bool mvkVkFormatIsSupported(VkFormat vkFormat) {
return _platformPixelFormats.vkFormatIsSupported(vkFormat);
}