Merge pull request #867 from billhollings/master

MVKPixelFormats enables atomic capabilities only when applicable.
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.h b/MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.h
index 5a1317f..a1cba32 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.h
@@ -44,12 +44,13 @@
 	kMVKMTLFmtCapsRead     = (1<<0),
 	kMVKMTLFmtCapsFilter   = (1<<1),
 	kMVKMTLFmtCapsWrite    = (1<<2),
-	kMVKMTLFmtCapsColorAtt = (1<<3),
-	kMVKMTLFmtCapsDSAtt    = (1<<4),
-	kMVKMTLFmtCapsBlend    = (1<<5),
-	kMVKMTLFmtCapsMSAA     = (1<<6),
-	kMVKMTLFmtCapsResolve  = (1<<7),
-	kMVKMTLFmtCapsVertex   = (1<<8),
+	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),
@@ -291,14 +292,12 @@
 	MVKMTLFormatDesc& getMTLPixelFormatDesc(MTLPixelFormat mtlFormat);
 	MVKMTLFormatDesc& getMTLVertexFormatDesc(VkFormat vkFormat);
 	MVKMTLFormatDesc& getMTLVertexFormatDesc(MTLVertexFormat mtlFormat);
-	VkFormatFeatureFlags getOptimalTilingFeatures(VkFormat vkFormat, MVKMTLFmtCaps mtlFmtCaps);
-	VkFormatFeatureFlags getLinearTilingFeatures(VkFormat vkFormat, MVKMTLFmtCaps mtlFmtCaps, MVKFormatType mvkFmtType);
-	VkFormatFeatureFlags getBufferFeatures(VkFormat vkFormat, MVKMTLFmtCaps mtlFmtTexCaps, MVKMTLFmtCaps mtlFmtVtxCaps, MVKFormatType mvkFmtType);
 	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,
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.mm b/MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.mm
index 927af4b..5197c4d 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.mm
@@ -1072,6 +1072,10 @@
 // Modifies the format capability tables based on the capabilities of the specific MTLDevice
 #if MVK_MACOS
 void MVKPixelFormats::modifyMTLFormatCapabilities(id<MTLDevice> mtlDevice) {
+
+	addMTLPixelFormatCapabilities( macOS_GPUFamily1_v1, R32Uint, Atomic );
+	addMTLPixelFormatCapabilities( macOS_GPUFamily1_v1, R32Sint, Atomic );
+
 	if (mtlDevice.isDepth24Stencil8PixelFormatSupported) {
 		addMTLPixelFormatCapabilities( macOS_GPUFamily1_v1, Depth24Unorm_Stencil8, DRFMR );
 	}
@@ -1105,7 +1109,9 @@
 	addMTLPixelFormatCapabilities( iOS_GPUFamily2_v1, RG8Snorm, All );
 
 	addMTLPixelFormatCapabilities( iOS_GPUFamily1_v2, R32Uint, RWC );
+	addMTLPixelFormatCapabilities( iOS_GPUFamily1_v2, R32Uint, Atomic );
 	addMTLPixelFormatCapabilities( iOS_GPUFamily1_v2, R32Sint, RWC );
+	addMTLPixelFormatCapabilities( iOS_GPUFamily1_v2, R32Sint, Atomic );
 
 	addMTLPixelFormatCapabilities( iOS_GPUFamily1_v2, R32Float, RWCMB );
 
@@ -1227,13 +1233,8 @@
 				if ( !mtlDesc.isSupported() ) { vkDesc.mtlVertexFormatSubstitute = MTLVertexFormatInvalid; }
 			}
 
-			// Vulkan format features
-			MVKFormatType fmtType = vkDesc.formatType;
-			MVKMTLFmtCaps mtlPixFmtCaps = getMTLPixelFormatDesc(vkFmt).mtlFmtCaps;
-			MVKMTLFmtCaps mtlVtxFmtCaps = getMTLVertexFormatDesc(vkFmt).mtlFmtCaps;
-			vkDesc.properties = { getLinearTilingFeatures(vkFmt, mtlPixFmtCaps, fmtType),
-								  getOptimalTilingFeatures(vkFmt, mtlPixFmtCaps),
-								  getBufferFeatures(vkFmt, mtlPixFmtCaps, mtlVtxFmtCaps, fmtType) };
+			// Set Vulkan format properties
+			setFormatProperties(vkDesc);
 		}
 	}
 }
@@ -1246,87 +1247,65 @@
 										   VK_FORMAT_FEATURE_TRANSFER_DST_BIT |
 										   VK_FORMAT_FEATURE_BLIT_SRC_BIT),
 	kMVKVkFormatFeatureFlagsTexFilter   = (VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT),
