| /* |
| * MVKPixelFormats.h |
| * |
| * Copyright (c) 2015-2020 The Brenwill Workshop Ltd. (http://www.brenwill.com) |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #pragma once |
| |
| #include "mvk_datatypes.h" |
| #include "MVKEnvironment.h" |
| #include "MVKOSExtensions.h" |
| #include "MVKBaseObject.h" |
| #include <SPIRV-Cross/spirv_msl.hpp> |
| #include <unordered_map> |
| |
| #import <Metal/Metal.h> |
| |
| class MVKPhysicalDevice; |
| |
| |
| // Validate these values periodically as new formats are added over time. |
| static const uint32_t _vkFormatCount = 256; |
| static const uint32_t _vkFormatCoreCount = VK_FORMAT_ASTC_12x12_SRGB_BLOCK + 1; |
| static const uint32_t _mtlPixelFormatCount = 128; |
| static const uint32_t _mtlPixelFormatCoreCount = MTLPixelFormatX32_Stencil8 + 2; // The actual last enum value is not available on iOS |
| static const uint32_t _mtlVertexFormatCount = MTLVertexFormatHalf + 1; |
| |
| |
| #pragma mark - |
| #pragma mark Metal format capabilities |
| |
| typedef enum : uint16_t { |
| |
| kMVKMTLFmtCapsNone = 0, |
| kMVKMTLFmtCapsRead = (1<<0), |
| kMVKMTLFmtCapsFilter = (1<<1), |
| kMVKMTLFmtCapsWrite = (1<<2), |
| kMVKMTLFmtCapsAtomic = (1<<3), |
| kMVKMTLFmtCapsColorAtt = (1<<4), |
| kMVKMTLFmtCapsDSAtt = (1<<5), |
| kMVKMTLFmtCapsBlend = (1<<6), |
| kMVKMTLFmtCapsMSAA = (1<<7), |
| kMVKMTLFmtCapsResolve = (1<<8), |
| kMVKMTLFmtCapsVertex = (1<<9), |
| |
| kMVKMTLFmtCapsRF = (kMVKMTLFmtCapsRead | kMVKMTLFmtCapsFilter), |
| kMVKMTLFmtCapsRC = (kMVKMTLFmtCapsRead | kMVKMTLFmtCapsColorAtt), |
| kMVKMTLFmtCapsRCB = (kMVKMTLFmtCapsRC | kMVKMTLFmtCapsBlend), |
| kMVKMTLFmtCapsRCM = (kMVKMTLFmtCapsRC | kMVKMTLFmtCapsMSAA), |
| kMVKMTLFmtCapsRCMB = (kMVKMTLFmtCapsRCM | kMVKMTLFmtCapsBlend), |
| kMVKMTLFmtCapsRWC = (kMVKMTLFmtCapsRC | kMVKMTLFmtCapsWrite), |
| kMVKMTLFmtCapsRWCB = (kMVKMTLFmtCapsRWC | kMVKMTLFmtCapsBlend), |
| kMVKMTLFmtCapsRWCM = (kMVKMTLFmtCapsRWC | kMVKMTLFmtCapsMSAA), |
| kMVKMTLFmtCapsRWCMB = (kMVKMTLFmtCapsRWCM | kMVKMTLFmtCapsBlend), |
| kMVKMTLFmtCapsRFCMRB = (kMVKMTLFmtCapsRCMB | kMVKMTLFmtCapsFilter | kMVKMTLFmtCapsResolve), |
| kMVKMTLFmtCapsRFWCMB = (kMVKMTLFmtCapsRWCMB | kMVKMTLFmtCapsFilter), |
| kMVKMTLFmtCapsAll = (kMVKMTLFmtCapsRFWCMB | kMVKMTLFmtCapsResolve), |
| |
| kMVKMTLFmtCapsDRM = (kMVKMTLFmtCapsDSAtt | kMVKMTLFmtCapsRead | kMVKMTLFmtCapsMSAA), |
| kMVKMTLFmtCapsDRFM = (kMVKMTLFmtCapsDRM | kMVKMTLFmtCapsFilter), |
| kMVKMTLFmtCapsDRMR = (kMVKMTLFmtCapsDRM | kMVKMTLFmtCapsResolve), |
| kMVKMTLFmtCapsDRFMR = (kMVKMTLFmtCapsDRFM | kMVKMTLFmtCapsResolve), |
| |
| kMVKMTLFmtCapsChromaSubsampling = kMVKMTLFmtCapsRF, |
| kMVKMTLFmtCapsMultiPlanar = kMVKMTLFmtCapsChromaSubsampling, |
| } MVKMTLFmtCaps; |
| |
| inline MVKMTLFmtCaps operator|(MVKMTLFmtCaps leftCaps, MVKMTLFmtCaps rightCaps) { |
| return static_cast<MVKMTLFmtCaps>(static_cast<uint32_t>(leftCaps) | rightCaps); |
| } |
| |
| inline MVKMTLFmtCaps& operator|=(MVKMTLFmtCaps& leftCaps, MVKMTLFmtCaps rightCaps) { |
| return (leftCaps = leftCaps | rightCaps); |
| } |
| |
| |
| #pragma mark - |
| #pragma mark Metal view classes |
| |
| enum class MVKMTLViewClass : uint8_t { |
| None, |
| Color8, |
| Color16, |
| Color32, |
| Color64, |
| Color128, |
| PVRTC_RGB_2BPP, |
| PVRTC_RGB_4BPP, |
| PVRTC_RGBA_2BPP, |
| PVRTC_RGBA_4BPP, |
| EAC_R11, |
| EAC_RG11, |
| EAC_RGBA8, |
| ETC2_RGB8, |
| ETC2_RGB8A1, |
| ASTC_4x4, |
| ASTC_5x4, |
| ASTC_5x5, |
| ASTC_6x5, |
| ASTC_6x6, |
| ASTC_8x5, |
| ASTC_8x6, |
| ASTC_8x8, |
| ASTC_10x5, |
| ASTC_10x6, |
| ASTC_10x8, |
| ASTC_10x10, |
| ASTC_12x10, |
| ASTC_12x12, |
| BC1_RGBA, |
| BC2_RGBA, |
| BC3_RGBA, |
| BC4_R, |
| BC5_RG, |
| BC6H_RGB, |
| BC7_RGBA, |
| Depth24_Stencil8, |
| Depth32_Stencil8, |
| BGRA10_XR, |
| BGR10_XR |
| }; |
| |
| |
| #pragma mark - |
| #pragma mark Format descriptors |
| |
| /** Describes the properties of a VkFormat, including the corresponding Metal pixel and vertex format. */ |
| typedef struct { |
| VkFormat vkFormat; |
| MTLPixelFormat mtlPixelFormat; |
| MTLPixelFormat mtlPixelFormatSubstitute; |
| MTLVertexFormat mtlVertexFormat; |
| MTLVertexFormat mtlVertexFormatSubstitute; |
| uint8_t chromaSubsamplingPlaneCount; |
| uint8_t chromaSubsamplingComponentBits; |
| VkExtent2D blockTexelSize; |
| uint32_t bytesPerBlock; |
| MVKFormatType formatType; |
| VkFormatProperties properties; |
| const char* name; |
| bool hasReportedSubstitution; |
| |
| inline double bytesPerTexel() const { return (double)bytesPerBlock / (double)(blockTexelSize.width * blockTexelSize.height); }; |
| |
| inline bool isSupported() const { return (mtlPixelFormat != MTLPixelFormatInvalid || chromaSubsamplingPlaneCount > 0); }; |
| inline bool isSupportedOrSubstitutable() const { return isSupported() || (mtlPixelFormatSubstitute != MTLPixelFormatInvalid); }; |
| |
| inline bool vertexIsSupported() const { return (mtlVertexFormat != MTLVertexFormatInvalid); }; |
| inline bool vertexIsSupportedOrSubstitutable() const { return vertexIsSupported() || (mtlVertexFormatSubstitute != MTLVertexFormatInvalid); }; |
| } MVKVkFormatDesc; |
| |
| /** Describes the properties of a MTLPixelFormat or MTLVertexFormat. */ |
| typedef struct { |
| union { |
| MTLPixelFormat mtlPixelFormat; |
| MTLVertexFormat mtlVertexFormat; |
| }; |
| VkFormat vkFormat; |
| MVKMTLFmtCaps mtlFmtCaps; |
| MVKMTLViewClass mtlViewClass; |
| const char* name; |
| |
| inline bool isSupported() const { return (mtlPixelFormat != MTLPixelFormatInvalid) && (mtlFmtCaps != kMVKMTLFmtCapsNone); }; |
| } MVKMTLFormatDesc; |
| |
| |
| #pragma mark - |
| #pragma mark MVKPixelFormats |
| |
| /** Helper class to manage pixel format capabilities and conversions. */ |
| class MVKPixelFormats : public MVKBaseObject { |
| |
| public: |
| |
| /** Returns the Vulkan API opaque object controlling this object. */ |
| MVKVulkanAPIObject* getVulkanAPIObject() override; |
| |
| /** Returns whether the VkFormat is supported by this implementation. */ |
| bool isSupported(VkFormat vkFormat); |
| |
| /** Returns whether the VkFormat is supported by this implementation, or can be substituted by one that is. */ |
| bool isSupportedOrSubstitutable(VkFormat vkFormat); |
| |
| /** Returns whether the MTLPixelFormat is supported by this implementation. */ |
| bool isSupported(MTLPixelFormat mtlFormat); |
| |
| /** Returns whether the specified Metal MTLPixelFormat can be used as a depth format. */ |
| bool isDepthFormat(MTLPixelFormat mtlFormat); |
| |
| /** Returns whether the specified Metal MTLPixelFormat can be used as a stencil format. */ |
| bool isStencilFormat(MTLPixelFormat mtlFormat); |
| |
| /** Returns whether the specified Metal MTLPixelFormat is a PVRTC format. */ |
| bool isPVRTCFormat(MTLPixelFormat mtlFormat); |
| |
| /** Returns the format type corresponding to the specified Vulkan VkFormat, */ |
| MVKFormatType getFormatType(VkFormat vkFormat); |
| |
| /** Returns the format type corresponding to the specified Metal MTLPixelFormat, */ |
| MVKFormatType getFormatType(MTLPixelFormat mtlFormat); |
| |
| /** |
| * Returns the Metal MTLPixelFormat corresponding to the specified Vulkan VkFormat, |
| * or returns MTLPixelFormatInvalid if no corresponding MTLPixelFormat exists. |
| */ |
| MTLPixelFormat getMTLPixelFormat(VkFormat vkFormat); |
| |
| /** |
| * Returns the Vulkan VkFormat corresponding to the specified Metal MTLPixelFormat, |
| * or returns VK_FORMAT_UNDEFINED if no corresponding VkFormat exists. |
| */ |
| VkFormat getVkFormat(MTLPixelFormat mtlFormat); |
| |
| /** |
| * Returns the size, in bytes, of a texel block of the specified Vulkan format. |
| * For uncompressed formats, the returned value corresponds to the size in bytes of a single texel. |
| */ |
| uint32_t getBytesPerBlock(VkFormat vkFormat); |
| |
| /** |
| * Returns the size, in bytes, of a texel block of the specified Metal format. |
| * For uncompressed formats, the returned value corresponds to the size in bytes of a single texel. |
| */ |
| uint32_t getBytesPerBlock(MTLPixelFormat mtlFormat); |
| |
| /** |
| * Returns the size of the compression block, measured in texels for a Vulkan format. |
| * The returned value will be {1, 1} for non-compressed formats without chroma-subsampling. |
| */ |
| VkExtent2D getBlockTexelSize(VkFormat vkFormat); |
| |
| /** |
| * Returns the size of the compression block, measured in texels for a Metal format. |
| * The returned value will be {1, 1} for non-compressed formats without chroma-subsampling. |
| */ |
| VkExtent2D getBlockTexelSize(MTLPixelFormat mtlFormat); |
| |
| /** Returns the number of planes of the specified chroma-subsampling (YCbCr) VkFormat */ |
| uint8_t getChromaSubsamplingPlaneCount(VkFormat vkFormat); |
| |
| /** Returns the number of bits per channel of the specified chroma-subsampling (YCbCr) VkFormat */ |
| uint8_t getChromaSubsamplingComponentBits(VkFormat vkFormat); |
| |
| /** 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]); |
| |
| /** |
| * Returns the size, in bytes, of a texel of the specified Vulkan format. |
| * The returned value may be fractional for certain compressed formats. |
| */ |
| float getBytesPerTexel(VkFormat vkFormat); |
| |
| /** |
| * Returns the size, in bytes, of a texel of the specified Metal format. |
| * The returned value may be fractional for certain compressed formats. |
| */ |
| float getBytesPerTexel(MTLPixelFormat mtlFormat); |
| |
| /** |
| * Returns the size, in bytes, of a row of texels of the specified Vulkan format. |
| * |
| * For compressed formats, this takes into consideration the compression block size, |
| * and texelsPerRow should specify the width in texels, not blocks. The result is rounded |
| * up if texelsPerRow is not an integer multiple of the compression block width. |
| */ |
| size_t getBytesPerRow(VkFormat vkFormat, uint32_t texelsPerRow); |
| |
| /** |
| * Returns the size, in bytes, of a row of texels of the specified Metal format. |
| * |
| * For compressed formats, this takes into consideration the compression block size, |
| * and texelsPerRow should specify the width in texels, not blocks. The result is rounded |
| * up if texelsPerRow is not an integer multiple of the compression block width. |
| */ |
| size_t getBytesPerRow(MTLPixelFormat mtlFormat, uint32_t texelsPerRow); |
| |
| /** |
| * Returns the size, in bytes, of a texture layer of the specified Vulkan format. |
| * |
| * For compressed formats, this takes into consideration the compression block size, |
| * and texelRowsPerLayer should specify the height in texels, not blocks. The result is |
| * rounded up if texelRowsPerLayer is not an integer multiple of the compression block height. |
| */ |
| size_t getBytesPerLayer(VkFormat vkFormat, size_t bytesPerRow, uint32_t texelRowsPerLayer); |
| |
| /** |
| * Returns the size, in bytes, of a texture layer of the specified Metal format. |
| * For compressed formats, this takes into consideration the compression block size, |
| * and texelRowsPerLayer should specify the height in texels, not blocks. The result is |
| * rounded up if texelRowsPerLayer is not an integer multiple of the compression block height. |
| */ |
| size_t getBytesPerLayer(MTLPixelFormat mtlFormat, size_t bytesPerRow, uint32_t texelRowsPerLayer); |
| |
| /** Returns the default properties for the specified Vulkan format. */ |
| VkFormatProperties& getVkFormatProperties(VkFormat vkFormat); |
| |
| /** Returns the Metal format capabilities supported by the specified Vulkan format, without substitution. */ |
| MVKMTLFmtCaps getCapabilities(VkFormat vkFormat, bool isExtended = false); |
| |
| /** 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); |
| |
| /** Returns the name of the specified Vulkan format. */ |
| const char* getName(VkFormat vkFormat); |
| |
| /** Returns the name of the specified Metal pixel format. */ |
| const char* getName(MTLPixelFormat mtlFormat); |
| |
| /** |
| * Returns the MTLClearColor value corresponding to the color value in the VkClearValue, |
| * extracting the color value that is VkFormat for the VkFormat. |
| */ |
| MTLClearColor getMTLClearColor(VkClearValue vkClearValue, VkFormat vkFormat); |
| |
| /** Returns the Metal depth value corresponding to the depth value in the specified VkClearValue. */ |
| double getMTLClearDepthValue(VkClearValue vkClearValue); |
| |
| /** Returns the Metal stencil value corresponding to the stencil value in the specified VkClearValue. */ |
| uint32_t getMTLClearStencilValue(VkClearValue vkClearValue); |
| |
| /** Returns the Vulkan image usage from the Metal texture usage and format. */ |
| VkImageUsageFlags getVkImageUsageFlags(MTLTextureUsage mtlUsage, MTLPixelFormat mtlFormat); |
| |
| /** |
| * Returns the Metal texture usage from the Vulkan image usage and Metal format, ensuring that at least the |
| * usages in minUsage are included, even if they wouldn't naturally be included based on the other two parameters. |
| * isLinear further restricts the allowed usage to those that are valid for linear textures. |
| * isExtended expands the allowed usage to those that are valid for all formats which |
| * can be used in a view created from the specified format. |
| */ |
| MTLTextureUsage getMTLTextureUsage(VkImageUsageFlags vkImageUsageFlags, |
| 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. */ |
| void enumerateSupportedFormats(VkFormatProperties properties, bool any, std::function<bool(VkFormat)> func); |
| |
| /** |
| * Returns the Metal MTLVertexFormat corresponding to the specified |
| * Vulkan VkFormat as used as a vertex attribute format. |
| */ |
| MTLVertexFormat getMTLVertexFormat(VkFormat vkFormat); |
| |
| |
| #pragma mark Construction |
| |
| MVKPixelFormats(MVKPhysicalDevice* physicalDevice = nullptr); |
| |
| protected: |
| MVKVkFormatDesc& getVkFormatDesc(VkFormat vkFormat); |
| MVKVkFormatDesc& getVkFormatDesc(MTLPixelFormat mtlFormat); |
| MVKMTLFormatDesc& getMTLPixelFormatDesc(MTLPixelFormat mtlFormat); |
| MVKMTLFormatDesc& getMTLVertexFormatDesc(MTLVertexFormat mtlFormat); |
| void initVkFormatCapabilities(); |
| void initMTLPixelFormatCapabilities(); |
| void initMTLVertexFormatCapabilities(); |
| void buildMTLFormatMaps(); |
| void buildVkFormatMaps(); |
| void setFormatProperties(MVKVkFormatDesc& vkDesc); |
| void modifyMTLFormatCapabilities(); |
| void modifyMTLFormatCapabilities(id<MTLDevice> mtlDevice); |
| void addMTLPixelFormatCapabilities(id<MTLDevice> mtlDevice, |
| MTLFeatureSet mtlFeatSet, |
| MTLPixelFormat mtlPixFmt, |
| MVKMTLFmtCaps mtlFmtCaps); |
| void addMTLPixelFormatCapabilities(id<MTLDevice> mtlDevice, |
| MTLGPUFamily gpuFamily, |
| MVKOSVersion minOSVer, |
| MTLPixelFormat mtlPixFmt, |
| MVKMTLFmtCaps mtlFmtCaps); |
| void disableMTLPixelFormatCapabilities(MTLPixelFormat mtlPixFmt, |
| MVKMTLFmtCaps mtlFmtCaps); |
| void disableAllMTLPixelFormatCapabilities(MTLPixelFormat mtlPixFmt); |
| void addMTLVertexFormatCapabilities(id<MTLDevice> mtlDevice, |
| MTLFeatureSet mtlFeatSet, |
| MTLVertexFormat mtlVtxFmt, |
| MVKMTLFmtCaps mtlFmtCaps); |
| void addMTLVertexFormatCapabilities(id<MTLDevice> mtlDevice, |
| MTLGPUFamily gpuFamily, |
| MVKOSVersion minOSVer, |
| MTLVertexFormat mtlVtxFmt, |
| MVKMTLFmtCaps mtlFmtCaps); |
| |
| template<typename T> |
| void testFmt(const T v1, const T v2, const char* fmtName, const char* funcName); |
| void testProps(const VkFormatProperties p1, const VkFormatProperties p2, const char* fmtName); |
| void test(); |
| |
| MVKPhysicalDevice* _physicalDevice; |
| MVKVkFormatDesc _vkFormatDescriptions[_vkFormatCount]; |
| MVKMTLFormatDesc _mtlPixelFormatDescriptions[_mtlPixelFormatCount]; |
| MVKMTLFormatDesc _mtlVertexFormatDescriptions[_mtlVertexFormatCount]; |
| |
| // Vulkan core formats have small values and are mapped by simple lookup array. |
| // Vulkan extension formats have larger values and are mapped by a map. |
| uint16_t _vkFormatDescIndicesByVkFormatsCore[_vkFormatCoreCount]; |
| std::unordered_map<uint32_t, uint32_t> _vkFormatDescIndicesByVkFormatsExt; |
| |
| // Most Metal formats have small values and are mapped by simple lookup array. |
| // Outliers are mapped by a map. |
| uint16_t _mtlFormatDescIndicesByMTLPixelFormatsCore[_mtlPixelFormatCoreCount]; |
| std::unordered_map<NSUInteger, uint32_t> _mtlFormatDescIndicesByMTLPixelFormatsExt; |
| |
| uint16_t _mtlFormatDescIndicesByMTLVertexFormats[_mtlVertexFormatCount]; |
| }; |