From 53a2223abdc87a10050abcef6b978e8512a25129 Mon Sep 17 00:00:00 2001 From: Bill Hollings Date: Tue, 6 Jul 2021 17:57:47 -0400 Subject: [PATCH] Fix swizzle of depth and stencil values into RGBA (float4) variable in shaders. MVKImageViewPlane track Vulkan component swizzle instead of just packed swizzle, and modify swizzle when using depth or stencil sampling or reading. --- Docs/Whats_New.md | 1 + MoltenVK/MoltenVK/GPUObjects/MVKImage.h | 17 +++++--- MoltenVK/MoltenVK/GPUObjects/MVKImage.mm | 54 +++++++++++++++--------- 3 files changed, 45 insertions(+), 27 deletions(-) diff --git a/Docs/Whats_New.md b/Docs/Whats_New.md index e7daeb84..6a1c7496 100644 --- a/Docs/Whats_New.md +++ b/Docs/Whats_New.md @@ -19,6 +19,7 @@ MoltenVK 1.1.5 Released TBD - Fix incorrect translation of clear color values on Apple Silicon. +- Fix swizzle of depth and stencil values into RGBA (`float4`) variable in shaders. diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKImage.h b/MoltenVK/MoltenVK/GPUObjects/MVKImage.h index 95534996..a5ba5b88 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKImage.h +++ b/MoltenVK/MoltenVK/GPUObjects/MVKImage.h @@ -531,6 +531,9 @@ public: void releaseMTLTexture(); + /** Returns the packed component swizzle of this image view. */ + uint32_t getPackedSwizzle() { return _useSwizzle ? mvkPackSwizzle(_componentSwizzle) : 0; } + ~MVKImageViewPlane(); protected: @@ -541,10 +544,10 @@ protected: friend MVKImageView; MVKImageView* _imageView; - uint8_t _planeIndex; + id _mtlTexture; + VkComponentMapping _componentSwizzle; MTLPixelFormat _mtlPixFmt; - uint32_t _packedSwizzle; - id _mtlTexture; + uint8_t _planeIndex; bool _useMTLTextureView; bool _useSwizzle; }; @@ -570,16 +573,16 @@ public: id getMTLTexture(uint8_t planeIndex) { return _planes[planeIndex]->getMTLTexture(); } /** Returns the Metal pixel format of this image view. */ - inline MTLPixelFormat getMTLPixelFormat(uint8_t planeIndex) { return _planes[planeIndex]->_mtlPixFmt; } + MTLPixelFormat getMTLPixelFormat(uint8_t planeIndex) { return _planes[planeIndex]->_mtlPixFmt; } /** Returns the packed component swizzle of this image view. */ - inline uint32_t getPackedSwizzle() { return _planes[0]->_packedSwizzle; } + uint32_t getPackedSwizzle() { return _planes[0]->getPackedSwizzle(); } /** Returns the number of planes of this image view. */ - inline uint8_t getPlaneCount() { return _planes.size(); } + uint8_t getPlaneCount() { return _planes.size(); } /** Returns the Metal texture type of this image view. */ - inline MTLTextureType getMTLTextureType() { return _mtlTextureType; } + MTLTextureType getMTLTextureType() { return _mtlTextureType; } /** * Populates the texture of the specified render pass descriptor diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm b/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm index 18a74e22..b0e4e03b 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm @@ -1448,12 +1448,12 @@ id MVKImageViewPlane::newMTLTexture() { sliceRange = NSMakeRange(0, 1); } id mtlTex = _imageView->_image->getMTLTexture(_planeIndex); - if (_device->_pMetalFeatures->nativeTextureSwizzle && _packedSwizzle) { + if (_device->_pMetalFeatures->nativeTextureSwizzle && _useSwizzle) { return [mtlTex newTextureViewWithPixelFormat: _mtlPixFmt textureType: mtlTextureType levels: NSMakeRange(_imageView->_subresourceRange.baseMipLevel, _imageView->_subresourceRange.levelCount) slices: sliceRange - swizzle: mvkMTLTextureSwizzleChannelsFromVkComponentMapping(mvkUnpackSwizzle(_packedSwizzle))]; // retained + swizzle: mvkMTLTextureSwizzleChannelsFromVkComponentMapping(_componentSwizzle)]; // retained } else { return [mtlTex newTextureViewWithPixelFormat: _mtlPixFmt textureType: mtlTextureType @@ -1475,7 +1475,6 @@ MVKImageViewPlane::MVKImageViewPlane(MVKImageView* imageView, _mtlTexture = nil; getVulkanAPIObject()->setConfigurationResult(initSwizzledMTLPixelFormat(pCreateInfo)); - _packedSwizzle = _useSwizzle ? mvkPackSwizzle(pCreateInfo->components) : 0; // Determine whether this image view should use a Metal texture view, // and set the _useMTLTextureView variable appropriately. @@ -1488,7 +1487,7 @@ MVKImageViewPlane::MVKImageViewPlane(MVKImageView* imageView, ((_imageView->_mtlTextureType == MTLTextureType2D || _imageView->_mtlTextureType == MTLTextureType2DArray) && is3D)) && _imageView->_subresourceRange.levelCount == _imageView->_image->_mipLevels && (is3D || _imageView->_subresourceRange.layerCount == _imageView->_image->_arrayLayers) && - (!_device->_pMetalFeatures->nativeTextureSwizzle || !_packedSwizzle)) { + (!_device->_pMetalFeatures->nativeTextureSwizzle || !_useSwizzle)) { _useMTLTextureView = false; } } else { @@ -1499,24 +1498,39 @@ MVKImageViewPlane::MVKImageViewPlane(MVKImageView* imageView, VkResult MVKImageViewPlane::initSwizzledMTLPixelFormat(const VkImageViewCreateInfo* pCreateInfo) { _useSwizzle = false; - VkComponentMapping components = pCreateInfo->components; + _componentSwizzle = 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} ) + VkImageAspectFlags aspectMask = pCreateInfo->subresourceRange.aspectMask; + bool supportsSwizzling = _device->_pMetalFeatures->nativeTextureSwizzle || mvkConfig().fullImageViewSwizzle; + +#define SWIZZLE_MATCHES(R, G, B, A) mvkVkComponentMappingsMatch(_componentSwizzle, {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 // 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 && - mvkIsAnyFlagEnabled(_imageView->_usage, (VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT))) { - if (_mtlPixFmt == MTLPixelFormatDepth32Float_Stencil8) { - _mtlPixFmt = MTLPixelFormatX32_Stencil8; - } + + // Identity swizzles of depth/stencil formats can require special handling. + if (mvkIsAnyFlagEnabled(_imageView->_usage, (VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT))) { + + // If only stencil aspect is requested, possibly change to stencil-only format. + if (aspectMask == VK_IMAGE_ASPECT_STENCIL_BIT) { + if (_mtlPixFmt == MTLPixelFormatDepth32Float_Stencil8) { + _mtlPixFmt = MTLPixelFormatX32_Stencil8; + } #if MVK_MACOS - else if (_mtlPixFmt == MTLPixelFormatDepth24Unorm_Stencil8) { - _mtlPixFmt = MTLPixelFormatX24_Stencil8; - } + else if (_mtlPixFmt == MTLPixelFormatDepth24Unorm_Stencil8) { + _mtlPixFmt = MTLPixelFormatX24_Stencil8; + } #endif + } + + // When reading or sampling into a vec4 color, Vulkan expects the depth or stencil value in only the red component. + // Metal can be inconsistent, but on most platforms populates all components with the depth or stencil value. + // If swizzling is available, we can compensate for this by forcing the appropriate swizzle. + if (supportsSwizzling && mvkIsAnyFlagEnabled(aspectMask, VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) { + _componentSwizzle = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_ZERO, VK_COMPONENT_SWIZZLE_ZERO, VK_COMPONENT_SWIZZLE_ONE }; + _useSwizzle = true; + } } return VK_SUCCESS; @@ -1625,7 +1639,7 @@ VkResult MVKImageViewPlane::initSwizzledMTLPixelFormat(const VkImageViewCreateIn 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 (aspectMask == VK_IMAGE_ASPECT_STENCIL_BIT && mvkIsAnyFlagEnabled(_imageView->_usage, (VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT))) { _mtlPixFmt = MTLPixelFormatX32_Stencil8; if (SWIZZLE_MATCHES(R, ANY, ANY, ANY)) { @@ -1637,7 +1651,7 @@ VkResult MVKImageViewPlane::initSwizzledMTLPixelFormat(const VkImageViewCreateIn #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 (aspectMask == VK_IMAGE_ASPECT_STENCIL_BIT && mvkIsAnyFlagEnabled(_imageView->_usage, (VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT))) { _mtlPixFmt = MTLPixelFormatX24_Stencil8; if (SWIZZLE_MATCHES(R, ANY, ANY, ANY)) { @@ -1652,14 +1666,14 @@ VkResult MVKImageViewPlane::initSwizzledMTLPixelFormat(const VkImageViewCreateIn } // No format transformation swizzles were found, so we'll need to use either native or shader swizzling, if it is supported. - if ( !(_device->_pMetalFeatures->nativeTextureSwizzle || mvkConfig().fullImageViewSwizzle) ) { + if ( !supportsSwizzling ) { return getVulkanAPIObject()->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)); + mvkVkComponentSwizzleName(_componentSwizzle.r), mvkVkComponentSwizzleName(_componentSwizzle.g), + mvkVkComponentSwizzleName(_componentSwizzle.b), mvkVkComponentSwizzleName(_componentSwizzle.a)); } _useSwizzle = true;