Merge pull request #1395 from billhollings/apple-silicon-clear-color-adjustment

Fix incorrect translation of clear color values on Apple Silicon.
diff --git a/Docs/Whats_New.md b/Docs/Whats_New.md
index 374bd2d..e7daeb8 100644
--- a/Docs/Whats_New.md
+++ b/Docs/Whats_New.md
@@ -13,6 +13,15 @@
 
 
 
+MoltenVK 1.1.5
+--------------
+
+Released TBD
+
+- Fix incorrect translation of clear color values on Apple Silicon.
+
+
+
 MoltenVK 1.1.4
 --------------
 
diff --git a/MoltenVK/MoltenVK/API/vk_mvk_moltenvk.h b/MoltenVK/MoltenVK/API/vk_mvk_moltenvk.h
index c77d52d..149dd21 100644
--- a/MoltenVK/MoltenVK/API/vk_mvk_moltenvk.h
+++ b/MoltenVK/MoltenVK/API/vk_mvk_moltenvk.h
@@ -50,12 +50,12 @@
  */
 #define MVK_VERSION_MAJOR   1
 #define MVK_VERSION_MINOR   1
-#define MVK_VERSION_PATCH   4
+#define MVK_VERSION_PATCH   5
 
 #define MVK_MAKE_VERSION(major, minor, patch)    (((major) * 10000) + ((minor) * 100) + (patch))
 #define MVK_VERSION     MVK_MAKE_VERSION(MVK_VERSION_MAJOR, MVK_VERSION_MINOR, MVK_VERSION_PATCH)
 
-#define VK_MVK_MOLTENVK_SPEC_VERSION            31
+#define VK_MVK_MOLTENVK_SPEC_VERSION            32
 #define VK_MVK_MOLTENVK_EXTENSION_NAME          "VK_MVK_moltenvk"
 
 /** Identifies the level of logging MoltenVK should be limited to outputting. */
@@ -827,6 +827,14 @@
 
 } MVKConfiguration;
 
+/** Identifies the type of rounding Metal uses for float to integer conversions in particular calculatons. */
+typedef enum MVKFloatRounding {
+	MVK_FLOAT_ROUNDING_NEAREST     = 0,	 /**< Metal rounds to nearest. */
+	MVK_FLOAT_ROUNDING_UP          = 1,	 /**< Metal rounds towards positive infinity. */
+	MVK_FLOAT_ROUNDING_DOWN        = 2,	 /**< Metal rounds towards negative infinity. */
+	MVK_FLOAT_ROUNDING_UP_MAX_ENUM = 0x7FFFFFFF
+} MVKFloatRounding;
+
 /**
  * Features provided by the current implementation of Metal on the current device. You can
  * retrieve a copy of this structure using the vkGetPhysicalDeviceMetalFeaturesMVK() function.
@@ -906,6 +914,7 @@
     VkBool32 tileBasedDeferredRendering;        /**< If true, this device uses tile-based deferred rendering. */
 	VkBool32 argumentBuffers;					/**< If true, Metal argument buffers are supported. */
 	VkBool32 descriptorSetArgumentBuffers;		/**< If true, a Metal argument buffer can be assigned to a descriptor set, and used on any pipeline and pipeline stage. If false, a different Metal argument buffer must be used for each pipeline-stage/descriptor-set combination. */
+	MVKFloatRounding clearColorFloatRounding;	/**< Identifies the type of rounding Metal uses for MTLClearColor float to integer conversions. */
 } MVKPhysicalDeviceMetalFeatures;
 
 /** MoltenVK performance of a particular type of activity. */
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm
index ac1dbfd..1e8146b 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm
@@ -1192,6 +1192,19 @@
 
 	_metalFeatures.maxPerStageStorageTextureCount = 8;
 
+	// GPU-specific features
+	switch (_properties.vendorID) {
+		case kAMDVendorId:
+			_metalFeatures.clearColorFloatRounding = MVK_FLOAT_ROUNDING_DOWN;
+			break;
+		case kAppleVendorId:
+		case kIntelVendorId:
+		case kNVVendorId:
+		default:
+			_metalFeatures.clearColorFloatRounding = MVK_FLOAT_ROUNDING_NEAREST;
+			break;
+	}
+
 #if MVK_TVOS
 	_metalFeatures.mslVersionEnum = MTLLanguageVersion1_1;
     _metalFeatures.mtlBufferAlignment = 64;
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.mm b/MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.mm
index 22117dc..c40a156 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.mm
@@ -467,100 +467,108 @@
 			mtlClr.green	= vkClearValue.color.float32[1];
 			mtlClr.blue		= vkClearValue.color.float32[2];
 			mtlClr.alpha	= vkClearValue.color.float32[3];
