Cleanup handling of sampler border color and mirror edge clamp.

mvkMTLSamplerAddressModeFromVkSamplerAddressMode() support
MTLSamplerAddressModeClampToBorderColor and
MTLSamplerAddressModeMirrorClampToEdge if available.
Add MVKSampler::getMTLSamplerAddressMode() to test for device feature availability.
Remove special border color handling in MVKSampler::newMTLSamplerDescriptor().
diff --git a/MoltenVK/MoltenVK/API/mvk_datatypes.h b/MoltenVK/MoltenVK/API/mvk_datatypes.h
index 08b42b4..186a459 100644
--- a/MoltenVK/MoltenVK/API/mvk_datatypes.h
+++ b/MoltenVK/MoltenVK/API/mvk_datatypes.h
@@ -286,13 +286,10 @@
 
 #pragma mark Samplers
 
-/**
- * Returns the Metal MTLSamplerAddressMode corresponding to the specified Vulkan VkSamplerAddressMode,
- * or returns MTLSamplerAddressModeMirrorClampToEdge if no corresponding MTLSamplerAddressMode exists.
- */
+/** Returns the Metal MTLSamplerAddressMode corresponding to the specified Vulkan VkSamplerAddressMode. */
 MTLSamplerAddressMode mvkMTLSamplerAddressModeFromVkSamplerAddressMode(VkSamplerAddressMode vkMode);
 
-#if MVK_MACOS_OR_IOS 
+#if MVK_MACOS_OR_IOS
 /**
  * Returns the Metal MTLSamplerBorderColor corresponding to the specified Vulkan VkBorderColor,
  * or returns MTLSamplerBorderColorTransparentBlack if no corresponding MTLSamplerBorderColor exists.
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKImage.h b/MoltenVK/MoltenVK/GPUObjects/MVKImage.h
index d8dd620..ff719db 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKImage.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKImage.h
@@ -695,7 +695,6 @@
     /** Returns the number of planes if this is a ycbcr conversion or 0 otherwise. */
     inline uint8_t getPlaneCount() { return (_ycbcrConversion) ? _ycbcrConversion->getPlaneCount() : 0; }
 
-
 	/**
 	 * If this sampler requires hardcoding in MSL, populates the hardcoded sampler in the resource binding.
 	 * Returns whether this sampler requires hardcoding in MSL, and the constant sampler was populated.
@@ -705,6 +704,9 @@
 	/** Returns whether this sampler must be implemented as a hardcoded constant sampler in the shader MSL code. */
 	inline 	bool getRequiresConstExprSampler() { return _requiresConstExprSampler; }
 
+	/** Returns the Metal MTLSamplerAddressMode corresponding to the specified Vulkan VkSamplerAddressMode. */
+	MTLSamplerAddressMode getMTLSamplerAddressMode(VkSamplerAddressMode vkMode);
+
 	MVKSampler(MVKDevice* device, const VkSamplerCreateInfo* pCreateInfo);
 
 	~MVKSampler() override;
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm b/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm
index 435d78e..b5a503b 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm
@@ -1909,16 +1909,29 @@
 	return _requiresConstExprSampler;
 }
 
+// Ensure available Metal features.
+MTLSamplerAddressMode MVKSampler::getMTLSamplerAddressMode(VkSamplerAddressMode vkMode) {
+	if ((vkMode == VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER && !_device->_pMetalFeatures->samplerClampToBorder) ||
+		(vkMode == VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE && !_device->_pMetalFeatures->samplerMirrorClampToEdge)) {
+		return MTLSamplerAddressModeClampToZero;
+	}
+	return mvkMTLSamplerAddressModeFromVkSamplerAddressMode(vkMode);
+}
+
 // Returns an Metal sampler descriptor constructed from the properties of this image.
 // It is the caller's responsibility to release the returned descriptor object.
 MTLSamplerDescriptor* MVKSampler::newMTLSamplerDescriptor(const VkSamplerCreateInfo* pCreateInfo) {
 
 	MTLSamplerDescriptor* mtlSampDesc = [MTLSamplerDescriptor new];		// retained
-	mtlSampDesc.sAddressMode = mvkMTLSamplerAddressModeFromVkSamplerAddressMode(pCreateInfo->addressModeU);
-	mtlSampDesc.tAddressMode = mvkMTLSamplerAddressModeFromVkSamplerAddressMode(pCreateInfo->addressModeV);
+	mtlSampDesc.sAddressMode = getMTLSamplerAddressMode(pCreateInfo->addressModeU);
+	mtlSampDesc.tAddressMode = getMTLSamplerAddressMode(pCreateInfo->addressModeV);
     if (!pCreateInfo->unnormalizedCoordinates) {
-        mtlSampDesc.rAddressMode = mvkMTLSamplerAddressModeFromVkSamplerAddressMode(pCreateInfo->addressModeW);
+        mtlSampDesc.rAddressMode = getMTLSamplerAddressMode(pCreateInfo->addressModeW);
     }
+#if MVK_MACOS_OR_IOS
+	mtlSampDesc.borderColorMVK = mvkMTLSamplerBorderColorFromVkBorderColor(pCreateInfo->borderColor);
+#endif
+
 	mtlSampDesc.minFilter = mvkMTLSamplerMinMagFilterFromVkFilter(pCreateInfo->minFilter);
 	mtlSampDesc.magFilter = mvkMTLSamplerMinMagFilterFromVkFilter(pCreateInfo->magFilter);
     mtlSampDesc.mipFilter = (pCreateInfo->unnormalizedCoordinates
@@ -1940,20 +1953,6 @@
 		mtlSampDesc.compareFunctionMVK = mvkMTLCompareFunctionFromVkCompareOp(pCreateInfo->compareOp);
 	}
 
-#if MVK_MACOS_OR_IOS
-	mtlSampDesc.borderColorMVK = mvkMTLSamplerBorderColorFromVkBorderColor(pCreateInfo->borderColor);
-	if (getPhysicalDevice()->getMetalFeatures()->samplerClampToBorder) {
-		if (pCreateInfo->addressModeU == VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER) {
-			mtlSampDesc.sAddressMode = MTLSamplerAddressModeClampToBorderColor;
-		}
-		if (pCreateInfo->addressModeV == VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER) {
-			mtlSampDesc.tAddressMode = MTLSamplerAddressModeClampToBorderColor;
-		}
-		if (pCreateInfo->addressModeW == VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER) {
-			mtlSampDesc.rAddressMode = MTLSamplerAddressModeClampToBorderColor;
-		}
-	}
-#endif
 	return mtlSampDesc;
 }
 
diff --git a/MoltenVK/MoltenVK/Vulkan/mvk_datatypes.mm b/MoltenVK/MoltenVK/Vulkan/mvk_datatypes.mm
index e68c4a6..9953aa2 100644
--- a/MoltenVK/MoltenVK/Vulkan/mvk_datatypes.mm
+++ b/MoltenVK/MoltenVK/Vulkan/mvk_datatypes.mm
@@ -282,12 +282,12 @@
 	switch (vkMode) {
 		case VK_SAMPLER_ADDRESS_MODE_REPEAT:				return MTLSamplerAddressModeRepeat;
 		case VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE:			return MTLSamplerAddressModeClampToEdge;
-		case VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER:		return MTLSamplerAddressModeClampToZero;
 		case VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT:		return MTLSamplerAddressModeMirrorRepeat;
-#if MVK_MACOS
+#if MVK_MACOS || (MVK_IOS && MVK_XCODE_12)
 		case VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE:	return MTLSamplerAddressModeMirrorClampToEdge;
+		case VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER:		return MTLSamplerAddressModeClampToBorderColor;
 #endif
-		default:								return MTLSamplerAddressModeClampToZero;
+		default:											return MTLSamplerAddressModeClampToZero;
 	}
 }