-	kMVKVkFormatFeatureFlagsTexWrite    = (VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT |
-										   VK_FORMAT_FEATURE_STORAGE_IMAGE_ATOMIC_BIT),
+	kMVKVkFormatFeatureFlagsTexWrite    = (VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT),
+	kMVKVkFormatFeatureFlagsTexAtomic   = (VK_FORMAT_FEATURE_STORAGE_IMAGE_ATOMIC_BIT),
 	kMVKVkFormatFeatureFlagsTexColorAtt = (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT |
 										   VK_FORMAT_FEATURE_BLIT_DST_BIT),
 	kMVKVkFormatFeatureFlagsTexDSAtt    = (VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT),
 	kMVKVkFormatFeatureFlagsTexBlend    = (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT),
 	kMVKVkFormatFeatureFlagsBufRead     = (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT),
-	kMVKVkFormatFeatureFlagsBufWrite    = (VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT |
-										   VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_ATOMIC_BIT),
+	kMVKVkFormatFeatureFlagsBufWrite    = (VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT),
+	kMVKVkFormatFeatureFlagsBufAtomic   = (VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_ATOMIC_BIT),
 	kMVKVkFormatFeatureFlagsBufVertex   = (VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT),
 } MVKVkFormatFeatureFlags;
 
-#define enableTexFormatFeatures(CAP)  \
-	if (mvkAreAllFlagsEnabled(mtlFmtCaps, kMVKMTLFmtCaps ##CAP)) {  \
-		vkFeatures |= kMVKVkFormatFeatureFlagsTex ##CAP;  \
-	}
-VkFormatFeatureFlags MVKPixelFormats::getOptimalTilingFeatures(VkFormat vkFormat,
-															   MVKMTLFmtCaps mtlFmtCaps) {
-	VkFormatFeatureFlags vkFeatures = kMVKVkFormatFeatureFlagsTexNone;
-	enableTexFormatFeatures(Read);
-	enableTexFormatFeatures(Filter);
-	enableTexFormatFeatures(Write);
-	enableTexFormatFeatures(ColorAtt);
-	enableTexFormatFeatures(DSAtt);
-	enableTexFormatFeatures(Blend);
-	// Only support atomics on R32UI/R32I images for now. Until Metal supports this
-	// for real, we need to use the underlying buffer to support image atomics.
-	if (vkFormat != VK_FORMAT_R32_UINT && vkFormat != VK_FORMAT_R32_SINT) {
-		mvkDisableFlags(vkFeatures, VK_FORMAT_FEATURE_STORAGE_IMAGE_ATOMIC_BIT);
-	}
-	return vkFeatures;
-}
+// Sets the VkFormatProperties (optimal/linear/buffer) for the Vulkan format.
+void MVKPixelFormats::setFormatProperties(MVKVkFormatDesc& vkDesc) {
 
-VkFormatFeatureFlags MVKPixelFormats::getLinearTilingFeatures(VkFormat vkFormat,
-															  MVKMTLFmtCaps mtlFmtCaps,
-															  MVKFormatType mvkFmtType) {
-
-	// Depth-stencil or compressed formats cannot be used for linear textures.
-	if (mvkFmtType == kMVKFormatDepthStencil || mvkFmtType == kMVKFormatCompressed) {
-		return kMVKVkFormatFeatureFlagsTexNone;
+#	define enableFormatFeatures(CAP, TYPE, MTL_FMT_CAPS, VK_FEATS)        \
+	if (mvkAreAllFlagsEnabled(MTL_FMT_CAPS, kMVKMTLFmtCaps ##CAP)) {      \
+		mvkEnableFlags(VK_FEATS, kMVKVkFormatFeatureFlags ##TYPE ##CAP);  \
 	}
 
-	// Start with the optimal features.
-	VkFormatFeatureFlags vkFeatures = getOptimalTilingFeatures(vkFormat, mtlFmtCaps);
+	VkFormat vkFmt = vkDesc.vkFormat;
+	VkFormatProperties& vkProps = vkDesc.properties;
+	MVKMTLFmtCaps mtlPixFmtCaps = getMTLPixelFormatDesc(vkFmt).mtlFmtCaps;
+
+	// Set optimal tiling features first
+	vkProps.optimalTilingFeatures = kMVKVkFormatFeatureFlagsTexNone;
+	enableFormatFeatures(Read, Tex, mtlPixFmtCaps, vkProps.optimalTilingFeatures);
+	enableFormatFeatures(Filter, Tex, mtlPixFmtCaps, vkProps.optimalTilingFeatures);
+	enableFormatFeatures(Write, Tex, mtlPixFmtCaps, vkProps.optimalTilingFeatures);
+	enableFormatFeatures(ColorAtt, Tex, mtlPixFmtCaps, vkProps.optimalTilingFeatures);
+	enableFormatFeatures(DSAtt, Tex, mtlPixFmtCaps, vkProps.optimalTilingFeatures);
+	enableFormatFeatures(Blend, Tex, mtlPixFmtCaps, vkProps.optimalTilingFeatures);
+
+	// Linear tiling is not available to depth/stencil or compressed formats.
+	vkProps.linearTilingFeatures = kMVKVkFormatFeatureFlagsTexNone;
+	if ( !(vkDesc.formatType == kMVKFormatDepthStencil || vkDesc.formatType == kMVKFormatCompressed) ) {
+
+		// Start with optimal tiling features, and modify.
+		vkProps.linearTilingFeatures = vkProps.optimalTilingFeatures;
+
+		// Linear tiling can support atomic writing for some formats, even though optimal tiling does not.
+		enableFormatFeatures(Atomic, Tex, mtlPixFmtCaps, vkProps.linearTilingFeatures);
 
 #if MVK_MACOS
-	// On macOS, linear textures cannot be used as attachments, so disable those features.
-	mvkDisableFlags(vkFeatures, (kMVKVkFormatFeatureFlagsTexColorAtt |
-								 kMVKVkFormatFeatureFlagsTexDSAtt |
-								 kMVKVkFormatFeatureFlagsTexBlend));
+		// On macOS, linear textures cannot be used as attachments, so disable those features.
+		mvkDisableFlags(vkProps.linearTilingFeatures, (kMVKVkFormatFeatureFlagsTexColorAtt |
+													   kMVKVkFormatFeatureFlagsTexDSAtt |
+													   kMVKVkFormatFeatureFlagsTexBlend));
 #endif
-
-	return vkFeatures;
-}
-
-VkFormatFeatureFlags MVKPixelFormats::getBufferFeatures(VkFormat vkFormat,
-														MVKMTLFmtCaps mtlPixFmtCaps,
-														MVKMTLFmtCaps mtlVtxFmtCaps,
-														MVKFormatType mvkFmtType) {
-
-	// Depth-stencil or compressed formats cannot be used for texel buffers.
-	if (mvkFmtType == kMVKFormatDepthStencil || mvkFmtType == kMVKFormatCompressed) {
-		return kMVKVkFormatFeatureFlagsTexNone;
 	}
 
-	VkFormatFeatureFlags vkFeatures = kMVKVkFormatFeatureFlagsTexNone;
-	if (mvkAreAllFlagsEnabled(mtlPixFmtCaps, kMVKMTLFmtCapsRead)) {
-		vkFeatures |= kMVKVkFormatFeatureFlagsBufRead;
+	// Texel buffers are not available to depth/stencil or compressed formats.
+	vkProps.bufferFeatures = kMVKVkFormatFeatureFlagsTexNone;
+	if ( !(vkDesc.formatType == kMVKFormatDepthStencil || vkDesc.formatType == kMVKFormatCompressed) ) {
+		enableFormatFeatures(Read, Buf, mtlPixFmtCaps, vkProps.bufferFeatures);
+		enableFormatFeatures(Write, Buf, mtlPixFmtCaps, vkProps.bufferFeatures);
+		enableFormatFeatures(Atomic, Buf, mtlPixFmtCaps, vkProps.bufferFeatures);
+		enableFormatFeatures(Vertex, Buf, getMTLVertexFormatDesc(vkFmt).mtlFmtCaps, vkProps.bufferFeatures);
 	}
-	if (mvkAreAllFlagsEnabled(mtlPixFmtCaps, kMVKMTLFmtCapsWrite)) {
-		vkFeatures |= kMVKVkFormatFeatureFlagsBufWrite;
-	}
-	if (mvkAreAllFlagsEnabled(mtlVtxFmtCaps, kMVKMTLFmtCapsVertex)) {
-		vkFeatures |= kMVKVkFormatFeatureFlagsBufVertex;
-	}
-	// Only support atomics on R32UI/R32I images for now. Until Metal supports this
-	// for real, we need to use the underlying buffer to support image atomics.
-	if (vkFormat != VK_FORMAT_R32_UINT && vkFormat != VK_FORMAT_R32_SINT) {
-		mvkDisableFlags(vkFeatures, VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_ATOMIC_BIT);
-	}
-	return vkFeatures;
 }