-			// For normalized formats, increment the clear value by half the minimum delta
-			// (i.e. 1/(2*(2**component_size - 1))), to force Metal to round up. This should
-			// fix some problems with clear values being off by one.
-#define OFFSET_UNORM(COLOR, DENOM) if (mtlClr.COLOR > 0.0 && mtlClr.COLOR < 1.0) { mtlClr.COLOR += 1.0/DENOM; }
-#define OFFSET_SNORM(COLOR, DENOM) if (mtlClr.COLOR > -1.0 && mtlClr.COLOR < 1.0) { mtlClr.COLOR += 1.0/DENOM; }
-			switch (vkFormat) {
-				case VK_FORMAT_R4G4B4A4_UNORM_PACK16:
-					OFFSET_UNORM(red, 30.0)
-					OFFSET_UNORM(green, 30.0)
-					OFFSET_UNORM(blue, 30.0)
-					OFFSET_UNORM(alpha, 30.0);
-					break;
-				case VK_FORMAT_R5G6B5_UNORM_PACK16:
-					OFFSET_UNORM(red, 62.0)
-					OFFSET_UNORM(green, 126.0)
-					OFFSET_UNORM(blue, 62.0)
-					break;
-				case VK_FORMAT_R5G5B5A1_UNORM_PACK16:
-				case VK_FORMAT_A1R5G5B5_UNORM_PACK16:
-					OFFSET_UNORM(red, 62.0)
-					OFFSET_UNORM(green, 62.0)
-					OFFSET_UNORM(blue, 62.0)
-					break;
-				case VK_FORMAT_R8_UNORM:
-				case VK_FORMAT_R8_SRGB:
-					OFFSET_UNORM(red, 510.0)
-					break;
-				case VK_FORMAT_R8_SNORM:
-					OFFSET_SNORM(red, 254.0)
-					break;
-				case VK_FORMAT_R8G8_UNORM:
-				case VK_FORMAT_R8G8_SRGB:
-					OFFSET_UNORM(red, 510.0)
-					OFFSET_UNORM(green, 510.0)
-					break;
-				case VK_FORMAT_R8G8_SNORM:
-					OFFSET_SNORM(red, 254.0)
-					OFFSET_SNORM(green, 254.0)
-					break;
-				case VK_FORMAT_R8G8B8A8_UNORM:
-				case VK_FORMAT_R8G8B8A8_SRGB:
-				case VK_FORMAT_B8G8R8A8_UNORM:
-				case VK_FORMAT_B8G8R8A8_SRGB:
-				case VK_FORMAT_A8B8G8R8_UNORM_PACK32:
-				case VK_FORMAT_A8B8G8R8_SRGB_PACK32:
-					OFFSET_UNORM(red, 510.0)
-					OFFSET_UNORM(green, 510.0)
-					OFFSET_UNORM(blue, 510.0)
-					OFFSET_UNORM(alpha, 510.0)
-					break;
-				case VK_FORMAT_R8G8B8A8_SNORM:
-					OFFSET_SNORM(red, 254.0)
-					OFFSET_SNORM(green, 254.0)
-					OFFSET_SNORM(blue, 254.0)
-					OFFSET_SNORM(alpha, 254.0)
-					break;
-				case VK_FORMAT_A2R10G10B10_UNORM_PACK32:
-				case VK_FORMAT_A2B10G10R10_UNORM_PACK32:
-					OFFSET_UNORM(red, 2046.0)
-					OFFSET_UNORM(green, 2046.0)
-					OFFSET_UNORM(blue, 2046.0)
-					OFFSET_UNORM(alpha, 6.0)
-					break;
-				case VK_FORMAT_R16_UNORM:
-					OFFSET_UNORM(red, 131070.0)
-					break;
-				case VK_FORMAT_R16_SNORM:
-					OFFSET_SNORM(red, 65534.0)
-					break;
-				case VK_FORMAT_R16G16_UNORM:
-					OFFSET_UNORM(red, 131070.0)
-					OFFSET_UNORM(green, 131070.0)
-					break;
-				case VK_FORMAT_R16G16_SNORM:
-					OFFSET_SNORM(red, 65534.0)
-					OFFSET_SNORM(green, 65534.0)
-					break;
-				case VK_FORMAT_R16G16B16A16_UNORM:
-					OFFSET_UNORM(red, 131070.0)
-					OFFSET_UNORM(green, 131070.0)
-					OFFSET_UNORM(blue, 131070.0)
-					OFFSET_UNORM(alpha, 131070.0)
-					break;
-				case VK_FORMAT_R16G16B16A16_SNORM:
-					OFFSET_SNORM(red, 65534.0)
-					OFFSET_SNORM(green, 65534.0)
-					OFFSET_SNORM(blue, 65534.0)
-					OFFSET_SNORM(alpha, 65534.0)
-					break;
-				default:
-					break;
-			}
+
+			if (_physicalDevice && _physicalDevice->getMetalFeatures()->clearColorFloatRounding == MVK_FLOAT_ROUNDING_DOWN) {
+				// For normalized formats, increment the clear value by half the ULP
+				// (i.e. 1/(2*(2**component_size - 1))), to force Metal to round up.
+				// This should fix some problems with clear values being off by one ULP on some platforms.
+#define OFFSET_NORM(MIN_VAL, COLOR, BIT_WIDTH)  \
+	if (mtlClr.COLOR > (MIN_VAL) && mtlClr.COLOR < 1.0) {  \
+		mtlClr.COLOR += 1.0 / (2.0 * ((1U << (BIT_WIDTH)) - 1));  \
+	}
+#define OFFSET_UNORM(COLOR, BIT_WIDTH)    OFFSET_NORM(0.0, COLOR, BIT_WIDTH)
+#define OFFSET_SNORM(COLOR, BIT_WIDTH)    OFFSET_NORM(-1.0, COLOR, BIT_WIDTH - 1)
+				switch (vkFormat) {
+					case VK_FORMAT_R4G4B4A4_UNORM_PACK16:
+						OFFSET_UNORM(red, 4)
+						OFFSET_UNORM(green, 4)
+						OFFSET_UNORM(blue, 4)
+						OFFSET_UNORM(alpha, 4)
+						break;
+					case VK_FORMAT_R5G6B5_UNORM_PACK16:
+						OFFSET_UNORM(red, 5)
+						OFFSET_UNORM(green, 6)
+						OFFSET_UNORM(blue, 5)
+						break;
+					case VK_FORMAT_R5G5B5A1_UNORM_PACK16:
+					case VK_FORMAT_A1R5G5B5_UNORM_PACK16:
+						OFFSET_UNORM(red, 5)
+						OFFSET_UNORM(green, 5)
+						OFFSET_UNORM(blue, 5)
+						break;
+					case VK_FORMAT_R8_UNORM:
+					case VK_FORMAT_R8_SRGB:
+						OFFSET_UNORM(red, 8)
+						break;
+					case VK_FORMAT_R8_SNORM:
+						OFFSET_SNORM(red, 8)
+						break;
+					case VK_FORMAT_R8G8_UNORM:
+					case VK_FORMAT_R8G8_SRGB:
+						OFFSET_UNORM(red, 8)
+						OFFSET_UNORM(green, 8)
+						break;
+					case VK_FORMAT_R8G8_SNORM:
+						OFFSET_SNORM(red, 8)
+						OFFSET_SNORM(green, 8)
+						break;
+					case VK_FORMAT_R8G8B8A8_UNORM:
+					case VK_FORMAT_R8G8B8A8_SRGB:
+					case VK_FORMAT_B8G8R8A8_UNORM:
+					case VK_FORMAT_B8G8R8A8_SRGB:
+					case VK_FORMAT_A8B8G8R8_UNORM_PACK32:
+					case VK_FORMAT_A8B8G8R8_SRGB_PACK32:
+						OFFSET_UNORM(red, 8)
+						OFFSET_UNORM(green, 8)
+						OFFSET_UNORM(blue, 8)
+						OFFSET_UNORM(alpha, 8)
+						break;
+					case VK_FORMAT_R8G8B8A8_SNORM:
+						OFFSET_SNORM(red, 8)
+						OFFSET_SNORM(green, 8)
+						OFFSET_SNORM(blue, 8)
+						OFFSET_SNORM(alpha, 8)
+						break;
+					case VK_FORMAT_A2R10G10B10_UNORM_PACK32:
+					case VK_FORMAT_A2B10G10R10_UNORM_PACK32:
+						OFFSET_UNORM(red, 10)
+						OFFSET_UNORM(green, 10)
+						OFFSET_UNORM(blue, 10)
+						OFFSET_UNORM(alpha, 2)
+						break;
+					case VK_FORMAT_R16_UNORM:
+						OFFSET_UNORM(red, 16)
+						break;
+					case VK_FORMAT_R16_SNORM:
+						OFFSET_SNORM(red, 16)
+						break;
+					case VK_FORMAT_R16G16_UNORM:
+						OFFSET_UNORM(red, 16)
+						OFFSET_UNORM(green, 16)
+						break;
+					case VK_FORMAT_R16G16_SNORM:
+						OFFSET_SNORM(red, 16)
+						OFFSET_SNORM(green, 16)
+						break;
+					case VK_FORMAT_R16G16B16A16_UNORM:
+						OFFSET_UNORM(red, 16)
+						OFFSET_UNORM(green, 16)
+						OFFSET_UNORM(blue, 16)
+						OFFSET_UNORM(alpha, 16)
+						break;
+					case VK_FORMAT_R16G16B16A16_SNORM:
+						OFFSET_SNORM(red, 16)
+						OFFSET_SNORM(green, 16)
+						OFFSET_SNORM(blue, 16)
+						OFFSET_SNORM(alpha, 16)
+						break;
+					default:
+						break;
+				}
 #undef OFFSET_UNORM
 #undef OFFSET_SNORM
+#undef OFFSET_NORM
+			}
 			break;
 		case kMVKFormatColorUInt8:
 		case kMVKFormatColorUInt16: