From 5d173d0e093eed2b42994332ab2c735ff0c8eb8a Mon Sep 17 00:00:00 2001 From: Chip Davis Date: Wed, 7 Jun 2023 12:58:28 -0700 Subject: [PATCH] Support the `VK_EXT_4444_formats` extension. This turned out to be a little bit more involved than I had hoped. But, with this, we can now use the `VK_FORMAT_A4R4G4B4_UNORM_PACK16` and `VK_FORMAT_A4B4G4R4_UNORM_PACK16` formats from shaders, use them as blit sources, and even clear them. Storage images and render targets of these formats aren't supported, however. To support the latter would require the insertion of a swizzle into the fragment shader before returning. The former cannot be reasonably supported. --- Docs/MoltenVK_Runtime_UserGuide.md | 1 + Docs/Whats_New.md | 1 + MoltenVK/MoltenVK/API/mvk_datatypes.h | 9 ++ MoltenVK/MoltenVK/Commands/MVKCmdTransfer.mm | 20 +++- .../Commands/MVKCommandResourceFactory.h | 48 ++++++-- .../Commands/MVKCommandResourceFactory.mm | 29 ++++- MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm | 9 ++ .../GPUObjects/MVKDeviceFeatureStructs.def | 1 + MoltenVK/MoltenVK/GPUObjects/MVKImage.h | 3 + MoltenVK/MoltenVK/GPUObjects/MVKImage.mm | 38 ++++++ .../MoltenVK/GPUObjects/MVKPixelFormats.h | 23 ++++ .../MoltenVK/GPUObjects/MVKPixelFormats.mm | 111 +++++++++++++++--- MoltenVK/MoltenVK/Layers/MVKExtensions.def | 1 + MoltenVK/MoltenVK/Vulkan/mvk_datatypes.mm | 39 ++++++ 14 files changed, 302 insertions(+), 31 deletions(-) diff --git a/Docs/MoltenVK_Runtime_UserGuide.md b/Docs/MoltenVK_Runtime_UserGuide.md index 0148448b..3775a749 100644 --- a/Docs/MoltenVK_Runtime_UserGuide.md +++ b/Docs/MoltenVK_Runtime_UserGuide.md @@ -350,6 +350,7 @@ In addition to core *Vulkan* functionality, **MoltenVK** also supports the foll - `VK_KHR_timeline_semaphore` - `VK_KHR_uniform_buffer_standard_layout` - `VK_KHR_variable_pointers` +- `VK_EXT_4444_formats` *(requires 16-bit formats and either native texture swizzling or manual swizzling to be enabled)* - `VK_EXT_buffer_device_address` *(requires GPU Tier 2 argument buffers support)* - `VK_EXT_debug_marker` - `VK_EXT_debug_report` diff --git a/Docs/Whats_New.md b/Docs/Whats_New.md index e96a4d08..1a1284e0 100644 --- a/Docs/Whats_New.md +++ b/Docs/Whats_New.md @@ -19,6 +19,7 @@ MoltenVK 1.2.5 Released TBD - Add support for extensions: + - `VK_EXT_4444_formats` - `VK_EXT_shader_demote_to_helper_invocation` - Ensure non-dispatch compute commands don't interfere with compute encoding state used by dispatch commands. - Support `VK_PRESENT_MODE_IMMEDIATE_KHR` if `VkPresentTimeGOOGLE::desiredPresentTime` is zero. diff --git a/MoltenVK/MoltenVK/API/mvk_datatypes.h b/MoltenVK/MoltenVK/API/mvk_datatypes.h index c5c2e182..f5c103b4 100644 --- a/MoltenVK/MoltenVK/API/mvk_datatypes.h +++ b/MoltenVK/MoltenVK/API/mvk_datatypes.h @@ -227,6 +227,15 @@ MTLTextureSwizzle mvkMTLTextureSwizzleFromVkComponentSwizzle(VkComponentSwizzle /** Returns all four Metal texture swizzles from the Vulkan component mapping. */ MTLTextureSwizzleChannels mvkMTLTextureSwizzleChannelsFromVkComponentMapping(VkComponentMapping vkMapping); +/** Maps a clear color according to the specified VkComponentSwizzle. */ +float mvkVkClearColorFloatValueFromVkComponentSwizzle(float *colors, uint32_t index, VkComponentSwizzle vkSwizzle); + +/** Maps a clear color according to the specified VkComponentSwizzle. */ +uint32_t mvkVkClearColorUIntValueFromVkComponentSwizzle(uint32_t *colors, uint32_t index, VkComponentSwizzle vkSwizzle); + +/** Maps a clear color according to the specified VkComponentSwizzle. */ +int32_t mvkVkClearColorIntValueFromVkComponentSwizzle(int32_t *colors, uint32_t index, VkComponentSwizzle vkSwizzle); + #pragma mark Mipmaps diff --git a/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.mm b/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.mm index 816dbc3f..2c0ef546 100644 --- a/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.mm +++ b/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.mm @@ -374,6 +374,7 @@ bool MVKCmdBlitImage::canCopyFormats(const VkImageBlit2& region) { uint8_t srcPlaneIndex = MVKImage::getPlaneFromVkImageAspectFlags(region.srcSubresource.aspectMask); uint8_t dstPlaneIndex = MVKImage::getPlaneFromVkImageAspectFlags(region.dstSubresource.aspectMask); return ((_srcImage->getMTLPixelFormat(srcPlaneIndex) == _dstImage->getMTLPixelFormat(dstPlaneIndex)) && + !_srcImage->needsSwizzle() && (_dstImage->getSampleCount() == _srcImage->getSampleCount())); } @@ -456,7 +457,7 @@ void MVKCmdBlitImage::encode(MVKCommandEncoder* cmdEncoder, MVKCommandUse com uint32_t copyCnt = 0; uint32_t blitCnt = 0; - // Separate BLITs into those that are really just simple texure region copies, + // Separate BLITs into those that are really just simple texture region copies, // and those that require rendering for (auto& vkIB : _vkImageBlits) { if (canCopyFormats(vkIB) && canCopy(vkIB)) { @@ -500,6 +501,15 @@ void MVKCmdBlitImage::encode(MVKCommandEncoder* cmdEncoder, MVKCommandUse com id srcMTLTex = _srcImage->getMTLTexture(srcPlaneIndex); id dstMTLTex = _dstImage->getMTLTexture(dstPlaneIndex); if (blitCnt && srcMTLTex && dstMTLTex) { + if (cmdEncoder->getDevice()->_pMetalFeatures->nativeTextureSwizzle && + _srcImage->needsSwizzle()) { + // Use a view that has a swizzle on it. + srcMTLTex = [[srcMTLTex newTextureViewWithPixelFormat:srcMTLTex.pixelFormat + textureType:srcMTLTex.textureType + levels:NSMakeRange(0, srcMTLTex.mipmapLevelCount) + slices:NSMakeRange(0, srcMTLTex.arrayLength) + swizzle:_srcImage->getPixelFormats()->getMTLTextureSwizzleChannels(_srcImage->getVkFormat())] autorelease]; + } cmdEncoder->endCurrentMetalEncoding(); MTLRenderPassDescriptor* mtlRPD = [MTLRenderPassDescriptor renderPassDescriptor]; @@ -549,6 +559,14 @@ void MVKCmdBlitImage::encode(MVKCommandEncoder* cmdEncoder, MVKCommandUse com blitKey.srcFilter = mvkMTLSamplerMinMagFilterFromVkFilter(_filter); blitKey.srcAspect = mvkIBR.region.srcSubresource.aspectMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT); blitKey.dstSampleCount = mvkSampleCountFromVkSampleCountFlagBits(_dstImage->getSampleCount()); + if (!cmdEncoder->getDevice()->_pMetalFeatures->nativeTextureSwizzle && + _srcImage->needsSwizzle()) { + VkComponentMapping vkMapping = _srcImage->getPixelFormats()->getVkComponentMapping(_srcImage->getVkFormat()); + blitKey.srcSwizzleR = vkMapping.r; + blitKey.srcSwizzleG = vkMapping.g; + blitKey.srcSwizzleB = vkMapping.b; + blitKey.srcSwizzleA = vkMapping.a; + } id mtlRPS = cmdEncoder->getCommandEncodingPool()->getCmdBlitImageMTLRenderPipelineState(blitKey); bool isBlittingDepth = mvkIsAnyFlagEnabled(blitKey.srcAspect, (VK_IMAGE_ASPECT_DEPTH_BIT)); bool isBlittingStencil = mvkIsAnyFlagEnabled(blitKey.srcAspect, (VK_IMAGE_ASPECT_STENCIL_BIT)); diff --git a/MoltenVK/MoltenVK/Commands/MVKCommandResourceFactory.h b/MoltenVK/MoltenVK/Commands/MVKCommandResourceFactory.h index 1e37844f..b7f679c7 100644 --- a/MoltenVK/MoltenVK/Commands/MVKCommandResourceFactory.h +++ b/MoltenVK/MoltenVK/Commands/MVKCommandResourceFactory.h @@ -37,20 +37,31 @@ class MVKQueryPool; * This structure can be used as a key in a std::map and std::unordered_map. */ typedef struct MVKRPSKeyBlitImg { - uint16_t srcMTLPixelFormat = 0; /**< as MTLPixelFormat */ - uint16_t dstMTLPixelFormat = 0; /**< as MTLPixelFormat */ - uint8_t srcMTLTextureType = 0; /**< as MTLTextureType */ + uint16_t srcMTLPixelFormat : 12; /**< as MTLPixelFormat */ + uint16_t dstMTLPixelFormat : 12; /**< as MTLPixelFormat */ + uint8_t srcMTLTextureType : 4; /**< as MTLTextureType */ + uint8_t srcFilter : 4; /**< as MTLSamplerMinMagFilter */ uint8_t srcAspect = 0; /**< as VkImageAspectFlags */ - uint8_t srcFilter = 0; /**< as MTLSamplerMinMagFilter */ uint8_t dstSampleCount = 0; + uint8_t srcSwizzleR : 4; /**< as VkComponentSwizzle */ + uint8_t srcSwizzleG : 4; /**< as VkComponentSwizzle */ + uint8_t srcSwizzleB : 4; /**< as VkComponentSwizzle */ + uint8_t srcSwizzleA : 4; /**< as VkComponentSwizzle */ + + MVKRPSKeyBlitImg() : srcMTLPixelFormat(0), dstMTLPixelFormat(0), srcMTLTextureType(0), srcFilter(0), + srcSwizzleR(0), srcSwizzleG(0), srcSwizzleB(0), srcSwizzleA(0) {} bool operator==(const MVKRPSKeyBlitImg& rhs) const { if (srcMTLPixelFormat != rhs.srcMTLPixelFormat) { return false; } if (dstMTLPixelFormat != rhs.dstMTLPixelFormat) { return false; } if (srcMTLTextureType != rhs.srcMTLTextureType) { return false; } - if (srcAspect != rhs.srcAspect) { return false; } if (srcFilter != rhs.srcFilter) { return false; } + if (srcAspect != rhs.srcAspect) { return false; } if (dstSampleCount != rhs.dstSampleCount) { return false; } + if (srcSwizzleR != rhs.srcSwizzleR) { return false; } + if (srcSwizzleG != rhs.srcSwizzleG) { return false; } + if (srcSwizzleB != rhs.srcSwizzleB) { return false; } + if (srcSwizzleA != rhs.srcSwizzleA) { return false; } return true; } @@ -70,23 +81,40 @@ typedef struct MVKRPSKeyBlitImg { srcMTLTextureType == MTLTextureType1DArray); } + VkComponentMapping getSrcSwizzle() { + return { (VkComponentSwizzle)srcSwizzleR, (VkComponentSwizzle)srcSwizzleG, + (VkComponentSwizzle)srcSwizzleB, (VkComponentSwizzle)srcSwizzleA }; + } + std::size_t hash() const { std::size_t hash = srcMTLPixelFormat; - hash <<= 16; + hash <<= 12; hash |= dstMTLPixelFormat; - hash <<= 8; + hash <<= 4; hash |= srcMTLTextureType; + hash <<= 4; + hash |= srcFilter; + hash <<= 8; hash |= srcAspect; - hash <<= 8; - hash |= srcFilter; - hash <<= 8; hash |= dstSampleCount; + + hash <<= 4; + hash |= srcSwizzleR; + + hash <<= 4; + hash |= srcSwizzleG; + + hash <<= 4; + hash |= srcSwizzleB; + + hash <<= 4; + hash |= srcSwizzleA; return hash; } diff --git a/MoltenVK/MoltenVK/Commands/MVKCommandResourceFactory.mm b/MoltenVK/MoltenVK/Commands/MVKCommandResourceFactory.mm index 36fb03b7..4e1ed93c 100644 --- a/MoltenVK/MoltenVK/Commands/MVKCommandResourceFactory.mm +++ b/MoltenVK/MoltenVK/Commands/MVKCommandResourceFactory.mm @@ -167,6 +167,25 @@ id MVKCommandResourceFactory::newCmdClearMTLRenderPipeli return rps; } +static char getSwizzleChar(char defaultChar, VkComponentSwizzle vkSwizzle) { + switch (vkSwizzle) { + case VK_COMPONENT_SWIZZLE_IDENTITY: return defaultChar; + // FIXME: 0 and 1 (currently not used in any default swizzles) + case VK_COMPONENT_SWIZZLE_R: return 'x'; + case VK_COMPONENT_SWIZZLE_G: return 'y'; + case VK_COMPONENT_SWIZZLE_B: return 'z'; + case VK_COMPONENT_SWIZZLE_A: return 'w'; + default: return defaultChar; + } +} + +static void getSwizzleString(char swizzleStr[4], VkComponentMapping vkMapping) { + swizzleStr[0] = getSwizzleChar('x', vkMapping.r); + swizzleStr[1] = getSwizzleChar('y', vkMapping.g); + swizzleStr[2] = getSwizzleChar('z', vkMapping.b); + swizzleStr[3] = getSwizzleChar('w', vkMapping.a); +} + id MVKCommandResourceFactory::newBlitFragFunction(MVKRPSKeyBlitImg& blitKey) { @autoreleasepool { bool isLayeredBlit = blitKey.dstSampleCount > 1 ? _device->_pMetalFeatures->multisampleLayeredRendering : _device->_pMetalFeatures->layeredRendering; @@ -177,6 +196,7 @@ id MVKCommandResourceFactory::newBlitFragFunction(MVKRPSKeyBlitImg& NSString* typePrefix = @"texture"; NSString* typeSuffix; NSString* coordArg; + char swizzleArg[4] = { 'x', 'y', 'z', 'w' }; if (mvkIsAnyFlagEnabled(blitKey.srcAspect, (VK_IMAGE_ASPECT_DEPTH_BIT))) { typePrefix = @"depth"; } @@ -208,6 +228,9 @@ id MVKCommandResourceFactory::newBlitFragFunction(MVKRPSKeyBlitImg& } NSString* sliceArg = isArrayType ? (isLayeredBlit ? @", subRez.slice + varyings.v_layer" : @", subRez.slice") : @""; NSString* srcFilter = isLinearFilter ? @"linear" : @"nearest"; + if (!getDevice()->_pMetalFeatures->nativeTextureSwizzle) { + getSwizzleString(swizzleArg, blitKey.getSrcSwizzle()); + } NSMutableString* msl = [NSMutableString stringWithCapacity: (2 * KIBI) ]; [msl appendLineMVK: @"#include "]; @@ -263,15 +286,15 @@ id MVKCommandResourceFactory::newBlitFragFunction(MVKRPSKeyBlitImg& [msl appendLineMVK: @" constant TexSubrez& subRez [[buffer(0)]]) {"]; [msl appendLineMVK: @" FragmentOutputs out;"]; if (mvkIsAnyFlagEnabled(blitKey.srcAspect, (VK_IMAGE_ASPECT_DEPTH_BIT))) { - [msl appendFormat: @" out.depth = tex.sample(ce_sampler, varyings.v_texCoord%@%@, level(subRez.lod));", coordArg, sliceArg]; + [msl appendFormat: @" out.depth = tex.sample(ce_sampler, varyings.v_texCoord%@%@, level(subRez.lod)).%c;", coordArg, sliceArg, swizzleArg[0]]; [msl appendLineMVK]; } if (mvkIsAnyFlagEnabled(blitKey.srcAspect, (VK_IMAGE_ASPECT_STENCIL_BIT))) { - [msl appendFormat: @" out.stencil = stencilTex.sample(ce_stencil_sampler, varyings.v_texCoord%@%@, level(subRez.lod)).x;", coordArg, sliceArg]; + [msl appendFormat: @" out.stencil = stencilTex.sample(ce_stencil_sampler, varyings.v_texCoord%@%@, level(subRez.lod)).%c;", coordArg, sliceArg, swizzleArg[0]]; [msl appendLineMVK]; } if (!mvkIsAnyFlagEnabled(blitKey.srcAspect, (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT))) { - [msl appendFormat: @" out.color = tex.sample(ce_sampler, varyings.v_texCoord%@%@, level(subRez.lod));", coordArg, sliceArg]; + [msl appendFormat: @" out.color = tex.sample(ce_sampler, varyings.v_texCoord%@%@, level(subRez.lod)).%.4s;", coordArg, sliceArg, swizzleArg]; [msl appendLineMVK]; } [msl appendLineMVK: @" return out;"]; diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm index 93adca53..12f1514c 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm @@ -371,6 +371,15 @@ void MVKPhysicalDevice::getFeatures(VkPhysicalDeviceFeatures2* features) { portabilityFeatures->vertexAttributeAccessBeyondStride = true; // Costs additional buffers. Should make configuration switch. break; } + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_4444_FORMATS_FEATURES_EXT: { + auto* formatFeatures = (VkPhysicalDevice4444FormatsFeaturesEXT*)next; + bool canSupport4444 = _metalFeatures.tileBasedDeferredRendering && + (_metalFeatures.nativeTextureSwizzle || + mvkConfig().fullImageViewSwizzle); + formatFeatures->formatA4R4G4B4 = canSupport4444; + formatFeatures->formatA4B4G4R4 = canSupport4444; + break; + } case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADER_INTERLOCK_FEATURES_EXT: { auto* interlockFeatures = (VkPhysicalDeviceFragmentShaderInterlockFeaturesEXT*)next; interlockFeatures->fragmentShaderSampleInterlock = _metalFeatures.rasterOrderGroups; diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDeviceFeatureStructs.def b/MoltenVK/MoltenVK/GPUObjects/MVKDeviceFeatureStructs.def index 0674e4c1..c0bbb481 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKDeviceFeatureStructs.def +++ b/MoltenVK/MoltenVK/GPUObjects/MVKDeviceFeatureStructs.def @@ -62,6 +62,7 @@ MVK_DEVICE_FEATURE(VariablePointer, VARIABLE_POINTER, MVK_DEVICE_FEATURE(VulkanMemoryModel, VULKAN_MEMORY_MODEL, 3) MVK_DEVICE_FEATURE_EXTN(FragmentShaderBarycentric, FRAGMENT_SHADER_BARYCENTRIC, KHR, 1) MVK_DEVICE_FEATURE_EXTN(PortabilitySubset, PORTABILITY_SUBSET, KHR, 15) +MVK_DEVICE_FEATURE_EXTN(4444Formats, 4444_FORMATS, EXT, 2) MVK_DEVICE_FEATURE_EXTN(FragmentShaderInterlock, FRAGMENT_SHADER_INTERLOCK, EXT, 3) MVK_DEVICE_FEATURE_EXTN(PipelineCreationCacheControl, PIPELINE_CREATION_CACHE_CONTROL, EXT, 1) MVK_DEVICE_FEATURE_EXTN(Robustness2, ROBUSTNESS_2, EXT, 3) diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKImage.h b/MoltenVK/MoltenVK/GPUObjects/MVKImage.h index db83c323..572e8f06 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKImage.h +++ b/MoltenVK/MoltenVK/GPUObjects/MVKImage.h @@ -224,6 +224,9 @@ public: /** Returns the number of planes of this image view. */ uint8_t getPlaneCount() { return _planes.size(); } + /** Returns whether or not the image format requires swizzling. */ + bool needsSwizzle() { return getPixelFormats()->needsSwizzle(_vkFormat); } + /** Populates the specified layout for the specified sub-resource. */ VkResult getSubresourceLayout(const VkImageSubresource* pSubresource, VkSubresourceLayout* pLayout); diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm b/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm index cc8a1601..14870221 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm @@ -1575,6 +1575,26 @@ VkResult MVKImageViewPlane::initSwizzledMTLPixelFormat(const VkImageViewCreateIn VkImageAspectFlags aspectMask = pCreateInfo->subresourceRange.aspectMask; #define adjustComponentSwizzleValue(comp, currVal, newVal) if (_componentSwizzle.comp == VK_COMPONENT_SWIZZLE_ ##currVal) { _componentSwizzle.comp = VK_COMPONENT_SWIZZLE_ ##newVal; } +#define adjustAnyComponentSwizzleValue(comp, I, R, G, B, A) \ + switch (_componentSwizzle.comp) { \ + case VK_COMPONENT_SWIZZLE_IDENTITY: \ + _componentSwizzle.comp = VK_COMPONENT_SWIZZLE_##I; \ + break; \ + case VK_COMPONENT_SWIZZLE_R: \ + _componentSwizzle.comp = VK_COMPONENT_SWIZZLE_##R; \ + break; \ + case VK_COMPONENT_SWIZZLE_G: \ + _componentSwizzle.comp = VK_COMPONENT_SWIZZLE_##G; \ + break; \ + case VK_COMPONENT_SWIZZLE_B: \ + _componentSwizzle.comp = VK_COMPONENT_SWIZZLE_##B; \ + break; \ + case VK_COMPONENT_SWIZZLE_A: \ + _componentSwizzle.comp = VK_COMPONENT_SWIZZLE_##A; \ + break; \ + default: \ + break; \ + } // Use swizzle adjustment to bridge some differences between Vulkan and Metal pixel formats. // Do this ahead of other tests and adjustments so that swizzling will be enabled by tests below. @@ -1589,6 +1609,24 @@ VkResult MVKImageViewPlane::initSwizzledMTLPixelFormat(const VkImageViewCreateIn adjustComponentSwizzleValue(a, IDENTITY, ONE); break; + case VK_FORMAT_A4R4G4B4_UNORM_PACK16: + // Metal doesn't (publicly) support this directly, so use a swizzle to get the ordering right. + // n.b. **Do NOT use adjustComponentSwizzleValue if multiple values need substitution, + // and some of the substitutes are keys for other substitutions!** + adjustAnyComponentSwizzleValue(r, G, G, B, A, R); + adjustAnyComponentSwizzleValue(g, B, G, B, A, R); + adjustAnyComponentSwizzleValue(b, A, G, B, A, R); + adjustAnyComponentSwizzleValue(a, R, G, B, A, R); + break; + + case VK_FORMAT_A4B4G4R4_UNORM_PACK16: + // Metal doesn't support this directly, so use a swizzle to get the ordering right. + adjustAnyComponentSwizzleValue(r, A, A, B, G, R); + adjustAnyComponentSwizzleValue(g, B, A, B, G, R); + adjustAnyComponentSwizzleValue(b, G, A, B, G, R); + adjustAnyComponentSwizzleValue(a, R, A, B, G, R); + break; + default: break; } diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.h b/MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.h index 03fc0f98..479965b4 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.h +++ b/MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.h @@ -148,6 +148,7 @@ typedef struct MVKVkFormatDesc { uint32_t bytesPerBlock; MVKFormatType formatType; VkFormatProperties properties; + VkComponentMapping componentMapping; const char* name; bool hasReportedSubstitution; @@ -158,6 +159,13 @@ typedef struct MVKVkFormatDesc { inline bool vertexIsSupported() const { return (mtlVertexFormat != MTLVertexFormatInvalid); }; inline bool vertexIsSupportedOrSubstitutable() const { return vertexIsSupported() || (mtlVertexFormatSubstitute != MTLVertexFormatInvalid); }; + + bool needsSwizzle() const { + return componentMapping.r != VK_COMPONENT_SWIZZLE_IDENTITY || + componentMapping.g != VK_COMPONENT_SWIZZLE_IDENTITY || + componentMapping.b != VK_COMPONENT_SWIZZLE_IDENTITY || + componentMapping.a != VK_COMPONENT_SWIZZLE_IDENTITY; + } } MVKVkFormatDesc; /** Describes the properties of a MTLPixelFormat or MTLVertexFormat. */ @@ -320,6 +328,21 @@ public: */ size_t getBytesPerLayer(MTLPixelFormat mtlFormat, size_t bytesPerRow, uint32_t texelRowsPerLayer); + /** Returns whether or not the specified Vulkan format requires swizzling to use with Metal. */ + bool needsSwizzle(VkFormat vkFormat); + + /** Returns any VkComponentMapping needed to use the specified Vulkan format. */ + VkComponentMapping getVkComponentMapping(VkFormat vkFormat); + + /** + * Returns the inverse of the VkComponentMapping needed to use the specified Vulkan format. + * If the original mapping is not a one-to-one function, the behaviour is undefined. + */ + VkComponentMapping getInverseComponentMapping(VkFormat vkFormat); + + /** Returns any MTLTextureSwizzleChannels needed to use the specified Vulkan format. */ + MTLTextureSwizzleChannels getMTLTextureSwizzleChannels(VkFormat vkFormat); + /** Returns the default properties for the specified Vulkan format. */ VkFormatProperties& getVkFormatProperties(VkFormat vkFormat); diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.mm b/MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.mm index 79f1dffa..3044f66d 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.mm @@ -377,6 +377,44 @@ size_t MVKPixelFormats::getBytesPerLayer(MTLPixelFormat mtlFormat, size_t bytesP return mvkCeilingDivide(texelRowsPerLayer, getVkFormatDesc(mtlFormat).blockTexelSize.height) * bytesPerRow; } +bool MVKPixelFormats::needsSwizzle(VkFormat vkFormat) { + return getVkFormatDesc(vkFormat).needsSwizzle(); +} + +VkComponentMapping MVKPixelFormats::getVkComponentMapping(VkFormat vkFormat) { + return getVkFormatDesc(vkFormat).componentMapping; +} + +VkComponentMapping MVKPixelFormats::getInverseComponentMapping(VkFormat vkFormat) { +#define INVERT_SWIZZLE(x, X, Y) \ + case VK_COMPONENT_SWIZZLE_##X: \ + inverse.x = VK_COMPONENT_SWIZZLE_##Y; \ + break +#define INVERT_MAPPING(y, Y) \ + switch (mapping.y) { \ + case VK_COMPONENT_SWIZZLE_IDENTITY: \ + inverse.y = VK_COMPONENT_SWIZZLE_IDENTITY; \ + break; \ + INVERT_SWIZZLE(r, R, Y); \ + INVERT_SWIZZLE(g, G, Y); \ + INVERT_SWIZZLE(b, B, Y); \ + INVERT_SWIZZLE(a, A, Y); \ + default: break; \ + } + VkComponentMapping mapping = getVkComponentMapping(vkFormat), inverse; + INVERT_MAPPING(r, R) + INVERT_MAPPING(g, G) + INVERT_MAPPING(b, B) + INVERT_MAPPING(a, A) + return inverse; +#undef INVERT_MAPPING +#undef INVERT_SWIZZLE +} + +MTLTextureSwizzleChannels MVKPixelFormats::getMTLTextureSwizzleChannels(VkFormat vkFormat) { + return mvkMTLTextureSwizzleChannelsFromVkComponentMapping(getVkComponentMapping(vkFormat)); +} + VkFormatProperties& MVKPixelFormats::getVkFormatProperties(VkFormat vkFormat) { return getVkFormatDesc(vkFormat).properties; } @@ -464,13 +502,18 @@ MTLVertexFormat MVKPixelFormats::getMTLVertexFormat(VkFormat vkFormat) { MTLClearColor MVKPixelFormats::getMTLClearColor(VkClearValue vkClearValue, VkFormat vkFormat) { MTLClearColor mtlClr; + // The VkComponentMapping (and its MTLTextureSwizzleChannels equivalent) define the *sources* + // for the texture color components for reading. Since we're *writing* to the texture, + // we need to *invert* the mapping. + // n.b. Bad things might happen if the original swizzle isn't one-to-one! + VkComponentMapping inverseMap = getInverseComponentMapping(vkFormat); switch (getFormatType(vkFormat)) { case kMVKFormatColorHalf: - case kMVKFormatColorFloat: - mtlClr.red = vkClearValue.color.float32[0]; - mtlClr.green = vkClearValue.color.float32[1]; - mtlClr.blue = vkClearValue.color.float32[2]; - mtlClr.alpha = vkClearValue.color.float32[3]; + case kMVKFormatColorFloat: { + mtlClr.red = mvkVkClearColorFloatValueFromVkComponentSwizzle(vkClearValue.color.float32, 0, inverseMap.r); + mtlClr.green = mvkVkClearColorFloatValueFromVkComponentSwizzle(vkClearValue.color.float32, 1, inverseMap.g); + mtlClr.blue = mvkVkClearColorFloatValueFromVkComponentSwizzle(vkClearValue.color.float32, 2, inverseMap.b); + mtlClr.alpha = mvkVkClearColorFloatValueFromVkComponentSwizzle(vkClearValue.color.float32, 3, inverseMap.a); if (_physicalDevice && _physicalDevice->getMetalFeatures()->clearColorFloatRounding == MVK_FLOAT_ROUNDING_DOWN) { // For normalized formats, increment the clear value by half the ULP @@ -486,6 +529,8 @@ MTLClearColor MVKPixelFormats::getMTLClearColor(VkClearValue vkClearValue, VkFor #define OFFSET_SNORM(COLOR, BIT_WIDTH) OFFSET_NORM(-1.0, COLOR, BIT_WIDTH - 1) switch (vkFormat) { case VK_FORMAT_R4G4B4A4_UNORM_PACK16: + case VK_FORMAT_A4R4G4B4_UNORM_PACK16: + case VK_FORMAT_A4B4G4R4_UNORM_PACK16: OFFSET_UNORM(red, 4) OFFSET_UNORM(green, 4) OFFSET_UNORM(blue, 4) @@ -571,21 +616,22 @@ MTLClearColor MVKPixelFormats::getMTLClearColor(VkClearValue vkClearValue, VkFor #undef OFFSET_NORM } break; + } case kMVKFormatColorUInt8: case kMVKFormatColorUInt16: case kMVKFormatColorUInt32: - mtlClr.red = vkClearValue.color.uint32[0]; - mtlClr.green = vkClearValue.color.uint32[1]; - mtlClr.blue = vkClearValue.color.uint32[2]; - mtlClr.alpha = vkClearValue.color.uint32[3]; + mtlClr.red = mvkVkClearColorUIntValueFromVkComponentSwizzle(vkClearValue.color.uint32, 0, inverseMap.r); + mtlClr.green = mvkVkClearColorUIntValueFromVkComponentSwizzle(vkClearValue.color.uint32, 1, inverseMap.g); + mtlClr.blue = mvkVkClearColorUIntValueFromVkComponentSwizzle(vkClearValue.color.uint32, 2, inverseMap.b); + mtlClr.alpha = mvkVkClearColorUIntValueFromVkComponentSwizzle(vkClearValue.color.uint32, 3, inverseMap.a); break; case kMVKFormatColorInt8: case kMVKFormatColorInt16: case kMVKFormatColorInt32: - mtlClr.red = vkClearValue.color.int32[0]; - mtlClr.green = vkClearValue.color.int32[1]; - mtlClr.blue = vkClearValue.color.int32[2]; - mtlClr.alpha = vkClearValue.color.int32[3]; + mtlClr.red = mvkVkClearColorIntValueFromVkComponentSwizzle(vkClearValue.color.int32, 0, inverseMap.r); + mtlClr.green = mvkVkClearColorIntValueFromVkComponentSwizzle(vkClearValue.color.int32, 1, inverseMap.g); + mtlClr.blue = mvkVkClearColorIntValueFromVkComponentSwizzle(vkClearValue.color.int32, 2, inverseMap.b); + mtlClr.alpha = mvkVkClearColorIntValueFromVkComponentSwizzle(vkClearValue.color.int32, 3, inverseMap.a); break; default: mtlClr.red = 0.0; @@ -756,16 +802,21 @@ MVKPixelFormats::MVKPixelFormats(MVKPhysicalDevice* physicalDevice) : _physicalD buildVkFormatMaps(); } -#define addVkFormatDescFull(VK_FMT, MTL_FMT, MTL_FMT_ALT, MTL_VTX_FMT, MTL_VTX_FMT_ALT, CSPC, CSCB, BLK_W, BLK_H, BLK_BYTE_CNT, MVK_FMT_TYPE) \ +#define addVkFormatDescFull(VK_FMT, MTL_FMT, MTL_FMT_ALT, MTL_VTX_FMT, MTL_VTX_FMT_ALT, CSPC, CSCB, BLK_W, BLK_H, BLK_BYTE_CNT, MVK_FMT_TYPE, SWIZ_R, SWIZ_G, SWIZ_B, SWIZ_A) \ MVKAssert(fmtIdx < _vkFormatCount, "Attempting to describe %d VkFormats, but only have space for %d. Increase the value of _vkFormatCount", fmtIdx + 1, _vkFormatCount); \ _vkFormatDescriptions[fmtIdx++] = { VK_FORMAT_ ##VK_FMT, MTLPixelFormat ##MTL_FMT, MTLPixelFormat ##MTL_FMT_ALT, MTLVertexFormat ##MTL_VTX_FMT, MTLVertexFormat ##MTL_VTX_FMT_ALT, \ - CSPC, CSCB, { BLK_W, BLK_H }, BLK_BYTE_CNT, kMVKFormat ##MVK_FMT_TYPE, { 0, 0, 0 }, "VK_FORMAT_" #VK_FMT, false } + CSPC, CSCB, { BLK_W, BLK_H }, BLK_BYTE_CNT, kMVKFormat ##MVK_FMT_TYPE, { 0, 0, 0 }, \ + { VK_COMPONENT_SWIZZLE_ ##SWIZ_R, VK_COMPONENT_SWIZZLE_ ##SWIZ_G, VK_COMPONENT_SWIZZLE_ ##SWIZ_B, VK_COMPONENT_SWIZZLE_ ##SWIZ_A }, \ + "VK_FORMAT_" #VK_FMT, false } #define addVkFormatDesc(VK_FMT, MTL_FMT, MTL_FMT_ALT, MTL_VTX_FMT, MTL_VTX_FMT_ALT, BLK_W, BLK_H, BLK_BYTE_CNT, MVK_FMT_TYPE) \ - addVkFormatDescFull(VK_FMT, MTL_FMT, MTL_FMT_ALT, MTL_VTX_FMT, MTL_VTX_FMT_ALT, 0, 0, BLK_W, BLK_H, BLK_BYTE_CNT, MVK_FMT_TYPE) + addVkFormatDescFull(VK_FMT, MTL_FMT, MTL_FMT_ALT, MTL_VTX_FMT, MTL_VTX_FMT_ALT, 0, 0, BLK_W, BLK_H, BLK_BYTE_CNT, MVK_FMT_TYPE, IDENTITY, IDENTITY, IDENTITY, IDENTITY) + +#define addVkFormatDescSwizzled(VK_FMT, MTL_FMT, MTL_FMT_ALT, MTL_VTX_FMT, MTL_VTX_FMT_ALT, BLK_W, BLK_H, BLK_BYTE_CNT, MVK_FMT_TYPE, SWIZ_R, SWIZ_G, SWIZ_B, SWIZ_A) \ + addVkFormatDescFull(VK_FMT, MTL_FMT, MTL_FMT_ALT, MTL_VTX_FMT, MTL_VTX_FMT_ALT, 0, 0, BLK_W, BLK_H, BLK_BYTE_CNT, MVK_FMT_TYPE, SWIZ_R, SWIZ_G, SWIZ_B, SWIZ_A) #define addVkFormatDescChromaSubsampling(VK_FMT, MTL_FMT, CSPC, CSCB, BLK_W, BLK_H, BLK_BYTE_CNT) \ - addVkFormatDescFull(VK_FMT, MTL_FMT, Invalid, Invalid, Invalid, CSPC, CSCB, BLK_W, BLK_H, BLK_BYTE_CNT, ColorFloat) + addVkFormatDescFull(VK_FMT, MTL_FMT, Invalid, Invalid, Invalid, CSPC, CSCB, BLK_W, BLK_H, BLK_BYTE_CNT, ColorFloat, IDENTITY, IDENTITY, IDENTITY, IDENTITY) void MVKPixelFormats::initVkFormatCapabilities() { @@ -781,6 +832,8 @@ void MVKPixelFormats::initVkFormatCapabilities() { addVkFormatDesc( R4G4_UNORM_PACK8, Invalid, Invalid, Invalid, Invalid, 1, 1, 1, ColorFloat ); addVkFormatDesc( R4G4B4A4_UNORM_PACK16, ABGR4Unorm, Invalid, Invalid, Invalid, 1, 1, 2, ColorFloat ); addVkFormatDesc( B4G4R4A4_UNORM_PACK16, Invalid, Invalid, Invalid, Invalid, 1, 1, 2, ColorFloat ); + addVkFormatDescSwizzled( A4R4G4B4_UNORM_PACK16, ABGR4Unorm, Invalid, Invalid, Invalid, 1, 1, 2, ColorFloat, G, B, A, R ); + addVkFormatDescSwizzled( A4B4G4R4_UNORM_PACK16, ABGR4Unorm, Invalid, Invalid, Invalid, 1, 1, 2, ColorFloat, A, B, G, R ); addVkFormatDesc( R5G6B5_UNORM_PACK16, B5G6R5Unorm, Invalid, Invalid, Invalid, 1, 1, 2, ColorFloat ); addVkFormatDesc( B5G6R5_UNORM_PACK16, Invalid, Invalid, Invalid, Invalid, 1, 1, 2, ColorFloat ); @@ -1962,6 +2015,23 @@ void MVKPixelFormats::buildVkFormatMaps() { _vkFormatDescIndicesByVkFormatsExt[vkFmt] = fmtIdx; } + if (vkDesc.needsSwizzle()) { + if (_physicalDevice) { + id mtlDev = _physicalDevice->getMTLDevice(); +#if MVK_MACCAT + bool supportsNativeTextureSwizzle = [mtlDev supportsFamily: MTLGPUFamilyMacCatalyst2]; +#elif MVK_MACOS + bool supportsNativeTextureSwizzle = mvkOSVersionIsAtLeast(10.15) && [mtlDev supportsFeatureSet: MTLFeatureSet_macOS_GPUFamily2_v1]; +#endif +#if MVK_IOS || MVK_TVOS + bool supportsNativeTextureSwizzle = mtlDev && mvkOSVersionIsAtLeast(13.0); +#endif + if (!supportsNativeTextureSwizzle && !mvkConfig().fullImageViewSwizzle) { + vkDesc.mtlPixelFormat = vkDesc.mtlPixelFormatSubstitute = MTLPixelFormatInvalid; + } + } + } + // Populate the back reference from the Metal formats to the Vulkan format. // Validate the corresponding Metal formats for the platform, and clear them // in the Vulkan format if not supported. @@ -2078,6 +2148,13 @@ void MVKPixelFormats::setFormatProperties(MVKVkFormatDesc& vkDesc) { mvkDisableFlags(vkProps.optimalTilingFeatures, (VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT)); } + // These formats require swizzling. In order to support rendering, we'll have to swizzle + // in the fragment shader, but that hasn't been implemented yet. + if (vkDesc.needsSwizzle()) { + mvkDisableFlags(vkProps.optimalTilingFeatures, (kMVKVkFormatFeatureFlagsTexColorAtt | + kMVKVkFormatFeatureFlagsTexBlend)); + } + // Linear tiling is not available to depth/stencil or compressed formats. // GBGR and BGRG formats also do not support linear tiling in Metal. if ( !(vkDesc.formatType == kMVKFormatDepthStencil || vkDesc.formatType == kMVKFormatCompressed || diff --git a/MoltenVK/MoltenVK/Layers/MVKExtensions.def b/MoltenVK/MoltenVK/Layers/MVKExtensions.def index a50b3bb5..0a4bd964 100644 --- a/MoltenVK/MoltenVK/Layers/MVKExtensions.def +++ b/MoltenVK/MoltenVK/Layers/MVKExtensions.def @@ -92,6 +92,7 @@ MVK_EXTENSION(KHR_timeline_semaphore, KHR_TIMELINE_SEMAPHORE, MVK_EXTENSION(KHR_uniform_buffer_standard_layout, KHR_UNIFORM_BUFFER_STANDARD_LAYOUT, DEVICE, 10.11, 8.0) MVK_EXTENSION(KHR_variable_pointers, KHR_VARIABLE_POINTERS, DEVICE, 10.11, 8.0) MVK_EXTENSION(KHR_vulkan_memory_model, KHR_VULKAN_MEMORY_MODEL, DEVICE, MVK_NA, MVK_NA) +MVK_EXTENSION(EXT_4444_formats, EXT_4444_FORMATS, DEVICE, 11.0, 13.0) MVK_EXTENSION(EXT_buffer_device_address, EXT_BUFFER_DEVICE_ADDRESS, DEVICE, 13.0, 16.0) MVK_EXTENSION(EXT_debug_marker, EXT_DEBUG_MARKER, DEVICE, 10.11, 8.0) MVK_EXTENSION(EXT_debug_report, EXT_DEBUG_REPORT, INSTANCE, 10.11, 8.0) diff --git a/MoltenVK/MoltenVK/Vulkan/mvk_datatypes.mm b/MoltenVK/MoltenVK/Vulkan/mvk_datatypes.mm index 1960dedd..1aab82eb 100644 --- a/MoltenVK/MoltenVK/Vulkan/mvk_datatypes.mm +++ b/MoltenVK/MoltenVK/Vulkan/mvk_datatypes.mm @@ -228,6 +228,45 @@ MVK_PUBLIC_SYMBOL MTLTextureSwizzleChannels mvkMTLTextureSwizzleChannelsFromVkCo #undef convert } +MVK_PUBLIC_SYMBOL float mvkVkClearColorFloatValueFromVkComponentSwizzle(float *colors, uint32_t index, VkComponentSwizzle vkSwizzle) { + switch (vkSwizzle) { + case VK_COMPONENT_SWIZZLE_IDENTITY: return colors[index]; + case VK_COMPONENT_SWIZZLE_ZERO: return 0.f; + case VK_COMPONENT_SWIZZLE_ONE: return 1.f; + case VK_COMPONENT_SWIZZLE_R: return colors[0]; + case VK_COMPONENT_SWIZZLE_G: return colors[1]; + case VK_COMPONENT_SWIZZLE_B: return colors[2]; + case VK_COMPONENT_SWIZZLE_A: return colors[3]; + default: return colors[index]; + } +} + +MVK_PUBLIC_SYMBOL uint32_t mvkVkClearColorUIntValueFromVkComponentSwizzle(uint32_t *colors, uint32_t index, VkComponentSwizzle vkSwizzle) { + switch (vkSwizzle) { + case VK_COMPONENT_SWIZZLE_IDENTITY: return colors[index]; + case VK_COMPONENT_SWIZZLE_ZERO: return 0U; + case VK_COMPONENT_SWIZZLE_ONE: return 1U; + case VK_COMPONENT_SWIZZLE_R: return colors[0]; + case VK_COMPONENT_SWIZZLE_G: return colors[1]; + case VK_COMPONENT_SWIZZLE_B: return colors[2]; + case VK_COMPONENT_SWIZZLE_A: return colors[3]; + default: return colors[index]; + } +} + +MVK_PUBLIC_SYMBOL int32_t mvkVkClearColorIntValueFromVkComponentSwizzle(int32_t *colors, uint32_t index, VkComponentSwizzle vkSwizzle) { + switch (vkSwizzle) { + case VK_COMPONENT_SWIZZLE_IDENTITY: return colors[index]; + case VK_COMPONENT_SWIZZLE_ZERO: return 0; + case VK_COMPONENT_SWIZZLE_ONE: return 1; + case VK_COMPONENT_SWIZZLE_R: return colors[0]; + case VK_COMPONENT_SWIZZLE_G: return colors[1]; + case VK_COMPONENT_SWIZZLE_B: return colors[2]; + case VK_COMPONENT_SWIZZLE_A: return colors[3]; + default: return colors[index]; + } +} + #pragma mark Mipmaps